一篇关于「药店智能 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
2
3
4
5
6
7
8
9
analyze_query(意图分类 + 实体抽取)
│
├─→ greeting_response ──────────────→ finalize
├─→ inventory_retrieval / sales_retrieval(查库存/查销售,走工具)
├─→ drug_info_retrieval / general_retrieval
│ │
│ └─→ rag_retrieval(混合检索)→ rerank(精排)→ generate_response
└─→ data_query(NL2SQL 走数据查询)
└──────→ finalize → END

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,不裸死(详见系列第二篇)。

写在最后

这是系列的第一篇,先把”系统长什么样”讲清楚。后面会按改造里程碑一篇篇拆:

  1. reranker 1.1GB 落地实战:一行 .env 让精排器静默降级了一个多月(完整稿待发布)
  2. LLM 降级链:单点故障到多 provider 容错
  3. SSE 真流式证明与并发取舍:假流式是怎么被治好的
  4. MCP 接入、Eval 评测体系、Agent 记忆分层、可观测性、多 Agent 讨论共识……

改造即素材,掌握即发布。每一篇都是真实踩坑,不是教程复述。

(文中项目信息已脱敏。评测数据来自项目自身测试集与回归脚本。未包含任何私密配置。)



技术分享  

AI Agent LangGraph RAG FastAPI Python

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!