1924 lines
82 KiB
Markdown
1924 lines
82 KiB
Markdown
# AI Agent - 智能助手系统
|
||
|
||
一个基于 LangGraph + FastAPI 的智能对话助手,支持多模型切换、联网搜索、可视化图表、以及多个专业子图模块(通讯录、词典、资讯分析)等功能。
|
||
|
||
---
|
||
|
||
## 📑 目录导航
|
||
|
||
- [核心功能](#-核心功能) - 面向用户的功能和技术特性
|
||
- [技术架构](#️-技术架构) - 技术栈、系统架构图、工作流流程图
|
||
- [核心算法与实现原理](#-核心算法与实现原理) - LangGraph 工作流、多模型路由、SSE 流式响应
|
||
- [快速开始](#-快速开始) - Docker 和本地部署指南
|
||
- [使用指南](#-使用指南) - 基础对话、工具调用、多模型切换
|
||
- [开发指南](#-开发指南) - 添加工具、添加模型、Docker 部署
|
||
- [实现指南与最佳实践](#️-实现指南与最佳实践) - 性能优化、扩展开发、部署实践
|
||
- [环境配置](#️-环境配置) - 配置文件、环境变量
|
||
- [故障排查](#-故障排查) - 常见问题
|
||
|
||
---
|
||
|
||
## 🎯 核心功能
|
||
|
||
### 面向用户的功能
|
||
|
||
- 💬 **智能对话**:支持多轮对话,自动记忆上下文
|
||
- 🌐 **联网搜索**:免费使用 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 模式
|
||
|
||
---
|
||
|
||
## 🏗️ 技术架构
|
||
|
||
### 技术栈总览
|
||
|
||
| 层级 | 组件 | 技术选型 | 说明 |
|
||
|------|------|---------|------|
|
||
| **Agent 框架** | 工作流编排 | LangGraph + LangChain | 状态机驱动的智能体工作流 |
|
||
| **主图系统** | 主流程 | main_graph/ | 混合路由 + React 循环 + 工具执行 |
|
||
| **子图系统** | 模块化子图 | subgraphs/ | 通讯录、词典、资讯分析等子图 |
|
||
| | 核心工具 | core/ | 意图理解、格式化输出、人工审核、联网搜索、可视化图表 |
|
||
| **向量数据库** | 向量检索 | Qdrant | 高性能向量相似度检索(远程服务器) |
|
||
| **后端框架** | API 服务 | FastAPI + Uvicorn | RESTful API + SSE 流式输出 |
|
||
| **前端框架** | Web 界面 | Streamlit | 交互式对话界面 |
|
||
| **关系数据库** | 持久化存储 | PostgreSQL | 对话记忆持久化(远程服务器) |
|
||
| **容器化** | 服务编排 | 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) |
|
||
|
||
### 系统架构流程图
|
||
|
||
```mermaid
|
||
graph TB
|
||
User[用户浏览器] -->|HTTP/SSE| Frontend[Streamlit 前端 :8501]
|
||
Frontend -->|REST API| Backend[FastAPI 后端 :8079]
|
||
|
||
Backend --> AgentService[AIAgentService]
|
||
|
||
AgentService -->|初始化| LangGraph[LangGraph 工作流引擎]
|
||
|
||
LangGraph -->|阶段1| RetrieveMemory[记忆检索 retrieve_memory]
|
||
LangGraph -->|阶段2| MemoryTrigger[记忆触发 memory_trigger]
|
||
LangGraph -->|阶段3| InitState[初始化状态 init_state]
|
||
LangGraph -->|阶段4| HybridRouter[⭐ 混合路由 hybrid_router]
|
||
LangGraph -->|阶段5| ReactLoop[React 循环 react_loop]
|
||
LangGraph -->|阶段6| FastPath[快速路径 fast_*]
|
||
LangGraph -->|阶段7| LLMCall[LLM 调用 llm_call]
|
||
LangGraph -->|阶段8| Summarize[记忆摘要 summarize]
|
||
LangGraph -->|阶段9| Finalize[最终处理 finalize]
|
||
|
||
HybridRouter -->|闲聊| FastChitchat[fast_chitchat]
|
||
HybridRouter -->|知识查询| FastRAG[fast_rag]
|
||
HybridRouter -->|工具调用| FastTool[fast_tool]
|
||
HybridRouter -->|复杂任务| ReactLoop
|
||
|
||
ReactLoop -->|推理| ReactReason[react_reason 推理节点]
|
||
ReactLoop -->|RAG检索| RAGRetrieve[rag_retrieve]
|
||
ReactLoop -->|联网搜索| WebSearch[web_search]
|
||
ReactLoop -->|通讯录| ContactSubgraph[contact_subgraph]
|
||
ReactLoop -->|词典| DictionarySubgraph[dictionary_subgraph]
|
||
ReactLoop -->|资讯分析| NewsSubgraph[news_analysis_subgraph]
|
||
ReactLoop -->|错误处理| HandleError[handle_error]
|
||
|
||
RAGRetrieve -->|向量检索| Qdrant[Qdrant向量库]
|
||
RAGRetrieve -->|重排序| RerankService[Rerank服务]
|
||
RAGRetrieve -->|嵌入| EmbeddingService[Embedding服务]
|
||
|
||
AgentService -->|模型路由| ChatServices[模型服务层 chat_services]
|
||
ChatServices -->|自动降级| FallbackChain[FallbackServiceChain]
|
||
FallbackChain -->|创建| Zhipu[智谱 GLM-4]
|
||
FallbackChain -->|创建| DeepSeek[DeepSeek V3]
|
||
FallbackChain -->|创建| OpenAI[OpenAI GPT-4o]
|
||
FallbackChain -->|创建| LocalQwen[本地 Qwen3.5-9B]
|
||
|
||
RetrieveMemory -->|存储/读取| PostgreSQL[PostgreSQL]
|
||
Summarize -->|存储| PostgreSQL
|
||
|
||
style User fill:#e1f5ff
|
||
style Frontend fill:#fff4e1
|
||
style Backend fill:#e8f5e9
|
||
style HybridRouter fill:#fff3e0,stroke:#ff9800,stroke-width:3px
|
||
style ReactLoop fill:#f3e5f5
|
||
style FastPath fill:#e3f2fd
|
||
style LangGraph fill:#c8e6c9
|
||
style ChatServices fill:#c8e6c9
|
||
style PostgreSQL fill:#ffebee
|
||
style Qdrant fill:#ffebee
|
||
```
|
||
|
||
---
|
||
|
||
### 主图与子图架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "主图 MainGraph"
|
||
StartMain[START]
|
||
RetrieveMemory[记忆检索]
|
||
MemoryTrigger[记忆触发]
|
||
InitState[初始化状态]
|
||
HybridRouter[⭐ 混合路由<br>规则分流 + LLM意图分类]
|
||
FastChitchat[fast_chitchat<br>闲聊快速路径]
|
||
FastRAG[fast_rag<br>RAG快速路径]
|
||
FastTool[fast_tool<br>工具快速路径]
|
||
ReactReason[react_reason<br>React推理节点]
|
||
LLMCall[llm_call<br>LLM调用节点]
|
||
FinalMain[最终响应]
|
||
EndMain[END]
|
||
|
||
StartMain -->|用户输入| RetrieveMemory
|
||
RetrieveMemory --> MemoryTrigger
|
||
MemoryTrigger --> InitState
|
||
InitState --> HybridRouter
|
||
|
||
HybridRouter -->|闲聊| FastChitchat
|
||
HybridRouter -->|知识查询| FastRAG
|
||
HybridRouter -->|工具调用| FastTool
|
||
HybridRouter -->|复杂任务| ReactReason
|
||
|
||
FastChitchat -->|成功| LLMCall
|
||
FastChitchat -->|失败| ReactReason
|
||
FastRAG -->|成功| LLMCall
|
||
FastRAG -->|失败| ReactReason
|
||
FastTool -->|成功| LLMCall
|
||
FastTool -->|失败| ReactReason
|
||
|
||
ReactReason -->|rag_retrieve| RAGRetrieve[RAG检索]
|
||
ReactReason -->|web_search| WebSearchNode[联网搜索]
|
||
ReactReason -->|contact_subgraph| ContactNode[通讯录子图]
|
||
ReactReason -->|dictionary_subgraph| DictNode[词典子图]
|
||
ReactReason -->|news_analysis_subgraph| NewsNode[资讯子图]
|
||
ReactReason -->|llm_call| LLMCall
|
||
|
||
RAGRetrieve --> ReactReason
|
||
WebSearchNode --> ReactReason
|
||
ContactNode --> ReactReason
|
||
DictNode --> ReactReason
|
||
NewsNode --> ReactReason
|
||
|
||
LLMCall --> FinalMain
|
||
FinalMain --> EndMain
|
||
end
|
||
|
||
subgraph "通讯录子图 ContactSubgraph"
|
||
StartContact[START]
|
||
IntentContact[parse_intent<br>解析意图]
|
||
ListContacts[list_contacts<br>列出联系人]
|
||
AddContact[add_contact<br>添加联系人]
|
||
ListEmails[list_emails<br>列出邮件]
|
||
GenEmail[generate_email_draft<br>生成邮件草稿]
|
||
HumanReview[human_review<br>人工审核]
|
||
SendEmail[send_email<br>发送邮件]
|
||
SniffContact[sniff_contacts<br>智能嗅探]
|
||
FormatContact[format_result<br>格式化输出]
|
||
EndContact[END]
|
||
|
||
StartContact --> IntentContact
|
||
IntentContact -->|list| ListContacts
|
||
IntentContact -->|add| AddContact
|
||
IntentContact -->|list_emails| ListEmails
|
||
IntentContact -->|generate_email| GenEmail
|
||
IntentContact -->|sniff| SniffContact
|
||
ListContacts --> FormatContact
|
||
AddContact --> FormatContact
|
||
ListEmails --> FormatContact
|
||
SniffContact --> FormatContact
|
||
GenEmail --> HumanReview
|
||
HumanReview -->|approve| SendEmail
|
||
HumanReview -->|reject| FormatContact
|
||
SendEmail --> FormatContact
|
||
FormatContact --> EndContact
|
||
end
|
||
|
||
subgraph "词典子图 DictionarySubgraph"
|
||
StartDict[START]
|
||
IntentDict[parse_intent<br>解析意图]
|
||
QueryWord[query_word<br>查询单词]
|
||
Translate[translate_text<br>翻译文本]
|
||
ExtractTerms[extract_terms<br>提取专业术语]
|
||
DailyWord[get_daily_word<br>每日一词]
|
||
LookupWord[lookup_word_book<br>查询生词本]
|
||
AddToWord[add_to_word_book<br>添加到生词本]
|
||
FormatDict[format_result<br>格式化输出]
|
||
EndDict[END]
|
||
|
||
StartDict --> IntentDict
|
||
IntentDict -->|query| QueryWord
|
||
IntentDict -->|translate| Translate
|
||
IntentDict -->|extract| ExtractTerms
|
||
IntentDict -->|daily| DailyWord
|
||
IntentDict -->|lookup| LookupWord
|
||
IntentDict -->|add| AddToWord
|
||
QueryWord --> FormatDict
|
||
Translate --> FormatDict
|
||
ExtractTerms --> FormatDict
|
||
DailyWord --> FormatDict
|
||
LookupWord --> FormatDict
|
||
AddToWord --> FormatDict
|
||
FormatDict --> EndDict
|
||
end
|
||
|
||
subgraph "资讯分析子图 NewsSubgraph"
|
||
StartNews[START]
|
||
IntentNews[parse_intent<br>解析意图]
|
||
QueryNews[query_news<br>查询资讯]
|
||
AnalyzeUrl[analyze_url<br>分析链接]
|
||
ExtractKeywords[extract_keywords<br>提取关键词]
|
||
GenReport[generate_report<br>生成报告]
|
||
FormatNews[format_result<br>格式化输出]
|
||
EndNews[END]
|
||
|
||
StartNews --> IntentNews
|
||
IntentNews -->|query| QueryNews
|
||
IntentNews -->|analyze| AnalyzeUrl
|
||
IntentNews -->|keywords| ExtractKeywords
|
||
IntentNews -->|report| GenReport
|
||
QueryNews --> FormatNews
|
||
AnalyzeUrl --> FormatNews
|
||
ExtractKeywords --> FormatNews
|
||
GenReport --> FormatNews
|
||
FormatNews --> EndNews
|
||
end
|
||
|
||
ReactReason -.->|调用<br>状态传递| StartContact
|
||
ReactReason -.->|调用<br>状态传递| StartDict
|
||
ReactReason -.->|调用<br>状态传递| StartNews
|
||
|
||
style HybridRouter fill:#fff3e0,stroke:#ff9800,stroke-width:3px
|
||
style ReactReason fill:#e8eaf6
|
||
---
|
||
|
||
### 索引工作流(离线构建)
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph 文档输入
|
||
A1[文档源]
|
||
A2[PDF/DOCX/TXT/Markdown]
|
||
end
|
||
|
||
subgraph 文档加载
|
||
B1[rag_indexer/loaders.py]
|
||
B2[UnstructuredLoader]
|
||
B3[PyMuPDFLoader]
|
||
B4[TextLoader]
|
||
end
|
||
|
||
subgraph 文本切分
|
||
C1[rag_indexer/splitters.py]
|
||
C2[RecursiveCharacterTextSplitter<br/>按分隔符递归切分]
|
||
C3[SemanticChunker<br/>基于语义相似度]
|
||
C4[ParentChildSplitter<br/>父子块切分]
|
||
end
|
||
|
||
subgraph 嵌入生成
|
||
D1[Embedding 生成]
|
||
D2[稠密向量<br/>Qwen3-Embedding-0.6B<br/>llama.cpp server:18001]
|
||
D3[稀疏向量 BM25<br/>FastEmbed]
|
||
end
|
||
|
||
subgraph 向量存储
|
||
E1[Qdrant Vector Store]
|
||
E2[稠密向量索引<br/>HNSW 算法]
|
||
E3[稀疏向量索引<br/>BM25]
|
||
E4[rag_core/vector_store.py]
|
||
end
|
||
|
||
A1 --> A2
|
||
A2 --> B1
|
||
B1 --> B2
|
||
B2 --> C1
|
||
C1 --> C2
|
||
C1 --> C3
|
||
C1 --> C4
|
||
C2 --> D1
|
||
C3 --> D1
|
||
C4 --> D1
|
||
D1 --> D2
|
||
D1 --> D3
|
||
D2 --> E1
|
||
D3 --> E1
|
||
E1 --> E2
|
||
E1 --> E3
|
||
|
||
style A1 fill:#e3f2fd
|
||
style B1 fill:#fff3e0
|
||
style C1 fill:#f3e5f5
|
||
style D1 fill:#e8f5e9
|
||
style E1 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 检索 |
|
||
|
||
### 检索工作流(在线查询)
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph 查询输入
|
||
Q1[用户查询]
|
||
Q2[Query: "公司报销流程是什么?"]
|
||
end
|
||
|
||
subgraph 查询处理
|
||
R1[查询改写 MultiQuery]
|
||
R2[rag_indexer/splitters.py]
|
||
R3[使用 chat_services 生成多角度查询]
|
||
end
|
||
|
||
subgraph 混合检索
|
||
S1[并行检索]
|
||
S2[稠密向量检索<br/>Embedding → 向量相似度]
|
||
S3[稀疏 BM25 检索<br/>词频统计]
|
||
S4[rag_core/sparse_embedder.py]
|
||
end
|
||
|
||
subgraph 结果融合
|
||
F1[RRF 融合]
|
||
F2[rag_indexer/fusion.py]
|
||
F3[RRF(d) = Σ 1/(k + rank)]
|
||
F4[Qdrant Fusion API<br/>服务端融合]
|
||
end
|
||
|
||
subgraph 重排序
|
||
P1[Cross-Encoder 重排]
|
||
P2[bge-reranker-v2-m3]
|
||
P3[rerank_services.py<br/>llama.cpp server:18002]
|
||
P4[Query-Document 交互编码]
|
||
end
|
||
|
||
subgraph LLM 生成
|
||
G1[LLM 生成回答]
|
||
G2[chat_services.py]
|
||
G3[Context + 生成回答]
|
||
end
|
||
|
||
Q1 --> Q2
|
||
Q2 --> R1
|
||
R1 --> R2
|
||
R2 --> S1
|
||
S1 --> S2
|
||
S1 --> S3
|
||
S2 --> F1
|
||
S3 --> F1
|
||
F1 --> P1
|
||
P1 --> P2
|
||
P2 --> G1
|
||
G1 --> G2
|
||
G2 --> G3
|
||
|
||
style Q1 fill:#e3f2fd
|
||
style R1 fill:#fff3e0
|
||
style S1 fill:#f3e5f5
|
||
style F1 fill:#e8f5e9
|
||
style P1 fill:#ffebee
|
||
style G1 fill:#e1f5ff
|
||
```
|
||
|
||
**技术组件说明:**
|
||
|
||
| 阶段 | 技术选型 | 说明 |
|
||
|------|---------|------|
|
||
| 查询改写 | MultiQuery | 使用 LLM 生成 3-5 个多角度查询 |
|
||
| 稠密检索 | Qwen3-Embedding | 向量相似度计算,余弦相似度 |
|
||
| 稀疏检索 | FastEmbed BM25 | 词频 TF-IDF 统计 |
|
||
| 结果融合 | Qdrant Fusion API | 服务端 RRF 融合,无需传输数据 |
|
||
| 重排序 | bge-reranker-v2-m3 | Cross-Encoder 交互编码,精度更高 |
|
||
| LLM 生成 | chat_services | 统一的大模型服务接口 |
|
||
|
||
### 数据流向图
|
||
|
||
```
|
||
用户请求
|
||
│
|
||
├─→ 前端 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 重排序提升相关性
|
||
|
||
**详细文档:**
|
||
- 算法原理详见 [backend/docs/RAG_ALGORITHM.md](backend/docs/RAG_ALGORITHM.md)
|
||
- 系统架构详见 [backend/docs/RAG_ARCHITECTURE.md](backend/docs/RAG_ARCHITECTURE.md)
|
||
|
||
---
|
||
|
||
### 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 状态机设计
|
||
|
||
```python
|
||
# 核心状态定义
|
||
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)
|
||
|
||
#### Level 1: 基础向量搜索 (Basic Similarity Search)
|
||
|
||
**核心算法**: 近似最近邻搜索 (ANN, 常用 HNSW 算法)
|
||
|
||
```
|
||
算法原理:
|
||
用户问题 → Embedding 模型 → 向量表示
|
||
↓
|
||
计算与库中向量的余弦相似度
|
||
↓
|
||
取距离最近的 K 个块返回
|
||
|
||
优缺点:
|
||
✅ 速度极快(毫秒级)
|
||
❌ 只能捕捉"语义相似",专有名词匹配差
|
||
|
||
实现代码:
|
||
from 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 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 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 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 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 app.rag.tools import search_knowledge_base
|
||
from 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) │
|
||
└───────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
### 子图架构总览
|
||
|
||
```mermaid
|
||
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
|
||
```
|
||
|
||
---
|
||
|
||
### 状态传送机制
|
||
|
||
```mermaid
|
||
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、是否需要重新检索、是否需要调用工具。
|
||
|
||
```python
|
||
# 核心功能
|
||
- 意图分类:闲聊 / 查询 / 任务
|
||
- RAG 决策:是否需要检索知识库
|
||
- 检索策略:是否需要多路检索 / 是否需要重新检索
|
||
```
|
||
|
||
#### 3. formatter.py - 格式化输出工具
|
||
使用 Jinja2 模板提供美观的 Markdown 格式化输出。
|
||
|
||
#### 4. human_review.py - 人工审核节点
|
||
使用 LangGraph 的 interrupt 机制实现人工审核流程,支持:
|
||
- 确定 / 取消 / 继续 三种操作
|
||
- 状态持久化
|
||
- 异步等待
|
||
|
||
---
|
||
|
||
### 子图开发指南
|
||
|
||
#### 创建新子图的步骤
|
||
|
||
1. **创建子图目录**
|
||
```bash
|
||
mkdir backend/app/subgraphs/my_subgraph
|
||
```
|
||
|
||
2. **创建状态定义 (state.py)**
|
||
```python
|
||
from typing_extensions import TypedDict
|
||
from ..core.state_base import BaseSubgraphState
|
||
|
||
class MySubgraphState(BaseSubgraphState):
|
||
\"\"\"
|
||
我的子图状态
|
||
\"\"\"
|
||
my_field: str
|
||
my_list: list[str]
|
||
```
|
||
|
||
3. **实现节点 (nodes.py)**
|
||
```python
|
||
from langgraph.graph import StateGraph
|
||
from .state import MySubgraphState
|
||
|
||
def my_node(state: MySubgraphState) -> MySubgraphState:
|
||
\"\"\"
|
||
我的节点
|
||
\"\"\"
|
||
# 实现逻辑
|
||
return state
|
||
```
|
||
|
||
4. **构建图 (graph.py)**
|
||
```python
|
||
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()
|
||
```
|
||
|
||
5. **添加 API 客户端 (api_client.py)**(可选)
|
||
用于与外部 API 交互。
|
||
|
||
---
|
||
|
||
### 已实现的子图
|
||
|
||
#### 1. 通讯录子图 (contact/)
|
||
|
||
**详细流程图:**
|
||
```mermaid
|
||
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/)
|
||
|
||
**详细流程图:**
|
||
```mermaid
|
||
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/)
|
||
|
||
**详细流程图:**
|
||
```mermaid
|
||
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](QUICKSTART.md)
|
||
|
||
### 方式一:Docker Compose(推荐)
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
### 方式二:本地开发模式
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
---
|
||
|
||
## 📖 使用指南
|
||
|
||
### 基础对话
|
||
|
||
直接在聊天框输入问题即可:
|
||
|
||
```
|
||
你好,请介绍一下自己
|
||
帮我写一个 Python 脚本
|
||
```
|
||
|
||
### 主要功能
|
||
|
||
| 功能 | 说明 | 示例提问 |
|
||
|------|------|---------|
|
||
| 🧠 混合路由智能分流 | 自动判断任务类型,选择最佳路径 | 自然对话即可 |
|
||
| ⚡ 快速路径 | 闲聊、RAG查询、工具调用可走快速路径 | "你好"、"什么是 RAG" |
|
||
| 🔄 React 推理循环 | 复杂任务走完整的思考-行动-观察循环 | "帮我分析一下这个文档" |
|
||
| 🌐 联网搜索 | 免费 DuckDuckGo 搜索 | "今天北京天气怎么样?" |
|
||
| 📚 RAG 知识库检索 | 检索本地知识库 | "如何配置系统?" |
|
||
| 📇 通讯录管理 | 联系人 CRUD、邮件处理 | "帮我查看一下张三的联系方式" |
|
||
| 📖 智能词典 | 翻译、生词本、专业术语提取 | "帮我翻译这句话" |
|
||
| 📰 资讯分析 | 资讯获取、内容分析 | "帮我分析一下这篇新闻" |
|
||
| 📊 可视化图表 | 支持 Mermaid 图表生成 | "帮我画一个流程图" |
|
||
|
||
### 多模型切换
|
||
|
||
1. 在左侧边栏选择模型:
|
||
- **智谱 GLM-4**:在线服务,速度快
|
||
- **DeepSeek V3**:深度推理模型
|
||
- **OpenAI GPT-4o-mini**:通用对话模型
|
||
- **本地 Qwen3.5-9B**:本地部署,隐私性好
|
||
|
||
2. 可随时切换,甚至在同一会话中
|
||
|
||
3. 点击 "🔄 新会话" 清空当前对话
|
||
|
||
---
|
||
|
||
## 🔧 开发指南
|
||
|
||
### 添加新工具
|
||
|
||
在 [backend/app/main_graph/tools/common_tools.py](file:///home/huang/Study/AIProject/Agent1/backend/app/main_graph/tools/common_tools.py) 中添加新的 `@tool` 装饰函数:
|
||
|
||
```python
|
||
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.py](file:///home/huang/Study/AIProject/Agent1/backend/app/main_graph/tools/graph_tools.py) 的 `AVAILABLE_TOOLS` 列表中注册。
|
||
|
||
### 添加新模型
|
||
|
||
在 [backend/app/model_services/chat_services.py](file:///home/huang/Study/AIProject/Agent1/backend/app/model_services/chat_services.py) 中添加新的服务提供者:
|
||
|
||
```python
|
||
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` 字典中注册:
|
||
|
||
```python
|
||
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` 中定义:
|
||
|
||
```python
|
||
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` 中实现节点函数:
|
||
|
||
```python
|
||
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` 中构建:
|
||
|
||
```python
|
||
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](file:///home/huang/Study/AIProject/Agent1/backend/app/main_graph/tools/subgraph_tools.py) 中添加子图调用工具:
|
||
|
||
```python
|
||
@tool
|
||
async def my_subgraph_tool(query: str) -> str:
|
||
"""
|
||
我的子图工具描述
|
||
|
||
Args:
|
||
query: 用户查询
|
||
|
||
Returns:
|
||
子图执行结果
|
||
"""
|
||
# 调用子图逻辑
|
||
return result
|
||
```
|
||
|
||
#### 6. 在 React Reason 中添加路由
|
||
|
||
在 [backend/app/core/intent.py](file:///home/huang/Study/AIProject/Agent1/backend/app/core/intent.py) 的 `react_reason_async` 函数中添加对子图工具的支持。
|
||
|
||
### Docker 部署
|
||
|
||
项目包含完整的 Docker 配置:
|
||
|
||
- **docker-compose.yml**:服务编排(Backend + Frontend,连接远程数据库)
|
||
- **docker/Dockerfile.backend**:后端镜像构建
|
||
- **docker/Dockerfile.frontend**:前端镜像构建
|
||
- **.gitea/workflows/deploy.yml**:CI/CD 自动化部署
|
||
|
||
详见 [QUICKSTART.md](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)
|
||
- ⚠️ **修改后重启**:修改 `.env` 后,Docker 部署需要执行 `docker compose down && docker compose up -d --build`
|
||
|
||
---
|
||
|
||
## 🔍 故障排查
|
||
|
||
### 常见问题
|
||
|
||
**Q: 无法连接远程数据库?**
|
||
```bash
|
||
# 测试 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](QUICKSTART.md)
|
||
|
||
---
|
||
|
||
## 📝 许可证
|
||
|
||
本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。
|
||
|
||
## 🤝 贡献
|
||
|
||
欢迎提交 Issue 和 Pull Request!
|