Agent 慢,不是模型慢,是问得太多。这句话是我在一次实际排查后得出的结论。当时业务方反复反馈,一个基于 DeepSeek 搭建的 Agent 汇报任务太慢,点一下按钮,要等两三分钟才能看到最终结果。第一反应是模型拖后腿,可我把单次模型调用的耗时拉出来看,模型本身平均只需要一两秒,并不慢。真正的问题,藏在 Agent 与模型之间的几十次往返里。
这里要先把概念划清楚。用户感知到的“慢”,是端到端耗时。而我们习惯说的“模型慢”,通常指单次模型响应时间。两者之间隔着工具调度、消息传递、上下文组装、代码执行、外部接口等待,也隔着 Agent 内部一个接一个的模型提问。一个看似简单的任务,如果被拆成二十个子步骤,每一步都让模型确认一次,即便模型每次只花 1 秒,累计也要 20 秒。如果再算上每轮输入历史越来越长,后面那几次调用每轮花 3 秒、5 秒,也完全可能。
所以当你的 Agent 变慢时,不要急着换模型、加并发或升级机器。先做一个动作:记录一次完整任务中,向大模型发起了多少次请求,每次请求带了多少 token,花了多长时间。多数情况下你会发现,瓶颈不在模型“答”,而在 Agent“问”。这也是我想借这篇文章讲清楚的:优化 Agent 性能,很多时候要优化的是提问节奏,而不是模型能力。
1. 先别急着换模型,先数一数一次任务到底“问”了多少次
1.1 模型的“单次响应速度”和 Agent 的“端到端耗时”是两码事
很多团队做技术调研,第一件事就是打开模型排行榜,比较不同模型的推理速度。这个方向没有错,但在 Agent 场景下,单次响应速度往往不是决定用户体感的关键因素。
模型 API 返回一个回答,通常只需要一两秒。可一个 Agent 任务从入口到最终输出,中间可能发生过十几次、几十次内部模型调用。每一次调用都有网络开销、输入 token 处理开销、输出 token 生成开销,以及等待上一次工具结果返回的时间。这些时间累加在一起,才是用户感受到的“慢”。
以我在 DeepSeek 上的实际体验来说,模型单条请求非常敏捷,但用同一个模型搭出来的 Agent,如果编排写得啰嗦,照样能拖到让人失去耐心。原因不是模型不行,而是应用层把一次任务拆成了太多轮对话。你可以在自己的项目里做个简单测试:跑一个完整任务,把每次模型调用的耗时全部打印出来。很可能模型层累计耗时只有总耗时的三四成,剩下六七成都耗在了编排和等待上。
1.2 提问次数越多,后面每轮都越慢
“问得太多”的坏处不只是次数上的累加,还有一个容易被忽略的滚雪球效应。
大多数 Agent 为了避免模型“失忆”,会把前面所有对话消息和工具调用结果一并传给模型。于是从第一轮的两三千 token,到第五轮可能变成一两万 token,到第十轮可能更多。而大模型处理输入 token 是需要时间的,在实际体验里就表现为:越到后面的提问,单次响应越慢。
你可以回想平时使用大模型网页版的经验:同一个对话里聊到后面,感觉加载变慢,记忆变卡,其实就是上下文变长导致的。Agent 也一样,它不断往上下文里塞东西,每一轮提问都在背着越来越重的背包跑步。所以,优化次数不仅是为了减少网络往返,也是在减少模型每次需要处理的上下文重量。
1.3 先用“调用次数”建立一个基准感
那么,多少次算“问得太多”?
这个数字取决于任务复杂度,没有统一标准,但我们可以建立一个区间感。对一个普通的信息整理或内容生成任务,我认为健康的目标是控制在 3 到 5 次模型调用:一次理解用户意图,一次制定计划或决定工具调用,一次生成最终结果。如果需要多工具协作,可以允许到 8 次左右。一旦超过 10 次,就要开始怀疑编排策略是不是有问题。
我在真实项目里的做法是,给 Agent 包一层简单的计数逻辑,每次调用模型就累加一次,任务结束时把总次数和总 token 数写进日志。先跑 5 条真实用例,如果平均调用次数落在健康区间内,说明编排方式基本合格;如果动辄二三十次,那优化方向应该立刻转向减少提问,而不是继续去测模型速度。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么 Agent 会“问”得那么多?四个常见原因
先观察问题,再谈优化。如果不知道为什么 Agent 会不停发问,改来改去很容易把原有逻辑改坏。我归纳了四个常见原因,几乎每个“慢 Agent”都能在其中找到对应。
2.1 大任务被拆得过碎,每一步都要模型确认
很多 Agent 框架鼓励“计划-执行”模式,让模型先制定详细步骤,再一步一步执行。这个方向是对的,但如果拆分粒度太细,就会变成灾难。
举例来说,一个写周报的 Agent,本来只需要一次模型调用生成全文,但有的实现会把任务拆成“先判断读者是谁”“再想标题”“再写第一部分”“再写第二部分”“再整体润色”……每一小步都向模型发一次请求。看起来每一步都很合理,但总耗时被切割成了十几段,而且每段都要重新加载前面的历史。
根源在于开发者在设计流程时没有想清楚:哪些决策必须由模型做,哪些只是顺序执行。把“顺序执行”也交给模型确认,本质上是用高价模型做普通代码也能完成的事。
2.2 工具调用结果没有合并,反复送回模型
多工具 Agent 里常见一种低效写法:每调用完一个工具,就把完整结果作为单独一轮消息发给模型,让模型“继续”。如果一次任务需要调用 5 个工具,模型就要多读 5 轮工具结果。很多工具结果还很冗长,比如数据库里拉出来的 100 行记录,网页抓回来的正文,全部原封不动塞进上下文。
问题在于,Agent 的目标通常不需要这些原始数据的每个细节。它只需要从中提取几个关键值作为决策依据,或者直接汇总成结果。但实现时偷懒,把原始输出全部还给模型,模型自然要花更多时间读,也就更慢。
2.3 上下文滚雪球式膨胀,重复读历史
前面说过滚雪球效应,这里再展开一点。很多 Agent 实现里,为了让模型每一步都有“完整记忆”,会选择把整个会话历史全量传入。但这个“全量”里包含大量已经用过的工具结果、中间推理、无关对话。真正对下一步有影响的信息,可能只占 20%。可模型没有能力只读这 20%,它必须从头处理全部 token。
这也是为什么优化 Agent 时,一个常见动作是“上下文压缩”。把已经完成的小节替换成一句摘要,把工具输出只保留结构化摘要,把历史消息裁剪到必要范围。目的不是让模型更聪明,而是每次提问时少读一点。这个优化往往立竿见影。
2.4 稳定决策被交给模型,而不是代码
Agent 中有很多决策其实是稳定的、有明确规则的。比如“如果搜索接口返回的错误码是网络超时,就重试一次”“如果温度参数大于 0.8 则属于高创造力场景”。这些规则用代码写起来非常容易,且几乎不耗时。
但有些 Agent 设计会把它们也丢给模型判断,每次判断都是一次模型调用。一个任务里有五六个类似分支,就会多出五六次无意义的模型请求。这背后是开发者对 LLM 能力的过度期待,觉得自己在“让 Agent 更智能”,实际上只是让流程更慢、成本更高、结果更不稳定。
注意:不要把“交给模型判断”当成默认选项。只有规则无法覆盖、确实需要语义理解的地方,才值得占用一次模型调用。
3. 从“少问”入手的三级优化策略
了解了原因之后,优化就有方向了。我通常按照从易到难、从快见效到长期稳定的顺序做三级处理,每一级都有自己的适用条件和代价。
3.1 第一级:合并问题,把多个结论一次问完
最直接的做法是减少模型调用次数,一次问多个问题,而不是每个问题问一次。
比如在内容生成场景里,过去要先生成标题,再生成正文,再生成摘要。优化后可以用一次模型调用,让它输出一个结构化 JSON,里面同时包含标题、正文、摘要、关键词。代码拿到 JSON 后,再按需拆分使用。这样原来三次调用变成一次,而且输出结构更可控。
合并的关键是选好“问题聚合面”。如果多个问题之间有强依赖,必须等前一个结果出来才能问下一个,那就不能硬合并。但如果多个问题相互独立,或者可以同时判断,就应该一次性丢给模型。具体到工程实现,就是设计好 prompt,让模型一次给出多条结论。很多模型都支持 JSON 输出模式,这正好用来实现“一次问完、分字段取用”。
3.2 第二级:给 Agent 对应“地图”,减少探索式提问
合并解决的是“重复问”,第二级解决的是“盲目问”。很多 Agent 慢在它不知道下一步去哪,每一步都要问模型“我该怎么办”。这是因为 Agent 缺少一个稳定的任务地图。
我建议在编排层显式写出流程的骨架,而不是让模型从空白开始自由规划。比如一个客服工单 Agent,可以先把流程定义成:识别问题类型 -> 检索知识库 -> 生成回复 -> 风险检查。模型只需要在每个节点负责具体的内容输出,不需要每步都从“我现在该做什么”开始思考。
这样会大幅减少无意义的探索式提问。模型从“决策下一个动作”变成“完成当前动作”,提问次数自然下降。这个方式也会让调试变得更容易,因为流程结果是可预测的,而不是每次都可能跑出不同分支。
3.3 第三级:把稳定决策沉淀成规则,不再经过模型
第三级最有长期价值,也最容易被忽略。凡是一个决策在业务上已经有明确判断标准,就应该把它从 LLM 那里拿回来,交给代码。
比如判断一份文档是不是包含敏感词,可以先用正则或规则引擎筛一遍;判断请求是否超时,可以在代码层直接识别并重试;判断某个字段为空是否影响流程,就用 if 语句处理。只有当规则判断不了、需要语义理解时,才去请求模型。
这一级优化做得好,Agent 会变得非常“省话”。它只在真正需要自然语言推理的地方调用模型,其余环节全部用工程手段完成。长期看,这不只是提升速度,也是降低成本和提升稳定性。如果你希望 Agent 能长时间运行在业务环境里,这一步是必须做的。
4. 怎么确认是“问得太多”而不是“模型慢”:一条可落地的诊断链路
在开始优化前,你需要一套清晰的方法来定位问题。否则很容易出现“改了一堆东西,速度没变化”的情况。这里给一条诊断链路,每一步都不复杂,但能帮你快速排除干扰项。
4.1 先看端到端耗时和模型耗时的差
打开一次任务日志,先计算两件事:整个任务从开始到结束一共花了多长时间;其中所有模型 API 调用的耗时加起来是多少。
如果模型耗时之和接近端到端耗时的大头,那说明问题确实在模型调用本身,继续去优化 prompt 或模型选型。如果模型耗时之和只占 30%,剩下 70% 花在别的地方,比如工具等待、消息队列、代码阻塞,那模型根本不是瓶颈,重点要查编排层。很多“Agent 慢”的问题,在完成这一步之后就发现了。
4.2 数请求次数、工具调用次数和 token 增长曲线
接着,统计一次任务里发生了多少次模型请求,多少次工具调用,以及上下文 token 数随轮次的变化。
我建议把每个请求记录成一行日志,包括请求序号、输入 token 数、输出 token 数、耗时、调用场景。不需要用什么复杂框架,普通 log 就能满足。然后观察:请求次数是否远大于必要的决策点数量?token 数是否每轮都在显著增长?有没有连续好几轮在做类似“确认”和“补充”的无谓操作?
这些数据会直接暴露问得太多。举例来说,我排查过的一个任务一共发起了 27 次模型请求,其中有 9 次是在做“格式修整”,还有 6 次是在重复读取前几轮的搜索结果。这显然是编排层的问题,而不是模型层的问题。
4.3 给模型调用包一层 Harness,记录每次“提问”
这里说一下标题里的 Harness。很多 Agent 框架已经内置了 trace 功能,但如果你用的是自定义流程,我建议自己写一个非常轻量的 Harness,把所有调大模型的动作统一封装起来。
python复制import time
from dataclasses import dataclass, field
@dataclass
class CallRecord:
step: str
input_tokens: int
output_tokens: int
elapsed_ms: float
timestamp: str = field(default_factory=lambda: time.strftime("%Y-%m-%d %H:%M:%S"))
class ModelHarness:
def __init__(self, call_model_fn):
self._call_model_fn = call_model_fn
self.records = []
self._step = 0
def ask(self, prompt, **kwargs):
self._step += 1
start = time.time()
response = self._call_model_fn(prompt, **kwargs)
elapsed_ms = (time.time() - start) * 1000
record = CallRecord(
step=f"step-{self._step}",
input_tokens=estimate_tokens(prompt),
output_tokens=estimate_tokens(response),
elapsed_ms=round(elapsed_ms, 2),
)
self.records.append(record)
return response
这段代码只是一个示例,关键是让你能统一看到“谁在什么时候问了模型什么”。你不需要做得多么严谨,只要把调用入口收拢到一个函数里,所有模型请求都从那里过,就有了观测基础。如果项目是用 DeepSeek 或者 OpenAI SDK 这类服务,也建议在封装层统一加日志。这样一次任务的完整问答过程就变成了可回放的记录。
4.4 用对照实验验证优化效果
完成第一轮优化后,不要凭感觉说“好像快了一点”,用同一套 Harness 跑同样的用例,看三组数据:请求次数有没有下降,总输入 token 数有没有下降,端到端耗时有没有下降。
理想情况下,这组数据应该同步下降。如果请求次数变少了,但耗时没有下降,说明新的瓶颈可能在别的地方,比如某个工具调用依赖太慢。这时再回到第 4.1 步,继续分层定位。优化是一个迭代过程,但只有基于数据才能保证每一次改动都是有意义的。
为了让诊断更清楚,可以做一张提问类型统计表:
| 提问类型 | 典型表现 | 优化动作 |
|---|---|---|
| 探索型 | “我接下来该怎么做” | 预定义流程骨架 |
| 确认型 | “这个结果可以吗” | 用规则判断或合并 |
| 生成型 | “请输出某段内容” | 按需保留,尽量合并 |
| 整理型 | “把刚才内容总结一下” | 抽取摘要,避免全量历史 |
这个表格不是标准答案,但它能帮你判断哪些提问是必要的、哪些只是流程惯性。
5. “少问”优化也有边界,别把必要提问一并砍掉
看到这里,你可能会想:那就把所有任务都压缩成三次以内?不行。压缩提问次数要有一个度,过度减会影响任务质量。做性能优化之前,先区分哪些提问是“浪费”,哪些是“必要的思维停顿”。
5.1 复杂规划任务中,有些停顿是必要的
多步推理任务往往需要模型在步骤之间做中间决策。比如一个产品上市方案,涉及市场分析、渠道策略、时间表。如果你要求模型一次性生成全部内容,它可能顾此失彼,结构混乱。这时分几步让模型逐步规划,反而能得到更好结果。
所以,“少问”不等于“只问一次”,而是让每一次提问都有明确增量。如果一个提问能让后面的结果更准确、更可控,那它就是有价值的。如果只是机械化地让模型“确认一下”“再看看”,那才是浪费。
5.2 交互式场景中,用户主动要求澄清不能省
还有一种场景,Agent 需要在回答前确认用户意图。比如用户说“帮我找一个数据可视化工具”,但没说清楚是开源还是商用、需要哪些图表类型、部署方式是什么。此时 Agent 多问一句不是坏事,反而能避免后续生成一堆不符合要求的答案。
优化不能以牺牲产品体验为代价。如果用户明确希望 Agent 保持“多轮对话、逐步澄清”的风格,那么每多一次澄清都是在提升最终结果的匹配度。这种“问”是有业务价值的,不该被无差别压缩。
5.3 判断“该砍不该砍”的三个标准
我在每次优化时,会拿三个标准来筛一遍提问:
- 这次提问是否产生了新信息?如果有,保留;如果没有,砍掉。
- 这次提问是否会导致更少的外部调用?比如先问模型一次来决定调用哪个工具,避免把三个工具都调用一遍,那它是划算的。
- 这次提问是否被代码规则替代不了?如果能用 if 逻辑判断,就不该问模型。
当三个标准的答案都是“否”,那这段提问基本就是优化对象。当至少一个答案是“是”,就要谨慎处理,保留或降级,而不是一刀切砍掉。
注意:减少提问应以“不丢失必要状态”为前提。上下文压缩要保留对后续步骤真正有影响的信息;流程合并要保留关键决策点。否则你得到的速度,牺牲的可能是准确性。
6. 真正该沉淀下来的,是一套控制“提问节奏”的方法
最后我想回到开头那句话:Agent 慢,不是模型慢,是问得太多。这句话不只是标题,也是我整理 Agent 性能问题时的第一反应。技术选型可以迭代,模型可以换,但“让 Agent 少提无效问”这个方向,我认为会一直成立。
6.1 少问三步法:合并、给图、沉淀
如果把这次的经验收敛成一个可复用框架,我会叫它“少问三步法”:
- 第一步,合并:让一次模型调用同时处理多个独立问题,用结构化输出拆解结果。
- 第二步,给图:把任务流程写成显式骨架,让模型不再频繁追问“下一步做什么”。
- 第三步,沉淀:把稳定决策从模型手里拿回来,交给代码或规则引擎。
这三步的顺序有讲究。先做代价最低、收益最明显的合并,可以快速见效;再做流程骨架,解决的是探索式提问;最后才做规则沉淀,因为这一步需要业务规则足够清晰,改动成本更高。但长期来看,第三步能带来的收益最大。
6.2 落地时最该先做的两件事
如果你现在正为“Agent 慢”头疼,我建议第一步不是去改代码,而是在模型调用入口加一个最简单的 Harness 日志,统计请求次数和耗时。有了数据,你才知道自己的 Agent 是“问太多”还是“答太慢”。第二步是用一套真实用例跑一遍,把日志里的异常轮数挑出来,对应到具体原因,再按照“合并、给图、沉淀”的顺序逐步优化。
6.3 别让“少问”变成新的套路
最后提醒一个问题:减少提问次数不是目的本身,而是手段。有些团队把“少问”当成 KPI,强制所有任务只能调用三次模型,结果 Agent 为了凑次数,开始把多个强依赖的决策塞进同一个 prompt,输出质量大幅下滑。这是矫枉过正。
正确的心态是,把每一次模型调用都当作一次有成本的决策,而不是免费的算力。该合并的合并,该保留的保留,该下沉成规则的就下沉成规则。定期跑一批测试用例,复查请求次数和 token 消耗,让“少问”成为一种设计习惯,才是长期维持 Agent 性能的关键。
这类问题往往没有一劳永逸的解法。一次优化只能解决当前编排方式下的浪费,但项目在变,工具返回的数据在变,用户需求也在变。模型再快,也架不住流程来回折腾几十趟。把提问次数控下来,既是对模型性能的尊重,也是 Agent 真正进入生产环境前必须迈过的一道坎。
