AI Agent 开发系列 之 01 🔎重新认识 LLM

系列前言

当我们谈论 AI Agent 时,我们到底在说什么?当我们真正开始开发 AI Agent 时,又应该从哪里开始?

网上关于 Agent 的文章越来越多,但其中相当一部分要么缺乏系统性,要么直接从框架教程开始。我们跟着教程复制代码,很快就能跑起一个 Demo,却很难真正理解:代码为什么能工作?Agent 的能力到底从何而来?

本系列文章希望做一件不同的事情:

从 LLM 的基础原理出发,一步步搞清楚 Agent 的能力是如何建立起来的。

在这个过程中,我们一边理解这些原理,以便动手实现一个可运行的 Agent,由浅入深,把抽象概念落地成真正的项目。

本系列主要面向对 Agent 开发还不熟悉,或者处于"似懂非懂"阶段的朋友。我们不会一开始就使用 LangChain等框架,因为这些框架封装了大量细节。如果完全不了解它们的底层机制,就很难把项目真正做的出色。当然我们的目标不是重复造轮子,而是:先理解轮子是怎么转的,再决定什么时候使用现成的轮子。

本篇主旨

读完这篇,你会清楚地知道:

  • LLM 本质上是什么,它能做什么,不能做什么
  • 认识 Token
  • Temperature、Top-P、Top-K 这些参数在底层做了什么
  • 什么是上下文
  • 什么是系统提示词,它将如何演变

LLM 的本质

理解 Agent 之前,必须先把 LLM 的本质搞清楚。我们可能用了很久网页端或应用端的 ChatGPT、Claude,对他们表现出的能力赞叹不已,以至于我们自然的认为LLM 天生就是这么智能强大。首先要建立的观念就是LLM 远不是这么强大,它并不支持记忆,无法调用工具,更无法主动行动。它本身没有状态,每次调用都是独立推理。

在开发层面来看,LLM 的调用过程,归根结底就是一个函数。 我们向LLM提供指令输入(即Prompt),LLM返回响应结果。

scss 复制代码
f(输入文本) → 响应Token

LLM本质上就是一个在海量文本上训练出来的、极其复杂的概率预测机器 。 给它一段文字,它会预测并挑选接下来最可能出现的词(Token),然后把这个词加到文字末尾,再预测下一个,如此循环,直到生成一个完整的回答。这就是所谓的自回归生成,当前主流LLM的结果生成方式。

什么是Token

在使用LLM时,你一定会频繁遇到一个概念:Token 。模型处理文本的基本单位并不是字符,也不是单词,而是 Token 。Token 可以理解为一种 介于字符和单词之间的文本片段,它是模型进行计算、理解和生成文本时的最小处理单元。

在进行估算时,我们通常可以 粗略地把一个英文单词或一个汉字看作一个 Token 。但我们必须知道这并不完全准确,因为 Token 的划分方式取决于模型所使用的 分词算法(Tokenizer)

下面通过一个 Claude Token 计算器的例子来直观感受这一点:

同样由 7 个中文字符组成的句子,其对应的 Token 数量并不一定是 7。甚至在只替换其中一个字(例如将"今"替换为"昨")的情况下,Token 数量也发生了变化。

此外,不同的大模型使用的 Tokenizer 并不相同,因此 同一段文本在不同模型中的 Token 数量也可能不同

理解 Token 的概念非常重要,因为 大模型的很多核心机制都是围绕 Token 设计的

  1. 计费单位:绝大多数 LLM API 的调用费用都是按照 Token 计算的。输入的 Token 数量和模型生成的 Token 数量,都会直接影响最终的调用成本。
  2. 上下文长度:模型能够一次处理的最大文本长度(Context Window)也是以 Token 为单位进行限制的。例如 128K Token 即代表模型接受的最大上下文长度为128K个Token,超出这个限制,模型就无法获得完整的信息。
  3. 处理速率:我们通常以模型每秒能吐出多少个 Token 来评估模型的生成速度。

选词的核心------------概率分布

我们之前就提到过,当 LLM 生成文本时,并不是一次性输出完整句子,而是 逐个 Token 生成 。他的本质是根据当前上下文,预测"下一个 Token 出现的概率分布"。在生成一个 Token 时,模型会对整个词表中的所有候选 Token 计算概率。例如,假设有如下句子:

csharp 复制代码
The sky is ___

模型可能得到如下概率分布的候选词:

erlang 复制代码
"blue"    →  28.36%
"cloudy"  →  23.63%
"falling" →  18.90%
"a"       →  13.50%
"an"      →  10.80%
"green"   →  3.38%
"42"      →  1.35%
"xyz"     →  0.06%
...(省略剩余词汇)

那么问题来了,我们如何从这个概率分布中选出满足我们要求的 Token?

几个关键参数

以上提到的选出满足条件 Token的过程叫做采样。在调用LLM API 时,以下参数发挥了极其重要的作用。

Temperature

Temperature 在数学上是一个概率分布的缩放因子,它不会改变候选词的排序,但会改变 高概率词和低概率词之间的差距。我们继续以刚刚的候选词作为例子(假设只有数个候选词)。

Temperature = 1 :使用原始概率分布。此时概率分布如图示:

Temperature → 0(极低) :概率分布被「压尖」。最高概率的词会无限接近 100%,其他词趋近于 0。输出几乎完全确定,每次调用同一个 prompt 得到相同结果。

Temperature → 2(极高) :概率分布被「拉平」。所有词的概率趋于接近,此时生成的文本会更加 多样化,但也更容易出现不合理内容。

在实际开发中,针对同任务需要不同程度的确定性:

场景 推荐 Temperature 原因
结构化输出(JSON) 0 ~ 0.1 格式必须稳定精确
代码生成 0.1 ~ 0.3 逻辑正确优先
问答、摘要 0.3 ~ 0.7 准确性和自然度兼顾
Agent 任务规划 0.2 ~ 0.5 需要一定创造力
头脑风暴、创意写作 0.7 ~ 1.2 鼓励多样化的想法

这些值也不是固定的,模型输出不理想时可以根据实际情况进行调整。还有就是不同模型支持的Temperature值范围也不一样,但它们都遵循同一条规则值越小输出越稳定,值越大输出越随机

Top-P

Top-P 也叫 Nucleus Sampling (核采样),是另一种控制随机性的方式,但机制与 Temperature 不同。它的思路不是改变概率分布,而是 限制候选词的范围

它的工作原理大致为:把所有词按概率从高到低排序,然后从最高概率开始累加,累计概率达到 p 值时,其后的词不再考虑,采样时只会在这些选中的"核"中进行。

继续刚才的例子,假设Top-P为0.9,按照累计概率,核中的候选词为"blue"/"cloudy"/"falling"/"a"/"an"。处理过程如下:

erlang 复制代码
"blue"    →  28.36%  →  累计28.36%
"cloudy"  →  23.63%  →  累计51.99%
"falling" →  18.90%  →  累计70.89%
"a"       →  13.50%  →  累计84.39%
"an"      →  10.80%  →  累计95.19% ⬅ 触发截断,之后的不再考虑
"green"   →  3.38%
"42"      →  1.35%
"xyz"     →  0.06%
"..."

有了Temperature之后,我们为什么还需要Top-P?首先要明确Temperature和Top-P是从两个不同的独立的角度去处理候选词的。在某些情况下,有时候概率分布本身就很"平",很多词概率接近。如果只用 Temperature,模型仍然可能选到一些 极低概率的奇怪词汇 。此时通过Top-P 截断长尾概率,可以有效避免这种情况。

在实践中一般建议,只调Temperature和Top-P中的其中一个,另一个保持默认

Top-K

Top-K 是另一种直接粗暴的截断方式。每次生成时,只保留概率最高的 K 个词从中采样,完全忽略其他所有词。

例如当Top-K的值为3时,只会保留概率由高至低的前三个值"blue"/"cloudy"/"falling",无论其他词概率多少,一律排除。

Top-K 使用 固定数量截断,但没有考虑概率分布的形状。可能出现两种情况,排序靠前的词概率很大时(比如第一个词的概率为98%),Top-K 过大时会引入大量无意义候选词;另一种分布很平的情况下,所有候选词概率差不多,此时 Top-K 若过小又会过早截断,排除很多合理选项。另外就是,并不是所有的模型都支持这个参数,Agent开发时请务必以api文档优先。

在我们调整参数时,一般应按照如下优先级进行调整Temperature → Top-P → Top-K。没有固定的参数搭配,一切从实践中出真知。

上下文窗口

理解 Token 之后,上下文窗口(Context Window,也被称为上下文长度) 就很好理解了。它指的是: 模型在一次推理过程中,最多能够处理的 Token 数量。

在大模型调用过程中,一个基础的上下文窗口通常由固定的开销(System Prompt)以及用户输入组成,在某些情况下,我们还会添加RAG等手段产出的额外信息,这些都会挤占上下文窗口。

不同模型的上下文窗口亦不尽相同,但上下文窗口越来越大是目前大模型发展的一个趋势。比如早期的模型只支持128K的上下文长度,但时至今日,Claude Ops 4.6已支持1M上下文长度,Gemini更是有支持2M上下文长度的模型。不论上下文多大,目前来看,其都是一个有限的值,再大的上下文窗口都会触及其上限,因为我们说过LLM是无状态的,它本身不支持记忆功能。为了人为实现记忆功能,就需要将历史对话同时传递给大模型。

粗暴的将所有信息都填充到上下文中并不是一种好的方案 。过长的上下文会导致模型很难从其中捕捉到有效信息,从而使得性能降低。另一方面,不要忘了,长上下文代表着更多的Token,这也会导致成本暴增。如何在以上条件之间取得平衡,这就是 长对话管理(Long-context Management) 所要解决的问题。

系统提示词

时至今日,很多朋友仍然没有认识到如何写出一份有效的高质量的提示词,对于提提示词的理解,仍然停留在让大模型完成简单角色扮演的程度,比如:你是一个有帮助的 AI 助手,请尽可能回答用户的问题。 诚然,这个提示词会产生一定作用,但实际上很难充分发挥大模型的能力。

在 Agent 应用中,我们多数时候希望模型的行为更加稳定和可预测。这时就需要一份清晰的提示词,它应该是明确的、具体的、结构化的,一般来说可以由以下四个模块构成:

模块 作用
身份定义 模型扮演什么角色
行为规则 回答时必须遵守的原则
输出格式 结构化要求
边界限制 不允许做的事情

这并不代表一份好的提示词是复杂的,或者说越长越好,实践过程中我们需要从一份简短清晰的提示词开发,按照预期的输出对提示词进行评估,逐步地去迭代优化提示词,直至满足需求。在编写提示词时,也有一些技巧范式,比如:思维链(COT)、少样本(few-shot)等等 [参考:www.promptingguide.ai/zh] 。

不过随着模型能力的提升,其中一些技巧已经过时,比如现在主流模型本身就支持推理,再去编制思维链提示词反而会限制它的发挥。可以预见,未来关于 Prompt 设计 的"技巧工程"会进一步衰退,但Prompt仍然会有举足轻重的地位,其在"系统约束层面"的重要性会进一步上升。 System Prompt 的主要作用已经逐渐从"提示模型如何完成任务"转变为"定义模型应该遵循的行为协议"。这也正是我们之前提到的,当下设计 System Prompt 时,一般由身份定义、行为规则、输出格式以及边界限制四个模块构成的原因。在这种架构下,System Prompt 不再是唯一的输出控制手段,而成为了整个上下文系统中的一个组成部分。这也反映了当前的另一个趋势:提示词工程(Prompt Engineering) 正在被提示词工程(Context Engineering)所替代。

本篇结语

到这里,我们已经把 LLM 的一些关键基础概念梳理了一遍:Token、概率采样、上下文窗口以及 System Prompt。这些内容看起来零散,但实际上共同构成了 大模型工作的底层逻辑 。理解这些机制之后,我们就不再只是"调用一个会聊天的 API",而是开始真正理解 模型是如何生成结果的,以及我们可以如何影响它的行为

不过,还是那句话,LLM 本身仍然只是一个 概率生成器 。Agent 的核心价值,正是在 LLM 之上构建一层能力系统,让模型能够 规划任务、调用工具,并在多轮交互中持续完成目标

在下一篇中,我们将正式开始进入 Agent 的世界 :从零搭建一个最基础的 Agent 项目,实现 对话调用、多轮对话以及结构化输出,把前面理解的原理一步步落到真实代码之中。

相关推荐
_张一凡2 小时前
【大语言模型学习】一文详解阿里Qwen3大模型以及全参量微调入门实战教程(代码完整)
llm·aigc·大语言模型·多模态·qwen3·大语言模型微调·全参量微调
Henrybit933682 小时前
Claude与OpenAI的差异
人工智能
树上有只程序猿2 小时前
OpenClaw确实好用,但你得明白自己想要什么
人工智能
天天讯通2 小时前
智能语音机器人未来的发展方向
人工智能·机器人·语音识别
前端AI充电站2 小时前
Google 开始卷价格了:Gemini 3.1 Flash-Lite,会不会把 AI 应用成本真的打下来?
前端·人工智能
guts3502 小时前
使用python里的OpenCV包做简单的车道线检测
人工智能·python·opencv
金智维科技官方2 小时前
APA智能流程自动化是什么?
大数据·人工智能·ai·智能体·apa
智慧化智能化数字化方案2 小时前
可信数据空间——详解2025 华为AI可信数据空间白皮书【附全文阅读】
人工智能·可信数据空间·华为ai可信数据空间白皮书
两只羊2 小时前
折腾 OpenClaw:从零开始在 Ubuntu 上部署并搞定局域网访问
人工智能