Agent 小知识|Agent 环境工程:部分可观测性、状态转移与执行隔离

上一期我们聊了 Agent 的三种编排方式:ReAct、Plan-and-Execute 与 Workflow Graph。编排解决的是任务接下来往哪里走,工具负责把模型提出的行动交给真实系统执行。

到这里,我们要思考一个问题:这些行动究竟发生在哪里?

比如,一个 Coding Agent 要修改文件,得有代码仓库和文件系统;运行测试,需要有依赖、终端、进程和数据库;Browser Agent 点击按钮要面对一个正在运行的网页;Computer Use Agent 操作软件,还要面对窗口、鼠标、键盘和操作系统状态。

这些让 Agent 能行动,并随着行动持续发生变化的外部世界,就是 Agent 的环境。

懒人图解版

1

环境的边界

之前的 Agent 小知识提到过一个简化公式:

Agent = LLM + Context + Tool

LLM 负责判断和决策,Context 提供当前决策所需的信息,Tool 提供与外界交互的接口。但 Agent 要真正完成任务,还需要这些接口连接着的外部对象。

对于 Coding Agent,这个外部世界有:

代码仓库
文件系统
Git 分支
依赖环境
Shell 会话
运行中的进程
数据库
网络服务

对于 Browser Agent,环境就变成了网页、DOM、登录状态、Cookie、当前页面和浏览器进程;对于机器人,它们面对的则是传感器、机械结构和物理空间。

所以,Agent 环境的范围比代码沙箱更大。浏览器、远程桌面、软件工程工作区,甚至真实物理空间,都可以构成 Agent 面对的任务环境;虚拟机、容器和沙箱则可以用来承载和隔离这些执行环境。

从 Agent 的角度来看,环境中保存着文件、进程、网页、数据库等对象的当前状态。Agent 每执行一次操作,都可能让这些状态发生变化。

┌──────────── Agent ────────────┐
│ LLM                           │
│ Context                       │
│ Tool / Harness                │
└─────────────┬─────────────────┘
              │ 观察 / 行动
              ↓
┌───────── Environment ─────────┐
| 文件 / Git / 进程 / 数据库      |
| 网页 / 浏览器 / API / 用户      |
└───────────────────────────────┘

两种状态

谈到环境,这里先区分两个容易混淆的概念:任务状态和环境状态。

上一期讲 Agent 编排时,我们用过这样一组状态:

当前目标:发布到测试环境
当前阶段:运行测试
构建结果:成功
测试结果:失败
最近错误:数据库连接超时

这类记录任务当前推进到哪一步的信息,我们称之为任务状态。编排系统会根据当前的任务状态,判断下一步进入测试、诊断、审批还是部署等环节。

环境状态关注的则是外部世界此刻处于什么情况。例如:

当前分支:feature/login
auth.py:有未提交修改
数据库容器:停止
当前目录:/workspace/app
PORT:8080
dev server:运行中

两种状态会互相影响,环境中的观察结果经常会被写入任务状态,供编排系统继续判断。此外,侧重点有所不同。

任务状态回答的是:事情做到哪一步了?环境状态回答的是:外部世界现在是什么样?

假设任务状态显示“准备重新运行测试”,但测试数据库此时发生了故障。任务仍处于同一个执行阶段,但此时环境状态却发生了变化。如果 Agent 继续依据之前的环境信息行动,就可能在错误前提下反复重试。

因此,长任务除了记录“当前做到了哪一步”,还要持续确认“当前面对的环境处于什么状态”。

观察与行动空间

Agent 面对一个环境,并不代表它能感知其中的全部信息,也不代表它能操作所有的对象。Agent 与环境之间的交互边界,可以从两个方面来描述:观察空间和行动空间。

2

观察空间规定 Agent 能从环境中获取哪些信息。Coding Agent 可以读取文件、查看 Git 状态、获取 Shell 命令输出,也可以拿到测试结果和报错信息。

不同类型的 Agent,观察环境的方式也不同。Browser Agent 可以读取 DOM 或页面截图;Computer Use Agent 可以获取当前屏幕;机器人则可以通过摄像头、距离传感器、力传感器等设备感知外部环境。

行动空间规定 Agent 能对环境执行哪些操作。比如:

read_file      → 读取文件
write_file     → 修改文件
run_shell      → 执行命令
click          → 点击界面
send_email     → 发送消息
deploy         → 修改线上环境

这也进一步说明了工具与环境之间的关系。

工具定义 Agent 与外部世界交互的接口,环境则承载这些接口所连接的对象及其当前状态。

以同一个代码仓库为例,如果 Agent 只有 read_file 和搜索工具,它只能读取和分析代码;加入 write_file 后,就获得了修改能力;再接入 Shell 和测试环境,便可以形成“修改—运行—验证”的闭环;如果继续开放部署接口,它还能进一步影响线上系统。

因此,即使使用同一个模型,观察空间和行动空间不同,Agent 最终具备的能力边界也会不同。

部分可观测性

其实,还有一个问题需要解决,那就是 Agent 每次获得的信息只是环境中的局部信息。

一个代码仓库里可能有数万个文件,但 git status 只能展示哪些文件发生了变化,无法告诉 Agent 文件内部的具体内容。测试日志也是一样,它只能反映本次执行暴露出来的问题,很难覆盖整个系统的完整状态。

浏览器环境更明显。一张截图记录的是当前画面,DOM 提供的是页面结构,两者都只能呈现环境的一部分。面对复杂环境,Agent 每次感知到的其实都是一个局部视图。

因此,一次完整的感知过程可以拆成:

完整环境状态
      ↓
观察接口
      ↓
获得局部观察
      ↓
上下文管理
      ↓
模型本轮看到的信息

这正好可以和前面讲过的上下文预算接起来。

上下文篇提到,只有进入当前上下文的信息,模型才能在这一轮决策中使用。到了环境这一层,还需要再向前看一步:环境中的信息,首先要被 Agent 感知到,之后才有可能进入上下文。

3

所以,信息可能在两个位置丢失:

环境
│
|─ 没有被观察到
│
↓
观察结果
│
|─ 没有被选入上下文
│
↓
模型

这也解释了为什么 Agent 需要不断搜索、读取、截图和查询状态。很多时候,限制下一步判断的并非推理能力,而是关键的环境信息还没有被获取。

例如测试失败后,Agent 可以先查看报错日志;信息不足,再检查数据库进程;仍无法定位,再读取相关配置文件。每一次观察,都会进一步缩小未知范围。

状态转移

Agent 获得环境信息后,会基于当前观察选择下一步行动。行动落到环境中后,环境状态也会随之发生变化。

这一过程可以用一个简单的状态转移来表示:

环境状态 S₀
    ↓
Agent 执行动作 A₀
    ↓
环境状态 S₁
    ↓
重新观察
    ↓
决定下一步动作 A₁

比如:

数据库:停止
    ↓
start_database
    ↓
数据库:运行

或者:

auth.py:旧代码
    ↓
write_file
    ↓
auth.py:新代码

这里还需要区分一件事:工具调用成功,只能说明动作完成,环境是否进入预期状态,还需要进一步验证。

例如,write_file 返回成功,只能说明文件完成写入;新代码能否正常运行,还要通过编译或测试确认。start_server 命令执行结束后,也需要检查进程状态或健康检查结果;浏览器完成点击后,还要确认页面是否进入预期状态。

因此,一次完整的 Agent 行动,还应包含对环境变化的再次观察和验证:

观察当前状态
    ↓
选择行动
    ↓
执行行动
    ↓
重新观察
    ↓
验证状态变化

工具篇关注的是调用本身:工具如何被调用,以及执行后返回什么结果。到了环境这一层,关注点会进一步延伸到:行动发生之后,外部环境是否真的发生了预期变化。

因此,测试、编译、健康检查和页面状态检查这类反馈都很重要。它们能帮助 Agent 判断当前行动是否生效,以及环境是否进入了下一步任务所需要的状态。

变化中的环境

前面的示例还隐含了一个前提:Agent 完成观察后,环境会保持不变,等待下一步行动。

但在真实任务中,环境本身也会持续变化。Agent 读取网页后,页面内容可能刷新;读取代码后,另一个进程可能修改文件;分析服务日志期间,服务可能发生故障;模型还在判断下一步时,后台构建任务也可能完成。

这就带来了另一个问题:Agent 获取到的环境观察会过期。

10:00:00  Agent 读取环境
10:00:05  环境发生变化
10:00:10  Agent 根据旧观察行动

模型这一轮的判断本身可能没有问题,但它依赖的信息已经落后于当前环境。

这也是环境工程和普通文本问答之间一个很重要的区别。文档内容在模型阅读期间不会自行变化,而运行中的软件系统会持续产生新的状态。

因此,在执行影响较大的操作前后,Agent 都需要重新确认关键状态是否仍然有效。修改文件前,可以检查文件有没有被其他进程改动;操作网页前,可以确认目标元素是否还存在;部署前,也要重新核对目标版本、测试结果和审批状态。

Agent 面对真实环境时,除了判断“下一步该做什么”,还要持续确认另一个问题:我现在看到的,还是当前真实的环境吗?

我刚才看到的世界,现在还是那个世界吗?

环境生命周期

环境和长任务还有一个紧密相关的工程问题:哪些状态需要保留下来。

Coding Agent 的执行环境中,会存在很多不会显式写进任务状态的运行信息,例如当前工作目录、环境变量、安装的依赖、正在运行的开发服务器,以及工作区中尚未完成的文件修改。

假设 Agent 连续执行:

cd /workspace/app
export NODE_ENV=test
npm run dev

如果每次 Shell 调用都启动一个新的执行环境,上一轮设置的工作目录、环境变量和后台进程就可能丢失。这样一来,Agent 虽然记得之前做过什么,下一步面对的执行现场却已经发生了变化。

因此,长任务中的环境设计需要同时处理两类需求。

4

一类是连续性。工作区、文件修改和必要的运行状态要能够跨步骤保留,让 Agent 在同一个执行现场继续工作。

另一类是隔离性。Agent 能执行 Shell、安装依赖、修改文件甚至访问网络时,这些行为不能无限扩散到用户设备和其他任务。虚拟机、容器和沙箱的价值,就在于给 Agent 划出一个受控的行动范围。

环境保存得越完整,任务恢复越方便,但旧进程、缓存、临时文件和错误配置也可能继续留在现场。环境清理得越彻底,执行起点更干净,但重建依赖和运行状态的成本也会增加。

因此,环境生命周期本身也是 Agent 系统的一项工程设计:哪些状态持久保存,哪些按任务重新建立,哪些环境在任务结束后销毁,都需要根据任务连续性、成本和安全边界来决定。

结语

回头再看 Agent 系统中的几个概念,它们之间的边界会更加清楚。

上下文决定模型这一轮能够看到哪些信息;工具定义 Agent 与外部环境交互的接口;编排决定任务按照什么路径推进;环境则承载文件、网页、进程、数据库等外部对象的真实状态,并随着 Agent 的行动持续发生变化。

要完成一个持续推进的任务,Agent 需要不断观察环境、采取行动,再根据新的环境状态判断行动是否生效。环境越复杂,Agent 面对的未知状态、过期观察、隐式依赖和行为风险也会越多。

因此,Agent 环境设计最终要回答三个问题:它能看到什么,它能改变什么,以及行动之后如何确认环境真的发生了预期变化。

posted @ 2026-08-20 14:53  小七-七牛开发者  阅读(0)  评论(0)    收藏  举报