理解大语言模型:Agent 的"大脑"
模型基础
本章学习地图
学完你能做到
- 用自己的话解释 token 与上下文窗口
- 理解采样参数如何影响输出
- 按质量、延迟、成本和能力选择模型
本章可交付成果
一张模型选型与参数决策表
前置知识
第 2 章;无需机器学习基础
建议读法: 先快速读概念与图示,再逐行运行实战代码;最后关闭文章,按"实践闭环"独立重做一次。
你手机里的输入法,其实就是一个"小 LLM"
我猜你每天用手机打字的时候,一定见过这样的场景:你输入"今天天气",输入法自动弹出"不错""真热""下雨了"。你输入"我明天要去",它给你补上"上班""医院""机场"。你输入"这支股票",后面跟着"涨了""跌了""涨停了"。
你有没有想过,输入法是怎么知道你想说什么的?它又不是你肚子里的蛔虫,凭什么猜得这么准?
答案说出来其实很简单:它不是"猜",而是"算"。 输入法背后有一个统计模型,这个模型读过了海量的中文文本------新闻、小说、聊天记录、微博、公众号文章。它从这些文本里学到了一个规律:在中文里,"今天天气"后面,出现"不错"的概率是 23%,出现"真热"的概率是 18%,出现"下雨了"的概率是 15%。当你打出"今天天气"这四个字,它就把概率最高的几个选项推给你。
大语言模型(LLM)的底层逻辑,跟这个输入法联想几乎一模一样。只不过输入法只看前几个字,LLM 看的是整个对话历史;输入法只预测下一个词,LLM 能预测接下来几百个词;输入法只会补全,LLM 会推理、会总结、会写代码。
但本质上,它们都在做同一件事:给定一段文字,预测接下来最可能出现的文字是什么。
这个认知很重要。因为很多人一听到"大语言模型"就觉得是黑魔法,觉得 AI 有了意识,觉得它真的"理解"了人类语言。其实不是的。LLM 不"理解"任何东西,它只是在做一件极其擅长的事情------文本接龙。只不过这个接龙游戏它玩得太好了,好到让你觉得它好像真的懂了。
什么是 LLM?一个"超级文本接龙游戏"
想象你在玩一个游戏。游戏规则很简单:我给你一段文字,你接着往下写,写得越像人写的越好。比如我给你"从前有座山,山里有座庙",你可能会接"庙里有个老和尚在讲故事"。我给你"const user =",你可能会接"{ name: '张三', age: 25 }"。这游戏你从小到大都在玩------语文课的续写作文、跟朋友聊天时接话茬,本质上都是文本接龙。
LLM 就是把这个游戏玩到了极致。它"读"过了互联网上几乎所有的公开文本------维基百科的全部词条、GitHub 上的所有开源代码、Reddit 上的所有帖子、StackOverflow 上的所有问答、无数本书籍和论文。它把这些文本全部"记住"(准确地说,是记住了文本中的统计规律),然后当你给它一段话,它就能接出最像人类写的下文。
那它到底是怎么"记住"这些规律的呢?这里我不用"神经网络""反向传播""注意力机制"这些词来吓你。我用一个最简单的比喻:
假设你是一个刚入行的厨师学徒。你师傅不给你讲烹饪原理,而是每天让你看一万道菜------看菜名、看食材、看步骤、看成品图。你看了一年,看了三百多万道菜。这时候有人对你说"宫保",你脑子里立刻蹦出"鸡丁";说"鱼香",你立刻接"肉丝";说"西红柿炒",你立刻补"鸡蛋"。你没学过任何烹饪理论,但你"见得太多了",所以你知道什么菜名跟什么食材搭配最合理。
LLM 就是这样"学"出来的。它没有真正理解"宫保鸡丁"是什么味道,它只是观察到"宫保"和"鸡丁"这两个词在训练数据里一起出现了几万次,所以当它看到"宫保"时,它知道下一个词大概率是"鸡丁"。这种"不靠理解,靠统计"的方式,在学术界有个正经名字,叫自回归语言建模,但说实话,你记住"超级文本接龙"就够了。
一个反直觉的事实
LLM 在接龙的时候,每次只生成一个 token(后面会讲什么是 token),然后把新生成的 token 拼到原文后面,再预测下一个。这个过程重复几百次,就有了你看到的完整回答。换句话说,LLM 不是"想好了再写",而是"边写边想"------每一个字都是在前一个字的基础上"接"出来的。
Token:LLM 世界的"乐高积木"
LLM 不看"字",它看的是"token"。你可以把 token 理解为 LLM 的最小处理单元,就像乐高积木的最小单位是一块塑料块一样。
但问题来了:一个 token 到底是几个字?答案是------不一定。这取决于语言和分词方式。
在英文里,一个 token 大约等于 0.75 个单词。也就是说,"I love programming" 这句话,大概被拆成 3 到 4 个 token:"I"、"love"、"program"、"ming"。注意,"programming" 被拆成了两半------"program" 和 "ming"。这是因为 LLM 的分词器不是按空格分的,而是按常见子词分的。它见过 "program" 很多次,也见过 "ing" 很多次,所以把它们分开处理更高效。
在中文里,情况就完全不一样了。一个汉字通常就是一个 token,有时候两个。比如"我喜欢编程"可能是 4 到 5 个 token。但"人工智能"这四个字,可能被拆成"人工"和"智能"两个 token,也可能拆成"人"、"工"、"智"、"能"四个 token------取决于模型用的什么分词器。
这个差异意味着什么?同样的意思,中文消耗的 token 数往往比英文多。 举个例子:"天气真好"是 4 个 token,而英文 "The weather is great" 大概也是 4 个 token。但如果你写的是"今天天气真不错"(6 个 token),英文 "The weather is quite nice today" 大概 6 个 token,看起来差不多。不过中文里很多表达更"浓缩",比如"风调雨顺"四个字四个 token,英文要说 "favorable weather for crops"------还是 6 个 token 左右。
所以结论是:中英文的 token 消耗大体相当,但细节上因表达方式不同会有差异。 在实际开发中,你不需要手动算 token,但你需要知道:API 按 token 收费,上下文窗口也按 token 算,理解这个概念能帮你省钱、省心。
| 语言 | 原文 | 大约 Token 数 | 说明 |
|---|---|---|---|
| 英文 | I love programming | 3 ~ 4 | "programming" 可能被拆成 "program" + "ming" |
| 英文 | Artificial intelligence is amazing | 5 ~ 6 | "Artificial" 可能拆成 "Art" + "ificial" |
| 中文 | 我喜欢编程 | 4 ~ 5 | 每个汉字通常是一个 token |
| 中文 | 人工智能太厉害了 | 6 ~ 7 | "人工智能"可能被拆成 "人工" + "智能" |
| 代码 | const user = await db.find() | 7 ~ 8 | 代码 token 化更复杂,括号、点号各算一个 token |
成本从 usage 开始
不要用"多少汉字等于多少 token"来算账。不同模型的 tokenizer、缓存价格和工具费用不同。记录 API 返回的输入、缓存输入、输出和推理 token,再按调用当天的官方价格计算。
上下文窗口:为什么不能无限制聊天?
你有没有过这种体验:你跟朋友聊了三个小时,聊到最后,你已经忘了最开始聊的是什么了。你们从"中午吃什么"聊到"我小时候养过一只猫",然后聊到"猫粮哪个牌子好",最后聊到"要不要一起开个宠物店"。等聊到开宠物店的时候,中午吃什么这件事早就被你们抛到九霄云外了。
这不是你的记忆力有问题,而是你的 "工作记忆"容量有限。心理学上有个概念叫"工作记忆容量"------人脑能同时保持活跃的信息,大概只有 4 到 7 个"组块"。超过这个范围,旧信息就会被挤出去。
LLM 也有完全一样的问题。它的"上下文窗口"(Context Window)就是它的工作记忆。窗口里能装多少 token,它就能"记住"多少内容。超过窗口的部分,它会直接忘掉------就像你忘了三个小时前聊的中午吃什么一样。
模型的上下文窗口从几万到百万级 token 不等,而且同一模型不同端点、版本或账户也可能有差异。工程上不要依赖记忆中的数字:把窗口、最大输出和支持能力放进模型配置,并在启动时校验。
| 配置项 | 为什么要记录 | 超限策略 |
|---|---|---|
| contextWindow | 输入、历史与输出共享预算 | 裁剪、摘要或重新检索 |
| maxOutputTokens | 防止回答过长与成本失控 | 按任务设置,不用全局常量 |
| supportsTools | 决定能否可靠执行工具循环 | 切换模型或关闭相关能力 |
| supportsStructuredOutput | 决定结构化结果的实现方式 | Schema 校验与修复/重试 |
窗口大当然是好事,但也不是越大越好。有两个现实问题你必须面对:
第一,成本。 你往上下文窗口里塞的每一个 token,API 都要计费。如果你把一整本《三体》塞进上下文,光是输入费用就得好几块钱。而且在超长上下文中,模型的表现会下降------它可能"注意"不到关键信息,就像给你一本完整的《新华字典》让你找答案一样,东西太多反而找不到重点。
第二,延迟。 上下文越长,模型处理的时间越长。你塞 100K token 进去,等它吐第一个字可能要等好几秒。用户可没这个耐心。
所以,做大窗口是厂商炫技的事,用好窗口是我们开发者的事。后面讲 RAG(检索增强生成)的时候,我们会学到一种更聪明的做法:不把所有内容都塞进上下文,而是先检索,只把相关的部分放进去。这样既省钱又快速,效果还更好。
注意"中间丢失"问题
研究发现,当上下文很长时,模型对中间部分的信息提取能力会明显下降。开头和结尾的内容它记得最清楚,中间的内容很容易被忽略。这个现象叫"Lost in the Middle"。所以如果你把重要信息放在提示词中间,可能模型根本没注意到。后面讲提示词工程的时候,我们会专门讲怎么避免这个问题。
三个关键参数:temperature、top_p、max_tokens
调用 LLM API 的时候,除了传递你的提示词,你还需要设几个参数。这些参数决定了模型"怎么说话"------是保守一点还是大胆一点,是说三句话还是说三十句。
temperature:模型的"面试紧张程度"
这个参数的名字取得很有意思------"温度"。把它想成面试时的紧张程度就很好理解了:
- temperature = 0:相当于你完全不紧张,对答如流,每次被问到同样的问题,你都会给出完全一样的标准答案。模型会永远选择概率最高的那个 token,回答非常稳定、可预测。适合代码生成、数学计算、翻译等需要精确性的任务。
- temperature = 0.5:稍微有点紧张,但总体还在掌控之中。回答大体稳定,偶尔会换一种说法。
- temperature = 1.0:适度的紧张感,让你发挥出了创造性。回答有变化,但不离谱。这是很多场景的默认值,适合日常对话、内容创作。
- temperature = 1.5:高度紧张,开始说胡话了。回答变得天马行空,可能会出现一些意想不到的创意,但也可能完全跑偏。适合头脑风暴、创意写作。
- temperature = 2.0:紧张到胡言乱语。每次回答都不一样,而且经常前言不搭后语。基本只在测试极端情况时用。
temperature 的取值范围通常是 0 到 2,不同模型可能略有差异。它的原理是:在模型决定下一个 token 时,每个候选 token 都有一个"概率分数"。模型本来会选分数最高的那个。temperature 做的,就是把所有候选的分数"拉平"一点------温度越高,拉得越平,那些本来不可能被选中的 token 也有了机会。
打个比方:正常状态下,你面前有 10 个苹果,其中 9 个红的、1 个青的,你闭着眼睛一抓,大概率抓到红的。temperature 升高,相当于把 10 个苹果重新染色------红的变少,青的变多,你抓到青苹果的概率就变大了。
top_p:只给模型"好选项"
top_p 是另一个控制随机性的参数,全名叫"nucleus sampling"(核采样)。它的逻辑是:只从概率最高的那一批 token 中选,直到这些 token 的累计概率达到 p 值。
比如设 top_p = 0.1,模型就只看概率最高的那 10% 的 token,其他 token 直接忽略。设 top_p = 0.9,模型会从概率最高的 90% 的 token 中选。
temperature 和 top_p 的区别在哪?temperature 是"改变概率分布",让所有 token 的分数都变平;top_p 是"截断候选池",直接砍掉那些概率太低的选项。一个是在"调整权重",一个是在"限定范围"。
实际使用中,通常只调 temperature 就够了,top_p 保持默认值 1.0。同时调两个参数容易产生意想不到的效果,而且很难调试。当然,如果你是个"参数控",非要两个都调,那也没问题------只是建议一次只改一个,观察效果后再改另一个。
max_tokens:让模型别"话痨"
这个参数最简单------限制模型一次最多输出多少个 token。比如你只需要一个"是"或"否"的回答,那设 max_tokens = 10 就绰绰有余。如果你需要模型写一篇 800 字的文章,那大概需要设 max_tokens = 2000 左右(中文 1 个 token 约等于 0.5~1 个字,还要考虑模型可能输出英文标点、换行等)。
max_tokens 有两个作用:
- 控制成本:很多模型的输出 token 单价高于输入,限制输出长度通常能直接省钱;具体比例以调用当天的官方定价为准。
- 控制行为:有时候你问了模型一个问题,它回答得滔滔不绝,把不该说的也说了。设一个合理的 max_tokens 能避免这种情况。
注意,max_tokens 不是"字数限制",而是"token 数限制"。如果你设 max_tokens = 100,模型在第 100 个 token 的时候就会戛然而止------哪怕它正在说一半的话。所以设的时候要留点余量。
模型选型:别背排行榜,用任务集做决定
模型迭代和价格变化太快,把某个"最强模型"写死在教材里,很快就会过期。更可靠的方法是:先用你的真实任务建立一个小型测试集,再比较候选模型。测试集不需要很大,起步时 20~50 条高价值样例,往往比网上的综合榜单更有用。
| 维度 | 要记录什么 | 常见误判 |
|---|---|---|
| 任务质量 | 准确率、格式通过率、工具选择正确率、人工偏好 | 只看一两个"惊艳回答" |
| 延迟 | 首 token 延迟、完整响应时间、P95/P99 | 只记录平均值 |
| 成本 | 输入、缓存输入、输出、工具调用和失败重试 | 只比较公开 token 单价 |
| 能力 | 结构化输出、工具、视觉、长上下文、流式事件 | 把"支持"当成"效果一致" |
| 工程约束 | 速率限制、地域、合规、SLA、数据保留策略 | Demo 跑通就认为能上生产 |
| 可迁移性 | 接口差异、回退模型、供应商锁定和测试成本 | 把兼容接口当成完全兼容 |
用三档路由代替"一把梭"
- 快速档: 分类、抽取、改写等高频确定性任务,优先低延迟和低成本。
- 均衡档: 日常问答、代码辅助和常规工具调用,兼顾质量与价格。
- 深度档: 复杂规划、关键决策和难例兜底,只在路由规则命中时使用。
这三档不是固定模型名,而是你系统里的业务角色。模型升级时只改配置和评估基线,不让业务代码到处出现供应商名称。
易变信息的阅读规则
本文不再复制会快速过期的价格表。OpenAI 当前模型、能力、上下文与价格请查官方模型目录;其他提供方也应以其官方模型页和账户控制台为准。每次选型都记录日期、模型快照和测试集版本。
把原理变成工程判断
理解 LLM,不是为了背术语,而是为了在工程现场做判断。看到回答不稳定,先检查采样与指令;看到响应慢,先拆分首 token 延迟和完整生成时间;看到成本上升,先观察输入、输出与缓存命中;看到事实错误,优先增加可靠数据源和验证,而不是盲目调大模型。
质量不够
先建立测试集,定位是知识、推理、提示词还是工具问题。
速度太慢
缩短上下文、流式返回、并行工具,并选择合适模型档位。
成本太高
减少重复上下文、缓存稳定前缀、设置输出上限和模型路由。
结果不稳
降低非必要随机性,使用结构化输出并增加代码校验。
本章小结
- LLM 是基于上下文逐 token 生成内容的模型,"像理解"不等于事实天然可靠。
- Token 同时影响窗口、速度与成本;上下文管理是 Agent 的核心工程能力。
- 模型选型要用自己的任务集比较质量、延迟、成本、工具能力和合规要求。
下一章预告
下一章不再停留在概念层:我们会把模型接进 TypeScript,完成普通调用、流式输出、结构化结果、错误处理和 NestJS Gateway。