Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

AI 学习助手(生产级 Agent)

基于 LangGraph + FastAPI + ChromaDB + SqliteSaver + Rerank 的多用户学习助手,支持用户画像持久化、意图分类、知识点问答、学习路径推荐、长对话摘要压缩。

核心能力

能力 实现方式 示例
知识点问答 RAG 检索增强生成(BGE Embedding + ChromaDB + BGE-Reranker 精排 + Query 改写) "LangGraph 是什么" → 改写检索语句 → 检索 Top-10 → Rerank Top-3 → 整理回复
学习资源推荐 LLM 读取用户画像,推荐下一步学习路线 "推荐一些学习资源" → 基于画像推荐
学习进度更新 LLM 从对话中提取进度,写入用户画像 "Python 学完了" → 更新 topics["Python"]="已掌握"
闲聊兜底 意图分类无法匹配时走 chat 路径 "你好" → 友好的学习助手对话
更新后串联推荐 先更新画像 → 再基于新画像推荐下一步 "Python 学完了,下一步学什么" → 提取进度 → 推荐
长对话摘要压缩 滑动窗口 + 增量摘要(Summary Node) 超 10 条消息自动触发,旧对话压缩为摘要

架构

                        POST /chat
                  {"user_id": "同学01", "message": "LangGraph是什么"}
                            │
                            ▼
                ┌───────────────────────┐
                │      main.py          │
                │   FastAPI + Pydantic   │
                │   ChatRequest 校验      │
                └───────────┬───────────┘
                            │
                            ▼
                ┌───────────────────────┐
                │     graph.py          │
                │   LangGraph 图编排     │
                │   StateGraph +         │
                │   SqliteSaver 持久化   │
                └───────────┬───────────┘
                            │
        START → init_profile → classify_intent
                                    │
          ┌─────────┬───────────────┼───────────────┬─────────┐
          ▼         ▼               ▼               ▼         ▼
      recommend  search           update           chat    (fallback)
          │         │               │               │
          │         │               ▼               │
          │         │           recommend            │
          │         │               │               │
          └─────────┴───────────────┴───────────────┘
                            │
                            ▼
                        respond ──(msg > 10?)── summarize
                            │                       │
                            ▼                       ▼
                          END                     END
                            │
                            ▼
                {"reply": "LangGraph 是 LangChain 推出的图引擎框架..."}

四个意图通过 LLM 意图分类器路由,update → recommend 串联实现"先记进度再推荐"。 超过 10 条消息自动触发 summarize 节点,旧消息压缩为摘要,最近 6 条保留原文。

RAG 管线(生产级:Query 改写 + 检索 + Rerank 精排)

用户口语提问
    │
    ▼
Query 改写(LLM:口语 → 正式检索语句)
    │
    ▼
BGE Embedding 向量化 → ChromaDB 检索(Top-10)
    │
    ▼
BGE-Reranker 精排(Top-10 → Top-3)
    │
    ▼
Prompt 组装 → DeepSeek 生成

知识库位于 docs/,包含 6 份 Agent 开发核心文档:

  • LangGraph 入门
  • MCP 协议详解
  • Agent 架构模式(FC / ReAct / Plan-Execute / Multi-Agent)
  • RAG 全链路
  • Python 异步编程
  • Prompt 工程

首次 import knowledge_base.py 时自动构建,之后持久化在 chroma_db/,不再重复入库。

技术栈

组件 技术 用途
Web 框架 FastAPI + Uvicorn HTTP 服务 + 前端页面
图引擎 LangGraph (StateGraph + SqliteSaver) 多节点编排 + 多用户状态持久化
大模型 DeepSeek Chat API 意图分类 + 资源推荐 + 答案生成 + 闲聊 + 摘要压缩
向量数据库 ChromaDB (PersistentClient) Agent 知识库存储与检索
Embedding 硅基流动 BAAI/bge-large-zh-v1.5 文本向量化
精排 硅基流动 BAAI/bge-reranker-v2-m3 检索结果重排序(Top-10 → Top-3)
前端 原生 HTML/CSS/JS(终端霓虹风) 可视化聊天界面

项目结构

study_assistant/
├── main.py            # FastAPI 入口,GET /(前端页面)+ POST /chat(API)+ GET /debug/{user_id}
├── graph.py           # LangGraph 图编排:7 节点 + SqliteSaver + 条件边(>10 条路由到 summarize)
├── state.py           # AssistantState 类型定义(TypedDict + Annotated,含 summary 字段)
├── tools.py           # 节点函数:classify_intent / recommend / search / update / chat / summarize_conversation
├── knowledge_base.py  # 自建 RAG:BGE 向量化 + ChromaDB + BGE-Reranker 精排
├── env_loader.py      # 环境变量加载 + OpenAI client 单例
├── requirements.txt   # Python 依赖列表
├── .gitignore         # Git 忽略规则
├── docs/              # Agent 开发知识库文档(6 份 .txt)
├── chroma_db/         # ChromaDB 向量库持久化数据(gitignore)
├── checkpoints.sqlite # SqliteSaver checkpoint 数据库(gitignore)
└── static/
    └── index.html     # 前端页面(终端霓虹风聊天界面)

快速开始

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

# 2. 配置 .env(放在 study_assistant/ 目录下或上级目录均可)
# DEEPSEEK_API_KEY=your_key
# SILICONFLOW_API_KEY=your_key

# 3. 启动服务
cd study_assistant
uvicorn main:app --reload --port 8001

# 4. 打开浏览器
http://127.0.0.1:8001

# 5. 或通过 API 测试
curl -X POST http://127.0.0.1:8001/chat \
  -H "Content-Type: application/json" \
  -d '{"user_id": "同学01", "message": "LangGraph是什么"}'

API 文档

启动后访问 http://127.0.0.1:8001/docs 查看 Swagger 文档。

GET /

返回前端聊天页面。

POST /chat

请求:

{
  "user_id": "同学01",
  "message": "LangGraph是什么"
}

响应:

{
  "reply": "LangGraph 是 LangChain 推出的图引擎框架,专门用于构建有状态的、多步骤的 AI Agent 工作流..."
}
  • user_id 决定 SqliteSaver 的 thread_id,不同用户画像隔离,重启不丢失
  • 多次调用同一 user_id,画像自动累积
  • 对话超 10 条消息自动触发摘要压缩

GET /debug/{user_id}

调试接口,查看用户当前状态(消息数、画像、摘要内容)。

踩坑记录

坑 原因 修复
目录名 study-assistant 含连字符 Python 模块名不能含 -,import 报 SyntaxError 重命名为 study_assistant
auto_tool_call.py import 触发天气助手交互循环 原文件缺 if __name__ == "__main__" 保护,顶层 while 循环直接跑 加 guard 包裹
rag_pipeline.py load_env 路径写死 "../.env" 基于 CWD 而非文件所在目录 改用 __file__ + os.path.dirname 动态计算
RAG 路径返回 500 ask_rag() 返回 (answer, sources) 元组,AIMessage 要求纯 str 创建独立 knowledge_base.py,ask() 只返回字符串
MemorySaver 首次调用无画像 首次 invoke 时 user_profile 为空,下游节点读取报错 新增 init_profile 节点:首次设 DEFAULT_PROFILE,后续返回 {} 不覆盖
recommend 回复生硬 只能推荐不能更新画像;update_profile 只能更新不能推荐 图串联 update → recommend → respond,两个节点各司其职
知识库回答"根据现有资料无法确定" 旧知识库是公司文档(产品手册/离职制度),不含 Agent 知识 新建 6 份 Agent 开发文档,切换为独立知识库
SiliconFlow API Key 未加载 knowledge_base.py 只检查了 DEEPSEEK_KEY 就跳过 .env 加载 条件改为 if not API_KEY or not SILICON_KEY
前端界面全英文 初始版本从项目1模板迁移 全部中文化:提示语、按钮、错误信息、默认用户ID

生产级打磨踩坑

坑 原因 修复
MemorySaver 重启丢失对话 MemorySaver 纯内存,服务重启后多轮对话历史全部消失 迁移到 SqliteSaver,手动 sqlite3.connect(check_same_thread=False) + SqliteSaver(conn),避免 from_conn_string 的 contextmanager 陷阱
chat 只取 messages[-1] 之前认为"同一会话的上下文在 messages 里,LLM 知道历史",实际 LLM 无状态,每次调用只看到传入的消息 改为传入 summary + 最近 6 条消息,summary 承载压缩后的长期记忆
OpenAI 422 错误:role human 不合法 LangChain HumanMessage.type 为 "human",但 OpenAI API 只接受 system/user/assistant/tool 在 chat() 构建 messages 时显式映射 role = "user" if m.type == "human" else "assistant"
from_conn_string 不能模块级用 from_conn_string 是 @contextmanager,with 退出后自动 closing(conn),模块级持久化不可用 手动 sqlite3.connect() + SqliteSaver(conn),连接生命周期由模块控制

设计决策

为什么用 LangGraph 而非手写 Agent 循环? 本项目有 5 个节点 + 条件分支 + 节点串联(update→recommend),手写 while+if 会变成意大利面。LangGraph 声明式定义图结构,新增节点只需加 node + edge,不碰已有逻辑。

为什么用 SqliteSaver 而非 MemorySaver? MemorySaver 零配置适合 demo,但纯内存导致服务重启后所有对话历史和用户画像丢失。SqliteSaver 数据落盘到 checkpoints.sqlite,重启后通过 thread_id 恢复状态,实现生产级持久化。手动管理 sqlite3 连接生命周期(而非 from_conn_string 的 contextmanager),保证模块级单例可用。

为什么引入 Summary Node? 长对话场景下全量传 messages 有两个极端:只取 messages[-1] 丢失短期上下文("金鱼记忆"),全量传导致 token 随对话线性增长最终超限。Summary Node 采用滑动窗口 + 增量摘要策略:超过 10 条消息触发 LLM 压缩,旧消息压为 ≤200 字摘要,最近 6 条保留原文。增量合并(旧摘要 + 新对话 → 新摘要)保证每次压缩成本恒定。

为什么 init_profile 返回 {} 而非覆盖已有画像? SqliteSaver 会在每次 invoke 后持久化 State。init_profile 只在首次调用时设默认值;后续调用时 SqliteSaver 已恢复画像,返回 {} 表示"不动这个字段"。如果每次都返回 DEFAULT_PROFILE,会把用户积累的画像覆盖掉。

为什么 respond 节点单独存在? 所有业务节点(recommend/search/update/chat)只负责写 state["reply"] 字符串,不碰 messages。respond 节点作为统一出口,将 reply 转为 AIMessage 写入对话历史。好处是新增节点只需关心回复内容,不用重复 AIMessage 转换逻辑,且多轮对话历史由 Annotated[list, add_messages] 自动累加。

为什么 update 后自动串联 recommend? 用户说"学完了,下一步学什么"包含两层意图:记录进度 + 请求推荐。图编排 update → recommend 让两个节点各司其职——update_profile 提取进度写入画像,recommend_resources 基于最新画像推荐下一步。函数职责单一,图负责编排。

为什么自建 knowledge_base.py 而非复用 rag_lab? rag_lab 的知识库是公司文档,不适合学习助手场景。且 ask_rag() 返回 (answer, sources) 元组,与本项目 AIMessage 需要的纯字符串不兼容。自建模块解耦两个项目,各自独立演进。

为什么意图分类单独一个 LLM 调用? 五种意图(recommend/ask/update/chat)语义差异大,用关键词匹配太脆弱。LLM 分类器(temperature=0)准确率接近 100%,且新增意图只需改 prompt 不用改代码。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages