refactor: 重构目录结构 - 简化层级
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Has been cancelled

This commit is contained in:
2026-04-29 12:52:41 +08:00
parent 223d1c9afd
commit ef5113bffb
54 changed files with 42 additions and 1819 deletions

View File

@@ -3,6 +3,6 @@ AI Agent 应用模块
""" """
from .agent.service import AIAgentService from .agent.service import AIAgentService
from .graph.graph_tools import AVAILABLE_TOOLS, TOOLS_BY_NAME from app.main_graph.graph_tools import AVAILABLE_TOOLS, TOOLS_BY_NAME
__all__ = ["AIAgentService", "AVAILABLE_TOOLS", "TOOLS_BY_NAME"] __all__ = ["AIAgentService", "AVAILABLE_TOOLS", "TOOLS_BY_NAME"]

View File

@@ -1,242 +0,0 @@
# backend/app/agent/hybrid_router.py
from enum import Enum
from typing import Optional, List, Dict, Any
from dataclasses import dataclass
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from app.agent.intent_classifier import IntentClassifier, IntentType, get_intent_classifier
class RouterAction(Enum):
"""路由动作"""
FAST_RAG = "fast_rag" # 快速 RAG 路径
FAST_TOOL = "fast_tool" # 快速工具路径
REACT_LOOP = "react_loop" # React 循环路径
DIRECT_ANSWER = "direct_answer" # 直接回答
CLARIFY = "clarify" # 澄清反问
@dataclass
class RouterDecision:
"""路由决策结果"""
action: RouterAction
intent: IntentType
confidence: float
reasoning: str
metadata: Dict[str, Any] = None
class HybridRouter:
"""混合路由决策器"""
def __init__(
self,
intent_classifier: IntentClassifier,
rag_pipeline = None,
tool_registry: Dict[str, Any] = None,
react_graph = None
):
self.classifier = intent_classifier
self.rag = rag_pipeline
self.tools = tool_registry or {}
self.react_graph = react_graph
async def route(self, user_input: str, context: Optional[str] = None) -> RouterDecision:
"""
路由决策
Args:
user_input: 用户输入
context: 对话上下文
Returns:
RouterDecision
"""
# 1. 意图分类
intent_result = await self.classifier.classify(user_input, context)
# 2. 根据意图路由
decision = self._make_decision(intent_result, user_input)
return decision
def _make_decision(self, intent_result: IntentResult, user_input: str) -> RouterDecision:
"""根据意图做出路由决策"""
intent = intent_result.intent_type
confidence = intent_result.confidence
# 低置信度 → 走 React 循环(更安全)
if confidence < 0.6:
return RouterDecision(
action=RouterAction.REACT_LOOP,
intent=intent,
confidence=confidence,
reasoning=f"置信度 {confidence:.2f} 较低,走 React 循环"
)
# 根据意图路由
routing_map = {
IntentType.KNOWLEDGE: RouterAction.FAST_RAG,
IntentType.REALTIME: RouterAction.FAST_TOOL,
IntentType.ACTION: RouterAction.FAST_TOOL,
IntentType.CHITCHAT: RouterAction.DIRECT_ANSWER,
IntentType.CLARIFY: RouterAction.CLARIFY,
IntentType.MIXED: RouterAction.REACT_LOOP,
IntentType.UNKNOWN: RouterAction.REACT_LOOP,
}
action = routing_map.get(intent, RouterAction.REACT_LOOP)
return RouterDecision(
action=action,
intent=intent,
confidence=confidence,
reasoning=intent_result.reasoning
)
async def execute(self, decision: RouterDecision, user_input: str, thread_id: str) -> str:
"""
根据决策执行对应路径
Args:
decision: 路由决策
user_input: 用户输入
thread_id: 线程 ID
Returns:
最终答案
"""
if decision.action == RouterAction.FAST_RAG:
return await self._execute_fast_rag(user_input)
elif decision.action == RouterAction.FAST_TOOL:
return await self._execute_fast_tool(user_input)
elif decision.action == RouterAction.DIRECT_ANSWER:
return await self._execute_direct_answer(user_input)
elif decision.action == RouterAction.CLARIFY:
return await self._execute_clarify(user_input)
elif decision.action == RouterAction.REACT_LOOP:
return await self._execute_react_loop(user_input, thread_id)
else:
return await self._execute_react_loop(user_input, thread_id)
async def _execute_fast_rag(self, user_input: str) -> str:
"""快速 RAG 路径"""
print("🚀 执行快速 RAG 路径")
# 1. 检索文档(如果 RAG 可用)
docs = []
if self.rag and hasattr(self.rag, 'aretrieve'):
docs = await self.rag.aretrieve(user_input)
# 2. 格式化上下文
context = ""
if self.rag and hasattr(self.rag, 'format_context'):
context = self.rag.format_context(docs)
# 3. 生成回答
prompt = f"""
请根据以下文档回答用户问题。
参考文档:
{context or "(无文档)"}
用户问题: {user_input}
"""
response = await self.classifier.llm.ainvoke(prompt)
return response.content
async def _execute_fast_tool(self, user_input: str) -> str:
"""快速工具路径"""
print("🚀 执行快速工具路径")
# 这里简化处理,实际项目中:
# 1. 解析需要调用的工具
# 2. 生成工具参数
# 3. 执行工具
# 4. 生成回答
return "快速工具路径:功能开发中..."
async def _execute_direct_answer(self, user_input: str) -> str:
"""直接回答路径"""
print("💬 执行直接回答路径")
prompt = f"""
用户说: {user_input}
请友好回应。
"""
response = await self.classifier.llm.ainvoke(prompt)
return response.content
async def _execute_clarify(self, user_input: str) -> str:
"""澄清反问路径"""
print("❓ 执行澄清反问路径")
prompt = f"""
用户说: {user_input}
用户的问题不太明确,请礼貌地询问更多细节。
"""
response = await self.classifier.llm.ainvoke(prompt)
return response.content
async def _execute_react_loop(self, user_input: str, thread_id: str) -> str:
"""React 循环路径"""
print("🔄 执行 React 循环路径")
# 这里调用现有的完整 LangGraph 流程
# 具体实现根据您的项目结构
return "React 循环路径:调用现有 LangGraph..."
# 便捷函数
async def hybrid_agent_route(
user_input: str,
thread_id: str,
context: Optional[str] = None
) -> str:
"""
混合 Agent 路由入口函数
Args:
user_input: 用户输入
thread_id: 线程 ID
context: 对话上下文
Returns:
最终答案
"""
# 获取依赖(实际项目应该用依赖注入)
classifier = get_intent_classifier()
# rag = get_rag_pipeline()
# tools = get_tool_registry()
# graph = get_react_graph()
# 创建路由器
router = HybridRouter(
intent_classifier=classifier,
rag_pipeline=None, # 实际项目中传入
tool_registry={}, # 实际项目中传入
react_graph=None # 实际项目中传入
)
# 路由决策
decision = await router.route(user_input, context)
print(f"🧭 路由决策: {decision.action} (意图: {decision.intent}, 置信度: {decision.confidence:.2f})")
print(f"📝 推理: {decision.reasoning}")
# 执行
# result = await router.execute(decision, user_input, thread_id)
# return result
# 临时返回
return f"路由决策: {decision.action}"

View File

@@ -7,8 +7,8 @@ import json
import asyncio import asyncio
# 本地模块 # 本地模块
from ..graph.graph_builder import GraphBuilder, GraphContext from app.main_graph.graph_builder import GraphBuilder, GraphContext
from ..graph.graph_tools import AVAILABLE_TOOLS, TOOLS_BY_NAME from app.main_graph.graph_tools import AVAILABLE_TOOLS, TOOLS_BY_NAME
from ..model_services.chat_services import get_all_chat_services, LocalVLLMChatProvider from ..model_services.chat_services import get_all_chat_services, LocalVLLMChatProvider
from .rag_initializer import init_rag_tool from .rag_initializer import init_rag_tool
from .intent_classifier import get_intent_classifier from .intent_classifier import get_intent_classifier

View File

@@ -1,744 +0,0 @@
# LangGraph Agent - 全能个人助手系统
该模块负责将个人助手从"查询型"升级为"全能执行型",通过 LangGraph 子图架构实现通讯录管理、智能词典、增强研究分析等核心功能。
---
## 🔧 公共工具与共享组件
三个子图共享一套统一的基础设施和通用工具,避免重复实现,确保架构一致。
### 公共工具架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 公共工具层 (Shared Tools) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │意图理解工具 │ │人工审核工具 │ │格式化输出 │ │状态管理│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │检查点持久化 │ │条件路由 │ │LLM 调用 │ │数据库 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 子图层 (Subgraphs) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 通讯录子图 │ │ 智能词典子图│ │ 研究分析子图│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 公共工具详解
#### 1. 意图理解工具 (Intent Understanding Tool)
**技术栈**LangChain LLM + 少样本提示词 + 结构化输出
**使用方法**
```python
from agent_subgraphs.common.intent import parse_intent
result = parse_intent(
user_input="添加张三电话13800138000",
intent_categories=["contact", "dictionary", "research", "chat"]
)
# 返回: {
# "intent_type": "contact",
# "sub_intent": "add",
# "extracted_info": {"name": "张三", "phone": "13800138000"}
# }
```
**实现逻辑**
- 使用 LLM 进行意图分类,输出结构化 JSON
- 关键词匹配兜底(如"保存"、"添加" → contact
- 自动提取关键信息(实体、参数)
- 支持自定义意图分类器
**所有子图共用**:在 `agent_subgraphs/common/intent.py`
---
#### 2. 人工审核工具 (Human-in-the-loop Tool)
**技术栈**LangGraph `interrupt()` + 状态持久化
**使用方法**
```python
from agent_subgraphs.common.human_loop import human_approval_node
def email_send_workflow(state: dict) -> dict:
# 生成邮件草稿...
state["pending_action"] = {"type": "email_send", "draft": draft}
return human_approval_node(state) # 挂起等待用户确认
```
**实现逻辑**
- 使用 `interrupt()` 触发挂起
- 支持三种操作:确认、修改、取消
- 超时自动取消(可配置超时时间)
- 用户反馈结构化存储
- 支持断点续作
**共享位置**`agent_subgraphs/common/human_loop.py`
**应用场景**
- 通讯录:邮件发送、联系人删除
- 研究分析:信息源确认、报告结构调整
- 智能词典:批量删除确认
---
#### 3. 格式化输出工具 (Formatting Tool)
**技术栈**Jinja2 模板 + Markdown 生成
**使用方法**
```python
from agent_subgraphs.common.format import format_output
result = format_output(
template="contact_result",
data={"contact": contact_data, "action": "added"}
)
```
**实现逻辑**
- 预定义输出模板库
- 支持 Markdown、HTML、纯文本三种格式
- 自动处理列表、表格等复杂结构
- 支持用户自定义模板
**共享位置**`agent_subgraphs/common/format.py`
---
#### 4. 检查点持久化工具 (Checkpoint Tool)
**技术栈**LangGraph CheckpointSaver + SQLite / PostgreSQL
**使用方法**
```python
from agent_subgraphs.common.checkpoint import get_checkpointer
checkpointer = get_checkpointer(db_url="sqlite:///checkpoints.db")
# 在图编译时传入
graph = workflow.compile(checkpointer=checkpointer)
```
**实现逻辑**
- 自动在每个节点执行后保存检查点
- 支持断点续作
- 状态版本管理
- 支持查询历史状态
**共享位置**`agent_subgraphs/common/checkpoint.py`
---
#### 5. 条件路由工具 (Conditional Routing Tool)
**技术栈**LangGraph 条件边 + 路由函数
**使用方法**
```python
from agent_subgraphs.common.routing import route_by_intent
def my_router(state: dict) -> str:
return route_by_intent(
state,
{
"translate": "translate_node",
"lookup": "lookup_node",
}
)
```
**实现逻辑**
- 标准化的意图路由
- 支持复杂条件判断
- 路由函数可组合
- 自动处理默认分支
**共享位置**`agent_subgraphs/common/routing.py`
---
#### 6. LLM 调用工具 (LLM Invocation Tool)
**技术栈**LangChain ChatOpenAI + 重试 + 流式输出
**使用方法**
```python
from agent_subgraphs.common.llm import call_llm, call_llm_stream
# 同步调用
result = call_llm(prompt="翻译这句话", system_prompt="你是翻译官")
# 流式调用
async for chunk in call_llm_stream(prompt="写一篇文章"):
print(chunk.content, end="")
```
**实现逻辑**
- 统一的 LLM 调用接口
- 自动重试(指数退避)
- 支持流式和非流式
- Token 计数和监控
- 模型 fallback 机制
**共享位置**`agent_subgraphs/common/llm.py`
---
#### 7. 数据库工具 (Database Tool)
**技术栈**SQLAlchemy + PostgreSQL / SQLite
**使用方法**
```python
from agent_subgraphs.common.db import get_db, add_record, query_records
# 获取 session
with get_db() as db:
# 添加记录
add_record(db, Contact, {"name": "张三", "phone": "13800138000"})
# 查询记录
contacts = query_records(db, Contact, filters={"name": "张三"})
```
**实现逻辑**
- 统一的 ORM 会话管理
- 标准 CRUD 操作封装
- 支持事务
- 自动错误处理
**共享位置**`agent_subgraphs/common/db.py`
---
#### 8. 状态基类工具 (State Base Classes)
**技术栈**TypedDict + 类型注解
**使用方法**
```python
from agent_subgraphs.common.state import BaseSubgraphState
class ContactState(BaseSubgraphState):
# 继承基础字段
contact_data: Optional[dict]
# 自定义字段...
```
**实现逻辑**
- 预定义基础状态字段
- 类型安全
- 状态验证
- 状态合并辅助函数
**共享位置**`agent_subgraphs/common/state.py`
---
### 公共工具目录结构
```
agent_subgraphs/
├── common/ # 公共工具层
│ ├── __init__.py
│ ├── intent.py # 意图理解工具
│ ├── human_loop.py # 人工审核工具
│ ├── format.py # 格式化输出工具
│ ├── checkpoint.py # 检查点持久化工具
│ ├── routing.py # 条件路由工具
│ ├── llm.py # LLM 调用工具
│ ├── db.py # 数据库工具
│ ├── state.py # 状态基类
│ └── prompts/ # 公共提示词模板
│ ├── intent.j2
│ └── format.j2
├── contact/ # 通讯录子图(简化版,使用公共工具)
├── dictionary/ # 智能词典子图(简化版,使用公共工具)
└── research/ # 研究分析子图(简化版,使用公共工具)
```
---
### 公共配置项
所有子图共享的配置项(在 `.env` 中配置):
```env
# LLM 配置
LLM_PROVIDER=deepseek
LLM_MODEL=deepseek-chat
LLM_API_KEY=***
LLM_TEMPERATURE=0.7
LLM_MAX_TOKENS=2000
# 数据库配置
DB_URL=postgresql://user:pass@localhost/agent_db
# 检查点配置
CHECKPOINT_DB_URL=sqlite:///checkpoints.db
# 人工审核配置
HUMAN_LOOP_TIMEOUT=3600
# 格式化配置
DEFAULT_OUTPUT_FORMAT=markdown
```
---
### 使用公共工具开发新子图
创建新子图的步骤:
1. **继承状态基类**
```python
from agent_subgraphs.common.state import BaseSubgraphState
class MySubgraphState(BaseSubgraphState):
my_field: str
```
2. **使用公共意图理解**
```python
from agent_subgraphs.common.intent import parse_intent
def intent_node(state: MySubgraphState) -> MySubgraphState:
result = parse_intent(state["user_input"], ["my_intents])
state.update(result)
return state
```
3. **使用公共人工审核**
```python
from agent_subgraphs.common.human_loop import human_approval_node
def my_workflow(state: MySubgraphState) -> MySubgraphState:
return human_approval_node(state)
```
4. **使用公共格式化输出**
```python
from agent_subgraphs.common.format import format_output
def format_node(state: MySubgraphState) -> MySubgraphState:
state["final_result"] = format_output("my_template", state)
return state
```
---
## 📑 目录导航
- [核心功能](#-核心功能) - 三大子图功能和技术特性
- [技术架构](#-技术架构) - 技术栈、子图架构图、数据流
- [子图设计](#-子图设计) - 三个核心子图的详细说明
- [状态管理](#-状态管理) - 主状态和子状态定义
- [安全与边界](#-安全与边界) - 安全机制和边界控制
- [快速开始](#-快速开始) - 开发环境搭建
- [实现指南](#-实现指南) - 子图开发、工具集成
- [未来规划](#-未来规划) - 多模态、流式输出等
---
## 🎯 核心功能
### 三大核心子图
| 子图模块 | 功能概述 | 关键特性 |
|---------|---------|---------|
| **通讯录子图** | 联系人 CRUD、邮件读取与审核、外发邮件、智能嗅探 | 人工审核强制、敏感信息加密、IMAP 邮箱绑定 |
| **智能词典子图** | 翻译、查词、每日一词、专业名词提炼、生词本管理 | 联想记忆法、艾宾浩斯遗忘曲线、Anki 导出 |
| **研究分析子图** | 联网搜索、报告生成、引用溯源、可视化图表 | 人工干预点、可定制报告结构、引用验证 |
### 通用机制
-**人工审核Human-in-the-loop**:通用节点,用于邮件发送、重要操作前的确认
-**自动联系人嗅探**:对话中出现"人名+联系方式"时,主动询问是否保存
-**流式输出**:所有 LLM 生成内容均采用流式传输,提升交互体验
-**长期记忆**PostgreSQL + pgvector 实现持久化记忆和语义检索
---
## 🏗️ 技术架构
### 技术栈总览
| 层级 | 组件 | 技术选型 | 版本 | 说明 |
|------|------|---------|------|------|
| **Agent 框架** | 工作流编排 | LangGraph + LangChain | latest | 子图架构,状态机驱动 |
| **LLM 服务** | 模型调用 | 智谱 AI / DeepSeek / 本地模型 | latest | 多模型路由 |
| **向量数据库** | 语义检索 | Qdrant / pgvector | v1.12+ | 对话记忆、联系人语义索引 |
| **关系数据库** | 结构化存储 | PostgreSQL | v16 | 联系人、生词本、邮件配置 |
| **邮件协议** | IMAP/SMTP | `imaplib` / `smtplib` | 内置 | 邮件读取和发送 |
| **后端框架** | API 服务 | FastAPI + Uvicorn | v0.115+ | 子图执行、状态管理 |
### 主图架构流程图
```mermaid
graph TB
Start[START] --> Intent[意图分类节点]
Intent -->|contact| ContactSubgraph[通讯录子图]
Intent -->|dictionary| DictSubgraph[智能词典子图]
Intent -->|research| ResearchSubgraph[研究分析子图]
Intent -->|chat| ChatNode[普通对话节点]
ContactSubgraph --> Final[最终响应]
DictSubgraph --> Final
ResearchSubgraph --> Final
ChatNode --> Final
Final --> End[END]
style Start fill:#e1f5ff
style Intent fill:#fff4e1
style ContactSubgraph fill:#e8f5e9
style DictSubgraph fill:#f3e5f5
style ResearchSubgraph fill:#ffebee
style Final fill:#fff9c4
```
### 子图架构总览
```mermaid
graph TB
MainGraph[主图<br/>MainState]
MainGraph --> ContactSub[通讯录子图<br/>ContactSubState]
MainGraph --> DictSub[词典子图<br/>DictSubState]
MainGraph --> ResearchSub[研究子图<br/>ResearchSubState]
ContactSub --> ContactNodes[内部节点<br/>parse_intent<br/>add_contact<br/>list_contacts<br/>generate_draft<br/>human_approval<br/>send_email]
DictSub --> DictNodes[内部节点<br/>translate<br/>lookup<br/>extract_terms<br/>daily_word]
ResearchSub --> ResearchNodes[内部节点<br/>decompose<br/>web_search<br/>extract_info<br/>generate_report]
ContactNodes --> PG[(PostgreSQL<br/>联系人)]
DictNodes --> PG2[(PostgreSQL<br/>生词本)]
ResearchNodes --> Qdrant[(Qdrant<br/>向量检索)]
style MainGraph fill:#e1f5ff
style ContactSub fill:#e8f5e9
style DictSub fill:#f3e5f5
style ResearchSub fill:#ffebee
```
### 状态传递机制
```
主状态 (MainState)
├─→ input: {user_input: messages[-1].content}
子图状态 (SubState)
├─→ 内部处理...
output: {messages: [AIMessage(final_result)], last_action: sub_state}
主状态更新
```
---
## 📂 项目结构
```
Agent1/
├── agent_subgraphs/ # LangGraph 子图模块
│ ├── __init__.py
│ ├── contact/ # 通讯录子图
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── state.py # 子图状态定义
│ │ ├── graph.py # 子图构建器
│ │ ├── nodes.py # 子图节点实现
│ │ ├── tools.py # 子图工具集
│ │ └── prompts.py # 提示词模板
│ ├── dictionary/ # 智能词典子图
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── state.py
│ │ ├── graph.py
│ │ ├── nodes.py
│ │ ├── tools.py
│ │ └── prompts.py
│ └── research/ # 研究分析子图
│ ├── README.md
│ ├── __init__.py
│ ├── state.py
│ ├── graph.py
│ ├── nodes.py
│ ├── tools.py
│ └── prompts.py
├── backend/ # 现有后端模块
│ └── app/
│ ├── graph/ # 主图构建
│ │ ├── graph_builder.py # 主图 + 子图集成
│ │ └── state.py # 主状态定义
│ └── ...
└── ...
```
---
## 🎯 状态管理
### 主状态定义 (MainState)
```python
from typing import TypedDict, List, Optional, Annotated
from langchain_core.messages import BaseMessage
from langgraph.graph import add_messages
class MainState(TypedDict):
messages: Annotated[List[BaseMessage], add_messages]
intent: str # 顶层意图contact / dictionary / research / chat
sub_intent: str # 细分意图
# 工具通用
tool_call: Optional[dict] # 当前要执行的工具调用
tool_result: str
# 控制流
next_node: str
human_feedback: Optional[str] # 用于挂起重启
# 子图结果
last_contact_action: Optional[dict]
last_dict_action: Optional[dict]
last_research_action: Optional[dict]
```
### 状态映射函数
```python
# 通讯录子图输入映射
def contact_input_mapper(state: MainState) -> dict:
return {
"user_input": state["messages"][-1].content,
}
# 通讯录子图输出映射
def contact_output_mapper(sub_state: dict, original: MainState) -> dict:
return {
"messages": [AIMessage(content=sub_state["final_result"])],
"last_contact_action": sub_state,
}
```
---
## 🎯 主图实现
### 主图构建器
```python
from langgraph.graph import StateGraph, START, END
from agent_subgraphs.contact import build_contact_subgraph
from agent_subgraphs.dictionary import build_dict_subgraph
from agent_subgraphs.research import build_research_subgraph
def build_main_graph() -> StateGraph:
main_graph = StateGraph(MainState)
# 添加子图节点
main_graph.add_node(
"contact_module",
build_contact_subgraph(),
input=contact_input_mapper,
output=contact_output_mapper,
)
main_graph.add_node(
"dict_module",
build_dict_subgraph(),
input=dict_input_mapper,
output=dict_output_mapper,
)
main_graph.add_node(
"research_module",
build_research_subgraph(),
input=research_input_mapper,
output=research_output_mapper,
)
main_graph.add_node("chat_node", chat_node)
main_graph.add_node("final_response", final_response_node)
# 主图路由
main_graph.add_conditional_edges(
START,
route_main_intent,
{
"contact": "contact_module",
"dictionary": "dict_module",
"research": "research_module",
"chat": "chat_node",
}
)
# 所有路径汇聚到最终响应
main_graph.add_edge("contact_module", "final_response")
main_graph.add_edge("dict_module", "final_response")
main_graph.add_edge("research_module", "final_response")
main_graph.add_edge("chat_node", "final_response")
main_graph.add_edge("final_response", END)
return main_graph.compile()
def route_main_intent(state: MainState) -> str:
"""顶层意图分类"""
user_input = state["messages"][-1].content
# 使用 LLM 分类
# 返回 contact / dictionary / research / chat
```
---
## 🔒 安全与边界
### 安全机制
| 风险点 | 对策 | 实现位置 |
|--------|------|---------|
| 邮箱密码泄露 | 使用环境变量存储,永不写入日志 | `agent_subgraphs/contact/tools.py` |
| 恶意邮件发送 | 强制人工审核,单日发送数量限制 | 人工审核节点 |
| 联系人隐私 | 向量检索结果脱敏展示,仅交互时完整显示 | 通讯录子图节点 |
| 搜索内容安全 | 过滤成人内容,屏蔽高风险域名 | 研究子图搜索工具 |
| 敏感词输出 | 流式输出后处理过滤 | 最终响应节点 |
### 人工审核节点
```python
from langgraph.graph import interrupt
def human_approval_node(state: dict) -> dict:
"""人工审核节点:挂起图,等待外部输入"""
pending_action = state["pending_action"]
# 触发中断,等待外部通过 update_state 传入 human_feedback
return interrupt(
{
"type": "human_approval",
"message": f"请确认操作:{pending_action}",
"options": ["确认", "修改", "取消"],
}
)
```
---
## 🚀 快速开始
### 环境配置
```bash
# 激活虚拟环境
source venv/bin/activate
# 安装依赖(新增)
pip install langgraph langchain-experimental
```
### 配置文件
`.env` 中新增:
```env
# 通讯录模块
IMAP_SERVER=imap.qq.com
IMAP_PORT=993
SMTP_SERVER=smtp.qq.com
SMTP_PORT=587
EMAIL_USER=your_email@qq.com
EMAIL_PASSWORD=your_auth_code
# 词典模块
DEEPL_API_KEY=your_deepl_key
YOUDAO_API_KEY=your_youdao_key
# 研究模块
SEARCH_API_KEY=your_search_key
```
### 运行测试
```python
from backend.app.graph.graph_builder import build_main_graph
# 编译主图
graph = build_main_graph()
# 调用子图
result = graph.invoke({
"messages": [HumanMessage(content="添加张三电话 13800138000")]
})
print(result["messages"][-1].content)
```
---
## 📖 实现指南
### 添加新子图
1.`agent_subgraphs/` 下创建新文件夹
2. 创建 `state.py` 定义子图状态
3. 创建 `nodes.py` 实现子图节点
4. 创建 `graph.py` 构建子图
5. 在主图 `graph_builder.py` 中注册新子图
### 子图开发最佳实践
- ✅ 子图拥有独立的状态,避免与主状态命名冲突
- ✅ 使用 `input`/`output` 映射函数进行状态转换
- ✅ 子图内部节点之间直接连接,主图只负责路由到子图入口
- ✅ 将工具封装在子图内部,通过节点调用
- ✅ 提供子图级别的单元测试
---
## 🎯 未来规划
### Phase 1: 基础功能(当前)
- [ ] 通讯录子图完整实现
- [ ] 智能词典子图完整实现
- [ ] 研究分析子图完整实现
- [ ] 主图集成和状态管理
### Phase 2: 增强功能
- [ ] 流式输出改造(所有 LLM 调用)
- [ ] 多模态语音输入STT
- [ ] 可视化图表生成
- [ ] Anki 生词本导出
### Phase 3: 高级特性
- [ ] 智能家居控制子图
- [ ] 日程管理子图
- [ ] 更多工具集成
---
## 📚 相关文档
- [通讯录子图 README](./contact/README.md) - 详细了解通讯录模块
- [智能词典子图 README](./dictionary/README.md) - 详细了解词典模块
- [研究分析子图 README](./research/README.md) - 详细了解研究模块
- [主项目 README](../README.md) - 了解整体架构

View File

@@ -1,72 +0,0 @@
"""
公共工具模块
提供可复用的基础组件
导出:
- formatter.py: 格式化输出工具
- intent.py: 意图理解工具
- human_review.py: 人工审核工具
- state_base.py: 状态基类工具
"""
from .formatter import (
MarkdownFormatter,
TemplateManager,
OutputRenderer,
PresetTemplates
)
from .intent import (
# React 模式 API
ReasoningAction,
RetrievalConfig,
ReasoningResult,
ReactIntentReasoner,
react_reason,
react_reason_async,
get_route_by_reasoning
)
from .human_review import (
ReviewStatus,
HumanReview,
HumanReviewStore,
InMemoryReviewStore,
HumanReviewNode,
ReviewManager
)
from .state_base import (
BaseState,
Phase,
TokenUsage,
StateUtils
)
__all__ = [
# formatter
"MarkdownFormatter",
"TemplateManager",
"OutputRenderer",
"PresetTemplates",
# intent - React 模式
"ReasoningAction",
"RetrievalConfig",
"ReasoningResult",
"ReactIntentReasoner",
"react_reason",
"react_reason_async",
"get_route_by_reasoning",
# human_review
"ReviewStatus",
"HumanReview",
"HumanReviewStore",
"InMemoryReviewStore",
"HumanReviewNode",
"ReviewManager",
# state_base
"BaseState",
"Phase",
"TokenUsage",
"StateUtils"
]

View File

@@ -1,633 +0,0 @@
# 研究分析子图 (Research Analysis Subgraph)
该子图负责处理研究分析相关的请求,基于 LangGraph 状态机编排多阶段研究流水线,支持联网搜索、信息提取与验证、结构化报告生成等功能。子图设计遵循"可中断、可恢复、质量优先、透明可追溯"原则,通过内置人工干预点和多源交叉验证保障输出质量。
> **使用公共工具**意图理解、人工审核、格式化输出、检查点持久化、条件路由、LLM 调用、数据库工具、状态基类
---
## 🎯 核心架构
### 技术栈
| 层级 | 组件 | 说明 |
|:-----|:-----|:-----|
| **编排框架** | LangGraph StateGraph | 状态机驱动的子图工作流编排,支持条件路由与中断恢复 |
| **LLM 服务** | 智谱 AI / DeepSeek API | 意图理解、任务分解、信息提取、报告生成等认知任务(使用公共 LLM 工具) |
| **向量检索** | Qdrant / pgvector | 历史研究结果语义检索,实现记忆增强研究 |
| **关系存储** | PostgreSQL | 研究项目、报告版本、引用记录持久化(使用公共数据库工具) |
| **搜索服务** | 多源搜索 API 网关 | 统一接入通用搜索、学术数据库、专业知识库等外部信息源 |
| **图表生成** | 图表服务 | 趋势图、对比图、分布图等可视化图表自动生成 |
### 子图分层架构
子图采用分层设计,各层职责清晰、边界明确,便于独立测试与演化。
```
┌─────────────────────────────────────────────────────────────────┐
│ 主图 (Main Graph) │
└──────────────────────────────┬──────────────────────────────────┘
│ 状态映射 / 结果聚合
┌─────────────────────────────────────────────────────────────────┐
│ 研究分析子图接口层 │
│ - 状态转换:主状态 ↔ 子图状态(使用公共状态基类) │
│ - 错误传播与优雅降级 │
└──────────────────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 工作流编排层 │
│ - 节点调度与条件路由(使用公共路由工具) │
│ - 人工干预点暂停/恢复管理(使用公共审核工具) │
│ - 状态持久化与检查点(使用公共检查点工具) │
└──────────────────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 节点层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │意图理解 │ │任务分解 │ │搜索执行 │ │信息提取 │ │报告生成│ │
│ │(公共工具)│ │ │ │ │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │信息源筛选│ │信息整合 │ │结构生成 │ │人工审核 │ │
│ │ │ │ │ │ │ │(公共) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 工具层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │搜索工具集│ │提取工具集│ │验证工具集│ │图表工具集│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 数据流总览
研究分析子图的数据流向遵循"输入 → 理解 → 检索 → 处理 → 整合 → 生成 → 审核 → 输出"的线性推进路径,每个阶段的状态变更均被完整记录,支持断点续作。
```
用户研究请求
┌─────────────┐
│ 意图理解 │ ← 使用公共意图理解工具
└──────┬──────┘
└─────┐
┌─────────────┐
│ 任务分解 │
└──────┬──────┘
┌─────────────┐ ┌─────────────┐
│ 多源搜索 │────▶│ 信息源筛选 │
└─────────────┘ └──────┬──────┘
┌─────────────┐ ┌─────────────┐
│ 报告生成 │◀────│ 报告结构确认│
└──────┬──────┘ └─────────────┘
┌─────────────┐
│ 格式输出 │ ← 使用公共格式化工具
└──────┬──────┘
返回主图
```
---
## 📂 模块与文件结构
子图代码组织遵循 LangGraph 最佳实践,核心模块职责分明。
```
app/research/
├── __init__.py
├── graph.py # 子图构建入口,定义状态图与路由
├── state.py # 子图状态定义(继承公共状态基类)
├── nodes/ # 节点实现
│ ├── __init__.py
│ ├── decomposition.py # 任务分解节点
│ ├── search.py # 搜索执行节点
│ ├── extraction.py # 信息提取节点
│ ├── integration.py # 信息整合与冲突检测节点
│ ├── structure.py # 报告结构生成节点
│ └── generation.py # 报告生成节点
├── tools/ # 子图特有工具集
│ ├── search/ # 搜索工具集
│ ├── extract/ # 信息提取工具集
│ ├── verify/ # 信息验证工具集
│ └── chart/ # 图表生成工具集
└── persistence/ # (使用公共检查点工具,无需单独实现)
```
> **注意**:以下模块使用公共工具,无需单独实现:
> - 意图理解节点 → 使用 `agent_subgraphs.common.intent`
> - 人工审核节点 → 使用 `agent_subgraphs.common.human_loop`
> - 格式输出节点 → 使用 `agent_subgraphs.common.format`
> - 检查点持久化 → 使用 `agent_subgraphs.common.checkpoint`
> - 条件路由 → 使用 `agent_subgraphs.common.routing`
> - LLM 调用 → 使用 `agent_subgraphs.common.llm`
> - 数据库操作 → 使用 `agent_subgraphs.common.db`
---
## 🎯 演进路线与核心机制
研究分析子图的能力演进分为五个层级,每一层在上一层的基座上叠加更复杂的认知与决策能力。
### Level 1基础单轮研究流水线
**核心机制**:单一意图理解 → 单次搜索 → 简单提取 → 直接报告生成。
- 使用 LLM 解析用户请求,生成搜索关键词。
- 调用通用搜索引擎获取原始结果。
- 基于规则或轻量模型提取关键信息片段。
- 按照预设模板生成 Markdown 格式报告。
**适用场景**:快速知识查询、简单概念解释。
**实现指引**:子图默认配置为完整流水线的最小化变体,通过路由条件识别“简单查询”后走该分支。
### Level 2多阶段搜索与信息验证
**核心机制**:引入“广度→深度→验证”三轮搜索策略,叠加信息冲突检测与多源交叉验证。
- **第一轮广度搜索**:使用主要关键词并行查询多个信息源,获取广泛上下文。
- **第二轮深度搜索**:基于首轮结果识别出的关键话题,生成细化关键词进行深入检索。
- **第三轮验证搜索**:针对关键事实(如时间、数据、人名)执行验证性搜索,对比多个独立来源。
- **冲突检测**:自动识别不同来源对同一事实的冲突描述,按严重程度分级标记。
- **交叉验证评分**:对每条关键信息计算多源一致性得分,低分项触发人工审核或补充搜索。
**适用场景**:市场调研、事件分析、学术资料整理等对信息准确性要求较高的任务。
**实现指引**:搜索执行节点内部维护搜索轮次状态,每一轮均可动态调整关键词;信息整合节点输出冲突报告。
### Level 3人工干预点与动态路由
**核心机制**:在关键决策点(信息源确认、报告结构调整)插入人工审核节点,子图挂起等待用户反馈,并根据反馈动态调整后续路由。
- **信息源筛选审核**:自动筛选后的信息源列表(含可信度、时效性评分)展示给用户,用户可增删或调整优先级。
- **报告结构审核**:生成的报告大纲与可视化建议展示给用户,用户可调整章节顺序、增删模块。
- **路由反馈闭环**
- 用户要求添加信息源 → 返回搜索执行节点补充检索。
- 用户调整报告结构 → 重新进入结构生成节点。
- 关键信息冲突且用户无法裁决 → 返回搜索执行节点补充验证来源。
**适用场景**:专业研究报告、决策支持分析等需要人类专家介入的场景。
**实现指引**:人工审核节点使用 LangGraph 的 `interrupt` 机制实现状态挂起;路由函数读取用户反馈字段决定下一节点。
### Level 4记忆增强研究
**核心机制**:利用历史研究结果作为上下文,避免重复搜索,提升研究深度与连贯性。
- **语义检索历史**:将当前研究主题与意图向量化,从向量库检索相关历史研究项目。
- **上下文注入**:将匹配到的历史报告摘要、关键结论、已确认信息源作为先验知识注入意图理解与任务分解阶段。
- **增量更新**:对于相似主题的后续研究,仅搜索新增或变化的信息,与历史结果融合生成更新报告。
**适用场景**:周期性行业分析、追踪式课题研究、企业内部知识累积复用。
**实现指引**:在意图理解节点前增加记忆检索步骤,检索结果存入子图状态的 `historical_context` 字段供下游节点消费。
### Level 5自主研究规划与多模态分析
**核心机制**:子图具备初步的自主规划能力,能够分解开放式研究问题,并协调多模态搜索与分析工具。
- **复杂任务自主分解**LLM 根据高层研究目标生成多步骤研究计划,包括子任务依赖关系、预期信息源类型。
- **多模态搜索与分析**:除文本外,支持图像、视频、表格数据的搜索与信息提取。
- **质量自评估与迭代**:生成报告后,由 LLM 对报告完整性、一致性进行自评,识别信息缺口并自动触发补充搜索。
- **插件化工具扩展**:第三方可通过标准接口注册自定义搜索源或分析工具。
**适用场景**:综合性行业白皮书撰写、跨领域技术调研、深度竞品分析。
**实现指引**:任务分解节点升级为规划节点,输出结构化的研究计划图;工具调用采用 OpenAI Function Calling 风格统一接口。
---
## 🔧 核心组件详解
### 1. 意图理解节点
**职责**:接收用户原始请求,区分简单查询、深度研究、对比分析等意图类型,评估研究复杂度。
**输入**:用户自然语言请求、历史记忆上下文(可选)。
**输出**
- `intent_type`:意图类别枚举。
- `complexity_level`:复杂度评分(影响流程分支选择)。
- `clarified_query`:消歧后的核心研究问题。
**实现要点**
- 使用 LLM 进行少样本分类,输出结构化 JSON。
- 结合关键词匹配与规则兜底(如“对比”、“区别” → 对比分析)。
- 复杂度评分综合考虑用户指定信息源数量、时间范围跨度、是否需要可视化等因素。
### 2. 任务分解节点
**职责**:将复杂研究任务拆解为原子子任务,生成搜索关键词列表,并设计初步报告结构。
**输入**:澄清后的研究问题、意图类型、复杂度评分。
**输出**
- `sub_tasks`:子任务列表,每个子任务包含描述与预期信息类型。
- `search_keywords`:多组搜索关键词(支持广度/深度/验证轮次)。
- `draft_outline`:初步报告章节大纲。
**实现要点**
- 针对对比分析类意图,自动生成对比维度矩阵。
- 关键词生成利用 LLM 同义词扩展与上下位词推理能力。
- 初步大纲作为后续报告结构生成的基础框架。
### 3. 搜索执行节点
**职责**:根据搜索策略执行多轮搜索,收集原始信息并记录元数据。
**输入**:搜索关键词组、搜索策略(广度/深度/验证)、信息源偏好。
**输出**
- `raw_search_results`原始搜索结果列表每条包含标题、摘要、URL、来源域名、发布时间。
- `search_metadata`:搜索执行记录(搜索词、时间戳、来源类型)。
**实现要点**
- 内部调用统一搜索网关,屏蔽不同搜索源的 API 差异。
- 支持并行请求多个搜索源,通过 `asyncio.gather` 提升效率。
- 每条结果附带初始可信度评分(基于域名信誉库与来源类型)。
### 4. 信息源筛选节点(人工干预点)
**职责**:自动筛选低质量信息源,将候选列表提交用户确认,根据反馈决定后续流向。
**输入**:原始搜索结果列表。
**输出**
- `confirmed_sources`:用户确认使用的信息源列表。
- `user_feedback`:用户添加/删除/优先级调整记录。
**实现要点**
- 自动筛选规则:排除可信度评分低于阈值的来源、发布时间过旧的内容、已被屏蔽的域名。
- 人工审核界面以简洁列表形式展示信息源,支持多选、拖拽排序。
- 若用户选择“跳过审核”,则直接使用自动筛选后的结果进入下一阶段。
### 5. 信息提取节点
**职责**:从确认后的信息源中提取结构化关键信息,标注实体与术语。
**输入**:确认后的信息源内容(网页全文或摘要)。
**输出**
- `extracted_fragments`:信息片段列表,每条包含内容文本、提取类型(事实/观点/数据)、来源指针。
- `entity_annotations`:实体识别结果(时间、地点、人物、组织、专业术语)。
**实现要点**
- 使用 LLM 进行开放式信息抽取,遵循预定义的抽取模式(如“主体-关系-客体”)。
- 对数值类信息(百分比、金额、增长率)进行归一化处理,便于后续对比。
- 每条信息片段保留完整来源元数据,支持最终报告的引用溯源。
### 6. 信息整合与冲突检测节点
**职责**:融合多源信息片段,检测冲突并进行交叉验证,生成结构化信息视图。
**输入**:提取的信息片段列表。
**输出**
- `integrated_info`:按主题/时间线/对比维度组织的结构化信息。
- `conflict_report`:冲突项列表,含冲突等级与各方陈述。
- `verification_summary`:关键事实的多源验证结果。
**实现要点**
- 采用聚类算法或 LLM 语义匹配将描述同一事实的片段聚合。
- 冲突检测基于事实三元组比对如“X 公司市场份额 20%” vs “X 公司市场份额 25%”)。
- 高优先级冲突(如核心数据差异超过阈值)触发路由至人工审核或补充搜索。
### 7. 报告结构生成节点(人工干预点)
**职责**:基于整合后的信息生成详细报告结构,提交用户确认或调整。
**输入**:结构化信息、初步大纲、用户偏好。
**输出**
- `confirmed_outline`:确认后的报告结构(多级标题)。
- `visualization_suggestions`:建议的图表类型与数据映射。
**实现要点**
- 报告结构生成考虑信息量分布与逻辑叙事顺序。
- 可视化建议基于数据特征(如时间序列 → 折线图,类别对比 → 柱状图)。
- 用户反馈可触发结构调整或返回信息整合节点重新组织内容。
### 8. 报告生成节点
**职责**:按照确认的结构生成完整报告草稿,自动插入引用标记。
**输入**:确认的报告结构、结构化信息、引用元数据。
**输出**
- `draft_report`:包含章节内容与引用标记的完整报告文本。
- `citation_map`:引用标记到来源 URL 的映射表。
**实现要点**
- 使用 LLM 逐章节生成内容,确保风格统一与逻辑连贯。
- 引用标记采用 `[^1]` 脚注风格,在报告末尾聚合展示来源链接。
- 对于数据可视化部分,调用图表服务生成图片并嵌入 Markdown 引用。
### 9. 人工审核节点
**职责**:在特定检查点挂起子图执行,等待用户输入,将反馈存入状态供后续路由消费。
**输入**:待审核内容(信息源列表、报告结构等)。
**输出**:用户反馈(确认、修改指令、取消等)。
**实现要点**
- 基于 LangGraph `interrupt` 函数实现状态持久化挂起。
- 反馈数据结构化存储包含操作类型confirm/modify/cancel与具体参数。
- 支持超时自动确认(可配置)以保证流程不无限阻塞。
### 10. 最终格式化节点
**职责**:将报告草稿转换为用户指定输出格式,生成执行摘要,准备返回主图。
**输入**:确认后的报告草稿、输出格式偏好。
**输出**
- `final_report`:格式化后的报告内容。
- `executive_summary`:执行摘要(可选)。
- `suggestions`:后续研究建议(可选)。
**实现要点**
- 支持 Markdown、HTML、纯文本三种输出格式。
- 执行摘要通过 LLM 从完整报告中提炼核心观点与结论。
- 最终输出聚合到子图状态 `output` 字段,由接口层转换回主状态。
---
## 🔀 条件路由详解
子图内部通过条件路由函数实现动态流程控制,主要路由点如下:
### 入口路由:选择流程模式
- **位置**:意图理解节点之后。
- **条件**
- `complexity_level == "simple"` → 简化流程(跳过任务分解与多轮搜索)。
- `complexity_level in ["moderate", "complex"]` → 完整流程。
- **实现**:路由函数读取状态中的 `complexity_level` 字段返回下一节点名称。
### 搜索策略路由
- **位置**:任务分解节点之后。
- **条件**
- 用户要求“快速概览” → 单轮搜索。
- 用户要求“深度分析” → 多轮搜索(广度→深度→验证)。
- 意图为“对比分析” → 按对比维度分别搜索后融合。
- **实现**:根据 `intent_type` 与用户偏好字段决定搜索执行节点的内部模式。
### 信息源确认路由
- **位置**:信息源筛选节点(人工干预点)之后。
- **条件**
- 用户点击“确认” → 进入信息提取节点。
- 用户点击“添加信息源” → 返回搜索执行节点,携带补充关键词。
- 用户点击“调整优先级” → 重新排序后再次进入审核(循环)。
- **实现**:读取 `user_feedback.action` 字段进行路由。
### 冲突处理路由
- **位置**:信息整合节点之后。
- **条件**
- 无冲突或仅有低优先级冲突 → 进入报告结构生成。
- 存在高优先级冲突且用户未选择“忽略” → 进入人工审核节点。
- 用户要求“补充验证” → 返回搜索执行节点进行第三轮验证搜索。
- **实现**:评估 `conflict_report` 中的最高冲突等级与用户历史选择。
### 报告结构确认路由
- **位置**:报告结构生成节点(人工干预点)之后。
- **条件**
- 用户确认 → 进入报告生成节点。
- 用户要求修改结构 → 重新调用结构生成节点(带修改指令)。
- 用户要求调整内容 → 返回信息整合节点调整结构化信息。
- **实现**:读取 `user_feedback` 中的结构修改指令进行路由。
### 输出格式路由
- **位置**:最终格式化节点之前。
- **条件**
- `output_format == "markdown"` → Markdown 格式化。
- `output_format == "html"` → HTML 格式化。
- `output_format == "text"` → 纯文本格式化。
- **实现**:读取用户偏好或默认配置选择格式化器。
---
## 📊 状态设计
子图状态采用 TypedDict 定义,按研究阶段分层组织,完整记录中间产物以支持中断恢复与调试。
### 状态结构概览
| 分组 | 字段 | 类型 | 说明 |
|:-----|:-----|:-----|:-----|
| **输入** | `user_request` | `str` | 用户原始研究请求 |
| | `preferences` | `dict` | 用户指定的信息源、输出格式等偏好 |
| | `historical_context` | `list[dict]` | 记忆检索注入的历史研究摘要 |
| **意图与任务** | `intent_type` | `str` | 意图类别 |
| | `complexity_level` | `str` | 复杂度评级 |
| | `clarified_query` | `str` | 澄清后的核心问题 |
| | `sub_tasks` | `list[dict]` | 子任务列表 |
| | `search_keywords` | `list[list[str]]` | 多轮搜索关键词组 |
| | `draft_outline` | `list[str]` | 初步报告大纲 |
| **搜索与收集** | `raw_search_results` | `list[dict]` | 原始搜索结果(含元数据) |
| | `confirmed_sources` | `list[dict]` | 用户确认使用的信息源 |
| | `user_source_feedback` | `dict` | 用户对信息源的调整记录 |
| **提取与整合** | `extracted_fragments` | `list[dict]` | 结构化信息片段 |
| | `integrated_info` | `dict` | 按主题/时间线组织的整合信息 |
| | `conflict_report` | `list[dict]` | 冲突项列表 |
| | `verification_summary` | `dict` | 关键事实验证结果 |
| **报告生成** | `confirmed_outline` | `list[dict]` | 确认后的报告结构 |
| | `visualization_suggestions` | `list[dict]` | 图表建议 |
| | `draft_report` | `str` | 报告草稿(含引用标记) |
| | `citation_map` | `dict` | 引用标记到来源映射 |
| | `user_structure_feedback` | `dict` | 用户对报告结构的调整 |
| **控制流** | `current_phase` | `str` | 当前执行阶段 |
| | `next_node` | `str` | 下一节点名称 |
| | `interrupt_point` | `str` | 中断点标识 |
| | `error_info` | `dict` | 错误信息(如有) |
| **输出** | `final_report` | `str` | 最终报告内容 |
| | `output_format` | `str` | 输出格式 |
| | `executive_summary` | `str` | 执行摘要 |
### 状态更新原则
- **增量写入**:每个节点只修改其职责范围内的字段,其他字段只读。
- **原子提交**:节点执行成功后才将变更合并到全局状态。
- **版本记录**:每次状态变更均记录前序版本,支持回滚(用于人工审核场景)。
- **持久化友好**:所有字段均可 JSON 序列化,便于通过检查点器持久化。
---
## 🔄 工作流程与中断恢复
### 完整研究流程(四个阶段)
| 阶段 | 步骤 | 节点 | 人工干预 |
|:-----|:-----|:-----|:---------|
| **理解与分解** | 1. 意图理解 | `intent_understanding` | 否 |
| | 2. 任务分解 | `task_decomposition` | 否 |
| **检索与收集** | 3. 多源搜索执行 | `search_execution` | 否 |
| | 4. 信息源筛选与确认 | `source_filtering` | **是** |
| **提取与整合** | 5. 关键信息提取 | `information_extraction` | 否 |
| | 6. 信息整合与冲突检测 | `information_integration` | 否(可能触发审核) |
| **生成与输出** | 7. 报告结构确认 | `structure_generation` | **是** |
| | 8. 报告生成与格式化 | `report_generation` + `final_formatting` | 否 |
### 简化流程(快速查询模式)
`complexity_level == "simple"` 时,子图走精简路径:
1. 意图理解(简化版,不拆解任务)。
2. 单次搜索执行(仅广度搜索)。
3. 快速信息提取(不进行深度整合)。
4. 直接生成简短回答(跳过结构确认)。
### 中断与恢复机制
子图支持在以下位置中断并持久化状态:
- 信息源筛选节点(人工干预点 1
- 报告结构生成节点(人工干预点 2
- 任意节点执行完成后的检查点(由检查点器自动保存)
**恢复流程**
1. 主图传入相同的 `thread_id` 与中断前状态。
2. 子图从检查点加载状态,定位 `interrupt_point`
3. 若中断点为人工审核节点,等待用户反馈后继续执行。
4. 若为其他检查点,直接从下一节点开始执行。
---
## 🔒 安全与边界控制
### 安全机制
| 类别 | 机制 | 实现位置 |
|:-----|:-----|:---------|
| **内容安全** | 搜索关键词过滤、结果内容审查、成人内容屏蔽 | 搜索工具网关 |
| **数据安全** | 用户数据加密存储、传输层 TLS、敏感信息脱敏 | 持久化层与接口层 |
| **访问控制** | 基于角色的功能权限(普通/高级/管理员) | 接口层中间件 |
| **资源限制** | 单用户 QPS 限制、单次研究最大 Token 消耗、最大信息源数量 | 工作流编排层 |
| **审计日志** | 记录所有搜索、提取、生成操作,包含操作者、时间戳、资源消耗 | 各节点内置日志 |
### 人工审核触发边界
以下情况强制进入人工审核节点:
- 信息源可信度平均评分低于阈值。
- 高优先级信息冲突且无法自动裁决。
- 用户请求超出常规研究边界(如要求访问受限领域)。
- 首次使用特定高风险搜索源。
### 错误处理边界
- **可恢复错误**(如单次搜索超时):自动重试或切换备选搜索源。
- **不可恢复错误**(如 LLM 服务不可用):终止执行,向主图返回错误状态与友好提示。
- **部分成功**:即使部分子任务失败,仍返回已完成的部分结果(如已提取的信息片段)。
---
## 🛠️ 工具集成
### 工具集概览
| 工具集 | 功能 | 外部依赖 |
|:-------|:-----|:---------|
| **搜索工具集** | 通用网页搜索、学术论文检索、专业知识库查询、新闻资讯获取 | 搜索 API 网关 |
| **信息提取工具集** | 实体识别、数据提取、术语标注、关系抽取、摘要生成 | LLM 服务 |
| **信息验证工具集** | 多源交叉验证、可信度评分、时效性检查、一致性检查 | 域名信誉库、LLM |
| **报告生成工具集** | 内容生成、引用插入、图表生成、格式转换 | LLM、图表服务 |
| **记忆检索工具集** | 历史研究语义检索、项目检索、时间范围检索 | Qdrant / pgvector |
### 工具调用规范
所有工具遵循统一接口规范:
- **输入**:标准字典参数。
- **输出**:标准字典结果,包含 `status``data``error` 字段。
- **元数据**:每次调用记录调用时间、耗时、资源标识。
### 工具扩展方式
新增工具仅需三步:
1. 在对应工具集中实现标准接口的适配器。
2. 在工具注册表中声明工具元数据(名称、描述、参数模式)。
3. 在相关节点中通过工具名称调用,无需修改节点核心逻辑。
---
## 📑 快速开始(概念级)
研究分析子图作为主图的一个子图节点被调用,典型集成方式如下:
1. **主图路由**:当主图识别到用户意图为“研究分析”时,路由至 `research_subgraph` 节点。
2. **状态映射**:主状态中的 `user_input``user_id` 等字段映射到子图状态输入部分。
3. **子图执行**:子图按照上述工作流自主执行,可能在人工干预点挂起。
4. **结果回传**:子图执行完毕后,将 `final_report``executive_summary` 等字段回写到主状态。
子图内部配置项(如默认搜索源、重试次数、审核超时)通过环境变量或配置文件管理。
---
## ⚙️ 配置项参考
| 配置项 | 说明 | 默认值 |
|:-------|:-----|:-------|
| `RESEARCH_LLM_MODEL` | 意图理解与生成使用的 LLM 模型 | `deepseek-chat` |
| `SEARCH_API_GATEWAY_URL` | 统一搜索网关地址 | `http://localhost:8080` |
| `SEARCH_DEFAULT_SOURCES` | 默认启用的搜索源列表 | `["web", "news"]` |
| `MAX_SEARCH_ROUNDS` | 最大搜索轮次 | `3` |
| `RERANK_TOP_N` | 信息源筛选保留数量 | `20` |
| `CONFLICT_SEVERITY_THRESHOLD` | 触发人工审核的冲突等级 | `high` |
| `HUMAN_LOOP_TIMEOUT_SEC` | 人工审核超时自动确认时间 | `3600` |
| `VECTOR_DB_URL` | 记忆检索向量库地址 | `http://localhost:6333` |
| `CHART_SERVICE_URL` | 图表生成服务地址 | `http://localhost:3000` |
---
## 🤝 与主系统集成
研究分析子图通过 LangGraph 子图机制与主系统解耦集成:
- **状态隔离**:子图状态字段使用前缀 `research_` 避免与主状态冲突。
- **错误传播**:子图内部异常捕获后转换为标准错误结构向上传递,主图可选择重试或降级。
- **检查点共享**:子图与主图共用同一检查点器后端,确保整体流程的断点续作能力。
子图对外暴露的唯一接口是编译后的 `StateGraph` 实例,主图通过 `builder.add_node("research", research_subgraph)` 将其作为一个节点加入。
---
## 📈 性能考量
- **并行搜索**:多源搜索与多关键词检索采用异步并行,典型场景下搜索阶段耗时控制在 3 秒内。
- **流式报告生成**:报告生成节点支持流式输出,用户在报告结构确认后可实时看到内容逐段生成。
- **结果缓存**:对于相同搜索词在短时间内的重复请求,搜索网关层提供 TTL 缓存。
- **状态压缩**:持久化前对大型字段(如原始搜索结果全文)进行摘要化处理,减少存储开销。
---
## 🔮 未来演进方向
参见需求文档中的详细规划,技术实现层面重点关注:
- **LLM 工具调用标准化**:向 OpenAI Function Calling 风格对齐。
- **多模态管道**:集成图像描述生成与视觉问答模型。
- **插件市场**:提供标准工具接口 SDK支持第三方搜索源接入。
- **协同研究**:支持多用户对同一研究项目的评论与版本分支管理。

View File

@@ -14,18 +14,18 @@ from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, Depe
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from pydantic import BaseModel from pydantic import BaseModel
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver from app.main_graph.checkpoint.postgres.aio import AsyncPostgresSaver
from .agent.service import AIAgentService from .agent.service import AIAgentService
from .agent.history import ThreadHistoryService from .agent.history import ThreadHistoryService
from .agent_subgraphs.common.human_review import ( from app.core.human_review import (
ReviewManager, ReviewManager,
InMemoryReviewStore, InMemoryReviewStore,
ReviewStatus, ReviewStatus,
HumanReview HumanReview
) )
from .agent_subgraphs.contact.api_client import ContactAPIClient from app.subgraphs.contact.api_client import ContactAPIClient
from .agent_subgraphs.dictionary.api_client import DictionaryAPIClient from app.subgraphs.dictionary.api_client import DictionaryAPIClient
from .agent_subgraphs.news_analysis.api_client import NewsAPIClient from app.subgraphs.news_analysis.api_client import NewsAPIClient
from .db.init_db import init_subgraph_tables from .db.init_db import init_subgraph_tables
from .db.models import ContactRepository, DictionaryRepository, NewsRepository from .db.models import ContactRepository, DictionaryRepository, NewsRepository
from .logger import info, error from .logger import info, error

View File

@@ -0,0 +1 @@
"""核心模块 - 基类和通用工具"""

View File

@@ -1,73 +0,0 @@
"""
Graph 子模块 - React 模式增强版(带超时重试)
"""
from .graph_builder import GraphBuilder
from .subgraph_builder import build_main_graph, build_react_main_graph
from .react_nodes import (
init_state_node,
react_reason_node,
error_handling_node,
final_response_node,
route_by_reasoning
)
from .rag_nodes import (
rag_retrieve_node,
rag_re_retrieve_node,
inject_rag_tool_to_state,
get_rag_tool_from_state
)
from .state import (
MessagesState,
GraphContext,
MainGraphState,
CurrentAction,
ErrorRecord,
ErrorSeverity
)
from .retry_utils import (
RetryConfig,
RetryResult,
RetryStrategy,
with_retry,
with_async_retry,
create_retry_wrapper_for_node,
RAG_RETRY_CONFIG,
SUBGRAPH_RETRY_CONFIG
)
__all__ = [
# 旧版兼容性
"GraphBuilder",
"build_main_graph",
"MessagesState",
"GraphContext",
"MainGraphState",
"CurrentAction",
# 新版 React 模式
"build_react_main_graph",
"init_state_node",
"react_reason_node",
"error_handling_node",
"final_response_node",
"route_by_reasoning",
"ErrorRecord",
"ErrorSeverity",
# RAG 节点(独立模块)
"rag_retrieve_node",
"rag_re_retrieve_node",
"inject_rag_tool_to_state",
"get_rag_tool_from_state",
# 超时和重试工具
"RetryConfig",
"RetryResult",
"RetryStrategy",
"with_retry",
"with_async_retry",
"create_retry_wrapper_for_node",
"RAG_RETRY_CONFIG",
"SUBGRAPH_RETRY_CONFIG"
]

View File

@@ -0,0 +1 @@
"""主图模块 - LangGraph 主流程"""

View File

@@ -4,7 +4,7 @@ LangGraph 状态图构建模块 - 精简版,仅负责组装图
""" """
from langchain_core.language_models import BaseLLM from langchain_core.language_models import BaseLLM
from langgraph.graph import StateGraph, START, END from app.main_graph.graph import StateGraph, START, END
from .state import MessagesState, GraphContext from .state import MessagesState, GraphContext
from ..nodes import ( from ..nodes import (
should_continue, should_continue,
@@ -14,7 +14,7 @@ from ..nodes import (
create_summarize_node, create_summarize_node,
finalize_node, finalize_node,
) )
from ..nodes.memory_trigger import memory_trigger_node, set_mem0_client from app.main_graph.nodes.memory_trigger import memory_trigger_node, set_mem0_client
from ..memory import Mem0Client from ..memory import Mem0Client

View File

@@ -0,0 +1 @@
"""主图节点"""

View File

@@ -4,10 +4,10 @@
""" """
from typing import Any, Dict from typing import Any, Dict
from langgraph.config import get_stream_writer from app.main_graph.config import get_stream_writer
# 本地模块 # 本地模块
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..utils.logging import log_state_change from ..utils.logging import log_state_change
from ..logger import info, error from ..logger import info, error

View File

@@ -9,7 +9,7 @@ from langchain_core.language_models import BaseLLM
from langchain_core.messages import AIMessage from langchain_core.messages import AIMessage
# 本地模块 # 本地模块
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..agent.prompts import create_system_prompt from ..agent.prompts import create_system_prompt
from ..utils.logging import log_state_change from ..utils.logging import log_state_change
from ..logger import debug, info, error from ..logger import debug, info, error

View File

@@ -1,6 +1,6 @@
from typing import Any, Dict from typing import Any, Dict
from langchain_core.runnables.config import RunnableConfig from langchain_core.runnables.config import RunnableConfig
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..memory.mem0_client import Mem0Client from ..memory.mem0_client import Mem0Client
from ..logger import info from ..logger import info

View File

@@ -254,7 +254,7 @@ async def initialize_rag_from_initializer() -> None:
注意这是示例代码实际使用时需要提供 local_llm_creator 注意这是示例代码实际使用时需要提供 local_llm_creator
""" """
try: try:
from ..agent.rag_initializer import init_rag_tool from app.main_graph.utils.rag_initializer import init_rag_tool
# 注意:这里需要传入 local_llm_creator # 注意:这里需要传入 local_llm_creator
# 示例: # 示例:

View File

@@ -15,13 +15,13 @@ from typing import Dict, Any, Optional
from datetime import datetime from datetime import datetime
# 导入我们的 intent.py # 导入我们的 intent.py
from ..agent_subgraphs.common.intent import ( from app.core.intent import (
react_reason, react_reason,
get_route_by_reasoning, get_route_by_reasoning,
ReasoningAction, ReasoningAction,
ReasoningResult ReasoningResult
) )
from ..agent_subgraphs.common.state_base import StateUtils from app.core.state_base import StateUtils
from .state import MainGraphState, ErrorRecord, ErrorSeverity from .state import MainGraphState, ErrorRecord, ErrorSeverity
from .retry_utils import ( from .retry_utils import (
RetryConfig, RetryConfig,

View File

@@ -8,7 +8,7 @@ from langchain_core.messages import AIMessage
# 本地模块 # 本地模块
from ..config import ENABLE_GRAPH_TRACE, MEMORY_SUMMARIZE_INTERVAL from ..config import ENABLE_GRAPH_TRACE, MEMORY_SUMMARIZE_INTERVAL
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..logger import info from ..logger import info

View File

@@ -6,7 +6,7 @@
from typing import Any, Dict from typing import Any, Dict
# 本地模块 # 本地模块
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..memory.mem0_client import Mem0Client from ..memory.mem0_client import Mem0Client
from ..utils.logging import log_state_change from ..utils.logging import log_state_change
from ..logger import debug, info, error, warning from ..logger import debug, info, error, warning

View File

@@ -6,10 +6,10 @@
import asyncio import asyncio
from typing import Any, Dict from typing import Any, Dict
from langchain_core.messages import AIMessage, ToolMessage from langchain_core.messages import AIMessage, ToolMessage
from langgraph.config import get_stream_writer from app.main_graph.config import get_stream_writer
# 本地模块 # 本地模块
from ..graph.state import MessagesState from app.main_graph.state import MessagesState
from ..utils.logging import log_state_change from ..utils.logging import log_state_change
from ..logger import debug, info from ..logger import debug, info

View File

@@ -6,7 +6,7 @@ Main Graph State Definition - React Mode Enhanced
from enum import Enum, auto from enum import Enum, auto
from typing import Optional, Dict, Any, Annotated, Sequence, TypedDict, List from typing import Optional, Dict, Any, Annotated, Sequence, TypedDict, List
from dataclasses import dataclass, field from dataclasses import dataclass, field
from langgraph.graph import add_messages from app.main_graph.graph import add_messages
from langchain_core.messages import BaseMessage from langchain_core.messages import BaseMessage

View File

@@ -0,0 +1 @@
"""主图工具"""

View File

@@ -22,13 +22,13 @@ def dictionary_tool(query: str, action: Optional[str] = None) -> str:
格式化的结果文本 格式化的结果文本
""" """
try: try:
from backend.app.agent_subgraphs.dictionary import ( from app.subgraphs.dictionary import (
DictionaryState, DictionaryState,
DictionaryAction, DictionaryAction,
parse_intent, parse_intent,
format_result format_result
) )
from backend.app.agent_subgraphs.dictionary.nodes import ( from app.subgraphs.dictionary.nodes import (
query_word, translate_text, extract_terms, get_daily_word query_word, translate_text, extract_terms, get_daily_word
) )
@@ -87,13 +87,13 @@ def news_analysis_tool(query: str, action: Optional[str] = None) -> str:
格式化的结果文本 格式化的结果文本
""" """
try: try:
from backend.app.agent_subgraphs.news_analysis import ( from app.subgraphs.news_analysis import (
NewsAnalysisState, NewsAnalysisState,
NewsAction, NewsAction,
parse_intent, parse_intent,
format_result format_result
) )
from backend.app.agent_subgraphs.news_analysis.nodes import ( from app.subgraphs.news_analysis.nodes import (
query_news, analyze_url, extract_keywords, generate_report query_news, analyze_url, extract_keywords, generate_report
) )
@@ -150,13 +150,13 @@ def contact_tool(query: str, action: Optional[str] = None) -> str:
格式化的结果文本 格式化的结果文本
""" """
try: try:
from backend.app.agent_subgraphs.contact import ( from app.subgraphs.contact import (
ContactState, ContactState,
ContactAction, ContactAction,
parse_intent, parse_intent,
format_result format_result
) )
from backend.app.agent_subgraphs.contact.nodes import ( from app.subgraphs.contact.nodes import (
query_contact, add_contact, list_contacts query_contact, add_contact, list_contacts
) )

View File

@@ -0,0 +1 @@
"""主图工具函数"""

View File

@@ -3,7 +3,7 @@ React 模式主图构建器 - 完整循环推理版本
Main Graph Builder - Full React Mode with Loop Reasoning Main Graph Builder - Full React Mode with Loop Reasoning
""" """
from langgraph.graph import StateGraph, START, END from app.main_graph.graph import StateGraph, START, END
from typing import Dict, Any from typing import Dict, Any
from .state import MainGraphState, CurrentAction from .state import MainGraphState, CurrentAction
@@ -15,9 +15,9 @@ from .react_nodes import (
route_by_reasoning route_by_reasoning
) )
from .rag_nodes import rag_retrieve_node from .rag_nodes import rag_retrieve_node
from ..agent_subgraphs.contact import build_contact_subgraph from app.subgraphs.contact import build_contact_subgraph
from ..agent_subgraphs.dictionary import build_dictionary_subgraph from app.subgraphs.dictionary import build_dictionary_subgraph
from ..agent_subgraphs.news_analysis import build_news_analysis_subgraph from app.subgraphs.news_analysis import build_news_analysis_subgraph
# ========== 子图包装器(处理子图错误传递) ========== # ========== 子图包装器(处理子图错误传递) ==========

View File

@@ -25,7 +25,7 @@ load_dotenv(PROJECT_ROOT / ".env")
from app.agent.service import AIAgentService from app.agent.service import AIAgentService
from app.config import DB_URI from app.config import DB_URI
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver from app.main_graph.checkpoint.postgres.aio import AsyncPostgresSaver
import asyncio import asyncio

View File

@@ -1,19 +0,0 @@
"""
节点模块 - 导出所有 LangGraph 节点函数
"""
from .router import should_continue
from .llm_call import create_llm_call_node
from .tool_call import create_tool_call_node
from ..graph.retrieve_memory import create_retrieve_memory_node
from .summarize import create_summarize_node
from .finalize import finalize_node
__all__ = [
"should_continue",
"create_llm_call_node",
"create_tool_call_node",
"create_retrieve_memory_node",
"create_summarize_node",
"finalize_node",
]

View File

@@ -0,0 +1 @@
"""子图模块"""

View File

@@ -4,7 +4,7 @@ Contact Subgraph Builder
支持 API 注入的工厂模式 支持 API 注入的工厂模式
""" """
from langgraph.graph import StateGraph, START, END from app.main_graph.graph import StateGraph, START, END
from .state import ContactState from .state import ContactState
from .nodes import create_contact_nodes from .nodes import create_contact_nodes

View File

@@ -3,7 +3,7 @@
Dictionary Subgraph Builder - Complete Dictionary Subgraph Builder - Complete
""" """
from langgraph.graph import StateGraph, START, END from app.main_graph.graph import StateGraph, START, END
from .state import DictionaryState from .state import DictionaryState
from .nodes import ( from .nodes import (

View File

@@ -3,7 +3,7 @@
News Analysis Subgraph Builder News Analysis Subgraph Builder
""" """
from langgraph.graph import StateGraph, START, END from app.main_graph.graph import StateGraph, START, END
from .state import NewsAnalysisState from .state import NewsAnalysisState
from .nodes import ( from .nodes import (