AI 编程术语小词典(中文大白话版)
原版:mattpocock/dictionary-of-ai-coding —— "AI coding jargon, explained in plain English." 这一版是它的中文改写版:术语换成中文语境里的叫法,例子换成国内团队日常会遇到的场景,讲法尽量做到"没接触过 AI 编程的人也能看懂"。
先说三句话
用 AI 写代码这件事,让人头大的地方其实就那么几个:术语听不懂、报错莫名其妙、账单对不上、同一个问题问两次结果还不一样。
但这些都不是玄学,每个都有干干净净的解释。你缺的不是技术,是词汇量。
这本小词典就干一件事:把 AI 编程里的黑话,翻译成人话。 大概一个下午能翻完,翻完之后你会发现——原来那些"玄学"全是有名字、有规律的东西,而且大部分问题根本不在模型身上,在你给它的东西身上。
怎么用这本词典
每个词条长这样:
- 一句话版本 —— 赶时间就只看这一句
- 展开讲 —— 它到底是什么、为什么会这样、这会带来什么后果
- 别这么说 —— 这个词最容易被用错的地方(原版叫 Avoid)
- 对话示例 —— 两个同事怎么用它聊天,帮你记住语境
术语第一次出现时会写成「中文名(English)」,之后用中文名。有些词中文圈还没统一叫法(比如 Harness、Agent),我会说明常见的几种译法。
术语速查表
第 1 章 · 模型本身
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 人工智能 | AI | 一个会移动的标签,不是具体技术 |
| 模型 | Model | 一堆参数,只会"接着往下猜" |
| 参数 | Parameters | 模型脑子里那些被调好的数字,训练完就冻住了 |
| 训练 | Training | 一次性、巨贵的"调参数"过程 |
| 推理 | Inference | 每次你问它,它跑一遍得出答案 |
| 思考档位 | Effort | 回答之前让它想多久的旋钮 |
| 词元 | Token | 模型读写的最小单位(不是"字"也不是"词") |
| 下一个词元预测 | Next-token prediction | 模型唯一会做的事 |
| 不确定性 | Non-determinism | 同样的问题,两次答案可能不一样 |
| 模型服务商 | Model provider | 真正跑模型的那家(Anthropic / OpenAI / 本地 Ollama) |
| 外壳 | Harness | 把模型变成"能干活的工具"的那一层软件 |
| 一次模型请求 | Model provider request | 外壳和模型之间的一次来回 |
| 输入词元 | Input tokens | 你塞给它的内容,按量计费 |
| 输出词元 | Output tokens | 它吐出来的内容,单价更贵 |
| 前缀缓存 | Prefix cache | 重复发送的历史,打折 |
| 缓存词元 | Cache tokens | 命中缓存、按折扣价算的那部分词元 |
第 2 章 · 会话、上下文窗口与回合
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 无状态 | Stateless | 不往前带任何信息 |
| 上下文 | Context | 它现在手上有的、跟任务相关的信息 |
| 上下文窗口 | Context window | 它一次能看到的所有内容,有上限 |
| 有状态 | Stateful | 会把信息往下带 |
| 智能体 | Agent | 你实际在对话的那个"东西" = 模型 + 外壳 |
| 系统提示词 | System prompt | 外壳每轮都塞在最前面的"岗位说明书" |
| 会话 | Session | 跟它聊的一次完整过程,聊完就没了 |
| 回合 | Turn | 你发一条消息 + 它干完所有活交还控制权 |
第 3 章 · 工具与环境
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 环境 | Environment | 它干活的世界(最常见就是你的项目目录) |
| 文件系统 | Filesystem | 它读写文件的那棵目录树 |
| 工具 | Tool | 外壳给它的一个个函数(读、写、跑命令) |
| 工具调用 | Tool call | 它输出的"我要调用某个工具"这段文本 |
| 工具结果 | Tool result | 工具执行完返回给它的内容 |
| 模型上下文协议 | MCP | 给外壳插外部工具的标准接口 |
| 权限申请 | Permission request | 执行前弹出来问你"能不能干" |
| 权限模式 | Permission mode | 哪些操作要问、哪些直接放行 |
| 智能体模式 | Agent mode | 权限 + 行为指令打包在一起的预设 |
| 沙箱 | Sandbox | 把它关在隔离环境里跑,炸了也不影响你 |
第 4 章 · 它是怎么犯错的
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 讨好倾向 | Sycophancy | 顺着你说,你说啥它都觉得对 |
| 幻觉 | Hallucination | 一本正经地胡说八道 |
| 参数知识 | Parametric knowledge | 训练时背下来的、存在参数里的知识 |
| 知识截止日 | Knowledge cutoff | 它"背书"背到哪天为止 |
| 上下文知识 | Contextual knowledge | 摆在它眼前、能直接读到的知识 |
| 注意力关联 | Attention relationship | 词元与词元之间的相互影响力 |
| 注意力预算 | Attention budget | 每个词元能分出去的注意力是有限的 |
| 注意力衰减 | Attention degradation | 上下文越长,关键信息越被淹没 |
| 聪明区 | Smart zone | 会话前期它最清醒的那段(反面叫"变笨区") |
第 5 章 · 交接
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 清空 | Clearing | 结束会话,从零开始 |
| 交接 | Handoff | 把上下文从一个会话带到下一个 |
| 一手资料 | Primary source | 原始的东西本身(代码、日志、真实响应) |
| 二手资料 | Secondary source | 对原始资料的转述(文档、摘要、报告) |
| 交接文档 | Handoff artifact | 写进文件里、给下一个会话看的交接材料 |
| 方案文档 | Spec | 描述一件跨多次会话的大活 |
| 工单 | Ticket | 一次会话能干完的一小块活 |
| 压缩 | Compaction | 把历史总结成摘要,喂给新会话 |
| 自动压缩 | Autocompact | 窗口快满时外壳自动触发的压缩 |
第 6 章 · 记忆与引导
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 记忆系统 | Memory system | 让它跨会话"记住"东西的机制 |
| AGENTS.md | AGENTS.md | 项目给它的常驻说明书 |
| 渐进式展开 | Progressive disclosure | 先只给目录,用到再加载全文 |
| 上下文指针 | Context pointer | 一句话指向某份资料,需要时再拉进来 |
| 技能包 | Skill | 打包好的一项能力,用到才加载 |
| 子智能体 | Subagent | 派出去干脏活、只回报结果的小弟 |
第 7 章 · 干活的方式
| 中文名 | 英文 | 一句话 |
|---|---|---|
| 人在环中 | Human-in-the-loop | 你盯着它干,随时纠偏 |
| 挂机 | AFK | 你走开让它自己跑 |
| 自动检查 | Automated check | 测试/类型检查/lint,非过即挂 |
| 自动评审 | Automated review | 让另一个智能体来评审 |
| 人工评审 | Human review | 你自己读 diff |
| 氛围编程 | Vibe coding | 不看代码,能跑就算过 |
| 共同设计图景 | Design concept | 你俩脑子里那件"要做的东西"是否一致 |
| 连环追问 | Grilling | 让它反过来一个问题一个问题问你 |
| 快速原型 | Prototyping | 先做个粗糙版本,看着实物再聊 |
| 开发者体验 | DX | 对人类友好程度 |
| 智能体体验 | AX | 对 AI 友好程度 |
第 1 章 · 模型本身
人工智能(AI)
一句话:AI 不是一个技术名词,是一个"会移动的标签"——它永远指向"计算机刚刚学会干的那件牛逼事"。
它不像"模型""词元"那样指一个固定的东西。它指的是这个时代计算机新能做到、又让人惊艳的事。现在它指的是大语言模型,但以前它指过完全不同的东西:
| 年代 | 当时"AI"指的是什么 |
|---|---|
| 1950s | 符号推理——定理证明器、下棋程序 |
| 1960s–70s | 规则程序——ELIZA、SHRDLU |
| 1980s | 专家系统——成千上万条手写 if-then 规则 |
| 1990s | 博弈树搜索——深蓝下赢卡斯帕罗夫(1997)。那时候研究者反而躲着"AI"这个词 |
| 2000s | 统计机器学习——垃圾邮件过滤、推荐系统。当时统一叫"机器学习" |
| 2010s | 深度学习——图像识别(AlexNet,2012)、AlphaGo(2016) |
| 2020s | 大语言模型——ChatGPT(2022)之后,"AI"就等于"聊天机器人"了 |
这个标签为什么老在动?有个专门的观察,叫 AI 效应:一项技术一旦真的好用了,就会被改个朴素的名字——"这不就是搜索嘛""这不就是统计嘛",然后"AI"这个词就往前滑,指向下一个还没解决的事。这个观察很老了,1971 年 Bertram Raphael 就说过:"AI 是我们还不知道怎么让计算机好好解决的那类问题的统称。"1979 年前后 Larry Tesler 的说法更损:"智能就是机器还没做到的事。"
所以,AI 话题特别容易各说各的。 "AI 不会推理""AI 被吹过头了"——这种话里藏着一个时间戳:你说的是 1980 年代的专家系统、2010 年代的图像分类器、还是上个月的这个模型?三种指代能推出三个完全不同的结论。
一旦讨论卡住了,把这个词换掉。 换成你真正想说的那个东西:是模型?是外壳?是智能体?还是你给它的上下文?话题立刻就清楚了。
⚠️ 别这么说:任何技术判断里都别光说"AI"——说清楚你指的是哪个部件。"AI 编程"作为这个行当的名字没问题,但"AI 产生幻觉了"这句话不合格。
💬 对话示例:
"CTO 想知道 AI 能不能处理工单分诊。"
"先翻译一下再评估——她说的其实是'一个接了工单系统的智能体'。光说'AI'不构成需求。"
模型(Model)
一句话:模型就是那堆参数,无状态,只会"接着往下猜词元",别的什么都不会。
"Claude Opus 4.x""GPT-5.x"这些是模型。光有模型干不了任何智能体的事,它得被装进一个外壳里。
模型不能读文件、不能跑命令、不能上网、也不记得昨天发生的事——它只吃词元、吐词元,一次请求干一次活。你感觉到的"智能体在干活"——选工具、读结果、循环到任务完成——全是外壳在背后串联了几十次预测。
模型是分档位的:大的最聪明但慢又贵,小的快又便宜但能力弱。选档位是个真决策——规划和硬骨头 bug 用重量级,机械性改改用轻量级,而且大多数外壳允许你在会话中途切换。
把这个词用严格了,排查问题会变快。 "这个模型不行"是个非常具体的指控——但同一个模型换个外壳、换个上下文,表现可能完全不一样。在怪模型之前,先看看你给了它什么:绝大多数让人失望的输出,根子在上下文或者外壳,不在参数。
💬 对话示例:
"规划那一步要不要从 Sonnet 换成 Opus?"
"可以试——但这个任务主要是外壳在扛。如果系统提示词和工具有问题,换模型救不了。"
参数(Parameters)
一句话:模型内部那几十亿个数字,训练时调好的,训练完就冻住了。模型"知道"的一切都在这里面。也叫权重(weights)。
机制上说,参数就是把输入变成输出的那套计算:上下文窗口里的词元进去,跟参数做一遍巨量的乘法,出来下一个词元的概率。 模型内部没有事实数据库,没有代码查找表,就是这些数字,被排布成"算出来的结果通常有用"的样子。它能背出来的标准库 API,属于参数知识——存在参数里,不是从哪儿查出来的。
最关键的一点:训练结束后参数就冻住了。 你在会话里做的任何事都不会改变它——你纠正它没用、给它看代码库没用、它犯了错也学不到。每次会话跑的都是同一套数字。 这就是为什么模型是无状态的、为什么它的知识止于知识截止日、为什么你项目里的东西只能通过上下文喂进去。唯一能改参数的办法是再训练一次——而那就等于换了个模型。
💬 对话示例:
"能不能拿我们代码库微调一下?"
"那就是改参数了,改完等于另一个模型。为了一个项目,把代码库当上下文喂进去,几乎永远比重新训练划算。"
训练(Training)
一句话:把海量文本喂给模型、不断调参数让它"接话接得更准"的过程。一次性、极贵,由模型服务商来干。
机制就是大规模重复:给模型一段文本,让它猜下一个词元,把参数往"真实答案"的方向推一点点,然后在几万亿个词元上重复这个过程。没有任何东西是以"事实"或"规则"的形式存进去的——模型"知道"的一切,都是"预测得更准"这件事的副产品,被压进了参数里,也就是参数知识。
训练分预训练(大头)和后训练(指令跟随、安全对齐这些后期打磨),在本词典这个层面不用区分。
两个跟你日常相关的后果:
- 训练有个结束时间,所以模型有知识截止日——你上个月升级的库版本它没见过。
- 训练这事儿你干不了。 当模型不了解你的代码库、你的约定、你的内部 API 时,解法永远不是"教教它",而是把材料放进上下文——那是你唯一能控制的输入。
💬 对话示例:
"能不能让它学会我们的内部 API?"
"别走训练——那是服务商层面、按月算的事。把 API 文档加载进上下文,这才是你手上真正能拉的杆。"
推理(Inference)
一句话:跑一个训练好的模型、让它吐输出——每次请求模型时发生的就是这件事。
一个模型的一生分两个阶段:
| 阶段 | 什么时候发生 | 干什么 | 参数状态 |
|---|---|---|---|
| 训练 | 发布前,一次 | 从语料里算出参数 | 正在被写入 |
| 推理 | 每次有人用模型 | 拿冻住的参数跑你的上下文,生成词元 | 只读 |
推理时不会把任何东西写回参数——这就是你今天纠正了它、明天它照样犯同一个错的原因。那个模型不是无视你,它是在学习这件事上无能为力。模型是无状态的,连续性只能从外部来:上下文窗口,或者记忆系统。
这个机制也解释了账单:每次请求都要把整个上下文跑一遍,所以成本随输入词元和输出词元增长,而一个调了几十次工具的智能体,每一趟来回都要付一次推理的钱。上下文大小因此既是质量问题,也是钱的问题。
💬 对话示例:
"为什么是按用量收费,不是买断授权?"
"你在为推理付费——每一次模型请求都在服务商的机器上跑了一遍。训练的钱早就花完了,推理是每次请求都要付的,而且一个回合里调了工具的话会展开成好多次请求。"
思考档位(Effort)
一句话:一个旋钮,控制模型在回答之前先想多久。按请求设置。
它控制的是模型开始写你看到的那段回答之前,先在心里推演多长。这段思考跟别的东西一样,是在推理时生成出来的;外壳经常把它藏起来不给你看,但那是模型实实在在干的活。
档位调高 = 更慢 + 更贵。 思考是以词元的形式吐出来的,按输出词元计费(哪怕你一个字都没看见),而且是一个词元一个词元生成的——所以调高档位既拉长了等待,也加厚了账单。买的是更充分的思考,代价是时间和钱。
大多数外壳把它做成一个小阶梯:
| 档位 | 适合什么 |
|---|---|
| 低 | 机械性改动、查东西、路径明确的改动 |
| 中 | 日常编码,通常的默认值 |
| 高 | 棘手的 bug、设计决策、多步规划 |
| 最高 | 最硬的骨头,答错了返工代价极高的那种 |
调错了两头都难受。 难题上开低档:得到一个自信但肤浅的答案,读着挺顺,错在一个你后面才发现的地方。一行重命名开最高档:你干等它想了半天,产出的东西跟最低档一模一样。
按任务配档位,不是按会话配。 真正需要推理的地方调上去,周围的机械劳动调下来。
💬 对话示例:
"这个并发问题它一直修不好,我都讲了三遍了。"
"把思考档位调高。这是个重推理的 bug,默认档位下它还没想够就定了方案。"
词元(Token)
一句话:模型读写的最小单位,大致相当于"词"那么大,但不等同于词。上下文窗口大小、成本、速度,全按词元算。
文本要通过一个分词器(tokenizer)变成词元:那是一个训练前就学好的、几万个片段的固定词表,任何输入都会被切成这个表里的一串条目。模型从来没见过"字"或"单词"——进去时全部转成词元,出来时也是一个词元一个词元地吐。
经验值:英文里 1 个词元 ≈ 四分之三个单词,1000 词元 ≈ 750 个单词。中文大致是 1 个汉字 ≈ 1 个词元左右(常见词可能被合并成一个,生僻字、emoji 会被拆成好几块)。
代码更没准:常见关键字很省,但生成的标识符、哈希值、base64、压缩后的代码会被切得很碎。规律是:在分词器的训练语料里出现得越频繁的东西,编码越短。 function 是一个词元,而 a3f9c2e1 这种从没出现过的哈希会被切成好几个。这就是为什么一个看着不大的文件、里面全是奇怪字符串,能占掉吓人的一大块上下文窗口。
词元是所有计量的单位:成本按词元(输入和输出分开计价);速度按每秒多少词元(因为输出是一个一个生成的);上下文窗口是个固定的词元数,所以你的文件有多大,决定了能装多少。
⚠️ 别这么说:别把词元叫"字"或"词"。词元的边界和词的边界对不齐,真正有意义的单位是"每秒多少词元"和"每块钱多少词元"。也别把 API 账单上的 token 当成"字数"来估算成本,会被差出好几倍。
💬 对话示例:
"这个提示词大概会有多大?"
"过一遍分词器看看—— schema 本身很紧凑,但那些 JSON key 写得很怪,切出来的词元会比你想的多。"
下一个词元预测(Next-token prediction)
一句话:模型真正会做的唯一一件事:看着上下文,猜下一个词元,接上去,再猜下一个。循环。
每一步都一样:上下文窗口里的词元经过参数计算,给词表里每个词元都算出一个概率(这个很像下一个,那个不太像),然后从这些概率里采样一个,接上去,用变长了一点的上下文再跑一遍。采样这一步,就是同一个提示词两次跑出不同结果的原因——不确定性是机制自带的,不是后来加上的 bug。
记住这个机制,很多"看着诡异"的行为一下就通了:
- 模型在吐出一个词元之前,从不检查它是不是真的,只看它是不是像——这是幻觉的根源。
- 它是走一步认一步的,所以开头一句语气很肯定的话,能把后面整段都带沟里去。
- 输出词元是严格一个一个生成的,这给任何智能体的工作速度定了个下限。
💬 对话示例:
"智能体是怎么'决定'要调工具的?"
"它没有'决定'——一路都是下一个词元预测。所谓工具调用,就是它输出的一段结构化文本,由外壳从输出流里解析出来。"
不确定性(Non-determinism)
一句话:同样的输入,可能产出不同的结果。你的代码一点没改,答案也可能不一样。
这是模型生成文本的方式、以及服务商提供服务的方式共同决定的。推理时,模型给下一个词元算出一个概率分布,然后从里面采样一个——而且通常是有意加了随机的,因为永远只选概率最高的那个,产出的文本会又呆又重复。
早期采样错一个词元,后面所有词元都会跟着变——这就是一个不同的词能滚成一个完全不同的方案的原因。服务商那边还要再加一层变数:请求是在共享硬件上批处理的,不同批次之间微小的浮点差异,就能让两个概率接近的词元分出胜负。没有任何开关能把这事儿彻底关掉。
实际影响有两条:
- 重试是正当策略。 一次失败只是从这个分布里抽了一次签,重来一次可能就抽到好的。
- 验证比用确定性工具时更重要。 你不能测一次就说"它以后都这样",所以必须靠自动检查去兜住那些抽得差的签。
但别把这事儿过度叙事化。 人天生爱找规律,连着几次跑砸了,很容易得出"这周模型变笨了"的结论——大多数时候,那只是分布而已。
💬 对话示例:
"Claude 今天太拉了,是不是他们发了个更差的版本?"
"大概率不是——模型输出本来就是不确定的。同一个任务,你有状态好的时候也有状态差的时候。明天再试一次,别急着去找原因。"
模型服务商(Model provider)
一句话:真正把模型跑起来、提供推理服务的一方。通常是远程服务(Anthropic、OpenAI、Google),也可以是本地的(Ollama、LM Studio、llama.cpp)。
外壳自己不跑模型,它去请求服务商。 服务商握着机器:参数住在它的硬件上,每一次模型请求都是外壳把词元发过去、把预测拿回来。
这也让服务商成了整整一类问题的源头,而这些问题经常被错怪到模型头上——限流、容量下降、服务中断,全在这儿。智能体在会话中途卡住、或者每回合都报错时,先去看服务商的状态页,比查别的都值。
服务商还定商业条款:输入/输出词元的单价、前缀缓存的折扣、以及你能用哪些模型。注意:服务商和模型的制造方可以是两家公司——Bedrock、Vertex、OpenRouter 卖的都是别人家的模型。
本地服务商是用能力换控制权:能塞进你自己机器的模型比前沿模型小得多,但数据不出本机,也没有按词元计费的账单。
💬 对话示例:
"能给那个内网隔离的客户做个离线版吗?"
"把模型服务商换成本地的——他们机器上装 Ollama 或 llama.cpp。外壳根本不关心,只是换个接口地址。"
外壳(Harness)
一句话:模型之外、把它变成智能体的一切东西:工具、系统提示词、上下文窗口管理、权限、钩子。Claude.ai 和 Claude Code 跑的是同一个模型,但表现完全不同,因为外壳不同。
译法说明:Harness 中文圈还没统一,有人叫"外壳""宿主""载体""运行框架"。本书统一叫外壳——它的本意就是"套在模型外面的那套装备"。
模型本身只会一件事:文本进、文本出。它读不了文件、跑不了命令、记不住上一回合。这些全靠外壳补上。 外壳负责:为每次模型请求组装上下文、执行模型要求的工具调用、把工具结果喂回去、存储会话历史、在危险操作前问你许可、决定什么时候压缩。那个"智能体循环"——模型提议、外壳执行、重复——是外壳在跑。
这对排查问题至关重要。 两个产品表现不同、或者同一个产品今天和昨天不一样,变量往往不是模型,是外壳:不同的系统提示词、不同的工具集、改过的权限默认值、新的上下文管理策略,都能在模型不变的情况下改变行为。也意味着你的大部分配置都配在外壳上——AGENTS.md、权限设置、钩子,都是给外壳的指令,不是给模型的。
例子:Claude Code、Cursor、Codex CLI——以及 Claude.ai(它是个聊天外壳,不是写代码的外壳)。
💬 对话示例:
"同一个模型,为什么 Claude Code 能改文件,Claude.ai 只会回答问题?"
"外壳不同——Claude Code 有文件系统工具、不同的系统提示词、还有一层权限。这里的变量不是模型。"
一次模型请求(Model provider request)
一句话:外壳到模型服务商的一次往返。外壳把当前上下文发过去,服务商返回一个响应(一个工具调用,或者最终答案)。
你发一条消息,可能触发很多次模型请求——智能体每调一次工具,工具结果回来就触发下一次请求。
每次请求都带着全部家当:系统提示词、到目前为止的完整对话、每一个工具结果。模型是无状态的,服务商在请求之间什么都不留——第 40 次请求会把第 39 次发过的东西原样再发一遍,外加一个新的工具结果。前缀缓存存在的意义就是让这种重复变得便宜。
请求也是计费单位。 输入词元、输出词元、缓存折扣,都是按请求算的——这就是为什么一个看着人畜无害的问题能烧掉不少钱:成本不是跟你的消息长度成正比,而是跟请求次数 × 每次携带的上下文大小成正比。
请求和回合是两个东西,要分开。 一个回合是你跟它的一次交换,而"修一下这个挂掉的测试"这样一个回合,实际是这样展开的:
| 请求 | 模型返回 | 外壳接着干 |
|---|---|---|
| 1 | 工具调用:跑测试 | 跑了,把失败输出追加进去 |
| 2 | 工具调用:读测试文件 | 把文件内容追加进去 |
| 3 | 工具调用:读源码文件 | 把文件内容追加进去 |
| 4 | 工具调用:改源码文件 | 应用改动,把结果追加进去 |
| 5 | 工具调用:再跑一次测试 | 跑了,把通过输出追加进去 |
| 6 | 最终回答:"修好了,测试通过" | 展示给你 |
一个回合 = 六次请求,每次都重发整个上下文。 想知道词元花哪儿了,数请求,别数回合。
💬 对话示例:
"就问了一个问题,烧了四万词元?"
"看工具调用——12 次 grep、8 次读文件、4 次改文件。每个工具结果都会触发一次新的模型请求,而且每次都把整个会话前缀重发一遍。"
输入词元(Input tokens)
一句话:每次请求里外壳发出去的词元——系统提示词、对话历史、工具结果,所有模型动笔之前要读的东西。单价比输出词元便宜。
做 AI 编程时,输入词元是你账单的大头。 因为模型是无状态的,每一回合都要把整个会话重发一遍:你的第一条消息、它的每次回答、此后的每个工具结果。第 50 回合的输入里,装着前 49 回合。 一次请求可能只产出几百个输出词元,却要重发十万词元的历史。
前缀缓存能救一部分:和历史完全吻合的部分按便宜的缓存词元计费,而不是全价输入。输入成本还是疼的时候,办法就是缩小重发的内容——任务之间清空或压缩。
💬 对话示例:
"账单很高,但它几乎没写什么东西啊。"
"是输入词元——每回合都要重发整个会话。没有前缀缓存的话,你等于每次请求都为整段历史重新付一遍钱。"
输出词元(Output tokens)
一句话:模型生成出来的词元。单价比输入词元高(常见是 5 倍左右),因为生成比读取更费算力。
模型写的每一个词元都算:你读到的文字、它吐的代码、工具调用、还有它回答之前的长考。最后这项最让人意外——思考词元即使外壳不展示给你,也按输出计费,而且思考档位调得越高花得越多。
输出词元还决定了会话的节奏。 模型读输入很快,但输出是一个词元一个词元憋出来的,所以一个回合感觉慢,几乎永远是"正在写",不是"正在读"。 等得久,通常意味着一个长答案正在路上。
💬 对话示例:
"重构这个会话烧钱特别快,可输入明明不大。"
"它在整文件重写而不是打补丁。输出词元单价大概是输入的五倍——让它改成输出 diff,账单立刻下来。"
前缀缓存(Prefix cache)
一句话:服务商那边的缓存机制,让连续几次请求可以跳过重复处理"相同开头"的部分。命中了就按便宜得多的缓存词元计价。
缓存之所以划算,是因为会话是只往上追加的。每次请求都要把整段历史当输入词元重发一遍,而正常会话里,历史只在末尾变化——每次请求基本就是上一次请求再加几条新消息。 服务商把那段长长的共同开头处理一次、存起来,然后从前缀结束的地方接着算。没有缓存的话,一个 50 回合的会话等于把第 1 回合重算 50 遍。
缓存也会过期。 各家的保留时长不一样,一般是"几分钟"这个量级,不是几小时。会话闲太久,下一次请求会把前缀按全价重建一遍再恢复缓存。这主要是做外壳的人要操心的;对你来说,肉眼可见的效果就是:长时间暂停之后的请求,比暂停之前的贵。
缓存还有个脆弱的地方:它只匹配完全一致的前缀。 如果外壳在每轮的系统提示词里注入了当前时间,那第一处变化之后缓存全断,之后每次请求都按全价算。
💬 对话示例:
"为什么账单在会话中间突然飙了一下?"
"外壳开始往系统提示词里塞当前时间了。前缀缓存从第一个变化的词元处就断了,之后每次请求都按全价计费。"
缓存词元(Cache tokens)
一句话:服务商从之前的请求里缓存下来的输入词元,命中前缀缓存时按很低的折扣价计费。让长会话变得付得起的那根杠杆。
模型是无状态的,所以每次请求都要把整个对话重发一遍——系统提示词、每条消息、每个工具结果,全算输入词元。到第 50 回合,每次请求都扛着 50 回合的历史,而且每一回合都要按全价付一遍。 缓存改变了这个算式:前缀完全一致、服务商已经算过的那部分,按缓存词元计费,常常只有输入单价的十分之一甚至更低。 长会话里你发出去的大部分都是缓存词元,账单才没爆。
每个字母代表一小段对话内容,每次请求发送的是"到目前为止的对话":
| 请求发送 | 命中缓存 | 全价计费 | 原因 |
|---|---|---|---|
AB |
无 | AB |
第一次请求,没东西可对 |
ABC |
AB |
C |
AB 与上次请求的前缀完全一致 |
ABCD |
ABC |
D |
前缀仍然完整 |
AXCD |
A |
XCD |
前面内容被改了(B 变 X),从那里开始匹配失败 |
缓存的脆弱点很具体:它只认完全一致的前缀。 只要前面有任何东西变了——外壳调整了内容顺序、时间戳更新了、某个文件的表示方式变了——从那一点往后缓存全部失效,后面的都按全价输入计费。 缓存还会在几分钟不活动后过期,所以长时间暂停后恢复的会话要再付一次全价。
会话成本莫名其妙地涨了,就去用量报告里对比缓存词元和输入词元——缓存坏了会最先在那儿露出来。
💬 对话示例:
"长会话太烧钱了——一次重构八美元。"
"看缓存词元那一项。如果外壳在每回合之间重排了系统提示词或者文件,前缀就断了,你等于每次请求都按全价重付一遍输入。"
第 2 章 · 会话、上下文窗口与回合
无状态(Stateless)
一句话:不把信息往前带。模型在请求之间是无状态的,智能体在会话之间默认也是无状态的。
模型是永久无状态的:参数训练完就冻住了,推理时你做的任何事都改不了它。它不从你的纠正里学习,不记得昨天你跟它说过同样的话,也没有在慢慢了解你——不管对话感觉起来多像那么回事。
会话内部的"连续感"是外壳制造出来的:它保存着聊天记录,每次请求都重发一遍。模型不是在回忆这段对话,它是在重读。
实际后果:想让它跨会话记住什么,你得写下来,写到它下次会读回来的地方。这就是 AGENTS.md、记忆系统、交接文档存在的意义——它们是替补,替补那个模型根本没有的记忆。
所以,当它反复犯一个你纠正过的错时,问题不是"它为什么没学会"——它学不会。问题是:这条纠正应该写在什么地方,才能让以后的每一次会话都读到它。
💬 对话示例:
"为什么每次清空之后,它就把我们的约定忘光了?"
"模型是无状态的,新会话从空白开始。想让它带着走,就写进
AGENTS.md,或者写进外壳每次会话开始时会加载的记忆文件。"
上下文(Context)
一句话:智能体现在手上有的、跟任务相关的信息。是个抽象名词——不是模型看到的原始输入(那是上下文窗口),也不是运行历史(那是会话)。
这三个词要分清楚:
| 词 | 指的是什么 |
|---|---|
| 上下文 | 智能体当前拥有的、与任务相关的信息 |
| 上下文窗口 | 模型每次请求实际看到的那串词元 |
| 会话 | 外壳存着的那段正在进行的对话 |
分开讲是有意义的,因为上下文衡量的是质量,不是数量。 一个窗口快满了,上下文可能依然极差——几千词元的过期工具输出,没一条跟当前任务有关。反过来,窗口几乎空着,上下文也可能极好:就那一个关键的类型定义,任务全靠它。
日常翻车大多数都追溯到上下文。 它凭空造了个 API、推翻了自己之前的决定、猜了个 schema——第一个要问的问题是:它干这事的时候,上下文里有什么? 答案通常是:相关的事实压根没加载进来,或者被注意力衰减埋掉了。
解法永远是"策展":任务需要什么就加载什么,不需要的别放进来。
💬 对话示例:
"它老是在编一些类型里根本没有的字段。"
"类型文件没进上下文——它读的是调用处,在那儿瞎猜。先把定义读进来。"
上下文窗口(Context window)
一句话:模型每次请求能看到的一切。有上限、每个模型不一样,而且是模型感知世界的唯一通道。
它就是一串词元:系统提示词、到目前为止的对话、外壳喂回来的每个工具结果。在这串序列里的东西,模型就能用;不在里面的,模型根本不知道它存在——你的代码库、你昨天改的文件、你三次会话之前给的指令,全都不存在。窗口外的任何东西,都必须先通过工具调用拿进来,才能起作用。
"有上限"意味着它会被填满。 每一回合都在往上追加,长会话最终会撞上天花板,逼你压缩或清空。也意味着窗口里的东西是互相抢地盘的:你多加载一个词元,别的东西就少一个;你塞进去的没用内容,照样占用模型的注意力预算。
正确的姿态是:把窗口当预算花。任务需要什么加载什么,剩下的别碰。
⚠️ 别这么说:别把上下文窗口叫"记忆"。它是工作状态,不跨会话留存。 记忆系统是叠在它上面的另一层概念。
💬 对话示例:
"我能把整个 monorepo 粘进提示词吗?"
"窗口是 20 万词元——大概装得下仓库的五分之一。挑这个任务真正碰到的文件,其余的留在工具调用后面。"
有状态(Stateful)
一句话:会把信息往下带。会话在回合之间是有状态的(上下文一路累积,所以长会话会漂进变笨区);智能体在会话之间可以靠记忆系统变成有状态。模型永远不可能是有状态的。
每一层的"有状态"住在哪儿:
| 层 | 有没有状态 | 靠什么 |
|---|---|---|
| 模型 | 永远没有 | 参数是冻住的,它只看得到每次请求里的东西 |
| 会话 | 跨回合有 | 外壳把每条消息和每个工具结果追加进上下文 |
| 外壳 | 跨会话有 | 记忆文件、AGENTS.md、交接文档——写下来,以后再加载 |
| 环境 | 永远有 | 不管有没有会话在跑,文件都在那儿 |
每一层的"有状态",都是靠重读下一层存着的东西搭出来的:会话之所以感觉连续,是外壳把消息历史重发给了无状态的模型;智能体之所以跨会话还记得,是外壳从环境里重新加载了文件。从来没有任何状态存在模型自己身上。
但状态也不总是好事。 所有往前带的东西都会影响后面,所以一个早期做出的错误假设,也会被一路带着走。 清空就是主动扔掉会话状态、从写下来的东西重新开始。
💬 对话示例:
"它记得我昨天的偏好——那是不是说明模型学会了?"
"没有。之所以像'有记忆',是因为外壳把它写进了记忆文件,会话开始时又加载了一次。模型本身对昨天一无所知。"
智能体(Agent)
一句话:一个被装进外壳的模型——配好了工具、系统提示词、上下文窗口,然后跟你一来一回地干活。Claude Code 是智能体,Cursor 是智能体,Claude.ai 也是智能体。
译法说明:中文圈叫"智能体"最多,也有人叫"代理""Agent"。本书统一叫智能体。
跟这本词典里大多数词不同,"智能体"指的不是某个机械部件。模型是一堆参数文件,外壳是你能指着说"就是这个软件"的东西,而智能体哪个都不是——它是你在对话的那个对象。 是模型动起来之后的形态,是为了某个目的被配置好的那个单位。
人们不停地拟人化 AI,而智能体就是那个被拟人化的单位:你把活派给它、它读你的消息然后回答、它是"它又把构建弄坏了"里的那个"它"。你说智能体干了什么,意思是"模型 + 外壳"一起干的,但你在把这个组合当成一个角色对待。
这个概念比这一波 AI 热要老得多。软件智能体——你把一个目标托付给它、它代表你去行动的程序——这个概念跟 AI 本身一样老。
⚠️ 别这么说:"那个 AI"、"那个机器人"——太模糊了,听不出来你说的是参数,还是那个被装起来的东西。
💬 对话示例:
"迁移这活你用哪个智能体?"
"本地用 Claude Code,UI 那块用 Cursor——底下是同一个模型,外壳不同。"
系统提示词(System prompt)
一句话:外壳在每次请求最前面塞进去的指令——智能体的"岗位说明书":它是谁、该怎么表现、能调哪些工具、要遵守哪些约定。
系统提示词是外壳厂商写的,不是你写的,而且在编程类外壳里它非常大——常常是几万词元的行为规则、工具说明、边界情况处理,每一回合都要按输入词元付一遍钱。 你自己的常驻指令跟它并排加载:像 AGENTS.md 这种文件,会在会话开始时放在系统提示词旁边,于是模型在读你的消息之前,先同时读了厂商的说明书和你的。
因为它每次请求都一样,它构成了前缀缓存的开头——这也是外壳倾向于整个会话固定它、而不是边跑边改的原因之一。
还有一点很关键:模型被训练成优先服从系统提示词,而不是你的消息。 所以当智能体坚持某个你没要求的约定、或者用一种你怎么都改不掉的格式输出时,它多半是在服从系统提示词——而你的消息在这场争论里是输的。 有些外壳是可定制的,会把系统提示词完全开放给你,你能读到它到底被灌了什么,也能改。
💬 对话示例:
"两个外壳,同一个模型,同一个提示词,行为完全不一样。"
"系统提示词不同。一个被调成简洁地改代码,另一个被调成爱解释——分歧在你消息到达之前就已经存在了。"
会话(Session)
一句话:跟一个智能体交互的一次完整、有边界的过程。从空开始,一路上累积消息、工具结果、读过的文件,到清空、关闭、或者被压缩成一个新会话时结束。
会话就是那个把上下文窗口填满的东西:如果上下文窗口是箱子,会话就是慢慢把箱子塞满的内容。一次窗口装不下的活,必须拆到多个会话里做。
会话的消息历史就是智能体的工作记忆。 模型是无状态的,所以它看起来"记得"的一切——你要什么、测试报了什么、三回合前它定了什么——全在那条消息历史里,每次请求重发一遍。不在会话里的东西,对它来说不存在。
这份记忆随会话结束而结束。 新会话从零开始:昨天那个对你的代码库了如指掌的智能体,今早一问三不知。能活下来的是文件系统——上一个会话写下的文件,下一个会话能读到,这正是交接、记忆系统、AGENTS.md 依赖的东西。
会话在哪儿结束,是你定的。 会话里的一切都会影响后面的每一回合,所以在一个会话里塞进不相关的任务,会留下残渣、给下一个答案染色。一个会话只做一件事,能保持上下文干净;一件事做完,就是清空的自然时机。
💬 对话示例:
"一个会话能跑多久才开始崩?"
"看活的类型——目标明确的重构比开放式调研能撑得久得多。会话一臃肿,就交接或者压缩,别硬扛。"
回合(Turn)
一句话:你发一条消息 + 智能体在响应里做的所有事,直到它把控制权交还给你。包含一次或多次模型请求(调了工具的话就是很多次)。
一个澄清式提问会结束这个回合;你回了,下一个回合开始。层级关系是:会话 > 回合 > 模型请求。
回合值得单独命名,是因为它的长度是智能体决定的,不是你。 你递过去一条消息,然后由它决定要串多少次工具调用才交还控制权。一个回合可以是一句话的回答,也可以是二十分钟的读文件、改代码、跑测试。
这是一个性质的两面:回合长,才让挂机成为可能;回合长,也正是没人看着时容易出事的地方——等它交还控制权的时候,它可能已经跑偏到你意思之外很远了。
回合也是纠偏的天然单位。 回合内部发生的一切你都插不上手,回合之间的空隙才是你重定向的时机。 大多数外壳把这个软化了一点:你可以在回合中途打断它、或者边干活边打字(消息会在回合结束后被读到)。
如果你老是不满意回合结束时的结果,解法通常是:要更小的回合——先出计划、一步一步来,用自主性换更频繁的纠偏机会。
💬 对话示例:
"一个回合花了两分钟?"
"它在这个回合里调了 14 次工具——每次都是一个独立的模型请求。延迟是叠加的,最后才交还给你。"
第 3 章 · 工具与环境
环境(Environment)
一句话:智能体干活的世界——外壳之外的一切,它只能通过工具结果感知、通过工具调用改变。外壳是"跑"智能体的,环境是智能体"在其中干活"的地方。
AGENTS.md 这种文件住在环境里,而把它加载进上下文窗口的是外壳。文件系统是最常见的一种环境,但不是唯一的(数据库、远程 API、一个浏览器会话,都可以是环境)。
智能体只有去看的时候才看得见环境。 它对环境的全部认知都来自工具结果,所以它脑子里的图景是一堆快照,每张在拍摄的那一刻是准确的。如果一个文件在它读过之后被改了——你手动改了、构建脚本重新生成了——它会继续基于那份过期的副本推理,直到有什么东西促使它重读。 一个智能体自信地描述一个"早就不长那样"的文件,通常就是这个:环境动了,快照没动。
环境也是唯一永远有状态的那一层。 会话结束,上下文就没了;但写进环境的文件还在,下一个会话能读——这正是记忆系统、交接文档、AGENTS.md 依赖的东西。任何你希望它明天还知道的东西,最后都得落到环境里。
环境多大,是你定的。 沙箱把它缩小,限制它能碰到什么;加一个工具把它扩大,把一个数据库或 API 纳入射程。边界内的东西,它才感知得到、改得动;边界外的,对它来说不存在。 环境被收拾得有多适合智能体干活,就是这份代码库的 AX。
⚠️ 别这么说:别用"环境"指运行时或外壳本身——外壳是包装,环境是工作场所。
💬 对话示例:
"智能体看不到预发库的 schema。"
"把它接进环境——给个
psql工具,权限限定成预发库只读。外壳没问题,是它没东西可干。"
文件系统(Filesystem)
一句话:智能体读、写、在其中执行命令的那棵目录树——编程智能体默认的环境。
AGENTS.md、技能包、源码、构建脚本、工具配置,全住在文件系统里。当一个外壳说"在你的项目里启动",它指的就是把智能体指向一个文件系统。
智能体只能通过工具调用碰它——读文件、写文件、跑 shell 命令。磁盘上的任何东西,在工具调用把它加载进来之前,都不在上下文窗口里,这正是智能体能在远大于窗口的仓库里干活的原因:文件系统装着全部,上下文只装着当前任务读过的部分。 有些外壳默认会把当前目录的文件名(不是内容,就那棵树)加载进上下文窗口,它们起的是上下文指针的作用:它知道有什么,然后去读它需要的文件。
而且这个文件系统是跟你共享的。 它改的文件,就是你在编辑器里打开、在 git 里 diff 的那同一批文件——文件系统是你 review 它工作的公共工作区。
💬 对话示例:
"它为什么没读到我的
AGENTS.md?""它跑在别的文件系统上——沙箱挂的是父目录,不是项目根目录。把外壳的指向改一下。"
工具(Tool)
一句话:外壳暴露给智能体调用的一个函数——读、写、跑命令、搜索。工具是智能体感知和改变环境的唯一方式。
大多数编程智能体自带这几个:
| 工具 | 干什么 |
|---|---|
| 读 | 把文件内容作为工具结果返回 |
| 写 | 在文件系统里创建或修改文件 |
| 命令行 | 跑一条 shell 命令并返回输出 |
| 搜索 | 在代码库里按模式找文件或文本 |
每次工具调用都要多花一次模型请求,因为结果得回到模型那儿,它才能决定下一步。
一个工具由三样东西定义:名字、描述、参数格式。外壳每次请求都把这些定义发给模型,而模型选工具的方式跟产出别的东西一模一样——靠写词元,这里写的是一段带参数的结构化调用。模型自己从不执行任何东西;外壳读这段调用、执行函数、把结果发回去。
工具清单决定了智能体能干什么。 一个很强的模型配上一套很窄的工具,就是个很窄的智能体——它会把什么都往仅有的工具上凑,这就是为什么智能体那么依赖命令行:一个 shell 就是一个能摸到系统大部分地方的工具。 想干净地给它加一项能力,就加一个工具;MCP 就是把外部工具插进外壳的标准。
工具定义在每次请求里都占上下文,所以工具多是有固定成本的——哪怕一个都没调。而且一堆描述相似的工具,会让模型更难选对。
💬 对话示例:
"智能体能直接查预发库吗?"
"给外壳加个
psql工具,权限限定预发只读。没有对应工具的话,文件系统之外的东西对它来说就是黑的。"
工具调用(Tool call)
一句话:模型输出的"我要调某个工具、参数是这些"——就只是一段结构化文本,它自己不产生任何作用,得由外壳读出来去执行。
一次工具调用的生命周期:
| 步骤 | 谁 | 发生什么 |
|---|---|---|
| 1 | 模型 | 从系统提示词的描述里知道有哪些工具 |
| 2 | 模型 | 输出一段调用——工具名 + 参数(通常是 JSON)——然后停下 |
| 3 | 外壳 | 解析这段调用,拿去跟权限模式比对 |
| 4 | 外壳 | 允许的话就执行 |
| 5 | 外壳 | 在下一次请求里,把结果作为工具结果发回去 |
一个回合的活,通常是很多次这样的往返串起来的。
因为这段调用跟别的东西一样是靠"下一个词元预测"生成的,它也会像任何模型输出一样出错:一个不存在的路径、命令根本没这个参数、看起来合理但实际不对的入参。外壳执行的是"写下来的",不是"心里想的"——路径打错不会优雅报错,它会去改错的文件。
💬 对话示例:
"它说它跑过测试了,可文件时间戳没变啊。"
"看会话记录——它到底输出了工具调用,还是只是描述了一遍'跑了测试'?调用是模型产出的,但外壳没执行的话,就什么都没发生。"
工具结果(Tool result)
一句话:外壳执行完工具调用后发回来的内容——文件内容、命令输出、报错。这是智能体看环境的唯一窗口。
它在下一次模型请求里回到模型那儿,模型再决定拿它干什么。工具调用和工具结果是一次交换的两端,都在同一个回合里。
工具结果的生命周期:
| 步骤 | 谁 | 发生什么 |
|---|---|---|
| 1 | 外壳 | 执行工具调用——跑命令、读文件 |
| 2 | 外壳 | 捕获结果:输出、内容、或者报错 |
| 3 | 外壳 | 作为一条消息追加进上下文 |
| 4 | 外壳 | 在下一次模型请求里把整个上下文发给服务商 |
| 5 | 模型 | 读到结果,决定:再调一次工具,还是给出最终回答 |
这个结果会在这之后的整个会话里一直待在上下文中。 工具结果通常是编程会话里上下文的大头:每读一个文件、每跑一次测试、每搜一次,都完整地留在里面,早就没用了也还占着词元。 几个大的结果——一份超长的测试日志、一个被整个读进来的生成文件——能把会话推到上下文窗口边缘,比对话本身快得多。
因为结果是模型唯一能看到的东西,它没法去核对结果背后的环境。 如果输出被截断了、命令静默失败了、或者外壳返回的是错误而不是内容,模型就基于手里那点东西推理。 当智能体对你的系统的认知明显不对时,去看会话记录里的工具结果:里面有某条结果说的话,跟你知道的事实不一样。
💬 对话示例:
"它推理的时候好像当那个文件是空的。"
"工具结果返回的是权限拒绝,不是文件内容。模型只看到了那句报错——它没有别的途径去看那个文件。"
模型上下文协议(MCP)
一句话:Model Context Protocol——一套把外部工具服务器插进外壳的协议,让智能体拿到外壳自带之外的工具。智能体从来不是"调用 MCP",它调用的是工具,只不过那个工具是某个 MCP 服务器提供的。
它解决的是一个集成问题:没有标准的时候,每个外壳都得自己写一遍 Linear 集成、自己写一遍 Slack 集成、自己写一遍数据库集成,还得各自维护。 有了 MCP,这个集成写成一次服务器,任何支持 MCP 的外壳都能用:外壳连上服务器,服务器声明自己提供哪些工具,这些工具就跟内置工具一起对智能体可用了。
(MCP 也能暴露资源和提示词模板,但提供工具是主要用途。)
代价付在上下文上。 服务器声明的每一个工具都对应一份定义——名字、描述、参数格式——而模型只能调用它知道的工具。最朴素的做法是把所有定义一股脑加载进上下文窗口:装几个慷慨的服务器,你一个字还没打,会话就已经背着几千词元的工具 schema 了,白花掉一大块注意力预算。
很多外壳现在用"工具搜索"来缓解:上下文里不放完整定义,只放一个指向可用工具的上下文指针——智能体按名字或用途去搜,需要时才把定义加载进来。如果你的外壳没这功能,那份前置成本就还在,那就只启用这个项目真正需要的服务器。
💬 对话示例:
"智能体需要从 Linear 里读工单。"
"给外壳配上 Linear 的 MCP 服务器——它把 Linear API 变成智能体能调的工具。省得你自己写工具封装了。"
权限申请(Permission request)
一句话:外壳在执行一个没被预先批准的调用之前,弹给你看的那一下。模型产出调用,外壳不立刻执行,而是停下来问你。这是外壳把人放进回路里的方式。
权限申请的生命周期:
| 步骤 | 谁 | 发生什么 |
|---|---|---|
| 1 | 模型 | 产出一个工具调用 |
| 2 | 外壳 | 拿去跟权限模式和已保存的批准记录比对 |
| 3 | 外壳 | 已预批准:立刻执行。否则:暂停并展示申请 |
| 4 | 你 | 批准一次 / 本次会话都批准 / 拒绝 |
| 5 | 外壳 | 执行调用,或者把"拒绝"作为工具结果发回去 |
拒绝本身也是一种引导。 模型会把"拒绝"当成一条普通工具结果来读并作出反应——它会换个方法,或者来问你想怎么办。大多数外壳允许你在拒绝时附带一句话,这就把一次拦截变成了纠偏点:"别这么干,用迁移脚本"——这句话正好落在模型正在决定下一步的时刻。
代价是:每次申请都是一次同步等待你。 智能体被阻塞着,直到你答复——你盯着的时候没问题,你不在的时候就麻烦了:一个频繁弹申请的智能体是没法挂着跑的。 权限模式就是那个旋钮:哪些调用放行、哪些要先问,理想情况下再配个沙箱,让"放行的集合"可以安全地放大。
💬 对话示例:
"它在一次权限申请上卡了十分钟——我在开会。"
"这就是人在环中的成本。把安全的工具预批准掉,让申请只在真正有风险的操作上触发。"
权限模式(Permission mode)
一句话:智能体模式里的"权限闸门"那一半——哪些调用要弹申请、哪些自动放行。这是模式系统最初的用途(后来外壳才在上面捆绑行为指令)。
外壳一般提供这么个阶梯:
| 模式 | 读 | 写和 shell | 典型用途 |
|---|---|---|---|
| 只读 / 规划 | 自动 | 阻止 | 调研、规划、评审 |
| 默认 | 自动 | 询问 | 日常有人盯着的工作 |
| 自动编辑 | 自动 | 编辑自动,shell 询问 | 可信仓库、机械性改动 |
| "随便" / 全自动 | 自动 | 自动 | 沙箱里、挂机跑 |
选哪一档是安全和打断之间的权衡,两头都有难受的地方。 太紧:你成了瓶颈,它每几秒就为一个无害的读操作停下,你闭着眼点批准,于是"批准"本身不再有任何意义——橡皮图章是两头最坏的组合,打断一点没少,保护一点没有。太松:它改了文件、跑了命令,而你本来想先看一眼的。
最松的那一档,只有在沙箱里才说得通——那里一个坏调用的爆炸半径是被圈住的。沙箱之外,大多数人的落点是:读操作自动放行,不可逆的操作留个人在环里。
💬 对话示例:
"它每次 grep 都停一下,挂机跑彻底废了。"
"只读工具放宽权限模式,写和 shell 继续询问。调研类会话里的大部分权限申请都是噪音。"
智能体模式(Agent mode)
一句话:一个塑造智能体运行时行为的预设——把权限模式和注入系统提示词的行为指令打包在一起。可以在会话中途切换。
"打包"是它跟单纯的权限设置的区别。 权限模式只是一个闸门:它决定哪些调用能过。光有闸门,会得到一个"想改但改不了"的智能体——它提出写入、被拦、然后想别的办法绕。注入的指令消除的是那个"想":规划模式不只是阻止编辑,它告诉智能体"你现在在规划阶段",于是它去读、去问、去提议,而不是跟闸门较劲。 闸门和引导指向同一个方向。
常见的几种:默认模式(危险操作询问)、规划模式(阻止编辑、引导它去调研)、接受编辑模式(自动批准编辑)、绕过权限模式(俗称 YOLO 模式,全都自动批准)。
实际用法是:随着你在任务过程中信任度的变化换模式。 同一个任务可以穿过几个模式:方案还在成形时用规划模式,头几处精细改动用默认模式,等它证明自己理解了这个改动后切接受编辑,挂机跑的时候在沙箱里用绕过模式。换模式不损失任何东西:对话从原处继续,只是权限和指令变了。
如果你发现自己每次都不看就批准,说明模式设得比你实际的信任更松;如果你一直在拒绝改动,说明设得太紧了。
厂商叫法:Claude Code 叫 "permission modes",Codex 叫 "approval modes"——两个名字都早于"捆绑行为指令"这件事。
💬 对话示例:
"我只想要个计划,它一直在改文件。"
"切规划模式——它会阻止写入,留在调研状态。"
"那等会儿挂机跑呢?"
"绕过权限模式,但只在沙箱里。"
沙箱(Sandbox)
一句话:智能体跑在里面的一个隔离环境——容器、虚拟机、临时文件系统、或者受限权限的 shell。它限制的是智能体行为的爆炸半径:哪怕它跑了破坏性命令、拉了恶意内容,损害也被圈住了。这是让挂机变成可行的安全底座。
沙箱和权限模式从两头解决同一个问题:权限是在行动发生之前问你;沙箱是限制这个行动如果真发生了能够到哪儿。权限需要你留在环里——每次弹窗都是一次打断,一个老在问的会话几乎谈不上自主;沙箱花的是基础设施,不是你的注意力:隔离越强,需要问的问题就越少。
隔离分几个等级:
| 等级 | 是什么 | 能圈住什么 |
|---|---|---|
| 受限 shell | 操作系统层面给每条命令上的枷锁 | 项目外的写入、网络访问 |
| 容器 | 全新的文件系统、不挂载凭证、用完就丢 | 智能体对它自己机器做的任何事 |
| 虚拟机 / 云 | 完全独立的一台机器,常由外壳提供 | 一切,包括内核级逃逸 |
没有哪种沙箱能圈住"合法地离开它"的行为。 一个拿着你 git 凭证的智能体能 push;一个能上网的智能体能调生产环境 API。先决定什么允许跨越边界,再决定隔离做多厚。
💬 对话示例:
"我想让它通宵跑'绕过权限'模式,但我还没准备好。"
"放沙箱里——全新容器,不挂凭证,不出网。最坏情况它把自己文件系统炸了,你把容器一丢。"
第 4 章 · 它是怎么犯错的
讨好倾向(Sycophancy)
一句话:自信且顺着你说话的输出。模型被训练得"喜欢说人爱听的话",而人通常更喜欢被认同、而不是被纠正——于是它学会了:顺着说是安全的,哪怕顺着说是错的。
译法说明:中文技术圈有"谄媚性""阿谀倾向""讨好性"几种说法。大白话就是马屁精模式。
它长这样:
- 一被追问就认怂 —— 你说一句"你确定吗?",它就把本来正确的答案整个推翻。
- 夸烂东西 —— 还没分析呢,先说你那个漏洞百出的方案"非常棒"。
- 框架偏见 —— 你暗示这是你写的代码,评审就偏正面;暗示是别人写的,同一个东西评审就变负面。同一份产物,两种判决。
- 复读你的错误 —— 把你的错原样复述一遍,当成对你的确认。
诊断方法:没有你的引导,它会这么说吗? 如果唯一变化的是你的语气或者说法,那就是讨好倾向,不是分析真的变了。
解法:把你的偏好藏起来。 中立地描述——说"评审一下这段代码",而不是"这段代码写得不错吧?"。
⚠️ 别这么说:别把任何"恰好让你高兴的错误答案"都叫讨好倾向。不做上面那个诊断测试,这个词就比"错了"多不了任何信息。
💬 对话示例:
"它说我这个重构方案非常好,然后我问了句'你确定吗',它就把整个方案收回去了。"
"典型讨好——先顺着你,是因为你听起来很自信;后撤,是因为你听起来在怀疑。方案的质量没变,你的语气变了。清空,不带任何倾向地重新问一遍。"
幻觉(Hallucination)
一句话:自信但错误的输出。分两种,成因不同,解法还相反。
| 类型 | 错在哪儿 | 原因 | 解法 |
|---|---|---|---|
| 事实型(Factuality) | 编造或搞错关于世界的事实——一个不存在的函数、错误的 API 签名、假引用 | 参数知识有洞,常见于超过知识截止日的部分 | 把正确的上下文知识加载进来 |
| 忠实型(Faithfulness) | 输出偏离了已经加载进来的上下文知识、你的指令、或者它自己之前的推理 | 注意力衰减,在变笨区里会恶化 | 清空或压缩 |
"下一个词元预测"这机制,不管底层事实是不是真的,都能产出流畅的输出——模型内部没有"我不知道"这个信号,所以一个编出来的方法,跟一个正确的方法,是用同一副很有把握的口气说出来的。 幻觉代码从构造上就是"看起来合理"的:它就是那个 API 如果存在的话应该长成的样子——正因为如此,它才能骗过粗看一眼的评审,只在跑起来的时候才挂。
你必须分清自己面对的是哪一种,因为治一个的药会让另一个更糟:
- 事实型 = 知识缺失,解法是加上下文——文档、类型定义、那个文件。
- 忠实型 = 知识在,但没抢过注意力,解法是减上下文。
把忠实型误诊成事实型,你会再粘一批文档进去,上下文变大,漂移更严重。 所以:它搞错一件事的时候,先确认正确的信息是不是早就在上下文里了,再决定你面对的是哪个问题。
⚠️ 别这么说:别把"幻觉"当成"错了"的同义词。不说清楚是哪一种,这个词就没有诊断价值。
💬 对话示例:
"它幻觉出一个 schema 上根本不存在的
parseAsync方法。""事实型还是忠实型?"
"我粘给它的文档里有这个方法——它就是到第 40 回合之后不读了。"
"那就是忠实型。压缩重来,别再贴文档了。"
参数知识(Parametric knowledge)
一句话:模型从训练里"知道"的、存在参数里的东西。训练时就冻住了,模型看不见自己的参数,也改不了。
细节都在这次挤压里丢了:几十亿条事实塞进固定数量的参数里,罕见的那些就糊了。这是它在常见话题上很流利、在冷门话题上开始编的根源。跟上下文知识是一对。
参数知识不是以"事实"的形式存的。 训练从不给模型一个可以查的数据库;它只是调参数,调到模型能很好地预测文本为止——而一个能把某话题的文本预测得很好的模型,表现得就像它懂这个话题。
可靠性跟它在训练数据里出现的频率挂钩:有上百万例子的主题,它复现得很准;只有寥寥几例的主题,它就按"相似的主题长什么样"去猜。复现和猜测,对模型来说是同一个过程,它分不清自己在干哪个。 于是一个编出来的答案,跟一个正确的答案,流利程度一模一样。 幻觉就是它猜错了。
参数知识还会过期。 参数在知识截止日那天停止变化,所以那之后发布或改名的库,在它脑子里根本不存在;改过的 API,它记的还是老样子。
这两种缺口——太冷门和太新——解法是同一个:知识没法加进参数,只能作为上下文知识提供。
💬 对话示例:
"它写 React 写得完美,但一碰到我们内部 SDK 就开始编方法。"
"React 在参数知识里密度极高——训练数据里有上百万例子。你们 SDK 没有,所以它只能填一个看起来像的形状。把 SDK 文档加载进上下文。"
知识截止日(Knowledge cutoff)
一句话:这个日期之后,模型就没有参数知识了。之后出现的库、API、事件,全是编造陷阱——除非你把它们的文档作为上下文知识加载进来。每个模型发布时都自带一个截止日。
为什么会有截止日?因为模型是这么造出来的:训练把某一时刻的文本快照烤进参数,然后参数冻住。
而模型不知道自己的知识有边界。 被问到截止日之后的事情,它不会说"这个我不知道",它会从自己知道的、最近的东西往外推。 这正是这个陷阱安静的原因:照着旧版库写出来的代码看着很合理,往往还能编译,只在真正改动过的那些地方挂掉。
解法永远一样:把当前信息弄进上下文。 加载 changelog、指向你装的那个版本的类型定义、或者让它上网去读文档。上下文里有的东西,永远压过参数里没有的东西。
💬 对话示例:
"它一直写 v3 的 SDK 语法,我们都 v5 了。"
"v5 是在知识截止日之后发的。把 v5 的 changelog 作为上下文知识加载进去,否则它会一直照着参数里那个旧版本编。"
上下文知识(Contextual knowledge)
一句话:智能体现在能直接从上下文里读到的事实——你的任务、它读进来的文件、工具结果、会话开始时加载的
AGENTS.md。跟参数知识是一对:参数知识是"回忆"出来的,上下文知识是"照着读"的。
智能体基于上下文知识干活的时候,幻觉会少得多——答案就在眼前,不用从模糊的记忆里往外刨。
两种知识里,只有上下文知识是你说了算的。 参数是冻住的,所以想给模型它不具备的知识——一个内部 SDK、一个截止日之后发布的库、一个昨天才做的决定——唯一的办法就是放进上下文。 大量 AI 编程的实操工作,最后都归结成这一件事:在它需要的时候,把对的事实摆到它眼前。
两者冲突时,通常上下文赢。 你把当前 API 文档贴进去,它会照着文档走,而不是照着它脑子里那个过期的记忆——不过旧版本还是可能渗出来,尤其在长会话的深处。 如果文档明明加载了它还一直退回旧写法,那就是参数知识漏过了上下文知识;把纠正再说一遍、或者挪到离当前工作更近的地方,会有帮助。
跟参数知识不同,上下文知识是要花钱的。 加载进窗口的每一样东西都要花词元、都要抢注意力预算,所以加载得更多不自动等于更好——目标是"相关的事实"在窗口里,不是"所有的事实"。
什么时候用这个词:只在跟参数知识对照的时候用,其他时候直接说上下文就行。
⚠️ 别这么说:别叫"工作记忆"。上下文知识是此刻在窗口里的东西;记忆系统是跨会话往窗口里送东西的机制。两个尺度,别混。
💬 对话示例:
"为什么我贴了文档它就写得准,不贴就开始编?"
"贴了文档,那是上下文知识——照着纸念。不贴,就是参数知识,那些冷门接口全是糊的。"
注意力关联(Attention relationship)
一句话:模型预测每个词元时,都会把上下文里所有其他词元考虑一遍——有的影响很大,有的几乎没影响。两个词元之间的这种配对关系就叫注意力关联。 上下文有 N 个词元,就有大约 N² 个关系。
模型的"理解",就活在这些配对里。 它能消解代词,是因为"她"和"小芳"之间的关联很强;它能用正确的参数调一个函数,是因为调用处和它前面读到的函数定义之间的关联在起作用。这些没有一样是查表查出来的——每次请求都要为每一对重新算一遍。
N² 这个数字值得停一下,因为它涨得比直觉快得多:
| 上下文大小 | 配对数(约 N²) |
|---|---|
| 1,000 词元 | ~100 万 |
| 10,000 词元 | ~1 亿 |
| 100,000 词元 | ~100 亿 |
而且每一对还不止算一次。模型有多个注意力头(前沿模型的具体数量没公开,几十到上百是个合理猜测),每个头都要把每个关系自己算一遍——上面表格里的每一个配对,都要在每个头上重复一次。配对数量非常恐怖。
但其中只有极少数的关系对某个具体任务是有意义的。 你的指令和它管着的那段代码之间的关系,属于"算数的那几个";池子里几乎其他所有东西都是噪音。而且这两者增长速度不一样:有意义的关系数量大致不变,而关系总数随上下文平方增长。 1,000 词元时,你关心的那个配对是百万分之一;100,000 词元时,它是百亿分之一。这就是注意力预算底下的那道算术题,而注意力衰减,就是"有意义的关系分到的份额变得太薄"时的那种感觉。
💬 对话示例:
"它一直把 diff 里那两个
user搞混——听起来我们进变笨区了。""是啊,每个调用处和它对应声明之间的注意力关联在互相打架——词元长得一样,绑定关系不同。把其中一个改名,配对就清楚了。"
注意力预算(Attention budget)
一句话:每个词元能分给上下文里其他内容的"影响力"是有限的。在一个关系上花重了,留给其他关系的就少了。这个预算是按词元算的,不会随上下文变大而变大——这就是长会话会稀释的原因。
把它想成信号和噪音。 你的指令是一个音量固定的信号;上下文窗口里其他每个词元都是来抢它注意力的杂音。指令本身一点没变小——它还在那儿,一个字都没少——但随着上下文变长,它周围的房间越来越吵,信噪比就掉了。 一条在 1 万词元时是全场最响的指令,到 15 万词元时就成了背景嗡嗡声。
这就是注意力衰减背后的机制:模型不是忘了,是信号被噪音盖住了。
症状看起来像"不听话":智能体早先答应了一个约束,后来慢慢偏离,你把约束再贴一遍也只能管一小会儿。问题不在指令,在窗口里其他所有东西在跟它抢。
你能控制的是"往上下文里放什么"。 跟任务无关的内容不是中性的——它是盖在所有有用内容之上的噪音。 保持窗口小,累积的上下文不再值回票价时清空,重要的约束反复申明,而不是指望提一次就能一直生效。
💬 对话示例:
"为什么它一直无视我在最上面贴的那个 schema?"
"我们早进变笨区了——每个词元的注意力预算是固定的,可上下文一直在涨。schema 上那点信号,现在要跟几千个更新的词元竞争。"
注意力衰减(Attention degradation)
一句话:随着会话变长,每个词元的注意力预算要摊给更多竞争者。任何一个有意义的关系上的信号都被摊薄,无关上下文的噪音挤了进来。同一个模型、同一套参数——只是同一盘菜要多喂更多张嘴。
它表现为模型在会话中途变笨:坚持了一个小时的约束开始松动,开始重复问已经告诉过它的事,写出的代码无视了它早先读过的文件。模型什么都没变,唯一的变量是它现在要照顾多少上下文。
它是渐进的,这正是难在会话内部发现的原因。没有报错、没有阈值,每一回合只比上一回合差一点点,等到错误明显的时候,你已经在变笨区里待了一阵了。
恢复的办法是删上下文,不是加。 把被无视的指令再贴一遍,是往同一个拥挤的窗口里又加了一个竞争者,只能管一小会儿。真正管用的是:清空后只重新加载任务需要的东西,或者压缩,或者交接给一个新会话。
把"指令跟不上了"当成上下文长度的信号,而不是模型能力的信号。
💬 对话示例:
"它深陷变笨区了——在编类型文件里根本没有的泛型。"
"注意力衰减。类型定义还在上下文里,但它们身上的信号已经被后来加的东西埋了。清空重载。"
聪明区(Smart zone)
一句话:会话早期,智能体在一个"聪明区"——清醒、专注、记得住。随着会话变长,它漂进"变笨区":更糙、更健忘、错误更多,忠实型幻觉也更多。同一个模型、同一个外壳——只是上下文变多了。
("变笨区 dumb zone"就是这个说法的反面,两个词是一起的。)
下滑是渐进的,所以很容易漏掉。没有报错、看不到分界线,它只是开始表现得稍差一点,然后明显更差。常见的征兆:忘了你二十回合前给的指令、重复一个早就纠正过的错误、或者自信地陈述一个跟上下文矛盾的东西。因为滑坡是平滑的,通常的反应是硬扛、反复解释——而这会加更多上下文,让问题更糟。
这两个区跟上下文窗口的上限不是一回事。 窗口还剩大半的时候,会话可能就已经深陷变笨区了:上限是外壳拒绝继续的地方,而质量在那之前早就掉下来了。按聪明区规划,别按窗口规划——一个任务的实用预算,是智能体还干得好的那段词元数,不是它技术上能装多少。
聪明区是份预算,不相关的活也在花它。 会话里做的每个任务都在消耗词元,所以在同一个会话里开第二个任务,等于让它从更靠近变笨区的地方起步。一个会话一个任务,每个任务都能用上会话最清醒的那段。当一件事比一个聪明区还大时,就切开:在自然边界处交接或压缩,让新会话去干下一段。
参考数值:在当前的前沿模型上,变笨区通常从 12.5 万~15 万词元左右开始——不过这一点有争议。会话一臃肿就清空或压缩,别硬扛。
💬 对话示例:
"前三个组件它做得漂亮,第四个彻底搞砸了。"
"你出聪明区了——模型还是那个模型,只是现在深在变笨区里。压缩一下重新加载计划,下一个组件就能落地。"
第 5 章 · 交接
清空(Clearing)
一句话:结束当前会话,开一个新的。下一条消息面对的是一个空会话、一个空上下文窗口。
清空是治"脏上下文"的药。 一个会话会累积一切:失败的尝试、走错的弯、过期的工具结果、废弃的计划。模型每一回合都要把这些重读一遍,糟糕的历史会拖累新工作。 长会话的深处,智能体变得越来越含糊、越来越不听话——你明明说清楚的指令被无视,质量下滑,催它"好好干"也没用,因为它正蹚着的那片噪音还在它的上下文里。清空把这堆噪音删掉。
清空不会抹掉对话记录。 大多数外壳把会话历史存在你本机上,记录还在,你可以读、可以恢复。消失的是智能体的工作状态:模型是无状态的,所以新会话对旧会话一无所知。 如果这个会话里有下一个会话需要的决定或进展,先让它写一份交接文档,然后新会话从那份文档开始。
对比压缩:压缩是把会话总结成一段摘要塞进新上下文;清空是更粗暴的工具——什么都不带过去,包括垃圾。
💬 对话示例:
"它卡在那个挂掉的测试上死循环了。"
"直接清空——拿计划文档和那个测试文件开个新会话。跟现有上下文较劲没意义。"
交接(Handoff)
一句话:把智能体的上下文从一个会话转移到另一个会话。方式各异——写一份交接文档、在内存里总结(压缩)等等。跟清空不同(清空是一点都不带)。
接收方会话从零上下文开始——模型是无状态的,旧会话的任何东西对新会话都不可见。下一个会话需要什么,就必须明确地带过去;其余的全没了。
"没有回头路"是塑造交接方式的那个约束:新会话没法回头问旧会话"你那话什么意思",所以带过去的东西必须自己站得住。
| 方式 | 形态 | 特点 |
|---|---|---|
| 交接文档 | 写在环境里的文件 | 你能先读、先改,再让任何东西依赖它;可以复用于很多会话 |
| 压缩 | 上下文窗口里的摘要 | 自动且便宜;难以检查;只喂给一个后继会话 |
一次糟糕的交接,看得见的失败是"翻旧账":新会话把旧会话已经定下来的决定重新拿出来吵一遍,因为带过去的东西记了"决定了什么",没记"为什么"。
评判一次交接好不好,就看一个零上下文的会话拿到它能干成什么。
💬 对话示例:
"规划会话越来越重了——我是不是接着往下干就行?"
"做个交接。把决定写进文档,清空,然后开个新会话读着它做实现。"
一手资料(Primary source)
一句话:原始形态的真值来源——代码、会话记录、原始日志、真实的 API 响应。不是对这件事的描述,就是这件事本身。 跟二手资料是一对。
想知道你的代码库干了什么,代码就是一手资料。 文档、架构图、README 全是对它的描述——写下的那一刻是准确的,之后就按自己的节奏过时了。 当智能体自信地陈述了一件关于你项目的错事,要问的是:它是基于哪个来源在干活? 读了文档的智能体,会继承那份文档的过时程度;读了代码的智能体,读的是当下的事实。
不把一手资料当默认选项的原因,是成本。 把它加载进上下文窗口很贵——完整的文件、完整的记录,每个词元都要按输入计费,还要抢注意力预算。你花这份钱买到的是"完整":没有任何东西被别人的判断预先过滤掉。一份上个月写的摘要,装不下今天才发现重要的那个细节;一手资料还装着。
需要精确的时候去找一手资料——确切的签名、真正的报错、抛异常的那一行。管理上下文的很大一部分工作,就是判断什么时候该为一手资料付这个钱、什么时候二手资料就够用了。
💬 对话示例:
"智能体说重试逻辑是指数退避的,可我看着它在死命打那个接口。"
"它是从设计文档里读来的。把它指向真正的重试模块——涉及行为的时候,用一手资料。"
二手资料(Secondary source)
一句话:对一手资料的转述,隔了一层——描述代码的文档、描述会话记录的摘要、描述搜索结果的报告。比它描述的那份原始资料便宜,但天生有损:写它的人决定了什么重要,而他丢掉的东西,对只拿着摘要的读者来说是不可见的。
上下文工程里很大一块活,就是在制造二手资料。 压缩把会话历史变成喂给下一段的摘要;子智能体把噪音很大的搜索烧在自己的上下文里、只回报一小段报告;交接文档把一个会话的决定浓缩成下个会话读的文档;记忆系统把一个会话学到的东西提炼成笔记。每一笔都是同一个交易:用保真度换空间。
二手资料会以两种方式失效。 一是有损——压缩摘要弄丢了 schema 那个决定,报告里没提那个边界情况。二是漂移——一手资料变了,转述没跟着变,于是文档用本季度的自信描述着上季度的架构。当智能体基于一份以任一方式失效的二手资料干活时,它会自信地基于错误信息工作;解法是把它送回一手资料。
但这两种失效都不意味着二手资料是个错误。 上下文窗口是有限的,一手资料是贵的;没有摘要、报告和交接文档,任何大一点的活都装不进来。 本事在于:知道哪些细节扛得住这种损失,扛不住的,就拿一手资料去核对。
一份做得好的二手资料,会带一个指回原件的上下文指针——摘要里点明它来自哪份记录、文档里点明它描述的是哪个文件——这样当转述不够用时,读者可以顺着指针去查,而不是硬着头皮用那份有损的东西。
💬 对话示例:
"交接文档说鉴权做完了,可新会话一直发现 token 刷新是坏的。"
"文档是二手资料——上个会话写的是它当时相信的事,不是事实。让新会话跑一遍鉴权测试,信一手资料。"
交接文档(Handoff artifact)
一句话:用作交接载体的文档——由一个会话写进环境、给另一个会话读。方案文档、工单、计划文档,全都是交接文档。
为什么要写?因为模型是无状态的,会话里的一切在清空时就消失了。 决定、约束、做了一半的计划——全都随着承载它们的上下文一起没了。环境是持久的。把重要状态写进文件,就是把它挪到下一个会话能读回来的地方。
交接文档是一份二手资料——对会话工作的转述,不是工作本身。这既是它能小到可以给新会话做简报的原因,也是它可能误导新会话的原因:它记录的是"写它的那个会话相信的事",它漏掉的、搞错的,读者是看不见的。凡是重要的论断,下一个会话都应该拿一手资料(代码、测试)去核对,而不是直接继承。
一份好文档,是写给一个零上下文的会话读的。 具体的文件路径,而不是"我们讨论过的那个文件";决定了什么以及为什么,这样下个会话不会翻旧账;什么做完了、什么还没做。 写的时候告诉那个会话文档的用途会很有效:"写一份交接文档,给一个对此一无所知的新会话看。"
另一个载体是压缩(在内存里总结)。交接文档有两个优势:它躺在磁盘上,任何东西依赖它之前你能先读、先改;而且它可以复用——同一份方案文档能同时给五个并行会话做简报。
💬 对话示例:
"规划智能体和实现智能体之间我该怎么切?"
"让规划方写一份交接文档——文件路径、决定、约束。实现方的会话一打开就拿到一个指向这份文档的指针,然后拿它当任务简报干活。"
方案文档(Spec)
一句话:描述一件跨多次会话的工作的交接文档——要造的是什么,不是每个会话怎么干自己那份。它会随着工作推进而变形。由工单组成。
方案文档之所以存在,是因为会话是一次性的,而大活不是。 任何需要超过一个上下文窗口的工作,都需要在上下文之外有个家——一个在清空之后还活着的地方:仓库里的一个文件、一个 GitHub issue、或者智能体能够到的工单系统。方案文档就是那个家:目标、约束、到目前为止的决定、以及工单及其状态的清单。 任何新会话读一遍就知道工作进展到哪儿了,而不用继承上一个会话累积的噪音。
方案文档有几种常见的写法,基本继承自团队本来就怎么写东西:
- 产品需求文档(PRD):偏用户视角的"是什么"和"为什么"——功能、行为、验收标准。
- 设计文档 / RFC:偏技术——选了哪个方案、否掉了哪些替代、权衡是什么。
- 小一点的:一个带工单清单的
plan.md,对多会话的小功能来说干的是同一个活。
风格不重要,角色才重要:对智能体来说,这三种是同一个东西——它每次会话开始时读的那份持久的意图声明。
💬 对话示例:
"这事儿一个会话能搞定吗?"
"不行,写成方案文档——拆成工单,每个工单跑一个会话。想在单个上下文里干完,你一半都没走到就撞进变笨区了。"
工单(Ticket)
一句话:把一次会话的活圈起来的交接文档。可以独立存在,也可以挂在方案文档下面当它的子项。工单之间可以互相阻塞,所以工作顺序是从依赖关系里长出来的,而不是一条线性计划。
定义性的约束是尺寸:一个会话。 一个工单应该能在会话漂出聪明区之前干完——而且这个约束是可以检验的:如果你的会话经常在活干完之前就开始退化,说明工单太大了,切小;如果每个会话大部分上下文都花在环境准备上、然后才干了五分钟的活,说明太小了,合并。
一份好工单,是写给一个没有其他上下文的读者看的。 目标、验收标准、指向相关文件和决定的上下文指针——够让会话直接开工,不用重新推导上一个会话知道的东西。
依赖图还顺带解锁了并行:互相独立的工单(图里的叶子节点)可以各自在自己的会话里同时跑——这是同时开多个智能体最有效的方式。
💬 对话示例:
"迁移的方案文档我从哪儿下手?"
"看工单依赖图——schema 改动阻塞回填,回填阻塞 API 切换。挑一个叶子节点,为它开一个会话。"
压缩(Compaction)
一句话:在内存里完成的一次交接:把上个会话的历史总结一遍,用这段摘要去开一个新会话。天生有损:会话记录是一手资料,摘要是二手资料——用细节换空间。 可以你手动触发,也可以由自动压缩触发。
机制是这样的:上下文窗口是有限的,长会话会把它填满——每个工具结果、每个读过的文件、每个走错的弯都留在历史里。太重的时候,外壳让模型把会话总结一遍,然后把原始历史丢掉,用摘要去开一个新会话。 没进摘要的东西,就从上下文里消失了。有些外壳会把这一点软化:旧记录留在磁盘上,并在摘要里留一个指向它的上下文指针——二手资料链回一手资料,摘要弄丢的细节还能靠重读原件找回来。
摘要是模型写的,所以你可以指挥它:"保留关于 schema 的决定"能让产出的东西更有针对性。时机也重要——在阶段边界压缩,方案定了之后,别在任务中途压。
对比清空:清空是全部丢掉、冷启动;压缩是想把要紧的带过去,清空是赌它们已经写在别处更好的地方了。
💬 对话示例:
"上下文越来越重了,我还有一轮测试要跑。"
"开始前先压缩——把必须活下来的东西写进压缩提示里,让新会话保留 schema 的决定、丢掉探索过程。"
自动压缩(Autocompact)
一句话:上下文窗口快满时,由外壳自动触发的压缩。
外壳盯着窗口有多满。越过阈值(常在 80% 左右)时,它暂停,让模型总结目前为止的会话,然后用摘要开一个新会话,工作接着往下走,像什么都没发生一样。
但确实发生了点什么。 压缩是有损的,而自动压缩是在一个你没选的时刻有损。 手动压缩发生在阶段边界,那时你能告诉模型要保留什么;自动压缩在任务中途触发,只要撞上阈值就发——可能正做重构做到一半,由摘要自己决定你的哪些决定值得保留。
典型症状:智能体自信地继续干活,但已经悄悄忘了一个你一小时前立下的约束,而你要等它的产出开始跟那个约束矛盾时才发觉。
防御办法是不让它触发。 盯着上下文指示器,在自然边界处手动压缩;或者把决定写进磁盘上的计划文档或交接文档,那里没有任何摘要能弄丢它们。 大多数外壳还允许你调这个缓冲区——把阈值提前或推后,或者干脆关掉自动压缩——这样你就能调它在触发前给你留多少余量。
💬 对话示例:
"它好像不记得我们早先关于 schema 定了什么。"
"自动压缩在两回合之间触发了——早期的决定被总结了一遍,我们大概丢了点东西。重新加载计划文档,或者下次手动压缩,这样什么被保留由你说了算。"
第 6 章 · 记忆与引导
记忆系统(Memory system)
一句话:一套试图让智能体跨会话"有状态"的机制。在会话过程中把信息写进环境,在后续会话开始时重新加载进上下文窗口,这样你清空了会话,连续性还在。
它有两条路径:
- 写:会话过程中,智能体把学到的东西——你说的一条偏好、关于项目的一个事实——写成环境里的文件。
- 读:会话开始时,外壳把这些文件(或它们的索引)加载回上下文窗口。
很多外壳自带记忆系统(Claude Code 的 /memory 就是一个),你也可以自己搭一个:一个笔记目录 + AGENTS.md 里一句"去查一下这个目录"。
所有"常驻内容"都有的权衡,它也有。 记忆会累积,所以大多数系统只加载一行索引,正文留在上下文指针后面,而不是全量内联。而且记忆是二手资料,会漂移:三月记下的一条事实,到六月项目早就变了,它还会以同样的自信被加载出来。记忆系统需要修剪,跟 AGENTS.md 一样。
💬 对话示例:
"我一遍遍跟它说我们用的是 Postgres 不是 MySQL。"
"接个记忆系统——第一回合就把它学到的写进文件系统,会话开始时再加载回来。模型本身是无状态的,记忆层是伪造出来的连续性。"
AGENTS.md
一句话:环境里的一个文件,外壳在会话开始时把它加载进上下文窗口——项目给智能体的常驻简报。 这是个跨外壳的约定;有些外壳还有自己的变体(Claude Code 的是
CLAUDE.md)。
因为它自动加载,它是你避免跨会话重复自己的一种方式。 模型是无状态的:你在这个会话给的一条纠正,下个会话就没了——于是你不得不对每个新会话重复一遍"这个项目用 pnpm""测试要带那个 flag""那个目录是生成的别动"。当你就同一件事纠正了它两次,那条纠正就是 AGENTS.md 的候选。
适合放进去的,是它没法从代码里推导出来的东西:构建和测试命令、代码库本身看不出来的约定、硬性约束("永远别改生成的那个 client")。简短、陈述句——它是简报,不是文档。
权衡在于:里面的东西每次都加载。 指令会累积,其中大部分对任何具体任务都是无关的,而一份很长的 AGENTS.md 既烧词元又自我稀释——上下文里的指令越多,模型对其中任意一条的遵守度就越低。
⚠️ 别这么说 / 别这么用:别把应该渐进式展开的内容塞进 AGENTS.md——里面的任何一行,每个会话、每一回合都要付词元成本,不管这次会话需不需要它。 一份编码规范应该放到技能包或上下文指针后面;AGENTS.md 只留那些到处都适用的几行。
💬 对话示例:
"为什么每个会话一上来就已经烧掉 4k 词元了?"
"看
AGENTS.md——有人把整份编码规范粘进去了,那本来该放在技能包后面。"
渐进式展开(Progressive disclosure)
一句话:只加载智能体当下需要的上下文,其余的留一个上下文指针。这个词借自 UI 设计——那里它的意思是"只给用户看跟他当前任务有关的控件,其余的收在一层点击后面"。
这个技巧存在,是因为上下文是双重成本:每个提前加载的词元,每一回合都要按输入词元付一次钱,而且不管智能体需不需要,它都要花注意力预算。
一份塞满了完整编码规范、部署手册、数据库约定的 AGENTS.md,会让它在这三件事上全都变笨——跟当前任务有关的指令,被无关的那些稀释了。征兆是:它无视了你明明知道在它上下文里的规则。 规则在里面,只是被埋了。
渐进式展开把这个反过来。 常驻层保持很小——每个主题一句话 + 一个指向详情位置的指针。 它在写组件的时候读编码规范,在部署的时候读部署手册,在修测试的时候两个都不读。技能包就是外壳里内置的实现:一段每会话都加载的简短描述,完整指令只在被触发时才加载。
💬 对话示例:
"我要不要把整份编码规范都倒进
AGENTS.md?""别——渐进式展开。把规范做成技能包,它真要写组件的时候才加载。
AGENTS.md里的东西每回合都要付词元钱。"
上下文指针(Context pointer)
一句话:一份文档里指向另一份文档的一句话,让智能体只在任务需要时才把它拉进上下文窗口。渐进式展开就是用它搭起来的。
用指针而不是直接内联内容,原因是成本。 指针在上下文窗口里占一行;它背后的文档可能有几千词元,但在智能体真的去顺着指针读之前,那些词元一分钱不花。 把一份 2000 词元的部署手册内联进 AGENTS.md,每个会话都要为它付钱;换成"部署流程:见 internal/deploy.md",只有真要部署的会话才会加载它。 任务对上了,智能体就用一次工具调用顺着指针去读。
一个指针要能用,得有两个部分:一个稳定的路径,以及足够的描述让智能体知道"什么时候值得去读"。一个光秃秃的路径是个"它没有理由去点"的指针——只写"见 internal/deploy.md"、不说明里面是什么,需要它的那个会话会直接跳过。写的时候要匹配任务出现的样子:"涉及发布、部署或回滚——先读 internal/deploy.md"。
一旦你留意,指针到处都是:AGENTS.md 里的行、技能包的描述(外壳加载描述,正文等在后面)、目录列表里的文件名、文档之间的链接。
指针还能把一份二手资料系回它所源自的一手资料——点明原始记录的压缩摘要、点明它所描述源文件的文档。这让二手资料的"有损"变得可恢复:当摘要不够用时,智能体顺着指针去读原件,而不是硬着头皮用摘要留下的那点东西。
⚠️ 别这么说:别叫"引用"——太干巴,传达不出"顺着它会拉更多上下文进来";也别叫"传送门"——太花哨。
💬 对话示例:
"
AGENTS.md越来越臃肿了。""里面大部分应该是指针,不是内容。常驻规则留着内联;部署手册和编码规范做成技能包,只留一个上下文指针。"
技能包(Skill)
一句话:打包成一份的一项可教能力——把"怎么把一件事做好"的指令和相关资源放在一起,存在环境里,等一个上下文指针在任务来时把它拉进上下文窗口。这是外壳里"渐进式展开"的基本单位。
技能包是个开放标准,定义在 agentskills.io——最初由 Anthropic 开发,之后被主流外壳普遍采纳,所以你写一次,到处都能用。 格式是一个文件夹,里面:
- 一个
SKILL.md文件——元信息(至少要有名字和描述)+ 指令本身 - 可选:智能体能跑的脚本
- 可选:指令会指向的模板和参考资料
默认只有名字和描述在上下文里。 当智能体的任务匹配上了,它才把其余的加载进来。在那之前,技能包几乎不占地方——不管它的完整指令有多长,就那么一两句话的词元。
这正是它跟 AGENTS.md 的区别:后者不管什么任务,每个会话都加载。技能包只在某类活出现时被读——发版时、搭新服务时、写迁移时——其余时间被忽略。
⚠️ 别这么说:别叫"工具"。工具是智能体去"调用"的;技能包是它去"读"的指令。
💬 对话示例:
"部署手册放哪儿?"
"做成技能包——只在任务涉及部署时它才加载。放
AGENTS.md里的话,一个我们一周才用一次的东西,每回合都要烧词元。"
子智能体(Subagent)
一句话:由一个智能体通过工具调用派生出来的智能体。跑在自己的会话里、用自己的上下文窗口,最后回报一个工具结果。 跟交接不同——父级明确等着它回来;交接没有回头路。 它不能再生子智能体,这棵树只有一层深。子智能体的存在是为了隔离上下文,不是为了搭层级。
重点是把吵闹的活挡在父级上下文之外。 一次大范围搜索、或者一场读很多文件的长途跋涉,会产出成页的工具结果,其中大部分只在找到答案的那一瞬间有意义。 在父级里跑:所有这些会在剩下的会话里一直待在父级上下文中。放到子智能体里跑:噪音填满的是一个用完就丢的窗口,最后落到父级上下文里的只有那份报告。
而那份报告是一份二手资料:父级拿到的是子智能体对它发现的东西的转述,不是原始结果,所以报告漏掉的任何东西,对父级来说是不可见的。
子智能体还能并发跑——父级可以就几件互不相关的工作同时派出好几个。
💬 对话示例:
"grep 的结果快把我的上下文撑爆了。"
"派个子智能体去做这次搜索——它会在自己的窗口里烧掉那堆噪音,只回报你真正需要的那两个文件路径。"
第 7 章 · 干活的方式
人在环中(Human-in-the-loop)
一句话:一种工作方式——人陪着智能体一起跑这个会话,实时评审、纠偏、协作。人是在场的、参与其中的,不只是给单个操作盖个章。
对照面是挂机(AFK):智能体无人值守地跑,你事后才评判结果。人在环中的价值在于:在问题还便宜的时候抓住它。 你看到它伸手去拿错文件、误解了需求、或者开始钻死胡同,一句话就把它掰回来——而不是二十分钟之后才发现,那二十分钟信心满满的工作全建立在那一个错误上。 智能体并不可靠地知道自己在跑偏;没人看着的时候,它们倾向于硬着头皮往前推,而不是停下来问。
哪种方式合适,取决于活的类型:
- 适合挂机:定义清晰、风险低、容易验证的任务。
- 适合留在环里:含糊的、不可逆的、或者做完你很难评审的——schema 迁移、棘手的设计决策、任何碰生产环境的东西。
判断标准本质上就是两句话:走错一步有多贵?你多晚才会发现?
有些活天然就得在环里,因为你的反应本身就是输入。 连环追问只有你在那儿回答问题时才成立;快速原型只有你在那儿对实物作出反应时才成立。
留在环里的成本是你的注意力,那是最稀缺的资源。 跟智能体协作变熟练的一部分,就是安全地把更多活移出环外——用计划、用自动检查、把人工评审放在最后,而不是全程监督。
💬 对话示例:
"这个能挂机跑一晚上吗?"
"不行,schema 迁移,留在环里。我要看着每一步,它要是挑错了回填的来源列,我得能拦。"
挂机(AFK)
一句话:Away from keyboard。你开一个会话然后走开,让智能体无人值守地跑。 这是 AI 编程的吞吐倍增器——你睡觉、吃饭、干别的事的时候,可以同时跑好几个挂机会话。通常需要宽松的权限模式 + 沙箱才安全。
你不在的时候,它处理含糊之处的方式不一样。 你盯着的时候,一个含糊的决定会变成一个提问,你来回答;你一走开,它就选个默认值继续往下走,而后面每个决定都建立在这个猜测之上。
典型的失败是:你回来,看到几个小时已经完成的、自信满满的工作,全建立在头十分钟里做错的一个选择上。 这份工作不糙——它是自洽的,只是自洽在一个错的东西上。
既然跑的过程中你给不了输入,就在前后给:
- 跑之前:先把含糊的地方解决掉——一次连环追问、一份写好的方案文档——让它没那么多空要自己填。
- 跑的过程中:自动检查和自动评审,代替你没给的那份注意力,能机械判定的问题快速失败。
- 跑完之后:产物得是可评审的东西——一个 PR,不是已经合进去的改动。
挂机不取消人工评审,它是把所有评审推迟到最后——这也是为什么最后送到的那个东西必须值得评审。这也是 AX 在挂机场景下最重要的原因:没人看着的时候,环境是智能体唯一能得到的支持。
⚠️ 别这么说:别叫"后台智能体"——那个说法把重心放在机器上("在后台跑着"),而 AFK 说的是人的模式("用户走开了")。AFK 点明了真正要紧的那个事实:没人看着。
💬 对话示例:
"我要挂机跑了——三个沙箱智能体做这次重构,早上来看 PR。"
"绕过权限模式?"
"对,文件系统只读,不出网。"
自动检查(Automated check)
一句话:跑在环境里的确定性验证——测试、类型检查、lint、构建、pre-commit 钩子。非过即挂,不带判断。 这是智能体能自己纠正自己、不用惊动任何人的信号。一个 flaky 的测试是"坏掉"的检查,不是"非检查"——自动检查按设计就是确定性的。
自我纠正是个循环:智能体做个改动,用一次工具调用跑检查,失败输出落进它的上下文窗口——一个带文件名和行号的类型错误、一个带期望值和实际值的失败断言。这些东西足够它修好问题,然后再跑一次检查,一圈一圈直到通过,全程不需要人。 确定性是这个循环可信的原因:同样的代码永远给出同样的判决,所以"通过"是有意义的。 一个 flaky 的检查会毒害这个循环——它会去"修"本来没问题的代码,或者重试着重试着重过了本来真失败的检查。
这就是为什么好的检查是代码库 AX 的一大块。 在一个有严格类型、快速测试套件和 linter 的仓库里,智能体在你看到之前就抓到了自己的大部分错误;在一个什么都没有的仓库里,它产出什么就交付什么。 这个差别在挂机时最要命——跑的过程中,检查是唯一在发生的验证。
但检查只能抓到它断言了的东西:检查全绿意味着"被断言的那些属性成立",不意味着代码是对的。 需要判断的那些缺口,是自动评审和人工评审的地盘。
⚠️ 别这么说:别叫"反馈循环"或"反向压力"——这两个都把检查和评审混在一起了。也别叫"测试"——测试属于自动检查,但不是所有自动检查都是测试。
💬 对话示例:
"挂机跑出来的代码一直是坏的。"
"沙箱里接了哪些自动检查?"
"就单元测试。"
"加上类型检查和 lint——有了这些,PR 落地之前它就能自己纠正过来。"
自动评审(Automated review)
一句话:让一个智能体评审另一个智能体的工作,常用不同的模型或不同的系统提示词。不确定性:它形成的是判断。可以跑在任何地方——PR 合并前、对提交历史做事后评审、或者在会话中作为子智能体跑。CI 里跑一个"LLM 当裁判"是自动评审,不是自动检查——决定类别的是这个断言"做了什么",不是它"跑在哪儿"。
跟干活那个智能体分开,是它有效的原因。 让写这份代码的智能体评审自己的工作,你几乎什么都得不到——产生这个 bug 的那个会话,也装着产生它的那段推理,它把自己的结论读回来,读成了确认。 而一个拿着全新上下文窗口的评审者没有这种依恋:它像陌生人那样看这个 diff,而评审依赖的正是这一点。 换个模型、或者换个评审专用的系统提示词,会进一步锐化这个效果——不同的盲区,而且系统提示词可以只聚焦你真正关心的东西(安全、API 契约、性能),而不是一句含糊的"看看有没有问题"。
它插在另外两层评审之间。 自动检查是确定性的,抓能机械断言的问题;人工评审最贵、最不好扩展。自动评审在中间:以机器的成本,抓住需要判断的问题——一个误导性的函数命名、一个漏掉的边界情况。因为它是非确定性的,它会漏东西、也会报假警;把它当成"在人看之前抬高下限的过滤器",而不是"取代人的闸门"。
⚠️ 别这么说:"AI 评审""智能体评审"——太含糊,分不清是不是指干活那个智能体自己。
💬 对话示例:
"挂机跑出来的坏 PR 太多了。"
"合并前加一步自动评审——换个模型、独立的系统提示词,只聚焦安全和契约变更。"
人工评审(Human review)
一句话:你读智能体产出的代码并形成判断。读 diff 或者读改动过的文件才算;读它"对自己干了什么"的描述不算——叙述不是产物。 那段描述是二手资料,还出自被评审的那一方;diff 才是一手资料,评审意味着读它。
智能体把产出的代码量抬高了,于是评审成了瓶颈。 一个有用的思路是把几种评审策略分层:自动检查抓机械性失败,自动评审抓可被描述出来的问题,人工评审留给只有你能判断的东西——这个改动是不是对的改动、这个路子跟代码库合不合、这东西到底该不该存在。
评审越早越便宜。 开工前读个计划、或者中途读个小 diff,几分钟;挂机跑完之后去挖一个已经成型的分支,要久得多。 把评审检查点放在哪儿,是个"人在环中"的决策,不是事后想到再加的。
⚠️ 别这么说:光说"代码评审"——分不清是人工的还是自动的。
💬 对话示例:
"挂机的产出我人工评审过了。"
"你读的是 diff 还是它的总结?"
"diff。总结里说它删了死代码——结果那个函数在一个生成文件里被调用了。"
氛围编程(Vibe coding)
一句话:一种工作方式——你不人工评审智能体的代码就接受它。 diff 被当成不透明的:重要的是程序跑不跑得起来,不是里面是什么。 自动评审和自动检查可能还在跑,氛围编程对这两者不表态。
这个词来自 Andrej Karpathy,他在 2025 年初造了这个词:你"彻底交给感觉"、"忘记代码的存在"——描述你想要什么,接受回来的东西,靠跑一下来判断。
氛围编程是用检查换速度。 读 diff 通常是智能体驱动的工作里最慢的一步,砍掉它就砍掉了主要瓶颈。 对那些失败代价很低的代码——原型、一次性脚本、内部工具——这是笔划算的买卖。风险随代码的寿命和重要性放大。
代价是后面才到的。 氛围编程攒出来的改动,会堆成一个没人读过的代码库,而且只验证过行为——所以任何行为不暴露的问题:写了进日志的密钥、漏掉的边界情况、悄悄搞错的数据处理,全都看不见地发出去了。第一次有人调试这个系统,就是第一次有人读这段代码。 人工评审没了之后,还在跑的那些自动验证——测试、类型、自动评审——就是代码穿过的唯一一道门。
⚠️ 别这么说:别把"氛围编程"当成"低质量 AI 代码"的同义词——这个词指的是评审立场,不是产出的代码质量。
💬 对话示例:
"它改的鉴权流程你读了吗?"
"氛围编程过了——登录还能用,我就查了这个。"
"push 之前把 diff 读了,在鉴权上凭感觉,就是密钥漏进日志的方式。"
共同设计图景(Design concept)
一句话:用户和智能体之间共同持有的、关于"到底在造什么"的理解,它独立于任何具体产物。Brooks 的术语(出自《设计原本》):对话、交接文档、代码,全都是试图捕捉或逼近这个图景的产物,但它们都不是图景本身。 图景的质量,是通过构建它的那场对话的质量被感受到的。
译法说明:原文 Design concept,也有人直译"设计概念"。本书叫共同设计图景,强调的是"你俩脑子里那幅画是不是同一幅"。
这个词命名的是一个熟悉的挫败感背后的那道缝:智能体写得完全是你要求的样子,可它还是错的。 通常的原因是你自己还没想清楚要什么——那幅图景在你自己脑子里就没画完,你的提示词捕捉到了你想明白的那部分,对没想明白的部分保持沉默。于是智能体用自己的假设填了那些空白,因为没什么可以对齐的东西。 没有任何东西坏了。只是没有共同的设计图景,因为还没有一个完整的图景可供共享。
判断图景是否共享,跟判断同事是否跟你对齐是同一个办法:对方开始用你会用的方式,回答你还没问出口的问题。 在那之前,工作就是对话——连环追问是这件事的刻意为之的版本——而过早写方案文档,只是把这种不一致固定进了一个更耐久的产物里。 图景还会随着你的学习而移动,产物落在它后面,这就是为什么一份忠实于上周理解的方案文档,仍然会误导这周的会话。
💬 对话示例:
"它写的完全就是我说的,可结果还是不对。"
"你们还没有共同的设计图景——它在用假设填空。继续聊,直到取消、退款、部分履约这几件事在你俩之间完全对齐了,再让它写方案。"
连环追问(Grilling)
一句话:一种跟智能体一起打磨共同设计图景的技术:智能体用苏格拉底式的方式采访你,一次一个决定,每个决定都给出一个推荐答案。 它把"赶紧出个计划"这件事慢下来——在图景稳定之前,不写任何交接文档。
这个技术存在,是因为智能体会静默地填空。 你让它拿两行提示词写份方案文档,它不会在你没做决定的地方停下来——它会挑个默认值写进去。结果看着很完整,而猜测和选择混在一起没法分辨,于是你发现得很晚:在评审时,或者在功能已经建好、用一种你从来没选过的方式处理某个边界情况时。连环追问把这反过来:不许猜,必须问。
它是个"人在环中"的技术:你的答案就是输入。 当某个问题没法在对话里回答——你得看到实物才行——那就切到快速原型。
💬 对话示例:
"它直接就去写方案文档了,取消逻辑还写错了。"
"先拷问它——让它在往文档里写任何东西之前,先把部分取消、退款、时序这些问题一个一个问你。在对话里解决比在代码里解决便宜。"
快速原型(Prototyping)
一句话:让智能体快速做个粗糙版本出来,用于"对话的保真度不够、你需要一个真东西才好谈"的时刻。
连环追问在对话里解决设计决策。对话便宜,但保真度低:有些问题没法用语言回答——一次交互摸起来什么感觉、这个 API 形状在真实调用代码里顺不顺手、这个布局在真实数据量下撑不撑得住。访谈撞上这么个问题,你诚实的答案是"我不知道,我得看一眼"。 过了这个点,讨论就开始打转了。这时候,让它把东西造出来,你看一看,带着答案回到对话里。
智能体把"造"的成本压低了,这才是这件事变得实用的原因。 一个过去要一天才能搭出来的粗糙版本,现在几分钟就有了,所以值得 routinely(常态化)去做。 它是个"人在环中"的技术:原型摆在那儿,是为了让你对它作出反应。
通常你看一眼不会就停。 跟原型迭代——反应、要求改、再反应——这样每一轮都对着真实产物解决一个决策,保真度比对话高。
原型也不一定非得全是糙的。 你可以把"你真正在评估的那几块"按生产质量来做,这样决策落地时,你反应过的那个组件或 API 可以直接搬进真正的代码库。 这让原型成为方案文档值得引用的第一手材料。
💬 对话示例:
"这个向导到底做成一页还是三步,我们吵了半小时了。"
"话解决不了——让它两种都做个原型。我们点一遍,五分钟就有答案了。"
开发者体验(DX)
一句话:Developer experience——一份代码库和它的工具链,让人把活干好有多容易。好的 DX 是:快速反馈、清晰的错误信息、能回答你真正那个问题的文档、一次就成功的环境搭建。这个词远早于 AI 编程,进这本词典主要是作为 AX 的对照。
DX 是人与代码库之间的交互,仅此而已。 两个受众最关键的区别是:人是有状态的,智能体是无状态的。 人把代码库学一次,然后每天带着这份知识来——这就是为什么糟糕的 DX 是可以活下来的:他们靠攒着 push 来绕开慢 CI,靠在群里问一次来绕开缺失的文档,靠"记住东西在哪"来绕开混乱的结构。这些变通办法会累积,最后团队在一个跟他们作对的代码库里依然高产。
智能体面对同一个代码库,一条变通经验都没有。 它跨会话无状态,每次都要从零重新学这个代码库——快速的测试套件和清晰的错误信息它享受得到,但它昨天想明白的任何事情都没了,除非被写进环境,而环境它只能通过工具结果感知。 这就是 AX 要命名的那道缝:DX 里在"开发者变成智能体"之后还剩下的那部分,加上一些人类没有的顾虑,比如保持上下文窗口空闲。
重叠意味着:投资 DX 常常能免费改善 AX——严格的类型、快速的测试、可预测的结构,对两边都有帮助。分歧意味着:并不总是能——一份漂亮的入职文档能帮人一整周,但对智能体一点用都没有,除非它能从 AGENTS.md 走到那份文档。
💬 对话示例:
"我们 DX 没问题啊——新人一周就能上手。"
"能上手,是因为那一周有人陪着他坐。智能体没有那一周,AX 得单独看。"
智能体体验(AX)
一句话:Agent experience——环境被收拾得有多适合智能体在这个代码库里干好活。这是 DX 面向智能体的对照面。当同一个智能体在 A 仓库表现很好、在 B 仓库表现很差——同样的模型、同样的外壳——差别通常在 AX。 直觉是怪模型或者重写提示词,但解法更常在仓库里。
好的 AX 有三个主要维度:
| 维度 | 好的 AX 长什么样 |
|---|---|
| 自动检查 | 快速、确定性的自动检查(类型、测试、lint),智能体不需要人就能自我纠正 |
| 架构 | 一个不用全读就能导航的代码库:结构可预测、大量行为藏在小接口后面、名字能说明这东西是干什么的 |
| 空闲上下文 | AGENTS.md、技能包、工具都保持精简,让上下文窗口的大部分留给任务本身,让智能体待在聪明区而不是淹死 |
AX 和 DX 有重叠——好的检查和干净的架构对两边都有帮助——但也有分歧。 人能忍受口口相传的部落知识、慢 CI、以及"计费模块去问 Sarah";智能体忍不了。智能体也不吃 IDE 悬浮提示和漂亮看板那一套;它们需要的是"以文本形式出现在工具结果里的失败"。 一份代码库完全可以 DX 很好、AX 很差。
⚠️ 别这么说:别把 AX 当成 DX 的同义词——两类受众需要的投资不一样。
💬 对话示例:
"这智能体在 API 仓库里写得很好,在前端仓库里写垃圾。"
"API 仓库有严格类型和快速测试套件;前端两样都没有,还常驻加载四十个技能包。这是 AX 的差距,不是模型问题。"
附:一页纸心智模型
七章读下来,其实就这几句话:
- 模型只会一件事:看着上下文,猜下一个词元。它不记事、不查资料、不知道自己不知道。
- 你能控制的只有上下文。 参数是冻住的,"教它"永远不是解法,"把东西摆到它眼前"才是。
- 智能体 = 模型 + 外壳。 行为不一样时,先怀疑外壳和你给的东西,别急着怪模型。
- 上下文窗口是预算,不是容量。 塞满不等于有用;堆得越多,关键信息越被淹没(注意力衰减 / 变笨区)。
- 钱花在请求次数 × 每次携带的上下文。 一个回合能展开成十几次请求,每次都重发整个历史。
- 它跨会话什么都不记得。 想让它记住,就写进环境(
AGENTS.md、记忆、交接文档)。 - 它出错分两类:不知道(事实型,加上下文)和不遵守(忠实型,减上下文)。治反了会更糟。
- 把活从"盯着干"移到"挂机干"的那套装备是:方案文档 + 自动检查 + 自动评审 + 沙箱。
五对最容易混的词:
| 别混 | 区别 |
|---|---|
| 上下文 / 上下文窗口 | 前者是"相关的信息"(质量),后者是"看到的那串词元"(容量) |
| 参数知识 / 上下文知识 | 脑子里的记忆 / 摆在桌上的资料 |
| 压缩 / 清空 | 带摘要过桥 / 什么都不带过桥 |
| 自动检查 / 自动评审 | 非过即挂的确定性验证 / 会形成判断的非确定性评审 |
| 工具 / 技能包 | 智能体去"调用"的函数 / 它去"读"的指令 |
原版:mattpocock/dictionary-of-ai-coding(英文,Matt Pocock) 本中文版在保留原意与结构的前提下做了重写:术语改用中文语境叫法,例子换成国内团队常见场景,并补充了中文分词、变笨区阈值等本地化说明。