Agent 生产落地:从 demo 到产品的血泪经验
前言
虽然正儿八经做架构设计不到两年,但是我越来越笃信一个理念:好的架构不是”一步到位”的 perfection,而是在成本约束下的渐进式演进。
尤其是在构建 AI Agent 系统 时,这个理念更为重要。Agent 不同于传统的 API 调用或批处理任务,它是一个能自主决策、持续学习、与环境交互的智能体。这意味着:
- 它不是静态的:今天有效的策略,明天可能需要调整
- 它是有状态的:需要维护上下文、记忆、反馈循环
- 它是渐进的:从简单的固定工作流(Workflow)到复杂的自主决策(ReAct),需要一步步验证
这里的成本不只是钱,还包括:
- 实现成本:团队对 Agent 模式的理解、Prompt Engineering 能力、RAG 调优经验
- 部署成本:LLM 推理的延迟和费用、向量数据库的运维、多模型路由的复杂度
- 机会成本:为了做通用 Agent 而放弃的专用模型,值不值得
但我说的渐进式演进,绝不是无脑地给自己挖坑、留技术债的借口——当然,这话也不能说太绝。
实际上,有时候我会故意选择留一些”可控的技术债”,或者接受部分妥协。比如明知道某个设计将来可能需要推翻重来,但如果当前阶段的目标只是快速验证(打单、demo、MVP),那我可能干脆就不考虑那么远的扩展性。等真到了需要推倒重来的那一天,说明业务已经跑通了,那点重构成本完全值得。
还有一种情况:完全推翻重来也没关系。比如早期为了抢时间做的 demo,本来就是用来验证需求的,验证完了直接扔掉重写,反而比在一堆妥协之上缝缝补补更干净。
所以关键不是”不能留技术债”,而是清醒地知道自己在做什么选择——是为了赶时间而主动承担的技术债?还是为了偷懒而无意间埋的雷?前者可控,后者致命。
每一阶段都要有清晰的”逃生通道”和”回滚方案”,但具体是渐进演进还是推倒重来,得看场景,不能教条。
先说说背景
我们是在构建一个病历质控 Agent——核心职责是:医生写完病历后,Agent 自动评估质量,识别潜在风险并给出改进建议。
这看起来就是调个 LLM API,但实际上要做一个能持续运行、自主决策、从反馈中学习的 Agent 系统,坑很多:
- 医疗场景容错率极低:漏了过敏史或剂量错误,就是事故
- 需要可解释:必须说明为啥判定有问题,依据是什么
- 需要可控制:不能让它胡说,得有明确边界
- 需要可进化:能从医生反馈中学习,越用越准
所以我们设计了两阶段路线:Phase 1 固定工作流 → Phase 2 ReAct Agent,渐进式演进。
技术选型:不同阶段,不同选择
技术选型不是非黑即白,而是看阶段、看目标。我们的演进路径经历了三个阶段:
阶段一:预研验证(LangChain / LangGraph + Python)
刚开始预研时,我们的目标其实挺大:想做一个 1 + N 的 AI 能力底座。
1 是一个可微调、可沉淀领域能力的小模型,用来理解宠物医疗里的术语、病历结构、诊疗知识;N 是围绕业务长出来的一组 Agent,比如病历质控 Agent、用药检查 Agent、诊疗辅助 Agent、话术合规 Agent。
这个阶段最大的问题不是”怎么写一个 HTTP 调用”,而是 Agent 的边界到底在哪:
- 要不要多 Agent 协作?
- 工具调用怎么编排?
- 状态怎么保存?
- 什么时候需要 human-in-the-loop?
- 失败后怎么恢复?
所以当时选了 LangChain / LangGraph 这类流行框架。LangChain 更像 LLM 应用开发生态,模型调用、工具封装、RAG、Agent 都有;LangGraph 更偏 Agent 编排,适合探索长流程、状态化、多工具调用这些问题。
这时候用成熟生态没问题,因为核心目标是跑通 Agent 的各种可能性,快速验证边界。但这只是预研工具,不是生产承诺。等后面发现病历质控主链路其实是固定 Workflow,不需要复杂 Agent 编排时,我们才逐步做减法。
阶段二:打单/MVP(Spring AI + Java)
预研验证通过后,需要给客户、投资人演示。但目前主要的技术栈是 Java,团队内当前资源也有限,没法快速用 Python demo 去交付。
于是迁移到 Spring AI。这时候的目标是快速产出可演示的产品,框架封装得好,开发速度快,哪怕有点”魔法”、有点性能损耗,也先不管。
阶段三:生产落地(WebClient + Jackson)
真正要上线生产了,目标变成了稳定、可控、可运维。这时候 Spring AI 的问题暴露出来了:
- 对我们这个固定 Workflow 来说,框架抽象带来的收益不大
- 真正出问题时,我更想直接看到 HTTP 请求、响应、超时、重试和 JSON 解析细节
- 我们暂时不需要复杂的 Advisor、Tool Calling、Memory 抽象,自己控制反而更简单
这不是说 Spring AI 不适合生产。相反,如果你需要快速接入多模型、结构化输出、工具调用、RAG 编排,它是一个很好的选择。
但在我们的场景里,控制流很固定,业务风险又高,于是做减法,直接上 WebClient + Jackson。自己写 HTTP 调用、自己处理 JSON,代码多了一点,但超时、重试、日志、Schema 校验、错误码映射,每一层都能看得见、控得住。
| 阶段 | 目标 | 选型 | 理由 |
|---|---|---|---|
| 预研 | 探索 1+N Agent 边界 | LangChain / LangGraph (Python) | 生态成熟,适合快速验证多 Agent、工具调用、状态编排 |
| 打单 | 快速交付 | Spring AI (Java) | 适配技术栈,演示效果好 |
| 生产 | 稳定可控 | WebClient + Jackson | 少一层 AI 框架抽象,排查简单,长期维护成本低 |
你看,没有绝对的好坏,只有适合不适合当前阶段。
当然,基础设施层的选择是贯穿始终的——这些一旦选定就不容易改,所以一开始就做了减法:
| 最初的念头 | 最终的方案 | 理由 |
|---|---|---|
| Qdrant 向量库 | pgvector | 已有的 PostgreSQL 加个插件就搞定,零新增基础设施;Qdrant 留作规模上来后的迁移选项 |
| 独立的 Schema 隔离 | 表名前缀 agent_ | 同团队同代码库,schema 隔离是过度设计 |
| 只用在线 API | 先 DeepSeek API,私有化再本地 vLLM/Qwen3 | API 性价比高、交付快;只有客户明确要求私有化时,才值得承担本地推理成本 |
| text2vec-base-chinese | Qwen3 Embedding | 中英混合、长文本、RAG 检索效果更稳,后续可接 Qwen3 Reranker |
生产环境跟 POC 最大的区别就是:能少一个组件就少一个组件。每多一个依赖,就是多一个故障点。
模型部署方式这块,我反而觉得不是最核心的选择。
这里说的在线 API,不是指国外 API。国外 API 肯定不在我们的候选里,不是模型能力问题,而是数据合规、网络稳定性、客户接受度都绕不过去。主路径更现实的选择是 DeepSeek API:接入快,成本可控,也不用一开始就背 GPU 服务器和推理运维的包袱。
本地部署一开始并不是首选。真要自己上 vLLM、上 Qwen3,本质上是在买一套推理基础设施:GPU、显存、并发、排队、监控、扩容、模型升级,哪一个都不是免费的。对 MVP 和早期生产来说,性价比远不如 API。
后来之所以补本地部署方案,是因为有客户明确提了私有化诉求,希望模型和数据都在本地环境里跑。那我们就把 LLM 调用层抽成统一接口:默认走 DeepSeek API,私有化部署时切到 vLLM + Qwen3。这个切换并不复杂,真正复杂的是 Agent 的流程、工具边界、RAG 命中、反馈闭环和安全护栏。
换句话说:模型可以换,Agent 设计不能乱。这篇文章真正想讲的重点,也不是“到底用哪个模型”,而是怎么把 Agent 设计贴到宠物医疗的真实业务流程上。
这里很多人会问:那为什么不用 Qdrant?
Qdrant 是一个很强的向量数据库,尤其适合:
- 数据量上来以后,需要更稳定的低延迟 ANN 检索
- metadata filter 很复杂,比如按医院、科室、宠物类型、疾病分类、时间范围一起过滤
- 想做 dense + sparse hybrid search,甚至多向量检索、late interaction rerank
- 向量检索已经变成系统核心能力,需要单独扩容、监控、调参
但我现在还是先选 pgvector。原因很现实:我们已有 PostgreSQL,病历、反馈、样本、审计日志都在里面。Phase 1 的样本量也就几百到几万级,pgvector + HNSW 足够跑。医疗场景里,事务一致性、审计链路、少一个运维组件,比单纯追求向量检索性能更重要。
所以我的判断是:pgvector 是当前阶段的正确选择,Qdrant 是下一阶段的迁移选项。如果后面样本库涨到百万级,或者我们发现复杂过滤下 pgvector 召回/延迟开始不稳,再把检索层抽成独立服务迁到 Qdrant。这样迁移成本可控,也不会太早把系统复杂度抬上去。
RAG 设计:向量检索不是万能药
做医疗质控,核心是怎么让 AI “懂业务”。我们用了 RAG(检索增强生成)来做知识注入,但这里面有几个细节值得说说。
两种检索策略
我们设计了两条检索路径,分别对应不同的业务场景:
第一种:Few-shot 样本检索(对应 agent_few_shot_examples 表)
根据病历内容,从历史优质病例里找相似的。Embedding 首选 Qwen3 Embedding,存 pgvector。
这里有个坑:不能把整份病历一股脑塞进去做 embedding。病历是长文本,而且字段语义差异很大。主诉、诊断、用药、检查结果、医嘱,混在一起向量化,很容易把关键风险冲淡。
所以我们按字段拆 chunk:
- 主诉/现病史:用于找相似病例
- 诊断:用于找诊疗规范
- 用药:用于找剂量、禁忌、配伍规则
- 化验结果:用于找参考值和异常解释
第一版用纯向量召回,后面会加 keyword + vector 的混合检索,再接 Qwen3 Reranker 做二次排序。医疗场景里,召回率比“看起来很准”更重要,漏掉一条禁忌才是真正要命的问题。
1 | -- pgvector 用法很简单 |
这只是最小示例。生产里还得建索引,不然样本库一大,延迟会很难看:
1 | -- HNSW 构建慢一点、吃内存一点,但通常有更好的召回/延迟平衡 |
pgvector 还有 IVFFlat,构建更快,但要调 lists/probes。我们的选择是:样本量还不大时直接 HNSW,先把线上查询延迟和召回稳定住。
第二种:知识库检索(对应 agent_knowledge_base 表)
药典、诊疗规范、化验参考值。注意:图片不参与向量检索,存 OSS 里,检索命中后返回链接即可。
Embedding 模型的选择
embedding 模型我现在首选 Qwen3 Embedding,原因很直接:
- 跟 Qwen3 主模型体系更一致,RAG 链路里少一点“模型语义空间错位”
- 对中英混合、长文本、多任务检索更稳
- 后续可以接 Qwen3 Reranker,把“多召回一点”变成“排序更准一点”
text2vec-base-chinese 不是不能用。它的优点是轻、便宜、CPU 能跑,适合做边缘部署或降级方案。但如果目标是病历质控这种高风险场景,我会把它放在 fallback,而不是主路径。
1 | # 示例:Embedding 服务可以独立部署,主 Java 服务只通过 HTTP 调用 |
多模型路由:Agent 也得”看菜吃饭”
不同任务对模型能力要求不同,我们做了 LlmRouter,让 Agent 按需选择。
这里路由的重点不是把代码写死到某个模型厂商,而是按任务能力分层:
| 任务 | 复杂度 | 默认接入 | 私有化替代 | 理由 |
|---|---|---|---|---|
| 病历质控 | 高(需推理) | DeepSeek API | Qwen3-32B | 理解上下文、判断合规性 |
| 用药建议 | 高(需知识) | DeepSeek API | Qwen3-32B | 剂量计算、配伍禁忌 |
| 话术合规 | 低 | DeepSeek API 低成本配置 | Qwen3-7B | 敏感词检测,不需要每次都上高推理成本 |
| 诊疗辅助 | 高(需对话分析) | DeepSeek API | Qwen3-32B | 根据问诊对话分析可能病症,给出检查和处置建议 |
1 |
|
这样做以后,默认线上可以走 DeepSeek API;客户要私有化,就换成 vLLM + Qwen3 的 LlmClient 实现。Agent 的业务代码不用跟着模型一起改。
成本降低 60%,不是因为某个模型突然变魔法了,而是因为任务被拆清楚了。高风险、高推理任务用高能力档;低风险、规则性任务用低成本档。生产环境嘛,能省则省,但不能省到影响医疗安全。
当然,这句话不能只靠感觉。我们内部看的是一组固定评测集:
| 指标 | 为什么看它 |
|---|---|
| 高风险漏检率 | 医疗场景最怕漏掉过敏、剂量、禁忌这类问题 |
| 误报率 | 误报太多,医生会直接不看 |
| 医生采纳率 | 真实业务反馈,比离线 benchmark 更接近产品价值 |
| P95 延迟 | 病历保存后多久能看到质控报告 |
| 单次评估成本 | 模型路由到底有没有省钱 |
尤其是高风险漏检率,只要这个指标变差,成本省再多也不能上。医疗场景不是推荐系统,不能用“整体点击率提升”那套逻辑糊弄自己。
Phase 1 vs Phase 2:Agent 的渐进式落地
这里想重点聊聊落地节奏。我们分两阶段走:
Phase 1:固定工作流(已上线)
先做”傻瓜式”的固定流程:
- 病历保存 → 触发评估事件 → Redis Stream
- Worker 消费消息 → PromptBuilder 拼装提示词
- RAG 检索相似样本 → 注入 Prompt
- 调用 LLM → 返回 JSON 格式的评估结果
- 前端展示报告 → 医生可反馈(采纳/拒绝/评分)
这个阶段的 Agent 没有”自主决策”能力,就是按固定套路走。但好处是可控、可预期、好调试。出问题了一步步排查,不会遇到那种”Agent 突然抽风”的诡异 bug。
Phase 2:ReAct Agent(灰度中)
等工作流稳定了,再逐步引入 ReAct 循环,让 Agent 能主动调用工具:
- 药典查询(判断用药合理性)
- 历史病历检索(了解既往病史)
- 诊疗规范检索(判断诊断是否规范)
- 化验参考值(判断检查结果)
1 | // ReAct 循环核心 |
有个 agent.enabled 开关可以切模式,万一 Phase 2 出问题,随时回滚到 Phase 1。
生产护栏:比模型更重要的东西
Agent 上生产,模型能力只是一半。另一半是护栏。
我们做了几件很朴素但很关键的事:
- 结构化输出校验:LLM 必须返回符合 JSON Schema 的结果,解析失败重试一次,再失败就转人工复核
- Prompt 和规则版本化:每次评估都记录 prompt 版本、规则版本、模型版本,出了问题能回放
- RAG 命中留痕:命中了哪些样本、哪些知识条目、相似度多少,都落库
- 高风险结论必须有依据:涉及过敏、剂量、禁忌、诊疗规范,不能只有 LLM 一句话,必须能指回规则或知识库
- 工具调用白名单:ReAct 能调用哪些工具、每个工具最大超时、最大轮次,都写死在配置里
- 灰度开关和降级路径:Phase 2 出问题,马上切回 Phase 1;LLM 出问题,转人工复核,不让系统硬撑
这些东西不酷,但这才是生产环境真正救命的地方。
进化机制:让 Agent 自己长本事
最让我们团队骄傲的,是这个”每周进化”机制。
传统的 AI 项目上线了就完事了,效果衰减没人管。我们设计了一个定时任务(每周日凌晨 3 点):
- 统计反馈数据:过去一周医生的采纳率、评分
- 提炼高质量样本:采纳率>80% 且 评分>80 的案例,自动提升为 Few-shot 样本
- 分析 Bad Case:高拒绝率的案例,让 LLM 分析原因,生成候选规则
- 淘汰低效样本:长期不被采纳的样本自动下线
这就形成了一个闭环:Agent 评估 → 医生反馈 → 自动学习 → Agent 能力提升 → 更多评估 → 更多反馈…
但这里必须克制:Agent 不能自己改生产规则。
我们做的是“自动发现,人工发布”。LLM 可以帮忙总结 Bad Case,生成候选 prompt 规则,推荐哪些样本应该提升为 Few-shot,但这些变更要先进审核队列。医生或业务专家确认后,才会发布到生产版本。每次发布都有版本号,也能回滚。
上线两个月,样本库从初始的 50 条涨到了 300+ 条,采纳率从 65% 提升到了 82%。关键不是“零人工干预”,而是把人工精力用在审核和校准上,而不是每天手工翻 Bad Case。这才是可持续的进化。
部署架构:能复用就复用
最后简单说下部署。我们的原则是:默认不新增重基础设施,客户要求私有化时再补本地推理层。
默认 SaaS 形态是这样:
1 | ┌─────────────────────────────────────────────────────────────┐ |
如果客户要求私有化部署,再把 DeepSeek API 那一层替换成本地推理服务:
1 | DeepSeek API |
这个替换只发生在 LlmClient 实现层,不影响 Agent 的工作流、工具调用、RAG 检索和反馈闭环。也正因为这样,我们没有一开始就为了“看起来更自主可控”去买 GPU 堆本地模型。早期最贵的不是 API 调用费,而是团队把精力消耗在推理运维上,却还没证明 Agent 真的贴合业务。
写在最后
做 Agent 生产落地,最大的感悟是:克制比炫技更重要。
我们当然可以搞个多 Agent 协作系统,每个 Agent 都有自己的记忆、工具、规划能力,看起来很酷。但现实是,大部分业务场景不需要那么复杂。一个固定的 Workflow + RAG 增强 + LLM 推理,能解决 80% 的问题。这就是我们 Phase 1 的选择。
剩下的 20%,才是需要 ReAct、自主决策、工具调用的场景。而且即使做 Agent,也要给足控制手段——开关、降级、人工介入,一样都不能少。
Agent 不是魔法,它是一个需要工程化打磨的智能系统。把预期放低一点,把工程做实一点,反而更容易成功。
参考链接:
关联阅读:
- AI Agent 架构深度解析:原理、模式与生产落地
- Agentic 入门:让 AI 不再一把梭,而是像人一样反复干活
- Anthropic:长期运行 Agent 应用的 Harness 设计
- Context Engineering:比提示词工程更底层的那件事
- RAG 深度解析:从原理到生产落地的完整指南
如果你也在做 Agent 落地,欢迎交流。踩过的坑,说不定能帮到你。