Skip to content

Agent 是怎么运转的 ​

前面我们已经知道,Agent 的核心不是像模型一样一次生成结果,而是围绕目标持续:

text
判断
 ↓
行动
 ↓
观察
 ↓
再判断

但真正把一个 Agent 跑起来时,这个循环内部到底发生了什么呢?

我们从一次真实任务的执行过程来看:

一个 Agent 从接到任务开始,到最后完成、失败或者交给人工,中间到底经历了哪些步骤。


从一次 Agent Run 开始 ​

先认识两个概念:

  • Agent Run
  • Agent Loop

Agent Run 可以理解成:

一次完整的 Agent 任务执行。

例如给 Coding Agent 一个任务:

修复项目中的登录接口 500 错误。

从 Agent 接收到这个目标开始,到最后:

  • 修复成功
  • 执行失败
  • 达到停止条件
  • 请求人工介入

这一整段过程,都可以看作一次 Agent Run。

一个 Run 内部通常不会只调用一次大模型。

因为 Agent 需要不断和外部环境交互。

例如:

text
读取日志
   ↓
分析错误
   ↓
搜索代码
   ↓
读取文件
   ↓
修改代码
   ↓
执行测试

测试以后又可能失败。

于是任务不会结束,而是继续:

text
测试失败
   ↓
读取新的错误
   ↓
重新判断
   ↓
继续修改

所以一次 Agent Run 内部,往往会包含很多轮重复执行。

这些不断重复的过程,就是:

Agent Loop。

可以先把两者理解成:

text
Agent Run
│
├─ Loop 1
├─ Loop 2
├─ Loop 3
├─ Loop 4
│
└─ 任务结束

也就是说:

Run 是一次完整任务,Loop 是任务内部不断重复的执行循环。


一轮 Agent Loop 里发生了什么 ​

把一个 Agent Loop 展开以后,可以大致看到下面几个步骤:

text
构建当前上下文
      ↓
模型决定下一步
      ↓
调用工具 / 执行动作
      ↓
获取环境反馈
      ↓
更新当前状态
      ↓
进入下一轮

这几步不断重复,Agent 才能够把一个复杂任务逐步往前推进。


构建当前上下文 ​

Agent 每一次让模型做判断之前,都需要先告诉模型:

我们现在到底处于什么状态?

这就是当前一轮的上下文。

上下文中可能包含:

  • 系统指令
  • 对话历史
  • 用户目标
  • 当前任务状态
  • 已经执行过的步骤
  • 上一轮工具调用结果
  • 当前可用的工具
  • 必要的外部资料

例如用户要求:

修复登录接口 500 错误。

Agent 第一轮可能拥有的上下文非常少:

text
系统指令:
你现在是 Coding Agent,请帮我完成这个任务。

目标:
修复登录接口 500 错误

当前信息:
暂无错误日志

可用工具:
读取文件
搜索代码
执行命令
修改文件
运行测试

模型看到这些信息以后,可能判断:

现在还不知道错误原因,应该先查看日志。

于是进入下一步。

工具执行以后,Agent 获得:

text
NullPointerException
at LoginService.java:82

第二轮的上下文就发生变化了:

text
目标:
修复登录接口 500 错误

已知信息:
LoginService.java 第 82 行发生 NullPointerException

已经执行:
读取错误日志

下一步需要:
继续定位异常原因

所以 Agent 并不是每一轮都从头开始。

它真正做的是:

基于当前已经掌握的信息继续判断。

上下文越准确,模型越容易做出正确的下一步决策。

注:在实际系统中,这些上下文信息通常以结构化的消息历史(Message History / Tool Call & Tool Result)或工作记忆的形式追加并传入模型。


模型决定下一步动作 ​

上下文准备完成以后,模型开始判断:

现在最应该做什么?

它可能决定:

  • 直接生成最终回答
  • 搜索资料
  • 读取文件
  • 查询数据库
  • 执行代码
  • 调用浏览器
  • 请求用户补充信息
  • 调用某个业务 API
  • 结束当前任务

例如看到:

text
NullPointerException
at LoginService.java:82

模型可能选择:

text
读取 LoginService.java

再比如 Research Agent 已经搜索了一轮资料,但发现信息不足。

模型可能判断:

text
继续搜索官方文档

所以模型在 Agent 中承担的一个重要职责就是:

根据当前状态选择下一步动作。

需要注意的是,模型通常只是产生:

“我要调用哪个工具,以及传什么参数。”

真正执行工具的并不是模型本身。


调用工具并执行动作 ​

如果模型决定调用工具,Agent 系统就会真正执行这个动作。

例如模型生成:

text
read_file("LoginService.java")

真正负责读取文件的,是 Agent 外部的工具执行环境。

可以简单理解成:

text
模型
↓
决定调用 read_file
↓
Agent Runtime / Harness
↓
真正读取文件
↓
返回结果

所以更准确地说:

模型负责决定做什么,工具系统负责真正去做。

不同 Agent 拥有的工具完全不同。

Coding Agent 可能有:

text
read_file
search_code
write_file
run_command
run_test

Research Agent 可能有:

text
search_web
open_page
read_document

Data Agent 可能有:

text
query_database
execute_sql
run_python

Browser Agent 则可能有:

text
click
type
scroll
open_url
download

工具决定了 Agent 能和哪些外部环境发生交互。


获取环境反馈 ​

工具执行以后,会返回结果。

这个结果可以叫:

  • Observation
  • Tool Result
  • Feedback

名称虽然不同,但表达的是同一件事:

Agent 刚才做了一个动作,外部环境给出了什么结果?

例如:

text
read_file("LoginService.java")

可能返回:

text
public User login(String username) {
    User user = userRepository.findByUsername(username);
    return user.getProfile();
}

或者:

text
run_test()

返回:

text
FAILED

NullPointerException:
userRepository.findByUsername(username) returned null

Browser Agent 点击按钮以后,也可能获得:

text
页面跳转成功

或者:

text
表单验证失败:Email is required

这些反馈非常重要。

因为 Agent 不是只依赖自己刚才的推理继续往下走。

它需要不断从真实环境中拿到新的信息。

所以整个过程实际上是:

text
模型认为应该做 A
      ↓
真正执行 A
      ↓
环境返回结果
      ↓
再根据真实结果继续判断

这就是 Agent 能够逐渐逼近真实任务结果的基础。


更新状态并进入下一轮 ​

工具结果返回以后,Agent 不能只是“看一下”。

还要把新的信息加入当前任务状态。

例如:

text
目标:
修复登录接口 500 错误

已读取:
LoginService.java

当前发现:
findByUsername 可能返回 null

测试结果:
失败

任务状态:
尚未完成

这些新状态会参与下一轮上下文构建。

于是:

text
新的状态
   ↓
新的上下文
   ↓
再次调用模型
   ↓
模型重新判断

模型可能继续判断:

需要给用户不存在的情况增加空值处理。

然后:

text
修改代码
 ↓
运行测试
 ↓
测试结果
 ↓
更新状态
 ↓
再次进入模型

这样一轮一轮执行下去。

所以一个完整的 Agent Loop 可以进一步展开成:

text
当前状态
   ↓
构建上下文
   ↓
LLM
   ↓
选择动作
   ↓
工具执行
   ↓
Observation
   ↓
更新状态
   ↓
进入下一轮

前一步的结果,就是下一步判断的重要输入。


为什么 Agent 需要循环执行 ​

既然模型已经可以推理,为什么不能一开始就让它生成完整计划,然后从头执行到尾?

原因在于:

真实任务的很多信息,在任务开始时根本不存在。

例如 Coding Agent 不可能在运行测试之前,就准确知道测试会返回什么。

Research Agent 也不可能在搜索之前,就知道:

  • 会找到哪些页面
  • 信息是否完整
  • 不同来源有没有冲突

Browser Agent 也无法提前知道:

  • 页面有没有弹窗
  • 是否需要登录
  • 表单有没有验证失败
  • 点击以后会跳到哪里

真实任务更像:

text
做一步
 ↓
看看发生了什么
 ↓
再决定下一步

而不是:

text
第一次就预测所有情况
 ↓
一次性执行完整计划

例如:

text
修改代码
 ↓
运行测试
 ↓
失败

测试失败这一信息,在真正执行测试之前并不知道。

有了新的错误以后,Agent 才能继续:

text
读取错误
 ↓
重新判断
 ↓
再次修改

所以 Agent 天然就适合循环执行。

可以把它理解成:

text
Think
 ↓
Act
 ↓
Observe
 ↓
Think Again

或者我们前面一直使用的:

text
判断
 ↓
行动
 ↓
观察
 ↓
再判断

Agent 的关键,并不是第一次就把所有任务都想对。

而是:

能够在新的反馈出现以后继续修正。


Agent 怎么知道什么时候结束 ​

既然 Agent 会循环运行,就一定还需要解决一个问题:

什么时候应该停止?

如果没有停止条件,Agent 很容易变成:

text
判断
 ↓
行动
 ↓
失败
 ↓
继续判断
 ↓
继续行动
 ↓
……

最后无限循环。

所以一次 Agent Run 通常需要明确的退出条件。


任务已经完成 ​

最正常的结束方式,就是目标已经完成。

例如 Coding Agent:

text
修改代码
 ↓
运行测试
 ↓
测试全部通过
 ↓
任务完成

Research Agent:

text
已经找到足够资料
 ↓
关键事实完成验证
 ↓
生成报告
 ↓
任务完成

Browser Agent:

text
填写表单
 ↓
提交
 ↓
页面显示成功
 ↓
任务完成

所以 Agent 在每一轮结束以后,都需要判断:

当前结果是否已经满足用户目标?

如果已经满足,就可以退出 Agent Loop,并返回最终结果。


达到停止条件 ​

任务不一定每次都能正常完成。

所以系统通常还会设计一些硬性停止条件。

例如:

  • 最大执行步数
  • 最大重试次数
  • 最大运行时间
  • Token 预算
  • API 成本限制
  • 连续工具失败次数

例如:

text
最大 Agent Loop:30 次

执行到第 30 轮仍然没有完成任务:

text
继续执行?
↓
否
↓
停止 Agent Run

或者工具连续失败:

text
调用 API
 ↓
失败

重试
 ↓
失败

再次重试
 ↓
失败

达到最大重试次数
 ↓
停止

这些限制并不是为了让 Agent “变笨”。

而是为了防止:

  • 无限循环
  • 无限消耗 Token
  • 不断重复错误动作
  • 长时间占用计算资源

所以一个可运行的 Agent 系统必须知道:

什么时候不能再继续。


需要人工介入 ​

还有一种很常见的情况:

Agent 不是结束任务,而是把控制权暂时交给人。

例如 Agent 准备执行:

text
删除文件
发送正式邮件
提交退款
部署生产环境
修改数据库

这些动作可能风险比较高。

系统可以设计成:

text
Agent 准备执行操作
        ↓
    风险检查
        ↓
    需要人工确认
        ↓
      暂停 Run
        ↓
   用户允许 / 拒绝

如果允许:

text
继续执行

如果拒绝:

text
调整路径 / 停止任务

还有一种情况是 Agent 自己无法继续。

例如:

text
连续搜索多次
↓
仍然无法确认关键信息
↓
请求用户补充资料

或者:

text
没有权限访问数据库
↓
请求人工处理

所以 Agent 并不一定追求:

完全无人参与。

更常见的是:

可以自动推进的大部分步骤自动推进,需要确认的地方再交给人工。


执行失败以后会发生什么 ​

Agent 执行真实任务时,失败非常正常。

例如:

  • API 超时
  • 搜索没有结果
  • 文件不存在
  • SQL 执行错误
  • 浏览器页面变化
  • 模型判断方向错误

关键不在于:

Agent 会不会失败。

而在于:

失败以后系统怎么继续。


重试当前动作 ​

如果失败更像暂时性的异常,可以直接重试。

例如:

text
调用 API
 ↓
Timeout
 ↓
等待
 ↓
再次调用

或者:

text
网页加载失败
 ↓
刷新页面
 ↓
重新读取

这种情况下,原来的决策本身可能没有问题。

只是工具执行暂时失败。

所以最简单的处理方式就是:

重试当前动作。

当然,重试通常也要有次数限制。

否则:

text
失败
↓
重试
↓
失败
↓
重试
↓
……

仍然可能陷入循环。


调整路径继续执行 ​

还有一些失败说明:

原来的方案不对。

这时继续重复同一个动作没有意义。

Agent 需要根据失败结果修改路径。

例如 Research Agent:

text
搜索关键词 A
 ↓
没有有效结果
 ↓
重新组织关键词
 ↓
搜索关键词 B

Coding Agent:

text
修改方案 A
 ↓
测试失败
 ↓
分析新的错误
 ↓
采用方案 B

Data Agent:

text
按渠道查询
 ↓
没有发现异常
 ↓
换成按地区查询

Browser Agent:

text
点击按钮
 ↓
页面没有变化
 ↓
重新定位按钮
 ↓
换一种操作方式

这种能力就是 Agent 和普通固定脚本之间非常明显的区别之一。

系统可以利用失败反馈重新做决策,而不是一直走原来的路径。


停止并返回控制权 ​

如果多次重试和调整以后仍然无法完成任务,就不应该无限尝试。

例如:

text
信息始终不足

或者:

text
没有执行权限

或者:

text
工具连续失败

或者:

text
已经达到最大步数

这时更合理的做法是:

text
停止当前 Agent Run
       ↓
返回当前进度
       ↓
说明失败原因
       ↓
告诉用户需要什么

例如:

已经检查登录模块和数据库连接,目前无法继续确认问题,因为缺少生产环境日志。

这比 Agent 自己不断尝试更加可靠。

所以失败处理的最终原则可以概括成:

text
能重试 → 重试

需要换方案 → 调整路径

无法继续 → 停止并交还控制权

一个完整的 Agent 执行过程 ​

把前面的内容放到一起,我们再完整看一次 Agent 是怎么运行的。

假设用户提出:

修复项目里的登录接口 500 错误。


第一步,创建一次 Agent Run。

text
用户目标:
修复登录接口 500 错误

系统准备当前上下文:

text
目标
+
项目基本信息
+
可用工具
+
当前任务状态

模型开始第一次判断:

先读取错误日志。

于是模型请求:

text
read_log()

工具真正执行以后返回:

text
NullPointerException
at LoginService.java:82

系统把这个 Observation 写回任务状态:

text
已知:
LoginService.java 第 82 行出现空指针异常

进入下一轮。

模型看到新的上下文以后判断:

应该读取 LoginService.java。

于是调用:

text
read_file("LoginService.java")

工具返回代码。

模型分析以后发现:

text
userRepository.findByUsername(username)

可能返回 null。

于是下一轮决定修改代码。

text
write_file(...)

修改完成以后,模型决定运行测试。

text
run_test()

结果:

text
FAILED

UserRepository mock is null

这时候任务还没有完成。

系统把新的失败信息写回状态:

text
修改已经完成
但测试仍然失败
新的问题:UserRepository mock is null

再次进入下一轮。

模型重新判断:

测试环境中的 Mock 配置可能有问题。

于是继续搜索测试代码。

text
search_code("UserRepository")

找到相关测试以后继续修改。

再次执行:

text
run_test()

这次返回:

text
PASSED

Agent 判断:

text
登录相关测试通过
↓
目标已经满足

于是结束 Agent Run,并生成最终结果:

已修复登录接口空指针问题,同时补充了测试中的 UserRepository Mock 配置,相关测试已经通过。

整个执行过程可以压缩成:

text
用户目标
   ↓
创建 Agent Run
   ↓
构建上下文
   ↓
模型判断
   ↓
调用工具
   ↓
Observation
   ↓
更新状态
   ↓
重新构建上下文
   ↓
模型再次判断
   ↓
继续行动
   ↓
……
   ↓
满足停止条件
   ↓
结束 Run

再抽象一层,就是:

text
Run
 ↓
Context
 ↓
Decision
 ↓
Action
 ↓
Observation
 ↓
State Update
 ↓
Loop
 ↓
Exit

这基本就是一个 Agent 从接到任务到最终结束的完整运行过程。


总结 ​

理解 Agent 怎么运转,可以先区分两个概念:

text
Agent Run
→ 一次完整任务执行

Agent Loop
→ Run 内部不断重复的执行循环

一轮最基本的 Agent Loop 可以表示成:

text
构建上下文
   ↓
模型判断
   ↓
执行动作
   ↓
获取反馈
   ↓
更新状态
   ↓
再次循环

其中最重要的一点是:

Agent 会把真实环境中的执行结果重新带回下一轮决策。

所以它不是:

text
想一次
↓
执行到底

而更像:

text
判断
 ↓
行动
 ↓
观察
 ↓
修正
 ↓
继续行动

一次 Agent Run 最终通常会以几种方式结束:

  • 任务正常完成
  • 达到停止条件
  • 执行失败后无法继续
  • 需要人工确认或接管

而当中间步骤失败时,Agent 可以:

text
重试
调整路径
停止并返回控制权

所以真正理解 Agent 的运行原理以后,会发现 Agent 的关键并不是“模型一次能够想多远”。

而是:

系统能不能在行动、反馈和下一次决策之间形成一个稳定、可控、能够收敛的执行闭环。