你有没有遇到过这种情况同一个提示词温度已经调到 0输出结果还是会偶尔飘一下再把 top_p 往下压文本突然就稳定了很多。大模型的打分、采样以及后来常听说的 Pi Agent 这类智能体表面上是几套独立的概念实际上串在同一条逻辑线上。只有把“模型到底怎么选下一个词”这件事想清楚后面调参数、调试 Agent 才不会靠猜。这篇文章我会结合自己实际跑过的项目把 token 打分与采样机制拆开讲再延伸到 Pi Agent 这类智能代理的核心原理以及工程落地时到底该怎么调、怎么避坑。我默认读者至少部署过或者调用过大模型 API对 temperature、top_p 有模糊印象但没有系统理解。如果你正准备写一个偏向稳定性、工具调用的应用这篇文章会更适合你。内容不会太上头讲数学但关键公式和计算逻辑我会保留因为后面排错的时候靠的就是这些细节。1. 先从“打分”说起模型脑子里其实是一张词表账单1.1 每个候选词都有一个 logit 分数大模型在生成下一个 token 时做的事情本质上是一次“全词表打分”。假设词表大小是 32K 或者 128K那么模型最后会输出一个同样长度的向量每一个位置对应词表里某个 token 的原始得分这个得分在深度学习里叫 logit。它可以是正的也可以是负的数值大小没有任何绝对意义只有相对比较才有意义。我举个例子。比如输入“今天的天气很”模型可能给“好”打了 5.2 分给“不”打了 3.1 分给“热”打了 0.8 分给“ ”打了 -0.5 分。注意这里不是概率只是分数。真正的概率分布要通过 softmax 才能得到。softmax 做的事情就是把这一堆分数压缩成总和为 1 的概率分数最高的那个 token概率未必就压倒性大具体要看分数之间的差距。如果最高分和次高分只差 0.1那么两者概率差不多如果差了 5.0那么概率会非常集中。这个“分数差距”非常重要因为它决定了采样的随机性还有多大空间。每一层 Transformer 计算到最后都会接一个 lm_head 线性层把最后一个隐状态映射到词表大小。这个映射就是打分的全过程。很多人以为模型“想好了一句话再开始说”其实不是它是一个 token 一个 token 现算现卖的。1.2 softmax 之后才是概率但不是每个概率都适合直接用softmax 的公式写出来很简洁p_i exp(logit_i / T) / sum_j(exp(logit_j / T))这里 T 就是 temperature。当 T1 时就是标准 softmax。当 T 大于 1logit 会被缩小概率分布变得更平缓当 T 小于 1logit 会被放大高概率 token 的胜率更高。一个很常见的误解是temperature0 就是“随机性最小”。严格来说T0 时公式里除以 0 没有意义所以几乎所有推理框架在 T0 时会直接取 logit 最大的 token也就是贪心解码等价于 argmax。我遇到过有人把 T 设成 0.0然后问“为什么有时候还会随机”这多半是因为框架内部把 0.0 解释成了“不指定”悄悄换成了默认值。你在代码里真要关闭采样最好显式把 do_sampleFalse 或者直接调用 greedy 接口不要只丢一个 temperature0。另一个容易踩的点top_p、top_k 影响的是“截断哪些 token 参与重算”temperature 影响的是“截断前每个 token 的权重”。它们的执行顺序在大部分框架里是先缩放 logits再做 top_k/top_p 过滤最后 softmax。这个顺序影响实测效果后面我单独讲。2. 采样策略不是玄学temperature、top_k、top_p 各自管哪一块2.1 temperature 管“胆子大小”但挡不住低概率 tokentemperature 的本质是调节概率分布的形状。T 高尾部 token 被选中的概率变大输出更有“发散感”T 低头部 token 的概率被拉大输出更保守。但只调 temperature 有一个问题即便温度调得很低只要次高和最高的概率没有拉开差距模型依然会在几个近义词之间抖动。你让模型生成“今天天气很__”候选的“好”“不错”“晴朗”“棒”如果分数接近输出就容易不稳定。这时候需要 top_p 或 top_k 把这一撮候选词之外的全部砍掉。我自己的习惯是写代码、做 JSON 抽取、跑 Agent 工具调用temperature 直接给到 0.1 到 0.3同时 top_p 给 0.9 左右做营销文案、故事草稿temperature 给 0.8 到 1.2top_p 给 0.95。不要只调一个。2.2 top_k 像是一个固定名额的投票池top_k 表示只保留概率最高的 K 个 token然后把其余 token 全部排除再重新归一化概率。K 是一个绝对值比如 50代表每一轮解码只在得分最高的 50 个候选词里面选。它的优点是简单、可控。缺点是词在不同位置的分布状态差别太大有些位置模型非常确定前 5 个候选就占了 99% 概率有些位置模型很犹豫前 100 个候选都差不多。用一个固定 K要么在确定的位置引入了不必要的噪声要么在犹豫的位置把合格候选误杀了。我在做中文内容生成时对 top_k 比较谨慎因为中文词表切分方式和英文不一样一个汉字或词组在词表里的地位很不平均。固定 K 很容易让一些生僻字偶尔冒出来英语环境里这种现象会轻一些。2.3 top_p 是“看菜下饭”的核采样top_p 又叫 nucleus sampling它的逻辑比 top_k 更合理从概率最高的 token 开始往下累加直到累计概率刚好超过 p 就停然后只在这些 token 里重新归一化。比如 p0.9那么无论最后剩下 20 个还是 200 个 token都是“足以覆盖 90% 可能性的那一组”。在模型确定的位置截断名单会很短在不确定的位置截断名单会变长。这个适应性正是 top_p 比 top_k 更稳的原因。实际使用中我一般把 top_p 当作“保险丝”给 0.85 到 0.95 之间。它不负责“让结果更有创意”只负责“把明显不合理的低概率垃圾选项排除掉”。2.4 惩罚项解决“车轱辘话来回说”的常用手段除了 temperature、top_k、top_p现在主流框架还会暴露 repetition_penalty、frequency_penalty、presence_penalty 这几个参数。它们的作用维度不一样repetition_penalty对已经出现过的 token 做额外降权降权力度只看“出现过没有”出现次数越多降得越狠。frequency_penalty按 token 出现次数比例进行惩罚次数越多惩罚越大。presence_penalty不管出现几次只要出现就惩罚一次偏向鼓励引入新主题。我在摘要生成任务里遇到过特别典型的重复问题模型第一段写了“公司今年营收增长”第二段又写“公司今年营收增长”不是 bug而是解码时这些 token 的联合概率确实很高。这时候把 frequency_penalty 调到 0.5 左右比调低 temperature 更有效因为问题不在随机性而在局部高概率循环。下面的对比表是我自己整理的一个快速选参参考适合大多数中小模型场景temperaturetop_pfrequency_penaltypresence_penalty代码生成0.1-0.30.900结构化 JSON 抽取0-0.20.9500对话助手0.70.90.30.3创意写作0.9-1.20.950.50.4摘要生成0.30.90.50这套组合不是金科玉律但它能覆盖我 80% 的调参场景。剩下 20% 的情况需要根据模型体量、微调数据和任务目标微调。3. 实战中采样参数为什么必须联动着调3.1 先调 temperature还是先调 top_p我的经验是先确定任务的“确定性需求”再定 temperature然后用 top_p 补一刀。如果任务要求稳定输出比如代码补全、SQL 生成、实体抽取temperature 直接降到 0.3 以下。如果任务要求多样性比如批量生成不同风格的标题temperature 提到 0.8 以上。接着再看输出里有没有“看起来不合常理但概率不低”的词。如果任务稳定但偶发怪词那就是 top_p 太大压到 0.85 一般能解决如果任务发散但输出太干巴把 top_p 调到 0.98 以上让更多尾部词参与竞争。有一个容易忽略的点temperature 升高后top_p 的截断效果也会变化。因为温度改变了 logits 的相对差距同一组 token 排序可能不变但累计概率的分布会变。这意味着 temperature 和 top_p 不是各自独立生效你改了一个另一个的“表现”也会跟着变。调参时尽量一次只动一个变量否则很难定位是谁引起了变化。3.2 采样参数对 Agent 工具调用的影响比想象中大如果你只是做聊天问答采样参数影响的是措辞和风格但如果你在做 Agent 工具调用采样参数直接影响“能不能正确触发一个函数”。一个很常见的故障是模型该调用 search_products 工具结果却输出了“search_products”字符串或者自己编了个 searchProducts。这种情况大多不是模型能力不够而是解码策略给了模型太多自由让它从“最可能的 token 序列”里滑到了“第二可能的 token 序列”。我在跑 Pi Agent 这类能自动规划、自动执行代码的智能体时第一个动作就是把采样参数拉回确定性区间。之前的项目里temperature 从 0.7 改成 0.2 之后工具调用失败率直接降了大约 40%代价是生成内容确实更“死板”但在 Agent 场景里“死板”不是缺点。3.3 利用 logprob 判断模型置信度很多推理框架会额外返回每个 token 的 logprob。这个值很有用它能告诉你模型在对当前内容“犹豫不决”。比如模型生成“import os”之后下一个 token 的 logprob 很高说明这一步模型很有把握如果某个 token 的 logprob 和次高 token 差得很少说明这里是个容易出错的点。工程上可以用这个信息做两件事记录低置信度 token 的位置后期人工审查。在 Agent 循环里如果连续多个 token 置信度都很低就提前跳出重新规划提示词或让模型生成多个候选再投票。不要只看最终文本“看起来像不像”logprob 是解码阶段直接暴露出来的内部信号比人工抽检客观得多。4. Pi Agent 核心原理从“模型聊天”到“模型干活”4.1 什么是 Agent为什么不能直接靠提示词解决把大模型当作聊天机器人用交互模式是“输入问题 - 输出答案”把大模型当作 Agent 用交互模式则是“输入目标 - 计划拆分 - 调用工具 - 观察结果 - 调整计划 - 完成目标”。Pi Agent 就是这一类智能代理中的一个典型实现尤其适合代码分析、批量文件操作这类需要多步骤执行的任务。为什么普通提示词工程做不到因为真实任务往往有时间线、外部状态和反馈闭环。你让模型“把项目里的所有 TODO 整理成 Excel”模型在一轮对话里无法真正读取文件、遍历目录、处理格式。Agent 的价值是给模型装了“手”它能执行命令、读文件、写文件、运行测试然后根据执行结果决定下一步动作。Pi Agent 的逻辑核心并不复杂我理解下来就是一个带状态机的循环1. 解析任务目标 2. 生成一个执行计划 3. 选择一个工具并构建参数 4. 执行工具拿到观察结果 5. 判断结果是否符合预期 6. 不符合则修订计划回到第 3 步符合则继续下一步 7. 所有步骤完成后汇总输出这个循环一旦抽象出来你会发现市面上大多数 coding agent 都是同一套骨架。区别在于每个模块的工程实现细节。4.2 Pi Agent 的工具调用设计让模型“在一个受限选项里做选择”Agent 里最关键的一步不是最终文本生成而是“决策”。模型每次决定调用哪个工具、传入什么参数本质上还是一个 token 级别的解码问题但它被强行约束在了一个结构化空间里。实现上通常有两种约束方式。第一种是传统的 function calling 路线模型输出 JSON里面包含 function name 和 arguments框架解析后执行。第二种是更严格的 constrained decoding在解码阶段直接通过 mask 掉非法 token保证模型不可能输出一个不在预定义 schema 里的 JSON。Pi Agent 这类偏工程向的框架我更推荐用第二种思路去理解。因为它不是让模型自由发挥而是把模型的行为空间限制在一个很小的集合里采样参数在这种模式下才能发挥真正的价值。如果模型可以随意输出那温度调多低都救不回来。正因为如此采样参数在 Agent 里有更直接的影响temperature 决定“策略探不探索”top_p 决定“工具名会不会被截断”penalty 决定“计划步骤会不会重复”。我之前遇到的一个教训是给 Agent 开了 frequency_penalty结果它把某个必要函数调用当成“重复内容”给惩罚掉了导致执行链断裂。所以 Agent 场景里惩罚项默认关掉除非你明显看到循环重复。4.3 记忆与反思Agent 比 RAG 多出来的那一层很多人会把 Agent 和 RAG 混在一起实际上两者的定位不同。RAG 解决的是“模型不知道什么”把外部知识塞进上下文Agent 解决的是“模型不能做什么”把外部工具接入决策循环。Pi Agent 的上下文管理更接近一个工作区它不只存放检索回来的资料还存放中间执行结果、报错信息、上一步的状态。“反思”模块是 Agent 强大和危险并存的原因。强大在于当工具执行失败时模型能读取错误信息修正计划重试危险在于如果采样参数过于发散模型会在失败后脑补一个根本不存在的解决方案越改越偏。我的工程经验是给 Agent 设置“重试上限”与“失败回退”规则。比如同一步骤连续失败 3 次就强制切换策略不依赖模型自动判断是否该停下来。这是把“模型自由决策”和“流程硬约束”结合起来不能只靠其中一边。5. 采样和打分如何反向影响 Agent 的每一步决策5.1 温度越低Agent 越“乖”但也越容易错过替代方案在 Agent 执行路径上低温度可以保证每一步决策都选择概率最高的动作不易出幻觉尤其适合工具调用。但代价是模型几乎不会探索“另一种做法”。举个例子如果第一步计划应该是“读取配置文件”但当前目录下没有这个文件模型大概率会直接报错退出如果温度稍高它可能会尝试列出目录、搜索文件、查看 README从而找到正确路径。这种探索能力对复杂任务有价值但你也要承担它突然偏离目标的风险。我的选择是分阶段控制规划阶段用稍高温度执行阶段用最低温度。规划阶段需要发散把所有可能的路径想全面执行阶段需要收敛避免对工具参数做无中生有的替换。5.2 用 top_p 抑制工具名的“近义词漂移”工具调用中一个隐蔽的问题模型意思对了但表达不对。比如定义好的工具名是 list_files模型偏要输出 listFile 或者 listfiles。这种问题在开了 top_p 过大时更容易出现因为候选集中混入了一些表面合理但 schema 不匹配的 token。解决思路不只是调低 top_p还要在提示词里把工具名、函数签名、参数类型写得足够“死”。在 Pi Agent 这类框架里工具描述就是给模型看的“API 文档”描述模糊模型就只能猜。参数值也一样如果某个参数只允许字符串枚举值那就明确写出来同时把这几个枚举值本身也放进解码约束条件里。5.3 结构化输出时采样参数要让位于约束解码如果你在做一个数据抽取 Agent最终结果要落成 JSON那么单纯调小 temperature 还不够。更可靠的做法是使用约束解码或者严格的 JSON schema 校验。约束解码意味着在每一轮生成 token 时根据当前已生成内容和目标 schema直接屏蔽掉非法 token。这个方法对采样参数几乎是免疫的因为模型没有机会输出 schema 之外的文本。它解决的是“大模型不会稳定地输出合法 JSON”的问题比任何咒语式提示词都靠谱。代价是实现成本高一些而且不是所有推理框架都支持。如果框架不支持退而求其次的做法是temperature0.1 top_p0.9 生成后做 schema 校验校验失败就重新生成但重试次数控制在 3 次以内。6. 综合落地一条可复用的 Agent 采样配置路径6.1 第一步把任务拆成“决策型”和“生成型”在给 Pi Agent 配置采样参数之前我会先做一个任务拆分。写入文件的代码、查询数据库的 SQL、调用函数的参数属于决策型这些内容必须精确解释性注释、错误分析报告、最终总结属于生成型这些内容可以有风格变化。理想做法是决策型步骤用低温度 结构化输出生成型步骤用默认温度 较长输出限制。如果整个 Agent 只允许一套采样参数我建议无脑选低温度因为生成型文字可以后期润色决策型错误却会导致整个任务失败。6.2 第二步给循环加上“质量门禁”Agent 只有循环还不够循环的退出条件必须设计清楚。我通常设置三层门禁第一层是工具执行层命令返回非零退出码就算失败直接进反思。第二层是结构化校验层JSON 解析失败、字段缺失、枚举值非法都算失败。第三层是任务完成度层由模型自己判断目标是否达成但为了防止它自欺欺人我还会额外要求它给出完成证据比如文件路径、关键输出摘要、测试结果。这三层门禁和采样参数没有直接关系但没有门禁单纯调参数无法保证系统质量。采样决定“模型有没有自由”门禁决定“模型乱来之后系统会不会兜底”。6.3 第三步建立可量化的调参基线调参不能靠感觉尤其是 Agent 场景。我的习惯是固定一组测试任务跑 20 轮记录三个指标工具调用成功率模型输出的调用是否被框架成功解析并执行。重试次数同一子任务被循环捞回几次才算成功。完成率最终目标是否达成结果是否可以验收。然后改一个参数再跑同一组任务对比指标。不要同时改 temperature 和 top_p否则出了问题你不知道是谁的责任。我遇到过一个项目修改后工具调用成功率从 80% 掉到 50%排查半天发现是 temperature 从 0.2 调到了 0.4top_p 没动。这个幅度看起来很小但对 Agent 的稳定性影响就是这么大。6.4 第四步必要时退回到“多候选 投票”如果某个步骤要求特别精确但模型单个候选不稳定可以用“多次采样再选”来解决。做法是让模型针对同一个决策点生成 N 个候选结果每个用较高的 temperature 采样最后拿 N 个结果做投票或者按 logprob 总和排序取最一致的答案。这个方法比单纯把 temperature 降到极低更有效。因为 temperature0 只是贪心地选择第一步最可能的 token但“第一步最可能”不等于“完整序列最合理”。多个候选取一致能抵消解码路径上的偶然偏移。代价是推理成本乘以 N所以通常只在关键路径上使用。我在关键的文件操作和代码生成环节用 3 个候选投票非关键步骤直接单次低温度生成。7. 最后分享两个容易被忽略的细节7.1 “打分”不只是解码阶段的事评估阶段也重要项目标题里提到的“打分”在 Agent 工程化中还可以理解成“对任务结果的评估”。Pi Agent 这类框架里经常内置一个模型打分的环节让大模型评估自己或者别的新能手的输出给质量打分。这一步同样受采样参数影响。给分的时候如果把 temperature 调得太高模型可能因为表达差异给出不稳定分数。我的建议是评估任务一律用低温度同时要求模型先给出评分理由再给分数顺序不能反。先给理由会让模型被迫思考先给分数则容易跟着锚定走。7.2 Agent 的上下文窗口不是免费的采样也不会帮你省资源最后提醒一句Agent 和多轮对话不同每次工具返回结果都会塞进上下文时间一长成本会涨得很快。采样参数再合理也解决不了上下文膨胀。工程上要做的不是放任模型把整个文件读进来而是给 Agent 加文件裁剪、行号过滤、摘要前置这些约束让它只看到和自己当前任务相关的部分。我当时第一次把 Pi Agent 用在代码仓库分析上就吃过这套亏Agent 把一个大文件反复读取单次任务的 token 消耗翻了快五倍。后来设置了最大读取行数和按关键字过滤成本立刻降下来稳定性反而更好了。这个优化和模型本身的能力无关但它是能让 Agent 真正跑在业务里的关键一步。大模型打分与采样机制其实没有多高深它就是“词表上每个候选 token 的分数 - 概率化 - 按策略选择”这么一条链路。把这条链路想清楚再看 Pi Agent 这类智能体会发现很多设计都是顺理成章的Agent 要做决策所以必须把自由度收紧Agent 要执行多步所以需要循环和门禁Agent 要可靠所以需要反复评估和策略兜底。我在实际项目中最大的体会是不要把这些东西割裂地看采样参数不是某一步的微调而是整个系统稳定性的基石。少改参数多测指标比追任何新框架都管用。
