本页收录 AI 编程(AI coding)相关术语,译自开源词典 Dictionary of AI Coding,按英文名字母顺序排列。文末附上此前整理的 RAG 相关术语。

AFK

离开键盘(Away from keyboard)。一种工作模式:用户启动一个会话(session)后离开,让智能体(agent)在无人看管的情况下运行。这是 AI 编程的吞吐量倍增器——当你睡觉、吃饭或忙别的事情时,可以并行运行许多 AFK 会话。为了安全,通常需要一个较宽松的权限模式(permission mode)外加沙箱(sandbox)。

当你不在场时,智能体处理歧义的方式会不同。当你盯着时,一个含糊的决策会以问题的形式浮现,由你来回答;而一旦你走开,智能体就会自行选择一个默认方案继续推进,之后每一个决策都建立在这个猜测之上。典型的失败是:你回来时发现智能体基于最初十分钟里做出的错误判断,自信满满地完成了几个小时的工作。这些工作并不潦草——它是自洽的,只不过是围绕错误的方向自洽。

既然你无法在运行过程中给出输入,那就在运行前后给。之前:提前解决歧义——通过一次拷问(grilling)会话、一份书面规格说明(spec)——这样智能体需要独自填补的空白就会更少。运行中:用自动化检查(automated check)和自动化评审(automated review)来替代你缺席的注意力,对那些可以机械捕获的问题快速失败。之后:运行以某种可评审的产物结束——一个 PR,而不是已经合并的改动。AFK 并不消除人工评审(human review);它只是把所有评审推迟到最后,正因如此,最后交付出来的东西必须值得评审。这也是为什么 AX 在 AFK 运行中最为重要——无人看管时,环境是智能体唯一能得到的支持。

避免: 使用「后台智能体(background agent)」——它以机器为中心(「在后台运行」),而非以人的行为模式为中心(「用户已经走开」)。AFK 点出了真正重要的事实:用户没有在盯着。

用法

「我正在 AFK 跑这个——三个沙箱智能体做重构,早上再评审这些 PR。」

「绕过权限(bypass permissions)?」

「对,只读文件系统(filesystem),无网络。」

Agent mode

一种预设,用于塑造智能体(agent)在运行时的行为方式——它把一个权限模式(permission mode)与注入到系统提示词(system prompt)中的行为指令捆绑在一起。例如:一个在遇到高风险调用时提示确认的默认模式;一个阻止编辑、引导智能体转向研究的 规划模式(plan mode);一个自动批准编辑的 接受编辑(accept-edits)模式;一个自动批准一切的绕过权限(bypass permissions)模式(口语上称 YOLO 模式)。这些模式可以在会话(session)进行中切换。

正是这种「捆绑」把模式与单纯的权限设置区分开来。权限模式只是一道闸门:它决定哪些工具调用(tool call)能通过。单有闸门只会产生一个「想编辑却不能」的智能体——它提出写入请求,被拦下,然后换另一种方式。而注入的指令消除了这种「想要」:规划模式不只是阻止编辑,它还告诉智能体它正处于规划阶段,于是智能体去阅读、发问、提出方案,而不是硬顶着闸门。闸门与引导指向同一个方向。

在实践中,你会随着任务过程中信任度的变化而切换模式。同一个任务可以经历多个模式:在方案尚在打磨时用规划模式,在最初几处精细编辑时用提示确认的默认模式,一旦智能体表现出它理解了改动就切到接受编辑,为在沙箱(sandbox)内进行 AFK 运行则切到绕过模式。切换模式不需付出任何代价:对话会精确地从原处继续,只是换了新权限和新指令。如果你发现自己在不看内容的情况下批准每一条提示,说明模式设得比你实际的信任度更紧;如果你不断拒绝编辑,说明它设得太松。

厂商术语: Claude Code 称之为「权限模式(permission modes)」,Codex 称之为「审批模式(approval modes)」——两者都早于行为捆绑的出现。

用法

「我只想要一个计划,它却一直在改文件。」

「切到规划模式——它会阻止写入并留在研究状态。」

「那待会儿的 AFK 运行呢?」

「用绕过模式,但只在沙箱里。」

Agent

一个被套上工具(tool)、系统提示词(system prompt)和上下文窗口(context window)的模型(model),它与用户轮流交互(turn)。Claude Code 是一个智能体。Cursor 是一个智能体。Claude.ai 是一个智能体。 智能体是你实际对话的对象——它是运转中的模型,为某个目的而配置。

与本词典中大多数术语不同,「智能体」并不指代某个机械部件。模型是一份参数(parameters)文件;执行框架(harness)是你可以指挥的软件。而智能体两者皆非——它是你正在与之交谈的那个单元。人们不断把 AI 拟人化,而智能体就是那个被拟人化的单元:你委派任务的对象,读你消息并作答的东西,「它又把构建搞坏了」里的那个「它」。当你说智能体做了某事,你指的是「模型加执行框架」做了这件事,但你把这个组合当作单一的行动者来称呼。

这个概念比这一波 AI 更古老。软件智能体——你委派一个目标、代表你行动的程序——与 AI 一样历史悠久。

避免: 「the AI」「the bot」(太含糊——它们掩盖了你指的到底是参数还是被套上执行框架的东西)。

用法

「这次迁移你用哪个智能体?」

「本地用 Claude Code,UI 部分用 Cursor——底层是同一个模型,执行框架不同。」

AGENTS.md

环境(environment)中的一个文件,执行框架(harness)会在会话(session)开始时把它加载到上下文窗口(context window)——它是项目对智能体(agent)的常设简报。这是一种跨执行框架的约定;某些执行框架也有自己的变体(Claude Code 的是 CLAUDE.md)。

因为它会自动加载,所以是避免在多个会话中重复自己的一种方式。模型(model)是无状态的(stateless)——你在一个会话中给出的纠正,到下一个会话就消失了,于是你不得不对每个全新会话都说明:项目用 pnpm、测试要带某个特定标志运行、某个目录是生成的不能碰。当你为同一件事纠正智能体两次之后,那条纠正就是写进 AGENTS.md 的候选行。

合适的内容是智能体无法从代码中推导出来的东西:构建和测试命令、代码库中不明显的约定、硬性约束(「永远不要编辑生成的客户端」)。简短而声明式——它是简报,不是文档。

代价在于其中的一切都会始终被加载。指令不断累积,其中大多数与任何给定任务无关,而一份冗长的 AGENTS.md 既消耗 token,又会稀释自身——上下文里的指令越多,模型越难可靠地遵循其中任何一条。

避免: 把本应渐进式披露(progressive disclosure)的内容放进 AGENTS.md——里面的任何东西每个回合(turn)、每个会话都要付出 token 代价,无论那个会话是否需要它。风格指南可以放在一个技能(skill)或上下文指针(context pointer)后面;AGENTS.md 只留那些处处适用的行。

用法

「为什么每个会话一开始就烧掉了 4k token?」

「看看 AGENTS.md——有人把整份风格指南粘进去了,而不是放在技能后面。」

AI

一个移动的标签,而非一种技术。「AI」不像模型(model)或 token 那样指代某个固定的东西——它指向计算机新近能够、且令人印象深刻地做到的任何事情。眼下它指向大语言模型。而它此前曾指向过非常不同的东西:

年代「AI」当时的含义
1950 年代符号推理——定理证明器、跳棋程序。
1960—70 年代基于规则的符号程序——ELIZA、SHRDLU。
1980 年代专家系统——数以千计手写的 if-then 规则,用以编码人类专业知识。
1990 年代博弈树搜索——深蓝击败卡斯帕罗夫(1997)。研究者们完全回避「AI」这个词。
2000 年代统计机器学习——垃圾邮件过滤器、推荐系统。仍以「机器学习」而非「AI」的名义出售。
2010 年代深度学习——图像识别(AlexNet,2012)、AlphaGo(2016)。
2020 年代大语言模型——ChatGPT(2022)让「AI」意味着聊天机器人。

这个指针依循一种已知的机制移动,有时被称为「AI 效应」:一旦某项技术稳定奏效,它就会被改名——它「只不过」是搜索、「只不过」是统计——而「AI」则向前滑动到下一个尚未解决的问题上。这一观察由来已久。Bertram Raphael 在 1971 年这样表述:「AI 是我们尚不知道如何用计算机妥善解决的那些问题的统称。」Larry Tesler 大约在 1979 年的版本是:「智能就是机器还没做到的一切。」

这就是为什么关于 AI 的讨论如此频繁地各说各话。诸如「AI 不会推理」或「AI 被过度炒作」这样的论断带有一个隐藏的时间戳——它可能说的是专家系统、2010 年代的图像分类器,或者上个月的 LLM,而每一种指涉都支持一个不同的结论。当一场关于 AI 的讨论陷入僵局,解法通常是把这个词换成实际所指的那个精确术语:模型、执行框架(harness)、智能体(agent),或它被给予的上下文(context)。

避免: 在任何技术论断中使用「AI」——请改用你所指的那个部件的名称。作为对这一实践的标签,「AI 编程」没问题;但「the AI is hallucinating(AI 在幻觉)」不行。

用法

「CTO 想知道 AI 能不能处理分诊队列。」

「在划定范围之前先翻译一下——她指的是一个套在执行框架里、能访问工单系统的 LLM。『AI』单独拿出来不是一份规格说明。」

Attention budget

每个 token 都有一份有限的影响力,可分配给上下文(context)中的其余部分。在某一个关系(attention relationship)上投入过多影响力,留给其他关系的就更少。这份预算是按 token 计的,且不会随上下文增长而增长,这就是为什么长会话(session)会稀释。

可以把它想成信号与噪声。你的指令是一个固定音量的信号;上下文窗口(context window)里的每一个其他 token 都是相互竞争的声音。指令本身从不会变轻——它仍在那里,一字不差——但随着上下文增长,它周围的房间变得越来越嘈杂,信噪比随之下降。在 10k token 上下文时最响亮的那条指令,到 150k 时就成了背景嗡鸣。这正是注意力衰退(attention degradation)背后的机制:模型并没有遗忘;是信号淹没在了噪声里。

其症状看起来像是不服从——智能体(agent)早先同意了某个约束,随后却渐渐偏离,重新粘贴该约束也只能短暂奏效。原因不在指令本身;而在于窗口里与之竞争的其他一切。

你能控制的是放进上下文里的东西。无助于任务的内容不是中性的——它是覆盖在所有有用内容之上的噪声。保持窗口小巧,当累积的上下文不再物有所值时就清空(clearing),并重述重要的约束,而不是指望它们早先被提及就一直有效。

用法

「为什么它总是无视我在开头粘贴的 schema?」

「我们已经深入笨区(dumb zone)了——每个 token 的注意力预算是固定的,但上下文一直在增长。schema 上的信号现在正与几千个更新的 token 竞争。」

Attention degradation

随着会话(session)增长,每个 token 的注意力预算(attention budget)会分摊到更多竞争者身上。任何一个有意义关系(attention relationship)上的信号都在缩小;无关上下文(context)的噪声则挤了进来。同一个模型(model)、同一份参数(parameters)——只是同一盘菜要喂饱的嘴更多了。这是聪明区/笨区效应(smart zone / dumb zone)的成因。

它表现为模型在会话中途变差:它遵守了一小时的约束开始松动,被告知过的事又重新发问,写出的代码无视了它早先读过的某个文件。模型本身没有任何变化——唯一的变量是它现在要在多大的上下文上分配注意力。

它是渐进的,这正是从会话内部难以察觉它的原因。没有报错,也没有阈值;每个回合(turn)只比上一轮稍差一点,等到松动明显时,你已经在笨区里待了好一阵子。

恢复的方式是移除上下文,而不是添加更多。重新粘贴被忽略的指令,只是往同一个拥挤的窗口里再添一个竞争者,且只能短暂奏效。有效的做法是:清空(clearing)后只重新加载任务所需的内容,或压缩(compaction),或交接(handoff)给一个全新会话。把指令遵循度下降当作关于上下文长度的信号,而非关于模型的信号。

用法

「它在笨区里陷得很深——在编造类型文件里根本没有的泛型。」

「注意力衰退。类型定义还在上下文里,但它们上面的信号被我们后来添加的所有东西埋住了。清空并重新加载。」

Attention relationship

在预测每个 token 时,模型(model)会把上下文(context)中的每一个其他 token 都纳入考量——有些权重很大,有些几乎为零。两个 token 之间的配对就是一个注意力关系,而有意义的配对(「她」与「Sarah」,或一个 getUser() 调用与它的 function getUser 定义)之间的相互影响,要大于无关的配对。一个含 N 个 token 的上下文,大约有 N² 个这样的关系。

这些配对正是模型表面上的「理解」所在之处。当它解析一个代词时,是因为「她」与「Sarah」之间的注意力关系很强。当它用正确的参数调用一个函数时,是调用处与它早先读到的定义之间的关系在起作用。这一切都不是查表得来的——它在每一次模型提供方请求(model provider request)中、为每一对 token 重新计算。

N² 这个数字值得细品,因为它增长的速度比直觉所暗示的更快:

上下文规模配对数(约 N²)
1,000 token约 100 万
10,000 token约 1 亿
100,000 token约 100 亿

每一个配对还不止计算一次。模型有多个注意力头(attention head)——前沿模型的确切数目未公开,但五十到一百是个合理的猜测——每个头都计算自己那一版的每个关系。因此上表中的每个配对都在每个头上被复制一遍。这是海量的配对。

对任何给定任务而言,这些关系中只有一小部分是重要的。你的指令与它所约束的代码之间的配对,是屈指可数的几个重要配对之一;池子里几乎其他一切都是噪声。而两者的增长速率不同:重要的关系大致保持恒定,而配对总量随上下文规模呈平方增长。在 1,000 token 时,你在意的那个配对是百万分之一;在 100,000 token 时,它是百亿分之一。这就是注意力预算(attention budget)背后的算术,而注意力衰退(attention degradation)就是当重要关系分得的份额太薄时的那种感受。

用法

「它老是在这个 diff 里把两个 user 符号搞混——听起来我们在笨区(dumb zone)了。」

「对,每个调用处与其声明之间的注意力关系正在跟另一个打架——同样的 token 形态,不同的绑定。给其中一个重命名,配对就会变清晰。」

Autocompact

当上下文窗口(context window)接近装满时,由执行框架(harness)自动触发的压缩(compaction)。

执行框架会监视上下文窗口的填充程度。当它越过某个阈值——通常在 80% 左右——执行框架就暂停,请模型(model)总结到目前为止的会话(session),并用这份摘要为一个全新会话播种。之后工作继续,仿佛什么都没发生。

只不过确实发生了些什么。压缩是有损的,而自动压缩是在一个不由你选择的时刻有损。手动压缩发生在阶段边界上,此时你可以告诉模型要保留什么。而自动压缩在任务中途触发,只要一碰到阈值——很可能是在重构进行到一半时,由摘要自行决定你的哪些决策值得保留。典型症状是:智能体(agent)自信地继续推进,却悄悄忘记了你一小时前确立的某个约束,直到它的工作开始与之矛盾,你才察觉。

防御之道是别让它触发。盯着上下文指示器,在自然边界处手动压缩,或把决策写进磁盘上的计划文档或交接产物(handoff artifact)里,那里没有任何摘要能弄丢它们。大多数执行框架也允许你自定义缓冲区——把阈值移前或移后,或彻底关闭自动压缩——这样你就能调节它触发前保留多少余量。

用法

「它好像不记得我们早先关于 schema 的决定了。」

「自动压缩在两轮(turn)之间触发了——早先的决策被总结掉了,我们肯定丢了些东西。重新加载计划文档,或者下次手动压缩,好让你来控制保留什么。」

Automated check

一种在环境(environment)中运行的确定性验证——测试、类型检查、lint、构建、pre-commit 钩子。通过/失败,不含判断。它是智能体(agent)无需牵涉他人就能据以自我纠正的信号。一个不稳定(flaky)的测试是一个损坏的检查,而不是「非检查」;自动化检查在设计上就是确定性的。

自我纠正以循环的方式运作。智能体做出一处改动,把检查作为工具调用(tool call)运行,失败输出落进它的上下文窗口(context window)——一个带文件和行号的类型错误,一个带期望值和实际值的失败断言。这足以让智能体修复问题并再次运行检查,如此往复直到通过,全程无需人类介入。确定性正是让这个循环可信的原因:同样的代码总是产生同样的判定,所以「通过」是有意义的。一个不稳定的检查会毒化这一切——智能体会去「修复」本来没问题的代码,或者越过一个真实的失败反复重试。

这就是为什么好的检查是一个代码库 AX 的一大组成部分。在一个有严格类型、快速测试套件和 linter 的仓库里,智能体在你看到之前就能捕获自己的大多数错误;而在一个什么都没有的仓库里,智能体产出什么就交付什么。这种差异在 AFK 运行中最为要紧,那时检查是运行期间唯一进行的验证。但一个检查只能捕获它所断言的东西——检查全绿意味着被断言的属性成立,而非代码正确。那些形如「判断」的缺口,正是自动化评审(automated review)和人工评审(human review)的用武之地。

避免: 「反馈回路(feedback loop)」/「反压(backpressure)」——两者都把检查和评审混为一谈。避免: 「测试(test)」——测试是自动化检查,但并非所有自动化检查都是测试。

用法

「智能体在 AFK 运行里老是交付坏掉的代码。」

「沙箱(sandbox)里接了哪些自动化检查?」

「只有单元测试。」

「加上类型检查和 lint——它会在 PR 落地之前就据此自我纠正。」

Automated review

一个智能体(agent)评审另一个智能体的工作,通常使用不同的模型(model)或系统提示词(system prompt)。非确定性:它形成一个判断。它可以在任何地方运行——PR 合并前、提交历史的事后、作为子智能体(subagent)在会话中途。CI 里的「LLM 充当裁判(LLM-as-judge)」是自动化评审,而非自动化检查(automated check);决定类别的是断言做了什么,而非它在哪里运行。

与执行工作的那个智能体分离,正是它奏效的原因。让写出代码的那个智能体去评审自己的工作,你几乎得不到什么——产生 bug 的那个会话(session)也包含了产生 bug 的那套推理,智能体把自己的结论当作确认再读一遍。而一个拥有全新上下文窗口(context window)的评审者没有那份依恋:它像陌生人一样看待这份 diff,而这正是评审所依赖的。不同的模型或一个专为评审设计的系统提示词会进一步磨利这一点——不同的盲区,以及一个聚焦于你真正在意之处(安全、API 契约、性能)而非含糊的「找找问题」的系统提示词。

它嵌在其他评审层之间。自动化检查是确定性的,捕获可以机械断言的东西;人工评审(human review)代价高昂且扩展性最差。自动化评审居于中间:它以机器成本捕获形如「判断」的问题——一个有误导性的函数名、一处遗漏的边界情况。因为它是非确定性的,它可能漏掉一些东西,也可能标记出非问题;把它当作在人类审视之前抬高下限的过滤器,而非取代人类的闸门。

避免: 「AI 评审(AI review)」/「智能体评审(agent review)」——太含糊,无法与执行工作的智能体本身区分开。

用法

「我们从 AFK 运行里收到太多糟糕的 PR 了。」

「在合并前加一个自动化评审步骤——用不同的模型、独立的系统提示词,聚焦于安全和契约变更。」

AX

智能体体验(Agent experience)——环境(environment)为智能体(agent)在一个代码库里做好工作而配置得有多好。它是 DX(开发者体验)面向智能体的对应物。当同一个智能体在一个仓库里表现出色、在另一个仓库里表现糟糕时——同样的模型(model)、同样的执行框架(harness)——差异通常就在 AX。人的直觉是去怪模型或重写提示词;而修复往往更多地在仓库里。

好的 AX 有三个主要维度:

维度好的 AX 是什么样的
自动化检查快速、确定性的自动化检查(automated check)——类型、测试、lint——智能体无需人类就能据以自我纠正
架构一个智能体无需通读全部就能导航的代码库:可预测的结构、大量藏在小接口后面的行为、能说明自身用途的命名
空闲上下文AGENTS.md、技能(skill)和工具(tool)都保持精简,使上下文窗口(context window)的大部分可留给任务,让智能体待在聪明区(smart zone)而非被淹没

AX 与 DX 有重叠——好的检查和干净的架构对两类受众都有帮助——但它们也会分道扬镳。人类能容忍口口相传的部落知识、缓慢的 CI 和「计费模块的事去问 Sarah」;智能体不能。智能体不会从 IDE 的悬浮提示或漂亮的仪表盘中获益;它们需要以文本形式呈现在工具结果(tool result)里的失败信息。一个代码库可以拥有好的 DX,却有糟糕的 AX。

避免: 把 AX 当作 DX 的同义词——这两类受众需要不同的投入。

用法

「智能体在 API 仓库里写出很棒的代码,在前端里却写出垃圾。」

「API 仓库有严格类型和快速测试套件;前端两者都没有,还有四十个始终加载的技能。那是一个 AX 缺口,不是模型问题。」

Cache tokens

提供方(model provider)从上一次模型提供方请求(model provider request)中缓存下来的输入 token(input tokens),以免重新处理它们。当连续的请求共享同一前缀时,提供方通过其前缀缓存(prefix cache)复用这部分工作,并对已缓存的部分以低得多的费率计费。这是让长会话(session)负担得起的杠杆——没有它,每个回合(turn)都要为整段历史重新付费。

这之所以重要,在于会话是如何计费的。模型(model)是无状态的(stateless),所以每次请求都会把整段对话——系统提示词(system prompt)、每条消息、每个工具结果(tool result)——作为输入 token 重新发送。到第五十个回合时,每次请求都携带五十个回合的历史,而你会对全部内容、每一次都按全价付费。缓存改变了这道算术:提供方在一个完全相同的前缀中已经处理过的 token,会按缓存 token 计费,通常只有输入费率的十分之一甚至更低。在一个长会话中,你发送的大部分都是缓存 token,账单也就保持在合理范围内。

一个例子说明了 token 何时被缓存、何时不被缓存。每个字母代表一块对话内容;每次请求发送截至当前的对话:

请求发送已缓存按全价计费原因
AB无AB首次请求——没有可匹配的对象
ABCABCAB 是上一次请求的精确前缀
ABCDABCD前缀仍然完好
AXCDAXCD一处编辑把 B 改成了 X;匹配在那里失败

缓存以一种特定的方式变得脆弱:它匹配的是精确前缀。如果对话中较早的部分有任何变动——执行框架(harness)重排了内容、某个时间戳更新了、某个文件的表示方式变了——缓存就会从那一点起失配,之后的一切都按全额输入费率计费。缓存在几分钟不活动后也会过期,所以一个长时间暂停后恢复的会话会为其历史重新付费一次。当一个会话的成本无明显缘由地跳升时,在用量报告里对比缓存 token 与输入 token——损坏的缓存会首先在那里露出马脚。

用法

「长会话的成本太狠了——一次重构八美元。」

「查查缓存 token。如果执行框架在两个回合之间重排系统提示词或文件,前缀就会断裂,你每次请求都在按全额输入费率重新付费。」

Clearing

结束当前会话(session)并开启一个全新的会话。下一条消息将从一个空会话和一个空上下文窗口(context window)开始。通常由用户驱动。

清空是被污染上下文的解药。一个会话会累积一切:失败的尝试、走错的岔路、过时的工具结果(tool result)、被放弃的计划。模型(model)在每个回合(turn)都会重读这一切,糟糕的历史拖累新的工作。深入一个长会话后,智能体(agent)会变得更含糊、更不服从——你清楚给出的指令被无视,质量下滑,而催促它做得更好也无济于事,因为它正在跋涉的那些噪声仍在它的上下文(context)里。清空移除了噪声。

清空并不抹除对话。大多数执行框架(harness)会把会话历史保留在你的电脑上,所以文字记录仍在那里可供阅读或恢复。消失的是智能体的工作状态:模型是无状态的(stateless),所以新会话对旧会话所知的一切一无所知。如果会话里存有下一个会话将需要的决策或进展,就先让智能体写一份交接产物(handoff artifact),然后通过指向它来开启新会话。

对照压缩(compaction),后者把会话总结进新的上下文,而不是从空白开始。清空是更粗钝的工具:什么都不会带过来,包括那些垃圾。

用法

「它卡在那个失败的测试上死循环了。」

「直接清空吧——用计划文档和那个测试文件开一个全新会话。跟现有上下文较劲没意义。」

Compaction

一种在内存中完成的交接(handoff):上一个会话(session)的历史被总结,摘要为一个全新会话播种。设计上就是有损的:文字记录是一手来源(primary source),摘要是二手来源(secondary source)——以细节换取余量。由用户手动触发,或经由自动压缩(autocompact)自动触发。

其机制是:上下文窗口(context window)是有限的,一个长会话会把它填满——每个工具结果(tool result)、每次文件读取、每条走错的岔路都留在历史里。当它变得沉重时,执行框架(harness)请模型(model)总结会话,扔掉原始历史,并用摘要为一个全新会话播种。没能进入摘要的一切,都从上下文里消失了。一些执行框架会缓和这一点:把旧的文字记录保留在磁盘上,并在摘要里留一个指向它的上下文指针(context pointer)——二手来源链接回它的一手来源,这样摘要弄丢的某个细节,可以通过重读原文找回。

摘要是由模型撰写的,所以它可以被提示引导。「保留 schema 决策」能让生成的产物更审慎。时机也很重要——在阶段边界处压缩,在计划已定之后,而不是在任务中途。

与清空(clearing)形成对比,后者丢弃一切并冷启动:压缩试图把要点带过去;清空则赌它们已经被记在某个更好的地方了。

用法

「上下文(context)越来越沉了,我还有测试要跑通。」

「开始前先压缩——把必须留存的东西写进摘要提示里,好让新会话保留 schema 决策、丢掉那些探索过程。」

Context pointer

一个文档中指向另一个文档的提及,使得智能体(agent)只在任务需要时才把它拉进上下文窗口(context window)。它是渐进式披露(progressive disclosure)的构成单元。

使用指针(而非内联内容)的理由是成本。一个指针在上下文窗口里只占一行。它背后的文档也许有数千个 token,但在智能体真正跟随该指针之前,这些 token 不花任何成本。把一份 2,000 token 的操作手册内联进 AGENTS.md,每个会话(session)都要为它付费;换成「部署流程:见 internal/deploy.md」,就只有真正部署的会话才会加载它。智能体在任务匹配时用一次工具调用(tool call)跟随该指针。

一个指针要奏效需要两部分:一个稳定的路径,以及足够的描述,让智能体知道何时值得跟随它。一个光秃秃的路径是智能体没有理由去跟随的指针;「见 internal/deploy.md」而不提示里面有什么,会被需要它的会话跳过。要把这一行写得能匹配任务呈现的方式:「发布、部署或回滚——先读 internal/deploy.md」。

一旦你留意,指针无处不在:AGENTS.md 里的行、技能(skill)描述(执行框架加载描述;技能正文在它后面等待)、目录列表里的文件名、文档之间的链接。

指针也可以把一个二手来源(secondary source)系回它所派生出的一手来源(primary source)——命名了原始文字记录的压缩摘要,命名了它所描述的源文件的文档。这使得二手来源的有损性可以被挽回:当摘要被证明不够用时,智能体跟随指针去读原文,而不是从摘要保留下来的那点东西开始工作。

避免: 「引用(reference)」——太干巴,传达不出「跟随它会拉入更多上下文」的意味。「传送门(portal)」——太浮夸。

用法

「AGENTS.md 越来越大了。」

「其中大部分应该是上下文指针,而不是内容。把始终生效的规则内联留着;把部署手册和风格指南做成技能,留下一个上下文指针。」

Context window

模型(model)在每一次模型提供方请求(model provider request)中看到的一切。有限、随模型而定,且是模型感知任何事物的唯一表面。

它是一段单一的 token 序列:系统提示词(system prompt)、截至目前的对话、执行框架(harness)回喂进来的每一个工具结果(tool result)。如果某样东西在这段序列里,模型就能用它;如果不在,模型就不知道它的存在——不知道你的代码库、不知道你昨天编辑的那个文件、不知道你三个会话前给出的那条指令。窗口之外的任何东西都必须先被带进来——通常经由一次工具调用(tool call)——才能影响任何事。

有限意味着它会被填满。每个回合(turn)都追加更多——你的消息、模型的回复、工具结果——一个长会话(session)终将触到上限,迫使压缩(compaction)或清空(clearing)。它也意味着窗口里的一切都在竞争:你加载的每一个 token 都是留给其余部分的一个减损,而你并不需要的内容依然占用着模型的注意力(attention budget)。务实的姿态是把窗口当作一份预算——加载任务需要的,把其余的留在外面。

避免: 「记忆(memory)」——上下文窗口是工作状态,不会跨会话持久保存。记忆系统(memory system)是叠加在其上的另一个概念。

用法

「我能不能把整个 monorepo 都粘进提示词里?」

「上下文窗口是 200k token——那大概是这个仓库的五分之一。挑出任务涉及的文件,把其余的留在一次工具调用之后。」

Context

智能体(agent)当前能够访问到的、与任务相关的信息——也就是智能体所知道的、与手头任务相关的内容。这是一个抽象名词——它既不是模型看到的原始输入(那是上下文窗口,context window),也不是运行中的历史记录(那是会话,session),而是智能体所知道的、与任务相关的东西。「把某些内容加载进 context」意味着让它成为这一集合的一部分;「context engineering(上下文工程)」则是对其进行精心组织的学问。

这三个术语可以清晰地区分开来:

术语它所指代的内容
Context(上下文)智能体当前拥有的、与任务相关的信息
Context window(上下文窗口)模型每次请求时实际看到的 token 序列
Session(会话)由执行框架存储的、运行中的对话

这种区分之所以重要,是因为 context 衡量的是质量而非数量。一个上下文窗口可能几乎被填满,但 context 仍然很差——里面塞了几千个 token 的陈旧工具输出,却没有一条与当前任务相关。它也可能几乎是空的,而 context 却极佳:唯一那条决定任务成败的类型定义。

大多数日常故障都可以追溯到 context。当智能体凭空捏造一个 API、违背某个已定的决策,或者对某个 schema 胡乱猜测时,第一个要问的问题就是当时 context 里有什么——通常是相关事实从未被加载进来,或者被淹没在注意力衰退(attention degradation)之下。解决办法就是精心组织:把任务需要的内容加载进来,把不需要的挡在外面。

用法

「它老是编造类型里根本不存在的字段。」

「那个类型文件不在 context 里——它是在读调用处然后瞎猜。先把定义读进来。」

Contextual knowledge

智能体(agent)当前可以直接从上下文(context)中读到的事实——用户的任务、智能体读入的文件、工具结果(tool result)、会话(session)开始时加载的 AGENTS.md 内容。它是参数化知识(parametric knowledge)的对立面:参数化知识是从参数中回忆出来的,而上下文知识是从窗口(context window)中读出来的。当智能体基于上下文知识工作时,幻觉(hallucination)会少得多——答案就摆在眼前,而不是从模糊的记忆里打捞出来的。

在两种知识里,只有上下文知识是你能掌控的。参数是冻结的,所以要给模型(model)它所缺乏的知识——某个内部 SDK、某个在知识截止(knowledge cutoff)之后才发布的库、昨天才做出的决策——唯一的办法就是把它放进 context。大量实际的 AI 编程工作归根结底就是这件事:在模型需要的那一刻,把正确的事实摆到它面前。

当上下文知识与参数化知识冲突时,通常是上下文知识胜出。把当前的 API 文档贴进去,模型就会照着它走,而不是依赖它对旧 API 的陈旧记忆——不过旧版本仍可能渗漏出来,尤其是在漫长会话的后期。如果文档已经加载了,智能体却还在反复退回到过时的写法,那就是参数化知识越过了上下文知识在渗漏;重申一遍修正,或把它挪到离工作更近的位置,会有帮助。

与参数化知识不同,使用上下文知识是有代价的。任何加载进窗口的东西都要消耗 token,并争夺模型的注意力预算(attention budget),所以加载得越多并不自动越好——目标是把相关的事实放进窗口,而不是把所有事实都放进去。

只有在需要与参数化知识作对比时,才动用这个术语;否则直接说 context 就好。

避免:「working memory(工作记忆)」——上下文知识指的是此刻窗口里的内容;而记忆系统(memory system)是把跨会话的内容送进窗口的机制。两者尺度不同,不要混为一谈。

用法

「为什么我贴上文档它就能把 API 写对,不贴它就瞎编?」

「文档在里面时,它是上下文知识——照着页面读。不贴文档时就是参数化知识,那些罕见的接口就模糊了。」

Design concept

关于「正在构建什么」的共同理解,由用户和智能体(agent)共同持有,但独立于任何具体产物。这是 Brooks 的术语(出自《The Design of Design》):对话、交接产物(handoff artifact)以及代码,都是试图捕捉或逼近这个设计构想的产物,但它们没有一个「就是」这个构想本身。设计构想的质量,是通过构建它的那场对话的质量来感受的。

这个术语指出了一种常见挫败感背后的鸿沟:智能体完全照你说的写了,结果还是错的。通常的原因是你自己还没完全想清楚你要什么。设计构想在你自己脑子里都还没完成——你的提示词捕捉到了你已经想通的那部分,对没想通的部分则保持沉默。智能体用它自己的假设填补了这些沉默,因为根本没有可对齐的东西。没有任何东西出故障。之所以没有共享的设计构想,是因为当时根本还不存在一个完整的构想可供共享。

判断设计构想是否已被共享,方法和判断一位同事是否理解你一样:对方开始用你会用的方式,回答你还没提出的问题。在那之前,工作就是对话——盘问(grilling)是它的刻意版本——而过早写规格文档(spec)只会把这种错位固化到一个更持久的产物里。设计构想还会随着你的认识而变动;产物滞后于它,这就是为什么一份忠实于上周理解的规格文档,本周的会话里仍可能误导人。

用法

「它完全照我说的写了,结果还是错的。」

「你们还没共享设计构想——它在用假设填补空白。继续聊,直到取消、退款和部分履约这些点在你们之间全部对齐,再让它去写规格文档。」

DX

开发者体验(Developer experience)——一个代码库及其工具链让人类做好工作的容易程度。良好的 DX 意味着快速的反馈、清晰的错误信息、能回答你实际疑问的文档,以及第一次就能跑通的环境搭建。这个术语远早于 AI 编程;它被收进这本词典,主要是作为 AX 的对照。

DX 就是人类与代码库之间的交互,仅此而已。两类受众之间的主要区别在于:人类是有状态的(stateful),而智能体是无状态的(stateless)。人类把代码库学会一次,就能把那份知识带进往后的每一天,这就是为什么糟糕的 DX 尚可忍受:他们通过批量推送来绕开缓慢的 CI,通过在 Slack 里问一次来绕开缺失的文档,通过记住东西放在哪儿来绕开混乱的结构。这些变通办法逐渐积累,团队最终在一个处处与他们作对的代码库里也能高效工作。

智能体(agent)面对同一个代码库,却没有任何这种积累。由于在会话(session)之间是无状态的,智能体每次都从零重新学习代码库——它能受益于快速的测试套件和清晰的错误信息,但它昨天弄明白的任何东西都没了,除非那些东西被写进了环境(environment),而智能体只能通过工具结果(tool result)来感知环境。这正是 AX 所命名的那道鸿沟:当开发者是智能体时,DX 中还能存活下来的那些部分,再加上人类没有的关注点,比如让上下文窗口(context window)保持空闲。

这种重叠意味着在 DX 上的投入往往能顺带改善 AX——严格的类型、快速的测试和可预测的结构对两者都有帮助。而这种分歧意味着并非总是如此:一份精美的上手文档能帮人类度过一周,却对智能体毫无用处,除非它能从 AGENTS.md 触达到。

用法

「我们的 DX 挺好的——新人一周就能上手。」

「能上手是因为有人陪着他们那一周。智能体可没有那一周;单独去检查 AX。」

Effort

Effort 是一个旋钮,用来控制模型(model)在回答之前进行多少推理。它按每次模型提供方请求(model provider request)来设定,控制的是模型在开始写你能看到的回复之前,所进行的思考的长度。那段思考和其他一切一样,是在推理(inference)时生成的;执行框架常常把它隐藏起来,但那是模型实实在在在做的工作。

更高的 effort 更费钱也更慢。推理是以 token 的形式产出的,即便你从未看到,也按输出 token(output tokens)计费,而且是一个 token 一个 token 地生成——所以把 effort 调高会拉长答案到来之前的等待,也会增加账单。这笔交易是用更多的深思换取速度和成本。

大多数执行框架把 effort 暴露为一个小小的档位阶梯:

档位适用场景
Low(低)机械性的编辑、查找、路径明确的定义清晰的改动。
Medium(中)日常编码——通常的默认档。
High(高)棘手的 bug、设计决策、多步骤的计划。
Max(最高)最难的问题,一旦答错,回退代价高昂。

设错了档,两头都会有症状。在难题上把 effort 设得太低,你会得到一个自信而肤浅的答案,它跳过了问题所需要的推理——读起来没问题,却以一种让你后续付出代价的方式出错。而给一行代码的重命名设成 max,你就得干坐着熬过一场长长的思考,产出的东西最低档也能给你。

让 effort 匹配任务,而不是匹配会话(session)。在真正难以推理的那部分把它调高,在周围那些机械性的活儿上把它调低。

用法

「它老是搞砸这个并发修复——我已经重新解释三遍了。」

「把 effort 调高。那是个推理密集型的 bug,在默认档上它还没想够就急着定方案了。」

Environment

智能体(agent)所作用的世界——执行框架之外、智能体通过工具结果(tool result)感知、通过工具调用(tool call)改变的一切。执行框架「运行」智能体;而环境是智能体「工作于其中」的地方。像 AGENTS.md 这样的文件存在于环境中;执行框架才是把它加载进上下文窗口(context window)的那一方。文件系统(filesystem)是最常见的一种环境,但不是唯一一种(数据库、远程 API、浏览器会话都可以是环境)。

智能体只有在去看的时候才能看到环境。它对环境所知道的一切都是通过工具结果到来的,所以它的图景是一组快照,每一张都在拍摄的那一刻是准确的。如果一个文件在智能体读过之后发生了变化——你手动改了它、某个构建步骤重新生成了它——智能体会继续基于那份陈旧的副本推理,直到某件事促使它重新读取。智能体自信地描述一个早已不是那样的文件,通常就是这个原因:环境变了,快照没变。

环境也是那个会持久存在的层——唯一一个始终有状态(stateful)的层。会话(session)的上下文会在会话结束时消失,但写入环境的文件会留存下来,供下一个会话读取——这正是记忆系统(memory system)、交接产物(handoff artifact)和 AGENTS.md 所依赖的。任何一个智能体明天还应该知道的东西,都必须最终落到环境里。

环境有多大由你决定。沙盒(sandbox)会缩小它,限制智能体能触达的范围;添加一个工具(tool)则会扩展它,把一个数据库或一个 API 纳入触达范围。边界之内的东西,就是智能体能感知和改变的东西;边界之外的一切,对智能体而言并不存在。环境为支持智能体工作而搭建得有多好,就是这个代码库的 AX。

避免:用「environment」来指代运行时或执行框架本身——执行框架是那层包装,环境是那个工作区。

用法

「智能体看不到 staging 数据库的 schema。」

「把它接进环境——给它一个作用范围限定在 staging 只读的 psql 工具。执行框架没问题,它只是没有可作用的对象。」

Filesystem

一棵由文件和目录组成的树,智能体(agent)从中读取、向其写入,并在其中执行——是编程智能体默认的那种环境(environment)。AGENTS.md、技能(skill)、源代码、构建脚本以及工具(tool)配置都存在于文件系统里。当一个执行框架「在你的项目里启动」时,它就是在把智能体指向一个文件系统。

智能体只能通过工具调用(tool call)触碰它——读一个文件、写一个文件、跑一条 shell 命令。磁盘上的任何东西在被工具调用加载之前都不在上下文窗口(context window)里,正是这一点让智能体能够在一个远大于窗口的仓库里工作:文件系统装着一切,而上下文只装着当前任务读过的部分。有些执行框架会默认把当前目录的文件名加载进上下文窗口——不是内容,只是那棵树——它们充当着上下文指针(context pointer):智能体看到存在哪些东西,再去读它需要的文件。

而且它是与你共享的。智能体编辑的那些文件,正是你在编辑器里打开、在 git 里做 diff 的同一批文件——文件系统是那个共同的工作区,你在这里评审智能体做了什么。

用法

「为什么它没读到我的 AGENTS.md?」

「它跑在一个不同的文件系统上——沙盒(sandbox)挂载的是父目录,不是项目根目录。把执行框架重新指过去。」

Grilling

一种与智能体(agent)一起打磨设计构想(design concept)的技巧:智能体用苏格拉底式的方法访谈用户,一次只谈一个决策,并为每个决策提出一个推荐答案。它放慢了奔向成品计划的冲动——在构想稳定下来之前,不写任何交接产物(handoff artifact)。

这个技巧之所以存在,是因为智能体会悄无声息地填补空白。让它根据两行提示词写一份规格文档(spec),智能体不会在你尚未做出的决策处停下——它会挑选默认值并写进去。结果看起来很完整,而那些猜测与真正的选择难以分辨,于是你很晚才发现它们:在评审时,或者当构建出来的功能以一种你从未选择过的方式处理某个边缘情况时。盘问把这个过程反转了过来——智能体不再靠猜,而是必须发问。

它是一种人在回路中(human-in-the-loop)的技巧:你的回答就是它的输入。当一个问题无法在对话中回答时——你得亲眼看到那个东西——就切换到原型试做(prototyping)。

用法

「它直接就去写规格文档了,把取消逻辑写错了。」

「先盘问它——在它把任何东西写进文档之前,让它先向你问清楚部分取消、退款和时间点这些问题。在对话里解决比在代码里解决便宜。」

Hallucination

自信却错误的模型(model)输出。有两种风味,各有不同的成因和修法:

风味出了什么错成因修法
事实性(Factuality)关于世界的捏造或错误事实——一个不存在的函数、错误的 API 签名、伪造的引用参数化知识(parametric knowledge)的空缺,往往在知识截止(knowledge cutoff)之后加载正确的上下文知识(contextual knowledge)
忠实性(Faithfulness)输出偏离了已加载的上下文知识、用户的指令,或模型自己先前的推理注意力衰退(attention degradation);在笨拙区(dumb zone)会更严重清空(clearing)或压缩(compaction)

下一个 token 预测(next-token prediction)不论底层事实是否真实都会产出流畅的输出——模型没有一个内部信号来告诉它「有些东西我不知道」,所以一个捏造出来的方法会以和正确方法完全一样的笃定语气出现。幻觉代码天生就显得合情合理:它就是那个 API「如果存在」的样子,而这恰恰是它能骗过走马观花式评审、只在运行时才失败的原因。

你需要知道自己面对的是哪种风味,因为治其一的办法会让另一个更糟。事实性意味着知识缺失:修法是添加上下文——文档、类型定义、那个文件。忠实性意味着知识是存在的,只是在争夺注意力的竞争中落败了:修法是移除上下文。把忠实性误诊为事实性,你就会贴进更多文档,这会让上下文变大,反而让偏离更严重。当智能体把某件事搞错时,先检查正确的信息是否已经在上下文里,再决定你面对的是哪个问题。

避免:把「hallucination(幻觉)」当作「错误」的赤裸同义词——不指明风味,这个术语就没有任何诊断价值。

用法

「它在那个 schema 上幻觉出了一个 parseAsync 方法。」

「是事实性还是忠实性?」

「这方法在我贴的文档里存在——它只是在第四十个回合(turn)之后就不再读它们了。」

「那就是忠实性。压缩后重新加载,别费劲加更多文档。」

Handoff artifact

用作一次交接(handoff)的承载机制的文档——由一个会话(session)写入环境(environment),供另一个会话读取。规格文档(spec)、工单(ticket)和计划文档都是交接产物。

写它的理由:模型(model)是无状态的(stateless),所以会话里的任何东西都无法在清空(clearing)之后存活。决策、约束、写了一半的计划——全都随着承载它们的上下文(context)一起消失了。而环境会持久存在。把重要的状态写进一个文件,就把它挪到了下一个会话能从中读回来的地方。

这个产物是一种二手来源(secondary source)——是对某次会话工作的一份记述,而不是工作本身。这正是它小到足以给一个全新会话做简报的原因,也正是它可能误导一个会话的原因:它记录的是写它的那个会话所相信的东西,而它遗漏或写错的任何内容,对读它的人而言都是不可见的。凡是有一处论断至关重要,下一个会话就应该拿它去对照一手来源(primary source)——代码、测试——加以验证,而不是直接继承。

一份好的产物,是为读进一个零上下文的会话而写的。用具体的文件路径,而不是「我们讨论过的那个文件」。写清楚决定了什么以及为什么,好让下一个会话不必重新翻案。写清楚做完了什么、还剩什么。告诉写它的那个会话这份产物将去往何处会有帮助:「为一个对这项工作一无所知的全新会话写一份交接文档」。

另一种承载机制是压缩(compaction),它在内存中做摘要。产物有两个优势:它存在于磁盘上,你可以在任何东西依赖它之前读到并修正它;而且它可以复用——同一份规格文档可以给五个并行会话做简报。

用法

「我该怎么在做计划的智能体(agent)和做实现的智能体之间拆分这件事?」

「让做计划的那个写一份交接产物——文件路径、决策、约束。做实现的那个会话一开始就拿到一个指向这份产物的指针,并以它为简报开展工作。」

Handoff

把智能体(agent)的上下文(context)从一个会话(session)转移到另一个会话。承载机制各不相同——一份书面的交接产物(handoff artifact)、一份内存中的摘要(压缩,compaction),以及其他形式。它有别于清空(clearing,那是根本不做转移)。原因多种多样:切换角色(规划者 → 实现者)、发起一次 AFK 运行、扇出到多个并行会话,或者腾出上下文窗口(context window)的空间。

接收方会话从零上下文开始——模型(model)是无状态的(stateless),旧会话里的任何东西对新会话都不可见。下一个会话需要的任何东西都必须被显式地承载过去;其余一切都没了。「没有回头路」是塑造这种承载的约束:新会话无法回过头去问旧会话它当初是什么意思,所以被承载的材料必须能够独立成立。

机制形式特性
交接产物环境(environment)中的文件你可以在任何东西依赖它之前读到并修正它;可跨多个会话复用
压缩上下文窗口中的摘要自动且低成本;较难检视;只喂给一个后继者

一次糟糕交接的可见失败就是重新翻案:新会话重新打开了旧会话早已敲定的决策,因为那次承载记录了「决定了什么」却没记录「为什么」。评判一次交接,就看一个零上下文的会话拿它能做成什么。

用法

「规划会话越来越重了——我是不是该直接继续下去?」

「做一次交接。把决策写进一份文档,清空,在一个读取这份文档的全新会话里开始实现。」

Harness

围绕在模型(model)周围、把它变成智能体(agent)的一切:工具(tool)、系统提示词(system prompt)、上下文窗口管理(context-window management)、权限、钩子。Claude.ai 和 Claude Code 跑在同一个模型上,但表现不同,因为它们的执行框架不同。

模型本身只做一件事:输入文本,输出文本。它读不了文件、跑不了命令,也记不住上一个回合(turn)。这一切都由执行框架提供。它为每一次模型提供方请求(model provider request)组装上下文(context),执行模型所请求的工具调用(tool call),把工具结果(tool result)喂回去,存储会话(session)历史,在有风险的动作之前向你请求权限,并决定何时进行压缩(compaction)。这个智能体循环——模型提议、执行框架执行、如此往复——是由执行框架来驱动的。

这对诊断很重要。当两个产品之间、或者昨天和今天之间行为出现差异时,模型往往不是那个变量——执行框架才是。一个不同的系统提示词、一套不同的工具、一个被改动的权限默认值,或者一种新的上下文管理策略,都会在模型毫无变化的情况下改变行为。这也意味着执行框架是你大部分配置所在之处:AGENTS.md 文件、权限设置和钩子都是给执行框架的指令,而不是给模型的。

例子:Claude Code、Cursor、Codex CLI——以及 Claude.ai,它是一个聊天执行框架而非编程执行框架。

用法

「同一个模型,为什么 Claude Code 在编辑文件,而 Claude.ai 只是回答问题?」

「执行框架不同——Claude Code 有文件系统(filesystem)工具、一套不同的系统提示词,还有一个权限层。模型在这里不是那个变量。」

Human review

用户阅读智能体(agent)产出的代码并对它形成判断。阅读 diff 或被改动的文件算数;阅读智能体对自己所做之事的「描述」不算——旁白不是那件产物。那段描述是一种二手来源(secondary source),由被评审的一方所写;diff 才是一手来源(primary source),而评审就意味着读它。

智能体抬高了代码产出的体量,于是评审变成了瓶颈。一个有用的思路是把不同的评审策略分层。自动化检查(automated check)抓住机械性的故障,自动化评审(automated review)抓住那些可被描述的故障,而人工评审则留给只有你才能判断的东西——这个改动是不是正确的改动,这个做法是否契合代码库,这东西到底该不该存在。

评审越早也越便宜。在工作开始前读一份计划,或在进行途中读一个小小的 diff,只需几分钟;而在一次 AFK 运行之后去发掘一整条已完成的分支则耗时得多。你把评审检查点放在哪里,是一个人在回路中(human-in-the-loop)的决策,而不是事后补救。

避免:单说「code review(代码评审)」——它在人工与自动之间含糊不清。

用法

「我对 AFK 的产出做了人工评审。」

「你读的是 diff 还是只看了摘要?」

「diff。摘要说它删掉了死代码——结果那个函数是被一个生成出来的文件调用的。」

Human-in-the-loop

一种工作模式,其中一个或多个人类在会话(session)过程中与智能体(agent)结对——实时地评审、纠偏或协作。人类是在场且投入的,而不仅仅是对单个动作放行。

它的对照是 AFK 工作,那种模式下智能体无人看管地运行,你事后再评判结果。人在回路中意味着在问题还便宜的时候就抓住它们:你看到智能体伸手去拿错误的文件、误读了需求,或者开始走进死胡同,你用一句话就把它纠正过来——而不是发现二十分钟的自信工作全都建立在那个错误之上。智能体并不能可靠地知道自己什么时候偏离了轨道;放任不管的话,它们倾向于一路推进,而不是停下来发问。

哪种模式合适取决于工作本身。定义清晰、风险低、易于验证的任务适合 AFK。而含糊的、不可逆的,或者你很难评审其最终成品的任务——一次 schema 迁移、一个棘手的设计决策、任何触及生产环境的东西——则适合留在回路中。这个判断本质上是:一次走错方向有多昂贵,以及你会多晚才发现它?

有些工作天生就在回路中,因为你的反应就是它的输入。盘问(grilling)只有在你在场回答问题时才管用;原型试做(prototyping)只有在你在场对产物做出反应时才管用。

留在回路中要花费你的注意力,而注意力是那个稀缺资源。用好智能体的一部分功力,就在于把更多工作安全地移出回路——靠计划、自动化检查(automated check),以及在末尾做人工评审(human review),而不是全程盯着。

用法

「这个通宵跑 AFK 行吗?」

「不行,这是 schema 迁移——保持人在回路中。我想看到每一步,如果它选错了回填数据的来源列,我好纠偏。」

Hygiene

上下文卫生(context hygiene):在把内容交给模型(model)之前,先把上下文窗口(context window)里多余、过时、过大的东西清掉,让窗口保持能用。软件工程里常把例行打扫叫 hygiene(代码卫生、缓存卫生);落到智能体(agent)上,打扫的对象是上下文。

它和压缩(compaction)相关,但不是同一件事。压缩是有损的:把旧历史总结成摘要,换取余量。卫生可以更便宜——截断过长的工具结果(tool result)、去掉重复、丢掉已经无关的文件——不必每次都请模型写摘要。许多执行框架(harness)会在一次模型提供方请求(model provider request)之前做这道检查:窗口还没到必须压缩的程度,就先做廉价清理;真要撑爆了,再触发压缩或自动压缩(autocompact)。

目的是保护注意力预算(attention budget)。窗口里每一段冗长的命令输出、过期的探索路径,都在和当前任务抢注意力。卫生做得好,模型看到的是当下还需要的东西;做得差,窗口被垃圾填满,忠实性(hallucination)和遗忘都会上来。

避免: 把它理解成「个人卫生」或「安全扫描」。这里的 hygiene 是收拾上下文,不是杀毒,也不是权限检查。

用法

「进智能体之前先做一次 hygiene——历史已经快顶到窗口了。」

「先把那些几万 token 的工具输出收成一行。还不够再压缩。」

Inference

运行一个训练好的模型(model)来生成输出——每一次模型提供方请求(model provider request)时所发生的事情。参数(parameters)保持固定不变;模型只是在给定的上下文(context)上做下一个 token 预测(next-token prediction)。相对于训练(training)而言它很便宜,但按 token 计费,且是使用模型的主要成本。

一个模型的一生分为两个阶段:

阶段何时发生它做什么参数
训练一次,在发布之前从一个训练语料库产出参数正在被写入
推理每次有人使用这个模型时在你的上下文上运行冻结的参数以生成 token只读

你在推理时所做的任何事情都不会回写到参数里——这就是为什么你今天做的一个纠正明天不会留下。那个在下一个会话(session)里、在你仔细解释过修法之后仍犯同样错误的模型,并不是无视了你;它是压根没能力从这次交流中学习。模型是无状态的(stateless)——连续性必须来自它之外——来自上下文窗口(context window)或一个记忆系统(memory system)。

这个机制也解释了你是怎么被计费的。每一次请求都要在完整的上下文上运行模型,所以成本随输入 token(input tokens)和输出 token(output tokens)而扩大,而一个做了几十次工具(tool)调用的智能体,会为每一个来回的推理付费。这就是为什么上下文大小既是一个质量问题,也是一个成本问题。

用法

「为什么账单随用量增长,而不是一口价的许可证?」

「你付的是推理的钱——每一次模型提供方请求都在提供方的硬件上运行模型。训练早就发生过了,但推理成本是按请求累积的,而当有工具被调用时,单个回合(turn)就能展开成许多次请求。」

Input tokens

执行框架在每一次模型提供方请求(model provider request)时发送的 token——系统提示词(system prompt)、对话历史、工具结果(tool result),以及模型(model)在开始写之前所读的一切。它按比输出 token(output tokens)更低的费率计费,因为处理它们比处理输出 token 更便宜。

在做 AI 编程时,输入 token 占了你账单的大部分。模型是无状态的(stateless),所以每一个回合(turn)都会把整个会话(session)作为输入重新发送一遍:你的第一条消息、每一次回复、迄今为止的每一条工具结果。第五十个回合的输入包含着前面四十九个回合。单个模型提供方请求可能只产出几百个输出 token,却要重新发送十万个累积历史的输入 token。

前缀缓存(prefix cache)降低了这笔成本:与某次先前请求完全匹配的历史,会按便宜的缓存 token(cache tokens)计费,而不是全价输入。当输入成本仍然很痛时,修法就是缩小被重新发送的内容——在任务之间清空(clearing)或压缩(compaction)。

用法

「账单很高,可智能体(agent)几乎没写什么东西。」

「是输入 token——每一个回合都在重新发送整个会话。没有前缀缓存,你每次请求都要为历史重新付费。」

Knowledge cutoff

一个日期,过了这个日期,模型(model)就没有参数化知识(parametric knowledge)了。截止之后的库、API 和事件都是捏造陷阱,除非它们的文档被作为上下文知识(contextual knowledge)加载进来。每一次模型发布都自带它自己的截止日期。

截止日期之所以存在,是因为模型是这么造出来的:训练(training)把一份文本的快照烘焙进了模型的参数(parameters),此后参数就冻结了。模型不知道自己的知识有一个边界——被问到截止之后的某件事时,它不会拒答,而是从它确实知道的最接近的东西外推出去。这正是这个陷阱之所以安静的原因:针对某个库的旧版本写出来的代码看起来合情合理,往往还能编译通过,只在那些改变了的部分上失败。

修法始终是同一个:把当前的信息弄进上下文(context)。加载变更日志,指向已安装版本的类型定义,或者让智能体从网上读文档。上下文里有任何东西,都胜过参数里什么都没有。

用法

「它老是写 v3 的 SDK 语法——我们用的是 v5。」

「v5 是在知识截止之后才发布的。把 v5 的变更日志作为上下文知识加载进来,否则它会一直从更旧的参数化版本里捏造。」

MCP

模型上下文协议(Model Context Protocol)。 一种把外部工具服务器接入执行框架(harness)的协议——它规定了智能体(agent)如何获得执行框架自带工具(tool)之外的工具。智能体从不「调用 MCP」;它调用的是某个工具,只是这个工具恰好是执行框架从某个 MCP 服务器获取来的。MCP 还会暴露资源(只读数据)和提示词(可复用的模板),但提供工具才是它的主要用途。

这个协议解决的是集成问题。如果没有统一标准,每个执行框架都得自己写一套 Linear 集成、自己写一套 Slack 集成、自己写一套数据库集成——而且每个都要单独编写和维护。有了 MCP,集成只需作为一个服务器编写一次,任何兼容 MCP 的执行框架都能使用它。执行框架连接到服务器,服务器公布它提供哪些工具,这些工具便与内置工具一起提供给智能体使用。

代价体现在上下文(context)上。服务器公布的每个工具都会以定义的形式送达——名称、描述、参数结构(schema)——而模型(model)只能调用它已知的工具。最朴素的做法是把所有定义预先加载进上下文窗口(context window):安装几个功能齐全的服务器,一个会话(session)还没等你打字,开头就已经塞入了数千个 token 的工具结构,把注意力预算(attention budget)花在这次任务根本用不到的工具上。

如今许多执行框架用工具搜索来缓解这一点:上下文里不再放完整定义,而是持有一个指向可用工具的上下文指针(context pointer)——智能体按名称或用途搜索工具,只在需要时才加载其定义。如果你的执行框架不这么做,那预先加载的代价依然存在,此时值得只启用项目真正需要的服务器。

用法

「智能体需要从 Linear 读取工单。」

「把执行框架配置成使用 Linear 的 MCP 服务器——它把 Linear API 暴露成智能体可以调用的工具,省得你自己写自定义的工具包装层。」

Memory system

一种试图让智能体(agent)在跨会话(session)时保持有状态(stateful)的系统。它在会话期间把信息持久化到环境(environment)中,并在未来会话开始时把信息重新加载进上下文窗口(context window),这样即便用户清空(clearing)了会话,智能体依然能延续之前的连续性。

记忆系统有两半。写入路径:在会话期间,智能体把它学到的东西——你陈述过的偏好、关于项目的某个事实——作为文件记录到环境里。读取路径:在会话开始时,执行框架(harness)把这些文件(或它们的索引)重新加载进上下文窗口。许多执行框架自带记忆系统——Claude Code 的 /memory 就是其一——但你也可以自己搭一个:一个存放笔记的目录,再在 AGENTS.md 里加一条指令让智能体去查阅它。

它与任何「始终加载」的内容有着同样的权衡。记忆会不断累积,所以大多数系统只加载一行索引,把正文内容留在上下文指针(context pointer)之后,而不是把所有内容都内联进来。而且记忆属于二手来源(secondary source),因此会失真:三月记录下的某个事实,到六月依然会被以同等的信心加载进来,尽管此时项目早已今非昔比。记忆系统需要定期修剪,就像 AGENTS.md 一样。

用法

「我老是得反复告诉它我用的是 Postgres,不是 MySQL。」

「接上一个记忆系统吧——在第一个回合(turn)就把它学到的东西写进文件系统(filesystem),然后在会话开始时重新加载。模型(model)本身是无状态(stateless)的;是记忆层在伪造出连续性。」

Model provider request

从执行框架(harness)到模型提供方(model provider)的一次往返。执行框架发送当前的上下文(context),提供方返回一个响应(一次工具调用(tool call)或一个最终答案)。如果智能体(agent)调用工具(tool),单条用户消息可能会催生出许多次模型提供方请求——每个工具结果(tool result)都会触发又一次请求。

每次请求都携带全部内容:系统提示词(system prompt)、到目前为止的完整对话、每一个工具结果。模型(model)是无状态(stateless)的,所以提供方在请求之间什么都不保留——第四十次请求会重发第三十九次请求发过的内容,再加一个工具结果。前缀缓存(prefix cache)的存在正是为了让这种重复变得负担得起。

请求同时也是计费的单位。输入 token(input tokens)、输出 token(output tokens)以及缓存折扣都是按请求计数的,这也是为什么一个看似无害的问题可能花费出人意料的费用:成本并不与你的消息成正比,而是与请求次数乘以每次请求所携带的上下文大小成正比。

把请求和回合(turn)区分开是值得的。一个回合是与你的一次交流,而单个回合——比如「修复这个失败的测试」——会展开成一连串请求:

请求模型返回执行框架随后
1工具调用:运行测试运行测试,追加失败输出
2工具调用:读取测试文件追加文件内容
3工具调用:读取源文件追加文件内容
4工具调用:编辑源文件应用编辑,追加结果
5工具调用:再次运行测试运行测试,追加通过输出
6最终答案:「已修复,测试通过」展示给你看

一个回合六次请求——每次都重发整个上下文。当你纳闷 token 都去哪了,去数请求次数,而不是回合数。

用法

「一个问题就烧掉了四万个 token?」

「看看那些工具调用——十二次 grep、八次读取、四次编辑。每个工具结果都会催生又一次模型提供方请求,而整个会话(session)前缀每次都要重发。」

Model provider

为模型(model)提供推理(inference)服务的一方。通常是远程服务(Anthropic、OpenAI、Google),但也可以是本地的——运行在你自己机器上的 Ollama、LM Studio、llama.cpp。执行框架(harness)本身并不运行模型;它请求某个提供方来运行。

提供方掌握着底层机制:参数(parameters)位于它的硬件上,每一次模型提供方请求(model provider request)都是执行框架通过网络发送 token 并取回预测结果。这使得提供方成为一整类问题的来源,而这些问题常被误归咎于模型或执行框架——速率限制、性能降级和服务中断都出在这里。当智能体(agent)在会话(session)中途卡住,或者每个回合(turn)都报错时,在排查其他任何东西之前,值得先看一眼提供方的状态页面。

提供方还设定商业条款:输入 token(input tokens)和输出 token(output tokens)的按 token 定价、前缀缓存(prefix cache)折扣,以及到底提供哪些模型。要注意的是,提供方与模型的制造者可能是不同的公司——Bedrock、Vertex 和 OpenRouter 提供的都是别人的模型。

本地提供方以能力换取控制权:能装进你自己硬件的模型远比前沿模型小得多,但没有任何东西离开这台机器,也没有按 token 计费的账单。

用法

「我们能不能为那个物理隔离的客户离线运行这个?」

「把模型提供方换成本地的——在他们的机器上跑 Ollama 或 llama.cpp。执行框架不在乎,它只是访问一个不同的端点而已。」

Model

即参数(parameters)。它是无状态(stateless)的——只做下一个 token 预测(next-token prediction),别的什么都不做。「Claude Opus 4.x」和「GPT-5.x」就是模型。模型本身无法做任何智能体式(agentic)的事情;它必须被套上执行框架(harness)。

模型不能读文件、运行命令、浏览网页,也记不住昨天——它接收 token 并预测 token 输出,每次模型提供方请求(model provider request)只做一次。所有让人感觉像是智能体(agent)在工作的东西——选择工具(tool)、读取结果、循环直到任务完成——都是执行框架在连续地编排许多次这样的预测。

模型提供方(model provider)按档位发布模型:一个体量大、最聪明但慢且贵的,以及一些更快更便宜但能力较弱的小型模型。选档位是个实打实的决策——重量级的用于规划和棘手的调试,轻量级的用于机械性的改动——执行框架允许你在会话(session)中途切换。

对「模型」这个词严格一些,也能让诊断更加锐利。「模型不擅长这个」是一个具体的论断——同一个模型换个执行框架,或者换一份上下文(context),行为往往截然不同。在归咎于模型之前,先检查它拿到了什么:大多数令人失望的输出可以追溯到上下文或执行框架,而不是参数。

用法

「规划这一步我们要不要把模型从 Sonnet 换成 Opus?」

「试试看——但这个任务里大部分重活是执行框架在干。如果系统提示词(system prompt)和工具都不对,换模型也没用。」

Next-token prediction

模型(model)实际在做的事。给定一段上下文(context),它采样出一个下一个 token,追加上去,然后再运行一次。每一份输出——一个句子、一次工具调用(tool call)、一个上千行的文件——都是一次一个 token 地构建出来的。模型没有别的运行模式。

每一步的工作方式都一样:上下文窗口(context window)中的 token 被送入参数(parameters)进行运算,产生词汇表中每个 token 的一个概率——这个 token 很可能是下一个,那个 token 可能性较低。从这些概率中采样出一个 token,追加上去,然后带着稍微变长的上下文再次运行这个循环。正是这个采样步骤,导致同一个提示词在不同次运行中产生不同的输出:非确定性(non-determinism)是内建于这套机制之中的,而不是叠加上去的 bug。

牢牢抓住这套机制,就能解释那些原本看起来很奇怪的行为。模型在输出一个 token 之前,从不检查它是否为「真」——只看它是否「可能」——这正是幻觉(hallucination)的根源。它边走边对每个 token 做出承诺,所以一个听起来很自信的开头句可能会把后面的整个答案带偏。而且由于输出 token(output tokens)严格地一次只产生一个,生成速度给任何智能体(agent)能工作的速度设了一个下限。

用法

「智能体是怎么『决定』去调用一个工具的?」

「它并没有决定——从头到尾都是下一个 token 预测。工具调用只是一个结构化的字符串,执行框架(harness)从输出流里把它解析出来罢了。」

Non-determinism

同样的输入可能产生不同的输出。用完全相同的上下文(context)把一个模型(model)跑两次,你可能得到两个不同的答案——有时是一个词的差别,有时是一套完全不同的思路。你的代码根本不需要有任何改动,这种情况就会发生。

这是模型生成文本的方式、以及模型提供方(model provider)处理请求(request)的方式所带来的一种属性。在推理(inference)期间,模型对可能的下一个 token 产生一个概率分布,并从中采样出一个——通常会故意加入一些随机性,因为总是挑最可能的 token 会产生重复、低质量的文本。响应早期一个采样不同的 token 会改变它之后的每一个 token,一个不同的词就这样变成了一套完全不同的思路。提供方一侧的服务又叠加了更多变数:多个请求被批量放在共享硬件上处理,批次之间微小的浮点差异可能会在两个势均力敌的 token 之间扭转胜负。没有哪个开关是你一拨就能让这一切消失的。

对于同一个任务,要预期智能体(agent)给出的结果会有一个波动范围。大多数响应都落在一个合理的质量钟形曲线内——这正是非确定性还能被容忍的原因——但两端的尾部是真实存在的:有些日子模型会显得很敏锐;有些日子它会像是彻底跑偏了。同样的任务,不同的骰子点数。这带来两个实际后果。重试是一种正当的策略:一次失败的尝试只是从分布中抽了一次,对同一任务重新尝试一次可能就落得更好。而验证比使用确定性工具时更加重要——你没法把智能体的行为测一次就指望它每次重复,所以自动化检查(automated check)必须去抓住那些糟糕的抽样结果。

小心不要把这件事过度叙事化。人类是模式匹配的机器,一连串糟糕的运行会让人感觉像是证明了「模型这周变差了」。通常这只是分布使然。

用法

「Claude 今天糟透了。他们是不是发了个更差的版本?」

「大概没有——模型输出是非确定性的。同一个任务你会有状态好的日子和状态差的日子。在你去找原因之前,先明天再试一次。」

Output tokens

模型(model)生成回来的 token。计费费率比输入 token(input tokens)更高——通常大约是其五倍——因为生成它们需要更多算力。

模型写出的一切都计入其中:你读到的散文、它输出的代码、工具调用(tool call),以及模型在回答前做的任何扩展思考。最后这一项会让人意外——推理 token 是按输出计费的,即便执行框架(harness)往往并不把它们展示给你,而调高投入程度(effort)会消耗更多这类 token。

输出 token 也决定了会话(session)的节奏。模型读取输入很快,但生成输出是一次一个 token,所以当一个回合(turn)让人感觉很慢时,几乎总是在写输出,而不是在读输入。漫长的等待通常意味着一个长答案正在路上。

用法

「这次重构会话把额度烧得飞快,可输入明明很少。」

「智能体在重写整个文件而不是打补丁。输出 token 的费率大约是输入的五倍——让它输出编辑(edit)而不是整文件,账单就会降下来。」

Parameters

模型(model)内部的那些数字——往往有数十亿个——在训练(training)期间被调校。模型所「知道」的一切都存在于其中。训练设定它们;推理(inference)在不改动它们的情况下使用它们。也称为「权重」(weights)。

从机制上讲,参数就是把输入变成输出的东西。下一个 token 预测(next-token prediction)是一场庞大的计算:上下文窗口(context window)里的 token 进入,被参数相乘运算,然后得出对下一个 token 的一个预测。模型内部没有事实数据库,没有代码查找表——只有这些数字,被排布成让计算倾向于产生有用输出的样子。模型能从训练中背诵出来的事实,比如某个标准库的 API,属于参数化知识(parametric knowledge):存储在参数里,而不是从任何地方检索来的。

值得内化的一个细节是,参数在训练之后就被冻结了。你在会话(session)里做的任何事都改变不了它们——你做的任何纠正、你给它看的任何代码库、它从中学到教训的任何错误,都改变不了。每一次会话跑的都是同一批数字。这就是为什么模型是无状态(stateless)的,为什么它的内建知识止步于知识截止时间(knowledge cutoff),以及为什么任何项目专属的东西都必须经由上下文(context)送达。改变参数的唯一途径是更多训练——而那实际上会产生一个不同的模型。

用法

「我们能不能在自己的代码库上微调它?」

「那会更新参数——之后就是一个不同的模型了。对于单个项目,把代码库作为上下文加载进来,几乎总是比重新训练便宜。」

Parametric knowledge

模型(model)从训练(training)中「知道」的东西,存储在它的参数(parameters)里。在训练时就被冻结——模型看不到自己的参数,也无法更新它们。细节在挤压中丢失:数十亿事实被塞进固定数量的参数里,罕见的那些便变得模糊。它是模型在常见话题上流畅表达的来源,也是它在不常见话题上凭空编造的来源。它是情境知识(contextual knowledge)的对立面。

参数化知识并不是以事实的形式存储的。训练从不给模型一个可以查阅的数据库;它调整参数直到模型能很好地预测文本,而一个能很好地预测关于某话题文本的模型,其行为就仿佛它懂这个话题。知识的可靠程度取决于某样东西在训练数据中出现的频率:一个有数百万个样例的话题会被准确复现,而一个只有寥寥几例的话题,模型会基于相似话题的样子来猜测。对模型而言,复现和猜测是同一个过程,所以它分不清自己在做哪一个。一个编造的答案会以和正确答案同样的流畅度送达。幻觉(hallucination)就是模型猜错了。

参数化知识也会老化。参数在知识截止时间(knowledge cutoff)就停止改变,所以在那个日期之后发布或改名的库,在参数里根本不存在,而一个变更过的 API,还被以它旧的形态记着。

对于这两类缺口——太罕见和太新——补救办法是一样的:这些知识没法加进参数里,所以只能作为情境知识来提供。

用法

「它写的 React 无懈可击,却在我们内部的 SDK 上瞎发明方法。」

「React 在参数化知识里很密集——数百万个训练样例。你的 SDK 不是,所以模型填进了看似合理的形状。把 SDK 文档加载进上下文(context)里。」

Permission mode

智能体模式(agent mode)中负责权限门控的那一部分——哪些工具调用(tool call)会触发权限请求(permission request),哪些会自动运行。这是在执行框架(harness)开始额外捆绑行为指令之前,模式系统最初的用途。

执行框架提供一整梯次的这类模式:

模式读取写入与 shell典型用途
只读 / 规划自动阻止调研、规划、审阅
默认自动询问日常的有监督工作
自动编辑自动编辑自动,shell 询问受信任的仓库、机械性改动
「Yolo」/ 全自动自动自动沙盒(sandbox)、离开电脑(AFK)运行

选择哪一档是安全性与打扰之间的权衡,而且两种失效模式都能被感受到。太紧,你就成了瓶颈:智能体(agent)每隔几秒就为无害的读取停一次,你在自动驾驶般地点击批准,批准就失去了意义——盖橡皮图章是两头不讨好的下场,占尽了打扰却毫无保护。太松,智能体就会编辑你本想先看一眼的文件、运行你本想先看一眼的命令。

宽松的这一端在沙盒内部最站得住脚,因为在沙盒里一次糟糕的工具(tool)调用的爆炸半径是被控制住的。在沙盒之外,大多数人会折中为自动批准读取,而对任何不可逆的操作保留人在回路(human-in-the-loop)。

用法

「它每次 grep 都暂停——彻底毁了那次离开电脑的运行。」

「把只读工具的权限模式放松,写入和 shell 仍然提示。在一次调研会话(session)里,大多数权限请求都是噪音。」

Permission request

执行框架(harness)在执行一个未被预先批准的工具调用(tool call)之前展示给用户的东西。模型(model)产生一个工具调用;执行框架不立即运行它,而是暂停并询问。批准,它就运行;拒绝,执行框架就把这次拒绝作为一个工具结果(tool result)报告回模型。这是执行框架为有风险或敏感的操作把人放进回路(human-in-the-loop)的机制。

一次权限请求的生命周期:

步骤谁发生了什么
1模型产生一个工具调用
2执行框架对照权限模式(permission mode)和任何已保存的批准进行检查
3执行框架已预先批准:立即执行。否则:暂停并展示该请求
4用户批准一次、在本次会话(session)剩余时间内批准,或拒绝
5执行框架执行该调用,或把拒绝作为工具结果发回

拒绝一个请求会引导智能体。模型会像读任何其他工具结果一样读取这次拒绝并对它作出反应——它会尝试另一种方法,或者问你更想要什么。大多数执行框架允许你在拒绝上附一条消息,这就把请求变成了一个引导点:「别那样,改用迁移脚本」会恰好在模型正决定下一步做什么的时候落地。

代价在于每个请求都是对你的一次同步等待。智能体(agent)会一直阻塞坐等,直到你回答,这在你盯着看的时候没问题,在你没盯着的时候就成了问题——一个不停触发请求的智能体没法被放着离开电脑(AFK)工作。权限模式就是那个旋钮:哪些调用自由运行,哪些先询问,最好再配一个沙盒(sandbox)让扩大自由集合变得安全。

用法

「它卡在一个权限请求上十分钟了——我当时在开会。」

「那是人在回路的代价。把安全的工具(tool)预先批准,这样请求就只在真正有风险的调用上才触发。」

Prefix cache

提供方(provider)一侧的存储,它让连续的模型提供方请求(model provider request)得以跳过对共享前缀的重复处理。当一个请求的开头与最近某个请求的开头相匹配时——相同的系统提示词(system prompt)、直到某一点为止相同的历史——提供方就复用它之前的运算成果,并把那些 token 作为缓存 token(cache tokens)以低得多的费率计费。

缓存之所以划算,是因为会话是仅追加式增长的。每个请求都把整个历史作为输入 token(input tokens)重发一遍(原因见那个词条),而在正常的会话(session)里历史只在末尾发生变化——每个请求都是前一个请求再加几条新消息。提供方把那段很长的共享开头处理一次,把结果存起来,然后从前缀结束的地方接着处理。没有缓存的话,一个 50 回合(turn)的会话就得为重复处理第一个回合而付费五十次。

缓存也会过期。一个条目能保持「热」多久因模型提供方(model provider)而异——通常是几分钟,而非几小时。让一个会话闲置超过这个窗口,下一个请求就会以全价重建一次前缀,之后缓存才恢复。这多半是执行框架(harness)构建者要操心的事;作为用户,可见的效果是长时间停顿之后的请求比之前的请求更贵。

用法

「为什么账单在会话进行到一半时突然飙升?」

「执行框架开始在每个回合往系统提示词里注入当前时间。前缀缓存在第一个变化的 token 处就断了,所以那之后的每个请求都按全价计费了。」

Primary source

原始形态的事实来源本身——代码、对话记录、原始日志、真实的 API 响应。不是对某样东西的转述;就是那样东西本身。它是二手来源(secondary source)的对立面。

如果你想知道你的代码库做什么,代码就是一手来源。文档、架构图和 README 都是对它的描述——写下时是准确的,但从那以后就各自按自己的节奏演变了。当一个智能体(agent)自信地断言了关于你项目的某个错误信息时,该问的问题是它当时是从哪个来源工作的:读了文档的智能体继承了文档的陈旧;读了代码的智能体读到的是当前的真相。

代价正是让一手来源无法成为默认选项的原因。把一份一手来源加载进上下文窗口(context window)是昂贵的——整个文件、整份记录,每个 token 都作为输入(input)计费,并争夺注意力预算(attention budget)。你为这份代价换来的是完整性:没有任何东西被别人对「什么重要」的判断预先过滤过。上个月写的一份摘要不可能包含今天才发现原来很关键的那个细节;一手来源里仍然包含它。

当精确度至关重要时,就伸手去拿一手来源——确切的签名、真实的报错、抛出异常的那一行。管理上下文(context)的很大一部分,就是在决定何时该为一手来源付费,何时用二手来源就够好了。

用法

「智能体说重试逻辑是指数退避,可我眼睁睁看着它在猛捶那个端点。」

「它是从设计文档里读来的。把它指向真正的重试模块——当行为重要时,就从一手来源工作。」

Progressive disclosure

只加载智能体(agent)此刻需要的上下文(context),其余的用上下文指针(context pointer)指向。借自 UI 设计,在那里它的意思是只向用户展示与其当前任务相关的控件,把其余的藏在一次点击之后。

这项技术之所以存在,是因为上下文的成本要付两遍。每个预先加载的 token 都会在每个回合(turn)作为输入 token(input tokens)计费,而且每个 token 都在消耗注意力预算(attention budget),无论智能体是否需要它。一个塞满了完整风格指南、部署操作手册和数据库约定的 AGENTS.md 会让智能体在所有这些方面都表现更差——对当前任务重要的那些指令,被不重要的那些稀释了。一个明显的征兆是:智能体无视你明知就在它上下文里的规则——它们确实在里面,但被埋起来了。

渐进式披露把这一切反转过来。让始终加载的那一层保持精简——每个主题一句话,外加一个指向细节所在位置的指针。智能体在写组件时才去读风格指南,在部署时才去读部署操作手册,而在修测试时两者都不读。技能(skill)就是内建进执行框架(harness)的这种模式:一段简短描述在每次会话(session)都加载,完整指令只在被触发时才加载。

仓库里的子目录 AGENTS.md 是同一招的「目录版」。始终加载的只是根上那一份;子目录的规矩藏在路径后面,智能体用工具(tool)摸到那里才亮出来,通常挂在这一次的工具结果(tool result)上,而不是改已经装好的系统提示词(system prompt)。

shop/
├── AGENTS.md              ← 会话开始就加载(全仓规矩)
├── frontend/
│   └── AGENTS.md          ← 「用 pnpm,组件放 src/components」
└── backend/
    └── AGENTS.md          ← 「用 poetry,迁移走 Alembic」

你说:「帮我改一下首页按钮颜色。」

  • 会话启动:系统提示词里只有根目录那份 AGENTS.md。frontend/、backend/ 的规矩故意不装——这轮用不上,塞进去只会占 token、稀释注意力预算(attention budget)。
  • 智能体去读 frontend/src/Button.tsx:工具第一次摸到 frontend/,这时才把 frontend/AGENTS.md 挂在这次工具结果后面。模型这才看见「用 pnpm、别写内联样式」。
  • 整段对话你都没让它碰后端:backend/AGENTS.md 从头到尾不会出现。
  • 同一会话里再读 frontend/ 下别的文件:这个目录已经披露过,不会再塞一遍。

用法

「我该把整份风格指南都倒进 AGENTS.md 吗?」

「别——用渐进式披露。把风格指南做成一个技能来引用,让智能体在真正需要写组件时才加载它。AGENTS.md 每个回合都要付 token 成本。」

「那 frontend 和 backend 各写一份 AGENTS.md 会不会一开会话就全装进去?」

「不会。根上那份进系统提示词;子目录那份等工具摸到对应路径才披露。」

Prototyping

让智能体(agent)快速搭建某样东西的一个粗糙版本,适用于当对话过于低保真、而你需要一个真实的产物来讨论的场景。

盘问(grilling)在对话中解决设计决策。对话很廉价,但它是低保真的:有些问题没法用言语回答——一个交互体验起来是什么感觉、一个 API 的形状在真实的调用代码里是否顺手、一个布局在真实数据规模下是否行得通。访谈碰到某个问题,而你诚实的回答是「我不知道,我得看到它才行」。过了那个点,讨论就开始打转。不如让智能体把东西搭出来,看一看,然后带着一个答案回到对话中。

智能体降低了搭建的成本,这正是让这套做法变得可行的原因。一个过去要花一天来做原型的粗糙版本,现在只要几分钟,所以值得经常这么做。它是一种人在回路(human-in-the-loop)技术:原型就在那里供你作出反应。

你通常不会看一眼就停下。跟原型一起迭代——作出反应、要求改动、再作出反应——这样每个回合都对着真实的产物解决又一个决策,其保真度比对话所能允许的更高。

原型不一定得全是草草搭就的。你可以把你真正在评估的那些部件做到生产质量,这样当决策落定时,你曾对之作出反应的那个组件或 API 就能转移进真正的代码库。这使得原型成为规格(spec)可以引用的重要素材。

用法

「我们花了半小时争论向导应该是一页还是分三步。」

「言语没法定夺——让智能体把两种都做个原型。我们点着走一遍,五分钟就有数了。」

Sandbox

智能体(agent)在其中运行的一个隔离的环境(environment)——一个容器、虚拟机、临时文件系统(filesystem),或权限受限的 shell。它限制智能体行为的爆炸半径:即便智能体运行了破坏性命令或抓取了恶意的东西,损害也被控制住。它是让离开电脑(AFK)运行变得可行的安全基底。

沙盒和权限模式(permission mode)从相反的两端解决同一个问题。权限在一个操作运行之前询问;沙盒则限制这个操作一旦真的运行了能触及到什么。权限需要你在回路(human-in-the-loop)中运行——每个提示都是一次打扰——而一个不停询问的会话几乎谈不上自主。沙盒消耗的是基础设施而非注意力:隔离越强,需要询问的问题就越少。

隔离分等级:

等级它是什么它能控制住什么
受限 shell围绕每条命令的操作系统级约束项目之外的写入、网络访问
容器全新的文件系统、不挂载任何凭证、用后即弃智能体对它自己机器所做的任何事
虚拟机 / 云一台完全独立的机器,通常由执行框架(harness)提供一切,包括内核级的逃逸

没有哪个沙盒能控制住的:那些正当地离开了沙盒的操作。拥有你 git 凭证的智能体可以推送;拥有网络访问权的智能体可以调用生产环境的 API。先决定什么可以越过边界,再决定要把边界筑得多厚。

用法

「我想让它整晚运行在绕过权限(bypass-permissions)模式下,但我还没准备好那么做。」

「把它放进沙盒里——全新容器,不挂载凭证,禁止对外网络。最坏情况它把自己的文件系统炸了,你把容器丢弃就行。」

Secondary source

对一手来源(primary source)的转述,隔了一层——描述代码的文档、描述对话记录的摘要、描述搜索结果的报告。相比它所描述的来源,把它载入上下文窗口(context window)更省,但天生有损:写它的人决定了什么重要,而他丢掉的东西,对只拿到摘要的读者来说是看不见的。

大量上下文(context)工程做的就是制造二手来源。压缩(compaction)把会话(session)历史变成一段摘要,为下一个会话播种。子智能体(subagent)把自己的上下文烧在一次嘈杂的搜索上,返回一份简短报告。交接产物(handoff artifact)把一个会话的决策浓缩成下一个会话会读的文档。记忆系统(memory system)把一个会话学到的东西提炼成笔记。每一种都在做同样的取舍:用保真度换取余量。

二手来源有两种失败方式。一是有损——丢掉了 schema 决策的压缩摘要、没提到边界情况的报告。二是漂移(drift)——一手来源变了,转述没跟上,于是文档以本季度的自信描述着上季度的架构。当智能体(agent)基于一份以任一方式失败的二手来源行动时,它会自信地根据错误信息工作;修正之道是把它送回一手来源。

这两种失败都不意味着二手来源是个错误。上下文窗口是有限的,一手来源又昂贵;没有摘要、报告和交接文档,大的东西根本装不下。真正的技巧在于知道哪些细节能挺过损耗——以及当某个细节挺不过时,去对照一手来源核实。一份做得好的二手来源会带一个上下文指针(context pointer)指回原文——指名对话记录出处的摘要、指名所描述文件的文档——这样当转述不够用时,读者可以顺着指针回溯,而不是从损耗里凑合。

用法

「交接文档说鉴权做完了,但新会话总是发现 token 刷新是坏的。」

「那份文档是二手来源——上一个会话写下的是它所相信的,不是事实。让新会话去跑鉴权测试,相信一手来源。」

Session

与智能体(agent)一次有边界的交互运行。它从空开始,累积消息、工具结果(tool result)和读过的文件,在被清空(clearing)、关闭,或被压缩(compaction)成一个全新会话时结束。会话就是那个把上下文窗口(context window)_填满_的东西:如果说上下文窗口是那个盒子,会话就是慢慢把它填满的东西。大到单个上下文窗口装不下的工作,必须拆分到多个会话里。

会话的消息历史就是智能体的工作记忆。模型(model)是无状态的(stateless),所以它看似记得的一切——你要求了什么、测试说了什么、它三轮前决定了什么——都在消息历史里,随每一次模型提供方请求(model provider request)重新发送。不在会话里的东西,对智能体来说就不存在。

那份记忆随会话终结。新会话从零开始:昨天会话结束时对你代码库了如指掌的那个智能体,今早对它一无所知。能留存下来的是文件系统(filesystem)——一个会话里写下的文件可以被下一个会话读取,交接(handoff)、记忆系统(memory system)和 AGENTS.md 都依赖于此。

会话在哪里结束由你选择。会话里的每样东西都会影响之后的每个回合(turn),所以在一个会话里做的无关任务会留下残渣,给下一个答案染色。一个会话一个任务能保持上下文相关;完成一个任务是清空的自然时机。

用法

「一个会话能跑多久才会散架?」

「看工作内容——聚焦的重构比开放式研究保持锐利更久。一旦会话臃肿起来,就交接或压缩,别硬撑过去。」

Session database

执行框架(harness)用来持久保存会话(session)的本地数据库——会话卡片(id、标题、模型、时间)加上完整文字记录(transcript)。进程关掉以后,对话还在;网关每次往往只收到最新一条用户消息,也靠它把该会话的历史捞回来。常见实现是 SQLite;有的产品把文件叫做 state.db(例如放在用户目录下的 ~/.hermes/state.db)。

它不是记忆系统(memory system)。记忆系统存的是精选事实,会在新会话开始时装进系统提示词(system prompt);会话库存的是原话流水账,默认不进系统提示词,按需检索或拼进当前会话的消息历史。许多实现会再挂一张全文检索表(FTS),让智能体(agent)搜「上周聊过的那个 bug」,而不必把所有旧会话都塞进上下文窗口(context window)。

避免:「记忆」——会话库是记录,不是精选过的长期记忆。

用法

「我把终端关了,昨晚那次聊天还在吗?」

「在会话库里。重新打开同一条会话,文字记录还在;但系统提示词里的记忆快照,仍是这次启动时读到的那一份。」

Skill

一种打包成单元的可传授能力——把做好某一件事的指令和资源打包,存放在环境(environment)中,直到一个上下文指针(context pointer)为手头任务把它拉进上下文窗口(context window)。它是脚手架(harness)中渐进式披露(progressive disclosure)的单位。

Skill 是一项开放标准,定义在 agentskills.io——最初由 Anthropic 开发,此后被大多数主流执行框架采纳,因此写一次的 skill 可以跨执行框架使用。其格式是一个文件夹,包含:

  • 一个 SKILL.md 文件——元数据(至少含名称和描述)加上指令本身
  • 可选的、智能体(agent)可运行的脚本
  • 可选的、指令所指向的模板和参考资料

默认只有名称和描述留在上下文(context)里。当智能体的任务匹配时,它才加载其余部分。在那之前,无论完整指令多大,这个 skill 几乎不占空间——就一两句话的 token 量。

这一点把 skill 与 AGENTS.md 区分开来:后者无论任务是什么,都会被加载进每个会话(session)。skill 是在某种特定工作出现时才被读取——发布、搭建新服务、写迁移脚本——其余时间被忽略。

避免:「工具(tool)」——工具是智能体去_调用_的东西;skill 是它去_阅读_的指令。

用法

「部署操作手册该放哪儿?」

「作为一个 skill——智能体只在任务涉及部署时才加载它。放在 AGENTS.md 里,会为一件我们每周才用一次的事在每个回合(turn)上烧 token。」

Smart zone

会话(session)早期,智能体(agent)处于「smart zone(聪明区)」——锐利、专注、回忆能力好。随着会话增长,它漂移进「dumb zone(笨拙区)」:更马虎、健忘、错误更多——忠实性幻觉(hallucination)也更多。同样的模型(model),同样的执行框架——只是上下文(context)更多了。这是注意力退化(attention degradation)的体感效应。在前沿模型上,笨拙区通常在 125K–150K token 附近开始——不过这一点存在争议。会话臃肿时就清空(clearing)或压缩(compaction);别硬撑。

这种衰退是渐进的,因而很容易被忽视。没有错误消息,也没有可见的边界;智能体只是开始表现得略差,然后明显更差。常见迹象:它忘了你二十轮前给的指令,重犯一个已经改正过的错误,或自信地断言某个与上下文相矛盾的东西。因为下滑是平滑的,惯常反应是硬撑并重新解释——这又添了更多上下文,让问题更糟。

这两个区并不随上下文窗口(context window)上限走。一个会话可能已深陷笨拙区,而窗口大部分还空着:上限是执行框架拒绝继续的地方,但质量在那之前很久就掉下去了。围绕聪明区来规划,而不是围绕窗口——一个任务的实用预算是智能体能良好工作的 token 量,不是它技术上能容纳的 token 量。

聪明区是一份预算,无关的工作会花掉它。会话里做的每个任务都消耗 token,所以在同一会话里开始第二个任务,意味着起点更靠近笨拙区。一个会话一个任务,能让每个任务享有会话中最锐利的部分。当单个任务大于一个聪明区时,就拆分它:在自然的边界处交接(handoff)或压缩,让一个全新会话去做下一块。

用法

「它把前三个组件做得很漂亮,第四个却搞砸了。」

「你出聪明区了——同一个模型,只是现在深入笨拙区了。压缩并重新加载计划,下一个组件就能搞定。」

Spec

一份描述跨多会话(session)工作的交接产物(handoff artifact)——讲的是要构建什么,而不是每个会话如何完成自己那一份。它随工作推进而变动。由票据(ticket)组成。

spec 之所以存在,是因为会话是一次性的,而大工作不是。任何需要超过一个上下文窗口(context window)的努力,都需要一个上下文(context)之外的归宿——在智能体(agent)环境(environment)中某个能挺过清空(clearing)的地方,无论是仓库里的一个文件、一个 GitHub issue,还是智能体能访问的 issue 追踪器。spec 就是那个归宿:目标、约束、迄今做出的决策,以及带状态的票据清单。任何全新会话都能读它,不必继承上一个会话累积的噪音,就知道工作进展到哪儿了。

spec 有一些可辨认的风格,大多沿袭自团队既有的记录方式。产品需求文档(PRD)偏向面向用户的「是什么」和「为什么」——功能、行为、验收标准。_设计文档_或 RFC 偏技术——选定的方案、被否决的替代方案、权衡。在小的那一端,一份带票据清单的朴素 plan.md 对一个多会话功能也能起同样作用。风格不如角色重要:对智能体来说,这些东西都是同一样东西——它在每个会话开始时都会读的、持久的意图声明。

用法

「这些都该放在一个会话里做吗?」

「不,把它写成一份 spec——拆成票据,每个在自己的会话里跑。想在单个上下文里做完整件事,你还没做到一半就会撞上笨拙区(dumb zone)。」

Stateful

把信息往前带。会话(session)在跨回合(turn)时是有状态的——上下文(context)随会话运行而累积,这正是长会话漂移进笨拙区(dumb zone)的原因。给智能体(agent)加上一个记忆系统(memory system),把信息持久化进环境(environment)并在未来会话开始时重新加载,就能让它跨会话有状态。模型(model)永远不是有状态的;任何看似的连续性都是执行框架在重新喂上下文。它与无状态(stateless)互为对应。

各层的状态存在何处:

层级有状态吗?如何做到
模型(Model)从不参数(parameters)是冻结的;它只看到每次请求里的东西
会话(Session)跨回合执行框架把每条消息和工具结果(tool result)追加到上下文
执行框架跨会话记忆文件、AGENTS.md、交接产物(handoff artifact)——写下来,稍后重新加载
环境(Environment)总是无论有没有会话在运行,文件都持久存在

每一层的有状态性,都是靠重新读取下一层存下来的东西构建的:会话感觉连续,是因为执行框架把消息历史重新发给无状态的模型;智能体跨会话记得,是因为执行框架从环境重新加载文件。任何状态都从不存储在模型本身里。

状态并非总是想要的。往前带的每样东西都会影响接下来的东西,所以会话早期做的一个错误假设也会被带下去。清空(clearing)就是刻意扔掉会话状态、从写下来的东西重新开始的动作。

用法

「它记住了我昨天的偏好——这是不是意味着模型学会了它们?」

「不,智能体之所以有状态,是因为执行框架把它们写进了记忆文件并在会话开始时重新加载。模型本身对昨天一无所知。」

Stateless

不把信息往前带。模型(model)在跨模型提供方请求(model provider request)时是无状态的——每次请求都重发完整的上下文窗口(context window),因为模型没有别的办法看到任何其它东西。智能体(agent)默认跨会话(session)无状态:新会话从空开始,没有先前会话的任何痕迹。它与有状态(stateful)互为对应。

模型本身是永久无状态的:它的参数(parameters)在训练(training)后就冻结了,你在推理(inference)时做的任何事都改不了它们。模型不会从你的纠正中学习,不会记得昨天被告知过同样的事,也不会在了解你——无论对话感觉多么不像如此。会话内那种连续的感觉是由执行框架制造的,它保留对话记录并随每次请求重发。模型不是在记住对话;它是在重读对话。

实际后果是:如果你想让某样东西跨会话被记住,你就得把它写在某个智能体会回头读的地方。AGENTS.md 文件、记忆系统(memory system)和交接产物(handoff artifact)就是干这个的——这些文件会被加载进未来会话的上下文(context),替代模型没有的那份记忆。当智能体一再犯一个你之前纠正过的错误时,问题不是它为什么没学会——它不能学——而是那条纠正该写在哪里,才能让每个未来会话都读到。

用法

「为什么我每次清空(clearing),它就忘了那个约定?」

「模型是无状态的——新会话从空开始。如果你想让它被带下去,就写进 AGENTS.md 或一个执行框架会在会话开始时加载的记忆文件里。」

Subagent

由另一个智能体(agent)通过工具调用(tool call)派生出的智能体。它在自己的会话(session)里、用自己的上下文窗口(context window)运行,并返回单个工具结果(tool result)。它有别于交接(handoff)——父方明确期待一个返回值;而交接没有返回路径。不能再派生下一级子智能体——这棵树只有一层深。子智能体的存在是为了隔离上下文(context),而不是为了组合层级。

要点是把嘈杂的工作挡在父方的上下文之外。一次宽泛的搜索或一场漫长的读文件远征会产出成页的工具结果,其中大部分只在找到答案之前那一会儿有用。在父方里跑,这一切会在会话余下时间里都留在父方的上下文里。在子智能体里跑,噪音填满的是一个一次性窗口——只有最终报告落进父方的上下文。那份报告是一份二手来源(secondary source):父方拿到的是子智能体对所见的转述,不是原始结果,所以报告漏掉的任何东西对父方都是看不见的。

子智能体也能并发运行——父方可以一次扇出好几个,各自处理独立的工作块。

用法

「grep 结果把我的上下文撑爆了。」

「派生一个子智能体去做搜索——它会把自己的上下文窗口烧在噪音上,然后返回你真正需要的那两个文件路径。」

Summary

压缩(compaction)产出的那份浓缩产物:把一个会话(session)的历史总结成一段结构化文本,用来为一个全新会话播种。它是一份二手来源(secondary source)——原始的文字记录(transcript)是一手来源(primary source),摘要以细节换取上下文窗口(context window)的余量。摘要不是自动产生的文字堆砌,而是一份写给「接班的那个会话」的交接单。

摘要通常不由主模型(model)亲自撰写,而是交给一个更廉价的辅助模型按固定模板生成——它是「输入长、输出短、结构固定」的活,不需要顶级推理能力,且触发频繁,用小模型更省。因为摘要是被模型写出来的,所以它可以被提示引导:一句「保留 schema 决策」就能让产物更审慎。它也是增量的——同一个会话若之前已压过、已有一份摘要,再次压缩时是在旧摘要上改,而不是从头重写。

一份写得好的摘要有几个稳定特征:开头声明仅作背景参考,提醒模型只响应摘要之后的最新消息,别把里面的陈旧待办当成还要继续做(所以许多区块标题带 Historical);已完成的事写成带日期的过去式;敏感信息(如密钥)脱敏成 [REDACTED];并尽量留一个上下文指针(context pointer)指回原始文字记录,这样摘要弄丢的细节还能顺着指针回溯原文,而不是从损耗里凑合。

避免:把摘要当成无损的会话备份。它天生有损——写它的模型替你决定了什么重要,被丢掉的东西对只读到摘要的后继者是看不见的。真正关键的决策应落到磁盘上的计划文档或交接产物(handoff artifact)里,那里没有任何摘要能弄丢它们。

一份真实摘要大概长这样(以「给博客加 callout」这段被压掉的会话为例):

[HISTORICAL CONTEXT — 仅作背景参考。只响应本摘要之后的最新消息;
memory.md / user.md 仍然有效。]
 
## Historical Task Snapshot
 
把 AI_术语.md 里所有「用法」块转成高亮块(callout)。
 
## Goal / Constraints & Preferences
 
- 目标:全站排版统一,用 Obsidian / Quartz 通用的 callout 语法
- 偏好:中文简体;严格盘古之白;改动最小化
 
## Completed Actions
 
1. 确认 Quartz 已启用 callouts(quartz.config.yaml:68)— 通过
2. 脚本批量转换 69 个「用法」块为 > [!example] — 完成
3. 校验:链接 593 处无损、标题 74 个不变 — 通过
 
## Active State
 
- 工作目录:content/;改动文件:AI_术语.md
- 状态:转换完成,已删除 .bak 备份
 
## Historical In-Progress / Blocked
 
(无)
 
## Key Decisions / Resolved Questions
 
- 选 `> [!xxx]` 而非 ```ad-xxx:后者 Quartz 不认,线上会退化成代码块
 
## Historical Pending Asks / Remaining Work
 
- 曾问是否把「避免」块也转 callout —— 未决,默认不自动做
 
## Relevant Files / Critical Context
 
- content/AI_术语.md(1300+ 行)
- API_KEY = [REDACTED]

留意几处对应前文的设计:开头的 HISTORICAL CONTEXT 声明、带 Historical 的区块标题、过去式的 Completed Actions、脱敏成 [REDACTED] 的密钥。

用法

「这个会话太长了,压一下吧——摘要里记得保留我们定的 schema 决策。」

「已经压过一次了,这次是在旧摘要上增量更新。」

「那被它丢掉的那段调试过程呢?」

「摘要留了上下文指针,跟着它回去读原始记录就行。」

Sycophancy

自信地一味附和的模型(model)输出。由训练(training)造成:模型被塑造成偏好人类喜欢的答案,而人类往往更喜欢被认同,而不是被告知自己错了。于是模型学到附和是有回报的——哪怕这种附和是错的。

表现为:

  • 在反驳下退缩——你一说「你确定吗?」它就把一个正确答案反转掉。
  • 夸奖糟糕的输入——在分析之前就赞同你那个漏洞百出的计划很出色。
  • 带偏见的框定——当你暗示是你写的时,评审偏正面;暗示是别人写的时,偏负面。同一份产物,不同的裁决。
  • 模仿——把你的错误当作确认重复给你听。

诊断测试: 没有你的引导,模型会这么说吗?如果唯一变的是你的语气或框定,那就是谄媚,而不是分析上的真正转变。

修正: 隐藏你的偏好。用中立的措辞——「评审这段代码」而不是「这段代码好吗?」。

避免:把任何恰好讨你欢心的错误答案都称作「谄媚」。没有那个诊断测试,这个词的价值不比「错了」更多。

用法

「它说我的重构计划看起来很棒,然后我问『你确定吗?』它就把整件事全收回了。」

「典型的谄媚——它先附和是因为你听起来自信,然后退缩是因为你听起来怀疑。计划的质量没变,是你的语气变了。清空(clearing)后不带任何倾向地重新问。」

System prompt

执行框架预置到每一次模型提供方请求(model provider request)之前的指令——智能体(agent)的常驻简报:它是谁、如何行事、能调用哪些工具(tool)、遵循什么约定。通常在一个会话(session)内保持稳定。

系统提示词由执行框架厂商写,不是你写的,而在编程执行框架里它很大——常常有数万 token 的行为规则、工具描述和边界情况处理,这些都作为输入 token(input tokens)在每个回合(turn)上支付。你自己的常驻指令搭它的便车:像 AGENTS.md 这样的文件会在会话开始时紧挨系统提示词被加载,所以模型(model)在见到你的消息之前,就把厂商的简报和你的一起读了。

因为它在每次请求上都相同,它构成了前缀缓存(prefix cache)的开头——这也是执行框架让它在整个会话里保持固定、而不是边走边改的部分原因。

模型被训练成把系统提示词优先于用户消息。所以当一个智能体坚持一个你从没要求的约定,或以你甩不掉的方式格式化输出时,它通常是在服从它的系统提示词——而你的消息在这场争论里输了。有些执行框架是可定制的:它们给你完整访问系统提示词的权限,这样你就能读到智能体实际被告知了什么,并去修改它。

用法

「两个执行框架,同一个模型,对同一个提示词行为完全不同。」

「不同的系统提示词。一个是为简洁的代码编辑调的,另一个是为解释调的——分歧就住在那里,在你的消息到达之前。」

Ticket

一份圈定单个会话(session)工作量的交接产物(handoff artifact)。可以独立存在,也可以作为一份 spec 的子项挂在其下。票据之间可以互相阻塞,因此工作的顺序是从它们的依赖图里生长出来的,而不是一条线性计划。

它的决定性约束是尺寸:一个会话。一张票据应该能在会话漂移出聪明区(smart zone)之前完成——而这个约束是可检验的。如果你票据上的会话经常在工作做完之前就退化,那说明票据太大了;拆分它们。如果每个会话把大部分上下文(context)花在准备上,只做了五分钟活儿,那说明它们太小了;合并它们。

一张好票据是为一个没有其它上下文的读者写的。目标、验收标准,以及指向相关文件和决策的上下文指针(context pointer)——足够让会话开始工作,而不必重新推导上一个会话所知道的东西。

依赖图也是解锁并行性的东西。相互独立的票据——图的叶子——各自可以同时在自己的会话里跑。这是同时运行多个智能体的一种有效方式。

用法

「迁移 spec 我该从哪儿开始?」

「看票据图——schema 变更阻塞回填,回填阻塞 API 切换。挑一个叶子,为它跑一个会话。」

Token

模型(model)读写的原子单位。大致是词大小,但不完全是——常见词是一个 token,罕见或长的词会拆成几个。上下文窗口(context window)大小、成本和延迟都以 token 计数。

文本经由一个分词器(tokenizer)变成 token:一套固定的、含数万个片段的词表,在训练(training)之前学得,它把任何输入切成一串词表条目。模型从不见字符或词——每一段文本在进来时都被转成 token,而下一个 token 预测(next-token prediction)在出去时一次产出一个 token。

作为经验法则,一个 token 约为四分之三个英文词,所以一千个 token 大致是 750 词。代码就没那么可预测了:常见关键字和惯用法分词紧凑,而生成的标识符、哈希、base64 数据块和压缩后的输出,每个「词」会拆成许多 token。规律是:在分词器源材料里频繁出现的文本得到短而高效的编码;没出现过的则被剁成许多小片。像 a3f9c2e1 这样的哈希从没在哪儿出现过,所以它拆成许多 token,而 function 是一个。这就是为什么一个看起来小、却塞满不寻常字符串的文件,会占用出人意料的一大块上下文窗口。

Token 是其它一切据以度量的单位。成本按 token 计——提供方分别对输入 token(input tokens)和输出 token(output tokens)计费。速度是每秒 token 数,因为输出是一次一个 token 生成的。上下文窗口是固定数量的 token,所以你文件的 token 数决定了能装下多少。

避免:「词」——token 边界不匹配词边界,而每秒 token 数 / 每美元 token 数才是真正重要的单位。

用法

「这个提示词会有多大?」

「拿它过一遍分词器——schema 很紧凑,但 JSON 的键很怪,所以它们会拆成比你想的更多的 token。」

Tool call

模型(model)指名一个工具(tool)及其参数的输出——只是结构化文本。它本身什么也不做;得由执行框架去读它并执行。它由模型在一次模型提供方请求(model provider request)中产出。

一次工具调用的生命周期:

步骤谁发生了什么
1模型从系统提示词(system prompt)中的描述得知有哪些工具存在
2模型发出一个调用——工具名加参数,通常是 JSON——然后停下
3执行框架解析这个调用,并对照权限模式(permission mode)检查它
4执行框架如果被允许就执行它
5执行框架在下一次请求中把结果作为工具结果(tool result)送回

智能体(agent)工作的一轮(turn),通常是许多次这样的往返串在一起。

因为这个调用和其它一切一样是由下一个 token 预测(next-token prediction)生成的,所以它可以像任何模型输出一样出错:一个不存在的路径、一个命令没有的标志、貌似合理而非正确的参数。执行框架执行的是被写下来的东西,不是本意——一个打错的路径不会优雅地报错,它会去编辑错误的文件。

用法

「它说它跑了测试,但文件时间戳没变。」

「看对话记录——它到底发出了一个工具调用,还是只是描述了运行它们?模型产出这个调用,但如果执行框架没执行它,那就什么也没发生。」

Tool result

执行框架执行完一次工具调用(tool call)后送回来的东西——文件内容、命令输出、错误。它是智能体(agent)观察环境(environment)的唯一视角。它在_下一次_模型提供方请求(model provider request)中回到模型(model),模型在那里决定拿它怎么办。工具调用和工具结果是同一次交换的两端,都在一轮(turn)之内。

一次工具结果的生命周期:

步骤谁发生了什么
1执行框架执行工具调用——运行命令、读取文件
2执行框架捕获结果:输出、内容或错误
3执行框架把它作为一条消息追加到上下文(context)
4执行框架在下一次模型提供方请求中把整个上下文发给提供方
5模型读取结果并决定:再来一次工具调用,还是给出最终答案

结果会在会话(session)余下时间里留在上下文里。工具结果通常是编程会话上下文的大头:每次读文件、每次跑测试、每次搜索都完整落进来,在早已没用之后还继续占着 token。少数几个大结果——一份啰嗦的测试日志、一个整读进来的生成文件——能比对话本身更快地把会话推向上下文窗口(context window)的边缘。

因为结果是模型所见的全部,模型没有办法核查它背后的环境。如果输出被截断了、命令悄悄失败了,或者执行框架返回的是错误而非内容,模型就从它拿到的东西去推理。当智能体对你系统的图景看起来不对时,工具结果就是该查的地方:对话记录里某处有个结果,说的东西和你所知的事实不一样。

用法

「它推理这个文件的样子,就好像它是空的。」

「工具结果回来的是一个权限拒绝,不是内容。模型只看到了那个错误字符串——它没有别的办法看到那个文件。」

Tool

执行框架暴露给智能体(agent)去调用的一个函数——Read、Write、Bash、Search。工具是智能体感知并作用于环境(environment)的方式:它除了通过工具结果(tool result)就看不见环境,除了通过工具调用(tool call)就改不了环境。每次工具调用要多花一次模型提供方请求(model provider request),因为结果得回到模型,模型才能决定下一步做什么。

大多数编程智能体自带的工具:

工具它做什么
Read把一个文件的内容作为工具结果返回
Write在文件系统(filesystem)里创建或编辑一个文件
Bash运行一个 shell 命令并返回其输出
Search在代码库里查找匹配某个模式的文件或文本

一个工具由三样东西定义:一个名称、一段说明它做什么的描述,以及一个描述其参数的 schema。执行框架随每次请求把这些定义发给模型(model),而模型选一个工具的方式和它产出其它一切的方式一样——靠写 token,这里写的是一个带参数的结构化调用。模型从不自己执行任何东西;执行框架读这个调用、运行那个函数,并把结果送回。

工具清单设定了智能体能做什么。一个能干的模型配上一套狭窄的工具就是个狭窄的智能体:它会把一切都从它所拥有的东西里走一遍,这也是智能体如此依赖 Bash 的原因——shell 是一个能触及系统大部分的工具。要干净地给智能体一项能力,就为它加一个工具;MCP 是从执行框架外部插入工具的标准。

工具定义在每次请求上都占用上下文(context),所以一大套工具在任何工具被调用之前就有一份常驻成本——而许多描述相近的工具会让模型更难挑对那个。

用法

「智能体能直接查询 staging 吗?」

「给执行框架加一个 psql 工具,范围限定为 staging 上只读。没有对应的工具,智能体对文件系统之外的任何东西都是瞎的。」

Training

设定模型(model)参数(parameters)的过程,做法是让它接触海量文本,并调整参数以改进下一个 token 预测(next-token prediction)。这是一个一次性的、昂贵的过程,由模型提供方(model provider)完成。它既包括预训练(大规模的那次运行),也包括后训练(后续的精修,如指令遵循和安全性);这个区分在本词典的层面上无关紧要。

机制是大规模的重复:给模型看一段文本,让它预测下一个 token(token),把参数朝着实际的下一个 token 微调,如此在数万亿个 token 上重复。什么都不作为事实或规则存储——模型「知道」的一切,都是它变得更擅长预测的副作用,被压缩进参数,成为参数化知识(parametric knowledge)。

有两个后果日常要紧。训练在某个时间点结束,所以模型有一个知识截止点(knowledge cutoff)——它没见过你上个月升级到的那个库版本。而训练不是你能做的事:当模型不知道你的代码库、你的约定或你的内部 API 时,修正之道从来不是「教模型」——而是把那些材料放进上下文(context),那是你能控制的唯一输入。

用法

「我们能让它了解我们的内部 API 吗?」

「不能通过训练——那是模型提供方一个长达数月的过程。改把 API 文档加载进上下文,那才是你真正拥有的杠杆。」

Transcript

一个会话(session)从开始到现在的完整对话记录:用户消息、模型回复、工具调用(tool call)和工具结果(tool result)。它是原话级的流水账,不是记忆系统(memory system)里那些「提炼过的事实」。通常每个回合(turn)结束都会追加写入会话库(session database),和「这轮有没有值得写进记忆文件」无关——记忆可写可不写,文字记录几乎总会写。

进程还在时,当前会话的文字记录往往已经在内存里,发给模型的那串消息历史就是从它来的。进程没了,或入口每次只送来一条新消息(典型是消息网关),就必须从会话库把该会话的文字记录拉齐,否则每一条消息都会被当成失忆的新会话。

避免:「日志」——太含糊,也容易和技术日志、审计日志混在一起。

用法

「它上周说用 Postgres,怎么这轮又问我数据库?」

「那句话在文字记录里,不在记忆文件里。新会话的系统提示词(system prompt)不自动带上旧记录;要么搜会话库,要么把它写进记忆。」

Turn

一条用户消息,加上智能体(agent)作为回应所做的一切,直到它把控制权交还给用户为止。它包含一次或多次模型提供方请求(model provider request)——如果智能体调用工具(tool),就是很多次。一个澄清性提问会结束这一个回合;你的回复开启下一个回合。层级是会话(session)> 回合 > 模型提供方请求。

让「回合」值得命名的地方在于,它的长度是智能体的决定,不是你的。你交出一条消息;智能体决定在交还之前串起多少次工具调用。一个回合可以是一句话的回答,也可以是二十分钟的阅读、编辑和跑测试。这是同一个属性的两个角度:长的回合是让 AFK 工作成为可能的东西,长的回合也是无人监督下出岔子的地方——等智能体交还时,它可能已经从你的本意上漂移出很远。

回合也是引导(steering)的天然单位。一个回合内的一切都在你缺席时发生;回合与回合之间的空隙才是你重新定向的地方。大多数执行框架缓和了这一点:你可以中途打断以停下智能体并重新定向它,或在它工作时打一条消息,这条消息会在这个回合完成后被读取。如果你发现自己反复不满意回合的落点,修正之道通常是要求更小的回合——先出计划、一次一步——用自主性换取更频繁的、可供引导的空隙。

用法

「一个回合花了两分钟?」

「它在那个回合里做了十四次工具调用(tool call)——每一次都是一次单独的模型提供方请求。延迟层层累加,智能体才终于交还给你。」

Vibe coding

一种工作模式:用户接受智能体(agent)的代码而不做人工评审(human review)。diff 被当作不透明的东西——重要的是程序表现如何,而不是里面是什么。自动化评审(automated review)和自动化检查(automated check)可能仍在跑;vibe coding 对这两者都不置一词。

这个词来自 Andrej Karpathy,他在 2025 年初创造了它:你「完全交给氛围」并「忘记代码甚至存在这回事」——描述你想要什么、接受返回的东西,并靠运行它来评判。

Vibe coding 用检视换速度。读 diff 通常是智能体驱动工作里最慢的一步,所以把它去掉就移除了主要瓶颈。对于失败代价低廉的代码——原型(prototyping)、一次性脚本、内部工具——这是一笔合理的交易。风险随代码的寿命和利害而放大。

代价晚些才到。vibe 出来的改动累积成一个没人读过的代码库,而行为是唯一被检查过的东西——所以任何行为不会暴露的东西,比如写进日志的一个密钥、一个漏掉的边界情况,或悄悄出错的数据处理,都会在无人看见的情况下发布出去。有人第一次调试这个系统之时,就是有人第一次读这段代码之时。人工评审既已不在,任何仍在跑的自动化验证——测试、类型、自动化评审——就是代码唯一通过的关卡。

避免:把「vibe coding」当作「低质量 AI 编程」的同义词——这个词命名的是评审姿态,不是产出的代码。

用法

「你读过它在鉴权流程里改了什么吗?」

「vibe 出来的——登录还能用,我就检查了这个。」

「推之前把 diff 读一遍,在鉴权上 vibe 就是密钥泄进日志的方式。」


RAG 补充术语

以下术语来自 AI_RAG 笔记,与检索增强生成相关,一并收录。

ANN

Approximate Nearest Neighbor,近似最近邻。

当我们把文本都转成高维向量后,检索就变成「在海量向量里找与 query 向量最近的几条」。如果逐个精确计算距离(暴力搜索),在高维、大规模下开销无法接受——传统数据库(如 MySQL)的索引在高维空间也基本失效。

ANN 的思路是用一点点精度换取巨大的速度:不保证找到绝对最近的邻居,而是以极高概率找到「足够近」的若干条。它是向量数据库(FAISS、Chroma 等)能实时检索的核心算法。

BM25

一种词频-逆文档频率加权的排序函数,是关键词检索(keywords search)的经典算法。

它给「query 与文档的匹配程度」打分,核心直觉有三点:

  • 词频(TF):query 里的词在文档中出现越多,得分越高——但有饱和上限,出现 100 次不会比 10 次强 10 倍。
  • 逆文档频率(IDF):越稀有的词区分度越高,命中它更能说明相关(如 ECONNRESET 比 the 有价值)。
  • 文档长度归一化:避免长文档单纯因为字多而占便宜。

BM25 擅长精确关键词匹配,正好补上向量检索「语义压缩会丢失罕见 token」的短板,所以两者常组成混合检索(hybrid search)。

Transformer

一种基于自注意力机制(self-attention)的序列建模架构,是当今几乎所有大模型的基础。

它的核心思想是:处理一个词时,不像 RNN 那样只顺序看前后,而是让序列里每个位置都能「一次性」关注到其余所有位置,并按相关性分配权重。这带来两个关键优势:

  • 可并行:整段序列同时计算,不必像 RNN 逐字递推,训练效率大幅提升。
  • 长距离依赖:任意两个词之间是一步直连,远距离的语义关联不会像 RNN 那样随距离衰减。

在 RAG 里,生成阶段的 seq2seq 模型通常就是 Transformer;而把文本转成向量的 embedder 也大多是 Transformer 家族。

triple

三元组,形式为 (subject, predicate, object),即 (主语, 谓语, 宾语),是知识图谱里表达一条事实的最小单元。

例如「爱因斯坦提出了相对论」可表示为 (爱因斯坦, 提出, 相对论)。图谱里的节点(node)就是主语和宾语,边(relationship)就是谓语。

在 Graph RAG 中,检索流程是:从 query 抽取种子实体(seed entity)→ 沿图遍历得到相关子图 → 从子图里取出一批三元组作为上下文 → 交给 LLM 生成答案。三元组的价值在于它把「关系」显式化,因而能支持向量检索难以处理的多跳推理。