一篇关于「药店智能 Agent 系统」的实践记录。这是我自己独立开发的全栈 AI Agent 项目:LangGraph 编排 11 节点工作流 + 三级 RAG(向量 / BM25 / 交叉编码重排)+ Function Calling 工具系统 + Guardrails + 真 SSE 流式输出,知识库 702 条,一套 FastAPI 服务端加 Web 前端。这篇是系列总纲,讲清楚系统长什么样、为什么这么设计,以及后续会拆开的几个技术点。
一、为什么是药店?
选药店场景不是随手挑的。
Agent 落地最常见的形态就是客服助手——LangChain《State of AI Agents》报告里,客服场景占了生产用例的 45.8%。药店恰好是客服助手的理想样本:
- 有真实检索需求:顾客问药,回答必须基于可靠的药品知识,不能靠模型硬编——RAG 的天然落地场;
- 有工具调用场景:查库存、查销售、查药品相互作用,需要接数据、接 SQL,而不是纯聊天;
- 有安全底线:医药回答错不得,幻觉检测、来源引用、Guardrails 都是刚需,正好把”安全”这个面试必考点做扎实。
一句话:把 Agent 的核心能力(编排、检索、工具、安全)全部压进一个能跑起来的真实产品里。
二、系统全景
| 层 | 技术 | 职责 |
|---|---|---|
| 编排层 | LangGraph StateGraph,11 节点 | 问答主链路的状态机:意图分类 → 分支路由 → 检索 → 生成 |
| RAG 层 | ChromaDB 向量 + BM25 + CrossEncoder 重排 | 三级检索,向量召回、BM25 兜底、交叉编码器精排 |
| 工具层 | Function Calling + ToolRegistry | 库存 / 销售 / 药品信息 / NL2SQL / 排班等工具,LLM 自主选调 |
| 安全层 | Guardrails + 幻觉词库 | 输入分级防护、输出幻觉检测、来源归属校验 |
| 服务层 | FastAPI,49 端点 | /query、/query/stream(真SSE)、/rag/*、/admin/*、/api/training/、/api/metrics |
| 数据层 | ChromaDB + SQLite + JSON 知识库 | 702 条知识(32 个 JSON 主题)、会话记忆、审计日志 |
| 前端 | 原生 HTML/JS(index/admin/traces) | 聊天界面、管理后台、trace 查看 |
7 个模块、完整闭环:agents/(主 Agent + 6 个业务子 Agent)、rag/、core/(llm/guardrails/metrics/auth/audit…)、tools/、web/、training/。
三、问答主链路:LangGraph 11 节点工作流
为什么用 LangGraph 状态图而不是裸 ReAct 循环?因为可控性和可观测性——每个节点一个职责,路由是显式的,出了错能定位到具体节点。
1 | analyze_query(意图分类 + 实体抽取) |
8 类意图:问候(greeting)、数据查询(data_query)、库存(inventory)、销售(sales)、药品信息(drug_info)、药物相互作用(interaction)、副作用(side_effect)、通用问答(general)。意图分类用”关键词加权 + LLM 二次分类”的混合方式,既快又准。工程上把原本 1600 多行的单文件按节点域拆成了 agents/nodes/ 子包(state/prompts/entities/analysis/retrieval/agentic/rag/generation/graph),AST 验证零改写,32 个对外名字全兼容。
四、三级 RAG:向量 + BM25 + 重排
检索是这套系统的精度核心,三层各司其职:
| 级 | 手段 | 作用 |
|---|---|---|
| 1 | ChromaDB 向量检索(all-MiniLM) | 语义召回 |
| 2 | BM25 关键词检索(jieba 词级分词) | 精确匹配兜底,专有名词不漏 |
| 3 | CrossEncoder 交叉编码重排(bge-reranker-base) | 最后一道精排,精度最高 |
真实评测数据(2026-10 验收口径,P021/P029 全量回归):
1 | P@3 = 73.3% MRR = 96.3% Bad Case = 0 |
这两个数不是拍出来的:修 BM25 分词粒度(单字 → jieba 词级 + 功能词停用表)后,P@3 从 70.4% 提到 73.3%,MRR 从 92.96% 提到 96.3%,bad case 归零。重排这一级踩的坑(模型 1.1GB 国内拉不下来、静默降级、测试全绿但精排关了两个多月)值得单独写一篇——系列第一篇就是它。
五、工具调用:让 Agent 不只动嘴
问答只是入口,真正的价值在工具链。系统里注册了一组 Function Calling 工具,LLM 根据用户意图自主选择调用:
- 库存查询(
inventory_tool):查库存、库存阈值、效期预警; - NL2SQL(
nl2sql):自然语言转 SQL,安全校验后只放行 SELECT,3 表 JOIN; - 药品信息(
function_calling):惰性加载知识库索引,标题精确 > 包含 > tags 命中; - 销售分析、员工、地理、天气等工具。
工具层还预留了 MCP 接入——把现有工具包成 MCP Server 是波次 2 的既定路线,也是系列后续选题。
六、工程化:49 端点 + JWT + 审计 + 多店
个人项目最容易翻车在”能跑就行”。这套系统按生产标准做:
- FastAPI 49 端点,/query 与 /query/stream(真 SSE token 流,不是假流式)分离;
- JWT 认证(bcrypt 用户库 + PyJWT 签发校验,密钥环境变量化不硬编码)+ 操作日志审计(JSONL 落盘)+ 多店切换(门店/员工只读上下文隔离);
- 测试:pytest 11 脚本全绿,覆盖缓存/幻觉/AB/多轮/NL2SQL/全链路/并发;
- 版本纪律:VERSION + CHANGELOG + version.sh,每波次验收升版本;
- 可观测:metrics / trace 链路 + traces.html 可视化页面;
- 配置单点化:路径/模型名/温度全部收敛到
config.py,一处开关切全组。
七、防御与可靠性:幻觉守卫 + 降级链
医药场景回答错是事故,所以防御做在生成之前和之后:
- Guardrails:输入分级防护(拒答越权/危险指令)+ 输出侧幻觉检测;
- 幻觉词库:共现判定 + 承诺词 + 数字承诺正则,实测正常样本误标 8 → 0,真幻觉 6/6 仍拦截;
- 来源引用:回答强制带知识来源,幻觉检测同步核验来源归属;
- LLM 降级链:DeepSeek 主 provider,故障/限流时自动降级到备用 provider,不裸死(详见系列第二篇)。
写在最后
这是系列的第一篇,先把”系统长什么样”讲清楚。后面会按改造里程碑一篇篇拆:
- reranker 1.1GB 落地实战:一行 .env 让精排器静默降级了一个多月(完整稿待发布)
- LLM 降级链:单点故障到多 provider 容错
- SSE 真流式证明与并发取舍:假流式是怎么被治好的
- MCP 接入、Eval 评测体系、Agent 记忆分层、可观测性、多 Agent 讨论共识……
改造即素材,掌握即发布。每一篇都是真实踩坑,不是教程复述。
(文中项目信息已脱敏。评测数据来自项目自身测试集与回归脚本。未包含任何私密配置。)