# LLM 只负责预测,其余的由你来工程化

关于用大语言模型来构建的速成课。24 个概念——下一个 token 预测、context window、幻觉、few-shot、结构化输出、context engineering、RAG、embeddings、grounding、tool use、agent loop、eval、guardrails 等等——每个都附英文名称、一张图示、一张卡片和一个例子。结尾是一页速查表,列出所有概念以及它们如何相互关联。

- **Author:** Fedor Chishchin
- **Level:** intro
- **Reading time:** ~35 分钟阅读
- **Published:** 2026-06-02
- **Source:** https://fedorthinks.com/zh/learn/building-with-llms-express

---

## 1. LLM 是什么(又不是什么)

在用语言模型构建之前,先弄明白它究竟是什么——因为几乎每一个错误,都源于期待它成为它本不是的那种东西。

### 课程地图:七组共 24 个概念

> 一张地铁线路图:先看整条线路,再一站一站地坐过去。

这门课从简单讲到复杂。先讲**模型**是什么、它的行为如何。然后是怎么跟它说话(**prompting**),往它的窗口里放什么(**context**),怎么把你的事实交给它(**RAG**),怎么让它行动(**agent**),怎么让这一切值得信赖(**可靠性**),以及怎么构建而不过度构建。

_Diagram: 七组概念——各章就按这个顺序展开。_

### 如何阅读每个概念

> 一本护照:每个人都是同样的栏目,所以很容易对比。

每个概念都用同样的方式来讲:它的**英文名称**(你在文档里会去搜的那个术语)、一幅日常生活里的画面、一张**图示**,以及一张带栏目的**卡片**——它是什么、一个例子、代价或陷阱、何时使用,以及它和其他概念的关系。这门课只有一个大观点:**模型是容易的部分;围绕它的工程才是真正的活儿。**

### Next-token prediction——它在预测下一个词,而且预测得极好

> 你手机键盘在猜下一个词——但放大到极致,以至于它能写一篇文章、调一段代码、讲明白物理,一次一个看似合理的词地往下推。

LLM 是一个**下一个 token 的预测器**,在海量人类文本上训练而成。它不去查东西;它生成统计上最可能接下来出现的内容。这就是为什么它在语言和推理模式上令人惊叹——也是为什么它不是一座事实的数据库。**它产出的是听起来对的东西,这通常是对的,有时却是自信满满地错的。**

_Diagram: 模型给候选的下一个 token 打分,挑一个可能性高的,接上去,然后重复。_

#### Next-token prediction (下一个 token 预测器(next-token predictor))

- **是什么:** 模型一次生成一个 token,挑选一个可能性高的接续。
- **例子:** 「猫坐在……上」→「垫子」比「沙发」更有可能。
- **陷阱:** 它不是事实查询:「听起来对」≠「就是对」。
- **怎么办:** 别把模型当事实数据库用——事实由你自己交给它。
- **关联:** **Hallucination** 和 **Non-determinism** 的根源;用 **RAG** 来应对。

### Context window——它唯一的记忆

> 一位才华横溢却彻底失忆的顾问——每一次开会,你都得把每一份文件重新递给他一遍,因为他对上次的事什么都不记得。

模型在两次请求之间什么都不记得。它唯一的工作记忆,就是**上下文窗口**(**context window**)——你在这次调用里发进去的文本。对话历史、你的数据、你的指令:只要不在 context 里,模型就不知道。这门课里的一切,在某种意义上,都是关于你往那个窗口里放什么。

_Diagram: 模型只看得到这次调用窗口里的东西。不在窗口里,对模型来说就不存在。_

#### Context window (模型的工作记忆)

- **是什么:** 一次调用的全部文本:system prompt、历史、文档、工具、问题。
- **例子:** 聊天机器人「记得」之前的消息,只是因为你每次调用都把它们重新发了一遍。
- **陷阱:** 窗口是有限的,里面的每个 token 都要花钱、花时间。
- **怎么办:** 有意识地组装窗口——这就是 **Context engineering**。
- **关联:** 由 **Context engineering** 和 **RAG** 来填充;**Memory** 存在它之外。

### Hallucination——它会胡编,而且自信满满

> 晚宴上一位迷人的万事通,他宁可编出一个有说服力的答案,也不愿承认自己不知道。

当模型不知道时,它不会停下——它会**产生幻觉**(**hallucination**),以与真相同样的自信,产出流畅、看似合理、却是错的文本。这不是你能彻底打补丁修掉的 bug;它就是一个猜测器的本性。**这门课里大部分工程的存在,都是为了驾驭这一个事实。**

_Diagram: 真相和编造从模型里出来时同样自信——光听语气分不出来。_

#### Hallucination (自信地编造)

- **是什么:** 一个流畅、看似合理、却是错的回答,说得信心十足。
- **例子:** 你要一个出处——模型引用了一篇根本不存在的论文。
- **陷阱:** 无法彻底消除:这是下一个 token 预测的本性。
- **怎么办:** 限定它可能出现的范围,出现时把它抓住——见第 6 章。
- **关联:** 由 **Grounding** 和 **RAG** 来降低;由 **Guardrails** 和 **Evals** 来抓住。

### Non-determinism——每次都不一样

> 把同一个问题问十位专家——你会得到十个略有不同的好答案,而不是一个一模一样的回复。

同一个 prompt 每次都可能给出不同的输出——这就是**非确定性**(**non-determinism**)。所以你不能把 LLM 当成一个返回值固定的函数;你把它当作**聪明、易错、非确定性的输入**——要去约束、去校验、去核查,而绝不盲目信任。这种心态,就是整盘棋的关键。

_Diagram: 同一个 prompt 调用三次——三个略有不同的答案。所以输出总是要检查。_

#### Non-determinism (同样的输入,不同的输出)

- **是什么:** 同样的输入可能产生不同的输出。
- **例子:** 让它总结一封邮件,问两次——得到两段不同的文字。
- **陷阱:** 普通的「相等 / 不相等」单元测试在这里会失效。
- **怎么办:** 约束格式(**Structured output**),校验,衡量质量(**Evals**)。
- **关联:** 你需要 **Evals** 和 **Guardrails** 的原因。

> **在你动手构建之前:** "我是在把它当成一个可靠的函数——还是当成一个才华横溢、健忘、偶尔还撒谎的实习生?"

> LLM 并不知道东西。它在预测东西。把其余的一切,都围着这一个真相去造。

## 2. prompt

prompt 是你告诉模型你想要什么的方式。指令越清晰,输出越好——而大多数「AI 真笨」的时刻,其实都是 prompt 不清楚。

### Prompt——是一份任务说明,不是咒语

> 把任务交给一位机灵的新员工:他扮演什么角色、你要什么、有哪些限制,以及一个「好结果」的样例。说明含糊,结果就含糊。

一个好的 **prompt** 会写明**角色**(「你是一位细致的编辑」)、**任务**、**约束**(长度、语气、格式),最好再附一个**例子**。没有什么魔法词——就是清晰的指令,跟你交代一个能干的人时一样。大多数 prompt 问题,其实是需求说明的问题。

_Diagram: 一份好说明的四个部分:角色、任务、约束、例子。_

#### Prompt (任务说明)

- **问题:** 含糊的要求 → 含糊的回答,看上去就像「AI 真笨」。
- **思路:** 把 prompt 写成一份说明:角色、任务、约束、例子。
- **例子:** 「你是一位细致的编辑。把这张工单压缩成 3 条要点,用简单的话。下面是一个好结果的样例:……」
- **代价:** 几乎没有——你只需要想清楚自己要什么。
- **何时使用:** 总是。它是阶梯的第一级。
- **关联:** 分成 **System prompt** 和用户 prompt;用 **Few-shot** 例子来加强。

### System prompt vs user prompt——长期规则与当次请求

> 一家餐厅的长期规定——「我们是素食餐厅,十点打烊」——对比今晚的具体点单。一个给一切定下框架;另一个才是请求本身。

**system prompt** 设定持久的行为——角色、规则、语气——在整个对话里始终有效。**user prompt** 是具体的请求。把你的人设和 guardrails 放进 system prompt,就能在用户消息不断变化时让它们保持稳定。

_Diagram: system prompt 给每一轮定下框架;user prompt 每一轮都在变。_

#### System prompt (长期指令)

- **是什么:** 对整个对话都有效的持久指令:角色、规则、语气。
- **例子:** 系统:「你是一家商店的客服机器人。回答要简短;不要承诺超出政策范围的退款。」用户:「我的订单在哪?」
- **陷阱:** 它不是一把锁:用户可能试图绕开它——你仍然需要 **Guardrails**。
- **何时使用:** 任何需要在多轮之间保持稳定的东西,都放这里。
- **关联:** **Prompt** 的一部分;位于 **Context window** 的开头。

### Few-shot prompting——光说不如演示

> 教人一种格式,给他看三个做好的样例,比用话描述它更快、更清楚。

想要得到你要的那种回答形态,最好的办法往往是直接在 prompt 里**放几个例子**,写明输入和期望的输出。这就是 **few-shot prompting**。模型对这些例子做模式匹配,比遵循一段抽象的描述要好。两三个好例子,可以胜过一大段说明。

_Diagram: prompt 里有两个「输入 → 输出」例子——模型会对新的输入延续同一个模式。_

#### Few-shot prompting (prompt 里的例子)

- **问题:** 某种格式或风格很难用话描述清楚。
- **思路:** 直接在 prompt 里给出 2–3 对「输入 → 期望输出」。
- **例子:** 「'又晚了!' → 负面;'太喜欢了' → 正面;'还行' → ?」
- **代价:** 例子占窗口的空间;一个坏例子会教出坏习惯。
- **何时使用:** 当你需要一个确切的形态、语气或分类时。
- **关联:** **Prompt** 的一部分;常与 **Structured output** 搭配。

### Structured output——要一种你的代码能用的结构

> 带标注格子的表单,给你的是可填写的数据;「给我写一段关于它的话」,给你的是你还得再去解析的散文。

如果你的程序要消费这个输出,就要求**结构化输出**(**structured output**)——符合某个 schema 的 JSON,而不是自由文本。现代模型可以被约束为输出合法的 JSON,这就把 LLM 从一个聊天机器人,变成了你的代码可以依靠的一个组件。**Structured output 是连接语言与软件的桥梁。**

_Diagram: 自由文本只能靠猜来解析;符合 schema 的 JSON 可以被校验,并直接交给代码。_

#### Structured output (JSON 模式、按 schema 输出(JSON mode, schema output))

- **问题:** 代码无法可靠地解析自由形式的散文。
- **思路:** 把回答约束到一个 JSON schema,并在使用前校验。
- **例子:** `{"order_id": 42, "status": "late"}`,而不是「您的订单 42 正在延误……」。
- **代价:** 你得定义 schema,还要处理校验失败的情形。
- **何时使用:** 只要读这个回答的是程序而不是人。
- **关联:** **Tool use** 的基础(一次工具调用就是 structured output);抵御 **Hallucination** 的一层防线。

> **关于 prompt 的问题:** "一个聪明的陌生人,只拿到这份 prompt、别的什么都没有,能产出我想要的东西吗?"

> 没有魔法词。好的 prompt 就是一份清晰的说明——角色、任务、约束、例子。

## 3. 从 prompt 到 context

随着应用变得严肃,手艺会从斟酌 prompt 的措辞,转向组装正确的 context。这才是真正的学问——「context engineering」。

### Context engineering——你往窗口里放什么

> 律师赢官司,靠的不是巧妙的措辞,而是把恰好正确的文件、按正确的顺序、不带任何无关内容地交给法官。

**Prompt engineering** 讲的是措辞;**context engineering** 讲的是为每一次调用,把正确的信息——相关的事实、历史和工具——组装进窗口。模型的回答,只会和你放在它面前的东西一样好。**大多数质量问题是 context 问题,而不是措辞问题。**

_Diagram: 从众多来源里挑出重要的,组装成窗口——每次调用都是新的。_

#### Context engineering (组装窗口)

- **问题:** prompt 措辞很好,模型却答得很差。
- **思路:** 每次调用,都把恰好需要的东西放进窗口:指令、事实、历史、工具。
- **例子:** 客服机器人拿到的不是整个知识库,而是退款政策、订单状态和客户的问题。
- **代价:** 你需要写代码来挑选和组装 context——这才是真正的活儿。
- **何时使用:** 只要应用不止一个简单 prompt,就该用。
- **关联:** 填充 **Context window**;**RAG** 是它主要的事实供应者;受 **Context budget** 约束。

### Relevance over completeness——少,但要少得对

> 一页精炼的简报,胜过 200 页的堆砌——读者能找到信号,而不是淹没其中。

context 并非越多越好。每个 token 都要配得上它的位置:无关的文本会稀释模型的注意力、花掉钱,而且在长 context 里**实际上会提高幻觉的概率**。真正的本事,是挑出少数重要的东西,而不是把一切可能有用的都塞进去。

_Diagram: 左:塞满的窗口,信号淹没在噪声里。右:只放答案需要的东西。_

### Context budget——窗口就是一笔预算

> 一只有重量限制的行李箱——你装的是真正会用到的东西,而不是整个衣柜,因为有硬性上限,还有罚款。

窗口是有限的,每个 token 都要花延迟和金钱。所以你要有意识地花它,把它当作一笔 **context budget**:裁剪历史,把旧内容做摘要,只检索眼下相关的东西。**把 context 当作稀缺的预算——而不是无底的垃圾桶——正是玩具和产品的分水岭。**

_Diagram: 窗口被分给各个部分,并撞上硬性上限。四个杠杆:裁剪、摘要、检索、缓存。_

#### Context budget (token 预算)

- **是什么:** 窗口的 token 上限,由 system prompt、历史、事实和回答共同分享。
- **例子:** 一段很长的聊天:旧的几轮被压缩成一段简短的摘要,而不是每次都完整地发送。
- **陷阱:** token 越多越贵、越慢——而且不一定更好。
- **怎么办:** 裁剪、做摘要、只检索需要的内容、缓存稳定的部分。
- **关联:** 约束 **Context engineering**;直接决定 **Cost & latency**。

> **关于 context 的问题:** "这个窗口里的每样东西,都配得上它的位置吗——还是我只是把文本一股脑倒进去,然后碰运气?"

> Prompt engineering 是措辞。Context engineering 是文件。质量就在后者里。

## 4. 把事实交给它

模型不了解你的数据,也不了解训练截止日期之后的任何事。RAG 就是你把事实交给它、让它据此作答的办法——严肃的 LLM 应用里最重要的一个模式。

### RAG——Retrieval-Augmented Generation:先检索,再回答

> 一场开卷考试——不靠记忆,而是先翻出相关的几页,再根据眼前的内容写出答案。

**Retrieval-Augmented Generation(RAG)** 的意思是:拿到用户的问题,**取回你文档中最相关的片段**,放进 context,然后要求模型据此回答。这样它依据的是你真实的数据,而不是它模糊的记忆。助手就是这样回答关于你的产品、你的文档、你的知识库的问题的。

_Diagram: 问题 → 搜索你的文档 → 最好的片段连同问题一起进入 prompt → 带引用的回答。_

#### RAG (Retrieval-Augmented Generation)

- **问题:** 模型不了解你的数据,也不了解训练截止日期之后的事——还会胡编。
- **思路:** 先找出你文档里正确的片段,再只根据它们来回答。
- **例子:** 文档机器人找到 3 个匹配的帮助中心章节,据此作答,并附上引用。
- **代价:** 你需要 chunking、索引、搜索——而且它们的质量必须被衡量。
- **何时使用:** 当回答必须建立在你的数据或最新事实之上时。
- **关联:** 基于 **Embeddings** 运行;带来 **Grounding**;为 **Context engineering** 供料。

### Embeddings——按含义做向量搜索

> 一位图书管理员,按书讲的是什么来找书,而不是按确切的书名——「类似这样的东西」,按含义来找。

要找到相关的片段,你把文档存成 **embeddings**(捕捉一段文本含义的向量),放进**向量数据库**,再按与问题的相似度来搜索(也就是数据库课程里讲的那个引擎)。好的检索是 RAG 的核心:放进来的片段对了,回答就有依据;放进来的是垃圾,模型就会自信地从垃圾里作答。

_Diagram: 含义相近的文本彼此靠得很近。搜索就是找出问题的最近邻。_

#### Embeddings (向量搜索)

- **是什么:** 文本 → 一串数字组成的向量;含义相近的文本,向量也相近。
- **例子:** 「怎么把钱要回来?」能找到「退款政策」那一节,尽管两者几乎没有共同的词。
- **陷阱:** 含义相近,并不保证这个片段就能回答问题。
- **何时使用:** 在 RAG 里按含义搜索,而不是按确切的词。
- **关联:** **RAG** 的引擎;质量取决于 **Chunking**。

### Grounding——能追溯到出处的回答

> 一位每个论断都必须注明出处的记者,编造的东西要比凭记忆写作的记者少得多。

RAG 的最大好处是 **grounding**:因为回答取自检索到的文本,你可以要求**引用**,并核实各个论断确实有依据。它不会抹去幻觉,但会大幅削减它——还能让你抓住漏网的那些。**能追溯到出处的回答,才是你能信任的回答。**

_Diagram: 每个论断都指向一个出处。没有出处的论断是个信号:标出来,或者丢掉。_

#### Grounding (引用)

- **问题:** 你无法核实模型的某个论断是从哪里来的。
- **思路:** 只根据检索到的文本作答,并在每个事实下面注明出处。
- **例子:** 「30 天内可退款 [1]」——其中 [1] = `refund-policy.md`。
- **代价:** 引用必须被核查;回答会变得更谨慎。
- **何时使用:** 凡是答错代价很高的地方:金钱、法律、健康、客服。
- **关联:** **RAG** 的回报;抵御 **Hallucination** 的主要防线。

### Chunking & retrieval quality——垃圾进,自信的垃圾出

> 把错的文件递给某人,让他做总结——他会给你一份完美的、关于错误内容的总结。

RAG 只有在检索有效时才有效。糟糕的 **chunking**、薄弱的搜索、过时的数据——模型就会忠实地从错误的 context 里作答,听起来一样笃定。所以 RAG 里大部分的精力不在模型上;而在 **chunking、索引,以及衡量检索质量**。先修好检索,再去怪模型。

_Diagram: 模型之前的任何一步都可能出错——结果听起来依然很自信。_

#### Chunking & retrieval quality (RAG 不起眼的另一半)

- **问题:** 模型自信地从错误的片段里作答。
- **思路:** 把文档切好,让索引保持新鲜,并单独衡量搜索是否找到了正确的东西。
- **例子:** 一张价格表被从中间切开——模型看到了价格,却没看到套餐名称,于是搞混了。
- **代价:** 不起眼的数据活儿,占去了 RAG 的大部分工作量。
- **何时使用:** 只要你用 RAG,就总是要做。先修检索,再修模型。
- **关联:** 决定 **RAG** 和 **Embeddings** 的质量;由 **Evals** 来检验。

> **关于 RAG 的问题:** "模型是根据检索到的、可引用的事实在回答——还是根据它自己模糊的记忆?"

> 别去问模型它知道什么。把事实交给它,让它据此作答。

## 5. tool 与 agent

prompt 产出的是文本。要让模型去做事——搜索、计算、发送、查询——你就给它 tool;要让它跨越许多步骤去追求一个目标,你就把它放进一个循环。这就是 agent。

### Tool use——function calling:让它调用你的函数

> 一位聪明的助理,自己够不到档案柜——但可以请你去拿,并准确告诉你该抽哪一份。

用 **tool use**(**function calling**),你描述模型可以请求的函数——`search_orders`、`send_email`、`get_weather`——当它想用其中一个时,它返回一个结构化的调用,你的代码去执行,再把结果喂回给它。模型决定*做什么*;*怎么做*仍由你的代码掌控。**这就是 LLM 如何从文本延伸到真实世界。**

_Diagram: 模型只是请求一次工具调用。你的代码来执行它——再把结果交还给模型。_

#### Tool use (function calling)

- **问题:** 光靠文本,查不了订单、算不了数、发不了邮件。
- **思路:** 描述函数;模型返回一个结构化调用;你的代码执行它并返回结果。
- **例子:** 「订单 42 在哪?」→ 模型请求 `search_orders(42)` → 代码调用 API → 模型作答。
- **代价:** 每多一个工具,就多一种在真实世界里出错的方式。
- **何时使用:** 当回答需要实时数据或一次操作时。
- **关联:** 建立在 **Structured output** 之上;是 **Agent loop** 的构件。

### Agent loop——agent 就是循环中的模型

> 一个人解决问题:想一想、采取一个行动、看看结果、再想一想——反复进行直到完成,而不是一步到位。

**agent** 把模型接进一个循环——推理、调用工具、观察结果、再推理(即 **agent loop**)——由 LLM 作为编排者,决定每一步接下来做什么。这让它能处理开放式的任务(「调研一下这个,并起草一封回复」),这是任何单个 prompt 都做不到的。编程助手和调研机器人就是这样工作的。

_Diagram: 推理 → 行动 → 观察,再来一轮——直到目标达成。_

#### Agent loop (agent)

- **问题:** 任务无法一步完成:得搜索、尝试、再修正。
- **思路:** 让模型处在「想 → 做 → 看」的循环里,每一步由它自己选。
- **例子:** 编程助手读一个文件,编辑它,跑测试,看到失败,再编辑。
- **代价:** 不可预测:它可能乱绕、重复自己,或者把成本推高。
- **何时使用:** 只有当任务确实需要许多自适应的步骤时。否则——一次调用,或者一条固定的链。
- **关联:** 由 **Tool use** 构成;需要 **Memory** 和 **Human-in-the-loop**。

### Memory——窗口之外的记忆

> 一个长期项目需要一本笔记本——你没法把几个月的工作都装在脑子里,所以把要紧的写下来,需要时再翻出来。

因为窗口是有限的,agent 需要 **memory**:把笔记、摘要和可检索的历史保存在 context 之外,相关时再取回来。没有它,agent 到一项长任务结束时,就忘了开头。Memory 让一场聊天变成能够随时间推进去工作的东西。

_Diagram: agent 把要紧的内容写进外部存储,只取回眼下需要的那部分。_

#### Agent memory (窗口之外的记忆)

- **问题:** 长任务装不进窗口——agent 忘了开头。
- **思路:** 把笔记和摘要放在窗口之外;需要时再按需取回。
- **例子:** 每个阶段之后,agent 写下「做完了什么、接下来做什么」,下一步再把它读回来。
- **代价:** 又多了一个系统:写什么、取什么、忘什么。
- **何时使用:** 用于一个窗口装不下的长任务。
- **关联:** 扩展 **Context window**;取回方式与 **RAG** 相同;**Agent loop** 需要它。

### Human-in-the-loop——循环很强大,所以要拴上缰绳

> 一台扫地机器人,大多数时候把家打扫得很好,但偶尔会卡在墙角原地打转,或者一头扎进泳池。

agent loop 是不可预测的:它可能走错路、重复自己、推高成本,或者依据一个糟糕的决定去行动。所以你要给它拴上缰绳——**步数上限、工具权限、对高风险操作要有人工批准(human-in-the-loop)、完整的日志。** 只有当任务确实需要许多自适应的步骤时,才去用 agent;一次调用或一条固定的链够用时,它更便宜也更安全。

_Diagram: agent 的缰绳:步数上限、工具白名单、日志,以及任何高风险操作之前的一个真人。_

#### Human-in-the-loop (agent 的缰绳)

- **问题:** agent 可能误做一个昂贵或不可逆的操作。
- **思路:** 步数上限、允许的工具、日志——并由真人确认高风险操作。
- **例子:** agent 准备好一笔退款,但要等操作员点了批准才会发出。
- **代价:** 更慢,还需要人——但错误永远不会流到现实世界。
- **何时使用:** 在任何昂贵或无法撤销的操作之前。
- **关联:** 约束 **Agent loop** 和 **Tool use**;**Guardrails** 的近亲。

> **关于 agent 的问题:** "这真的需要一个自适应的多步循环吗——还是一次调用,或一条固定的链,就能更安全地把事做完?"

> tool 让模型能行动。循环让它能坚持。两者都需要缰绳——没有限制的能力,就是一种负担。

## 6. 让它可靠

一个只能跑通一次的演示很容易。一个值得信赖的系统,需要那些不起眼的功夫——衡量、约束,以及盯着一个天生非确定性的组件。

### Evals——无法衡量的,就无法改进

> 一所从不给任何人打分的学校,不知道谁在学、谁没学——也就没法变好。

**eval** 是你的 AI 的一套测试:带有已知正确期望的输入,自动打分——它检索到正确的文档了吗、调用了正确的工具吗、回答忠实吗?没有 eval,你调 prompt 就是凭感觉。还要有心理准备面对一个残酷的事实:**把质量做到 80% 很快;从那里再磨到 95%,才是大部分的工作。**

_Diagram: 测试集 → 你的系统 → 自动打分。改一下 prompt,重新跑,对比。_

#### Evals (AI 的测试集)

- **问题:** 你说不清改了 prompt 之后是变好了还是变差了。
- **思路:** 一组带预期结果的输入,加上一个自动的质量评分。
- **例子:** 50 个真实的客服问题:检查是否找到了正确的文档,回答是否正确。
- **代价:** 你得建立并维护这组数据——持续的工作。
- **何时使用:** 在你开始调 prompt 之前。没有 eval,就是凭感觉。
- **关联:** 对 **Non-determinism** 的回答;衡量 **RAG**、**Tool use**、**Agent loop**。

### Guardrails——输入和输出两侧的边界

> 保龄球道上的护栏——它们不替你滚球,但能让球不掉进沟里。

**Guardrails** 是围绕模型的检查:在入口一侧,拦截 **prompt injection** 和超出范围的请求;在出口一侧,过滤不安全的内容,并在结果到达用户或另一个系统之前校验格式。它们在你的代码里,在模型之前和之后运行——因为你不能指望模型自己监督自己。

_Diagram: 入口一道检查,出口一道检查——在你的代码里,位于模型的两侧。_

#### Guardrails (输入与输出检查)

- **问题:** 你无法让模型可靠地监督自己。
- **思路:** 在模型之前检查输入,在模型之后检查输出——用普通的代码。
- **例子:** 入口:拒绝「忽略之前所有指令,然后……」。出口:按 schema 校验 JSON,并剔除不安全的文本。
- **代价:** 额外的延迟,以及误报。
- **何时使用:** 生产环境里总是要用,尤其是输出会到达人或系统时。
- **关联:** 为 **System prompt** 兜底;检查 **Structured output**;抓住 **Hallucination**。

### Cost & latency——是设计约束,不是事后补想

> 一个全程跳动的出租车计价器——每多一公里的 context、每多一个步骤,都在增加车费和等待。

每个 token 都要花钱、花时间,而且**成本随你发送的 context 量**和调用次数而增长。所以你要围绕它来设计:裁剪并缓存 context,选足够好的最小模型,在一次调用就够用的地方别跑 agent loop。一个太慢或太贵的好回答,不算好回答。

_Diagram: 成本 ≈ 每次调用的 token 数 × 调用次数。四个把它压下来的杠杆。_

#### Cost & latency (每次调用的钱和时间)

- **是什么:** 每次调用的钱和时间;它们随 context 和调用次数增长。
- **例子:** 每次调用都发送整个知识库,或者在一次调用就够的地方跑 agent,又慢又贵。
- **陷阱:** 一个太慢或太贵的好回答,不算好回答。
- **怎么办:** 裁剪并缓存 context,选足够好的最小模型,少调用几次。
- **关联:** 被 **Agent loop** 推高;通过 **Context budget** 来管理。

### Hallucination defense——你要围绕它来设计的风险

> 你不会把话筒交给一位才华横溢却不可靠的叙述者,还不加监督——付印之前,你要先核实事实。

这里的每一项技术——用 RAG 做 grounding、结构化输出、evals、guardrails、引用——都是为了管住一个核心风险:**模型把错误的话说得令人信服。** 你永远无法彻底消除它;你限定它可能发生的范围,发生时把它检测出来,并且绝不让一个未经核实的论断到达答错代价很高的地方。这是**纵深防御**:每一层都能抓住一部分错误。

_Diagram: 层层防御:每一层都抓住一些错误论断,所以到达用户的内容是经过检查的。_

> **关于可靠性的问题:** "我怎么衡量这是对的,怎么限定它可能出错的范围,出错时又怎么把它抓住?"

> 演示信任模型。产品则要衡量它、约束它、盯着它——因为它天生就是非确定性的。

## 7. 把它造好

这些模式从简单到复杂层层叠加。真正的本事,是用能解决你问题的最不强大的那一个——并把模型当作一个组件,而不是架构本身。

### Complexity ladder——只在不得不爬的时候才往上爬

> 你不会为了把一个箱子搬过房间而订一辆搬家卡车。你让投入和活儿相称。

有一道阶梯:**一个好的 prompt**,然后是 **structured output**,当它需要你的事实时用 **RAG**,再然后是 **tool**,最后是一个完整的 **agent loop**——当任务确实需要许多自适应的步骤时。每一级都增加能力、成本和新的失败方式。**从底部开始,只有当问题逼着你时才往上爬**——大多数功能根本用不到顶层。

_Diagram: 五级:越高的一级越强大、越昂贵、也越脆弱。从第一级开始。_

#### Complexity ladder (够用就行的最小能力)

- **问题:** 明明一个 prompt 就够了,却很容易想去造一个 agent。
- **思路:** Prompt → Structured output → RAG → Tools → Agent loop;被逼着才往上爬。
- **例子:** 工单分类:一个带 structured output 的 prompt 就够了——不用 RAG,不用 agent。
- **代价:** 每一级都增加成本和新的出错方式。
- **何时使用:** 为任何 LLM 功能选择方案时。
- **关联:** 把 **Prompt**、**Structured output**、**RAG**、**Tool use**、**Agent loop** 串起来。

### LLM as a component——是组件,不是架构

> 汽车有一台强劲的发动机,但它被装在防火墙后面,喂的是干净的燃料,周围还有刹车——它不是整辆车。

把模型放在**一个接口后面**,像对待其他任何依赖一样,在它的输入和输出周围做校验,并保留更换模型或供应商的自由。LLM 是你系统里一个强大却不可靠的部件——而不是它的地基。(这和系统设计里「LLM 是适配器,而不是架构」的是同一个教训。)

_Diagram: 应用与一个接口对话;供应商以适配器的形式接入;输入和输出都要经过检查。_

#### LLM as a component (LLM 即适配器(LLM as an adapter))

- **问题:** 代码和某一个模型焊死在一起,并直接信任它的回答。
- **思路:** 模型放在它自己的接口后面,输入和输出都有检查。
- **例子:** 应用调用 `complete(prompt)`;在它背后,你可以把供应商 A 换成 B,或者换成本地模型。
- **代价:** 一层薄薄的抽象。
- **何时使用:** 总是——从第一天起。
- **关联:** 用到 **Structured output** 和 **Guardrails**;和 Hexagonal(端口与适配器)是同一个思想。

### 这一切如何关联起来

> 一支管弦乐队:乐器各不相同,但共同演奏,而且彼此支撑。

这些概念不是孤立存在的。**Embeddings** 为 **RAG** 提供搜索;RAG 为 **Context engineering** 供给事实,后者填充 **Context window**。RAG 促成 **Grounding**,而 grounding、**Guardrails** 和 **Evals** 合力把 **Hallucination** 控制住。**Structured output** 是 **Tool use** 的基础,工具构成 **Agent loop**,而 **Memory** 延长它的寿命。

_Diagram: 本课各概念之间的主要联系。_

#### 在交付一个 LLM 功能之前

- **最简单的那一级是什么**——prompt、structured output、RAG、tool、agent——哪一级能解决这件事?
- **事实从哪里来**——模型的记忆(有风险),还是检索到的、可引用的数据?
- **它答错时会怎样**,而那个地方是答错代价低还是高?
- **我怎么衡量质量**——eval 是什么?
- **输入和输出上的 guardrails 是什么?**
- **每次调用要花多少钱**,有多慢?

#### 说明你过度构建了的嗅探信号

- 为一个单个 prompt 就能回答的事,上了一个 **agent loop**。
- 对三份你直接贴进 prompt 就行的文档,上了 **RAG**。
- **凭感觉**调 prompt,没有 eval 来告诉你有没有变好。
- 每次调用都把**整个知识库**发进 context。
- 把模型的输出**未经校验就直接**写进数据库或发进邮件。

#### 说明你造得好的信号

- 输出在你的代码使用之前,是**结构化且经过校验的**。
- 回答是**有依据、可引用的**,而不是凭空捏来的。
- 你有一个 **eval**,能告诉你一次改动是帮了忙还是添了乱。
- 输入和输出两侧都有 **guardrails**。
- 你用的是够用的**最简单的一级**——而且模型放在一个你可以替换的接口后面。

## 速查表:全部 24 个概念

### 模型

- **Next-token prediction** (下一个 token 预测器): 模型生成一个可能性高的接续;它不查事实。
- **Context window** (工作记忆): 模型唯一的记忆——这次调用的文本。
- **Hallucination** (自信地编造): 流畅、看似合理的捏造。无法消除,只能限定。
- **Non-determinism** (同样的输入,不同的输出): 一个输入,多种输出。所以要检查、要衡量。

### Prompting

- **Prompt** (任务说明): 一份清晰的说明:角色、任务、约束、例子。
- **System prompt** (长期指令): 对整个对话有效的持久规则;user prompt 是具体的请求。
- **Few-shot prompting** (prompt 里的例子): 用 2–3 个「输入 → 输出」例子,代替一大段解释。
- **Structured output** (按 schema 输出 JSON): 一个形态能被代码校验和使用的回答。

### Context

- **Context engineering** (组装窗口): 每次调用,都把恰好需要的东西放进窗口。
- **Relevance over completeness** (少,但要对): 多余的文本会稀释注意力,并提高幻觉的概率。
- **Context budget** (token 预算): 窗口是稀缺的预算:裁剪、摘要、缓存。

### 事实(RAG)

- **RAG** (Retrieval-Augmented Generation): 先找出你文档里正确的片段,再据此作答。
- **Embeddings** (向量搜索): 按含义搜索:相近的文本就是相近的向量。
- **Grounding** (引用): 每个论断都指向一个出处——所以可以被核查。
- **Chunking & retrieval quality** (RAG 不起眼的另一半): 垃圾进,自信的垃圾出。先修检索。

### Agent

- **Tool use** (function calling): 模型请求一次函数调用;你的代码来执行它。
- **Agent loop** (agent): 处在「想 → 做 → 看」循环里的模型。
- **Agent memory** (窗口之外的记忆): 笔记和摘要放在窗口之外,按需取回。
- **Human-in-the-loop** (agent 的缰绳): 步数上限、权限、日志,以及高风险操作之前的一个真人。

### 可靠性

- **Evals** (AI 的测试集): 给你的 AI 做的测试:没有它们,你就是凭感觉在调。
- **Guardrails** (输入与输出检查): 模型前后的代码:拦截、过滤、校验。
- **Cost & latency** (每次调用的钱和时间): token × 调用次数。是设计约束,不是细节。

### 把它造好

- **Complexity ladder** (够用就行的最小能力): Prompt → structured output → RAG → tool → agent。被逼着才往上爬。
- **LLM as a component** (LLM 即适配器): 模型是系统里一个可替换的部件,而不是它的地基。

> 用 LLM 来构建,不是 prompt 的魔法。它是普通的工程——context、检索、校验、evals——围绕着一个非凡却不可靠的组件。

速成课结束 · 7 章 · 24 个概念

接下来是实践:去读你所用模型供应商的文档——Anthropic 和 OpenAI 关于 prompting、tool use 和 evals 的指南——然后从头到尾做一个小东西:先是一个 prompt,再是一个 RAG,再是一个工具。但要把一个观点放在所有观点之上。模型是容易的部分。围绕它的工程——context、grounding、衡量、限制——才是一个真正的产品被造出来的地方。
