LLM_Agentic_RAG个性化推荐快速编码开发实现:llmamalndex与agenticRAG框架支撑;虾骡主流智能机器人借力。电子信息云端线上查阅落地:http://115.190.138.213:7125/drctXpl。

1 设计思想与架构

1.1 设计思路

一个基于LlamaIndex框架、采用Agentic RAG架构的简单个性化推荐系统实现,适用于CPU和Windows环境。程序将整合中文语义检索增强,并使用本地部署的模型。

1.2 AI-DS借力架构

咨询提示词:给出CPU和Windows下采用LlamaIndex框架、基于Agentic RAG架构的简单个性化推荐的编码,整个程序用一个文件实现,注意各个依赖库的版本对应和中文的检索加强,不使用CONDA,LLM模型采用Ollama部署本地的DeepSeek-R1-1.5B,需要的嵌入模型己通过modelscope下载在本地D:\01ifmts\models\nlp_gte_sentence-embedding_chinese-base,详细说明各个实现过程和测调试运行。

2 开发及其模型准备

2.1 编程环境Python/Thonny

2.2 编辑环境Notepad++

2.3 Ollama/DeepSeek

2.4 准备NLP

2.5 依赖下载

3 功能编码实现

3.1 编码实现

# ==============================================================================
# Agentic RAG 个性化推荐系统
# 基于LlamaIndex框架 + Ollama(DeepSeek-R1-1.5B) + 本地GTE中文嵌入模型
# 单文件实现 | CPU & Windows | 中文检索增强
# ==============================================================================
"""
==============================================================================
[1] 环境准备 — 依赖安装 (pip, 不使用CONDA)
==============================================================================
在 Windows CMD 或 PowerShell 中依次执行:
    # 1. 创建并激活虚拟环境(推荐,非必须)
    python -m venv rag_env
    rag_env\Scripts\activate
    # 2. 核心依赖安装(注意版本对应)
    pip install llama-index-core==0.12.28
    pip install llama-index-llms-ollama==0.6.0
    pip install llama-index-embeddings-huggingface==0.5.1
    pip install llama-index-readers-file==0.4.3
    pip install sentence-transformers==3.4.1
    pip install torch==2.5.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
    pip install pydantic==2.10.4
    pip install numpy==1.26.4
    pip install huggingface-hub==0.27.1
    pip install nest-asyncio==1.6.0
    pip install requests==2.32.3
    # 3. 确保 Ollama 已安装并运行 DeepSeek-R1-1.5B
    #    安装 Ollama: https://ollama.com/download
    #    拉取模型:    ollama pull deepseek-r1:1.5b
    #    启动服务:    ollama serve    (默认 http://localhost:11434)
    # 4. 确认嵌入模型路径
    #    D:\\01ifmts\\models\\nlp_gte_sentence-embedding_chinese-base
    #    该目录下应包含: config.json, model.safetensors, tokenizer.json 等文件
==============================================================================
[2] 版本兼容性说明
==============================================================================
| 包名                                | 版本       | 说明                    |
|------------------------------------|------------|--------------------------|
| llama-index-core                   | 0.12.28     | LlamaIndex 核心包        |
| llama-index-llms-ollama            | 0.6.0       | Ollama LLM 集成         |
| llama-index-embeddings-huggingface | 0.5.1       | HuggingFace 嵌入集成     |
| llama-index-readers-file           | 0.4.3       | 文件读取器               |
| sentence-transformers              | 3.4.1       | SentenceTransformer 底层   |
| torch                              | 2.5.1+cpu   | CPU版PyTorch           |
| pydantic                           | 2.10.4      | 数据模型框架             |
| numpy                              | 1.26.4     | 数值计算                 |
| huggingface-hub                    | 0.27.1     | 模型下载/缓存             |
| nest-asyncio                       | 1.6.0      | 异步事件循环兼容(Jupyter等) |
| requests                           | 2.32.3     | HTTP请求(Ollama诊断用)   |
核心兼容链:
  llama-index-core 0.12.x
    ├─ llama-index-llms-ollama 0.5.x~0.6.x
    ├─ llama-index-embeddings-huggingface 0.4.x~0.5.x
    └─ llama-index-readers-file 0.3.x~0.4.x
  sentence-transformers 3.x → torch 2.x
  HuggingFaceEmbedding 底层调用 sentence-transformers.SentenceTransformer
  AgentWorkflow (0.12+) → FunctionAgent + asyncio
==============================================================================
[3] 运行方式
==============================================================================
    python agentic_rag_recommend.py
==============================================================================
"""
# ==============================================================================
# [4] 导入与配置
# ==============================================================================
import os, sys, json, time, asyncio, logging
from pathlib import Path
from typing import List, Dict, Optional
# ---- 日志配置 ----
logging.basicConfig( level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)] )
logger = logging.getLogger("AgenticRAG")
# ---- 关键路径配置(根据你的实际环境修改) ----
# Ollama 服务地址
OLLAMA_BASE_URL = "http://localhost:11434"
# DeepSeek-R1-1.5B 在 Ollama 中的模型名称
OLLAMA_MODEL_NAME = "deepseek-r1:1.5b"
# 本地GTE中文嵌入模型路径(ModelScope下载的模型目录)
# 注意:Windows路径中的反斜杠需要用原始字符串或双反斜杠
EMBEDDING_MODEL_PATH = r"D:\01ifmts\models\nlp_gte_sentence-embedding_chinese-base"
# ==============================================================================
# [5] LlamaIndex 核心导入
# ==============================================================================
from llama_index.core import ( Settings, VectorStoreIndex, Document, )
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.tools import QueryEngineTool
from llama_index.core.agent.workflow import FunctionAgent, AgentWorkflow
# ==============================================================================
# [6] LLM 配置 — Ollama + DeepSeek-R1-1.5B
# ==============================================================================
from llama_index.llms.ollama import Ollama
def setup_llm():
    """
    配置本地 Ollama LLM。
    DeepSeek-R1-1.5B 是蒸馏推理模型:
      - 参数量: 1.5B
      - 上下文窗口: 约 8192 tokens (实际使用 4096 以保证稳定性)
      - 支持思维链推理 (Chain-of-Thought)
    request_timeout 设为 300s,因为 CPU 推理速度较慢,
    DeepSeek-R1 的思维链可能产生大量 token。
    """
    llm = Ollama( model=OLLAMA_MODEL_NAME, base_url=OLLAMA_BASE_URL,
        temperature=0.7,           # 适中的创造性
        context_window=4096,      # 保守的上下文窗口,1.5B模型稳定运行
        request_timeout=300.0,     # CPU推理较慢,需要较长超时
        is_function_calling_model=True,  # 启用函数调用以支持Agent工具选择
        additional_kwargs={ "num_predict": 1024, }, )  # 限制最大生成token数
    # 全局设置 LLM
    Settings.llm = llm
    logger.info(f"LLM 配置完成: Ollama + {OLLAMA_MODEL_NAME}")
    return llm
# ==============================================================================
# [7] 嵌入模型配置 — 本地 GTE 中文嵌入模型
# ==============================================================================
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
def setup_embedding():
    """
    配置本地 GTE 中文嵌入模型。
    模型来源: 阿里达摩院 ModelScope -> iic/nlp_gte_sentence-embedding_chinese-base
    关键参数说明:
      - model_name: 本地路径,HuggingFaceEmbedding 会自动识别为路径并加载
      - device: "cpu",因为目标环境无GPU
      - max_length: 128,该模型默认最大文本长度
      - normalize: True,归一化嵌入向量以提高检索精度
      - trust_remote_code: True,本地模型可能包含自定义代码
    中文检索增强策略:
      1. GTE 模型原生支持中文语义编码
      2. 使用中文友好的 SentenceSplitter(按标点分句)
      3. chunk_size 设为 300(中文约150~200字),保证语义完整性
      4. 查询时使用中文查询模板增强检索效果
    """
    # 验证模型路径是否存在
    model_path = Path(EMBEDDING_MODEL_PATH)
    if not model_path.exists():
        logger.error( f"嵌入模型路径不存在: {EMBEDDING_MODEL_PATH}\n"
            f"请确认模型已下载到该目录,或修改 EMBEDDING_MODEL_PATH 变量" )
        sys.exit(1)
    # 检查关键模型文件
    required_files = ["config.json"]
    missing_files = [f for f in required_files if not (model_path / f).exists()]
    if missing_files:
        logger.warning(f"模型目录缺少文件: {missing_files},尝试继续加载...")
    embed_model = HuggingFaceEmbedding(
        model_name=EMBEDDING_MODEL_PATH,  # 直接指向本地目录
        device="cpu",                      # CPU 模式
        max_length=128,                    # GTE-base 默认最大长度
        normalize=True,                   # 归一化向量
        trust_remote_code=True,           # 允许本地自定义代码
        embed_batch_size=8,               # 小批量避免内存溢出
        show_progress_bar=True,           # 显示嵌入进度条
    )
    # 全局设置嵌入模型
    Settings.embed_model = embed_model
    # 验证嵌入模型
    test_embedding = embed_model.get_text_embedding("测试中文嵌入")
    logger.info( f"嵌入模型配置完成: GTE-base 中文\n"
        f"  向量维度: {len(test_embedding)}\n"
        f"  模型路径: {EMBEDDING_MODEL_PATH}" )
    return embed_model
# ==============================================================================
# [8] 中文增强的文档分割器
# ==============================================================================
def setup_node_parser():
    """
    配置中文友好的文档分割器。
    中文分割策略:
      - chunk_size: 300 字符(约 150~200 中文字),
        小 chunk 更精确匹配,提升检索召回率
      - chunk_overlap: 50 字符,保证跨 chunk 的语义连贯
      - separator: 保留默认空格(不影响中文切分)
      - paragraph_separator: 段落分隔符
      - secondary_chunking_regex: 加入中文标点的正则,精准断句
    LlamaIndex 的 SentenceSplitter 会按以下优先级分割:
      1. 段落分隔符 (\\n\\n)
      2. 句子级分隔符 (。!?;)
      3. 分词级
      4. 字符级
    """
    node_parser = SentenceSplitter( chunk_size=300, # 中文短 chunk,提升检索精度
        chunk_overlap=50,           # 适当重叠避免语义断裂
        separator=" ",              # 基础分隔符(保留默认)
        paragraph_separator="\n\n", # 段落分隔符
        # 中文标点加入正则,精准断句
        secondary_chunking_regex=r'[^,.;;。?!……,]+[,.;;。?!……,]?|[,.;;。?!……,]', )
    Settings.node_parser = node_parser
    logger.info("文档分割器配置完成: 中文增强 (chunk_size=300)")
    return node_parser
# ==============================================================================
# [9] 示例知识库数据 — 个性化推荐场景
# ==============================================================================
def create_sample_documents() -> List[Document]:
    """
    创建示例知识库文档,模拟个性化推荐系统的数据源。
    这里模拟三个不同领域的知识库:
      1. 电影推荐库 — 适合娱乐类查询
      2. 书籍推荐库 — 适合阅读类查询
      3. 美食推荐库 — 适合餐饮类查询
    Agentic RAG 的核心思想:
      Agent 根据用户查询,自主决定检索哪个知识库(工具选择),
      而不是像传统 RAG 把所有数据混在一起检索。
    每个文档的 metadata 包含:
      - category: 领域分类
      - tags: 标签(用于后续个性化过滤)
      - audience: 适合的受众
    """
    documents = []
    # ---- 电影推荐数据 ----
    movies_data = [
        {   "title": "星际穿越",
            "content": """《星际穿越》是克里斯托弗·诺兰执导的科幻电影。影片讲述了一组宇航员穿越虫洞,为人类寻找新家园的故事。电影融合了量子物理、时间膨胀等硬科幻元素,视觉效果震撼,配乐由汉斯·季默创作。适合喜欢科幻、物理和宇宙探索的观众。影片情感线围绕父女关系展开,非常感人。推荐指数:五颗星""",
            "tags": "科幻,冒险,亲情,诺兰", "audience": "科幻爱好者,物理迷",
        },
        {   "title": "千与千寻",
            "content": """《千与千寻》是宫崎骏执导的动画电影,荣获奥斯卡最佳动画长片奖。讲述少女千寻误入神灵世界,在汤屋工作的奇幻经历。影片充满日本神道教文化元素,画面精美细腻,配乐久石让操刀。
主题涉及成长、勇气、环保和身份认同。适合全年龄段观众。推荐指数:五颗星""",
            "tags": "动画,奇幻,成长,宫崎骏", "audience": "动画爱好者,家庭观众",
        },
        {   "title": "肖申克的救赎", "content": """《肖申克的救赎》被誉为影史最伟大电影之一。讲述银行家安迪被冤入狱,在肖申克监狱中用智慧和毅力重获自由的故事。影片主题是希望与自由,台词希望是好事也许是最好的事深入人心。节奏沉稳,演技精湛,结局令人热泪盈眶。推荐指数:五颗星""",
            "tags": "剧情,自由,希望,经典", "audience": "剧情片爱好者",
        },
        {   "title": "流浪地球2",
            "content": """《流浪地球2》是中国科幻电影里程碑之作。讲述了太阳即将毁灭,人类开启流浪地球计划寻找新家园的故事。视觉效果达到好莱坞水准,刘培强、图恒宇双线叙事。影片融合了AI伦理、数字生命、人类命运等深刻主题。适合中国科幻迷和喜欢宏大叙事的观众。推荐指数:四颗半星""",
            "tags": "科幻,中国电影,灾难,AI", "audience": "中国科幻迷",
        },
        {   "title": "盗梦空间",
            "content": """《盗梦空间》是诺兰又一力作,探索梦境与现实的边界。莱昂纳多饰演盗梦者Cobb,在多层梦境中执行植入想法的任务。影片结构精巧,梦层设计令人叹为观止,结尾陀螺是否停转成为永恒悬念。适合喜欢烧脑、逻辑推理和哲学思考的观众。推荐指数:五颗星""",
            "tags": "科幻,悬疑,梦境,诺兰", "audience": "烧脑片爱好者",
        },
    ]
    for m in movies_data:
        documents.append(Document(
            text=m["content"],
            metadata={ "category": "电影", "title": m["title"], "tags": m["tags"], "audience": m["audience"], },
            excluded_llm_metadata_keys=["tags", "audience"],
            metadata_template="{title}\n类别: {category}",
            text_template=" metadata: {metadata_str} \n 内容: {content}", ))
    # ---- 书籍推荐数据 ----
    books_data = [
        {   "title": "三体",
            "content": """《三体》是刘慈欣创作的硬科幻小说,中国科幻文学里程碑。讲述地球文明与三体文明的首次接触和博弈,涵盖宇宙社会学、黑暗森林法则等概念。三部曲包括《三体》《黑暗森林》《死神永生》,每一部格局递进。适合喜欢硬科幻、宇宙探索和哲学思考的读者。获雨果奖最佳长篇小说。
推荐指数:五颗星""",
            "tags": "科幻,中国文学,宇宙,哲学", "audience": "科幻迷,理工科读者",
        },
        {   "title": "活着",
            "content": """《活着》是余华的代表作,讲述了农民福贵坎坷的一生。从地主少爷到贫农,经历了内战、大跃进、文革等历史动荡,亲人一个个离去,但他依然坚强地活着。文字简洁有力,情感冲击极强,是中国当代文学经典。适合喜欢现实主义文学和深度思考的读者。推荐指数:五颗星""",
            "tags": "文学,现实主义,中国,生命", "audience": "文学爱好者",
        },
        {   "title": "小王子",
            "content": """《小王子》是法国作家圣埃克苏佩里的经典童话。讲述小王子从B612星球出发,游历各星球的奇妙旅程。表面是童话,实则是对成人世界的深刻反思——真正重要的东西,用眼睛是看不见的。适合所有年龄段的读者,尤其适合在迷茫时重读。推荐指数:五颗星""",
            "tags": "童话,哲学,法国,成长", "audience": "所有读者",
        },
        {   "title": "人类简史",
            "content": """《人类简史》是以色列学者尤瓦尔·赫拉利的代表作。从认知革命、农业革命到科学革命,重新审视人类7万年发展史。提出了虚构故事推动协作、小麦驯化了人类等颠覆性观点。语言生动,跨学科视角独特。适合对历史、人类学和社会学感兴趣的读者。推荐指数:四颗半星""",
            "tags": "历史,人类学,社科", "audience": "社科爱好者",
        },
    ]
    for b in books_data:
        documents.append(Document( text=b["content"],
            metadata={ "category": "书籍", "title": b["title"], "tags": b["tags"], "audience": b["audience"], },
            excluded_llm_metadata_keys=["tags", "audience"],
            metadata_template="{title}\n类别: {category}",
            text_template=" metadata: {metadata_str} \n 内容: {content}", ))
    # ---- 美食推荐数据 ----
    food_data = [
        {   "title": "四川火锅",
            "content": """四川火锅是中国最具代表性的美食之一。以麻辣鲜香著称,锅底由牛油、花椒、辣椒等数十种香料熬制。毛肚、鹅肠、黄喉是必点涮菜,蘸料以蒜泥香油为主。成都火锅偏麻,重庆火锅偏辣。适合喜欢重口味和热闹氛围的食客。冬天吃火锅是最幸福的体验。推荐指数:五颗星""",
            "tags": "川菜,辣,聚餐,冬天", "audience": "辣味爱好者",
        },
        {   "title": "广式早茶",
            "content": """广式早茶是广东饮食文化的精髓,又称饮茶。虾饺、烧卖、肠粉、叉烧包、凤爪是经典茶点,一盅两件是传统搭配。茶楼氛围悠闲,适合慢慢品味。广州人叹早茶是一种生活态度。适合喜欢精致小食、体验岭南文化的食客。推荐指数:五颗星""",
            "tags": "粤菜,点心,广东,休闲", "audience": "精致美食爱好者",
        },
        {   "title": "兰州牛肉面",
            "content": """兰州牛肉面是西北面食代表,讲究一清二白三红四绿五黄——汤清、萝卜白、辣油红、蒜苗绿、面条黄。手工拉面是灵魂,面条分毛细、二细、韭叶等多种粗细。清晨一碗热面,是兰州人的早餐标配。适合喜欢面食和朴实美味的食客。推荐指数:四颗半星""",
            "tags": "面食,西北,早餐,牛肉", "audience": "面食爱好者",
        },
        {   "title": "日式寿司",
            "content": """日式寿司是日本料理的代表,讲究食材新鲜和匠人技艺。握寿司是最经典形式,醋饭搭配新鲜鱼生,芥末提味。金枪鱼、三文鱼、鲷鱼是常见选择,高级寿司店追求时令鱼材。吃寿司讲究顺序:白身鱼、光物、赤身、卷物。适合喜欢海鲜和日式精致料理的食客。推荐指数:五颗星""",
            "tags": "日料,海鲜,精致,匠人", "audience": "日料爱好者",
        },
    ]
    for f in food_data:
        documents.append(Document( text=f["content"],
            metadata={ "category": "美食", "title": f["title"], "tags": f["tags"], "audience": f["audience"], },
            excluded_llm_metadata_keys=["tags", "audience"],
            metadata_template="{title}\n类别: {category}",
            text_template=" metadata: {metadata_str} \n 内容: {content}", ))
    logger.info(f"知识库文档创建完成: 共 {len(documents)} 篇")
    return documents
# ==============================================================================
# [10] 构建多领域向量索引 — Agentic RAG 的基础
# ==============================================================================
def build_category_indexes(documents: List[Document]) -> Dict[str, VectorStoreIndex]:
    """
    按领域分别构建 VectorStoreIndex。
    Agentic RAG 的核心架构:
      1. 不是把所有文档塞进一个大索引,而是按领域建立独立索引
      2. 每个索引封装为 QueryEngineTool,成为 Agent 可调用的"工具"
      3. Agent 根据用户查询语义,自主选择最合适的工具(索引)
    为什么分开索引优于合并索引:
      - 检索精度: "推荐一部科幻电影" 不会返回科幻书籍的结果
      - 可扩展性: 新增领域只需加索引,不影响已有索引
      - 个性化: 不同领域可以用不同检索策略
    中文检索增强要点:
      - GTE 中文嵌入模型天然支持中文语义相似度计算
      - chunk_size=300 保证中文短文本的精确匹配
      - VectorStoreIndex 使用余弦相似度(归一化向量)
    """
    category_indexes = {}
    # 按类别分组文档
    categories = {}
    for doc in documents:
        cat = doc.metadata.get("category", "未分类")
        if cat not in categories:
            categories[cat] = []
        categories[cat].append(doc)
    # 为每个类别构建独立的向量索引
    for category, cat_docs in categories.items():
        logger.info(f"正在构建 [{category}] 类别向量索引 ({len(cat_docs)} 篇文档)...")
        start_time = time.time()
        index = VectorStoreIndex.from_documents( cat_docs, show_progress=True, )
        build_time = time.time() - start_time
        logger.info(f"  [{category}] 索引构建完成,耗时 {build_time:.1f}s")
        category_indexes[category] = index
    return category_indexes
# ==============================================================================
# [11] 构建 Agentic RAG 查询引擎
# ==============================================================================
def build_agentic_query_engine(category_indexes: Dict[str, VectorStoreIndex]):
    """
    构建 Agentic RAG 的核心: 将多个索引封装为工具,由 Agent 自主路由。
    架构层次:
      用户查询
         │
         ▼
      AgentWorkflow (FunctionAgent)
      (DeepSeek-R1-1.5B 作为推理引擎)
      分析用户意图 → 选择最合适的工具
         │
         ├──────────┼──────────┐
         ▼          ▼          ▼
      电影索引    书籍索引    美食索引
      工具        工具        工具
    关键组件:
      1. QueryEngineTool: 将每个 VectorStoreIndex 的查询引擎包装为工具
      2. FunctionAgent: 使用 LLM 的函数调用能力选择工具
      3. AgentWorkflow: 编排 Agent 的推理和工具调用流程
      4. 工具描述: 中文描述帮助 LLM 理解每个工具的用途
    与传统 RAG 的区别:
      传统 RAG: 用户查询 → 单一索引检索 → 生成回答
      Agentic RAG: 用户查询 → Agent推理 → 选择索引 → 定向检索 → 生成回答
    注意: 新版 LlamaIndex (0.12+) 使用 AgentWorkflow + FunctionAgent
    替代了旧版 FunctionCallingAgent.from_tools() 接口。
    """
    tools = []
    # ---- 为每个领域构建查询引擎工具 ----
    tool_descriptions = { "电影": "电影推荐工具。当用户询问电影、影片、导演、演员、观影推荐等影视娱乐相关问题时使用此工具。可以检索到电影的详细介绍、推荐理由和评分。", "书籍": "书籍推荐工具。当用户询问书籍、小说、阅读推荐、文学、作者等阅读相关问题时使用此工具。可以检索到书籍的详细介绍、推荐理由和评分。", "美食": "美食推荐工具。当用户询问美食、餐厅、菜系、烹饪、餐饮推荐等饮食相关问题时使用此工具。可以检索到美食的详细介绍、口味特点和推荐指数。", }
    for category, index in category_indexes.items():
        # 创建查询引擎
        # similarity_top_k: 返回最相似的 k 个结果
        # 对于1.5B小模型,k=2避免信息过载
        query_engine = index.as_query_engine( similarity_top_k=2, )
        # 将查询引擎封装为工具
        tool = QueryEngineTool.from_defaults( query_engine=query_engine,
            name=f"{category}_recommend_tool",
            description=tool_descriptions.get(category, f"{category}领域推荐工具"), )
        tools.append(tool)
        logger.info(f"  工具已创建: {category}_recommend_tool")
    # ---- 创建 FunctionAgent + AgentWorkflow ----
    # 这是 Agentic RAG 的"大脑"
    # Agent 会:
    #   1. 分析用户查询的语义
    #   2. 决定调用哪个工具(或多个工具)
    #   3. 综合工具返回的结果
    #   4. 生成最终的个性化推荐回答
    # 系统提示词:指导 Agent 的行为
    system_prompt = ( "你是一个个性化推荐助手。根据用户的请求和偏好,"
        "选择最合适的工具检索知识库,然后给出个性化推荐。"
        "请用中文回答,推荐内容要具体,说明推荐理由。" )
    # 创建 FunctionAgent(新版 API)
    agent = FunctionAgent( name="RecommendAgent",
        description="个性化推荐智能体,能根据用户请求从电影、书籍、美食等领域给出推荐",
        tools=tools, system_prompt=system_prompt, llm=Settings.llm, verbose=True, )
    # 包装为 AgentWorkflow(统一入口)
    workflow = AgentWorkflow( agents=[agent], verbose=True, )
    logger.info("Agentic RAG 查询引擎构建完成")
    return workflow
# ==============================================================================
# [12] 个性化推荐函数
# ==============================================================================
def personalized_recommend(workflow, query: str, user_profile: Optional[Dict] = None) -> str:
    """
    执行个性化推荐查询。
    个性化策略:
      1. 将用户画像信息注入查询上下文
      2. Agent 根据查询语义选择最合适的知识领域
      3. 检索结果融入用户偏好进行推理
    user_profile 示例:
      {   "name": "小明",
          "age": 25,
          "interests": ["科幻", "动画"],
          "dietary": "不吃辣",
      }
    注意: DeepSeek-R1-1.5B 作为推理模型,会先进行思维链推理,
    再输出最终答案。
    新版 LlamaIndex 的 AgentWorkflow.run() 是异步方法,
    需要用 asyncio.run() 来同步调用。
    """
    # 构建增强查询
    if user_profile:
        profile_str = json.dumps(user_profile, ensure_ascii=False)
        enhanced_query = ( f"用户画像: {profile_str}\n"
            f"用户请求: {query}\n"
            f"请根据用户画像和请求,从知识库中检索并给出个性化推荐。" )
    else:
        enhanced_query = query
    logger.info(f"增强查询: {enhanced_query[:80]}...")
    start_time = time.time()
    # AgentWorkflow.run() 是异步方法,需要用 asyncio.run() 同步调用
    try:
        loop = asyncio.get_event_loop()
        if loop.is_running():
            # 如果已有事件循环运行中(如 Jupyter),使用 nest_asyncio
            import nest_asyncio
            nest_asyncio.apply()
            response = asyncio.run(workflow.run(user_msg=enhanced_query))
        else:
            response = asyncio.run(workflow.run(user_msg=enhanced_query))
    except RuntimeError:
        # 没有事件循环,直接创建
        response = asyncio.run(workflow.run(user_msg=enhanced_query))
    elapsed = time.time() - start_time
    logger.info(f"查询完成,耗时 {elapsed:.1f}s")
    return str(response)
# ==============================================================================
# [13] 连接测试与诊断
# ==============================================================================
def test_ollama_connection():
    """
    测试 Ollama 服务是否可用,模型是否已加载。
    诊断步骤:
      1. 尝试连接 Ollama API
      2. 检查 deepseek-r1:1.5b 模型是否存在
      3. 发送简单测试请求验证推理能力
    """
    import requests
    logger.info("=" * 60)
    logger.info("Ollama 连接测试")
    logger.info("=" * 60)
    # 测试1: Ollama 服务是否运行
    try:
        resp = requests.get(f"{OLLAMA_BASE_URL}/api/tags", timeout=10)
        if resp.status_code == 200:
            models = resp.json().get("models", [])
            model_names = [m.get("name", "") for m in models]
            logger.info(f"  Ollama 服务运行中,已安装模型: {model_names}")
            # 测试2: 检查目标模型
            target_found = any(OLLAMA_MODEL_NAME in n for n in model_names)
            if target_found:
                logger.info(f" OK 模型 {OLLAMA_MODEL_NAME} 已安装")
            else:
                logger.error( f" FAIL 模型 {OLLAMA_MODEL_NAME} 未找到!\n"
                    f" 请执行: ollama pull {OLLAMA_MODEL_NAME}" )
                return False
        else:
            logger.error(f" Ollama 返回异常状态码: {resp.status_code}")
            return False
    except requests.exceptions.ConnectionError:
        logger.error( f" FAIL无法连接Ollama 服务 ({OLLAMA_BASE_URL})\n"
            f" 请确认: 1) Ollama 已安装  2) 执行ollama serve 启动服务" )
        return False
    except Exception as e:
        logger.error(f"  FAIL Ollama 连接异常: {e}")
        return False
    # 测试3: 简单推理测试
    try:
        from llama_index.llms.ollama import Ollama
        test_llm = Ollama( model=OLLAMA_MODEL_NAME,
            base_url=OLLAMA_BASE_URL, request_timeout=60.0, )
        test_resp = test_llm.complete("你好,请用一句话介绍自己。")
        logger.info(f"  OK 推理测试成功: {str(test_resp)[:80]}...")
    except Exception as e:
        logger.error(f"  FAIL 推理测试失败: {e}")
        return False
    return True
def test_embedding_model():
    """
    测试嵌入模型是否可用。
    诊断步骤:
      1. 加载模型
      2. 计算测试文本的嵌入向量
      3. 验证向量维度和相似度计算
    """
    logger.info("=" * 60)
    logger.info("嵌入模型测试")
    logger.info("=" * 60)
    try:
        embed_model = HuggingFaceEmbedding( model_name=EMBEDDING_MODEL_PATH,
            device="cpu", max_length=128, normalize=True, trust_remote_code=True, )
        # 测试1: 基础嵌入
        vec = embed_model.get_text_embedding("今天天气真好")
        logger.info(f"  OK 嵌入计算成功, 向量维度: {len(vec)}")
        # 测试2: 中文语义相似度
        vec1 = embed_model.get_text_embedding("我喜欢看科幻电影")
        vec2 = embed_model.get_text_embedding("推荐一部科幻片")
        vec3 = embed_model.get_text_embedding("今天中午吃什么")
        import numpy as np
        sim_12 = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2) + 1e-8)
        sim_13 = np.dot(vec1, vec3) / (np.linalg.norm(vec1) * np.linalg.norm(vec3) + 1e-8)
        logger.info(f"  语义相似度测试:")
        logger.info(f"    '我喜欢看科幻电影' vs '推荐一部科幻片': {sim_12:.4f} (应较高)")
        logger.info(f"    '我喜欢看科幻电影' vs '今天中午吃什么': {sim_13:.4f} (应较低)")
        if sim_12 > sim_13:
            logger.info("  OK 中文语义区分度正常")
        else:
            logger.warning("  WARN 语义区分度异常,可能影响检索效果")
        return True
    except Exception as e:
        logger.error(f"  FAIL 嵌入模型测试失败: {e}")
        return False
# ==============================================================================
# [14] 主程序 — 完整运行流程
# ==============================================================================
def main():
    """
    完整运行流程:
      Step 1: 连接诊断
      Step 2: 配置 LLM + 嵌入模型 + 分割器
      Step 3: 加载/创建知识库文档
      Step 4: 按领域构建向量索引
      Step 5: 构建 Agentic RAG 查询引擎
      Step 6: 执行个性化推荐查询
    """
    print("\n" + "=" * 70)
    print("  Agentic RAG 个性化推荐系统")
    print("  LlamaIndex + Ollama(DeepSeek-R1-1.5B) + GTE中文嵌入")
    print("=" * 70 + "\n")
    # ──────────────────────────────────────────────────────
    # Step 1: 连接诊断
    # ──────────────────────────────────────────────────────
    print("Step 1: 环境诊断\n")
    ollama_ok = test_ollama_connection()
    embed_ok = test_embedding_model()
    if not ollama_ok:
        print("\nOllama 连接失败,请检查后重试。")
        print("   常见修复方法:")
        print("   1. 安装 Ollama: https://ollama.com/download")
        print("   2. 拉取模型: ollama pull deepseek-r1:1.5b")
        print("   3. 启动服务: ollama serve")
        return
    if not embed_ok:
        print("\n嵌入模型加载失败,请检查模型路径。")
        print(f"   当前路径: {EMBEDDING_MODEL_PATH}")
        print("   请确认该目录下包含 config.json, model.safetensors 等文件")
        return
    print("\n所有组件诊断通过\n")
    # ──────────────────────────────────────────────────────
    # Step 2: 配置全局模型
    # ──────────────────────────────────────────────────────
    print("Step 2: 配置模型\n")
    llm = setup_llm()
    embed_model = setup_embedding()
    node_parser = setup_node_parser()
    # ──────────────────────────────────────────────────────
    # Step 3: 创建知识库
    # ──────────────────────────────────────────────────────
    print("\nStep 3: 构建知识库\n")
    documents = create_sample_documents()
    print(f"  知识库包含 {len(documents)} 篇文档")
    # ──────────────────────────────────────────────────────
    # Step 4: 按领域构建向量索引
    # ──────────────────────────────────────────────────────
    print("\nStep 4: 构建向量索引\n")
    category_indexes = build_category_indexes(documents)
    # ──────────────────────────────────────────────────────
    # Step 5: 构建 Agentic RAG 引擎
    # ──────────────────────────────────────────────────────
    print("\nStep 5: 构建 Agentic RAG 查询引擎\n")
    workflow = build_agentic_query_engine(category_indexes)
    # ──────────────────────────────────────────────────────
    # Step 6: 个性化推荐查询测试
    # ──────────────────────────────────────────────────────
    print("\nStep 6: 个性化推荐测试\n")
    print("=" * 60)
    # 测试1: 电影推荐(带用户画像)
    print("\n--- 测试1: 带用户画像的电影推荐 ---")
    user_profile_1 = { "name": "小明", "age": 25, "interests": ["科幻", "烧脑"], "favorite_director": "诺兰", }
    query_1 = "推荐一部适合我的电影"
    print(f"用户画像: {json.dumps(user_profile_1, ensure_ascii=False)}")
    print(f"查询: {query_1}")
    print("Agent 推理过程:")
    response_1 = personalized_recommend(workflow, query_1, user_profile_1)
    print(f"\n推荐结果:\n{response_1}")
    # 测试2: 书籍推荐
    print("\n\n--- 测试2: 书籍推荐 ---")
    query_2 = "推荐一本好看的科幻小说"
    print(f"查询: {query_2}")
    print("Agent 推理过程:")
    response_2 = personalized_recommend(workflow, query_2)
    print(f"\n推荐结果:\n{response_2}")
    # 测试3: 美食推荐(带饮食偏好)
    print("\n\n--- 测试3: 带饮食偏好的美食推荐 ---")
    user_profile_3 = { "name": "小红", "taste": "喜欢精致料理,不太能吃辣", "occasion": "周末朋友聚餐", }
    query_3 = "推荐一个适合聚餐的美食"
    print(f"用户画像: {json.dumps(user_profile_3, ensure_ascii=False)}")
    print(f"查询: {query_3}")
    print("Agent 推理过程:")
    response_3 = personalized_recommend(workflow, query_3, user_profile_3)
    print(f"\n推荐结果:\n{response_3}")
    # 测试4: 跨领域查询(测试 Agent 的工具选择能力)
    print("\n\n--- 测试4: 跨领域模糊查询 ---")
    query_4 = "周末放松有什么好推荐的?"
    print(f"查询: {query_4}")
    print("Agent 推理过程: (测试Agent是否能选择多个领域工具)")
    response_4 = personalized_recommend(workflow, query_4)
    print(f"\n推荐结果:\n{response_4}")
    # ──────────────────────────────────────────────────────
    # 交互式查询模式
    # ──────────────────────────────────────────────────────
    print("\n\n" + "=" * 60)
    print("  进入交互式推荐模式 (输入 'quit' 退出)")
    print("=" * 60)
    while True:
        try:
            user_input = input("\n请输入推荐请求: ").strip()
            if user_input.lower() in ("quit", "exit", "q", "退出"):
                print("再见!")
                break
            if not user_input:
                continue
            # 可选: 输入用户画像
            profile_input = input("输入用户画像 (JSON格式,直接回车跳过): ").strip()
            profile = None
            if profile_input:
                try:
                    profile = json.loads(profile_input)
                except json.JSONDecodeError:
                    print("  (JSON解析失败,使用空画像)")
            print("\nAgent 推理过程:")
            response = personalized_recommend(workflow, user_input, profile)
            print(f"\n推荐结果:\n{response}")
        except KeyboardInterrupt:
            print("\n再见!")
            break
        except Exception as e:
            logger.error(f"查询出错: {e}")
            print(f"出错: {e}")
# ==============================================================================
# [15] 入口
# ==============================================================================
if __name__ == "__main__":
    main()

3.2 过程展现

3.2.1 命令行操作

3.2.2 IDE操作

4 部署试运行

4.1 基本操作

1 安装python运行环境

2 解压缩软件运行包

3 Ollama安装与模型部署

ollama pull deepseek-r1:8b

ollama serve

4 嵌入模型准备

pip install modelscope

modelscope download --model iic/nlp_gte_sentence-embedding_chinese-base

4.2 试运行与测试

5 丰富完善增强

7.1 方案与架构

龙虾机器人MuleRun:给出CPU和Windows下采用LlamaIndex框架、基于Agentic RAG架构的简单个性化推荐的编码,整个程序用一个文件实现,注意各个依赖库的版本对应和中文的检索加强,不使用CONDA,LLM模型采用Ollama部署本地的DeepSeek-R1-8B,需要的嵌入模型己通过modelscope下载在本地D:\01ifmts\models\nlp_gte_sentence-embedding_chinese-base,详细说明各个实现过程和测调试运行。注意编码精简与优化,增强预装案例外的个性化推荐适应能力。

7.2 编码运行

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐