root 5e762da740
All checks were successful
构建并部署 AI Agent 服务 / deploy (push) Successful in 5m49s
修复导入路径:从.nodes._utils导入
2026-05-06 19:33:59 +08:00
2026-05-05 23:17:00 +08:00
2026-05-06 04:26:06 +08:00
2026-04-12 01:42:34 +08:00
2026-05-06 04:26:06 +08:00

AI Agent - 智能助手系统

一个基于 LangGraph + FastAPI 的智能对话助手,支持多模型切换、联网搜索、可视化图表、以及多个专业子图模块(通讯录、词典、资讯分析)等功能。


📑 目录导航


🎯 核心功能

面向用户的功能

  • 💬 智能对话:支持多轮对话,自动记忆上下文
  • 🌐 联网搜索:免费使用 DuckDuckGo 搜索,无需 API Key支持引用溯源
  • 📊 可视化图表:支持 Mermaid 图表和 matplotlib 图表生成
  • 🔄 多模型切换:前端可选择不同大语言模型
  • 📇 通讯录管理:子图模块,联系人 CRUD、邮件处理
  • 📖 智能词典:子图模块,翻译、生词本、专业术语提取
  • 📰 资讯分析:子图模块,资讯获取、内容分析、格式化展示

技术特性

  • 持久化记忆PostgreSQL 存储对话历史,重启不丢失
  • 高可用架构:模型服务自动降级,确保服务稳定
  • 前后端分离FastAPI 后端 + Streamlit 前端
  • Docker 部署:一键启动所有服务
  • 远程服务架构PostgreSQL 和 Qdrant 部署在远程服务器
  • 流式响应SSE 流式输出,提升用户体验
  • 模块化设计:清晰的代码分层,易于扩展和维护
  • 模型服务层:统一的 Embedding、Rerank、Chat 服务接口,支持自动降级
  • 子图系统:模块化的子图架构,共享公共工具(意图理解、人工审核、格式化输出)
  • 公共工具库:联网搜索、可视化图表等通用工具,所有子图和主图均可使用
  • React 模式 Reasoning → Acting → Observing 循环LLM 先思考再行动,支持多次工具调用
  • 混合路由架构 :前置快速路由(规则分流 + 轻量级意图分类)+ 完整 React 循环(兜底)
  • 双模型服务 get_chat_service()(大模型)+ get_small_llm_service()(轻量级模型)
  • 自动升级机制:快速路径失败时,自动回到完整 React 循环
  • 向后兼容:可通过 use_hybrid_router=True/False 切换混合路由/纯 React 模式


📖 使用指南

基础对话

直接在聊天框输入问题即可:

你好,请介绍一下自己
帮我写一个 Python 脚本

主要功能

功能 说明 示例提问
🧠 混合路由智能分流 自动判断任务类型,选择最佳路径 自然对话即可
快速路径 闲聊、RAG查询、工具调用可走快速路径 "你好"、"什么是 RAG"
🔄 React 推理循环 复杂任务走完整的思考-行动-观察循环 "帮我分析一下这个文档"
🌐 联网搜索 免费 DuckDuckGo 搜索 "今天北京天气怎么样?"
📚 RAG 知识库检索 检索本地知识库 "如何配置系统?"
📇 通讯录管理 联系人 CRUD、邮件处理 "帮我查看一下张三的联系方式"
📖 智能词典 翻译、生词本、专业术语提取 "帮我翻译这句话"
📰 资讯分析 资讯获取、内容分析 "帮我分析一下这篇新闻"
📊 可视化图表 支持 Mermaid 图表生成 "帮我画一个流程图"

多模型切换

  1. 在左侧边栏选择模型:

    • 智谱 GLM-4:在线服务,速度快
    • DeepSeek V3:深度推理模型
    • OpenAI GPT-4o-mini:通用对话模型
    • 本地 Qwen3.5-9B:本地部署,隐私性好
  2. 可随时切换,甚至在同一会话中

  3. 点击 "🔄 新会话" 清空当前对话


🏗️ 技术架构

1. 技术栈总览

层级 组件 技术选型 说明
Agent 框架 工作流编排 LangGraph + LangChain 状态机驱动的智能体工作流
后端框架 API 服务 FastAPI + Uvicorn RESTful API + SSE 流式输出
前端框架 Web 界面 Streamlit 交互式对话界面
关系数据库 持久化存储 PostgreSQL 对话记忆持久化(远程服务器)
向量数据库 向量检索 Qdrant 高性能向量相似度检索(远程服务器)
容器化 服务编排 Docker + Docker Compose 一键部署所有服务
CI/CD 自动化部署 Gitea Workflows 代码推送自动构建部署
LLM 服务 云端模型 智谱 AI (glm-4-plus) 快速响应,适合日常对话
DeepSeek (deepseek-chat-v3) 深度推理,适合复杂问题
OpenAI (gpt-4o-mini) 通用对话
本地模型 Qwen3.5-9B.Q4_K_M 本地部署 GGUF 格式
模型服务层 Chat 服务 chat_services.py 统一的生成式大模型接口
Embedding 服务 embedding_services.py 统一的嵌入模型接口
Rerank 服务 rerank_services.py 统一的重排序接口
Embedding 向量嵌入 llama.cpp server 本地 embedding 服务 (:18001)

2. 系统全景图

展示系统各组件之间的高层交互关系,隐藏执行细节。

graph TB
    subgraph UserLayer["👤 用户层"]
        Browser["浏览器"]
        Streamlit["Streamlit 前端<br/>:8501"]
    end

    subgraph AppLayer["⚙️ 应用服务层"]
        FastAPI["FastAPI 后端<br/>:8079"]
        Agent["AIAgentService<br/>(智能体协调)"]
    end

    subgraph EngineLayer["🧠 智能体引擎"]
        LangGraph["LangGraph 工作流引擎<br/>路由 / React / 工具调用"]
    end

    subgraph ServicesLayer["🧩 领域服务"]
        RAG["RAG 检索服务"]
        Tools["工具集<br/>(搜索、通讯录、词典等)"]
    end

    subgraph ModelLayer["🤖 模型层"]
        LLM["LLM 服务<br/>(多模型降级链)"]
        Embedding["Embedding 服务<br/>:18001"]
        Rerank["Rerank 服务<br/>:18002"]
    end

    subgraph DataLayer["💾 数据层"]
        Qdrant["Qdrant 向量库"]
        PostgreSQL["PostgreSQL"]
    end

    Browser --> Streamlit
    Streamlit --> FastAPI
    FastAPI --> Agent
    Agent --> LangGraph
    LangGraph --> RAG
    LangGraph --> Tools
    LangGraph --> LLM
    RAG --> Embedding
    RAG --> Rerank
    RAG --> Qdrant
    Agent --> PostgreSQL
    LangGraph --> PostgreSQL

    style UserLayer fill:#e1f5ff
    style AppLayer fill:#fff4e1
    style EngineLayer fill:#ffe0b2
    style ServicesLayer fill:#e8f5e9
    style ModelLayer fill:#f3e5f5
    style DataLayer fill:#ffebee

3. 智能体引擎(主图)

3.1 核心流程

graph TB
    Start([开始]) --> Retrieve["检索长期记忆"]
    Retrieve --> Trigger["记忆触发"]
    Trigger --> Init["初始化状态"]
    Init --> Route{"混合路由<br/>hybrid_router"}
    
    Route -->|闲聊/简单问答| Fast["⚡ 快速路径<br/>fast_*"]
    Route -->|知识检索| FastRAG["⚡ 快速 RAG"]
    Route -->|工具调用| FastTool["⚡ 快速工具"]
    Route -->|复杂任务| React["🔄 React 循环"]
    
    React --> Reason["推理"]
    Reason --> Action["选择行动"]
    Action -->|需要检索| RAGNode["RAG 检索"]
    Action -->|搜索| Web["联网搜索"]
    Action -->|通讯录| Contact["通讯录子图"]
    Action -->|词典| Dict["词典子图"]
    Action -->|资讯| News["资讯子图"]
    Action -->|LLM| LLMCall["LLM 生成"]
    
    RAGNode --> Observe["观察结果"]
    Web --> Observe
    Contact --> Observe
    Dict --> Observe
    News --> Observe
    LLMCall --> Observe
    Observe -->|未完成| Reason
    Observe -->|完成| Final["生成最终回复"]
    
    Fast --> Final
    FastRAG --> Final
    FastTool --> Final
    Final --> End([结束])

    style Route fill:#fff3e0,stroke:#ff9800,stroke-width:3px
    style React fill:#f3e5f5,stroke:#7e57c2,stroke-width:2px

3.2 路由策略

  • 闲聊 / 简单问答fast_chitchat:直接调用 LLM 生成回复
  • 知识检索fast_ragRAG 检索后生成回复
  • 工具调用fast_tool:直接执行原子工具操作
  • 复杂任务React 循环:推理 → 行动 → 观察的多轮迭代

3.3 React 循环详解

当任务需要多步推理时,引擎进入 React 循环:

  1. 推理LLM 分析当前状态,决定下一步行动
  2. 行动执行工具调用RAG 检索、联网搜索、子图操作等)
  3. 观察:收集行动结果,更新状态
  4. 若任务未完成,回到步骤 1否则退出循环生成最终回复

4. 子图系统

每个子图都是独立的模块化工作流,由主图的 React 循环按需调用。

4.1 通讯录子图

支持联系人管理、邮件草稿生成与人工审核发送。

graph LR
    Start([START]) --> Intent["parse_intent"]
    Intent --> List["list_contacts"]
    Intent --> Add["add_contact"]
    Intent --> EmailList["list_emails"]
    Intent --> GenEmail["generate_email_draft"]
    Intent --> Sniff["sniff_contacts"]
    GenEmail --> Human["human_review"]
    Human --> Send["send_email"]
    Send --> Format["format_result"]
    List --> Format
    Add --> Format
    EmailList --> Format
    Sniff --> Format
    Format --> End([END])

4.2 词典子图

支持单词查询、翻译、术语提取、每日单词、单词本管理。

graph LR
    Start([START]) --> Intent["parse_intent"]
    Intent --> Query["query_word"]
    Intent --> Trans["translate_text"]
    Intent --> Terms["extract_terms"]
    Intent --> Daily["get_daily_word"]
    Intent --> Lookup["lookup_word_book"]
    Intent --> AddWord["add_to_word_book"]
    Query --> Format["format_result"]
    Trans --> Format
    Terms --> Format
    Daily --> Format
    Lookup --> Format
    AddWord --> Format
    Format --> End([END])

4.3 资讯分析子图

支持新闻检索、URL 分析、关键词提取与报告生成。

graph LR
    Start([START]) --> Intent["parse_intent"]
    Intent --> QueryNews["query_news"]
    Intent --> AnalyzeUrl["analyze_url"]
    Intent --> Keywords["extract_keywords"]
    Intent --> Report["generate_report"]
    QueryNews --> Format["format_result"]
    AnalyzeUrl --> Format
    Keywords --> Format
    Report --> Format
    Format --> End([END])

5. RAG 系统

5.1 离线索引

文档导入、切分、嵌入生成到存入向量库的完整流程。

flowchart TB
    subgraph Input["文档输入"]
        DocSource["文档源<br/>PDF/DOCX/TXT/Markdown"]
    end

    subgraph Load["文档加载"]
        Loader["Unstructured / PyMuPDF / TextLoader"]
    end

    subgraph Split["文本切分"]
        Recursive["RecursiveCharacterTextSplitter<br/>按分隔符递归切分"]
        Semantic["SemanticChunker<br/>基于语义相似度"]
        ParentChild["ParentChildSplitter<br/>父子块切分"]
    end

    subgraph Embed["嵌入生成"]
        Dense["稠密向量<br/>Qwen3-Embedding-0.6B"]
        Sparse["稀疏向量 BM25<br/>FastEmbed"]
    end

    subgraph Store["向量存储"]
        QdrantStore["Qdrant<br/>HNSW 索引 + 稀疏索引"]
    end

    DocSource --> Loader
    Loader --> Recursive
    Loader --> Semantic
    Loader --> ParentChild
    Recursive --> Dense
    Semantic --> Dense
    ParentChild --> Dense
    Recursive --> Sparse
    Semantic --> Sparse
    ParentChild --> Sparse
    Dense --> QdrantStore
    Sparse --> QdrantStore

    style Input fill:#e3f2fd
    style Load fill:#fff3e0
    style Split fill:#f3e5f5
    style Embed fill:#e8f5e9
    style Store fill:#ffebee

技术组件说明:

组件 技术选型 说明
文档加载 Unstructured / PyMuPDF / TextLoader 支持多种文档格式
文本切分 RecursiveCharacterTextSplitter 默认 500 字符,按分隔符递归切分
语义切分 SemanticChunker 基于 Embedding 相似度自动切分
父子切分 ParentChildSplitter 大块存储上下文,小块用于检索
稠密嵌入 Qwen3-Embedding-0.6B-Q8_0 llama.cpp server (:18001)
稀疏嵌入 FastEmbed BM25 本地计算,无需额外服务
向量存储 Qdrant HNSW 索引,高性能 ANN 检索

5.2 在线检索

用户查询经过改写、混合检索、融合、重排序,最终由 LLM 生成回答。

flowchart TB
    subgraph Input["查询输入"]
        Query["用户查询"]
    end

    subgraph Processing["查询处理"]
        Rewrite["查询改写<br/>LLM 生成多角度查询)"]
    end

    subgraph Retrieval["混合检索"]
        Parallel["并行检索"]
        DenseRet["稠密向量检索"]
        SparseRet["稀疏 BM25 检索"]
    end

    subgraph Fusion["结果融合"]
        RRF["RRF 融合<br/>Qdrant 服务端融合)"]
    end

    subgraph Rerank["重排序"]
        CrossEncoder["Cross-Encoder 重排<br/>bge-reranker-v2-m3"]
    end

    subgraph Generation["生成"]
        LLMGen["LLM 生成回答"]
    end

    Query --> Rewrite --> Parallel
    Parallel --> DenseRet
    Parallel --> SparseRet
    DenseRet --> RRF
    SparseRet --> RRF
    RRF --> CrossEncoder --> LLMGen

    style Input fill:#e3f2fd
    style Processing fill:#fff3e0
    style Retrieval fill:#f3e5f5
    style Fusion fill:#e8f5e9
    style Rerank fill:#ffebee
    style Generation fill:#fff3e0

技术组件说明:

阶段 技术选型 说明
查询改写 MultiQuery LLM 生成 3~5 个多角度查询
稠密检索 Qwen3-Embedding 余弦相似度向量检索
稀疏检索 FastEmbed BM25 TF-IDF 词频统计检索
结果融合 Qdrant Fusion API 服务端 RRF 融合,减少数据传输
重排序 bge-reranker-v2-m3 Cross-Encoder 交互编码,精度更高
LLM 生成 chat_services 统一的大模型服务接口


5.5. 模型服务使用情况详解

5.5.1. 模型服务架构总览

本项目采用分层模型策略,根据任务复杂度选择不同能力和成本的模型:

模型类型 用途 主要来源 成本考量
小模型 (Small LLM) 意图分类、路由决策、查询改写 本地模型 / DeepSeek小模型 低成本,高频率
大模型 (Main LLM) 对话生成、推理、工具调用 智谱 / DeepSeek / 本地 高能力,低频率
Embedding模型 文本向量化、语义检索 本地 llama.cpp / 智谱API 批量处理
Rerank模型 检索结果重排序 硅基流动 / 智谱API 精准排序
Sparse模型 BM25稀疏检索 FastEmbed本地 关键词匹配

5.5.2. 小模型使用场景及Token估算

小模型主要用于高频率、低复杂度的任务:

使用场景 位置文件 用途描述 单次Token估算 调用频率
意图分类 (1) app/core/intent_classifier.py 判断用户意图类型 ~300输入 + ~50输出 每轮对话1次
意图分类 (2) app/main_graph/nodes/hybrid_router.py 混合路由决策 ~200输入 + ~50输出 每轮对话1次
闲聊回复 app/main_graph/nodes/fast_paths.py 快速回复问候语 ~50输入 + ~30输出 按需调用

Token估算说明

  • 单次意图分类:总计 ~350-600 tokens
  • 小模型成本通常是大模型的 1/10 - 1/100
  • 每日1000次对话小模型仅消耗 ~350k-600k tokens

5.5.3. 大模型使用场景及Token估算

大模型用于核心对话生成和复杂推理:

使用场景 位置文件 用途描述 单次Token估算 调用频率
RAG查询改写 app/main_graph/utils/rag_initializer.py 生成多角度查询 ~100输入 + ~150输出 RAG调用时
主对话生成 app/main_graph/nodes/llm_call.py 用户查询响应 ~500-2000输入 + ~200-1000输出 每轮对话1次
React推理 app/main_graph/nodes/reasoning.py 任务分解与规划 ~300-1000输入 + ~100-500输出 复杂任务多次
记忆摘要 app/memory/mem0_client.py 长期记忆压缩 ~500-2000输入 + ~200-500输出 每N轮对话1次

Token估算说明

  • 普通对话:总计 ~1000-3000 tokens
  • RAG查询额外 ~250 tokens
  • 复杂多步推理:可能额外增加 500-3000 tokens
  • 每日1000次对话大模型预计消耗 1M-3M tokens

5.5.4. Embedding模型使用场景

Embedding模型用于语义检索和向量存储:

使用场景 位置文件 用途描述 估算
RAG文档索引 rag_indexer/index_builder.py 文档分片向量化 每个文档片段1次
在线检索 app/rag/retriever.py 查询向量化 + 相似度检索 每次检索1次
记忆向量化 app/memory/mem0_client.py 记忆内容向量化存储 每次记忆更新1次

Embedding说明

  • 向量维度1024 (Qwen3-Embedding-0.6B) 或 2048 (智谱 embedding-3)
  • 批量处理:建议使用 batch_size=10-20 提高效率
  • 本地优先:优先使用 llama.cpp 服务降低API调用成本

5.5.5. Rerank模型使用场景

Rerank模型用于检索结果精细化排序:

使用场景 位置文件 用途描述 估算
RAG结果重排 app/rag/rerank.py 提升检索相关性 每次检索调用
混合检索重排 app/rag/retriever.py 稀疏+稠密结果融合排序 每次检索调用

Rerank说明

  • 通常在 RRF 融合后使用,进一步提升精准度
  • 重排数量建议rerank_top_n=3-10
  • 成本权衡rerank 会增加额外调用成本,但精度提升明显

5.5.6. 模型服务选型参考对比

为方便不同部署场景选择,提供以下模型选型参考:

维度 本地优先方案 云端优先方案 混合方案
小模型 Qwen3.5-9B (本地) DeepSeek-Chat (API) 本地+DeepSeek降级
大模型 Qwen3.5-9B (本地) 智谱 GLM-4 / DeepSeek 本地+云端降级链
Embedding Qwen3-Embedding-0.6B (本地llama.cpp) 智谱 embedding-2 本地优先,智谱降级
Rerank (可选本地) 硅基流动 bge-reranker-v2-m3 硅基流动API
Sparse FastEmbed BM25 (本地) FastEmbed BM25 (本地) 本地

成本参考对比每1M tokens仅作示例

模型 输入成本 输出成本 适用场景
本地模型 ~0元 ~0元 有GPU机器隐私敏感
DeepSeek-Chat ~¥0.5 ~¥1.0 通用推理,成本适中
智谱 GLM-4 ~¥1.0 ~¥2.0 高质量对话
智谱 embedding-2 ~¥0.2 - 向量嵌入
硅基流动 Rerank ~¥0.3 - 精准重排

部署建议

  • 个人/测试:全云端方案,快速上手
  • 小团队:小模型本地,大模型云端降级
  • 企业/隐私敏感全本地部署或使用私有API
  • 生产环境:核心能力本地+云端降级链,保证高可用

5.5.7. 模型服务降级链路设计

本项目所有模型服务都设计了自动降级链路,保证服务高可用:

服务类型 主服务 降级服务
对话生成 本地模型 → 智谱GLM-4 → DeepSeek
Embedding 本地llama.cpp → 智谱embedding-2
Rerank 硅基流动 → 智谱rerank-2

降级逻辑实现在 app/model_services/base.py: FallbackServiceChain,对上层业务透明。


6. 模型服务层

6.1 多模型降级链

当首选模型调用失败时,自动依次尝试备用模型,保证服务可用性。

graph LR
    Start([API 请求]) --> Zhipu["智谱 GLM-4"]
    Zhipu -->|失败| DeepSeek["DeepSeek V3"]
    DeepSeek -->|失败| OpenAI["OpenAI GPT-4o"]
    OpenAI -->|失败| Local["本地 Qwen3.5-9B"]
    Local --> Response([返回响应])
    style Start fill:#e1f5ff
    style Response fill:#c8e6c9

降级策略: 云端模型按优先级依次尝试,最终回退到本地模型,确保无单点故障。

6.2 统一服务接口

所有模型调用均通过以下三个统一接口访问,上层业务不感知具体模型:

  • chat_services:对话生成
  • embedding_services:文本向量化
  • rerank_services:搜索结果重排序

7. 数据存储

存储 用途 访问方式
PostgreSQL 对话历史、长期记忆 远程服务器SQLAlchemy ORM
Qdrant 文档向量、知识库 远程服务器gRPC/HTTP API

数据流向图

用户请求
   │
   ├─→ 前端 Streamlit
   │      │
   │      ├─→ 发送 HTTP POST /api/chat
   │      │
   │      └─→ 接收 SSE 流式响应
   │
   └─→ 后端 FastAPI
          │
          ├─→ AIAgentService.achat()
          │      │
          │      ├─→ 初始化 LangGraph 状态
          │      │      ├─→ messages: 对话历史
          │      │      ├─→ user_id: 用户标识
          │      │      └─→ metadata: 元数据
          │      │
          │      └─→ 执行工作流(混合路由 + React 循环)
          │             ├─→ retrieve_memory: 从 PostgreSQL 检索历史
          │             ├─→ memory_trigger: 判断是否触发记忆
          │             ├─→ init_state: 初始化状态
          │             ├─→ hybrid_router: 混合路由决策
          │             │      ├─→ fast_chitchat: 闲聊快速路径
          │             │      ├─→ fast_rag: RAG 快速路径
          │             │      ├─→ fast_tool: 工具快速路径
          │             │      └─→ react_loop: React 循环(兜底)
          │             │
          │             ├─→ React 循环react_reason 节点)
          │             │      ├─→ rag_retrieve: RAG 检索
          │             │      ├─→ web_search: 联网搜索
          │             │      ├─→ contact_subgraph: 通讯录子图
          │             │      ├─→ dictionary_subgraph: 词典子图
          │             │      ├─→ news_analysis_subgraph: 资讯子图
          │             │      └─→ llm_call: LLM 调用
          │             │
          │             ├─→ summarize: 生成对话摘要(如需要)
          │             └─→ finalize: 格式化最终响应
          │
          └─→ 持久化存储
                 ├─→ PostgreSQL: 对话历史和摘要
                 └─→ Qdrant: RAG 向量索引

项目结构

Agent1/
├── backend/
│   ├── app/
│   │   ├── __init__.py
│   │   ├── config.py              # 配置管理
│   │   ├── logger.py              # 日志工具
│   │   ├── backend.py             # FastAPI 后端应用(含子图 API
│   │   ├── README.md              # 后端目录文档
│   │   │
│   │   ├── core/                  # ⭐ 核心模块 - 基类和通用工具
│   │   │   ├── __init__.py
│   │   │   ├── state_base.py      # 子图状态基类
│   │   │   ├── intent.py          # 意图理解React 模式)
│   │   │   ├── intent_classifier.py # 意图分类器
│   │   │   ├── formatter.py       # 格式化输出工具
│   │   │   ├── human_review.py    # 人工审核节点
│   │   │   ├── web_search.py      # ⭐ 联网搜索工具DuckDuckGo
│   │   │   └── visualization.py   # ⭐ 可视化图表工具Mermaid + matplotlib
│   │   │
│   │   ├── agent/                 # ⭐ Agent 服务层
│   │   │   ├── __init__.py
│   │   │   ├── agent_service.py  # Agent 服务核心(使用 chat_services
│   │   │   ├── history.py         # 历史查询服务
│   │   │   └── prompts.py         # 提示词模板
│   │   │
│   │   ├── main_graph/            # ⭐ 主图 - LangGraph 主流程(混合路由 + React 循环)
│   │   │   ├── __init__.py
│   │   │   ├── state.py           # 主图状态定义 MainGraphState
│   │   │   ├── graph.py           # LangGraph 组件导出
│   │   │   ├── config.py          # 主图配置
│   │   │   ├── nodes/             # 主图节点
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _utils.py     # 节点公共工具
│   │   │   │   ├── reasoning.py   # React 推理节点
│   │   │   │   ├── hybrid_router.py # 混合路由节点
│   │   │   │   ├── fast_paths.py # 快速路径节点
│   │   │   │   ├── llm_call.py   # LLM 调用节点
│   │   │   │   ├── routing.py    # 路由决策init_state, route_by_reasoning
│   │   │   │   ├── rag_nodes.py  # RAG 检索节点
│   │   │   │   ├── web_search.py # 联网搜索节点
│   │   │   │   ├── retrieve_memory.py # 记忆检索节点
│   │   │   │   ├── memory_trigger.py # 记忆触发节点
│   │   │   │   ├── summarize.py   # 记忆摘要节点
│   │   │   │   ├── finalize.py   # 最终处理节点
│   │   │   │   ├── tool_call.py  # 工具执行节点
│   │   │   │   └── error_handling.py # 错误处理节点
│   │   │   ├── tools/             # 主图工具
│   │   │   │   ├── __init__.py
│   │   │   │   ├── common_tools.py # 通用工具
│   │   │   │   ├── subgraph_tools.py # 子图调用工具
│   │   │   │   └── graph_tools.py # 图工具
│   │   │   └── utils/             # 主图工具函数
│   │   │       ├── __init__.py
│   │   │       ├── main_graph_builder.py # 主图构建器
│   │   │       ├── rag_initializer.py # RAG 初始化
│   │   │       └── retry_utils.py # 重试工具
│   │   │
│   │   ├── subgraphs/             # ⭐ 子图模块
│   │   │   ├── __init__.py
│   │   │   ├── contact/           # 通讯录子图
│   │   │   │   ├── state.py       # 状态定义
│   │   │   │   ├── nodes.py       # 节点实现
│   │   │   │   ├── graph.py       # 图构建
│   │   │   │   ├── api_client.py  # API 客户端
│   │   │   │   └── __init__.py
│   │   │   ├── dictionary/        # 词典子图
│   │   │   │   ├── state.py       # 状态定义
│   │   │   │   ├── nodes.py       # 节点实现
│   │   │   │   ├── graph.py       # 图构建
│   │   │   │   ├── api_client.py  # API 客户端
│   │   │   │   └── __init__.py
│   │   │   └── news_analysis/     # 资讯分析子图
│   │   │       ├── state.py       # 状态定义
│   │   │       ├── nodes.py       # 节点实现
│   │   │       ├── graph.py       # 图构建
│   │   │       ├── api_client.py  # API 客户端
│   │   │       └── __init__.py
│   │   │
│   │   ├── model_services/        # 模型服务层
│   │   │   ├── __init__.py
│   │   │   ├── base.py            # 基类BaseServiceProvider, FallbackServiceChain, SingletonServiceManager
│   │   │   ├── chat_services.py   # 生成式大模型服务
│   │   │   ├── embedding_services.py # 嵌入模型服务
│   │   │   └── rerank_services.py # 重排序服务
│   │   │
│   │   ├── mcp/                   # MCP 模块
│   │   │   ├── __init__.py
│   │   │   ├── mcp_manager.py    # MCP 管理器
│   │   │   ├── mcp_client.py     # MCP 客户端
│   │   │   ├── adapters/         # MCP 适配器
│   │   │   │   ├── __init__.py
│   │   │   │   ├── base_adapter.py
│   │   │   │   ├── contact_adapter.py
│   │   │   │   ├── dictionary_adapter.py
│   │   │   │   └── news_adapter.py
│   │   │   └── mcp_example.py
│   │   │
│   │   ├── memory/                # 记忆模块
│   │   │   ├── __init__.py
│   │   │   └── mem0_client.py     # Mem0 客户端封装
│   │   │
│   │   ├── rag/                   # RAG 模块
│   │   │   ├── __init__.py
│   │   │   ├── retriever.py       # 检索器
│   │   │   ├── rerank.py          # 重排序业务逻辑(使用 rerank_services
│   │   │   ├── query_transform.py # 查询转换
│   │   │   ├── pipeline.py        # RAG 流水线
│   │   │   ├── fusion.py          # RAG-Fusion
│   │   │   ├── tools.py           # RAG 工具
│   │   │   └── evaluate.py        # RAG 评估
│   │   │
│   │   ├── db/                    # 数据库模块
│   │   │   ├── __init__.py
│   │   │   ├── base.py            # 数据库基类
│   │   │   ├── models.py          # 数据模型
│   │   │   └── init_db.py         # 数据库初始化
│   │   │
│   │   └── utils/                 # 工具模块
│   │       ├── __init__.py
│   │       └── logging.py         # 日志工具
│   └── rag_core/                # ⭐ RAG 核心库(统一组件)
│       ├── __init__.py
│       ├── config.py            # RAG 配置
│       ├── client.py            # RAG 核心客户端
│       ├── embedders.py        # 嵌入模型
│       ├── sparse_embedder.py  # BM25 稀疏嵌入
│       ├── vector_store.py     # 向量存储Dense + Sparse
│       └── doc_store.py        # 文档存储
├── frontend/
│   ├── run.py                   # 前端启动脚本
│   ├── requirements.txt
│   └── src/
│       ├── __init__.py
│       ├── frontend_main.py       # Streamlit 主入口
│       ├── api_client.py          # API 客户端(含子图调用)
│       ├── config.py              # 前端配置
│       ├── state.py               # 状态管理
│       ├── logger.py              # 日志
│       ├── utils.py               # 工具函数
│       └── components/
│           ├── __init__.py
│           ├── sidebar.py         # 侧边栏组件
│           ├── chat_area.py       # 聊天区域组件
│           └── info_panel.py      # 信息面板组件
├── rag_indexer/
│   ├── __init__.py
│   ├── cli.py                 # 命令行入口
│   ├── index_builder.py       # 索引构建器
│   ├── loaders.py             # 文档加载器
│   ├── splitters.py           # 文本切分器
│   └── test/                  # 测试脚本
├── docker/
│   ├── docker-compose.yml     # Docker 服务编排
│   ├── backend/
│   │   └── Dockerfile         # 后端镜像构建
│   ├── frontend/
│   │   └── Dockerfile         # 前端镜像构建
│   └── models/                # spaCy 模型文件
├── scripts/
│   └── start.sh               # 启动脚本
├── test/                      # 测试目录
│   ├── test_backend.py
│   ├── test_rag.py
│   ├── test_qdrant.py
│   └── test_rag_indexer_result.py
├── .gitea/
│   └── workflows/
│       └── deploy.yml         # CI/CD 自动化部署
├── requirement.txt            # Python 依赖
├── .env.docker                # Docker 环境变量模板
├── .gitignore
├── LICENSE
├── QUICKSTART.md              # 快速开始指南
└── user_docs/                 # 用户文档目录

🧠 核心算法与实现原理

1. RAG 检索算法

项目采用稠密 + 稀疏混合检索架构,结合 RRF 融合和 Cross-Encoder 重排序,实现高精度知识库问答。

核心特性:

  • 三种文本切分策略:递归字符切分、语义切分、父子块切分
  • 稠密向量检索Embedding+ 稀疏 BM25 检索
  • RRF 融合算法实现多检索源结果合并
  • Cross-Encoder 重排序提升相关性

详细文档:


LangGraph 工作流详细流程

1. 混合路由 + React 循环架构

设计理念混合路由Hybrid Router作为前置决策快速路径处理简单任务React 循环作为复杂任务的兜底方案。

┌─────────────────────────────────────────────────────────────────────────────┐
│                        主图执行流程                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段1: 记忆检索 (retrieve_memory)                                      │    │
│  │     - 从 PostgreSQL 检索用户历史对话                                    │    │
│  │     - 生成 memory_context 供后续使用                                   │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段2: 记忆触发 (memory_trigger)                                     │    │
│  │     - 判断是否需要激活记忆上下文                                       │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段3: 初始化状态 (init_state)                                        │    │
│  │     - 初始化 MainGraphState                                           │    │
│  │     - 设置 user_query、messages 等                                    │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段4: 混合路由 (hybrid_router) ⭐                                     │    │
│  │     - 规则分流:闲聊关键词、子图关键词(<5ms                           │    │
│  │     - LLM 分类使用轻量级模型进行意图分类chitchat/knowledge/tool    │    │
│  │     - 输出决策fast_chitchat / fast_rag / fast_tool / react_loop      │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│        ┌──────────────────────┼──────────────────────┐                    │
│        ↓                      ↓                      ↓                      │
│  ┌──────────┐          ┌──────────┐          ┌──────────┐                    │
│  │快速路径  │          │快速路径  │          │快速路径  │                    │
│  │fast_*    │          │fast_rag  │          │fast_tool│                    │
│  └────┬─────┘          └────┬─────┘          └────┬─────┘                    │
│       │                     │                     │                          │
│       │    ┌────────────────┘     ┌──────────────┘                          │
│       │    │                      │                                         │
│       ↓    ↓                      ↓                                         │
│  ┌─────────────────────────────────────────────────────────────┐             │
│  │  阶段5: React 循环 (react_reason)                            │             │
│  │     - 调用 app/core/intent.py 的 react_reason_async()        │             │
│  │     - 使用 app/model_services/ 获取 chat 服务                │             │
│  │     - 推理下一步动作rag_retrieve/web_search/子图/llm_call │             │
│  └─────────────────────────────────────────────────────────────┘             │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  React 循环内节点                                                     │    │
│  │     rag_retrieve → react_reason回到推理                            │    │
│  │     web_search → react_reason回到推理                              │    │
│  │     contact_subgraph → react_reason回到推理                        │    │
│  │     dictionary_subgraph → react_reason回到推理                      │    │
│  │     news_analysis_subgraph → react_reason回到推理                   │    │
│  │     handle_error → react_reason错误处理后继续推理                    │    │
│  │     llm_call → 退出循环,进入完成阶段                                  │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段6: LLM 调用 (llm_call)                                           │    │
│  │     - 调用主 LLM 生成最终回答                                         │    │
│  │     - 使用 llm.bind_tools(tools) 绑定工具                             │    │
│  │     - 支持流式输出到前端                                              │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                  ↓                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  阶段7: 记忆摘要 (summarize) / 最终处理 (finalize)                      │    │
│  │     - 对话轮数 >= 5 时触发摘要                                        │    │
│  │     - 保存对话到 PostgreSQL                                           │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

2. React 推理循环详解

React 推理循环使用 app/core/intent.py 中的 react_reason_async() 函数:

┌─────────────────────────────────────────────────────────────────┐
│                    React 推理循环                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  1. Reasoning (推理) - react_reason 节点                    │  │
│  │     - 调用 react_reason_async()                            │  │
│  │     - 传入上下文retrieved_docs、reasoning_history、        │  │
│  │       previous_actions、messages、errors                     │  │
│  │     - LLM 决定下一步 action                                │  │
│  │     - 记录到 reasoning_history                             │  │
│  └───────────────────────────────────────────────────────────┘  │
│                            ↓                                    │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  2. Acting (行动)                                          │  │
│  │     - rag_retrieve: RAG 检索                               │  │
│  │     - web_search: 联网搜索                                  │  │
│  │     - contact_subgraph: 通讯录子图                         │  │
│  │     - dictionary_subgraph: 词典子图                          │  │
│  │     - news_analysis_subgraph: 资讯分析子图                   │  │
│  │     - handle_error: 错误处理                               │  │
│  └───────────────────────────────────────────────────────────┘  │
│                            ↓                                    │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  3. Observing (观察) / 循环                                 │  │
│  │     - 工具结果返回给 react_reason                           │  │
│  │     - 再次推理下一步                                         │  │
│  │     - 最多 10 次循环 (max_steps=10)                         │  │
│  │     - 或直到推理决定 llm_call                              │  │
│  └───────────────────────────────────────────────────────────┘  │
│                            ↓                                    │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  4. 退出条件                                               │  │
│  │     - action == llm_call: 退出循环,进入 llm_call 节点       │  │
│  │     - max_steps 达到: 强制退出到 llm_call                   │  │
│  │     - 错误累积过多: 进入 handle_error                       │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

关键实现点

  1. react_reason_async() - 在 app/core/intent.py 中,使用 chat_services 获取 LLM 进行推理
  2. route_by_reasoning - 路由函数,根据推理结果决定下一步节点
  3. 循环边 - 工具节点执行后回到 react_reason 继续推理
  4. 自动升级 - 快速路径失败时,回到 react_reason 继续推理

2.2 状态机设计

# 核心状态定义
class MainGraphState(TypedDict):
    messages: Annotated[list, add_messages]        # 对话历史(自动合并)
    user_id: str                                    # 用户标识
    user_query: str                                 # 用户查询
    memory_context: str                             # 检索到的记忆上下文
    memory_triggered: bool                          # 记忆是否触发
    should_summarize: bool                          # 是否需要生成摘要
    retrieved_docs: list                            # RAG 检索到的文档
    reasoning_history: list                         # React 推理历史
    previous_actions: list                          # 之前的动作
    errors: list                                    # 错误列表
    current_step: int                               # 当前步骤
    max_steps: int                                  # 最大步骤数
    llm_override: str                               # LLM 覆盖
    final_response: str                             # 最终响应

状态流转规则

初始状态 → retrieve_memory → memory_trigger → init_state → hybrid_router
                                                          ↓
                                ┌───────────────────────────┼───────────────────────────┐
                                ↓                           ↓                           ↓
                           fast_chitchat               fast_rag                   fast_tool
                                ↓                           ↓                           ↓
                   [成功→llm_call / 失败→react_reason]  [成功→llm_call / 失败→react_reason] [成功→llm_call / 失败→react_reason]
                                                                        ↓
                        ┌───────────────────────────────────────────────┘
                        ↓
                  react_reason
                        ↓
         ┌──────────────┼──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
         ↓              ↓              ↓              ↓              ↓              ↓              ↓
   rag_retrieve  web_search  contact_subgraph  dictionary_subgraph  news_analysis_subgraph  handle_error  llm_call
         ↓              ↓              ↓              ↓              ↓              ↓              ↓
   react_reason ←────────┴──────────────┴──────────────┴──────────────┴──────────────┴──────────────┘
                                                                        ↓
                                                                  llm_call
                                                                        ↓
                                                                  summarize
                                                                        ↓
                                                                  finalize

2.3 记忆管理算法

记忆检索策略:

1. 向量检索:将用户查询 Embedding在 PostgreSQL 中检索相似对话
2. 时间衰减:近期对话权重更高(可选)
3. 相关性过滤:仅返回相似度 > 阈值的记忆
4. 上下文窗口:限制记忆长度,避免超出 LLM 上下文限制

摘要生成策略:

触发条件:
  - 对话轮数超过阈值(默认 10 轮)
  - 记忆上下文长度超过限制
  
摘要生成:
  1. 提取最近 N 轮对话
  2. 调用 LLM 生成摘要
  3. 保存摘要到 PostgreSQL
  4. 清理旧对话记录
  
摘要格式:
  "用户在 {时间} 讨论了 {主题},关键信息:{要点}"

3. 多模型路由算法

模型选择逻辑:
┌─────────────────────────────────────────────┐
│  用户选择模型                                 │
└──────────────────┬──────────────────────────┘
                   │
        ┌──────────┼──────────┬──────────┐
        ↓          ↓          ↓          ↓
    ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐
    │ zhipu│  │deep  │  │openai│  │local │
    └──┬───┘  └──┬───┘  └──┬───┘  └──┬───┘
       │         │         │         │
       ↓         ↓         ↓         ↓
   ChatZhipu  ChatOpenAI ChatOpenAI ChatOpenAI
   (官方SDK)  (DeepSeek)  (OpenAI) (llama.cpp/Qwen)
       │         │         │         │
       └─────────┴─────────┴─────────┘
                 ↓
        ┌────────────────┐
        │  统一接口输出   │
        │  (BaseChatModel)│
        └────────────────┘

高可用降级策略:

1. 优先使用用户选择的模型
2. 如果模型不可用API 错误、超时等):
   - 尝试切换到备用模型
   - 记录错误日志
   - 返回降级提示给用户
3. 健康检查:定期检查各模型服务状态

4. SSE 流式响应算法

流式输出流程:
┌─────────────────────────────────────────────┐
│  LLM 生成 Token                              │
└──────────────────┬──────────────────────────┘
                   │
                   ↓
┌─────────────────────────────────────────────┐
│  事件格式化                                  │
│  data: {"content": "你", "type": "content"} │
│  data: {"content": "好", "type": "content"} │
│  data: {"content": "", "type": "content"} │
│  data: {"done": true, "type": "end"}        │
└──────────────────┬──────────────────────────┘
                   │
                   ↓
┌─────────────────────────────────────────────┐
│  HTTP Response Stream                        │
│  Content-Type: text/event-stream            │
│  Cache-Control: no-cache                    │
│  Connection: keep-alive                     │
└─────────────────────────────────────────────┘

实现要点:

  • 使用 asyncio.Queue 缓冲 Token
  • 生产者LLM 异步生成 Token 放入队列
  • 消费者FastAPI EventSourceResponse 从队列读取并推送
  • 超时保护30 秒无输出自动断开

📚 RAG 系统完整架构

离线索引构建 vs 在线检索生成

RAG 系统分为两个独立但协同的阶段:

┌──────────────────────────────────────────────────────────────────┐
│                    RAG 系统双阶段架构                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  阶段一:离线索引构建 (rag_indexer)                               │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  文档加载 → 文本切分 → Embedding → 向量存储              │     │
│  │  PDF/DOCX/TXT    递归/语义/父子块   llama.cpp       Qdrant    │     │
│  └────────────────────────────────────────────────────────┘     │
│                              │                                    │
│                              ▼                                    │
│  阶段二:在线检索生成 (backend/app/rag)                                   │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  用户查询 → 查询改写 → 多路检索 → RRF 融合 → 重排序      │     │
│  │              MultiQuery    Dense+Sparse    Cross-Encoder     │     │
│  │                              │                              │     │
│  │                              ▼                              │     │
│  │                        LLM 生成回答                          │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

RAG 演进路线 (Roadmap)

核心算法: 近似最近邻搜索 (ANN, 常用 HNSW 算法)

算法原理:
  用户问题 → Embedding 模型 → 向量表示
    ↓
  计算与库中向量的余弦相似度
    ↓
  取距离最近的 K 个块返回
  
优缺点:
  ✅ 速度极快(毫秒级)
  ❌ 只能捕捉"语义相似",专有名词匹配差
  
实现代码:
  from backend.app.rag.retriever import create_base_retriever
  
  retriever = create_base_retriever(
      collection_name="rag_documents",
      embeddings=embeddings,
      search_kwargs={"k": 20}
  )
  docs = retriever.invoke("什么是 RAG")

Level 2: 混合检索与重排序 (Hybrid Search + Reranker)

1. 基础召回(混合检索)

算法原理:
  稠密向量检索(语义相似)+ BM25 稀疏检索(关键词匹配)
    ↓
  两路结果并行获取,等待融合
  
实现代码:
  from backend.app.rag.retriever import create_hybrid_retriever
  
  retriever = create_hybrid_retriever(
      collection_name="rag_documents",
      embeddings=embeddings,
      dense_k=10,
      sparse_k=10,
      score_threshold=0.3
  )

2. 二次精排Cross-Encoder

算法原理:
  不同于双塔模型(分别算向量再求距离)
  交叉编码器将"问题 + 文档"拼接后整体输入 Transformer
  由模型直接输出 0~1 的相关性得分,精度极高
  
实现代码:
  from backend.app.rag.reranker import LLaMaCPPReranker
  
  reranker = LLaMaCPPReranker(
      base_url="http://127.0.0.1:8083",
      api_key="your-api-key",
      top_n=5
  )
  sorted_docs = reranker.compress_documents(documents, query)

Level 3: RAG-Fusion (多路改写与倒数排名融合)

1. 多路查询改写

算法原理:
  克服用户初始提问词不达意或视角受限的问题
  通过 LLM 将单一问题改写为多个不同角度的查询
  
实现代码:
  from backend.app.rag.query_transform import MultiQueryGenerator
  
  generator = MultiQueryGenerator(llm=llm, num_queries=3)
  queries = await generator.agenerate("如何申请项目资金?")
  # 返回:["如何申请项目资金?", "项目资金申请流程是什么?", "申请项目经费需要哪些步骤?"]

2. 倒数排名融合RRF

算法原理:
  RRF (Reciprocal Rank Fusion) 是一种无需评分归一化的融合算法
  公式RRF_score(d) = Σ 1/(k + rank_q(d))
  有效避免某一极端检索结果主导全局
  
实现代码:
  from backend.app.rag.fusion import reciprocal_rank_fusion
  
  # 多个查询的检索结果
  doc_lists = [result1, result2, result3]
  fused_docs = reciprocal_rank_fusion(doc_lists, k=60)

Level 4: Agentic RAG / Self-RAG (智能体与自我反思)

核心原理:
  基于 LangGraph 的 ReAct (Reasoning and Acting) 状态机路由
  大模型并非每次都去死板地执行检索,而是先判断问题:
  "这是闲聊?还是需要查知识库?"

工作流程:
  ┌──────────┐     ┌──────────────┐     ┌──────────┐     ┌────────┐
  │   User   │────>│ LangGraph    │────>│ RAG_Tool │────>│ Qdrant │
  │          │     │ Agent        │     │          │     │        │
  │ "公司报  │     │ 思考: 这是   │     │ ToolCall │     │ RAG-   │
  │ 销流程?"│     │ 内部规章问题 │     │ search_  │     │ Fusion │
  │          │     │ 需要查资料   │     │ knowledge│     │ & 混合 │
  │          │<────│ 资料充分,   │<────│ 返回最相 │<────│ 检索   │
  │ "根据知  │     │ 开始撰写回答 │     │ 关5条规定 │     │ Cross- │
  │ 识库规定 │     │              │     │          │     │ Encoder│
  │ ..."     │     │              │     │          │     │ 重排   │
  └──────────     └──────────────     └──────────┘     └────────┘

实现代码:
  from backend.app.rag.tools import search_knowledge_base
  from backend.app.main_graph.utils.main_graph_builder import MainGraphBuilder

  # 构建图
  builder = MainGraphBuilder()
  graph = builder.build_graph().compile(checkpointer=checkpointer)

Level 5: GraphRAG 集成 (基于图和关系的 RAG)

核心原理:
  结合知识图谱的结构化关系和向量检索的语义相似度
  解决跨文档复杂关系推理问题
  
实现代码:
  from langchain_community.graphs import Neo4jGraph
  from langchain_experimental.graph_transformers import LLMGraphTransformer
  
  # 实体关系抽取
  transformer = LLMGraphTransformer(llm=local_llm)
  graph_documents = transformer.convert_to_graph_documents(documents)
  
  # 存储到图数据库
  graph = Neo4jGraph(url="bolt://localhost:7687")
  graph.add_graph_documents(graph_documents)

检索策略对比

策略 优点 缺点 适用场景
基础向量检索 速度快,语义理解好 专有名词匹配差 通用问答
混合检索 语义 + 关键词匹配 需要配置稀疏向量 专业术语查询
多路改写 + RRF 搜索面广,结果稳定 延迟略高 复杂问题
重排序 精度高 依赖额外模型 最终精排
Agentic RAG 智能决策,灵活 实现复杂 生产环境
GraphRAG 关系推理能力强 需要图数据库 知识密集型场景

切分策略对比

策略 原理 优点 缺点 适用场景
递归字符 按分隔符递归切分 速度快,实现简单 可能截断语义 简单文档
语义切分 基于句子相似度阈值 语义连贯性好 需要 Embedding 模型 专业文档
父子块 大块存储+小块检索 检索精准+上下文完整 存储复杂度高 生产环境

🧩 子图系统架构

设计理念

子图系统采用模块化设计,每个子图是一个独立的 LangGraph 工作流,共享公共工具库。

┌───────────────────────────────────────────────────────────────────┐
│                        子图系统架构                                 │
├───────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │  公共工具库 (common/)                                        │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────┐ │ │
│  │  │ state_base  │ │   intent    │ │ formatter   │ │human │ │ │
│  │  │   (状态基类) │ │  (React模式) │ │  (格式化)   │ │review│ │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘ └──────┘ │ │
│  └───────────────────────────────────────────────────────────┘ │
│                              │                                    │
│              ┌───────────────┼───────────────┐                   │
│              ↓               ↓               ↓                   │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │  通讯录子图     │ │  词典子图       │ │  资讯分析子图   │   │
│  │  (contact/)    │ │  (dictionary/)  │ │ (news_analysis) │   │
│  ├─────────────────┤ ├─────────────────┤ ├─────────────────┤   │
│  │ state.py        │ │ state.py        │ │ state.py        │   │
│  │ nodes.py        │ │ nodes.py        │ │ nodes.py        │   │
│  │ graph.py        │ │ graph.py        │ │ graph.py        │   │
│  │ api_client.py   │ │ api_client.py   │ │ api_client.py   │ │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                              │                                    │
│                              ▼                                    │
│                     FastAPI 子图端点                             │
│                     (backend/app/backend.py)                      │
└───────────────────────────────────────────────────────────────────┘

子图架构总览

graph TB
    subgraph "主图 MainGraph"
        MainState[主图 MainState<br>用户输入 + 上下文]
        IntentRouter[意图分类器<br>intent.py]
        MainLLM[主 LLM 节点<br>普通对话]
    end

    subgraph "通讯录子图 ContactSubgraph"
        ContactState[ContactState<br>联系人状态]
        ContactNodes[内部节点<br>parse_intent<br>add_contact<br>list_contacts<br>generate_draft<br>human_review<br>should_continue]
        ContactDB[(PostgreSQL 联系人)]
    end

    subgraph "词典子图 DictionarySubgraph"
        DictState[DictionaryState<br>词典状态]
        DictNodes[内部节点<br>translate<br>lookup_word<br>extract_terms<br>daily_word<br>lookup_word_book<br>add_to_word_book]
        DictDB[(PostgreSQL 生词本)]
    end

    subgraph "资讯分析子图 NewsSubgraph"
        NewsState[NewsAnalysisState<br>资讯状态]
        NewsNodes[内部节点<br>query_news<br>analyze_url<br>extract_keywords<br>generate_report]
        NewsQdrant[(Qdrant 向量检索)]
    end

    MainState -->|用户查询| IntentRouter
    IntentRouter -->|contact| ContactState
    IntentRouter -->|dictionary| DictState
    IntentRouter -->|news| NewsState
    IntentRouter -->|chat| MainLLM

    ContactState -->|调用| ContactNodes
    ContactNodes -->|读写| ContactDB
    ContactNodes -->|返回结果| MainState

    DictState -->|调用| DictNodes
    DictNodes -->|读写| DictDB
    DictNodes -->|返回结果| MainState

    NewsState -->|调用| NewsNodes
    NewsNodes -->|检索| NewsQdrant
    NewsNodes -->|返回结果| MainState

    style MainState fill:#e3f2fd
    style IntentRouter fill:#ffe0b2
    style ContactState fill:#c8e6c9
    style DictState fill:#e1bee7
    style NewsState fill:#ffcdd2

状态传送机制

sequenceDiagram
    participant User as 用户
    participant Frontend as 前端
    participant Backend as 后端 API
    participant Main as 主图
    participant Intent as 意图分类器
    participant Subgraph as 子图
    participant DB as 数据库

    User->>Frontend: 输入查询
    Frontend->>Backend: POST /subgraph/{type}/{action}
    Backend->>Main: 初始化 MainState
    Main->>Intent: 调用意图分类
    Intent-->>Main: 返回子图类型 (contact/dictionary/news/chat)

    alt 是子图请求
        Main->>Subgraph: 传递状态
        activate Subgraph
        Subgraph->>Subgraph: 解析意图 (parse_intent)
        Subgraph->>DB: 读取/写入数据
        DB-->>Subgraph: 返回数据
        Subgraph->>Subgraph: 执行业务逻辑
        Subgraph->>Subgraph: 格式化结果 (format_result)
        Subgraph-->>Main: 返回结果状态
        deactivate Subgraph
    else 是普通对话
        Main->>Main: 调用主 LLM
    end

    Main->>Main: 合并状态
    Main-->>Backend: 返回最终结果
    Backend-->>Frontend: JSON 响应
    Frontend-->>User: 显示结果

核心工具说明

1. state_base.py - 状态基类

提供类型安全的状态基类,所有子图状态继承此类。

2. intent.py - 意图理解React 模式)

智能决策节点,判断是否需要调用 RAG、是否需要重新检索、是否需要调用工具。

# 核心功能
- 意图分类闲聊 / 查询 / 任务
- RAG 决策是否需要检索知识库
- 检索策略是否需要多路检索 / 是否需要重新检索

3. formatter.py - 格式化输出工具

使用 Jinja2 模板提供美观的 Markdown 格式化输出。

4. human_review.py - 人工审核节点

使用 LangGraph 的 interrupt 机制实现人工审核流程,支持:

  • 确定 / 取消 / 继续 三种操作
  • 状态持久化
  • 异步等待

子图开发指南

创建新子图的步骤

  1. 创建子图目录
mkdir backend/app/subgraphs/my_subgraph
  1. 创建状态定义 (state.py)
from typing_extensions import TypedDict
from backend.app.core.state_base import BaseSubgraphState

class MySubgraphState(BaseSubgraphState):
    \"\"\"
    我的子图状态
    \"\"\"
    my_field: str
    my_list: list[str]
  1. 实现节点 (nodes.py)
from langgraph.graph import StateGraph
from .state import MySubgraphState

def my_node(state: MySubgraphState) -> MySubgraphState:
    \"\"\"
    我的节点
    \"\"\"
    # 实现逻辑
    return state
  1. 构建图 (graph.py)
from langgraph.graph import StateGraph, END
from .state import MySubgraphState
from .nodes import my_node

def build_my_subgraph() -> StateGraph:
    \"\"\"
    构建我的子图
    \"\"\"
    graph = StateGraph(MySubgraphState)

    graph.add_node("my_node", my_node)
    graph.set_entry_point("my_node")
    graph.add_edge("my_node", END)

    return graph.compile()
  1. 添加 API 客户端 (api_client.py)(可选) 用于与外部 API 交互。

已实现的子图

1. 通讯录子图 (contact/)

详细流程图:

stateDiagram-v2
    [*] --> parse_intent: 开始

    parse_intent --> list_contacts: action=list
    parse_intent --> add_contact: action=add
    parse_intent --> list_emails: action=list_emails
    parse_intent --> generate_email_draft: action=generate_email
    parse_intent --> sniff_contacts: action=sniff

    list_contacts --> format_result: 返回联系人列表
    add_contact --> format_result: 添加成功
    list_emails --> format_result: 返回邮件列表
    sniff_contacts --> format_result: 嗅探完成

    generate_email_draft --> human_review: 生成草稿
    human_review --> send_email: action=approve
    human_review --> format_result: action=reject

    send_email --> format_result: 邮件已发送

    format_result --> [*]: 格式化输出

    note right of parse_intent
        解析用户意图
        支持的操作:
        - list: 列出联系人
        - add: 添加联系人
        - list_emails: 列出邮件
        - generate_email: 生成邮件草稿
        - sniff: 智能嗅探
    end note

    note right of human_review
        人工审核节点
        使用 LangGraph interrupt
        支持三种操作:
        - approve: 审核通过,发送邮件
        - reject: 审核拒绝,返回结果
        - modify: 审核修改,编辑内容
    end note

功能列表:

  • 联系人 CRUD增删改查
  • 邮件读取与审核
  • 外发邮件
  • 智能嗅探
  • 精美格式化展示

API 端点:

GET /subgraph/contact/{action}
参数:
- action: list | add | list_emails | generate_email | sniff
- query: 查询内容
- user_id: 用户 ID

2. 词典子图 (dictionary/)

详细流程图:

stateDiagram-v2
    [*] --> parse_intent: 开始

    parse_intent --> query_word: action=query
    parse_intent --> translate_text: action=translate
    parse_intent --> extract_terms: action=extract
    parse_intent --> get_daily_word: action=daily
    parse_intent --> lookup_word_book: action=lookup
    parse_intent --> add_to_word_book: action=add

    query_word --> format_result: 查询单词
    translate_text --> format_result: 翻译文本
    extract_terms --> format_result: 提取专业术语
    get_daily_word --> format_result: 每日一词
    lookup_word_book --> format_result: 查询生词本
    add_to_word_book --> format_result: 添加到生词本

    format_result --> [*]: 格式化输出

    note right of parse_intent
        解析用户意图
        支持的操作:
        - query: 查询单词
        - translate: 翻译文本
        - extract: 提取专业术语
        - daily: 每日一词
        - lookup: 查询生词本
        - add: 添加到生词本
    end note

功能列表:

  • 翻译、查词
  • 每日一词
  • 专业名词提炼
  • 生词本管理
  • 精美格式化展示

API 端点:

GET /subgraph/dictionary/{action}
参数:
- action: query | translate | extract | daily | lookup | add
- query: 查询内容
- user_id: 用户 ID

3. 资讯分析子图 (news_analysis/)

详细流程图:

stateDiagram-v2
    [*] --> parse_intent: 开始

    parse_intent --> query_news: action=query
    parse_intent --> analyze_url: action=analyze
    parse_intent --> extract_keywords: action=keywords
    parse_intent --> generate_report: action=report

    query_news --> format_result: 查询资讯
    analyze_url --> format_result: 分析链接
    extract_keywords --> format_result: 提取关键词
    generate_report --> format_result: 生成报告

    format_result --> [*]: 格式化输出

    note right of parse_intent
        解析用户意图
        支持的操作:
        - query: 查询资讯
        - analyze: 分析链接
        - keywords: 提取关键词
        - report: 生成报告
    end note

功能列表:

  • 资讯查询
  • 链接分析
  • 关键词提取
  • 报告生成
  • 精美格式化展示

API 端点:

GET /subgraph/news/{action}
参数:
- action: query | analyze | keywords | report
- query: 查询内容
- user_id: 用户 ID

4. 研究分析子图 (research/) - 未实现

以下功能尚未实现:

  • 联网搜索
  • 报告生成(资讯分析子图已提供基础报告生成)
  • 引用溯源
  • 可视化图表

🚀 快速开始

详细启动指南请查看 QUICKSTART.md

方式一Docker Compose推荐

# 1. 配置环境变量
cp .env.docker .env
# 编辑 .env 文件,填入真实的 API Key

# 2. 启动所有服务
docker compose -f docker/docker-compose.yml up -d --build

# 3. 访问应用
# 前端: http://127.0.0.1:8501
# 后端 API: http://127.0.0.1:8083

方式二:本地开发模式

# 1. 安装依赖
pip install -r requirement.txt

# 2. 配置环境变量
cp .env.docker .env
# 编辑 .env根据本地/远程环境调整配置

# 3. 启动后端
python backend/app/backend.py

# 4. 启动前端(新终端)
streamlit run frontend/src/frontend_main.py

# 或者使用启动脚本(推荐)
./scripts/start.sh both

🔧 开发指南

添加新工具

backend/app/main_graph/tools/common_tools.py 中添加新的 @tool 装饰函数:

from langchain_core.tools import tool
from typing import Optional

@tool
def my_new_tool(param: str) -> str:
    """
    工具描述(会显示给 LLM

    Args:
        param: 参数说明

    Returns:
        返回值说明
    """
    # 实现逻辑
    return result

然后在 backend/app/main_graph/tools/graph_tools.pyAVAILABLE_TOOLS 列表中注册。

添加新模型

backend/app/model_services/chat_services.py 中添加新的服务提供者:

class NewModelChatProvider(BaseServiceProvider[BaseChatModel]):
    """
    新模型服务提供者
    """

    def __init__(self, model: str = "new-model-name"):
        super().__init__("new_model_chat")
        self._model = model

    def is_available(self) -> bool:
        """
        检查新模型服务是否可用
        """
        if not os.getenv("NEW_MODEL_API_KEY"):
            logger.warning("NEW_MODEL_API_KEY 未配置")
            return False
        logger.info(f"新模型服务配置正确,准备使用: {self._model}")
        return True

    def get_service(self) -> BaseChatModel:
        """
        获取新模型服务
        """
        if self._service_instance is None:
            from langchain_openai import ChatOpenAI
            from pydantic import SecretStr

            self._service_instance = ChatOpenAI(
                base_url="https://api.new-model.com/v1",
                api_key=SecretStr(os.getenv("NEW_MODEL_API_KEY")),
                model=self._model,
                temperature=0.1,
                max_tokens=4096,
                timeout=60.0,
                max_retries=2,
                streaming=True,
            )
        return self._service_instance

然后在 CHAT_PROVIDERS 字典中注册:

CHAT_PROVIDERS: Dict[str, Callable[[], BaseServiceProvider[BaseChatModel]]] = {
    "local": lambda: LocalChatProvider(),
    "zhipu": lambda: ZhipuChatProvider(),
    "deepseek": lambda: DeepSeekChatProvider(),
    "openai": lambda: OpenAIChatProvider(),
    "new_model": lambda: NewModelChatProvider(),  # 新增
}

添加新的子图

1. 创建子图目录结构

backend/app/subgraphs/ 下创建新的子图目录:

backend/app/subgraphs/
└── my_subgraph/
    ├── __init__.py
    ├── state.py         # 子图状态定义
    ├── nodes.py         # 子图节点实现
    ├── graph.py         # 子图构建
    └── api_client.py    # 外部 API 客户端(可选)

2. 定义子图状态

state.py 中定义:

from typing import TypedDict, Annotated, Literal
from langgraph.graph.message import add_messages

class MySubgraphState(TypedDict):
    """子图状态"""
    messages: Annotated[list, add_messages]
    user_id: str
    query: str
    result: str
    step: Literal["init", "process", "format", "end"]

3. 实现子图节点

nodes.py 中实现节点函数:

from .state import MySubgraphState

def process_query(state: MySubgraphState) -> MySubgraphState:
    """处理查询"""
    query = state["query"]
    # 处理逻辑
    return {
        "step": "format",
        "result": "处理结果"
    }

def format_output(state: MySubgraphState) -> MySubgraphState:
    """格式化输出"""
    result = state["result"]
    return {
        "step": "end",
        "result": f"格式化后的结果: {result}"
    }

4. 构建子图

graph.py 中构建:

from langgraph.graph import StateGraph, END
from .state import MySubgraphState
from .nodes import process_query, format_output

def create_my_subgraph() -> StateGraph:
    """创建子图"""
    graph = StateGraph(MySubgraphState)

    graph.add_node("process_query", process_query)
    graph.add_node("format_output", format_output)

    graph.set_entry_point("process_query")

    graph.add_edge("process_query", "format_output")
    graph.add_edge("format_output", END)

    return graph

5. 在主图中注册子图工具

backend/app/main_graph/tools/subgraph_tools.py 中添加子图调用工具:

@tool
async def my_subgraph_tool(query: str) -> str:
    """
    我的子图工具描述

    Args:
        query: 用户查询

    Returns:
        子图执行结果
    """
    # 调用子图逻辑
    return result

6. 在 React Reason 中添加路由

backend/app/core/intent.pyreact_reason_async 函数中添加对子图工具的支持。

Docker 部署

项目包含完整的 Docker 配置:

  • docker-compose.yml服务编排Backend + Frontend连接远程数据库
  • docker/Dockerfile.backend:后端镜像构建
  • docker/Dockerfile.frontend:前端镜像构建
  • .gitea/workflows/deploy.ymlCI/CD 自动化部署

详见 QUICKSTART.md 的 Docker 部署章节。


⚙️ 环境配置

配置文件说明

项目采用两层环境配置文件体系:

文件 用途 是否提交 Git
.env.docker Docker 部署模板
.env 实际使用的配置 否(已忽略)

使用方法:

  • 本地开发cp .env.docker .env,修改为本地服务地址
  • Docker 部署cp .env.docker .env,使用远程服务器地址

重要配置(必需)

变量名 说明 示例值
ZHIPUAI_API_KEY 智谱AI API密钥 your-api-key
DEEPSEEK_API_KEY DeepSeek API密钥 your-api-key
LLAMACPP_API_KEY llama.cpp API密钥 your-api-key
VLLM_BASE_URL 主 LLM 服务地址 http://127.0.0.1:18000/v1
LLAMACPP_EMBEDDING_URL Embedding 服务地址 http://127.0.0.1:18001/v1
LLAMACPP_RERANKER_URL Rerank 服务地址 http://127.0.0.1:18002/v1
DB_HOST PostgreSQL 主机 115.190.121.151
DB_PORT PostgreSQL 端口 5432
DB_USER PostgreSQL 用户名 postgres
DB_PASSWORD PostgreSQL 密码 your-password
DB_NAME PostgreSQL 数据库名 langgraph_db
QDRANT_URL Qdrant 向量数据库地址 http://115.190.121.151:6333
QDRANT_API_KEY Qdrant API 密钥 your-api-key

其他配置(有默认值)

变量名 说明 默认值
BACKEND_PORT 后端服务端口 8079
API_URL 前端调用后端地址 http://backend:8079/chat
MEMORY_SUMMARIZE_INTERVAL 对话摘要生成间隔 10
ENABLE_GRAPH_TRACE 是否启用图追踪 true
FASTEMBED_CACHE_PATH FastEmbed 缓存路径 /app/fastembed_cache
RAG_COLLECTION_NAME RAG 集合名称 rag_documents
RAG_STRATEGY RAG 切分策略 parent-child
RAG_STORAGE_TYPE RAG 存储类型 postgres
LOG_LEVEL 日志级别 DEBUG
DEBUG 调试模式 true

注意事项

  • ⚠️ 不要硬编码敏感信息:所有 API Key 必须通过环境变量配置
  • ⚠️ 远程服务依赖:确保可以访问远程 PostgreSQL (115.190.121.151:5432) 和 Qdrant (115.190.121.151:6333)
  • ⚠️ 修改后重启:修改 .envDocker 部署需要执行 docker compose down && docker compose up -d --build

🔍 故障排查

常见问题

Q: 无法连接远程数据库?

# 测试 PostgreSQL
psql -h 115.190.121.151 -U postgres -d langgraph_db -c "SELECT version();"

# 测试 Qdrant
curl http://115.190.121.151:6333/collections

Q: 后端启动失败?

  • 确认端口 8079 未被占用
  • 检查 .env 中的 API Key 是否正确
  • 查看启动日志确认模型初始化成功

Q: 模型切换后无响应?

  • 检查所选模型的配置是否正确
  • 确认 llama.cpp 服务是否运行(如使用本地模型)
  • 尝试切换到另一个模型

Q: 混合路由异常?

  • 检查 ENABLE_GRAPH_TRACE=true 查看详细执行流程
  • 确认快速路径工具是否正确注册
  • 查看 React Reason 节点的输出

更多问题排查请查看 QUICKSTART.md


📝 许可证

本项目采用 MIT 许可证。详见 LICENSE 文件。

🤝 贡献

欢迎提交 Issue 和 Pull Request


TODO

1.llm的BUG修复

看了你的代码,目前的核心问题在于推理节点和回答节点的职责虽然分开了,但实现上仍有混淆,导致流程混乱。下面帮你梳理清楚。


当前架构分析

正确的部分

  • llm_call 节点已经使用了无工具模型models[model_name],没有 bind_tools),只负责根据上下文生成最终回答。这是对的。
  • AIAgentService 中,process_message_stream 处理了 tool_calls 事件,说明你预料到推理 LLM 可能产生 tool call。

混乱的根源

  • 推理 LLM 很可能仍然绑定了工具
    build_react_main_graph(chat_services=self.chat_services, tools=self.tools, ...) 中,你很可能在构建推理节点时,将 tools 传给了推理 LLMllm.bind_tools(tools))。这会导致:
    1. 推理 LLM 直接输出 OpenAI 格式的 function call而不是你期望的 {"action": "RETRIEVE_RAG"} 文本。
    2. LangGraph 会自动解析这些 tool call 并执行(如果工具节点被正确连接),这就绕过了你自己的路由逻辑(条件边)。
    3. 你的流式处理代码(_handle_message_chunk)收到了 tool_calls,但只是把它们当作事件转发给前端,并没有真正由你的代码去执行工具,导致工具调用可能由 LangGraph 自动完成,或者完全丢失。
  • 流式处理中的 _handle_message_chunk 处理了 tool_calls,但实际上这些 tool call 如果被 LangGraph 自动执行了,前端会看到工具调用,但之后突然就跳到了回答,中间缺乏透明控制。如果推理 LLM 输出的是文本动作(比如 JSONtool_calls 永远不会出现,这段处理逻辑就成了死代码。

应该实现的目标架构(也是你一贯讨论的)

推理节点 (react_reason)
  ├─ 使用不带工具绑定的 LLM
  ├─ Prompt 中列出可用动作RETRIEVE_RAG, WEB_SEARCH, DIRECT_RESPONSE
  ├─ 输出结构化 JSON{"action": "...", "query": "...", "reasoning": "..."}
  └─ 你的代码解析 JSON → 修改 state.next_action

条件边 (route_by_reasoning)
  ├─ 读取 state.next_action
  ├─ 映射到工具节点rag_retrieve, web_search_node
  └─ 或直接进入 llm_call 节点

工具节点 (rag_retrieve / web_search_node)
  ├─ 执行实际检索/搜索
  └─ 将结果写回 state.rag_context, state.retrieved_docs

回答节点 (llm_call)
  ├─ 使用无工具 LLM
  ├─ 基于 state.rag_context 生成最终回答
  └─ 绝不调用任何工具

需要现在修改的地方(按优先级)

1. 推理节点:移除工具绑定,改为文本决策

build_react_main_graph 中(或在推理节点的构造代码里),确保推理 LLM 是通过 prompt | llm 而不是 prompt | llm.bind_tools(tools) 调用的。
做法

  • 推理节点的 system prompt 里列出可用动作及格式要求。
  • 推理 LLM 只输出 JSON例如
    {"action": "RETRIEVE_RAG", "search_query": "吕布 事迹 三国演义"}
    
  • 在推理节点后增加一个解析函数,将 JSON 解析为具体的 action 和参数,更新到 state 中。

2. 条件边:根据解析的 action 干净路由

确保 route_by_reasoning 使用解析后的 state.next_action(字符串)进行路由,而不是再去检查 historyretrieved_docs。同时将置信度阈值判断放在路由之前或放在推理节点的 prompt 中(让 LLM 决策时就遵守规则),避免在条件边里重复判断。

3. llm_call 节点:保持当前的无工具状态

当前已正确,无需改动。唯一建议:确保 models[model_name] 确实没有绑定工具。

4. 流式处理:简化事件处理

如果推理节点不再输出 tool call可以将 _handle_message_chunk 中的 tool_call 处理分支删除或注释,避免混淆。
未来如果你想展示推理过程,可以发送 custom event如你现在做的 react_reasoning 事件)。


你现在应该做的具体步骤

  1. 检查 build_react_main_graph 函数,找到推理节点的创建代码,确认是否调用了 llm.bind_tools()。如果调用了,改为 llm,并更新 prompt 为 JSON 输出。
  2. 确保推理节点的 prompt 包含以下内容
    • 当前状态RAG 置信度、尝试次数、已有的检索结果摘要)
    • 决策规则(置信度阈值、最大重试次数等)
    • 要求输出纯 JSON不要代码块标记格式为 {"action": "...", "args": {...}}
  3. 在推理节点返回后,添加一个解析函数,提取 action 和参数,设置 state.next_actionstate.rag_query 等字段。
  4. 修改条件边,直接根据 state.next_action 跳转。
  5. 测试:运行“吕布的事迹?”查询,应该看到推理节点输出 RETRIEVE_RAG,然后 rag_retrieve 执行,再次推理(如果置信度低),或者直接 DIRECT_RESPONSEllm_call 生成回答。最终回答应基于检索到的吕布相关文本,而不是无关片段。

总结

当前逻辑有问题,主要是因为推理节点可能仍绑定了工具,导致 tool call 自动执行打乱了你的路由控制。按上述方案调整后Agent 的决策和执行会变得透明、可控,职责分明。如果你需要,我可以帮你重写推理节点的核心逻辑。

2.优化:实现推理验证

  1. 在 React 循环中增加“验证”步骤 在推理 LLM 输出 DIRECT_RESPONSE 后,不直接返回给用户,而是先进入一个 validate_answer 节点:

text 推理节点 → DIRECT_RESPONSE → validate_answer → 合格?→ 返回用户 ↓ 不合格 重新规划动作(如重新检索) 验证内容:检查回答是否自洽、引用依据是否充分、是否回答了用户问题等。

  1. 使用 LLM 自省Self-Reflection 在 validate_answer 节点里调用一个专门的校验 LLM可以是轻量模型给它这样的 prompt

text 你是一个严格的校验员。请检查以下回答是否满足要求:

【用户问题】 {user_query}

【检索到的资料】 {rag_context}

【生成的回答】 {llm_response}

请判断:

  1. 回答是否基于给定的资料?
  2. 回答是否直接回应了用户问题?
  3. 回答是否存在事实错误或逻辑漏洞?

输出 JSON{"pass": true/false, "reason": "..."} 如果 pass = false则退回推理节点重新规划如重新检索或联网搜索

  1. 在 System Prompt 里要求推理节点评估回答质量 你可以在推理节点的 prompt 里增加一条规则:

text 当你决定 DIRECT_RESPONSE 并收到 llm_call 的回答后,必须自我检查:

  • 回答是否与检索到的资料一致?
  • 是否回答了用户核心问题? 如果发现不一致或遗漏,必须重新规划。 这相当于把反思逻辑融入了推理循环。
Description
No description provided
Readme Apache-2.0 62 MiB
Languages
Python 94.3%
TypeScript 4.7%
Dockerfile 1%