1. 参数体系为什么是 Agent 调优的命门
1.1 从一次线上事故说起
去年冬天我接手了一个客服场景的 Agent 项目,上线第三天就出了状况。用户问“帮我查一下上个月的订单”,Agent 返回了一段洋洋洒洒三百字的分析,把订单号、金额、时间全列了一遍,最后还贴心地问“需要我帮您办理退货吗”。问题是,用户只是想知道订单状态,而且这段回复消耗了 1800 多个 token,响应时间从平均 1.2 秒飙到 4.7 秒。排查下来,问题不在提示词,也不在工具调用逻辑,而是max_tokens设成了 2048,temperature用了默认的 1.0。这两个参数一叠加,模型就开始“自由发挥”。
这件事让我意识到,很多做 Agent 开发的朋友把精力全放在框架选型、工具编排、记忆机制上,却忽略了最底层的东西——参数体系。参数就像汽车的油门、刹车和方向盘,框架再好,参数调不好,车照样开沟里。temperature、top_p、max_tokens这三个参数,加上 Agent 场景特有的循环控制、工具调用阈值、上下文窗口管理,构成了一套完整的调优体系。这套体系不搞明白,Agent 要么反应迟钝,要么胡说八道,要么烧钱如流水。
这篇文章面向的是正在做 Agent 开发、或者准备把 Agent 推向生产环境的工程师。我会从参数的本质讲起,把每个参数的物理意义、适用场景、调优方法拆开揉碎,再结合 Agent 的特殊性给出可落地的配置方案。不管你是刚接触 Agent 的新手,还是已经踩过一些坑的老手,都能从中找到可以直接抄作业的东西。
1.2 参数体系的三个层次
Agent 的参数体系不是孤立的几个数字,它分三个层次。最底层是采样参数,包括temperature、top_p、top_k、repetition_penalty这些控制模型输出随机性的参数。中间层是资源参数,包括max_tokens、context_window、timeout这些控制成本和性能的参数。最上层是Agent 特有参数,包括最大循环次数、工具调用超时、记忆检索条数、反思触发阈值等。
这三层参数相互影响。比如你把temperature调低,模型输出更确定,但可能需要更多轮循环才能完成任务,max_tokens的消耗反而可能增加。再比如top_p和temperature同时调整时,效果不是简单叠加,而是存在耦合。我见过不少项目把这三层参数混在一起调,改一个动全身,最后根本不知道哪个参数起了作用。正确的做法是分层调优,先固定底层采样参数,再调资源参数,最后优化 Agent 特有参数。
2. 采样参数:temperature、top_p 与 top_k 的三角关系
2.1 temperature 到底在调什么
temperature的本质是 softmax 函数里的温度系数。模型输出每个 token 的概率分布是 logits 经过 softmax 得到的,temperature 就是用来缩放 logits 的。公式很简单:p_i = exp(logit_i / T) / sum(exp(logit_j / T))。当 T=1 时,就是标准的 softmax;当 T<1 时,分布变得更尖锐,高概率的 token 更容易被选中;当 T>1 时,分布变得更平坦,低概率的 token 也有机会冒出来。
用生活化的例子说,temperature就像给模型喝咖啡。T=0 的时候模型完全清醒,每次都选概率最高的那个词,输出极其确定,但也很死板。T=1 的时候模型正常状态,有一定随机性。T=2 的时候模型喝大了,开始胡言乱语,什么离谱的词都敢往外蹦。在 Agent 场景里,temperature的设置取决于任务类型。做工具调用、参数提取、格式转换这类需要精确性的任务,temperature应该设在 0 到 0.3 之间。做创意生成、头脑风暴、多轮对话这类需要多样性的任务,可以设在 0.7 到 1.0 之间。
我实测下来,Agent 的主循环里temperature设 0.1 到 0.2 是最稳的。这个区间既能保证工具调用的准确性,又不会让输出完全僵化。有个细节要注意,有些模型在temperature=0时会出现重复输出的问题,因为概率最高的 token 被反复选中,陷入死循环。所以我不建议直接设 0,留 0.1 的余量更安全。
2.2 top_p 和 top_k 的截断逻辑
top_p叫核采样,它的逻辑是:把所有 token 按概率从高到低排序,累加概率,当累加值达到top_p时,只保留这些 token,后面的全部丢弃。比如top_p=0.9,就是保留概率累加前 90% 的 token。top_k更直接,只保留概率最高的 k 个 token,后面的全砍掉。
这两个参数和temperature的区别在于,temperature是调整整个分布的形状,top_p和top_k是直接截断分布。打个比方,temperature是调整筛子的孔径,top_p和top_k是直接拿剪刀把筛子剪小。实际使用中,top_p比top_k更常用,因为top_p是动态的,不同位置的截断数量不一样,而top_k是固定的,可能在概率分布很尖锐时保留了太多无关 token,在概率分布很平坦时又砍掉了太多有用 token。
Agent 场景下,我的经验是top_p设 0.9 到 0.95 比较合适。设太低,比如 0.7,模型输出会变得很保守,工具调用的参数可能不够灵活。设太高,比如 1.0,等于不截断,低概率的 token 会混进来,导致输出不稳定。top_k一般设 40 到 60,或者干脆不设,让top_p来管。有个坑要注意,temperature和top_p不要同时调,先固定一个调另一个,否则你根本分不清是哪个参数在起作用。
2.3 采样参数的组合策略
不同任务场景下,采样参数的组合策略完全不同。我整理了一个对照表,可以直接参考:
| 任务类型 | temperature | top_p | top_k | 说明 |
|---|---|---|---|---|
| 工具调用/参数提取 | 0.1 | 0.9 | 40 | 需要高确定性,避免参数幻觉 |
| 格式转换/结构化输出 | 0.0-0.1 | 0.85 | 30 | 最严格,确保 JSON 格式正确 |
| 多轮对话/客服 | 0.3-0.5 | 0.9 | 50 | 平衡确定性和自然度 |
| 创意生成/头脑风暴 | 0.8-1.0 | 0.95 | 60 | 需要多样性,允许发散 |
| 代码生成 | 0.2-0.3 | 0.9 | 40 | 偏确定,但保留一定灵活性 |
| 反思/自我批评 | 0.5-0.7 | 0.9 | 50 | 需要一定发散性来发现问题 |
这张表是我在多个项目里反复验证过的,但也不是铁律。比如代码生成,如果你用的是专门训练过的代码模型,temperature可以设到 0.1 甚至 0。关键是要根据实际输出效果微调,不要迷信任何固定值。
注意:采样参数调整后一定要做回归测试。我见过一个项目把
temperature从 0.7 调到 0.2,工具调用准确率上去了,但多轮对话的满意度下降了 15%。参数调优永远是权衡,没有银弹。
3. max_tokens 与上下文窗口的成本博弈
3.1 max_tokens 不是越大越好
max_tokens控制的是模型单次输出的最大 token 数。很多人觉得设大点没坏处,反正用多少算多少。这个想法在简单对话场景下没问题,但在 Agent 场景下会出大问题。原因有两个:第一,max_tokens会影响模型的输出行为。当max_tokens设得很大时,模型倾向于生成更长的回复,因为它知道有足够的空间。第二,Agent 的每一轮循环都会消耗 token,如果单次输出不受限制,多轮循环下来成本会指数级增长。
我做过一个测试,同一个 Agent 任务,max_tokens设 512 和设 2048,完成率差不多,但 token 消耗差了 3.2 倍。设 2048 的时候,模型会在回复里加很多“让我想想”“根据我的分析”这类废话,而这些废话在 Agent 循环里会被反复传递,造成巨大的浪费。所以max_tokens的设置原则是:够用就好,宁小勿大。具体设多少,取决于你的任务类型。工具调用的输出通常很短,256 到 512 足够了。多轮对话的回复可以设 512 到 1024。只有需要生成长文本的场景,比如报告生成、文章写作,才需要设到 2048 以上。
3.2 上下文窗口的分配策略
上下文窗口是模型能“看到”的 token 总数,包括输入和输出。现在主流模型的上下文窗口从 8K 到 128K 不等,但窗口大不代表你可以随便用。窗口越大,推理成本越高,延迟越大。而且有个“中间遗忘”现象,就是模型对上下文中间部分的记忆比开头和结尾弱。所以上下文窗口的分配需要策略。
在 Agent 场景下,上下文窗口通常被这几部分占用:系统提示词、工具定义、对话历史、记忆检索结果、当前用户输入。我的分配经验是:系统提示词和工具定义控制在窗口的 10% 到 15%,对话历史控制在 30% 到 40%,记忆检索结果控制在 10% 到 20%,剩下的留给当前输入和输出。如果窗口是 8K,系统提示词加工具定义不要超过 1.2K,对话历史不要超过 3.2K,记忆检索不要超过 1.6K,留 2K 给输入输出。
当对话历史超出预算时,就需要做历史压缩。常见的方法有滑动窗口、摘要压缩、关键信息提取。滑动窗口最简单,只保留最近 N 轮对话,但会丢失早期信息。摘要压缩是用模型把历史对话总结成一段话,保留语义但丢失细节。关键信息提取是只保留实体、意图、决策等结构化信息。我一般用滑动窗口加摘要压缩的组合,最近 5 轮保留原文,更早的做摘要。
3.3 成本控制的实战技巧
Agent 的成本控制是个系统工程,max_tokens只是其中一环。我总结了几条实战技巧,都是真金白银换来的:
第一,分级设置 max_tokens。Agent 的不同环节用不同的max_tokens。工具调用环节设 256,反思环节设 512,最终回复设 1024。不要全局用一个值。
第二,缓存高频请求。很多 Agent 场景下,用户的问题有大量重复。比如客服场景,“查订单”“退换货”“物流查询”这些意图的回复可以缓存。缓存命中率做到 30%,成本就能降三成。
第三,用小模型做路由。不是所有请求都需要大模型处理。先用小模型做意图分类和难度判断,简单请求直接小模型处理,复杂请求才路由到大模型。这个策略能省 40% 到 60% 的成本。
第四,监控 token 消耗分布。我习惯在 Agent 里埋点,记录每个环节的 token 消耗。跑一段时间后看分布,往往能发现某个环节消耗异常高,优化那个环节就能立竿见影。
| 优化手段 | 预期成本降幅 | 实施难度 | 适用场景 |
|---|---|---|---|
| 分级 max_tokens | 15%-25% | 低 | 所有 Agent |
| 高频请求缓存 | 20%-35% | 中 | 客服、问答类 |
| 小模型路由 | 40%-60% | 高 | 请求类型多样的场景 |
| 历史压缩 | 10%-20% | 中 | 多轮对话类 |
| 工具结果精简 | 5%-15% | 低 | 工具调用频繁的场景 |
4. Agent 特有参数:循环、工具与记忆的调优
4.1 最大循环次数与终止条件
Agent 和普通对话模型最大的区别在于循环。Agent 会反复“思考-行动-观察”,直到任务完成或达到终止条件。这个循环次数如果不控制,轻则烧钱,重则死循环。我见过一个项目因为终止条件写错了,Agent 在“查天气-发现需要定位-查定位-发现需要权限-请求权限-查天气”这个循环里转了 47 圈,最后把 API 配额耗光了。
最大循环次数的设置取决于任务复杂度。简单任务,比如单次查询、格式转换,3 到 5 轮足够了。中等任务,比如多步推理、需要调用两三个工具的,8 到 12 轮。复杂任务,比如需要规划、执行、反思、修正的,可以设到 15 到 20 轮。但不管多复杂,我建议不要超过 25 轮。超过 25 轮还没完成,大概率是任务定义有问题,或者模型能力不够,继续循环也是浪费。
终止条件比最大循环次数更重要。好的终止条件应该包括:任务完成标志(比如模型输出“任务完成”或返回最终答案)、工具调用失败次数超限、连续多轮无进展、总 token 消耗超预算。这几个条件要组合使用,任何一个触发就终止。我习惯在 Agent 里加一个“无进展计数器”,如果连续 3 轮的工具调用结果和之前高度相似,就强制终止,避免无效循环。
4.2 工具调用参数的精细控制
工具调用是 Agent 的核心能力,但工具调用的参数控制经常被忽略。这里有几个关键参数:工具调用超时、工具结果截断长度、工具调用重试次数、并行工具调用开关。
工具调用超时设多少,取决于工具的类型。查询类工具,比如数据库查询、API 调用,超时设 5 到 10 秒。计算类工具,比如代码执行、数据分析,超时设 30 到 60 秒。文件操作类工具,超时设 10 到 30 秒。超时设太短,工具还没返回就被砍了;设太长,Agent 卡在那里干等,用户体验差。
工具结果截断长度是个容易被忽视的参数。很多工具返回的结果很长,比如网页抓取、日志查询,一次返回几万个 token。如果不截断,直接塞进上下文,窗口瞬间就满了。我的做法是,工具结果超过 2000 token 就截断,只保留最相关的部分。截断策略可以是取前 N 个字符、取包含关键词的段落、或者用模型做摘要。取前 N 个字符最简单,但可能丢失关键信息。用模型做摘要最智能,但增加一次模型调用。我一般用关键词匹配加段落截断的组合,平衡效果和成本。
工具调用重试次数设 2 到 3 次比较合理。第一次失败可能是网络抖动,重试一次大概率能成功。但如果重试 3 次都失败,说明工具本身有问题,继续重试没意义,应该让 Agent 走降级逻辑或者报错。
4.3 记忆检索的参数调优
Agent 的记忆系统通常包括短期记忆(对话历史)和长期记忆(向量数据库)。记忆检索的参数直接影响 Agent 的“回忆”能力。关键参数有:检索条数 top_k、相似度阈值、记忆衰减因子、记忆去重策略。
检索条数 top_k 设多少,取决于记忆库的大小和任务需求。记忆库小(几百条),top_k 设 3 到 5 就够了。记忆库大(几万条以上),top_k 可以设 5 到 10。但不要超过 10,否则检索结果太多,上下文塞不下,而且相关性会下降。
相似度阈值是过滤低质量检索结果的。设太高,比如 0.9,可能什么都检索不到;设太低,比如 0.5,会混进大量无关记忆。我的经验是设 0.7 到 0.8 比较合适,具体值要根据 embedding 模型的特性调。有些 embedding 模型的相似度分布偏保守,0.7 就已经很相关了;有些偏激进,0.8 才算相关。这个需要拿实际数据测。
记忆衰减因子是给旧记忆降权的。时间越久的记忆,相关性越低。衰减因子可以按天算,比如每天衰减 0.95。也可以按轮次算,每 10 轮衰减 0.9。衰减因子设太大,旧记忆很快被遗忘;设太小,旧记忆一直占着位置。我一般设 0.95 每天,实测下来比较平衡。
实操心得:记忆检索最容易出的问题是“检索到了但不相关”。我习惯在检索后加一步重排序,用一个小模型对检索结果做相关性打分,只保留 top 3。这一步能提升 20% 以上的记忆利用率。
5. 调优实战:从 baseline 到生产级配置
5.1 建立 baseline 与评估指标
调优的第一步不是改参数,而是建立 baseline。没有 baseline,你改了半天也不知道是变好了还是变坏了。Baseline 的建立包括:固定一组初始参数、准备一组测试用例、定义评估指标。
初始参数我一般用:temperature=0.3、top_p=0.9、max_tokens=1024、最大循环 10 轮、工具超时 10 秒、检索 top_k=5。这组参数不一定是最好,但足够稳,适合作为起点。
测试用例要覆盖 Agent 的主要场景。比如客服 Agent,测试用例应该包括:单轮查询、多轮对话、工具调用、异常处理、边界情况。每个场景准备 10 到 20 条用例,总共 50 到 100 条。用例要标注预期结果,方便自动评估。
评估指标分三类:效果指标、性能指标、成本指标。效果指标包括任务完成率、工具调用准确率、回复相关性。性能指标包括平均响应时间、P95 响应时间、超时率。成本指标包括平均 token 消耗、单次任务成本。这三类指标要一起看,不能只看效果不看成本。
| 指标类型 | 具体指标 | 目标值 | 测量方法 |
|---|---|---|---|
| 效果 | 任务完成率 | >90% | 人工标注+自动评估 |
| 效果 | 工具调用准确率 | >95% | 对比预期工具和参数 |
| 效果 | 回复相关性 | >4.0/5.0 | 人工评分或模型评分 |
| 性能 | 平均响应时间 | <3s | 埋点统计 |
| 性能 | P95 响应时间 | <8s | 埋点统计 |
| 成本 | 平均 token 消耗 | <2000 | API 返回统计 |
| 成本 | 单次任务成本 | <0.01 元 | token 消耗换算 |
5.2 单参数扫描与敏感度分析
建立 baseline 后,开始单参数扫描。每次只改一个参数,其他参数固定,看指标怎么变。比如调temperature,从 0.1 到 1.0,每次加 0.1,跑一遍测试集,记录指标。这样能画出每个参数的敏感度曲线。
我实测下来,temperature在 0.1 到 0.3 之间对工具调用准确率影响最大,超过 0.5 后准确率断崖式下跌。max_tokens在 512 到 1024 之间对任务完成率影响不大,但超过 1024 后 token 消耗线性增长。最大循环次数在 5 到 15 之间对完成率有提升,超过 15 后提升不明显但成本继续涨。
敏感度分析能帮你找到每个参数的“甜点区”。甜点区就是指标最好、成本可接受的区间。比如temperature的甜点区是 0.1 到 0.2,max_tokens的甜点区是 512 到 768,最大循环次数的甜点区是 8 到 12。
5.3 组合调优与 A/B 测试
单参数扫描找到甜点区后,做组合调优。组合调优不是把所有参数都设到最优,因为参数之间有耦合。比如temperature调低后,模型输出更确定,可能需要更多循环次数来探索。max_tokens调小后,单次输出变短,也可能需要更多循环。
组合调优我一般用网格搜索加贝叶斯优化的组合。先在小范围做网格搜索,找到大致方向,再用贝叶斯优化精细调。网格搜索的粒度不用太细,每个参数取 3 到 5 个值就够了。比如temperature取 0.1、0.2、0.3,max_tokens取 512、768、1024,最大循环取 8、12、16,组合起来 27 组,跑一遍测试集,找到 top 3 的组合。
找到 top 3 组合后,做 A/B 测试。A/B 测试要在真实流量上做,不能只用测试集。因为测试集和真实流量的分布可能不一样。A/B 测试要跑够样本量,一般每组至少 1000 次请求,才能看出统计显著性。A/B 测试期间要监控所有指标,不只是效果指标,还要看性能和成本。
注意:A/B 测试不要同时跑太多组。我见过一个团队同时跑 8 组 A/B 测试,结果流量分散,每组样本都不够,跑了半个月也没得出结论。建议同时跑不超过 3 组。
5.4 生产环境的动态调优
生产环境的参数不是一成不变的。用户行为在变,数据分布在变,模型版本也在变。所以需要动态调优。动态调优有两种方式:定时调优和触发式调优。
定时调优是每周或每月跑一次完整的调优流程,重新找最优参数。触发式调优是当监控指标异常时自动触发调优。比如任务完成率连续 3 天下降超过 5%,就触发调优流程。
动态调优的关键是自动化。手动调优效率太低,而且容易出错。我习惯把调优流程脚本化:自动拉取测试集、自动跑参数组合、自动计算指标、自动生成报告。人只需要看报告做决策。这套流程搭起来大概需要两三天,但后续每次调优能省十几个小时。
还有个技巧是灰度发布。新参数不要一次性全量,先放 10% 流量,观察 24 小时,没问题再逐步放大到 50%、100%。灰度期间要密切监控,一旦指标恶化立即回滚。
6. 常见问题与排查技巧实录
6.1 参数调优的典型翻车现场
问题一:调完参数后效果反而变差。这是最常见的。原因通常是测试集和真实流量分布不一致,或者参数之间有隐藏耦合。排查方法是先回滚到 baseline,确认 baseline 正常,然后逐个参数加回去,看是哪个参数导致的。如果单个参数都没问题,那就是组合的问题,需要重新做组合调优。
问题二:Agent 陷入死循环。表现是循环次数达到上限才停止,token 消耗异常高。排查方法是看循环日志,找到重复的模式。常见原因是终止条件没覆盖某种情况,或者工具返回的结果让模型误判了状态。解决方法是加无进展检测,连续多轮结果相似就强制终止。
问题三:工具调用参数错误。表现是工具调用失败率高,或者调用了错误的工具。排查方法是看工具调用的输入输出,对比预期。常见原因是temperature设太高,或者工具定义不够清晰。解决方法是降低temperature,优化工具描述,加参数校验。
问题四:响应时间波动大。表现是 P95 响应时间远高于平均值。排查方法是看响应时间的分布,找到长尾请求的特征。常见原因是某些请求触发了大量工具调用或长文本生成。解决方法是给不同任务类型设不同的超时和max_tokens。
问题五:成本超预算。表现是 token 消耗远超预期。排查方法是看 token 消耗的分布,找到消耗大户。常见原因是上下文窗口管理不当,历史对话越积越多。解决方法是加历史压缩,设 token 预算上限。
6.2 排查工具与日志埋点
排查问题离不开日志。Agent 的日志要记录这些信息:每轮循环的输入输出、工具调用的参数和结果、token 消耗、响应时间、参数配置。日志要结构化,方便查询和分析。我一般用 JSON 格式,每个字段都有明确含义。
埋点要覆盖关键路径。比如工具调用前后各埋一个点,记录调用耗时和结果状态。循环开始和结束各埋一个点,记录循环次数和终止原因。参数变更也要埋点,记录变更前后的配置和指标变化。
排查工具我常用三个:日志查询系统(比如 ELK)、指标监控系统(比如 Prometheus + Grafana)、链路追踪系统(比如 Jaeger)。这三个配合使用,能快速定位问题。日志查细节,指标看趋势,链路看瓶颈。
6.3 参数调优速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 输出重复 | temperature 太低 | 检查 temperature 值 | 调到 0.1 以上 |
| 输出发散 | temperature 太高 | 检查 temperature 值 | 降到 0.5 以下 |
| 工具调用错误 | temperature 高/top_p 高 | 看工具调用日志 | 降 temperature 到 0.2 |
| 响应慢 | max_tokens 大/循环多 | 看 token 消耗分布 | 分级设 max_tokens |
| 成本高 | 上下文管理差 | 看历史对话长度 | 加历史压缩 |
| 死循环 | 终止条件不全 | 看循环日志 | 加无进展检测 |
| 记忆不相关 | 检索参数不当 | 看检索结果 | 调 top_k 和阈值 |
| 格式错误 | temperature 高 | 看输出格式 | 降到 0.1 以下 |
这张表是我从多次翻车中总结出来的,基本覆盖了 80% 的常见问题。遇到问题先查表,能省不少时间。
6.4 独家避坑技巧
技巧一:参数配置版本化。每次调参都记录配置和对应的指标,用 Git 管理。这样出问题能快速回滚,也能追溯哪个参数变更导致了指标变化。我见过太多团队调参不留记录,出了问题只能凭记忆猜。
技巧二:小流量先试。任何参数变更都先在小流量上试,不要直接全量。小流量试的时候要盯紧指标,一旦异常立即回滚。这个习惯能避免很多线上事故。
技巧三:关注参数之间的交互。temperature和top_p有交互,max_tokens和循环次数有交互,工具超时和重试次数有交互。调参时不要孤立地看单个参数,要看组合效果。
技巧四:定期重新评估。模型版本更新、数据分布变化、用户行为变化,都会让最优参数漂移。我习惯每月重新跑一次调优流程,确保参数始终在最优区间。
技巧五:保留人工兜底。参数调得再好,也有翻车的时候。关键场景要保留人工兜底,比如高风险操作、敏感信息查询。Agent 处理不了的,转人工。这不是参数能解决的,但能避免最坏情况。
我在实际项目里踩过的坑远不止这些。有一次把top_p从 0.9 调到 0.95,想着能提升一点多样性,结果工具调用的参数开始出现幻觉,把“查询订单”的参数从订单号变成了用户 ID。排查了半天才发现是top_p的锅。从那以后,我调任何参数都先跑一遍工具调用测试集,确认工具调用没问题再跑其他测试。
还有一次,为了省成本把max_tokens从 1024 降到 256,结果 Agent 的反思环节被截断了,模型还没想完就被迫输出,导致决策质量大幅下降。后来改成分级设置,反思环节保持 512,其他环节降到 256,成本和效果就平衡了。
参数调优这件事,说到底是个经验活。理论懂再多,不实际跑几遍,不踩几个坑,很难真正掌握。我的建议是,先按这篇文章的框架搭起调优流程,然后在自己的项目里反复实践。每次调参都记录、都复盘,慢慢就能形成自己的参数直觉。到那时候,看到一个新场景,你大概就能猜出参数该往哪个方向调。