阶段 5:升级为可讲的 Agent 项目
阶段 5:升级为可讲的 Agent 项目
本阶段目标:把前面学过的 FastAPI、RAG、Tool Calling、LangGraph、状态管理、结构化输出整合成一个完整项目。
这个项目不只是“能跑”,更要能在面试中讲清楚:
- 业务场景;
- 系统架构;
- 关键模块;
- 技术难点;
- 优化方案;
- 评估指标。
1. 阶段 5 的定位
阶段 3 你做的是 RAG 项目:
用户问题 -> 检索知识库 -> 拼接上下文 -> 模型回答
阶段 4 你学的是 Tool Calling 和 Agent:
用户目标 -> 模型选择工具 -> 执行工具 -> 工具结果回传 -> 模型继续决策
阶段 5 要做的是把它们合起来:
用户问题
-> 问题分类 / 意图识别
-> 知识类问题走 RAG
-> 外部任务走工具调用
-> 多轮上下文保存状态
-> 最终输出结构化答案
-> 记录日志与可视化调用过程
你最后要做出的项目可以叫:
Knowledge Agent Assistant:带知识库检索、外部工具调用和多轮状态的 AI Assistant
这个项目适合写进简历,也适合在 AI Agent / 大模型应用 / 后端开发相关岗位面试中讲。
2. 项目一句话介绍
面试时可以先用一句话介绍:
我做了一个面向论文 / 文档知识库的 AI Assistant,它不是简单的 RAG 问答,而是基于 LangGraph 构建了一个可控的 Agent 流程:系统会先判断用户问题类型,知识类问题调用 RAG 检索工具,任务类问题调用外部工具,同时维护多轮对话状态,最后输出带引用来源的结构化答案,并记录工具调用过程和问题日志,方便调试与评估。
3. 业务场景设计
3.1 为什么需要这个项目
很多企业或实验室都有大量文档,例如:
- 论文 PDF;
- 项目文档;
- 技术方案;
- 接口文档;
- 业务知识库;
- FAQ;
- 操作手册。
普通聊天模型的问题是:
- 不知道你的私有文档;
- 容易凭空编造;
- 不能调用外部工具;
- 多轮任务状态容易丢;
- 回答过程不可追踪;
- 出错后难排查。
所以你做这个项目的业务价值是:
让用户可以用自然语言查询知识库、执行简单外部任务,并且系统能给出可追踪、可解释、可复盘的回答。
3.2 示例业务场景
假设你的知识库里有多篇 RAG、Agent、LLM 应用相关论文和笔记。
用户可能会问:
1. RAG 为什么能减少幻觉?
2. 帮我总结这篇论文的核心方法。
3. 对比一下 RAG 和微调的区别。
4. 帮我列出知识库里关于 Agent Memory 的论文。
5. 根据刚才的回答,生成一个面试回答版本。
6. 查一下当前知识库有多少篇论文。
7. 把刚才的总结保存成学习笔记。
这些问题不是都应该用同一个流程处理。
例如:
| 用户问题 | 类型 | 处理方式 |
|---|---|---|
| RAG 为什么能减少幻觉? | 知识问答 | RAG 检索 |
| 总结这篇论文 | 文档总结 | RAG + 总结 |
| 对比两篇论文 | 多文档分析 | 多次检索 + 合成 |
| 知识库有多少篇论文 | 外部工具 | 调用 metadata / database 工具 |
| 保存成学习笔记 | 外部任务 | 调用文件保存工具 |
| 根据刚才内容继续改写 | 多轮对话 | 依赖 state / memory |
这就是 Agent 项目的意义:不是所有问题都强行走一次 RAG,而是由系统根据任务类型决定下一步。
4. 系统总体架构
建议系统分为 6 层:
┌─────────────────────────────────────────────┐
│ 前端 / Client │
│ Chat UI / 调用过程展示 / 引用片段展示 │
└─────────────────────┬───────────────────────┘
│
┌─────────────────────▼───────────────────────┐
│ FastAPI 服务层 │
│ 接收请求 / 参数校验 / 返回结构化响应 │
└─────────────────────┬───────────────────────┘
│
┌─────────────────────▼───────────────────────┐
│ LangGraph 编排层 │
│ classify -> route -> retrieve/tool -> answer│
└───────────────┬───────────────┬─────────────┘
│ │
┌───────────────▼───────┐ ┌───▼────────────────┐
│ RAG 检索层 │ │ Tool 层 │
│ PDF解析/切块/向量库/检索│ │ 外部API/数据库/文件工具 │
└───────────────┬───────┘ └───┬────────────────┘
│ │
┌───────────────▼───────────────▼─────────────┐
│ 状态存储与日志层 │
│ thread state / checkpoints / query logs │
└─────────────────────────────────────────────┘
4.1 架构层说明
| 层级 | 职责 | 面试说法 |
|---|---|---|
| FastAPI 服务层 | 提供 HTTP API,做请求校验和响应封装 | 对外暴露统一服务入口 |
| LangGraph 编排层 | 控制 Agent 流程、分支、循环、状态 | 负责决策与流程编排 |
| RAG 检索层 | 解析 PDF、切块、embedding、向量检索 | 提供知识增强能力 |
| Tool 层 | 封装外部能力,如查数据库、保存文件 | 让 Agent 能执行动作 |
| 状态存储层 | 保存多轮对话、checkpoint、日志 | 支持上下文连续与问题复盘 |
| 输出结构化层 | 统一答案格式、引用、错误信息 | 保证前端和评估可解析 |
5. 推荐技术栈
| 模块 | 推荐技术 |
|---|---|
| Web 服务 | FastAPI |
| 参数校验 | Pydantic |
| Agent 编排 | LangGraph |
| LLM 调用 | OpenAI API / LangChain ChatModel |
| RAG 框架 | LangChain 或自写检索流程 |
| 向量数据库 | Chroma / FAISS |
| 文档解析 | PyMuPDF / pypdf |
| 状态存储 | SQLite / Redis / LangGraph checkpointer |
| 日志 | Python logging / SQLite query logs |
| 前端 | Streamlit / React / 简单 HTML |
| 部署 | Docker / docker-compose |
初学阶段建议:
FastAPI + LangGraph + Chroma + SQLite + 简单前端
不要一开始就上太多复杂组件。
6. 项目核心流程
6.1 总流程
用户发送问题
-> FastAPI 接收请求
-> Pydantic 校验 request
-> LangGraph 初始化 state
-> classify_node 判断问题类型
-> route_node 决定下一步
-> 知识问题:retrieve_node
-> 外部任务:tool_node
-> 闲聊/改写:direct_answer_node
-> answer_node 生成最终答案
-> format_node 输出结构化结果
-> log_node 记录问题、工具调用、耗时、引用
-> FastAPI 返回响应
6.2 LangGraph 节点设计
建议先做这些节点:
| 节点 | 作用 |
|---|---|
classify_question |
判断问题类型 |
retrieve_knowledge |
调用 RAG 检索 |
execute_tool |
调用外部工具 |
generate_answer |
根据 state 生成最终回答 |
format_output |
转成统一 JSON |
handle_error |
错误兜底 |
save_log |
保存日志 |
7. 问题分类设计
问题分类是阶段 5 的关键模块。
不要所有问题都直接 RAG。
7.1 推荐分类
class QuestionType(str, Enum):
KNOWLEDGE_QA = "knowledge_qa"
DOCUMENT_SUMMARY = "document_summary"
COMPARISON = "comparison"
EXTERNAL_TASK = "external_task"
FOLLOW_UP = "follow_up"
CHITCHAT = "chitchat"
UNKNOWN = "unknown"
7.2 分类结果结构
{
"question_type": "knowledge_qa",
"need_retrieval": true,
"need_tool": false,
"confidence": 0.86,
"reason": "用户询问知识库中的概念解释,需要检索文档片段。"
}
7.3 路由规则
| 分类 | 下一步 |
|---|---|
knowledge_qa |
RAG 检索 |
document_summary |
RAG 检索 + 总结 |
comparison |
多次检索 + 对比 |
external_task |
工具调用 |
follow_up |
读取历史 state |
chitchat |
直接回答 |
unknown |
澄清问题或兜底回答 |
8. RAG 检索层设计
8.1 RAG 层职责
RAG 检索层不应该关心 Agent 决策,它只负责:
输入 query -> 返回相关文档片段
也就是说,它应该被封装成一个工具:
def search_knowledge_base(query: str, top_k: int = 5, filters: dict | None = None) -> dict:
...
返回:
{
"ok": true,
"query": "RAG 如何减少幻觉",
"chunks": [
{
"doc_id": "paper_001",
"title": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks",
"page": 3,
"score": 0.82,
"content": "RAG combines parametric memory with non-parametric memory..."
}
]
}
8.2 为什么要把 RAG 封装成 Tool
因为 Agent 的视角里,RAG 只是众多工具之一:
search_knowledge_base
get_document_metadata
list_documents
save_note
query_database
这样系统就从“固定 RAG 问答”升级为“Agentic RAG”。
8.3 检索优化点
| 优化点 | 作用 |
|---|---|
| query rewrite | 用户问题太口语化时改写检索词 |
| top-k 调整 | 简单问题少取,复杂问题多取 |
| metadata filter | 按文件、年份、章节过滤 |
| hybrid search | 语义检索 + 关键词检索 |
| rerank | 对初始召回结果重新排序 |
| source citation | 回答附来源,降低幻觉 |
| no-answer 判断 | 检索不到时不要硬答 |
9. Tool 层设计
Tool 层负责外部能力。
初学项目不用做太多工具,建议先做 3 个:
| 工具 | 功能 |
|---|---|
search_knowledge_base |
检索文档片段 |
get_document_metadata |
查询文档标题、作者、页数等 |
save_learning_note |
把回答保存成 Markdown 学习笔记 |
如果想加分,可以再做:
| 工具 | 功能 |
|---|---|
list_documents |
列出知识库文档 |
query_question_logs |
查询历史问题日志 |
calculator |
简单计算 |
web_search |
联网搜索,但要注意安全和引用 |
create_todo |
创建待办事项 |
9.1 工具 schema 示例
search_knowledge_tool = {
"type": "function",
"name": "search_knowledge_base",
"description": "Search relevant chunks from the local document knowledge base.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query rewritten from the user's question."
},
"top_k": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"description": "Number of chunks to retrieve."
},
"doc_type": {
"type": "string",
"enum": ["paper", "note", "manual", "all"],
"description": "Type of document to search."
}
},
"required": ["query", "top_k", "doc_type"],
"additionalProperties": False
},
"strict": True
}
9.2 工具执行器
def execute_tool(tool_name: str, arguments: dict) -> dict:
if tool_name == "search_knowledge_base":
return search_knowledge_base(**arguments)
if tool_name == "get_document_metadata":
return get_document_metadata(**arguments)
if tool_name == "save_learning_note":
return save_learning_note(**arguments)
return {
"ok": False,
"error_code": "UNKNOWN_TOOL",
"message": f"Tool {tool_name} is not registered.",
"retryable": False
}
10. 状态设计
阶段 5 一定要有状态。
因为面试官会问:
多轮对话怎么处理?
上一轮检索结果怎么保存?
用户追问“它”和“刚才那个方法”时怎么办?
10.1 State 字段设计
from typing import TypedDict, List, Optional, Literal, Any
class AgentState(TypedDict):
thread_id: str
user_id: str
question: str
question_type: Optional[str]
route: Optional[str]
messages: List[dict]
rewritten_query: Optional[str]
retrieved_chunks: List[dict]
tool_calls: List[dict]
tool_results: List[dict]
answer: Optional[str]
citations: List[dict]
error: Optional[dict]
step_count: int
final_output: Optional[dict]
10.2 每个字段的作用
| 字段 | 作用 |
|---|---|
thread_id |
区分不同对话 |
user_id |
区分用户 |
question |
当前问题 |
question_type |
分类结果 |
route |
当前走哪条路径 |
messages |
多轮上下文 |
rewritten_query |
改写后的检索 query |
retrieved_chunks |
检索片段 |
tool_calls |
工具调用记录 |
tool_results |
工具返回 |
answer |
最终答案 |
citations |
引用来源 |
error |
错误信息 |
step_count |
防止无限循环 |
final_output |
返回给前端的结构化结果 |
11. 结构化输出设计
不要只返回一段字符串。
建议 API 返回:
{
"answer": "RAG 可以通过检索外部知识减少模型凭空生成的概率...",
"question_type": "knowledge_qa",
"citations": [
{
"title": "RAG Survey",
"page": 5,
"chunk_id": "rag_001_p5_c2",
"snippet": "Retrieval-augmented models condition generation on retrieved documents..."
}
],
"tool_trace": [
{
"tool_name": "search_knowledge_base",
"arguments": {
"query": "RAG hallucination reduction",
"top_k": 5
},
"status": "success",
"latency_ms": 124
}
],
"confidence": 0.78,
"error": null
}
11.1 为什么结构化输出重要
面试可以这样说:
我没有直接把模型输出的文本返回给前端,而是定义了统一的响应结构,包括 answer、citations、tool_trace、confidence 和 error。这样前端可以展示引用片段,后端可以记录工具调用,后续评估也可以基于结构化字段做统计。
12. FastAPI 接口设计
12.1 核心接口
| 接口 | 方法 | 作用 |
|---|---|---|
/chat |
POST | 用户提问,返回 Agent 回答 |
/documents/upload |
POST | 上传 PDF |
/documents |
GET | 查看知识库文档 |
/logs/questions |
GET | 查看问题日志 |
/health |
GET | 健康检查 |
12.2 请求体
from pydantic import BaseModel, Field
from typing import Optional
class ChatRequest(BaseModel):
user_id: str = Field(..., description="User ID")
thread_id: str = Field(..., description="Conversation thread ID")
question: str = Field(..., min_length=1, max_length=2000)
top_k: int = Field(default=5, ge=1, le=10)
enable_trace: bool = True
12.3 响应体
from typing import List, Optional, Any
from pydantic import BaseModel
class Citation(BaseModel):
title: str
page: Optional[int] = None
chunk_id: str
snippet: str
class ToolTrace(BaseModel):
tool_name: str
arguments: dict
status: str
latency_ms: int
class ChatResponse(BaseModel):
answer: str
question_type: str
citations: List[Citation]
tool_trace: List[ToolTrace]
confidence: Optional[float] = None
error: Optional[dict] = None
12.4 FastAPI 路由示例
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
result = graph.invoke(
{
"thread_id": request.thread_id,
"user_id": request.user_id,
"question": request.question,
"messages": [],
"retrieved_chunks": [],
"tool_calls": [],
"tool_results": [],
"citations": [],
"step_count": 0,
"error": None,
"answer": None,
"final_output": None,
},
config={
"configurable": {
"thread_id": request.thread_id
}
}
)
return result["final_output"]
13. LangGraph 编排设计
13.1 推荐图结构
START
-> classify_question
-> route_question
-> retrieve_knowledge
-> execute_tool
-> direct_answer
-> generate_answer
-> format_output
-> save_log
-> END
如果要支持检索为空后的 query rewrite:
START
-> classify_question
-> route_question
-> retrieve_knowledge
-> check_retrieval
-> generate_answer
-> rewrite_query -> retrieve_knowledge
-> execute_tool
-> direct_answer
-> format_output
-> save_log
-> END
13.2 条件分支函数
def route_question(state: AgentState) -> str:
question_type = state["question_type"]
if question_type in ["knowledge_qa", "document_summary", "comparison"]:
return "retrieve_knowledge"
if question_type == "external_task":
return "execute_tool"
if question_type in ["chitchat", "follow_up"]:
return "direct_answer"
return "handle_error"
13.3 检索后判断
def check_retrieval(state: AgentState) -> str:
chunks = state["retrieved_chunks"]
if len(chunks) > 0:
return "generate_answer"
if state["step_count"] >= 2:
return "generate_answer"
return "rewrite_query"
14. 错误兜底设计
Agent 项目必须能讲错误兜底。
14.1 常见错误与处理
| 错误 | 场景 | 处理 |
|---|---|---|
| 参数错误 | 用户问题为空、top_k 超范围 | Pydantic 校验 |
| 检索为空 | 向量库没有相关内容 | query rewrite,仍无结果则说明未找到 |
| 工具失败 | 外部 API 超时 | retry + timeout + fallback |
| 模型输出不合法 | JSON 解析失败 | 重新生成或 fallback |
| 循环失控 | Agent 一直调用工具 | 最大步数限制 |
| 权限问题 | 查询无权限文档 | 返回权限错误 |
| 敏感操作 | 保存、删除、发送请求 | 二次确认 |
14.2 兜底回答模板
我在当前知识库中没有检索到足够可靠的依据,因此不能直接给出确定结论。
你可以尝试:
1. 换一种问法;
2. 指定具体文档;
3. 上传相关资料;
4. 降低筛选条件。
14.3 工具错误结构
{
"ok": false,
"error_code": "TOOL_TIMEOUT",
"message": "search_knowledge_base timed out after 5 seconds",
"retryable": true
}
15. 日志记录设计
日志是加分项,但非常适合面试。
15.1 为什么要记录日志
因为你需要知道:
- 用户都问了什么;
- 哪些问题经常检索不到;
- 哪些工具经常失败;
- 平均响应时间是多少;
- 哪些回答没有引用;
- 哪些问题成本高;
- 哪些分类不准确。
15.2 日志字段
CREATE TABLE question_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
thread_id TEXT,
question TEXT,
question_type TEXT,
answer TEXT,
citations_count INTEGER,
tool_calls_count INTEGER,
latency_ms INTEGER,
success BOOLEAN,
error_code TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
15.3 工具调用日志
CREATE TABLE tool_call_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
thread_id TEXT,
tool_name TEXT,
arguments_json TEXT,
result_status TEXT,
latency_ms INTEGER,
error_code TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
16. 工具调用过程可视化
前端可以展示:
用户问题:RAG 为什么能减少幻觉?
执行过程:
1. classify_question -> knowledge_qa
2. search_knowledge_base(query="RAG hallucination", top_k=5) -> success
3. generate_answer -> success
4. format_output -> success
引用来源:
- RAG Survey, page 5
- Lewis et al., 2020, page 3
16.1 可视化价值
面试可以这样说:
我把工具调用过程作为 tool_trace 返回给前端,这样用户能看到系统为什么这么回答,开发者也能快速定位是分类错了、检索错了,还是生成阶段出错。
17. 简单前端建议
不建议一开始做复杂 React。
优先级:
第一版:FastAPI Swagger / Postman 能调通
第二版:Streamlit 简单聊天界面
第三版:展示引用片段和工具调用 trace
第四版:再考虑 React
17.1 Streamlit 页面功能
| 功能 | 说明 |
|---|---|
| 聊天输入框 | 输入问题 |
| 答案展示 | 展示最终 answer |
| 引用展示 | 展示引用片段 |
| 工具 trace | 展示每一步工具调用 |
| 文档上传 | 上传 PDF |
| 日志查看 | 查看最近问题 |
18. 项目目录结构
建议仓库结构:
knowledge-agent-assistant/
README.md
requirements.txt
.env.example
docker-compose.yml
Dockerfile
app/
main.py
api/
routes_chat.py
routes_documents.py
routes_logs.py
schemas/
chat.py
document.py
common.py
agent/
state.py
graph.py
nodes.py
router.py
prompts.py
rag/
loader.py
splitter.py
embeddings.py
vectorstore.py
retriever.py
citation.py
tools/
registry.py
knowledge_tools.py
document_tools.py
note_tools.py
storage/
db.py
models.py
repositories.py
checkpointer.py
observability/
logger.py
trace.py
metrics.py
config.py
data/
raw_pdfs/
chroma_db/
notes/
tests/
test_rag.py
test_tools.py
test_graph.py
test_api.py
docs/
architecture.md
api.md
eval.md
interview_script.md
19. README 应该怎么写
README 不要只写“怎么运行”,还要体现项目价值。
建议结构:
# Knowledge Agent Assistant
## 1. 项目背景
## 2. 核心功能
## 3. 系统架构
## 4. 技术栈
## 5. 快速启动
## 6. API 示例
## 7. Agent 工作流
## 8. RAG 检索设计
## 9. 工具调用设计
## 10. 状态管理
## 11. 错误处理
## 12. 评估指标
## 13. 后续优化
20. 本周开发计划
Day 1:确定项目骨架
产出:
- 创建 Git 仓库;
- 搭 FastAPI;
- 写
/health; - 写基础目录结构;
- 写 ChatRequest / ChatResponse。
验收:
POST /chat 可以返回 mock 答案
GET /health 返回 ok
Day 2:接入 RAG 检索层
产出:
- PDF 上传;
- 文档解析;
- chunk 切分;
- embedding;
- Chroma 入库;
- 检索 top-k;
- 返回引用片段。
验收:
输入问题 -> 能检索出相关 chunk -> 返回 title/page/snippet
Day 3:封装 Tool 层
产出:
search_knowledge_base;get_document_metadata;list_documents;- 工具 registry;
- 工具错误结构。
验收:
工具可以被统一 execute_tool 调用
工具成功和失败都返回结构化结果
Day 4:接入 LangGraph
产出:
- AgentState;
- classify 节点;
- retrieve 节点;
- tool 节点;
- answer 节点;
- 条件边;
- 最大 step 控制。
验收:
知识问题自动走 RAG
工具问题自动走 Tool
普通问题可以直接回答
Day 5:加入状态与日志
产出:
- thread_id;
- SQLite 日志表;
- question_logs;
- tool_call_logs;
- 保存 tool_trace;
- 支持多轮上下文。
验收:
同一个 thread_id 下可以追问
日志里能看到问题、分类、工具调用、耗时
Day 6:结构化输出与前端
产出:
- 统一 ChatResponse;
- citations;
- tool_trace;
- error;
- Streamlit 简单界面;
- 引用展示;
- 工具过程展示。
验收:
前端能显示答案、引用和调用过程
Day 7:文档与面试材料
产出:
- README;
- 架构图;
- 2 分钟项目介绍稿;
- 难点总结;
- 评估指标;
- 后续优化计划。
验收:
可以完整演示项目
可以用 2 分钟讲清楚项目
可以回答面试追问
21. 架构图 Mermaid 版本
可以放到 README 或 docs/architecture.md 中。
22. 2 分钟项目介绍稿
下面这版可以直接背。
我做的是一个带知识库检索和工具调用能力的 AI Assistant,主要面向论文和技术文档问答场景。
项目最开始是一个普通 RAG 系统,用户提问后,系统从 PDF 知识库中检索相关 chunk,再把检索结果拼到 prompt 里让模型回答。但我后来发现,真实场景里的问题不都是知识问答,有些问题需要查文档元信息,有些需要列出已有文件,有些需要根据上一轮对话继续追问,所以我把它升级成了一个 Agent 系统。
架构上,我把系统分成六层:最外层是 FastAPI 服务层,负责请求校验和响应返回;中间用 LangGraph 做编排层,负责问题分类、路由、工具调用和状态流转;底层有 RAG 检索层,用来完成 PDF 解析、切块、向量化和 top-k 检索;同时还有 Tool 层,把知识库检索、文档元信息查询、笔记保存等能力封装成工具;状态存储层负责保存 thread_id、对话状态、工具调用日志;最后用结构化输出层统一返回 answer、citations、tool_trace 和 error。
这个项目的关键点是,我没有让所有问题都强行走 RAG,而是先做问题分类。知识类问题走 RAG,外部任务走工具调用,多轮追问会读取当前 thread 的状态。这样系统比普通 RAG 更灵活,也更接近真实 Agent 应用。
技术难点主要有三个:第一是检索结果不稳定,所以我加了 query rewrite、top-k 控制和引用片段;第二是 Agent 可能循环失控,所以我设置了最大步数、工具错误结构和兜底回答;第三是回答不可追踪,所以我记录了 tool_trace 和问题日志,方便定位是分类、检索还是生成环节出错。
最后我用检索命中率、回答引用覆盖率、工具调用成功率、平均延迟和错误率来评估系统效果。后续如果继续优化,我会加入 hybrid search、rerank、权限控制和更完善的前端可视化。
23. 面试讲解框架
面试官让你介绍项目时,不要上来讲代码细节。
按照这个顺序讲:
1. 业务场景
2. 系统架构
3. 核心流程
4. 关键模块
5. 技术难点
6. 优化与评估
23.1 业务场景
这个项目解决的是私有文档知识库问答和任务执行的问题。用户可以用自然语言询问 PDF 论文、技术文档,也可以让系统执行一些外部任务,比如查询文档元信息、保存学习笔记。相比普通聊天模型,它能基于私有知识回答,并给出引用来源。
23.2 系统架构
系统分为 FastAPI 服务层、LangGraph 编排层、RAG 检索层、Tool 层、状态存储层和结构化输出层。FastAPI 负责接口,LangGraph 负责任务编排,RAG 负责知识检索,Tool 层封装外部能力,状态层保存多轮上下文和日志,输出层保证回答结构统一。
23.3 关键模块
关键模块包括问题分类模块、RAG 检索模块、工具调用模块、状态管理模块和结构化输出模块。问题分类决定走 RAG 还是工具;RAG 模块返回 chunk 和引用;工具模块统一注册和执行工具;状态模块保存 thread_id 下的上下文;输出模块返回 answer、citations 和 tool_trace。
23.4 技术难点
难点主要是三类:第一,检索质量不稳定,需要做 query rewrite、chunk 设计和引用控制;第二,Agent 流程不可控,需要设置最大步数、工具白名单和错误兜底;第三,多轮上下文容易混乱,所以用 thread_id 和 state 管理当前会话状态。
23.5 优化方案
优化方面,我从检索、流程和工程三个方向做。检索上可以加入 hybrid search 和 rerank;流程上可以把固定流程用 workflow,开放任务用 Agent;工程上记录 tool_trace、latency 和 error code,方便调试和评估。
23.6 评估指标
评估指标包括检索命中率、答案引用覆盖率、工具调用成功率、平均延迟、p95 延迟、错误率和用户反馈。对于 RAG,我重点看问题是否检索到正确 chunk;对于 Agent,我重点看工具是否选对、调用是否成功、是否能在有限步骤内完成任务。
24. 面试高频追问
Q1:你这个项目和普通 RAG 有什么区别?
普通 RAG 是固定流程:
问题 -> 检索 -> 回答
我的项目是 Agentic RAG:
问题 -> 分类 -> 路由 -> 检索 / 工具 / 直接回答 -> 状态更新 -> 结构化输出
区别在于:
- 不是所有问题都走检索;
- RAG 被封装成工具;
- 支持外部工具调用;
- 支持多轮状态;
- 有工具调用 trace;
- 有错误兜底和日志。
Q2:为什么要用 LangGraph,不直接 while loop?
简单 Agent 可以用 while loop,但复杂后会出现分支混乱、状态散落、流程不可观察的问题。LangGraph 把流程显式建模成 State、Node、Edge,可以清楚表达分类、路由、检索、工具调用、错误处理等节点,也方便加入 checkpoint、状态恢复和调试。
Q3:如何判断一个问题走 RAG 还是 Tool?
我会先做问题分类,输出结构化分类结果,例如 knowledge_qa、external_task、follow_up。知识解释、论文总结、文档对比走 RAG;查询文档数量、保存笔记、查询日志这类动作走 Tool;如果是追问,则结合 thread state 判断是否需要复用上一轮结果。
Q4:怎么处理检索不到结果?
我不会让模型硬答。流程是:
第一次检索为空
-> query rewrite
-> 再检索
-> 仍然为空
-> 返回未找到足够依据的兜底回答
同时会在日志里记录这类问题,后续分析是不是 chunk 切分、embedding、query 改写或知识库覆盖有问题。
Q5:怎么防止 Agent 无限调用工具?
我设置了:
- 最大 step 数;
- 最大工具调用次数;
- 工具白名单;
- 工具超时;
- 连续失败停止;
- 敏感操作需要确认;
- 所有工具错误都结构化返回。
Q6:多轮对话怎么做?
每个会话有 thread_id。同一个 thread 下会保存 messages、检索结果、工具调用结果、引用信息和中间状态。用户追问“它”“刚才那个方法”时,系统可以从当前 state 中恢复上下文。
Q7:引用片段怎么保证可靠?
每次 RAG 检索都会返回 doc_id、title、page、chunk_id、snippet 和相似度分数。生成答案时要求模型只基于这些片段回答,最终 response 中会把 citations 返回给前端。如果没有足够片段,就返回不确定,而不是强行回答。
Q8:如何评估这个系统?
我会分 RAG 和 Agent 两部分评估。
RAG 侧:
- 检索命中率;
- top-k recall;
- 引用覆盖率;
- 答案是否基于引用。
Agent 侧:
- 问题分类准确率;
- 工具选择准确率;
- 工具调用成功率;
- 平均工具调用次数;
- 最大步数内完成率;
- 平均延迟和 p95 延迟;
- 错误率。
25. 简历写法建议
可以写成这样:
Knowledge Agent Assistant|基于 RAG + LangGraph 的知识库智能助手
- 基于 FastAPI + LangGraph 构建多工具 Agent 系统,将 PDF 知识库检索、文档元信息查询、学习笔记保存等能力封装为 Tool,并通过状态图实现问题分类、路由、检索、工具调用和结构化输出。
- 设计 RAG 检索层,支持 PDF 解析、chunk 切分、embedding 入库、top-k 检索和引用片段返回,回答中附带 doc_id、page、chunk_id 等来源信息,降低模型幻觉。
- 引入 thread_id 级别状态管理,保存多轮对话上下文、工具调用结果和引用信息,支持用户基于上一轮结果继续追问。
- 设计工具调用 trace、问题日志和错误兜底机制,记录问题类型、工具调用、耗时、错误码等信息,用于调试、评估和优化。
如果简历空间少,可以压缩成两条:
- 基于 FastAPI + LangGraph + Chroma 实现知识库 Agent,将 RAG 检索、文档元信息查询、笔记保存等能力封装为 Tool,支持问题分类、动态路由、多轮状态和结构化输出。
- 设计引用片段、tool trace、错误兜底和问题日志机制,提升回答可追踪性与系统稳定性,并使用检索命中率、工具成功率、延迟等指标评估效果。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)