0%

AI Agent ReAct 架构设计

这篇草稿好久了,刚好又学了点,今天好好整理后发出来。

我们为大客户发现了宠物医疗质控这个痛点场景。为了快速让客户看到效果、同时证明产品能力,我一个人从零开干——不要架构的完美,只要快、简单、能见人。这篇文章没有花架子,只有从 Workflow 到 ReAct 的真实决策、踩过的坑、以及回头看的反思。

一、为什么不是一套 Prompt 搞定?

大客户的诉求很直白:”让大模型读一下病历,打个分。”按常规思路,一套好的 Prompt 确实能应付大多数场景。

但自己从零搭这个系统,没有团队兜底,我必须同时考虑两件事:不同病例的复杂度差异,决定了单一 Prompt 策略必然在”简单病例浪费 token”和”复杂病例能力不足”之间两头不讨好。

一个普通的驱虫复诊,和一个同时涉及 4 种药物联用、肝肾功能异常、既往胰腺炎病史的复杂内科病例,对推理深度的要求完全不同。前者用单轮 Workflow 足够,后者必须给 Agent 工具调用和多轮推理的能力。

这个判断最终演化成了整个系统最核心的架构决策:双路径评估(Dual-Path Evaluation)。


二、架构全景:Workflow 为盾,ReAct 为矛

系统的入口是一个 CaseRouter,它读取一份外置的 risk_catalog.json,对病例做信号检测和复杂度评分:

  • 高危药物(如糖皮质激素、化疗药)
  • 药物-疾病冲突
  • 多药联用
  • 必须按指南执行的诊断
  • 主诉中的危急信号

这些信号的权重是可配置的(1.5 ~ 3.0),累加后得到一个 complexity_score。阈值设在 1.5,同时要求 tool_plan 非空,才会被路由到 ReAct Agent。其余 70%~80% 的病例走固定 Workflow。

1
2
3
4
5
6
POST /api/v1/evaluations


[CaseRouter] ── complexity_score >= 1.5 ──► [ReAct Agent + Tools]
│ │
└─ 简单病例 ─────────────────────────────► [EvaluationWorkflow]

这个分层不是炫技,而是成本与效果的精确博弈。在生产环境中,Workflow 的延迟约 13 秒,ReAct 由于多轮 LLM 调用,延迟在 38 秒,且 token 消耗通常是前者的 3~5 倍。让简单病例为复杂病例的架构买单,是不负责任的。


三、踩坑实录:代码能跑,但架构在痛

坑 1:AgentState 的上下文幻觉

我最初给 ReAct Agent 设计了一个 AgentState 来管理多轮对话的短期记忆。它记录了每一次工具调用的输入、输出和摘要,并在超过 6000 token 时触发 compact()

问题出在这个压缩策略上:它简单地保留了最近 3 条观察,把更早的记录折叠成一段纯文本摘要。没有语义重要性判断,没有工具调用结果的优先级排序。

这意味着什么?如果 Agent 在第 1 轮调用了 query_standard 获取了关键诊疗指南,但在第 4~5 轮因为工具调用过多触发了 compaction,这份指南的详细内容可能会被粗糙地压缩成一句”已查询相关标准”,导致后续推理失真。

教训:基于字符数的 token 估算(char/4)和基于条数的截断,只能算是”能跑”,离”可靠”差得很远。后续必须用语义摘要或重要性评分来替代。

坑 2:ReAct 循环里的消息格式泥潭

ReAct Agent 的核心是一个 _run_react_loop,最多 5 轮、最多 10 次工具调用。每一轮,我需要手动拼接 OpenAI 格式的消息列表:

  • system 角色定义工具和评分维度
  • user 角色注入病例 + tool_plan + RAG 检索结果
  • assistant 角色的 tool_calls
  • tool 角色的返回结果

这个嵌套循环(Agent 外层循环 + Provider 内层调用)让调试变得异常痛苦。有一次,LLM 明明返回了 tool_calls,但我在日志里只看到 content 为空,花了整整一个下午才发现是消息列表里的 role 顺序在某些边界情况下错位了。

教训:手写 ReAct 循环对消息格式的容错性极低。如果团队资源允许,应该尽早抽象出一个 “Turn Manager”,把消息构建、工具调用、结果回写封装成原子操作,而不是在 Agent 主逻辑里裸操作字典列表。

坑 3:Qdrant 与 PostgreSQL 的”半同步”陷阱

系统用 PostgreSQL 作为唯一数据源(Source of Truth),Qdrant 作为检索加速层。这本身没问题,但我在设计审核流程时犯了一个经典错误:

当医生审核通过一条 Few-Shot 示例或 Prompt 规则时,系统需要同时更新 PostgreSQL 的状态字段,并把新数据写入 Qdrant。这两个操作不在同一个事务里。

结果是:PG 提交成功,Qdrant 写入失败(比如网络抖动),系统就会进入不一致状态——PG 显示”已批准”,但检索端查不到这条数据。更麻烦的是,由于没有事务回滚机制,修复需要手动补偿。

任何跨存储的写操作,要么做成真正的分布式事务(Saga / 2PC),要么把 Qdrant 降级为”异步最终一致”,并通过后台任务持续对账。我最终选择了后者,但应该在第一版就明确这个策略,而不是事后补丁。

坑 4:Embedding 的沉默降级

Embedding 层我用 sentence-transformers 加载 text2vec-base-chinese,同时为了测试环境能跑,写了一个 SimpleHashEmbeddingClient 作为 fallback。

这个 fallback 的逻辑是把文本做 SHA-256,然后映射成一个固定维度的伪向量。它保证了”接口不报错”,但语义相似性完全失效——同样的文本永远得到同样的向量,不同的文本可能映射到相近的向量空间位置纯属巧合。

最危险的是,这个降级是静默的。如果生产环境的 sentence-transformers 因为依赖问题没能加载,系统不会崩溃,只会默默返回毫无意义的检索结果。

降级策略必须有明确的日志告警和人工介入机制。”能跑”不等于”可用”。

回头看坑 3 和坑 4,本质上是同一类问题:**”可工作”与”可靠”之间隔着一条静默失败的鸿沟。**Qdrant 写入失败后系统不会报错,Embedding 降级后系统不会报警——它们都在你眼皮底下悄悄背叛了你。生产环境中,一个明确报错的 500 远比一个悄悄返回错误结果却 HTTP 200 的系统要安全。生产系统不允许静默失败。如果做不到强一致性,至少要把”我降级了”这件事喊出来。


四、那些”对的”决定

踩坑之外,也有一些早期决策在后续迭代中被证明是正确的。这些决策背后有一条共同的主线:确定性逻辑(传统代码)做主干,大模型只做理解和分类。Agent 不是把一切交给 LLM 去”聪明地判断”,而是用代码框死边界,只在模糊地带请 LLM 出手。

1. 显式 Mock 开关

我在 LLM 路由层强制要求 LLM_USE_MOCK 必须是显式的布尔配置,绝不允许通过 API Key 的前缀或格式来”推测”是否使用 Mock。

这看起来是多此一举,直到有一次我在测试环境误配了 Key,系统如果走”自动推断”逻辑就会用真实模型跑测试,几分钟内烧掉几十块钱。显式开关让这种事故不可能发生。

2. Workflow 作为默认路径

团队里曾有争论:既然 Agent 更酷、更灵活,为什么不全部走 ReAct?

我的坚持基于一个朴素的工程原则:不要把通用炮当狙击枪用。简单病例走固定流程,不仅快、便宜,还更容易追溯和审计。复杂病例的 Agent 输出需要被标记、被抽样、被人工复核,这个成本必须被限制在 20%~30% 的真正复杂病例上。

3. Risk Catalog 外置化

把路由规则写成 JSON 文件而不是硬编码在 Python 里,让运营同学可以在不发布版本的情况下调整权重和关键词。这对一个医疗场景至关重要——新的高危药物或指南更新随时可能出现,代码发布周期赶不上业务变化。

4. 工具返回必须带 summary

我在工具层强制要求每个工具返回的 JSON 里必须包含一个 summary 字段。这个字段被 AgentState 用于 compaction 和 prompt 重建,避免了把完整的原始数据(比如 20 条历史病历或 10 页指南)反复塞进 LLM 上下文。

5. 硬编码上限:死循环计数器

ReAct 循环我设了两个硬上限:最多 5 轮推理、最多 10 次工具调用。触发上限后直接终止并报警,而不是让 Agent “再想想”。

这不是拍脑袋的数字。Agent 自主推理必须被硬编码上限约束,否则就是一个 Token 消耗黑洞。我在开发早期就踩过这个坑:一次因为 LLM 返回了格式异常的 tool_calls 导致 Agent 反复重试,如果不设上限,几分钟就能烧掉上万个 token。上限不是限制 Agent 的能力,而是保护系统不被 Agent 的”不确定性”拖垮。

6. 代码校验而非自我反思

学术论文里常见的套路是让 LLM “检查一下刚才的输出对不对”(Self-Reflection),在工业界这是一个昂贵的陷阱。它会带来双倍的延迟和双倍的 Token 成本,而且 LLM 审查自己的输出跟请作弊学生给自己监考没区别。

这个系统里,所有校验逻辑都由后端代码完成:ConsultationValidator 在评估前硬性拦截非法病历、Pydantic strict=True 拒绝任何多余字段的输出、评分维度用 model_validator 确保分项不超过满分上限、路由决策全走关键词规则而非 LLM 判断。LLM 只管生成,代码只管校验。不要让 LLM 给自己当裁判。


五、怎么知道 Agent 变好了还是变坏了?

前面聊的都是 Agent 怎么”做”,但一个更深刻的问题是:你改了 Prompt、调了路由阈值、换了 embedding 模型之后,怎么知道系统是变好了还是变坏了?没有量化的评估体系,Agent 的每一次改动都是在盲飞。

分层评估策略

我参考了业内的通用做法,把评估拆成了三个层级:

节点级(Node-Level):单独测试 CaseRouter 的路由准确率。我写了一个 agent_eval.py,加载手工标注的 JSONL 测试集,跑一轮后计算路由准确率、工具精确率/召回率,以及——这个在医疗场景最关键——高风险漏报率(应该走 ReAct 却路由到了 Workflow 的比例)。这个指标必须无限趋近于零。

组件级(Component-Level):针对 RAG 检索,需要检验检索到的 Few-Shot 示例是否真的相关、知识文档是否覆盖了问题域。这对应业界的 RAGAS 框架(忠实度、答案相关性、上下文召回率)。目前我主要靠人工抽查,但自动化 RAGAS 评估是优先级最高的待办。

端到端(End-to-End):终极指标只有一个——医生采纳率(Adoption Rate)。评估结果出来后,医生可以选择”采纳”或”拒绝”。采纳率高的评估就是好评估,拒绝率高的就是坏评估。这个反馈闭环是 Phase 3 进化的唯一燃料。

Golden Dataset 和 LLM-as-a-Judge

评估自动化依赖两样东西:

一是 Golden Dataset(金色数据集)——从历史评估中精选 100~500 个代表性病例,人工标注”标准答案”(应该走什么路径、各维度得分预期、应该查哪些知识文档)。这是你最重要的测试资产。

二是 LLM-as-a-Judge——用一个更强的模型(如 GPT-4o 或 Claude)来当裁判,把(用户输入 + Agent 输出 + Golden Dataset 标准答案)一起喂给它,让它按严格的评分 Rubric 打维度分。没有自动化裁判,评估体系就跑不起来。

这两样我目前都还在建设中。Golden Dataset 只有 3 条标注样本,LLM-as-a-Judge 还没接入。但方向是明确的,代码框架(agent_eval.py)已经铺好了。

线上链路追踪

最后,生产环境必须有 Trace 系统(LangSmith / Langfuse / Phoenix)。它们会把 Agent 一次完整执行的调用链拆解成一棵树——你能看到:请求进了哪个 Router → 耗时多少 → 调用了哪些 Tool → 每个 Tool 返回了什么 → 最终 LLM 如何组装输出。线上出了任何诡异回答,顺着 Trace 一眼就能定位到具体节点。

这一点我目前还没做,但放在这里是作为一条必经之路——没有 Trace,Agent 就是黑盒;有了 Trace,Agent 才是白盒。


六、Phase 3 进化系统:造好的船,等水到位

项目规划了三个阶段:

  • Phase 1:固定 Workflow(已完成)
  • Phase 2:ReAct Agent(已完成)
  • Phase 3:自我进化(Pipeline 代码已完成,但未接入定时触发)

Phase 3 的核心是 EvolutionScheduler,完整的流水线为:

1
2
3
4
5
run_evolution()
├─ Step 0: 聚合反馈指标(adoption_rate / rejection_rate)
├─ Step 1: 提取高质量样本 → 创建 Few-shot 候选(pending_review)
├─ Step 2: 分析 Bad Case → 创建 Prompt Rule 候选(pending_review)
└─ Step 3: 低质样本 → 创建停用审核任务(pending_review)

每一次”候选”都通过 AgentReviewTask 进入人工审核流程,状态机为:pending_reviewapproved / rejected不允许自动发布,这是对医疗场景的刻意保守。

Pipeline 完整的部分包括 EvolutionScheduler.run_evolution()(主流水线编排)、FewShotStatsAggregator(采纳率统计)、SampleExtractor(样本提取)、BadCaseAnalyzer(拒绝模式分析)。

尚未完成的部分是定时触发机制的接入。这是有意为之的克制——在 Phase 1 和 Phase 2 的稳定性没有足够数据支撑之前,启动自我进化等于在没有刹车的情况下踩油门。


七、架构全貌:那些容易被忽略的模块

以下几个实际存在但容易被忽略的模块,构成了系统的底座:

ConsultationValidator(数据校验层) —— ReAct 执行前必须通过数据合法性校验,拦截不合法病历。若校验失败,评估直接标记为 failed,不进入 ReAct 推理链。这是一道数据质量门。

AgentEvaluation 完整持久化模型 —— 评估结果写入数据库,包含 statuseval_modemodel_versionfew_shot_idskb_doc_idstotal_scoreresult_json 等字段,为后续的 EvolutionScheduler 提供反馈数据来源。

多租户隔离 —— 所有方法显式传入 tenant_idAgentEvaluation 等模型均以此为主要查询索引,确保数据隔离。

Few-shot + Knowledge 双检索 —— ReAct 执行前并行检索 Few-shot 示例(提供参考案例)和 Knowledge 文档(提供指南/标准),两者共同注入 prompt。

输出护栏 —— ConsultationValidator 管住输入端之后,输出端同样不能裸奔。评分结果用 Pydantic strict=True 模式解析,任何多余字段都会被拒绝;维度分通过 model_validator 确保不超过 25 分的上限;LLM 返回的 JSON 自动剥离 markdown code fence(```json 包裹的情况极其常见)。输入端防污染,输出端防事故,缺一不可。

BigInt 兼容 —— 数据库主键是 64 位整数,Python 端能正常处理,但通过 REST API 返回给前端时,JavaScript 的 Number 类型会丢失精度。所有 API 响应里的 ID 字段都通过 Pydantic field_validator 强制转为 string,前端永远不做精度假设。

优雅降级作为系统级设计 —— Qdrant 客户端加载失败时不是 crash,而是所有检索方法 return empty/no-op;Embedding 模型加载失败时 fallback 到 hash 伪向量。整个系统的设计原则是:任何一个外部依赖挂了,系统能降级运行,但一定会把”我降级了”这件事打出来。——对应坑 4 的教训。


八、写在最后

写 Agent 和写传统后端服务最大的区别是:不确定性不是 bug,而是特性的一部分。你无法像测试一个 REST API 那样精确断言 LLM 的输出,你只能设计边界、设计 fallback、设计人在回路(Human-in-the-Loop)。

这个项目教会我最重要的一课是:Agent 的架构质量,不体现在它有多”智能”,而体现在它有多”可控”。

双路径设计是对可控性的妥协,显式 Mock 是对可控性的坚守,人工审核的进化系统是对可控性的敬畏。医疗 AI 不允许惊喜,只允许可预期的、可解释的、可回滚的行为。

而这条”可控性”主线的底层,是同一个设计哲学:确定性逻辑(传统代码)做主干,大模型只做理解和分类。路由用关键词不用 LLM,校验用 Pydantic 不用自反思,超时和重试用计数器不用 prompt 约束——所有能代码化的边界,绝不让 LLM 来”商量”。

已知改进方向

如果时间允许,以下几件事是明确的下一步:

  • RAG Re-rank:向量检索后加一层 Cross-Encoder 精排,把最相关的 Top 3 文档强行喂给 LLM。这是 RAG 效果提升里性价比最高的一步。
  • Multi-Agent 拆分:当前 ReAct Agent 一个人干所有事(查药、查化验、查指南、评分)。当某个环节频繁出错时,把它拆成独立子 Agent,职责越聚焦,效果越可控。
  • 接入 Trace 系统(LangSmith / Langfuse / Phoenix):把每一次 Agent 执行的完整调用链可视化。没有 Trace,调 Agent 等于盲人摸象。
  • 完善 Golden Dataset + LLM-as-a-Judge:把评估自动化跑通,让每一次 Prompt 改动都有量化反馈。

Phase 1 和 Phase 2 已在线上运行。Phase 3 的 Pipeline 代码已完成,调度触发器待接入——这是一艘造好的船,等待数据的水位足够高,才允许启航。

Agent 不是终点,而是一个持续收敛的过程。

RAG Query Transformation 查询优化

这是之前学习 RAG 时整理的学习篇。用户问的问题往往很烂——要么太泛(「Python 教程」),要么太绕(「那个什么什么…」),Query Transformation 就是解决这个问题的:把烂问题改写成好问题,检索效果直接翻倍

一、为什么需要 Query Transformation

1.1 用户的提问有多不靠谱

做 RAG 的时候你会发现,用户的问题质量参差不齐

  • 太宽泛:「讲讲 Python」——讲什么?基础语法?进阶特性?Web 开发?数据分析?
  • 太模糊:「那个东西怎么用」——哪个东西?怎么用?
  • 有歧义:「Java 的继承」——是说 extends 继承父类?还是 implements 实现接口?还是组合优于继承(composition over inheritance)的重构方向?
  • 有拼写错误:「Pythno 教程」——搜索引擎能纠错的还好,向量检索直接傻眼
  • 太专业/太口语:「怎么让模型过拟合」——过拟合通常是负面词,但用户想问的是「怎么在特定领域微调」

核心痛点:用户的问题 ≠ 文档里的表达方式。

用户问「Python 教程」,文档里可能写的是「Python 3.9 入门指南」。字面完全不同,但语义相关。这就是 Query Transformation 要解决的问题。

1.2 Query Transformation 的本质

用一句话概括:

把用户原始查询,改写成更容易在向量空间里找到相关内容的形式。

改写可以是:

  • 扩展(加同义词、相关概念)
  • 分解(把复杂问题拆成多个子问题)
  • 重构(换个问法,更接近文档的表达方式)
  • 生成(直接生成一个「理想答案」的草稿去搜——这就是 HyDE)

二、常见的 Query Transformation 方法

2.1 方法对比总览

方法 原理 优点 缺点 适用场景
HyDE 生成假设文档再编码 零样本、效果好 需要 LLM、有延迟 问答式、语义模糊
Query Expansion 扩展同义词/相关词 简单、成本低 扩展质量不稳定 通用场景
Step-back Prompting 退一步问更抽象的问题 能召回更相关的背景知识 可能召回过多无关内容 需要背景知识的复杂问题
子查询分解 把复杂问题拆成多个子问题 适合复杂多跳问题 流程复杂、成本高 复杂推理问题
RAG-Fusion 生成多个查询取并集 召回更全面 计算成本高 对召回率要求高的场景

2.2 HyDE(Hypothetical Document Embeddings)

HyDE 是 Query Transformation 里最有意思的方法,本系列下一篇会专门深入讲,这里先把核心思路说清楚。

核心思想:与其纠结怎么优化问题,不如直接生成一个「理想答案」,然后用这个答案去搜。

用户问题 → LLM 生成假设文档 → 编码 → 检索真实文档

什么时候用

  • 用户问题很短(1-3 个词),语义太泛
  • 用户不知道怎么表达,需要「补全」语义
  • 你试过其他方法,效果都不够好

2.3 Query Expansion(查询扩展)

最传统、最轻量的方法。

原理:给原始查询加同义词、相关词,扩大检索范围。

举个例子

原始查询:Python 教程
扩展后:Python 教程 入门 基础 学习 guide tutorial

实现方式

  1. 基于词典:用 WordNet、同义词词典人工维护
  2. 基于模型:用 LLM 生成相关词(轻量版)

Prompt 示例

请为以下查询生成 3-5 个相关的搜索词,用逗号分隔:

查询:{query}

相关搜索词:

优点

  • 实现简单,几乎零成本
  • 不需要额外模型(如果用词典)或小模型(如果用 LLM 扩展)
  • 稳定性高

缺点

  • 扩展质量依赖词典或模型能力
  • 可能引入不相关的词(比如「Python」扩展到「蟒蛇」)
我的经验:Query Expansion 是性价比最高的第一步优化。先把这个做好,再考虑更复杂的方法。

2.4 Step-back Prompting(退一步提问)

这个方法来自 Google DeepMind 的论文《Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models》(Zheng et al., 2023),思路很有意思。

核心思想:当问题太具体时,退一步问更抽象、更通用的问题,先召回背景知识,再用背景知识辅助回答具体问题。

举个例子

原始问题 Step-back 问题 召回内容
「Transformer 的注意力机制在医疗文本上怎么优化」 「Transformer 注意力机制是什么」 基础原理
「React useEffect 依赖数组问题」 「React useEffect 是什么」 useEffect 文档

Prompt 示例

你是一个问题改写助手。用户的问题是具体的,你需要生成一个更通用、更抽象的问题,用于检索背景知识。

用户问题:{query}

请生成一个更通用的背景问题:

什么时候用

  • 问题很具体,需要背景知识才能理解
  • 用户问的是某个领域的专业问题,但知识库里有更基础的解释

注意事项

  • Step-back 可能会召回太多无关内容
  • 建议配合 Reranker 使用,先召回更多,再精确排序

2.5 子查询分解(Sub-query Decomposition)

适合那种「一步问完但其实包含多个问题」的复杂查询。

举个例子

原始问题:「怎么在 Kubernetes 上部署一个带数据库的 Web 应用,还要配置 SSL」

子查询 1:Kubernetes 部署 Web 应用
子查询 2:Kubernetes 部署数据库
子查询 3:Kubernetes 配置 SSL 证书

实现方式

  1. 用 LLM 分析问题包含哪些子问题
  2. 对每个子问题分别检索
  3. 合并所有检索结果作为上下文

Prompt 示例

请将以下复杂问题分解成 2-4 个更简单的子问题。每个子问题应该独立可回答。

原始问题:{query}

子问题:
1.
2.
3.

优点

  • 每个子问题更精准,检索质量更高
  • 适合复杂的多跳推理问题

缺点

  • 需要多次检索,成本翻倍
  • 合并策略需要设计(简单拼接还是智能合并)
什么时候用:当用户问题明显包含多个子任务时(「怎么实现 A 同时还要 B 和 C」),分解后效果更好。

2.6 RAG-Fusion

RAG-Fusion 由 Zackary Rackauckas 在 2024 年提出(arXiv:2402.03367),核心融合算法 RRF(Reciprocal Rank Fusion)则来自 Cormack 等人 2009 年的 SIGIR 论文。思路是:一个查询不够,我生成多个查询

流程

原始查询 → LLM 生成 5 个相关查询 → 分别检索 → 用 RRF 融合结果 → Top-K 返回

Prompt 示例

请根据以下查询,生成 5 个不同角度但相关的查询。每个查询应该能检索到相关的补充信息。

原始查询:{query}

相关查询:
1.
2.
3.
4.
5.

RRF 融合公式

RRF 分数 = Σ 1 / (k + rank_i)

简单说就是:一个文档在多个查询的结果里排名越高,最终分数越高。

优点

  • 召回更全面,不容易漏掉相关内容
  • 多个角度覆盖,减少 query 表述差异的影响

缺点

  • 需要多次检索,成本高
  • 需要调 RRF 的参数 k

三、方法选择决策树

这么多方法,怎么选?

决策流程:

用户问题质量如何?
├── 问题清晰、具体 → 直接用原始查询
│
├── 问题太泛/太短 → Query Expansion 或 HyDE
│   ├── 延迟敏感 → Query Expansion
│   └── 追求效果 → HyDE
│
├── 问题太复杂/多条件 → 子查询分解
│
├── 需要背景知识 → Step-back Prompting
│
└── 召回率不够 → RAG-Fusion(多查询生成)

我的建议:渐进式优化

不要一上来就搞最复杂的方法。建议这个顺序:

阶段 方法 预期收益 成本
第一阶段 原始查询 + 好的 Embedding 模型 60%
第二阶段 + Query Expansion 70% 很低
第三阶段 + Reranker(Cross-Encoder) 80%
第四阶段 + HyDE / Step-back / RAG-Fusion 85-90%
关键洞察:80% 的收益来自前三个阶段。HyDE 这类高级方法是为了最后那 10% 的提升,但成本和复杂度会显著增加。先跑基线,再决定是否需要。

四、生产落地的实战经验

4.1 延迟优化

Query Transformation 最大的问题是增加了延迟

  • Query Expansion:几毫秒(如果用词典)到几百毫秒(如果用 LLM)
  • HyDE:几百毫秒到几秒(取决于 LLM)
  • RAG-Fusion:查询数 × 单次检索时间

优化策略

  1. 分层缓存

    • 高频查询的改写结果缓存
    • 低频查询走原始查询
  2. 并行执行

    • RAG-Fusion 的多个查询并行检索
    • 子查询分解的多个查询并行处理
  3. 降级策略

    • LLM 超时或失败时,回退到原始查询
    • 设置改写质量的阈值,质量不够就回退
  4. 小模型替代

    • HyDE 的假设文档生成不需要大模型,用 GPT-3.5 级别的小模型替代 GPT-4,延迟可以降低 3-5 倍,效果损失在可接受范围内

4.2 改写质量评估

怎么知道改写有没有变好?

离线评估

  • 准备一批测试查询和期望的相关文档
  • 对比改写前后的 Recall@K、Precision@K、MRR

在线评估

  • A/B 测试:一部分用户走改写,一部分走原始
  • 观察下游指标:回答的点击率、满意度、任务完成率
避坑提醒:Query Transformation 可能导致「改写后召回的文档更相关,但 LLM 生成的答案反而更差」的情况。一定要评估端到端的效果,不要只看检索指标。

4.3 常见坑

表现 解决
改写过度 原始查询很清晰,改写后反而引入无关词 设置改写置信度阈值
改写跑偏 改写后的查询偏离了用户真实意图 用原始查询兜底
多语言问题 用户用中文问,改写成英文后检索不到中文文档 按语言分别处理或混合检索
领域适配 通用改写策略在专业领域效果差 用领域特定词典或微调

五、Prompt 工程技巧

5.1 通用原则

  1. 明确输出格式:告诉模型你要什么格式的输出
  2. 加示例(few-shot):给 1-2 个例子,模型学得更快
  3. 约束条件:限制输出的长度、风格、范围

5.2 示例:带 few-shot 的 Query Expansion Prompt

你是一个查询扩展助手。请为用户的查询生成 3-5 个相关的搜索词,帮助检索更多相关内容。

示例 1:
查询:Python 教程
扩展:Python 入门, Python 基础, Python 学习指南, Python 编程, Python 3.9

示例 2:
查询:Kubernetes 部署
扩展:K8s 部署, Kubernetes 安装, Kubernetes 集群搭建, Docker 容器编排

用户查询:{query}

请生成扩展词(用逗号分隔):

5.3 示例:子查询分解 Prompt

请将以下复杂问题分解成 2-4 个更简单的子问题。每个子问题应该独立可回答。

示例:
原始问题:「怎么在 AWS 上部署一个高可用的 Web 应用,还要配置负载均衡和自动扩缩容」
子问题:
1. AWS 上如何部署 Web 应用
2. AWS 负载均衡器(ELB)如何配置
3. AWS 自动扩缩容(Auto Scaling)如何设置
4. AWS 高可用架构设计

原始问题:{query}

子问题:
1.
2.
3.
4.

六、总结

Query Transformation 是 RAG 检索优化的核心手段,但不是银弹

核心观点: 1. 先跑基线,知道当前问题在哪 2. 从轻量方法(Query Expansion)开始,逐步迭代 3. 高级方法(HyDE、RAG-Fusion)是为了最后 10% 的提升,成本和复杂度会显著增加 4. 一定要评估端到端效果,不要只看检索指标 5. 改写可能跑偏,一定要有降级策略

最后送大家一句话:用户的烂问题不是你的错,但如果检索不到答案,那就是你的问题了。Query Transformation 就是来解决这个问题的。


参考资源

论文原文

官方实现参考

AI 编程哲学

前言

最近读到两篇很棒的博文,让我对 AI 时代的编程有了更深的思考。

一篇是 Bryan Cantrill 的《The peril of laziness lost》(失去懒惰的危险)。Bryan 是 DTrace 的作者、Oxide Computer 的 CTO,系统编程领域的传奇人物。

另一篇是 Mario Zechner 的《Thoughts on slowing the fuck down》(关于放慢脚步的思考)。Mario 是 libGDX 游戏框架的创建者,也是一位经验丰富的开发者和教练。

两位作者从不同角度提出了同一个警告:在 AI 编程时代,我们正失去一些宝贵的东西

Larry Wall 的三美德

要理解他们的观点,得先回到 Larry Wall。

Larry Wall 是谁?Perl 语言的创造者。他在那本著名的《Programming Perl》(被亲切地称为”骆驼书”)里,提出了程序员的三美德:

懒惰、急躁、傲慢

原文是这么说的:

如果我们要讨论好的软件设计,就必须谈到懒惰、急躁和傲慢。这些都是好软件设计的基础。我们都曾掉进复制粘贴的陷阱,其实应该定义更高层的抽象,哪怕只是一个循环或子程序。当然,有些人走向了另一个极端,定义了越来越多的高层抽象,其实应该用复制粘贴。但一般来说,我们大多数人需要思考的是使用更多抽象,而不是更少。

懒惰驱动我们让系统尽可能简单(但不要过于简单!)——开发强大的抽象,让我们能更容易地做更多事情。

懒惰的真正含义

这里的”懒惰”不是贬义词。

它是一种工程智慧

  • 懒得写重复代码 → 所以抽象出通用模块
  • 懒得维护复杂系统 → 所以追求简洁设计
  • 懒得处理边界情况 → 所以在设计时就考虑周全

Bryan 说得很好:

这种看似懒惰的背后,其实是在脑子里反复思考问题。我们承担开发抽象所需的艰难智力工作,部分原因是我们正在优化假设中的未来自己的时间,即使以牺牲当前时间为代价。

这就是 hammock-driven development (吊床驱动开发)的精髓——看似在偷懒,实则在深度思考。

LLM 的问题:不知疲倦的”勤奋”

现在问题来了。LLM 不会累,也不需要优化自己的时间

Bryan 举了一个例子:Garry Tan(Y Combinator 的 CEO)炫耀自己用 LLM 一天写了三万七千行代码,而且”还在加速”。

对比:整个 DTrace 项目大约也就六万行代码。

这意味着什么?

一位波兰工程师 Gregorein 解剖了 Garry Tan 的”newsletter-blog-thingy”项目,结果让人啼笑皆非:

  • 包含多个测试框架(?!)
  • 包含 Rails 的 Hello World 应用(?!)
  • 偷偷塞进去一个文本编辑器
  • 八个不同版本的同一个 logo,其中一个文件大小为零字节
这不是 bug 的问题(这些都能修),也不是方法论的问题。问题在于 LLM 本质上缺乏懒惰的美德。

错误在复合:Mario 的警告

Mario Zechner 从另一个角度提出了更严峻的警告。

他说现在的软件”一切都坏了”——98% 的正常运行时间反而成了常态,界面有各种奇怪的 bug。虽然这不能全怪 AI,但趋势在加速。

复合的”小错误”

Mario 提出了一个很形象的词:booboos(小错误)。

人类也会犯错,但:

  • 人类会学习,不会重复犯同样的错误
  • 人类是瓶颈,一天能写的代码有限
  • 当痛苦积累到一定程度,人类会去修复

但代理(Agents)不一样:

  • 代理没有学习能力(除非你显式教它,但它还是会忘)
  • 代理没有瓶颈,几小时能生成几万行代码
  • 代理不会感到痛苦,所以你不知道错误在积累

这些小错误(重复的代码、无用的方法、错误的抽象)会在代理手中指数级复合。直到有一天,你想加个新功能,发现架构已经烂到无法支持;或者用户数据被删除了,你才发现问题。

复杂性的商人

Mario 还有一个很犀利的观点:代理是“复杂性的商人”(merchants of learned complexity)

代理在训练数据中见过太多糟糕的设计决策。当你让它们帮你设计架构时,它们会引入大量复杂性——各种 Cargo cult 的”行业最佳实践”、为了抽象而抽象的代码、不一致的重复实现。

更糟糕的是,代理只有局部视野。它们看不到整个代码库,看不到之前的决策,所以总是在局部做”看起来对”的选择,最终导致全局的混乱。

Mario 说:”一个 2 人团队用代理,几周内就能达到传统企业代码库的复杂度。”

低召回率的代理搜索

Mario 还指出了一个技术问题:代理搜索的召回率很低

当代理试图重构或修复代码时,它需要先找到所有相关的代码。但无论是用 ripgrep、LSP 服务器还是向量数据库,当代码库变大时,代理总会遗漏一些代码。

结果就是:

  • 遗漏现有代码 → 重复造轮子
  • 遗漏相关代码 → 修复不完整
  • 遗漏上下文 → 引入新的不一致

这就是那些”小错误”产生的根本原因。然后它们会像滚雪球一样,变成一朵“美丽的屎花”(shit flower)

为什么这很危险

把这两篇文章放在一起读,我觉得他们说的是同一件事的两面:

LLM 不会感到需要为自己(或任何人)的未来时间做优化,它们会毫无顾虑地在一层又一层的垃圾上堆叠更多垃圾。

如果不加以约束,LLM 会让系统变得更大,而不是更好——可能迎合了某种扭曲的虚荣心指标,但代价是牺牲了一切真正重要的东西。

最好的工程总是源于约束,而我们人类时间的约束限制了我们对系统认知负荷的容忍度。正是这种约束驱使我们让系统更简单,尽管系统本质上就很复杂。

放慢脚步:我现在怎么做

基于两位作者的观点,我总结了几条实践建议:

1. 限制代码生成量

Mario 建议:给自己设定每天生成代码量的上限,与你能 review 的能力相匹配。

不要追求 37k 行/天,追求 100 行高质量的代码。

2. 手写架构,代理实现

架构、API 设计这些决定系统”气质”的东西,必须手写

用 tab 补全感受代码的构建过程,或者用结对编程的方式和代理一起写。这种摩擦能让你更好地理解系统在”感觉”上是否正确。

3. 把 LLM 当实习生用

它可以帮你写代码,但你需要 review、refactor、甚至重写。

好的代理任务应该是:

  • 范围有限,不需要理解整个系统
  • 有明确的评估标准(可以自动验证结果)
  • 不是关键路径,不会导致数据丢失

4. 追求”少即是多”

当 LLM 给你一大坨代码时,问它:”能不能更简洁?”

学会说”不”本身就是一种能力。做更少的功能,但做对的功能。

5. 保持批判性思维

LLM 生成的代码往往”能用”但”不够好”,别被数量迷惑。

定期重构,防止技术债务累积。如果你连自己写了什么都不知道,当出问题时就无法修复。

结语

Larry Wall 说懒惰是美德,这是一种智慧。

Bryan Cantrill 提醒我们,这种美德在 AI 时代变得更加稀缺。

Mario Zechner 告诉我们,如果不放慢脚步,我们会被代理带入一个复杂性的深渊。

机器不会累,不会觉得麻烦,不会为了追求简洁而绞尽脑汁。这些人类的”弱点”,恰恰是创造力的源泉。

这不是说不该用 AI 工具。而是说:为了将来能少做一点,现在要多做一点思考

这种”懒惰”,才是真正的懒惰。


参考阅读:

AI Agent
这篇是我agent实践记录的第一篇,主要聊聊我前段时间在做的一个宠物医疗"病历质控 Agent"从原型做到生产落地的大体情况,细节后续有时间会逐一整理。不是什么高大上的理论,就是实打实的踩坑记录和权衡取舍。

前言

虽然正儿八经做架构设计不到两年,但是我越来越笃信一个理念:好的架构不是”一步到位”的 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,渐进式演进。

AI 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
2
3
4
5
6
7
8
9
10
11
-- pgvector 用法很简单
CREATE EXTENSION IF NOT EXISTS vector;

ALTER TABLE agent_few_shot_examples
ADD COLUMN embedding vector(1024); -- Qwen3-Embedding-0.6B 默认 1024 维

-- 语义相似度检索
SELECT *, 1 - (embedding <=> CAST(:embedding AS vector)) AS similarity
FROM agent_few_shot_examples
ORDER BY embedding <=> CAST(:embedding AS vector)
LIMIT :limit;

这只是最小示例。生产里还得建索引,不然样本库一大,延迟会很难看:

1
2
3
4
-- HNSW 构建慢一点、吃内存一点,但通常有更好的召回/延迟平衡
CREATE INDEX agent_few_shot_examples_embedding_hnsw
ON agent_few_shot_examples
USING hnsw (embedding vector_cosine_ops);

pgvector 还有 IVFFlat,构建更快,但要调 lists/probes。我们的选择是:样本量还不大时直接 HNSW,先把线上查询延迟和召回稳定住。

第二种:知识库检索(对应 agent_knowledge_base 表)

药典、诊疗规范、化验参考值。注意:图片不参与向量检索,存 OSS 里,检索命中后返回链接即可。

Embedding 模型的选择

embedding 模型我现在首选 Qwen3 Embedding,原因很直接:

  • 跟 Qwen3 主模型体系更一致,RAG 链路里少一点“模型语义空间错位”
  • 对中英混合、长文本、多任务检索更稳
  • 后续可以接 Qwen3 Reranker,把“多召回一点”变成“排序更准一点”

text2vec-base-chinese 不是不能用。它的优点是轻、便宜、CPU 能跑,适合做边缘部署或降级方案。但如果目标是病历质控这种高风险场景,我会把它放在 fallback,而不是主路径。

1
2
3
4
5
6
7
8
9
10
11
# 示例:Embedding 服务可以独立部署,主 Java 服务只通过 HTTP 调用
from flask import Flask, jsonify, request
from sentence_transformers import SentenceTransformer

app = Flask(__name__)
model = SentenceTransformer('Qwen/Qwen3-Embedding-0.6B')

@app.route('/embed', methods=['POST'])
def embed():
text = request.json['text']
return jsonify({'embedding': model.encode(text).tolist()})
小提示:向量检索的召回率比精度更重要。我们宁可多召回几条再让 LLM 筛选,也不要漏掉相关的样本。

多模型路由:Agent 也得”看菜吃饭”

不同任务对模型能力要求不同,我们做了 LlmRouter,让 Agent 按需选择。

这里路由的重点不是把代码写死到某个模型厂商,而是按任务能力分层:

任务 复杂度 默认接入 私有化替代 理由
病历质控 高(需推理) DeepSeek API Qwen3-32B 理解上下文、判断合规性
用药建议 高(需知识) DeepSeek API Qwen3-32B 剂量计算、配伍禁忌
话术合规 DeepSeek API 低成本配置 Qwen3-7B 敏感词检测,不需要每次都上高推理成本
诊疗辅助 高(需对话分析) DeepSeek API Qwen3-32B 根据问诊对话分析可能病症,给出检查和处置建议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class LlmRouter {
private final Map<AgentType, ModelProfile> profiles;
private final LlmClient llmClient;

public LlmRouter(LlmClient llmClient) {
this.llmClient = llmClient; // 默认 DeepSeek API,私有化时切到 vLLM/Qwen3 实现
this.profiles = Map.of(
AgentType.RECORD_EVAL, ModelProfile.HIGH_REASONING,
AgentType.DRUG_CHECK, ModelProfile.HIGH_REASONING,
AgentType.SCRIPT_COMPLIANCE, ModelProfile.FAST_CHECK,
AgentType.DIAGNOSIS_ASSIST, ModelProfile.HIGH_REASONING
);
}

public LlmResponse chat(AgentType type, List<Message> messages) {
return llmClient.chat(profiles.get(type), messages);
}
}

这样做以后,默认线上可以走 DeepSeek API;客户要私有化,就换成 vLLM + Qwen3 的 LlmClient 实现。Agent 的业务代码不用跟着模型一起改。

成本降低 60%,不是因为某个模型突然变魔法了,而是因为任务被拆清楚了。高风险、高推理任务用高能力档;低风险、规则性任务用低成本档。生产环境嘛,能省则省,但不能省到影响医疗安全。

当然,这句话不能只靠感觉。我们内部看的是一组固定评测集:

指标 为什么看它
高风险漏检率 医疗场景最怕漏掉过敏、剂量、禁忌这类问题
误报率 误报太多,医生会直接不看
医生采纳率 真实业务反馈,比离线 benchmark 更接近产品价值
P95 延迟 病历保存后多久能看到质控报告
单次评估成本 模型路由到底有没有省钱

尤其是高风险漏检率,只要这个指标变差,成本省再多也不能上。医疗场景不是推荐系统,不能用“整体点击率提升”那套逻辑糊弄自己。

Phase 1 vs Phase 2:Agent 的渐进式落地

这里想重点聊聊落地节奏。我们分两阶段走:

Phase 1:固定工作流(已上线)

先做”傻瓜式”的固定流程:

  1. 病历保存 → 触发评估事件 → Redis Stream
  2. Worker 消费消息 → PromptBuilder 拼装提示词
  3. RAG 检索相似样本 → 注入 Prompt
  4. 调用 LLM → 返回 JSON 格式的评估结果
  5. 前端展示报告 → 医生可反馈(采纳/拒绝/评分)

这个阶段的 Agent 没有”自主决策”能力,就是按固定套路走。但好处是可控、可预期、好调试。出问题了一步步排查,不会遇到那种”Agent 突然抽风”的诡异 bug。

Phase 2:ReAct Agent(灰度中)

等工作流稳定了,再逐步引入 ReAct 循环,让 Agent 能主动调用工具:

  • 药典查询(判断用药合理性)
  • 历史病历检索(了解既往病史)
  • 诊疗规范检索(判断诊断是否规范)
  • 化验参考值(判断检查结果)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ReAct 循环核心
public EvaluationResult run(MedicalRecord record) {
List<Message> context = buildInitialContext(record);

for (int turn = 0; turn < MAX_TURNS; turn++) {
// LLM 推理
LlmResponse resp = llmRouter.chat(AgentType.RECORD_EVAL, context);

if (resp.isFinished())
return parseResult(resp.content()); // 输出结论

// 否则调用工具
ToolCall call = resp.getToolCall();
String toolResult = toolRouter.invoke(call);

// 工具结果回传上下文
context.add(Message.assistant(resp.content(), call));
context.add(Message.tool(call.id(), toolResult));
}
return fallbackToWorkflowOrHumanReview(context); // 超出轮次后降级,不强行给医疗结论
}

有个 agent.enabled 开关可以切模式,万一 Phase 2 出问题,随时回滚到 Phase 1。

经验之谈:Agent 系统一定要设计"逃生通道"。再聪明的 Agent 也可能犯错,得让人工随时能接管。

生产护栏:比模型更重要的东西

Agent 上生产,模型能力只是一半。另一半是护栏。

我们做了几件很朴素但很关键的事:

  1. 结构化输出校验:LLM 必须返回符合 JSON Schema 的结果,解析失败重试一次,再失败就转人工复核
  2. Prompt 和规则版本化:每次评估都记录 prompt 版本、规则版本、模型版本,出了问题能回放
  3. RAG 命中留痕:命中了哪些样本、哪些知识条目、相似度多少,都落库
  4. 高风险结论必须有依据:涉及过敏、剂量、禁忌、诊疗规范,不能只有 LLM 一句话,必须能指回规则或知识库
  5. 工具调用白名单:ReAct 能调用哪些工具、每个工具最大超时、最大轮次,都写死在配置里
  6. 灰度开关和降级路径:Phase 2 出问题,马上切回 Phase 1;LLM 出问题,转人工复核,不让系统硬撑

这些东西不酷,但这才是生产环境真正救命的地方。

进化机制:让 Agent 自己长本事

最让我们团队骄傲的,是这个”每周进化”机制。

传统的 AI 项目上线了就完事了,效果衰减没人管。我们设计了一个定时任务(每周日凌晨 3 点):

  1. 统计反馈数据:过去一周医生的采纳率、评分
  2. 提炼高质量样本:采纳率>80% 且 评分>80 的案例,自动提升为 Few-shot 样本
  3. 分析 Bad Case:高拒绝率的案例,让 LLM 分析原因,生成候选规则
  4. 淘汰低效样本:长期不被采纳的样本自动下线

这就形成了一个闭环:Agent 评估 → 医生反馈 → 自动学习 → Agent 能力提升 → 更多评估 → 更多反馈…

但这里必须克制:Agent 不能自己改生产规则

我们做的是“自动发现,人工发布”。LLM 可以帮忙总结 Bad Case,生成候选 prompt 规则,推荐哪些样本应该提升为 Few-shot,但这些变更要先进审核队列。医生或业务专家确认后,才会发布到生产版本。每次发布都有版本号,也能回滚。

上线两个月,样本库从初始的 50 条涨到了 300+ 条,采纳率从 65% 提升到了 82%。关键不是“零人工干预”,而是把人工精力用在审核和校准上,而不是每天手工翻 Bad Case。这才是可持续的进化。

进化飞轮

部署架构:能复用就复用

最后简单说下部署。我们的原则是:默认不新增重基础设施,客户要求私有化时再补本地推理层

默认 SaaS 形态是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────┐
│ 现有基础设施 │
├─────────────────────────────────────────────────────────────┤
│ PostgreSQL (+ pgvector 插件) ← 已有实例,加向量列即可 │
│ Redis ← 已有,新增 Stream key │
│ Nacos ← 已有,服务注册发现 │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ ai-agent-service (新服务,2C/2G) │
│ ├── 固定工作流 (Phase 1) │
│ ├── RAG 检索模块 │
│ └── ReAct Agent (Phase 2,可开关) │
└─────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌────────────────────────────┐
│ DeepSeek API │ │ embedding 服务 │
│ 默认 LLM 调用入口 │ │ Qwen3 Embedding 独立部署 │
└──────────────────────┘ └────────────────────────────┘

如果客户要求私有化部署,再把 DeepSeek API 那一层替换成本地推理服务:

1
2
3
DeepSeek API

vLLM + Qwen3(客户私有化环境)

这个替换只发生在 LlmClient 实现层,不影响 Agent 的工作流、工具调用、RAG 检索和反馈闭环。也正因为这样,我们没有一开始就为了“看起来更自主可控”去买 GPU 堆本地模型。早期最贵的不是 API 调用费,而是团队把精力消耗在推理运维上,却还没证明 Agent 真的贴合业务。

写在最后

做 Agent 生产落地,最大的感悟是:克制比炫技更重要

我们当然可以搞个多 Agent 协作系统,每个 Agent 都有自己的记忆、工具、规划能力,看起来很酷。但现实是,大部分业务场景不需要那么复杂。一个固定的 Workflow + RAG 增强 + LLM 推理,能解决 80% 的问题。这就是我们 Phase 1 的选择。

剩下的 20%,才是需要 ReAct、自主决策、工具调用的场景。而且即使做 Agent,也要给足控制手段——开关、降级、人工介入,一样都不能少。

Agent 不是魔法,它是一个需要工程化打磨的智能系统。把预期放低一点,把工程做实一点,反而更容易成功。

参考链接:

关联阅读:


如果你也在做 Agent 落地,欢迎交流。踩过的坑,说不定能帮到你。

AI RAG LLM 向量检索

这篇是我之前搭建 RAG 系统时的踩坑总结。从最初的”不就是向量检索+LLM吗”到真正生产上线,中间踩的坑比我想象的多得多,有些坑甚至让我怀疑人生。

其实在上一家公司的时候我就手痒自己搞过一个产品的 RAG,当时借着技术分享活动练手。那时候技术栈比较旧,加上边学边搭,效果只能说…勉强能跑。召回率估计就60%左右,不稳定,有时候幻觉大得离谱。不过当时内部项目不上线,虽然效果不咋地,但分享活动上大家认可度挺高——毕竟都是第一次了解这东西,有个能跑通的 Demo 已经算成功了。

当时问题一大堆:Embedding选哪个?chunking咋切?上下文太长怎么处理?幻觉怎么解决?评估指标怎么定?一个接一个冒出来,搞得我头大。

这次要真正上线生产了,逼着我把 RAG 的每个环节都仔仔细细学了一遍。

一、RAG 的起源:为什么 Meta 要搞这么个东西

1.1 2020 年的那篇开山之作

RAG 这个概念最早出自 Meta(当时还叫 Facebook AI)2020 年 5 月的一篇论文。那时候 GPT-3 还没发布,BERT 刚火了一年多。研究团队面临的核心问题是:怎么让语言模型具备知识,但又不需要把知识全部塞进模型参数里?

他们的思路很巧妙:与其让模型死记硬背所有知识,不如给模型配一个”外挂”——一个可检索的知识库。模型需要的时候就去查,查到了再生成答案。这就像是开卷考试,不用背整本书,知道怎么查就行。

这个思路解决了当时的大痛点:

  • 知识更新问题:模型参数是固定的,但世界在变。RAG 让知识库可以独立更新
  • 知识容量问题:模型参数量再大也有限,但外部知识库可以无限扩展
  • 可解释性问题:模型能告诉你答案是从哪查的

1.2 为什么现在 RAG 火了

2023 年 ChatGPT 爆火之后,RAG 突然成了企业落地 LLM 的首选方案。原因很简单:企业有大量私有数据,这些数据不能用来训练通用大模型——隐私、成本、时效性都是问题。RAG 提供了一个完美的解决方案:把私有数据放在向量数据库里,查询时再喂给 LLM。

所以现在你看到的各种”企业知识库”、”智能客服”、”文档问答”,底层基本都是 RAG。

说实话,我刚开始接触 RAG 的时候也觉得”这不就是搜索+大模型吗,有啥难的”。但真正做起来才发现,要让这个”简单”的架构在生产环境稳定跑起来,要考虑的东西远比想象的多。

二、RAG 到底是什么:名字里的三个关键词

RAG 全称是 Retrieval-Augmented Generation,三个词分别对应三个核心环节。我第一次看到这个名字的时候也觉得挺绕口的,其实就是”先检索,再增强,最后生成”的意思。

2.1 Retrieval(检索)

核心任务:从海量文档中找到与用户问题相关的片段。

关键技术点包括 Embedding(把文本转成向量)、向量数据库(存储和索引这些向量)、相似度搜索(找出最相似的文档片段)。

这里有个坑我踩过:刚开始我以为向量相似度越高,检索结果就一定越好。实际上不是这样,有时候语义相近但内容不相关的情况挺常见的,后面会讲怎么解决。

2.2 Augmented(增强)

核心任务:把检索到的信息注入到 LLM 的输入中。

这里的关键是 Prompt Engineering。你需要设计一个模板,把问题和检索到的上下文组合起来,让 LLM 知道”基于这些信息来回答”。

2.3 Generation(生成)

核心任务:LLM 根据增强后的输入生成最终答案。

这一步看起来简单,但实际上有很多细节:

  • 上下文长度限制怎么办?
  • 检索到多个片段怎么排序?
  • 如果检索结果里有矛盾信息怎么处理?
一句话总结:RAG = 先查资料,再回答。就像开卷考试,允许你翻书,但得自己组织语言作答。

三、完整的 RAG 系统应该长什么样

一个生产级的 RAG 系统绝不是”向量数据库+LLM”那么简单。我把完整链路画出来,你可以看看有多少环节:

3.1 数据准备阶段(Indexing)

1
原始文档 → 文档解析 → 文本分块 → Embedding → 存储到向量数据库

文档解析:PDF、Word、HTML、Markdown,各种格式都要能处理。特别是 PDF,里面有表格、图片、多栏布局,解析起来很头疼。我在这块上吃过不少苦头——有些 PDF 的表格解析出来格式全乱了,调试了好久才搞定。

文本分块(Chunking):这是最关键的一步,也是我踩坑最多的地方。分块策略直接决定检索效果:

  • 分太细:丢失上下文,回答不连贯
  • 分太粗:超出 Embedding 模型限制,或者检索不精确
  • 常见策略:按段落分、按句子分、按固定 token 数分、按语义分

我自己的经验是:先用固定长度(比如 512 tokens)做 baseline,然后根据实际情况微调。别一上来就搞复杂的语义分块,先把简单的做好。

Embedding:把文本块转成向量。选型要考虑:

  • 维度:768D、1024D、1536D 等
  • 语言支持:中英混合场景需要多语言模型
  • 领域适配:通用模型 vs 垂直领域模型

存储:主流选择包括 Pinecone、Milvus、Weaviate、Qdrant、PGVector 等。我在项目里用的是 Qdrant,部署简单,Rust 写的性能也不错,文档也比较清晰。

3.2 查询阶段(Querying)

1
用户问题 → 问题改写/扩展 → Embedding → 向量检索 → 重排序 → 上下文组装 → LLM生成

问题改写(Query Transformation)
用户的问法往往不完美。比如问”公司的年假政策”,可能需要改写成”年假有多少天?怎么申请?”才能检索到更相关的内容。

常用技巧:

  • HyDE(Hypothetical Document Embeddings):让 LLM 先生成一个假设的答案,然后用这个答案去检索
  • 子查询分解:把复杂问题拆成多个子问题分别检索
  • 多向量查询:一个问题生成多个 Embedding 去检索

向量检索
不只是简单的 cosine similarity。实际生产中会用到:

  • 近似最近邻(ANN):HNSW、IVF 等算法
  • 混合检索:向量相似度 + 关键词匹配(BM25)
  • 过滤条件:按元数据过滤(比如只查某个部门、某个时间段的文档)

重排序(Reranking)
向量检索速度快但精度有限。通常先召回 Top-K(比如 100 个),再用更精确的模型(比如 Cross-Encoder)重排序,选出最相关的 Top-N(比如 10 个)。

上下文组装
把选中的文档片段按一定策略拼接成 Prompt。策略包括:

  • 按相似度排序
  • 按时间排序
  • 按原文档顺序
  • 递归摘要(如果文本太长)

3.3 生成阶段(Generation)

Prompt 模板通常长这样:

1
2
3
4
5
6
7
8
基于以下参考信息回答问题。如果参考信息不足以回答,请明确说明。

参考信息:
{context}

问题:{question}

答案:

进阶技巧:

  • 引用标注:让 LLM 在答案中标注信息来源
  • 多轮对话:维护对话历史,支持追问
  • 拒绝回答:当检索结果不相关时,让 LLM 拒绝回答而不是胡说

四、关键技术深度解析

4.1 Embedding 选型指南

Embedding 模型是 RAG 的基石。选错了,后面再怎么优化都白搭。

主流模型对比

模型 维度 语言 特点
text-embedding-ada-002 1536 多语言 OpenAI 出品,效果稳定但贵
text-embedding-3-small/large 1536/3072 多语言 新版,效果更好
BGE-m3 1024 中英 开源,中文场景表现好
GTE-large 1024 多语言 阿里巴巴开源,性价比高
E5-mistral-7b-instruct 4096 多语言 指令式 Embedding,支持任务提示

选型建议

  1. 英语场景:OpenAI 的 text-embedding-3 系列或开源的 E5
  2. 中文场景:BGE-m3 或 GTE-large
  3. 成本敏感:考虑开源模型 + 本地部署
  4. 领域垂直:在通用模型基础上做微调

4.2 向量数据库怎么选

我调研过市面上主流的向量数据库,给你个对比:

托管服务

  • Pinecone:开箱即用,功能全,但贵
  • Weaviate:开源 + 托管,支持复杂查询
  • Zilliz Cloud:基于 Milvus,适合大规模

开源自托管

  • Milvus:功能最全,支持分布式,企业级首选
  • Qdrant:Rust 写的,性能不错,部署简单
  • Chroma:轻量级,适合本地开发和 POC
  • PGVector:PostgreSQL 插件,已有 PG 基础设施的首选

选型建议

  • 快速验证:Chroma 或 PGVector
  • 生产上线:Milvus(大规模)或 Qdrant(中小规模)
  • 不想运维:Pinecone 或 Zilliz Cloud

4.3 Chunking 策略实战

Chunking 可能是 RAG 里最容易被忽视,但又最关键的环节。

常见策略

  1. 固定长度:每 500 个 token 切一刀

    • 优点:简单
    • 缺点:可能切断语义
  2. 按段落:以换行符为界

    • 优点:保持段落完整性
    • 缺点:段落长度差异大
  3. 递归字符:先按段落,段落太长再按句子,句子太长再按固定长度

    • 优点:兼顾语义和长度
    • 缺点:复杂度高
  4. 语义分块:用模型识别语义边界

    • 优点:最智能
    • 缺点:计算开销大

Overlap(重叠)技巧
相邻 chunk 之间保留一部分重叠内容(比如 10-20%),避免关键信息被切分。

Metadata 标记
每个 chunk 要保留元数据:

  • 来源文档 ID
  • 章节标题
  • 页码
  • 时间戳

这些 metadata 对过滤和溯源都很重要。

五、Advanced RAG:不止于基础玩法

基础 RAG 解决的是”有没有”的问题,Advanced RAG 解决的是”好不好”的问题。

5.1 Self-RAG:让模型自己判断要不要检索

传统 RAG 的问题是:不管问题需不需要查资料,都先检索一遍。这会导致:

  • 浪费计算资源
  • 引入无关信息,反而影响生成质量

Self-RAG[^2] 的思路是:让 LLM 自己判断「需不需要检索」。模型在生成每个 token 时,可以决定:

  • Retrieve:去查资料
  • Generate:直接生成
  • Critique:评估生成质量

5.2 Corrective RAG:检索质量不好就换策略

CRAG[^3] 的思路是动态调整检索策略:

  • 如果检索结果置信度高 → 正常生成
  • 如果置信度低 → 用 web search 补充
  • 如果相关性一般 → 对检索结果做精炼

5.3 Multi-hop RAG:多跳推理

有些问题需要多步推理。比如问”公司 A 的 CEO 的母校的校训是什么”,需要:

  1. 先查公司 A 的 CEO 是谁
  2. 再查这个人的母校
  3. 最后查校训

Multi-hop RAG 就是递归地进行「检索-生成-再检索」。

5.4 GraphRAG:结合知识图谱

GraphRAG[^4] 把向量检索和知识图谱结合起来:

  • 用 LLM 从文档中提取实体和关系
  • 构建知识图谱
  • 查询时先在图谱里找相关实体,再检索相关文档

这种方法对复杂关系的问题效果更好。

选型建议:别一上来就用 Advanced RAG。先把基础 RAG 的效果做到 80 分,再根据痛点选对应的增强方案。Advanced RAG 带来的是复杂度,确保你真有这个需求。

六、RAG 评估:怎么知道你的系统好不好

RAG 最难的不是做出来,而是知道做得好不好。

6.1 评估维度

检索质量

  • 命中率(Hit Rate):正确答案是否在 Top-K 里
  • MRR(Mean Reciprocal Rank):正确答案的平均倒数排名
  • NDCG:考虑排序位置的加权指标

生成质量

  • 相关性(Relevance):答案是否回答了问题
  • 忠实度(Faithfulness):答案是否基于检索内容,有没有幻觉
  • 上下文精确率/召回率:用了多少检索到的内容

6.2 评估方法

人工评估

  • 最准,但最贵
  • 适合小数据集和关键案例

自动评估

  • 用 LLM 当评委:让 GPT-4 来打分
  • 指标计算:Ragas[^5] 等框架提供了自动评估能力

A/B 测试

  • 生产环境里最靠谱的评估方式
  • 看用户满意度、任务完成率等业务指标

6.3 Ragas 框架介绍

Ragas 是一个专门用于 RAG 评估的框架,提供了:

  • Context Precision:检索的上下文中有多少是相关的
  • Context Recall:回答问题需要的上下文有多少被检索到了
  • Faithfulness:答案是否被上下文支持
  • Answer Relevancy:答案是否相关

使用示例:

1
2
3
4
5
6
7
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy

result = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy]
)

七、生产落地的坑与解法

7.1 幻觉问题依然严重

RAG 能显著减少幻觉,但无法完全避免。常见情况:

  • 过度综合:LLM 把多个片段的信息错误组合
  • 无中生有:当检索结果不够时,LLM 会脑补

解法

  • 在 Prompt 里明确要求”只基于提供的上下文回答”
  • 设置拒绝回答的阈值
  • 对关键信息做事实核查

7.2 上下文窗口限制

GPT-4 有 128K 上下文,Claude 有 200K,但:

  • 检索结果多了,成本飙升
  • 长上下文下注意力分散,关键信息容易被淹没

解法

  • 限制检索结果数量(比如 Top-5)
  • 对长文档做摘要后再检索
  • 使用 Map-Reduce 策略:分别处理多个片段,再综合答案

7.3 性能优化

RAG 涉及多个环节,延迟容易失控。

优化策略

  • 并行化:Embedding 和检索可以并行
  • 缓存:常见问题直接返回缓存结果
  • 流式输出:LLM 生成时边生成边返回
  • 索引优化:预计算常用查询的检索结果

7.4 多租户隔离

企业场景下,不同用户/部门只能访问自己的文档。

解法

  • Metadata 过滤:查询时加过滤条件
  • 命名空间隔离:不同租户用不同的 collection
  • 权限系统:检索前做权限校验

八、RAG 的典型应用场景

8.1 企业知识库问答

最常见的场景。把公司内部文档(产品手册、技术文档、规章制度等)做成 RAG 系统,员工可以随时提问。

关键点

  • 文档格式多样,解析要 robust
  • 权限控制复杂
  • 需要支持多轮对话和追问

8.2 智能客服

替代传统 FAQ,基于产品文档自动生成回答。

关键点

  • 需要接入工单系统
  • 对回答准确率要求高(不能乱说)
  • 复杂问题要能转人工

8.3 代码助手

基于代码库做问答,比如”这个函数是做什么的”、”怎么使用这个 API”。

关键点

  • 代码 Embedding 需要专门模型
  • 要处理代码的上下文关系(import、继承等)
  • 可能需要结合 AST 分析

8.4 法律/医疗问答

专业领域的问答,对准确性要求极高。

关键点

  • 领域知识库建设成本高
  • 需要可解释性(引用来源)
  • 合规要求严格
选型建议:不是所有场景都适合 RAG。如果问题需要很强的推理能力,或者数据量很小(可以全塞进 Prompt),可能直接用 LLM 更简单。

九、技术选型与架构建议

9.1 完整技术栈推荐

入门版(快速验证)

  • LangChain / LlamaIndex:RAG 框架
  • OpenAI API:Embedding + LLM
  • Chroma:向量数据库
  • 预计开发时间:1-2 周

生产版(企业级)

  • 自研 Pipeline:更灵活可控
  • BGE / GTE:开源 Embedding
  • Milvus / Qdrant:自托管向量数据库
  • vLLM / TGI:LLM 推理服务
  • 预计开发时间:2-3 月

9.2 框架选择:LangChain vs LlamaIndex

LangChain

  • 生态最全,集成最多
  • 灵活性高,可以深度定制
  • 学习曲线较陡

LlamaIndex

  • 专注 RAG,抽象层次更高
  • 数据连接能力强
  • 上手更快

我的建议

  • 快速原型:LlamaIndex
  • 深度定制:LangChain
  • 大规模生产:自研 Pipeline

十、总结与展望

RAG 从 2020 年的一个学术概念,发展到现在成了企业 AI 落地的标配方案。它的价值在于在不对 LLM 做微调的情况下,让模型具备特定领域的知识

但这不意味着 RAG 就是银弹。实际落地中你会发现:

  1. 数据质量决定天花板:再强的 RAG 也救不了烂数据
  2. 评估比实现难:怎么知道好不好,比怎么做更难
  3. 维护成本不低:数据更新、索引重建、模型迭代都是持续工作

未来趋势

RAG 2.0 / Agentic RAG
RAG 不再是静态的「检索-生成」,而是让系统能主动决策:

  • 需不需要检索?
  • 检索几次?
  • 如何验证结果?

这其实就是 Agent 化的 RAG。

多模态 RAG
不只是文本,图片、视频、音频也能检索。比如问”视频里讲了什么”,系统能直接检索视频内容。

端到端优化
现在 Embedding、检索、生成是分开优化的。未来可能会出现端到端训练的 RAG 系统,整体优化。

最后的话: RAG 看着好像不是什么高深技术,但是真要把效果、成本、稳定性三者平衡好,比想象的要麻烦得多。

它把 NLP、数据库、系统工程、产品思维全搅在一起,每个环节都能让你怀疑人生。Embedding 选错了,后面怎么调都没用;Chunking 策略不对,检索质量直接拉胯;评估指标没定好,你甚至不知道自己在优化什么。

最近听到不少”RAG 不行了”的声音,说要被长上下文模型取代了。我的建议是:别急着跟风。先把业务指标跑出来,看看用户的真实反馈。技术趋势归趋势,能解决实际问题的方案才是好方案。希望这篇能让你少走点弯路,毕竟坑我已经帮你踩过了。


参考资源

学术论文

[^1]: Lewis, P., et al. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020.

[^2]: Asai, A., et al. (2023). Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection.

[^3]: Yan, S., et al. (2024). Corrective Retrieval Augmented Generation.

[^4]: Edge, D., et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization.

[^5]: Ragas: Automated Evaluation Framework for RAG.

官方文档与指南

技术博客与教程

视频资源

学术案例研究


延伸阅读:

AI Agent LLM OS

这篇是我最近准备搭 Agent 时整理的学习笔记。它和上一篇 《Agentic 入门:让 AI 不再一把梭,而是像人一样反复干活》 是一个系列:上一篇偏“怎么上手”,这一篇偏“底层怎么运转、上线怎么落地”。

最近我想给自己的工作流做一套 Agent,结果第一周就被现实教育了:这事远不只是“写个提示词模板”那么简单。

真动手之后你会发现,问题全在工程细节里。记忆怎么分层存储?任务队列怎么调度?循环什么时候该停?状态怎么恢复?这些都决定了系统是“能跑 Demo”,还是“能稳定跑在生产上”。

最不适应的一点是,传统软件那套确定性思维在这里会失灵一大截。以前写业务代码,if-else 写完,流程基本可预期;现在很多控制流是 LLM 在运行时动态决定的,排错方式也完全变了。

所以这篇我会把最近研究的几条主线串起来:Andrej Karpathy 的 LLM OS 视角、BabyAGI 的循环架构、以及 LangGraph 的状态机模式。目标很简单:把我自己踩坑后想明白的东西讲清楚,也给正在做 Agent 的你一个可复用的分析框架。

一、核心概念深度辨析:Agent 与 Agentic

先说我自己这次最大的认知纠偏:Agent 是”系统实体(名词)”,而 Agentic 是”设计模式与工作流(形容词)”。

1. Agent(智能体):基于大语言模型操作系统(LLM OS)的计算实体

Agent 不仅仅是一个高级聊天机器人,而应该被视为一种具备拟人化特征的数字实体(People Spirits),或者说是基于大语言模型操作系统(LLM OS)运行的一台新型计算机4-6。在 LLM OS 的宏观架构中:

  • CPU(中央处理器): 大语言模型(LLM)本身负责指令处理、推理与决策5, 7
  • RAM(内存/工作记忆): 模型的上下文窗口(Context Window)。这是一种短期的工作记忆,LLM 在此编排内存与计算以解决问题7, 8
  • 外设与接口(Tools): Agent 可以通过工具调用与数字世界的外部基础设施交互,例如访问文件系统、运行代码沙箱或调用网络浏览器5, 9, 10

2. Agentic Workflow(智能体化工作流):告别 Zero-shot 的迭代范式

吴恩达(Andrew Ng)指出,传统上我们主要以”零样本(Zero-shot)”模式使用 LLM,即提示模型逐字生成最终输出而不进行任何修改。这很像要求一个人从头到尾敲击键盘写文章,全程不允许按退格键11

Agentic 工作流是一种引入迭代循环(Loop)的系统方法论。 它允许系统像人类工程师一样:规划大纲 -> 决定是否需要网络搜索 -> 撰写初稿 -> 审查初稿中的逻辑漏洞 -> 结合审查结果继续修改12

惊人的工程收益:这种工作流带来了巨大的性能提升。在 HumanEval 代码编写基准测试中,GPT-3.5 的零样本正确率仅为 48.1%,GPT-4 为 67.0%;但是,如果将较弱的 GPT-3.5 放入 Agentic 迭代循环中,其正确率飙升至 95.1%13

二、Agent 的底层架构与运行机制

要理解 Agent 的底层架构,我们得先抛弃”它只是个聊天机器人”这个视角,把它当成一台基于自然语言指令运行的新型计算机

2.1 宏观架构:大语言模型操作系统(LLM OS)

前特斯拉 AI 总监 Andrej Karpathy 提出了理解 Agent 架构的最佳抽象模型:LLM OS(大语言模型操作系统)。在这个架构中:

组件 对应关系 功能描述
CPU(处理器) 大语言模型(LLM) 负责处理输入、执行逻辑推理、以及发出指令
RAM(内存 / 工作记忆) 模型的上下文窗口(Context Window) 和 KV Cache 短期工作记忆,任何在当前执行步骤中需要被 LLM 直接读取的信息,都必须被塞进这个窗口里
外存(硬盘 / 长期记忆) 外部的向量数据库(Vector Database,如 Pinecone) 甚至传统关系型数据库 由于 LLM 每次调用完成后会”失忆”(无状态),历史数据必须持久化在这里
外设与执行器(Tools) 代码沙箱(Code Sandbox)、文件系统读写权限、浏览器 API 相当于计算机的网卡、显卡或机械臂

2.2 核心模块拆解(以经典 Agent 为例)

如果剥开代码看,一个标准的自主运行 Agent(例如早期 BabyAGI 或现代框架的核心模块)通常由以下几个核心组件(Components)构成:

1. 大语言模型控制器(LLM Controller)

这是整个系统的大脑。它接收系统提示词(System Prompt),明确当前的角色设定、可用工具列表以及输出格式(通常为 JSON),以此将自然语言转化为结构化的机器动作。

2. 记忆管理系统(Memory System)

  • 短期记忆: 通常由开发框架(如 LangChain/LangGraph)维护当前的对话历史或近期几步的操作日志。
  • 长期记忆: 当任务周期极长时,框架会将之前的操作结果转化为”向量嵌入(Embeddings)”,存入向量数据库。当 Agent 执行新任务时,系统会通过”语义搜索(Semantic Search)”提取最相关的历史经验,拼接进当前的上下文中供 LLM 读取。

3. 工具与环境接口(Tool Integrations / Harness)

LangChain 创始人 Harrison Chase 强调,现代 Agent 的强大来源于强大的“脚手架(Harness)”。这包括预置的工具函数。最核心的工具是文件系统(File System)访问权限,因为它可以让 Agent 把长篇的中间结果写入文件,而不是全部塞进上下文里导致内存溢出。

2.3 运行机制:死循环与状态机(The Loop & State Machine)

Agent 并不是”一口气”运行完的,它的底层是一个事件驱动的循环结构(Loop)。在工程实现里,主流运行机制大致有两种:

机制 A:经典的”三节点”死循环(如 BabyAGI)

BabyAGI 证明了只要三个 Python 脚本节点相互配合,就能让 Agent 无限期自主运转:

  1. 任务执行节点(Task Execution): 读取当前任务队列的第一个任务,结合向量数据库中的历史上下文,调用 LLM 执行任务,并保存结果。
  2. 任务创建节点(Task Creation): 观察上一步的执行结果,调用 LLM 判断”为了实现总目标,接下来还需要新增什么任务?”,并将新任务丢进队列。
  3. 任务优先级排序节点(Task Prioritization): 调用 LLM 对现有的任务队列重新排序,清理冗余,决定下一步最该做什么。 这个循环(While Loop)会不断运转,直到任务列表清空或达到预设的终止条件。

机制 B:现代的图与状态机架构(如 LangGraph)

面对更复杂的企业级业务,简单的死循环难以控制。现代框架(如 LangGraph)将运行机制升级为有向图(Graph)和状态机(State Machine)

  • 节点(Nodes): 代表具体的处理单元(可以是一个调用 LLM 的函数,也可以是执行 Python 代码的工具)。
  • 边(Edges): 包含条件判断逻辑(如 IF-ELSE)。基于当前节点返回的结果,决定数据流向下一个哪个节点。
  • 状态管理(State Management): 整个图共享一个全局状态(State)。每次循环都在更新这个全局状态,并自带持久化(Persistence)功能。这意味着当 Agent 运行到一半崩溃,或者遇到高风险操作需要”人类审批”时,程序可以暂停保存状态,之后再无缝恢复。

2.4 底层工程的终极挑战:上下文工程(Context Engineering)与执行轨迹(Traces)

当我们把上面这些模块拼起来后,运行逻辑会发生根本变化,这也是研发人员最需要适应的地方:

  • 逻辑不再只存在于代码中: 传统软件的控制流(If-Else)写在代码里;但 Agent 的控制流很大一部分由 LLM 在运行时动态决定(具有不确定性)。
  • 上下文工程(Context Engineering)决定成败: 决定 Agent 表现的,是每次循环调用 LLM 时,你在这个循环点(通过检索或状态机)给它组装了什么上下文。如果塞入的信息太多,LLM 会忘记关键指令;如果太少,它会产生幻觉。
  • 排错(Debug)依赖执行轨迹(Traces): 在传统开发中,出 Bug 了我们看代码;但在 Agent 开发中,当系统在第 14 步失败时,看代码毫无用处,因为你需要知道前 13 步它往上下文(Context)里塞了什么。因此,记录每一步提示词、输入和输出的执行轨迹(Traces) 取代了源代码,成为了 Agent 开发中测试和调试的“唯一真相来源”。

三、驱动系统运转的核心:四大 Agentic 设计模式

在底层循环与状态机之上,真正让 Agent 表现出”智能感”的,是吴恩达总结的四种核心 Agentic 设计模式(Design Patterns)24

1. 工具使用(Tool Use)

赋予 LLM 网络搜索、代码执行等外部函数调用能力,以收集信息或处理数据24

2. 规划(Planning)

面对复杂目标时,LLM 能够自主提出并执行一个多步计划24

3. 反思(Reflection / Self-Correction)

强迫 LLM 检查自身的输出,发现缺陷并提出改进方案24。例如,在缓解模型幻觉的研究中,Chain-of-Verification (CoVe) 强制模型生成验证问题并自我审查,而 Self-Refine 则采用”生成 -> 反馈 -> 改进”的迭代框架扮演自我批评者的角色25, 26

4. 多智能体协作(Multi-agent Collaboration)

实例化多个不同的 AI 角色,通过分担任务和相互辩论,得出比单一 Agent 更优的解决方案24

四、研发视角的范式转换:开发 Agent 与传统软件的区别

LangChain 创始人 Harrison Chase 指出,构建 Agent 会显著改变传统软件工程的一些基本常识27

1. 逻辑存在于模型中(非确定性系统)

传统软件的逻辑被硬编码(Hard-coded)在代码中,开发者可以确切知道程序的流向;而在 Agent 中,程序的控制流是由大模型在运行时基于上下文动态涌现的,具有高度的黑盒性和不确定性27, 28

2. “执行轨迹(Traces)”取代代码,成为 Debug 的唯一事实依据

由于 Agent 在循环中反复运行,当系统在第 14 步报错时,单看代码无法推断前 13 步 LLM 的上下文窗口里到底装载了什么29。因此,详细记录每一步输入、输出、工具调用和状态变化的执行轨迹(Traces),取代了源代码,成为了开发者测试、排错(Debug)和团队协作的核心工具27, 30, 31

3. “脚手架(Harness)”与”上下文工程(Context Engineering)”决定成败

长周期 Agent(Long-Horizon Agents)的成败往往不仅仅取决于底层模型有多强大,更取决于开发者构建的 Harness(深度定制的脚手架体系)32, 33。这包括内置的规划工具、处理超长文本的压缩(Compaction)策略,以及至关重要的文件系统访问权限(File System Access)9, 33, 34。这一切本质上都是 “上下文工程(Context Engineering)”——即在 Agent 循环的每一个特定步骤,精准地为其上下文窗口注入最需要的记忆、工具结果和指引32, 35, 36

五、生产环境落地的终极指南

对于准备把 Agent 投入生产环境的研发团队,行业里有一个高度一致的共识:不要一上来就追求 100% 全自动(Fully Autonomous)

1. 做好迎接”Agent的十年”的准备,它是一个”九的游行”

Andrej Karpathy 强调,目前行业处于”Agent的十年(Decade of Agents)”,而非所谓的一年37。构建可靠的 Agent 与研发自动驾驶汽车极度相似:展示一个能解决 90% 问题的 Demo 非常容易,但要达到生产级别,需要经历漫长的”九的游行(March of nines,即追求 99.9% 到 99.99% 的极端可靠性)”38, 39

2. 定位为”初稿生成器(First Drafts)”

现阶段长周期 Agent 最杀手级的应用场景,不是直接交付最终结果,而是生成一份极具价值的”初稿”40。无论是代码的 Pull Request、SRE 的日志诊断分析报告,还是客户支持的复杂调研,Agent 的作用是承担粗重的初期工作,然后交由人类审核与编辑40, 41

3. 保留”自主性滑块(Autonomy Slider)”

在产品设计(如 Cursor 或 Perplexity)中,必须赋予用户控制权42-44。开发者可以选择只补全一行代码(Tap completion)、修改单个文件,或是让 Agent 放开手脚重构整个代码仓库,用户可以根据任务复杂度动态调整这种”自主性滑块”44, 45

4. 终极形态:环境智能体(Ambient Agents)与 智能体收件箱(Agent Inbox)

传统的基于聊天(Chat-based)的 Agent 受限于极高的延迟要求和一对一的交互瓶颈46-48。未来的生产级落地形态将走向环境智能体(Ambient Agents)46, 49

  • 事件驱动与异步执行: Ambient Agents 在后台静默运行,监听并响应事件流(如新收到的客户邮件、代码库提交、系统报警等)46。由于没有即时聊天的延迟压力,它们可以执行包含数十次工具调用和复杂规划的深层任务48, 50
  • 人类在环(Human-in-the-loop)与 Agent Inbox: 这种异步、高并发的运行模式绝不意味着失控。相反,系统必须设计一个类似”智能体收件箱(Agent Inbox)”的交互界面51。Agent 将执行到一半的高风险操作或半成品报告推送到收件箱中,人类在此进行审批(Approve)、编辑修改(Edit)、解答 Agent 的疑问(Answer),甚至进行”时间旅行(Time Travel,即回滚到之前某一步重新运行)”52, 53。这不仅保证了业务的安全性,人类的纠错反馈也将作为记忆(Memory),驱动 Agent 系统的自我迭代与进化51, 54

六、当前实践:病历质量评估 Agent 的版本演进(从简单到复杂)

为了和前面的学习框架对齐,我把当前病历质量评估 Agent 的实现拆成四个版本。每一版都解决上一版暴露的问题,而不是一上来就做“大而全”。

V1:纯规则引擎(无 Agent)

最早版本只有确定性规则:字段完整性、格式约束、必填项检查。

  1. 优点是稳定、可解释、上线快。
  2. 缺点是只能发现“结构性问题”,看不懂跨段语义矛盾。

这一版对应本文里的“传统确定性系统”,适合作为基线能力。

V2:单 Agent 循环(引入 Agentic Workflow)

第二版把 LLM 接入到循环里,流程是:读取病历 -> 发现问题 -> 生成建议 -> 自检修正。

  1. 能力明显增强,可以识别时间线冲突、表达不一致、潜在漏项。
  2. 也暴露出典型问题:偶发幻觉、建议过于抽象、同类问题重复输出。

这一版验证了本文第二部分的结论:有循环就会有收益,但没有工程约束就会不稳定。

V3:图与状态机编排(从“能跑”到“可控”)

第三版把单循环改成状态机,拆成多个节点:

  1. 输入结构化节点。
  2. 规则校验节点。
  3. 语义评估节点。
  4. 证据绑定节点。
  5. 报告生成节点。

每一步都有明确输入输出和状态落盘,支持暂停/恢复/重跑。
这一版对应本文的 LangGraph 思路,本质收益是“可观测、可回放、可定位”。

V4:生产化闭环(Human-in-the-loop + Traces)

当前线上思路是第四版:不是追求全自动判定,而是做“高质量建议系统”。

  1. 高风险结论必须进人工审核收件箱。
  2. 每条问题必须附原文证据和触发依据。
  3. 全链路记录 Traces(上下文、模型输出、工具调用、状态迁移)。
  4. 人工反馈回写,驱动下一轮提示词和规则优化。

这正好对应本文“上下文工程 + 执行轨迹 + 自主性滑块”的三条主线。

这条演进线给我的最大启发:医疗质控这种高风险场景里,Agent 的价值不在“替人做最终判断”,而在“把问题找全、证据对齐、把人工复核效率做上去”。

总结:我们正在经历软件工程范式的一次关键转折。从"软件 1.0"的确定性代码,到"软件 2.0"的神经网络权重,再到"软件 3.0"的 LLM 驱动 Agent,每一次跃迁都在重新定义"编程"这件事。对研发人员来说,理解 Agent 的底层架构——LLM OS 的计算模型、四大设计模式、以及上下文工程的核心地位——会是未来十年非常关键的技术投入。

核心参考文章

1. 吴恩达 (Andrew Ng) —— Agentic 核心概念与设计模式

3. Andrej Karpathy —— LLM OS 系统愿景

4. BabyAGI —— 自主智能体先驱

AI SaaS思考
这一篇也是很早在草稿箱的文章,今天才整理出来。

因为我这几年都在 SaaS行业,然后现公司也是做xx行业AI产品(SAAS),这段时间一直反复在想一个问题:AI+SaaS 和传统 SaaS,到底有什么不同?一开始很容易把注意力放到”要自己训一个大模型”这种问题上比如我们公司ceo或者外部投资人、,但我可能有些不成熟的观点,我是觉得这根本不是核心,最起码当你的体量没起来没被客户接受或者故事没被资本接收的时候我觉得更应该关注的是另外几件事。当然肯定只能写能说的,跟公司商业无关的哈。

先把结论放在前面。

我认为 AI+SaaS 和传统 SaaS 最本质的差异或者说核心关注点应该集中在三个地方:数据设计、人效提升方式、交付给客户的价值

但这三件事有一个前提,先别急着聊 AI,SaaS 的基本功必须先做扎实。


前提:SaaS 基本功不扎实,AI 进来只会更乱

这话我是越来越信。

很多团队一引入大模型,心态就容易飘。总觉得以前没做好的地方,权限没理清、流程不稳定、数据结构乱,好像 AI 一进来能顺带解决。

实际上恰恰相反。

AI 不会替你补基本功,它只会把原来系统里的那些问题,放大得更快、更明显。

如果一个 SaaS 系统的业务流程本身就跑不顺,系统边界本身就模糊,那接上 AI 以后,输出的结果会更不可信,追溯问题会更难,整体更乱。

所以这件事我的顺序是:先把系统做好,深入了解以及解决客户基本的痛点问题,先把业务跑顺,再来谈 AI 怎么加。

别把顺序弄反了。


一、数据设计,不只是给人用了,还要给 AI 用

这是 AI+SaaS 和传统 SaaS 差别最大的地方之一,而且是很多团队最容易忽视的。

传统 SaaS 里,数据设计的诉求其实很清楚:

  • 能支撑业务流程
  • 能做增删改查
  • 能出统计报表
  • 能满足权限和审计

这些要求到今天依然成立,没什么可怀疑的。

但 AI+SaaS 多了另一层要求:

你设计出来的数据,不只是给系统存和查的,还要能被 AI 理解、检索、调用。

这一要求一进来,标准就变了。

拿一个典型的线下服务型业务来说,传统 SaaS 更在意的通常是:

  • 客户档案能不能录进去
  • 服务记录能不能查出来
  • 收费能不能对上
  • 报表能不能导出

但 AI+SaaS 还需要往前再走一步:

  • 服务过程的内容能不能结构化
  • 客户、服务类型、处理结果之间有没有稳定的关联
  • 这些字段能不能支撑后面的摘要、推荐、风险提示

传统 SaaS 的数据更像是为业务流转服务的。

AI+SaaS 的数据要做两件事:既要支撑业务流转,也要支撑 AI 理解、检索和调用。

这就是为什么我说,很多团队问”要不要自己训模型”,问的方向其实偏了。

对大多数行业 SaaS 来说,真正有价值的不是模型的参数量,而是:

  • 行业数据有没有结构化
  • 字段语义清不清楚
  • 对象之间的关系完不完整
  • 历史记录好不好检索

这几个东西做扎实了,哪怕你接的是现成模型,AI 也能真正进入业务。

反过来,数据设计如果本身就散、乱、缺语义,换再强的模型,效果也不会有质变。

行业数据结构,才是垂直 SaaS 的护城河,不是模型参数。


二、提升人效,这才是 AI+SaaS 最直接的商业价值

很多人聊 AI+SaaS,喜欢聊技术能力、聊 Agent、聊多模态。

但我接触下来,客户真正关心的,往往比这朴实得多:

能不能少用几个人,或者让同样的人,把事情做得更多、更稳。

这件事在很多行业里都很具体。

拿一个高频服务场景举例,过去的流程大概是:

  • 服务过程中手动记录,或者靠记忆
  • 服务结束后手动录入系统
  • 手动整理各个字段
  • 手动生成跟进记录或报告

每一步都是人在做,每一步都可能出错,每一步都在消耗时间。

AI+SaaS 能做的,是把这个链条改成:

  • 语音或对话自动转文字
  • 自动抽取关键字段
  • 自动生成服务记录草稿
  • 自动同步到系统

这不是把人换掉,而是把那些高频、重复、容易出错的动作,尽量自动化掉,让人能把精力放在真正需要判断的地方。

传统 SaaS 能做的,更多是给你一套在线的操作台,让这些动作在系统里完成,而不是纸上或者微信群里。

AI+SaaS 往前多走了一步:让系统替人分担一部分原本不得不做的工作。

这个价值很直接:

  • 减少重复劳动
  • 提升单人产出
  • 降低遗漏概率
  • 让服务质量更稳定

AI+SaaS 比传统 SaaS 进一步的地方,不只是管理效率,而是执行效率也开始被提起来了。

当然这件事有个前提。

AI 要能真正进入业务,必须有一层东西支撑:系统里的那些”技能”。

比如 AI 说要查某条历史服务记录,系统得真的有这个查询能力让它调;AI 说要生成跟进报告,系统得真的能写入并存档。

模型负责的是理解意图、生成内容。系统负责的是真正执行动作。

只有这两件事都做到位了,人效提升才是真实的,而不是停在 demo 层面。


三、交付价值不一样了:从交付功能,到开始交付结果

传统 SaaS 卖的东西,本质是:把业务流程搬进系统

原来靠 Excel、靠微信群、靠纸张做的事情,变成线上化、标准化、可追踪。

这个价值到今天依然成立,依然有人在买单。

但 AI+SaaS 开始往前多走一步:

不只是把流程放进系统,而是把一部分结果做出来。

同样是一个服务场景,传统 SaaS 交付的是:

  • 一套录入服务记录的表单
  • 一套查询历史档案的功能
  • 一套出报表的界面

AI+SaaS 交付的是:

  • 服务结束,记录草稿已经生成好了
  • 关键字段已经自动提取出来了

客户买的,就不只是一套表单了。

他买的是”少填一堆表、少做一堆重复动作、事情还能推进得更快”这件事。

这就是交付价值的变化。

以前更像是交付功能。现在开始更像是交付结果。

当然这个结果交付,并不是一上来就能做到的。

它建立在:数据设计对了、系统能力封装好了、AI 工作流搭稳了,这三件事都就位之后,才能真正发生。


那要不要自己训模型?

顺带说一下这个经常被q到的问题。

我的判断比较直接:大多数行业 SaaS,一开始根本不需要训练自己的模型,甚至连微调都不用急。

理由很简单。

基础模型会越来越便宜越来越好用甚至可能会未来小参数的都能赶上现在大参数的模型,今天需要很高成本做的事,明年可能已经是基础设施。

你当前真正更稀缺的,不是模型参数,而是:

  • 数据结构设计
  • 行业 KnowHow
  • AI 工作流设计
  • 系统调用能力封装
  • 成本控制

这几件事做扎实,接现成模型就已经够用了,而且可以替换,可以随时换更便宜、更强的模型进来。

等到数据量足够大、样本质量足够高、业务场景足够稳定,那时候再考虑轻量微调,顺序才是对的。

不是永远不做,而是不要一上来就做。

你不是 AI 公司,你是用 AI 做增强的行业 SaaS 公司。这两件事,差别很大。


最后

如果要我把 AI+SaaS 和传统 SaaS 的差别压缩成一句话:

传统 SaaS 帮客户把流程搬进了系统;AI+SaaS 要做到的,是在流程进了系统之后,还能把结果尽量做出来、把重复劳动尽量替掉。

但要做到这一步,要走的顺序是:

  1. SaaS 基本盘先稳住
  2. 数据设计为 AI 理解和检索打好基础
  3. 系统能力封装好,让 AI 真正能做事
  4. 人效提升有了数据支撑,才是真实的
  5. 从交付功能,慢慢往交付结果走

这条路没有捷径。但每一步踩实了,护城河会越来越深。

AI Context Engineering 大模型

📎 说明:这篇文章是我在准备搭agent时学习过程的整理跟 《Agentic 入门:让 AI 不再一把梭,而是像人一样反复干活》 一起整理的。按理说这篇应该更早发,但当时一直没整理顺,所以拖到了后面。内容还是以我自己的学习理解为主。

我最早注意到 Context Engineering(上下文工程),大概是在 Prompt Engineering 被聊得很热之后。

Context Engineering(上下文工程)最早接触是Prompt Engineering 概念火了一段时间之后。
在这之前好像AI应用开发的核心焦点一直是“提示词工程(Prompt Engineering)”。然而,随着大语言模型(LLM)从单次问答向能够自主运行的智能体(Agent)演进,上下文工程(Context Engineering)变得更加重要。

很多时候,真正决定模型表现的,不是你那句话写得漂不漂亮,而是它在那一刻到底看到了什么,没看到什么。
构建大语言模型应用,正在从“如何遣词造句写出完美的提示词”,转变为“如何配置上下文才能最大概率地生成模型所需的行为”。

一、先说问题:Prompt Engineering 到底卡在哪儿?

只要你认真用过一段时间 AI,大概率都会碰到这种情况:

你写了一个很长的 Prompt,感觉已经把话说明白了,结果模型要么没懂,要么回答得很飘。可你换个问法,它又突然正常了。

你开始怀疑:是我 Prompt 写得不好,还是模型本身忽强忽弱?

这个现象背后,其实是个更底层的问题:模型能不能干好活,很大程度上取决于它当下到底能看到什么。

Prompt Engineering 解决的是“怎么把问题问清楚”。这当然重要,但它管不了另一件更麻烦的事: 当你要处理的信息,已经不是一轮对话能装下的时候,该怎么办?

比如:

  • 你有一本 500 页的技术文档,想让 AI 帮你答用户问题
  • 你的系统里有十几个上下文来源(用户历史、实时数据、业务规则、代码片段……)
  • 你的 Agent 要跑十几步任务,中间状态要一直传下去

到了这些场景里,“怎么问”就不是最核心的问题了,“怎么喂”才是

这就是 Context Engineering 要解决的事。


二、那 Context Engineering 到底是什么?

Anthropic 的说法是,它可以看成是从 Prompt Engineering 往前走了一步《Effective context engineering for AI agents》,但两者盯的东西并不一样:

  • Prompt Engineering:怎么写好这一句话
  • Context Engineering:怎么组织整个信息环境

如果用我自己的话讲,后者更像是在做“信息架构”,你要决定:

  1. 哪些信息该放进上下文
  2. 这些信息按什么结构组织
  3. 什么时机、以什么方式把它们喂给模型
  4. 超出上下文窗口时,怎么选择性地保留和丢弃

这件事听起来像工程,不是因为这个词高级,而是它真的就是工程问题。它不是让你背几个提示词套路,而是要把整个信息流理清楚。

有一个比方我觉得很贴:

Prompt Engineering 是教你怎么跟一个人说话;Context Engineering 是在他开口之前,帮你决定他今天能看到哪些材料、能记得哪些背景、桌上摆的是什么资料。


三、它真正卡住的,不只是窗口大小,而是“注意力预算”

很多人第一次接触上下文工程,都会先盯着“上下文窗口够不够大”。

这个视角没错,但还不够。

但我现在越来越觉得,更准确的说法应该是:模型真正稀缺的,不只是 token 容量,而是注意力预算。

你可以把上下文窗口理解成模型工作的“临时内存”。窗口大,确实代表理论上能塞进去更多东西;但塞得进,不等于它就能稳定地理解、提取、关联这些内容。

这时候会出现一个很现实的问题:上下文腐烂(Context rot)

说白了就是,上下文越长、信息越杂,模型越容易出现下面这些毛病:

  • 前面明明给过规则,后面还是忘
  • 中间插入很多工具输出后,开始抓不住重点
  • 看起来“都读了”,但真正回答时只抓住开头和结尾
  • 局部结论没问题,一到多步任务串起来就开始漂

所以 Context Engineering 的核心不是“拼命往里塞”,而是:用尽量少、但信号强的信息,占住模型最宝贵的注意力。

所以很多 AI 系统上线以后表现不稳,问题不一定出在模型不够聪明,很多时候就是上下文里混进了太多低信号内容。


四、底层是什么?先聊 Self-Attention

要真正理解”上下文”对模型意味着什么,绕不开一篇 2017 年的论文:《Attention Is All You Need》《Attention Is All You Need》,也就是提出 Transformer 架构的那篇。

它里面最核心的东西是 Self-Attention(自注意力机制)

粗暴一点讲,模型在生成每一个词的时候,都会去“扫一眼”上下文里别的词,算一遍“谁跟我现在最相关”,再决定往下吐什么。

这意味着:模型的能力,很大程度上取决于窗口里装了什么

你给它看的内容越准确、越有结构、越聚焦,它的注意力就越能集中在真正重要的部分。你塞一堆无关信息进去,它不会自动过滤掉,它只会”稀释”它的注意力。

所以“上下文工程”这件事,如果往底层追,本质上就是在跟 Self-Attention 的工作方式打交道。


五、系统提示词也有讲究:别太死,也别太飘

很多人一提上下文工程,第一反应还是去改 System Prompt。

这当然要改,但我现在觉得,问题通常不是“写得不够长”,而是写得那个劲儿不对

我现在更认同一个判断:好的系统提示词,应该在“规则足够清楚”和“保留模型推理空间”之间找到平衡。

两个极端都不行:

  • 写成 if-else 说明书,模型像被绑住手脚,遇到复杂情况反而不会变通
  • 写得过于抽象,只说“请你专业、严谨、友好”,基本等于没说

我现在更倾向于把系统提示词拆成几个清楚的区块,比如:

  • 背景信息:这个 Agent 是干什么的
  • 目标:这次任务最终要达成什么结果
  • 约束:哪些事情不能做,哪些风险要优先规避
  • 工具说明:什么时候该调用什么工具
  • 输出要求:最后结果要长什么样

你会发现,这时候你做的已经不只是“写一句提示词”了,而是在给模型搭一个小型运行环境。


六、RAG 里最容易踩的坑,Anthropic 想了个解法

现在很多 AI 应用都在做 RAG(检索增强生成)——就是把一堆文档切片,存进向量数据库,等用户提问时捞出相关片段塞给模型。

这条路当然没错,但里头有个很容易被忽略的问题:文档一切片,背景信息很容易丢。

举个例子:你有一份技术文档,里面有一段话:

“这个接口的超时时间配置建议设为 5 秒。”

这句话单独拿出来是完整的,但如果你不知道这是在说”内网服务调用”而不是”公网 HTTP 请求”,这条建议可能直接坑掉你。

Anthropic 在 2024 年提出了 Contextual Retrieval《Contextual Retrieval》 来解这个问题。核心思路其实不复杂:

在切片存储之前,先让模型给每个片段加一段 **”上下文注释”**,说明它来自哪个文档、属于哪个章节、要解决什么问题。然后把这个带注释的片段再做向量化。

这样检索出来的内容,就不只是一句话,而是“一句话 + 它原本待着的语义背景”,命中质量会明显好一些。

配合 BM25 关键字检索(而不是单纯依赖语义向量),精确度进一步提升。

简单来说:不是切得多就能捞得准,要让每一块碎片都知道自己是从哪来的。


七、真正有用的方式,不是预加载全部,而是按需取用

很多系统在早期都会很自然地走一条路:既然上下文重要,那就多塞一点。

比如:

  • 用户资料全塞进去
  • 历史对话全塞进去
  • 检索结果 top 10 全塞进去
  • 工具执行日志原样全塞进去

一开始看起来确实会有一种“信息很全”的安全感,但很快就会遇到两个问题:

  1. 模型越来越贵
  2. 模型越来越乱

更像样一点的做法,其实是 Just-in-time context,也就是“即时上下文”。

它的思路是:默认只保留索引、摘要、标识符,真正需要的时候再去取原文。

比如不要把整个代码库文档直接扔进上下文,而是先给模型:

  • 文件路径
  • 模块索引
  • 函数签名
  • 哪些文档可能相关

然后让它在任务执行过程中,通过检索、搜索、读取工具,一步步把真正需要的信息拿进来。

这种方式本质上是在做“渐进式披露”。不是一上来把整个图书馆砸给模型,而是先把目录递给它,再让它决定该翻哪几页。

这比一次性预加载所有材料,更接近人类真正工作的方式。


八、长任务为什么特别容易翻车?因为上下文会被历史污染

上面说的 Contextual Retrieval,主要解决的是“单次检索”的质量问题。但到了更复杂的 Agent 场景里,还会多出另一个麻烦:上下文不是一开始就完整的,它是任务跑着跑着一点点堆起来的

多步 Agent 工作流最烦人的地方,不一定是某一步直接做错,而是前面做过的所有事,最后都会变成后面的上下文负担。

比如一个任务跑了二十几步之后,上下文里可能已经混着:

  • 早期已经失效的假设
  • 很长但价值不高的工具输出
  • 中途试错留下来的噪音
  • 真正关键但只出现过一次的结论

这时候问题就不是“窗口够不够”,而是历史信息正在污染当前决策

所以长周期任务里,通常就得想办法处理这件事。常见做法大概有三类:

① 压缩(Compaction)

当窗口快满时,不是简单截断,而是把已经完成的阶段总结成高保真摘要,把冗长原始过程替换掉。

② 外部记忆(Structured note-taking / Agent memory)

把待办、关键决策、阶段性结论写到上下文窗口外,比如 NOTES.md、状态文件或者数据库。后续即使重新开一个新窗口,也能快速续上。

③ 子智能体隔离(Sub-agent)

把特别耗 token 的探索任务丢给独立子智能体去做,让它在自己的“干净窗口”里完成搜索、归纳,再把浓缩后的结论返回主智能体。

这三件事,本质上都在解决一个问题:别让历史细节一直赖在窗口里,持续污染当前判断。


九、Anthropic 的工程实践:XML 标签、工具边界和 Prompt Caching

具体到工程实践,Anthropic 在这篇文章里《Effective context engineering for AI agents》 给了几个我觉得挺接地气的抓手:

① XML 标签

当你需要在上下文里放多个来源的信息(用户资料 + 业务规则 + 代码片段 + 历史记录),用纯文本堆在一起,模型很容易把它们”混为一谈”。

用 XML 标签把它们显式分开,能让模型更清楚哪块是哪块:

1
2
3
4
5
6
7
8
9
10
11
<user_profile>
用户是一个年营收 500 万的 SaaS 创业公司 CTO
</user_profile>

<business_rules>
禁止直接推荐未上线的功能
</business_rules>

<user_query>
我们应该怎么设计权限系统?
</user_query>

这玩意儿不是什么魔法,说白了就是把结构摆明白,别让模型自己猜。

② 工具边界要清楚

很多 Agent 失败,不是因为工具不够多,而是因为工具太多、定义太像、边界太模糊。

如果一个工具库里同时有:

  • search_docs
  • lookup_docs
  • find_doc_context
  • query_knowledge

那别说模型,人都得先愣一下。

我现在越来越觉得,工具其实就是模型和外部世界的契约。契约越清楚,模型越稳;契约越含糊,它越容易瞎试。

③ Prompt Caching(提示词缓存)

如果你的系统里有一段很长的固定上下文(比如一份 300 页的产品手册),每次用户提问都要把这段内容完整喂给模型,成本很高。

Prompt Caching 允许你把这段内容缓存在 API 层,后续调用直接复用,不重复计算。

这事看起来像纯工程优化,但很关键。因为它会直接决定:“长期带着一大坨稳定上下文工作”这件事,在成本上到底能不能成立。


十、Karpathy 的那个比喻:上下文窗口是模型工作的“临时内存”

在 OpenAI 前首席科学家 Andrej Karpathy 的一个演讲里《Software Is Changing (Again)》演讲,他把上下文窗口比作大模型的”有限内存”。

这个比方我觉得挺好懂:

模型本体里存的是”知识”,但它在干活时看到的,只有上下文窗口里的内容。超出窗口的东西,它看不见,也不存在。

这就是为什么:

  • 你聊了很长一段时间之后,模型开始”忘事”
  • 你让它处理一个超长文档,它只抓住了开头和结尾
  • 你的多轮 Agent 任务执行到一半开始乱套

根本原因都一样:窗口装不下了,或者装的东西不对

上下文工程要做的,说到底就是在这个有限空间里,把最该放进去的东西,用合适的方式放进去。


十一、整理一张表:四个角度看上下文工程

来源 核心关键词 解决的问题
Anthropic Contextual Retrieval / XML 标签 / Prompt Caching 如何结构化组织海量背景,避免信息丢失和成本失控
Transformer 论文 Self-Attention 模型如何在机制层面消费上下文
Agentic Workflow 动态上下文 / 外部记忆 / 多步传递 如何在多轮任务里持续维护有效上下文
Karpathy Context Window / Working Memory 上下文为什么像“临时内存”,以及为什么会忘事

把这四个角度放在一起看,我自己现在的理解是,Context Engineering 其实横跨了四层:

  • 模型层:注意力到底怎么分配
  • 检索层:拿进来的资料到底准不准
  • 工作流层:多步任务怎么传递状态
  • 工程层:成本、延迟、缓存、工具契约怎么控制

也正因为它跨层,所以它不是一个“提示词小技巧”,而是 AI 系统设计里绕不过去的一部分。


十二、我目前的几个实际感受

老实说,我自己学这块之前,踩过的坑还真不少:

  1. 把所有背景信息一股脑塞进 System Prompt。当时总觉得“多给一点总没错”,结果就是模型越来越飘。后来才明白,不是信息越多越好,是相关信息越准越好

  2. 以为 RAG 就是检索 + 拼接。直接切片、存向量、捞出来就完事。后来真碰到上下文断裂的问题,才意识到:片段不能只被捞出来,它还得“知道自己是谁、从哪来”。

  3. 把 Prompt Engineering 和 Context Engineering 混在一起。一开始总觉得问题问对了就行,后来才发现,一到 Agent 场景里,“信息怎么组织”很多时候比“问题怎么问”更靠前。

  4. 误以为长上下文天然更强。后来越做越发现,长上下文只是给了你“可以多放点东西进去”的机会,但如果没有筛选、压缩、分层和按需取用,它反而更容易把模型带偏。

说白了,上下文工程是软件工程在 AI 时代的一个延伸——你不只是在写 Prompt,你在设计信息流。


结尾

这篇还是偏学习整理,没有往代码实现那层展开太多,主要先把我自己脑子里这套概念骨架搭起来。

如果你也在做 AI 应用,或者已经开始搭 Agent / AI 系统,我现在会更建议先盯住这几个问题:

  1. 你给模型的,到底是不是“最小但高信号”的信息?
  2. 你的检索结果,是不是保留了足够的背景语义?
  3. 你的多步任务里,有没有一套压缩、记忆、续跑机制?
  4. 你的工具和提示词结构,是否清晰到让模型不用猜?

这些问题如果没想清楚,模型就算再聪明,系统表现也还是会一阵一阵的。

Anthropic 最新发布了一篇关于长期运行 Agent 应用 Harness 设计的深度技术文章,作者是 Prithvi Rajasekaran。文章分享了如何通过多 Agent 架构(生成器+评估器)来突破传统单 Agent 的能力天花板,实现复杂应用的自主构建。

核心问题:为什么简单实现不够?

传统的单 Agent 实现会遇到两个关键问题:

1. 上下文焦虑(Context Anxiety)

随着任务时间增长,模型会出现”上下文焦虑”——在接近上下文限制时提前结束工作。虽然上下文压缩(compaction)可以延长会话,但无法给 Agent 一个干净的 slate。解决方案是上下文重置(Context Reset):完全清空上下文窗口,通过结构化交接(handoff)让新 Agent 接手。

2. 自我评估偏差

当要求 Agent 评估自己的工作质量时,它们往往过于乐观——即使质量明显 mediocre,也会自信地给予好评。这在主观任务(如设计)中尤为明显。

解决方案:分离生成与评估

借鉴 GAN(生成对抗网络)的思想,将生成器(generator)和评估器(evaluator)分离。虽然评估器也是 LLM,但调校一个独立的 skeptical evaluator 比让生成器自我批评要容易得多。


前端设计实验

作者首先在前端设计领域验证这个思路。Claude 默认倾向于安全、可预测的布局,技术可用但视觉平庸。

四项评分标准

为了让主观质量可量化,作者制定了四项评分标准:

标准 含义
Design Quality 设计是否像一个统一的整体,而非零件拼凑?色彩、排版、布局是否创造独特氛围?
Originality 是否有定制决策?还是模板布局、库默认值、AI 生成模式?
Craft 技术执行:排版层级、间距一致性、色彩和谐、对比度
Functionality 可用性:用户能否理解界面功能、找到主要操作、完成任务?

重点:Design Quality 和 Originality 权重更高,明确惩罚”AI slop”(紫色渐变+白卡片的典型 AI 生成模式)。

迭代循环

  • 生成器创建 HTML/CSS/JS 前端
  • 评估器通过 Playwright MCP 与页面交互、截图、评分
  • 反馈回流给生成器进行下一轮迭代
  • 运行 5-15 轮迭代,每轮 4 小时

惊人发现:在为一个荷兰艺术博物馆设计网站的第 10 轮迭代中,生成器完全推翻了之前的设计,重新想象为一个 3D 空间体验——CSS 透视渲染的方格地板、自由位置悬挂的艺术品、门洞导航而非滚动点击。这是单次生成从未见过的创意飞跃。


扩展到全栈开发

基于前端实验的经验,作者将其应用到全栈开发,构建了一个三 Agent 架构:

架构设计

Agent 角色 职责
Planner 规划器 将 1-4 句简单 prompt 扩展为完整产品 spec, ambitious 但避免过早指定技术细节
Generator 生成器 按 sprint 逐个实现功能,使用 React + Vite + FastAPI + SQLite/PostgreSQL 技术栈
Evaluator 评估器 通过 Playwright MCP 测试运行中的应用,评分并反馈 bug

Sprint 契约

在每次 sprint 前,生成器和评估器协商”sprint 契约”:明确定义”完成”的标准和可测试的行为。这弥补了用户故事与可测试实现之间的鸿沟。

对比实验

Prompt:创建一个 2D 复古游戏制作器,包含关卡编辑器、精灵编辑器、实体行为和可玩测试模式。

方案 时长 成本 结果
单 Agent 20 分钟 $9 核心功能损坏,实体无法响应输入
完整 Harness 6 小时 $200 功能完整,包含 AI 辅助精灵生成、关卡设计、游戏导出

关键差异

  • 单 Agent 版本:布局浪费空间、工作流程僵化、核心游戏功能损坏
  • Harness 版本:完整 viewport 利用、一致的视觉识别、AI 集成加速工作流、实际可玩的游戏

持续优化

移除 Sprint 结构

随着 Opus 4.6 发布(规划更仔细、更长任务保持、更好的代码审查和调试能力),作者尝试简化 harness:

  • 移除 sprint 结构,让生成器连续运行 2 小时以上
  • 评估器改为单次最终评估而非每 sprint 评估
  • 保留 planner(防止生成器 under-scope)和 evaluator(捕获边缘 bug)

新实验:浏览器端 DAW(数字音频工作站)

  • 运行 4 小时,$124
  • 生成器连续运行 2 小时无需分解
  • 评估器仍捕获关键缺陷:剪辑无法拖动、音频录制仅 stub、效果可视化不足

结论:评估器不是固定的是/否决策,而是取决于任务是否超出生成器独立可靠完成的边界。


核心启示

  1. 模型能力边界决定 harness 复杂度:随着模型改进,某些假设可能过时,值得定期 stress test
  2. 分解任务 + 专业化 Agent:复杂任务中,分解并应用专业化 Agent 能带来提升
  3. 新模型发布时重新评估 harness:剥离不再关键的组件,添加新组件实现更大能力
  4. 有趣 harness 组合的空间不会缩小:随着模型改进,空间会移动,AI 工程师的工作是不断找到下一个新颖组合

关键引用

“The space of interesting harness combinations doesn’t shrink as models improve. Instead, it moves, and the interesting work for AI engineers is to keep finding the next novel combination.”

“It is always good practice to experiment with the model you’re building against, read its traces on realistic problems, and tune its performance to achieve your desired outcomes.”


原文链接https://www.anthropic.com/engineering/harness-design-long-running-apps

作者:Prithvi Rajasekaran (Anthropic Labs)

AI Agentic 工作流

最近产品里想要集成业务 Agent,真动手之前我想先把底层逻辑搞清楚,但是深入了解 agent 之前我看到了:Agentic

它跟 Agent 不是一回事。Agent 是具体的东西,比如一个能自主干活的 AI 助手;Agentic 是一种工作方式——让 AI 能规划、执行、反思、修正,在循环里反复迭代,而不是一问一答就结束。搞懂 Agentic,才知道 Agent 该怎么搭。

说白了,Agentic 不是让 AI 更会说话,而是让 AI 更像一个会反复干活的人。

这篇是我自己的学习整理,不聊实现细节,就想先把这件事用人话讲透。


一、先想明白,我们平时到底是怎么用 AI 的

吴恩达先生以前讲过一个很形象的比喻[^1]。

他说我们现在大部分人用大模型,其实还是 Zero-shot(就是不给任何示例、不做任何拆解,直接让它一把回答)模式。你扔一个 prompt 进去,模型从第一句一路吐到最后一句,中途没有停顿,没有修改,也没有回头看一眼自己写得对不对。

这件事像什么?

像你让一个人从头到尾写一篇文章,但中途不准按退格键。

你想想,这活儿正常人都干不好,更别说我们还指望 AI 一把出神作。

这也是为什么,我现在回头看,会觉得很多时候不是 AI 本身忽强忽弱,而是我们给它的工作方式,本来就有点反人类。
不是它不能干,而是你要求它一次性把所有事情都想对、写对、做对。

但真实世界里的高手不是这么干活的。

不管是写文章、做方案、查资料,还是写代码,正常流程基本都是:

  1. 先想一下目标是什么
  2. 拆几个步骤出来
  3. 缺信息就去查
  4. 先搞一个初稿
  5. 回头检查漏洞
  6. 发现问题再改一轮

这套动作,我们人类觉得理所当然;一到 AI 身上,很多人反而想跳过。

所以我现在理解的 Agentic,本质上就是把这套”人类本来就会的迭代过程”,重新装回 AI 的工作流里。

用一张图来看,差别其实一目了然:

左边就是我们现在大部分人用 AI 的方式:一问一答,结束。
右边是 Agentic 的方式:它会自己转圈,直到结果靠谱为止。


二、Agentic 到底是什么?这是我目前最顺的一句解释

如果非要让我先给自己留一句最顺口的人话解释,我会这么说:

Agentic 不是让 AI 更聪明,而是让 AI 不再只回答一次。

它会在一个循环里反复工作。

比如你让它写一份研究报告,它不是上来就闷头输出 3000 字,而是可能先做这些事:

  • 先列提纲,确认应该从哪几个角度展开
  • 判断哪些部分信息不够,需要联网搜索或者调资料
  • 先写一个版本
  • 再自己检查这一版有没有逻辑漏洞、重复废话或者证据不足
  • 然后按发现的问题继续改

你看,这不就是一个稍微靠谱点的人在做事的样子吗?

这里最关键的,不是”会不会调用工具”,也不是”会不会写代码”,而是它终于不再被限制在”一次生成、不可回头”的模式里了。

这件事带来的提升,其实很夸张。

吴恩达先生他们测过一组数据,挺有意思的:

GPT-3.5 裸跑代码题只有 48% 正确率,但套上 Agentic 循环以后,直接拉到 95%。比 GPT-4 裸跑的 67% 还高一大截。(数据来自吴恩达 2024 年在 Sequoia AI Ascent 的演讲 Agentic Design Patterns[^1])

我觉得这组数字最值得咂摸的地方,不是某个模型赢了,而是它说明了一件事:

工作流设计,在很多场景下,已经开始比”模型单次裸跑能力”更重要了。

以前大家拼谁的 prompt 写得玄。
现在越来越像在拼,谁更懂得给 AI 安排一个像样的工作流程。


三、我先把 Agentic 拆成四个最核心的招式

吴恩达先生后来把 Agentic 总结成四种常见设计模式[^1]。我自己的感受是,这四个词看起来有点学术,但翻译成人话之后,反而会一下子清楚很多。

先上一张全景图,让你对这四个模式的关系有个整体感觉:


graph TB
    MA[多智能体协作
把任务分给不同角色] --> PL[规划
拆任务、定顺序] PL --> EX[执行 + 工具使用
干活、调工具] EX --> RF[反思
回头检查、修正] RF -->|发现问题| PL RF -->|通过| OUT[输出结果] style MA fill:#e6f3ff style PL fill:#fff3e6 style EX fill:#e6ffe6 style RF fill:#ffe6e6

简单说就是:多个角色分工(多智能体) → 每个角色先想清楚再动手(规划) → 该查就查、该调就调(工具使用) → 做完回头看一遍(反思) → 不行就再来一轮。

1. 反思(Reflection)

一句话类比:写完作文自己通读一遍,删掉废话、补上漏洞。

就是让 AI 不要交卷就跑,而是看一眼自己刚刚写了什么。

有没有逻辑不通的地方?
有没有明显没回答到题?
有没有一堆正确但没用的废话?

这一步特别像我们自己写完文章后,隔两分钟再回来看,突然发现:

“这段说了等于没说。”
“这个论点站不住。”
“这里应该删,不然啰嗦。”

很多看起来”更聪明”的 AI 应用,往往不是第一次就答得特别神,而是多了一轮自我审稿。

2. 工具使用(Tool Use)

一句话类比:查字典、翻参考书,而不是全靠记忆硬撑。

AI 靠自己脑补,肯定有上限。
所以你得允许它出门干活。

比如:

  • 去搜资料
  • 读文件
  • 跑代码
  • 查数据库
  • 调 API
  • 调用外部系统

工具这件事,本质上是在补模型的”手脚”。
只有脑子,没有手脚,它很多事永远只能停留在嘴上。

3. 规划(Planning)

一句话类比:做菜之前先看一遍菜谱,别上来就开火。

规划说白了就是,别一股脑往前冲,先想一下顺序。

面对复杂任务,先拆,再干,效率会高很多。比如:

  • 先定目标
  • 再拆子任务
  • 然后决定哪些任务要查资料,哪些任务可以直接写
  • 最后再汇总

人一旦不规划,容易瞎忙。
AI 一旦不规划,容易一本正经地跑偏。

4. 多智能体协作(Multi-agent Collaboration)

一句话类比:一个人拍电影不如分工——导演、编剧、摄影各管各的。

这个词听起来很高级,实际你可以把它理解成”别让一个 AI 同时扮演所有角色”。

比如一个负责搜集资料,一个负责写初稿,一个负责挑刺,一个负责做最终汇总。
甚至它们之间还可以互相辩论。

这套思路的价值,不在于搞得多复杂,而在于:
不同角色的关注点本来就不一样,拆开之后,结果往往比一个 AI 一把梭更稳。


四、从会跑到跑得稳,还差这两样东西:Harness 和 Traces

上面四个招式看起来挺简单的对吧?但一旦真让 AI 跑起来,你马上会碰到两个问题:

  1. 它跑偏了你怎么兜住?
  2. 它跑偏了你怎么知道?

第一个问题,对应的是 Harness;第二个问题,对应的是 Traces

Harness:给 AI 装上安全带和工具腰带

如果我只把 Agentic 理解成”让 AI 多跑几轮”,那其实只理解了一半。

LangChain 的 Harrison Chase 提过一个我很认同的点:今天很多 Agentic 系统之所以变强,不只是因为模型本身更好了,而是因为我们给它加了一套越来越完整的”外围设施”。

他管这个叫 Harness。说白了,就是你在让 AI 干活之前,先给它搭好的一整套工作台——什么工具能用、什么文件能动、上下文怎么喂、出了问题怎么回滚。

举个具体的例子:你让 AI 帮你写一段代码。如果是裸跑,它只能凭记忆硬编。但如果有了 Harness,它可以先读你项目里的现有代码(文件系统权限),记住你之前说过的偏好(记忆机制),按步骤拆解任务(规划能力),跑完代码自动测试(验证机制),测试没过就自动回退(回滚机制)。

这时候你就能理解,为什么现在很多团队开始讲 Context Engineering(上下文工程),而不只是讲 Prompt Engineering 了。

打个比方:如果 Prompt 是你跟 AI 说的那句话,那 Context Engineering 就是你在它开口之前,帮它把该看的资料、该知道的规则、该用的工具全部摆到桌面上。

这俩压根不是一个量级的事。

以前大家总想找一句神 prompt 包打天下。
现在越来越像搭一个工作台,把 AI 放进去之后,它每一步都有人给它递对工具、递对材料、递对约束。

Agentic 拼到后面,拼的不是一句提示词写得多花,而是谁更会给 AI 配环境、配反馈、配护栏。

Traces:给 AI 装上飞行记录仪

好,Harness 解决了”怎么兜住”。但还有个问题:AI 跑偏了,你怎么知道它是在哪一步开始偏的?

传统软件有个好处——代码基本就是规则本身,程序为什么这么跑,你顺着代码就能看明白。

但 Agentic 不一样。它不是一条固定路径跑到底,而是在循环里不断判断、选择、调用工具、修正方向。你第 14 步看到它翻车的时候,真正的问题很可能出在第 3 步,甚至第 1 步。

这时候最有价值的东西就不是”它最终输出了什么”,而是它每一步的操作日志——每一步看到了什么上下文、为什么做出这个判断、调了哪些工具、工具返回了什么、它是基于什么决定继续还是回退。

这些记录,就是 Traces(追踪日志)。

它特别像什么?像飞行记录仪。

平时你可能不看,但一旦出事,没有它你基本就是瞎猜。

所以我现在会提醒自己,未来如果真要在业务里落地 Agentic,不能只盯着”能不能跑通”。没有 trace 的系统,很多时候只是看起来能跑,一出问题你根本没法定位。


五、对我这种准备落业务的人来说,更现实的姿势不是全自动,而是”人类在环”

上图展示的是一个理想的自主智能体循环:任务执行 → 任务创建 → 优先级排序 → 继续执行,理论上可以无限循环直到目标达成。

但看到这里,我自己先记一个结论:

小白入门 Agentic,千万别一上来就追求 100% 自动驾驶。

这事听起来很性感,做起来经常很危险。

因为长链路任务一旦完全放飞,AI 很容易在你没注意的时候,往一个看似合理、其实已经偏掉的方向一路狂奔。

所以比”全自动”更现实的做法,是先把它当成一个很强的起草机、陪练和执行器。

比如:

  • 帮你先起一个方案初稿
  • 帮你做一版调研汇总
  • 帮你把一堆需求整理成结构化任务
  • 帮你先生成代码骨架和测试思路

然后关键决策点交给人来审。

我很认同一种说法,叫 Keep the AI on the leash
翻译得粗暴点,就是:别把绳子撒手。

你要给它一定的自主性,但这个自主性必须可调。

它能跑,但你随时能:

  • 看见它干了什么
  • 拒绝它的修改
  • 编辑它的中间结果
  • 在关键节点让它停下来等你确认

这就是所谓的 Human-in-the-loop(人类在关键节点把关)。

它听起来好像”不够自动化”,但对真实业务来说,这反而是更成熟的状态。
因为信任不是靠宣传建立的,是靠”我看得见、控得住、改得回”建立的。


六、如果我后面真要往业务里落,起步大概率会先套这个最小闭环

说了这么多,最后落到实操,我觉得最适合我这种还在往业务里试的人,Agentic 起步姿势就先四步:


graph LR
    P[1. 规划
拆任务、定目标] --> E[2. 执行
搜资料、调工具] E --> R[3. 自我反思
挑刺、补漏] R --> H[4. 人类审核
关键节点拍板] H -->|需要修正| P H -->|通过| D[交付] style P fill:#fff3e6 style E fill:#e6ffe6 style R fill:#ffe6e6 style H fill:#e6f3ff

1. 规划

先别急着让 AI 干活,先让它拆任务。

问清楚目标是什么,输出物是什么,哪些约束不能碰,哪些信息还缺。

2. 执行

该搜资料就搜,该读文件就读,该调工具就调。
这一步别只让它靠记忆硬编。

3. 自我反思

让它自己回头挑刺。
检查逻辑、补证据、删废话、找风险点。

4. 人类审核

关键节点必须有人拍板。
尤其是涉及代码变更、对外发布、客户沟通、业务决策的内容。

这四步看起来很朴素,但已经足够构成一个靠谱的 Agentic 最小闭环。

它真正打碎的,其实也是我自己之前对 AI 的一个幻觉:

不是”有没有一个神 prompt 能一次性解决问题”,而是”我能不能把问题组织成一个能不断修正的流程”。

这两个思路,差别非常大。


七、最后记一笔

如果我现在还把 AI 只当成一个高级搜索框,或者一个会写字的聊天机器人,那我大概率还没真正进入 Agentic 这套工作方式。

Agentic 的重点,从来不是”让 AI 更像人聊天”,而是”让 AI 更像一个可以在工作流里持续推进任务的执行者”。

它会规划,会调用工具,会回头反思,会在必要的时候把决定权交还给人。

我越来越觉得,接下来 AI 应用真正拉开差距的地方,不是大家都在拼模型参数,而是谁先把这套工作方式吃透。

毕竟现实里真正靠谱的人,也不是因为他永远第一次就做对。
而是因为他会:

  • 先想清楚
  • 再动手
  • 做完回头看
  • 发现不对就改

现在,只不过我们开始要求 AI 也这么干了。


参考与延伸阅读

[^1]: AI Agents that Work: Agentic Design Patterns - Andrew Ng, DeepLearning.AI

延伸阅读: