0%

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 不只是个更好用的聊天框,它正在改变用户的角色——从操作者变成监督者。”

—— Jakob Nielsen

最近读到 Nielsen 大神的一篇深度长文,讲的是 AI 时代 UX 设计范式的根本性转变。说实话,读完有种醍醐灌顶的感觉。这篇文章信息量很大,但我最想聊的是他提出的那个核心观点:我们正在经历 60 年来第一次重大的人机交互范式转移

从敲命令到说意图

先回忆一下人机交互的历史。最开始是批处理时代,你把一整批任务卡片交给计算机,过几个小时回来取结果。后来到了命令行时代,你和计算机交替回合——你敲个命令,它执行一步。再到 GUI 时代,点击图标本质上还是一种可视化的命令。

Nielsen 把这叫做命令式交互(Command-based Interaction)——你得一步步告诉计算机怎么做

而 AI 带来的意图式交互(Intent-based Interaction)完全不同。你告诉系统想要什么结果,它自己琢磨怎么搞定。就像维京海盗的领主说”去英格兰修道院搞点银子来”,他不需要交代”先做盾牌、再划船、再登陆”这些细节——手下知道该怎么做。

这个转变的本质是控制权的转移。以前人类是操作员,每一步都要亲自上;现在人类变成了监督者,看着 AI 干活,时不时干预一下。

🤖 AI 改变了人机关系的本质

UX 设计目标的三个阶段

文章里 Nielsen 把 UX 设计史分了三个阶段,我觉得这个划分特别准:

时代 时间段 目标 代表
商用计算时代 1960-1995 生产力 财务软件、文字处理
互联网时代 1995-2025 影响力 电商、社交媒体
AI 时代 2026 起 增强人类存在 AI Agent

前两个阶段好理解。商用计算时代,UX 的目标就是让你干活更快、犯错更少。互联网时代变成了怎么让你多买东西、多看广告、多停留——这就是为什么说”如果你不为产品付费,那你就是产品”。

但 AI 时代的 UX 目标变了。不再是单纯提升效率或者操纵行为,而是增强人的能力——不是让你更快地完成已知任务,而是帮你探索未知的可能性,释放想象力和创造力。

我觉得 Nielsen 引用 Douglas Engelbart 的话特别到位:最初的愿景是”增强人类智能”,现在这个愿景要扩展到”增强人类存在”——不只是思考能力,还有决策、想象、协作。

表达障碍:当前 AI 界面的最大问题

Nielsen 指出了一个很现实的痛点,我深有同感——表达障碍(Articulation Barrier)

现在的 AI 界面基本都是聊天框,要求你把需求写成自然语言。但问题是,写作比阅读难得多。研究表明,美国、德国这些发达国家的成年人里,大约一半属于”低读写能力”人群。更别说在发展中国家了。

这就解释了为什么”提示工程(Prompt Engineering)”这么火。如果你得学一堆技巧才能让 AI 给出好结果,那说明界面本身就存在严重的可用性问题。

好的设计应该让用户不需要成为专家就能用好产品。

Nielsen 提了几个解决思路,我觉得很实用:

  • 提示增强(Prompt Augmentation):比如风格画廊,让用户从选项里选,而不是自己描述
  • 辅助提示理解(Aided Prompt Understanding):帮用户理解和完善意图
  • 用户模型:让 AI 记住你的偏好、约束、习惯,不用每次都重新交代
💡 记忆是 AI 时代 UX 的一等公民

重新定义可用性指标

这个部分让我印象很深。Nielsen 说他经典的十大启发式评估准则要重新解释了,因为底层逻辑变了。

他列了几个关键的指标转变:

旧指标 新指标 含义变化
可发现性(Discoverability) 意图捕获(Intent Capture) 系统能否准确理解模糊的自然语言请求
错误预防(Error Prevention) 澄清质量(Clarification Quality) 系统如何处理歧义,问对跟进问题
学习时间(Time to Learn) 委托便捷度(Ease of Delegation) 用户能否放心地把多步骤任务交给 AI
执行效率(Execution Efficiency) 验证效率(Verification Efficiency) 用户多快能确认 AI 的输出符合预期

最核心的转变是:执行的认知负荷降低了,但验证的认知负荷成了瓶颈。

以前你一步步操作,知道每一步干了什么;现在 AI 一下子给你个结果,你得判断对不对、好不好、能不能用。这个”可验证性(Evaluability)”成了新的可用性关键。

三层设计模型

Nielsen 提出了一个三层架构,我觉得这是整篇文章最实操的部分:

第一层:意图表面(Intent Surface)

这是用户表达需求的入口。不能太依赖纯文本输入,得支持语音、图像、上下文感知,甚至隐式意图推断——通过你的日历、屏幕内容、鼠标停留位置来推测你可能想要什么。

第二层:编排表面(Orchestration Surface)

这是最关键的一层,也是目前最被忽视的。在 AI 执行高风险操作前,它得:

  • 展示它理解的意图是什么
  • 透露数据来源和推理过程
  • 请求明确的许可
  • 显示谁会被影响、哪些政策约束适用

Nielsen 特别强调了协作意图——在企业环境里,AI 得处理多个利益相关者、多个 AI 子代理之间的冲突,协商出共识才能执行。

第三层:直接操作表面(Direct Manipulation Surface)

传统的 GUI 不会消失,但角色变了。它不再是工作的起点,而是检查、协商、纠正的地方。你可以拖拽任务优先级、调整时间线、修改参数——但这些都是对 AI 生成的方案进行微调,而不是从零开始构建。

🎛️ 直接操作迁移到了更高的抽象层级

有意的认知摩擦

这部分挺反直觉的。Nielsen 说,对于高风险任务,UX 设计师应该故意增加摩擦

不是那种愚蠢的”多点几下”式的摩擦,而是 strategically placed 的认知摩擦

  • 渐进式授权:AI 一开始只能草稿,积累信任后才能执行低风险操作,最后才能碰高风险任务
  • 颗粒化授权:不要”全部批准”按钮,要逐项确认
  • 人工延迟:比如转账前来个 3 秒倒计时
  • 来源高亮:显示数据从哪来,置信度如何

自主性应该被逐步赢得,而不是一次性授予。

这让我想到,现在的 AI 界面往往追求极致的”零摩擦”,结果导致用户盲目相信 AI 的输出——Nielsen 称之为”可信度陷阱(Plausibility Trap)”。界面太干净、输出太流畅,反而让人失去批判性思考。

好的 AI UX 应该诚实展示不确定性,把人类的注意力引导到真正需要判断的地方。

慢 AI:批处理的回归

这个观察很有意思。简单聊天查询几秒钟搞定,但 Deep Research、视频生成这些强大的 AI 工具可能要跑 10 分钟到几小时。未来 AI 代理可能独立运行 30 小时甚至几天来协调复杂任务。

这就像是批处理时代的回归,但用户体验得跟上。Nielsen 提了几个设计要点:

  1. 澄清和运行契约:长任务开始前必须问清楚,给出时间窗口、成本上限、完成标准、硬边界
  2. 概念面包屑:不要用无意义的进度条,要给阶段性结论的摘要,让用户能及早发现 AI 走偏了
  3. 上下文重新载入:用户会忘记最初的需求,系统得友好地提醒
  4. 分层通知:紧急阻塞事项立即推送,质量相关决策发邮件,完成通知批量摘要
  5. 渐进式披露和可回收价值:展示中间结果让用户能及早调整;如果用户终止任务,要显示哪些中间产物还能用
⏱️ 时间投入会放大沉没成本谬误

未来:通过发现确定意图

文章最后聊的是更遥远的愿景。当 AI 能在一分钟内生成一千个合格的方案时,用户的核心需求不再是生产,而是发现

Nielsen 把这个叫做**”通过发现确定意图(Intent by Discovery)”**。不要假设用户知道自己想要什么,要帮他们在探索中逐步认清自己的需求。

他预测了几种可能的交互模式:

1. 潜在空间的视觉导航

不再是一次性抽奖式的提示,而是交互式 2D 地图。拖动光标在不同方向移动,实时看到输出如何变化,停在”感觉对了”的地方。

2. 直接对象操作(混合 GUI 和 AI)

你拖动网页 mockup 里的英雄图片放大,AI 不只会记录坐标变化,而是推断出”用户重视视觉冲击力和开放空间”,自动调整字体、光线、次要元素来保持整体协调。

3. 苏格拉底式支架

AI 不再是被动的命令接受者,而是主动的诊断者。用户说”我需要产品发布的策略”,AI 不急着生成 10 页文档,而是反问:”我们是优化短期收入还是长期品牌认知?”

4. 临时生成式界面

界面不再固定,而是根据用户当前的探索情境动态生成特定的控制项,任务完成后这些控件消失。

5. 策展作为意图

像 Midjourney 的情绪板那样,用户可以丢一堆乱七八糟的参考资料——竞品报告、照片配色、语音备忘——AI 整理、找到概念重叠、合成起点。用户通过看 AI 怎么连接这些碎片来发现自己的意图。

6. 减法雕刻

不再是”从空白开始添加”,而是”从过载开始删减”。AI 生成一个极尽详细、复杂、busy 的版本,用户通过删除、划掉、削减来得到想要的结果。编辑总是比生成容易。

UX 设计师的新角色

文章结尾 Nielsen 给了 UX 从业者一些建议。我觉得这个视角转换很重要:

从设计线性用户流(A 页面 → B 页面 → C 页面),到架构可能性空间。

设计师要设计的是:

  • 边界约束
  • 潜在空间的”物理规则”
  • 生成环境的反馈循环

UX 设计必须从移除预设路径上的摩擦,转变为扩展可行路径的范围,打开我们尚未想象的可能性。

但也要警惕零学习陷阱。如果用户永远不需要理解系统如何工作、不需要做决策、不需要思考,他们会陷入”认知萎缩循环”。好的 AI UX 应该教得”刚刚好”——足够让用户保持数字判断力,不至于变成数字生活的乘客。

⚠️ 设计应该是认知外骨骼,而不是认知轮椅

我的几点感想

读完这篇文章,我最大的感受是:我们正在见证一个时代的交替

过去 60 年,人机交互的基本假设是”人类操作,计算机执行”。这个范式产生了无数精妙的设计——菜单层级、快捷方式、撤销重做、直接操作……这些都是为了解决”如何让计算机听话”的问题。

但 AI 把问题翻转了。现在的问题是”如何让计算机理解”、”如何让计算机主动”、”如何让计算机在不确定性中与人协作”。这需要我们重新发明一整套设计原则和评估方法。

Prompt Engineering 的流行其实是一种症状——说明当前界面没有解决好意图表达的问题。真正的解决方案不是让用户学会写更好的提示,而是让系统更擅长理解不完美的表达。

另外,我对 Nielsen 说的”认知摩擦”很有共鸣。现在的 AI 产品太追求”丝滑”了,结果用户变成了盲目相信的傀儡。有时候故意慢下来、要求确认、展示不确定性,反而是更好的设计。

最后,关于 UX 设计师的角色转变——我觉得这既是挑战也是机遇。挑战在于要学的全新技能很多,从概率思维到多代理系统;机遇在于我们终于有机会跳出”画线框图”的框架,去设计更本质的东西:人与智能系统如何协作。

行动清单

如果你正在设计 AI 界面,Nielsen 给了几个重点方向:

  1. 测量意图捕获,而非点击效率——评估系统多准确地推断用户目标
  2. 设计编排层——意图和行动的协商表面是建立或失去信任的地方
  3. 策略性地设计摩擦——按风险等级给任务分类,高风险任务故意放慢
  4. 从一开始就规划慢任务——运行契约、概念面包屑、可回收价值披露不是边缘情况
  5. 抵制零学习陷阱——让用户保持对 AI 正在做什么、为什么做的认知参与

命令式范式辉煌地服务了我们 60 年。但现在世界在我们脚下转变,UX 专业必须随之转变——不是抛弃我们知道的,而是认识到可用性的定义本身正在被重写

委托而不投降——设计这种微妙关系是下一个十年最大的 UX 挑战。


参考来源:


本文基于 Nielsen 的深度文章整理并结合个人观点,如有理解偏差欢迎指正讨论。

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 ReAct Agent

前言

⚠️ 时间线说明: 这篇文章实际上是在《Agent 生产落地:从 demo 到产品的血泪经验》之前完成的,一直躺在草稿箱里忘了发。阅读顺序上,建议先看这篇理解 ReAct 原理,再看那篇的生产实践经验。

想象一下你现在要煮盆饺子。烧水、打料内心独白支配着每一个动作:”水开了,我该下饺子了。”当发现缺少关键调料,内心独白会调整策略:”没辣椒了?我用老干妈代替。”面对技术难点时,这个思维过程会触发外部行动:”我不确定需要煮多久,我需要打开美食APP查下。”

这种边想边做、边做边调整的能力,是人类解决问题的基本方式。但传统的大语言模型(LLM)完全没有这种能力——它们更像一个闭卷考试的学生,只能凭记忆一口气写到底,错了也没法改。

在人工智能领域,传统的大语言模型(LLM)一直像静态黑盒一样运作。虽然这些模型擅长语言模仿,但它们缺乏功能性的”工作记忆”和与世界动态交互的能力。这种隔离滋生了两种系统性失败:“幻觉循环”,即模型自信地编造事实;以及“错误传播”,即单一的逻辑失误会毁掉整个回答。

ReAct(Reason + Act)框架代表了 AI 架构的范式转变。 通过在孤立认知与外部执行之间搭建结构性桥梁,ReAct 将 LLM 从对话者转变为能够规划、执行和自我纠正的自主 Agent。

这篇是我学习 ReAct 的笔记,想把它讲清楚,也给自己留个档。


一、ReAct 到底在解决什么问题

传统的 AI 能力其实是分裂的。要么你用 Chain-of-Thought(CoT)让模型在脑子里一步步推理,要么你用 Action-generation 让模型调用外部工具——但两者各自有坑:

纯推理(CoT)的问题: 模型完全依赖内部参数知识,是个封闭系统。它没法查资料,没法更新自己的认知,结果就是自信满满地编事实(幻觉),而且一步错步步错(错误传播)。

纯行动(Act-only)的问题: 模型可以调用工具,但缺乏显式的长期规划能力。它没法跟踪子目标,也没法系统地探索环境,很容易陷入重复或无意义的操作。

ReAct 的做法是:把两者缝合起来。 推理痕迹帮助模型诱导、跟踪和更新行动计划,处理异常情况;而行动让模型能够对接外部信息源(Wikipedia API、浏览器、数据库)来获取实时信息。

核心机制:反馈循环

ReAct 把 Agent 的活动结构化成一个三段式交替序列:

1
Thought(思考)→ Action(行动)→ Observation(观察)→ Thought(再思考)...
  1. Thought: LLM 用思维链把大任务拆成可管理的小任务,分析数据,决定下一步做什么
  2. Action: 模型输出使用某个预定义工具的意图(比如搜索数据库)。注意:LLM 自己不执行工具,只输出工具名和参数
  3. Observation: 外部系统执行工具,把结果反馈给 LLM

Agent 基于这个观察重新评估进度,生成下一个 Thought。循环往复,直到输出最终答案。

这个循环看起来直白,但效果非常显著。在 HotpotQA 基准测试里,传统 CoT 的幻觉率(编瞎话的概率)是 14%,上了 ReAct 之后直接降到 6%[^1]。

关键洞察:ReAct 不是让模型"更聪明",而是给它一套"查资料→推理→再查资料"的机制,让它有机会认错和修正。

二、五个核心要点:为什么 ReAct 有效

要点 1:推理与行动的协同

ReAct 范式打破了传统上推理与执行的二分。其核心是形式化表达 Â = A ∪ L。在这个等式中,Agent 的行动空间从物理或程序步骤(A)扩展到包含内部的”语言空间”(L)。通过将”思考”视为模型轨迹中的另一种行动类型,ReAct 允许 Agent 生成口头推理痕迹——或称”想法”——与特定任务的动作交错进行。

这种协同使模型能够诱导、跟踪和更新计划,同时实时处理异常。正如 Yao 等人在其奠基性研究中所言:

"这种'行动'与'推理'之间的紧密协同使人类能够快速学习新任务,并在前所未有的情况下进行稳健的决策或推理。"

通过交错这些痕迹,模型使用推理来决定检索什么信息,而由此产生的观察又为后续推理提供基础。

要点 2:通过观察环节打破幻觉循环

传统的思维链(Chain-of-Thought, CoT)提示鼓励模型”一步步思考”,但它完全依赖模型的内部参数知识——一个容易”幻觉”不存在事实的封闭系统。ReAct 通过引入强制的 Observation(观察) 步骤来解决这个问题,将模型锚定在外部现实中。

在知识密集型任务中,ReAct 使用特定的动作词汇——包括 search[entity]lookup[string]finish[answer]——与 Wikipedia 等外部实体交互。这种迭代的 Thought-Action-Observation 循环迫使模型在继续之前根据真实知识库验证其逻辑。结果是统计学上显著的改进:在 HotpotQA 基准测试中,误报率(幻觉事实或逻辑的频率)从标准 CoT 的 14% 暴跌至 ReAct 框架下的 6%[^1]。这种锚定将 Agent 从创意作家转变为可信的调查者。

要点 3:代理技能的效率悖论

ReAct 扩展分析中最深刻的见解之一是:“代理技能”通常比原始模型大小更具参数效率。 我们在”小模型悖论”中看到了突破:教模型如何推理和行动,比强迫它在权重中记忆海量事实更具泛化能力。

数据揭示了一个惊人的层级关系:一个较小的 PaLM-8B 模型,仅在 3,000 条 ReAct 轨迹上微调,就能胜过完全依赖提示的巨大 PaLM-540B 模型。更令人印象深刻的是,微调的 62B 模型超过了 540B 提示版本的性能[^1]。这表明 AI 的未来不在于构建越来越大的记忆数据仓库,而在于提炼模型导航工具集和环境的程序能力。

模型 方式 表现
PaLM-540B Zero-shot 提示 基准线
PaLM-8B ReAct 微调(3,000 轨迹) 超越 540B
PaLM-62B ReAct 微调 大幅超越 540B

数据来源:Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models[^1]

要点 4:可解释性:终于能看懂 AI 在想什么了

传统模型的黑盒问题一直让人头疼。输入一个问题,输出一个答案,中间发生了什么?不知道。

ReAct 的”草稿本(scratchpad)”机制解决了这个问题。每一轮循环的 Thought、Action、Observation 都被记录下来,形成一条完整的”思维链”。

如果出错了,你能精确定位到是哪一步出的问题:

  • 推理错误:模型陷入了重复循环,比如一直问同一个问题
  • 检索错误:外部 API 返回了不相关的信息
  • 行动错误:调用了错误的工具或参数

更有意思的是,这种透明性支持人在回路(Human-in-the-loop)的干预。这有点像你带新人:他在那自言自语分析问题,你说”这一步想偏了”,他马上就能纠正。

ReAct 通过其人类可读的”草稿本”,为 AI 的”黑盒”提供了一个罕见的窗口。Agent 过程的每一步都被记录为想法、行动和观察的序列,提供了以前不可能的诊断水平。

如果 Agent 失败,开发者可以精确定位确切的失败模式——比如模型陷入重复循环的”推理错误”,或外部 API 返回无信息结果的”搜索错误”。这种透明性还促进了“人在回路(Human-in-the-loop)”的纠正。在 ALFWorld 环境(一个基于文本的任务模拟器)中,研究人员证明,人类可以通过简单编辑草稿本上的”想法”来引导失败的 Agent 回到正轨——删除幻觉或添加提示——使 AI 能够立即重新校准其计划[^1]。

示例:ReAct 轨迹

1
2
3
4
5
Thought: 用户询问 2024 年诺贝尔物理学奖得主,我需要确认最新获奖信息。
Action: search[2024 Nobel Prize in Physics winner]
Observation: John J. Hopfield and Geoffrey Hinton were awarded...
Thought: 好的,我已获得准确信息,现在可以回答用户了。
Action: finish[约翰·霍普菲尔德和杰弗里·辛顿]

要点 5:复杂环境的动态规划

虽然标准 LLM 在多步骤任务上挣扎,但 ReAct 在 ALFWorld(合成家庭任务)和 WebShop(拥有 118 万产品的在线购物环境)等交互式环境中表现出色。与遵循 rigid 脚本的”仅行动(Act-only)”Agent 不同,ReAct Agent 利用动态推理将高级目标分解为可管理的子目标。

所以ReAct 的真正威力,在处理多步骤、需要探索的任务时才会显现出来。

ALFWorld 里有个典型任务:”把一个发烫的平底锅放到冷却架上”。这看起来简单,但模型需要:

  • 找到平底锅(可能在灶台上)
  • 判断它是不是”发烫”的状态
  • 找到冷却架(可能在橱柜里)
  • 规划一条合理的移动路径

传统”只行动不思考”的 Agent(Act-only)在这种任务上成功率只有 45%,而 ReAct 能达到 71%[^1]。

ReAct 利用预训练的”常识知识”——例如,知道胡椒罐更可能在橱柜里而不是冰箱里——以手术般的效率探索环境。通过维护其进度的内部记录,ReAct Agent 避免了困扰纯反应式系统的无产出循环。


三、如何构建一个 ReAct Agent

你可以用 LangChain、LangGraph 或 BeeAI 等开源框架快速搭建(比如 LangChain 的 ZERO_SHOT_REACT_DESCRIPTION),也可以从零用纯 Python 手写。下面是手写版本的逻辑拆解:

步骤 1:定义工具集

首先确定模型能用的具体行动。写 Python 函数(计算器、搜索等),然后把它们的签名和描述提取成 LLM 能理解的格式(JSON 或字符串)。

1
2
3
4
5
6
7
8
9
10
11
# 示例工具定义
tools = {
"search": {
"description": "搜索 Wikipedia 获取实体信息",
"parameters": {"entity": "要搜索的关键词"}
},
"calculator": {
"description": "执行数学计算",
"parameters": {"expression": "数学表达式"}
}
}

步骤 2:编写 ReAct 提示词

ReAct Agent 极度依赖特定的提示技巧来结构化输出。系统提示必须:

  • 引导模型一步步思考,交错想法和行动
  • 提供可用工具列表
  • 定义严格的输出格式,通常用标签如 Thought:Action:Action Input:Observation:

提示词模板示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
你是一个能够使用工具的 AI 助手。按以下格式解决问题:

Thought: 分析当前情况,决定下一步行动
Action: 要使用的工具名称(从 [{tool_names}] 中选择)
Action Input: 传递给工具的参数
Observation: 工具执行结果
...(重复 Thought/Action/Observation 直到得出结论)
Thought: 我现在知道最终答案了
Final Answer: 用户的最终答案

可用工具:
{tools}

开始:
Question: {input}

步骤 3:构建执行循环

把提示词和用户问题传给 LLM 后,你需要一个程序循环来处理执行:

  1. 解析输出: 拦截 LLM 的文本输出,提取 Action(工具名)和 Action Input(参数)
  2. 执行工具: 你的 Python 代码调用对应的函数
  3. 返回观察: 把函数结果格式化为 Observation,追加到对话历史
  4. 重复: 把更新后的上下文传回 LLM,让它基于新数据生成下一个 Thought
  5. 终止: 当 LLM 确定有最终答案并使用 “Finish” 动作或输出最终响应标签时,循环结束

伪代码示意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
max_iterations = 10
history = prompt_template.format(input=user_question)

for i in range(max_iterations):
response = llm.generate(history)

# 解析 Thought/Action/Observation
thought = extract_thought(response)
action = extract_action(response)
action_input = extract_action_input(response)

if action == "Finish":
return action_input # 最终答案

# 执行工具
observation = execute_tool(action, action_input)

# 追加到历史
history += f"\nThought: {thought}\nAction: {action}\nAction Input: {action_input}\nObservation: {observation}"

四、生产环境的优化技巧

要让 ReAct Agent 稳定、高效地运行,避免常见陷阱,你需要以下优化:

1. 使用少样本示例(Few-Shot Exemplars)

虽然可以构建”零样本”ReAct Agent,但性能会在你加入人工编写的完整 Thought-Action-Observation 轨迹示例后显著提升。提供 3 到 6 个 高质量、任务特定的示例,能教会模型如何格式化想法、分解任务、处理不同工具。

技巧:示例要覆盖成功路径和典型的错误恢复场景,让模型学会"碰壁后怎么绕路"。

2. 与 Chain-of-Thought 和自一致性结合

研究表明,ReAct 高度依赖搜索结果质量;如果工具返回无用数据,模型的推理会跑偏。反过来,标准 CoT 受困于幻觉。

最佳实践是构建混合系统:

  • 如果 ReAct Agent 在给定步数内无法返回答案,回退到内部 CoT 推理(使用 Self-Consistency,即采样多条 CoT 路径并取多数答案)
  • 如果 CoT-SC 置信度低,切换到 ReAct 收集外部事实

这种”双保险”策略在知识密集型任务上效果最好。

3. 设置严格的循环限制

因为框架本质上是反馈循环,ReAct Agent 有时会陷入重复生成相同想法和行动的困境,导致高延迟和 token 成本爆炸。你必须在代码中设置最大迭代次数或特定结束条件(如置信度阈值)来防止无限循环。

1
2
3
4
5
6
7
8
9
10
11
# 示例:多层防护
MAX_ITERATIONS = 10
MAX_COST = 0.5 # 美元
seen_actions = set()

for step in range(MAX_ITERATIONS):
action_signature = (action_name, action_input)
if action_signature in seen_actions:
# 检测到循环,触发终止或切换策略
break
seen_actions.add(action_signature)

4. 微调实现可扩展性

纯粹从上下文提示中学习复杂推理和行动是困难的,尤其是对较小语言模型。然而,在几千条正确的 ReAct 轨迹上微调较小模型(如 8B 参数模型),能让它们大幅超越仅依赖标准提示的更大模型(如 PaLM-540B)。

工程建议:

  • 收集生产环境中的高质量轨迹作为微调数据
  • 使用 LoRA 等高效微调技术降低计算成本
  • 小模型+ReAct 往往比大模型裸跑更省算力、更可控

5. 实现人在回路(Human-in-the-Loop)纠正

因为 ReAct 生成可读的、 verbalized 的推理痕迹,系统具有高度可解释性。你可以设计 Agent 允许人类检查其内心独白。如果模型产生了幻觉想法或走错了路,人类可以直接编辑生成的 Thought 文本。这能纠正模型的后续行为,引导它成功,无需重写代码或手动执行动作。

典型应用场景:

  • 客服 Agent 遇到边界案例时,人工客服介入修正推理方向
  • 代码生成 Agent 逻辑走偏时,程序员编辑 Thought 引导它回到正轨
  • 研究 Agent 检索到无关信息时,研究员删除幻觉并提供正确线索

五、写在最后

ReAct 给我的最大启发,不是技术细节,而是一种设计哲学的转变。

以前我们总想造一个”什么都知道”的 AI——把人类知识全塞进模型参数里。ReAct 走的是另一条路:承认模型不可能全知,给它一套”查资料+推理”的机制,让它像人一样边做边学

当前 AI 的最先进水平并不依赖单一框架,而是协同的切换方法。最有能力的系统将 ReAct 与 CoT-自一致性结合,对逻辑任务利用内部参数知识,当内部置信度低时切换到 ReAct 进行外部锚定。

随着我们从对话式聊天机器人过渡到能够浏览网页、使用软件工具和管理工作流的自主 Agent,Thought-Action-Observation 循环正在成为安全和可靠性的行业标准。我们不再只是构建会说话的模型;我们正在构建为了行动而思考的 Agent。


参考

[1]: ReAct Prompting - Prompting Guide
[2]: What is a ReAct Agent? - IBM
[3]: ReAct Paper (PDF) - arXiv
[4]: ReAct: Synergizing Reasoning and Acting in Language Models

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. 你的工具和提示词结构,是否清晰到让模型不用猜?

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