系列一我们用 21 篇文章系统学完了 LLM 基础、单 Agent、Context/RAG、多 Agent 到生产化——但那始终是"站在岸上学游泳"。每个概念都懂,真要做一个能在我公司里跑起来的东西,还是得自己下水。
所以系列二换一种写法:不讲了,直接做。 我会从零开始实现一个真实可用的"企业知识助手 Agent",并且把每一步的实现过程、决策理由、踩过的坑都写成文章——既是开发日记,也是教程。你看到的就是一个真实项目从空目录到上线的全过程。
如果你还没读过系列一,建议先看完,因为本项目会反复用到那里的概念:RAG(阶段三)、Function Calling(阶段二)、权限与安全(阶段五)。
我要做什么:需求清单
每个公司都有这类痛点:制度文档散落各处、年假余额得找 HR 问、流程说不清。我想做一个内部助手,同事用自然语言提问,它能:
| 能力 | 说明 | 技术对应 |
|---|---|---|
| 私有文件问答 | 基于公司内部 PDF/Word 文档回答问题 | RAG |
| 数据库查询 | 比如"我还有几天年假",去数据库读数据 | 参数化查询工具 |
| 权限控制 | 张三不能问李四的年假 | 身份注入 + 行级过滤 |
| 友好降级 | 不知道或不能答时,给友好提示而非报错 | 统一降级模板 |
| 流程图表现 | 必要时用流程图说清步骤 | Mermaid 输出 |
注意最后一条:很多问答本质上是一套"流程"(比如请假步骤),文字描述很绕,用流程图一眼就懂。所以 Agent 在合适时要能输出 Mermaid 流程图。
整体架构
核心思路就一句话:用户身份从入口注入,之后所有数据访问都强制带上这个身份,模型只能决定"调不调工具",不能决定"查谁"。
流程是:
- 用户提问,入口层先鉴权,拿到
user_id(比如张三),把它注入到整条链路 - Agent 编排层(LLM + Function Calling)判断该调哪个工具
- 要么调
search_knowledge_base走 RAG,要么调get_leave_balance走数据库 - 数据库查询强制行级过滤:只返回当前用户自己的数据
- 生成层组织答案,需要时输出 Mermaid
- 任何异常(检索为空 / 工具失败 / 模型判不可答)都走降级层给友好提示
技术选型与理由
| 层 | 选型 | 为什么 |
|---|---|---|
| 语言 | Python 3.11+ | 生态全,和主流 LLM 库对齐 |
| Web | FastAPI + 轻前端 | 后端做登录和 Agent 逻辑,前端先快出 UI |
| LLM | 直连外网 API(OpenAI/Claude) | 配 key 即用 |
| RAG 解析 | PyMuPDF + python-docx | 专治 PDF/Word |
| 切分 | langchain RecursiveCharacterTextSplitter | 通用稳妥 |
| Embedding | OpenAI text-embedding-3-small | 云端 embedding,省本地算力 |
| 向量库 | ChromaDB | MVP 最轻,后续可换 Qdrant |
| 数据库 | SQLAlchemy + 参数化查询 | 防注入,年假现成字段直接查 |
| 流程图 | Mermaid + 前端 mermaid.js | 文本即可渲染,可版本化 |
| 降级 | 统一友好提示模板 | 体验兜底 |
几个关键决策值得说清楚:
- 数据库查询用"预定义函数"而不是 NL2SQL。年假是表里现成字段,直接
get_leave_balance(user_id)查就行;NL2SQL 在内网 + 权限场景准确率和安全性都差,MVP 阶段不碰。 - user_id 永远由服务端注入。工具暴露给模型的 schema 里
get_leave_balance()根本没有 user 参数,模型想"查李四"也无从下手——这是后面要反复强调的红线。 - Embedding 用云端。既然 LLM 都直连外网了,embedding 也用云端最省事。
文章阶段总览
系列二按"边做边写"组织,分 1 篇开篇 + 5 个阶段,每篇对应实现的一个真实里程碑:
| 阶段 | 主题 | 对应里程碑 | 这篇记录什么 |
|---|---|---|---|
| 🗺 开篇 | 为什么做 & 怎么搭 | 总览 | 需求、架构、选型、阶段计划(就是本文) |
| 一 | 搭地基:最小闭环 | M0 | FastAPI 骨架 + LLM 接入 + 单轮问答;为什么先不碰 RAG/DB |
| 二 | 让 Agent 读懂私有文件 | M1 | PDF/Word 解析→切分→Embedding→ChromaDB→检索→工具封装 |
| 三 | 查数据 + 守权限 | M2+M3 | SQLAlchemy 建模、参数化查询;JWT 登录、user_id 注入(权限红线) |
| 四 | 更聪明更安全 | M4 | Mermaid 流程图触发;检索空/失败/不可答→友好降级 |
| 五 | 交给同事 | M5 | Web UI + 登录页 + 内网部署 + 项目复盘 |
阶段三把"查数据"和"守权限"合成一篇,因为权限是数据库查询的出厂约束,拆开反而割裂。
每篇文章怎么写
为了保证"边做边写"也有统一节奏,每篇都按这 7 个固定部分来:
- 本篇目标 / 当前进度 —— 做完后 Agent 能多干啥
- 设计决策 —— 我为什么这么选,包括被我否掉的方案
- 关键实现 —— 核心代码片段 + 讲解(不贴全文,讲清思路)
- 踩坑与调试 —— 真实遇到的问题、怎么解决的
- 验证效果 —— 跑起来的示例对话 / 截图
- 架构图更新 —— 当前阶段的 SVG
- 下一篇预告
这样你读到的不是事后美化的"标准答案",而是一个人真实把项目做出来的全过程。
一条红线
整个项目有一条绝对不能破的红线:权限。
企业场景里,“张三查到李四的年假"不是 bug,是事故。所以权限不能靠"提醒模型别乱查"来实现,而是做成系统级的硬约束——user_id 从登录态来,数据库查询函数根本不接受外部传入的 user 参数。这条红线会贯穿阶段三,也会在测试环节专门验证。
下一篇预告
下一篇是阶段一:搭地基(M0)。我会从空目录开始,搭出 FastAPI 骨架、接通 LLM、跑通第一个单轮问答接口,并解释为什么第一步先不碰 RAG 和数据库——先把最不确定的"链路通不通"验证掉。
如果你也想从零做一个 Agent,欢迎跟着这个系列一起写。我们下篇见。