首页
/ 基于 LocalEnvironment 与 EnvironmentToolset 构建本地文件与命令执行 Agent(adk-python 实战指南)

基于 LocalEnvironment 与 EnvironmentToolset 构建本地文件与命令执行 Agent(adk-python 实战指南)

2026-09-12 14:58:50作者:郦嵘贵Just

导读

本文以 adk-python 仓库中的本地环境示例(contributing/samples/environment_and_skills/local_environment/README.md)为线索,深入讲解如何在 Agent 中接入 LocalEnvironmentEnvironmentToolset,使 Agent 具备读写本地文件、执行 shell 命令的能力。读完本文,你将掌握环境工具集的四个核心工具(Execute / ReadFile / EditFile / WriteFile)的调用契约、参数与错误语义、底层执行与安全机制,并能复现"写文件—读文件—执行脚本"的完整闭环示例。

一、示例概览:让 Agent 拥有"手脚"

绝大多数 Agent 只能与文本交互,无法触碰真实文件系统。本示例演示的正是 ADK 中"环境(Environment)"这一抽象:它把命令执行文件 I/O 封装为可供 Agent 调用的工具,从而让模型能够完成"创建文件、修改文件、运行本地脚本或命令"这一类任务。

示例由两个文件组成:

文件 作用
agent.py 定义根 Agent,配置模型与 EnvironmentToolset
README.md 说明示例用途、示例提示词与预期行为

示例 Agent 配置了 EnvironmentToolset,其中携带的 LocalEnvironment 为 Agent 提供了文件 I/O(读、写)命令执行两类工具。用户只需用自然语言下达涉及文件操作与命令执行的指令,Agent 即可自主编排工具完成整条任务链路。

二、核心概念:BaseEnvironment 抽象与 LocalEnvironment 实现

在展开示例前,先理解环境抽象的分层设计。

2.1 抽象基类 BaseEnvironment

BaseEnvironment 是所有代码执行环境的抽象基类,定义了统一的生命周期与能力契约:

  1. 构造__init__):创建环境对象;
  2. 初始化initialize()):首次使用前调用,例如创建工作目录;
  3. 使用execute() 执行 shell 命令、read_file() 读取文件、write_file() 写文件;
  4. 关闭close()):释放资源。

其抽象能力包括:

  • working_dir(只读属性):环境工作目录的绝对路径;
  • execute(command, timeout=None):在工作目录中执行 shell 命令,返回 ExecutionResult
  • read_file(path):读取文件,返回原始字节;
  • write_file(path, content):写入文件,父目录不存在时自动创建。

ExecutionResult 是一个 dataclass,包含四个字段:exit_code(进程退出码)、stdout(标准输出)、stderr(标准错误)、timed_out(是否超时)。

从该基类出发,ADK 还支持沙箱执行、容器环境、云端环境等多种具体实现;本文聚焦于本地子进程实现 LocalEnvironment

2.2 LocalEnvironment:本地 asyncio 子进程执行

LocalEnvironment 通过本地 asyncio 子进程执行命令,其构造参数为:

参数 类型 默认值 说明
working_dir Path | None None 工作区目录的绝对路径;为 None 时在 initialize() 时通过 tempfile.mkdtemp(prefix='adk_workspace_') 自动创建临时目录,并在 close() 时删除
env_vars dict[str, str] | None None 额外注入子进程的环境变量,会合并到当前进程环境的副本之上

需要注意两个使用要点:

  • 必须初始化working_dir 属性与 execute / read_file / write_file 在未调用 initialize() 前调用会抛出 RuntimeError
  • 路径隔离_resolve_path() 会把相对路径拼接进工作目录,并在 resolve() 后校验结果是否仍位于工作目录内,一旦发现 Path escapes working directory 立即抛出 ValueError——这是防止 Agent 越权读写工作区外文件的关键防线。

三、EnvironmentToolset:四个工具的调用契约

EnvironmentToolset 是环境工具的聚合器,构造时接收一个环境实例:

EnvironmentToolset(
    environment=LocalEnvironment(),
    max_output_chars=None,  # 可选:stdout/stderr/文件输出的截断字符数上限
)

当 Agent 请求工具时,它首先确保环境已初始化,然后按序暴露四个工具:

工具名 用途 关键参数
Execute 在工作目录执行 shell 命令 command(必填)
ReadFile 读取文件内容(带行号) path(必填)、start_lineend_line
EditFile 对已有文件做精确文本替换 pathold_stringnew_string(均必填)
WriteFile 创建或整体覆写文件 pathcontent(均必填)

3.1 Execute:命令执行

ExecuteTool 接收 command 字符串,调用环境的 execute() 方法执行,默认超时 30 秒DEFAULT_TIMEOUT,见 _constants.py),默认输出截断上限 30000 字符MAX_OUTPUT_CHARS)。

其返回结构为 JSON 字典:

  • 成功:{"status": "ok", "stdout": ..., "stderr": ...}
  • 退出码非 0:status 变为 "error" 并附带 exit_code
  • 超时:status 变为 "error" 并提示 Command timed out after 30s.
  • 命令为空或执行抛异常:返回 {"status": "error", "error": ...}

工具描述中对模型有明确约束:仅用于运行程序、测试和构建命令,禁止用于读文件(如 catheadtail 应改用 ReadFile)。

3.2 ReadFile:带行号的文件读取

ReadFileToolpath 外还支持两个可选参数:

  • start_line:起始行号(从 1 开始,含),默认 1;
  • end_line:结束行号(含),默认文件末尾。

返回内容带行号前缀(形如 1\t...),便于模型定位;当只读取了文件的一部分时,还会附带 total_lines 字段告知文件总行数。边界情况下返回错误:start_line 超过文件行数、start_line 大于 end_line、文件不存在等。

3.3 EditFile:外科手术式精确替换

EditFileTool 适用于对已有文件的小改动:将 old_string 精确匹配的文本替换为 new_string。其实现细节值得注意:

  • old_string 做正则转义,并将 \r\n 归一化为可匹配 \r?\n 的模式,容忍行尾差异;
  • old_string 必须在文件中恰好出现一次:出现 0 次返回"未找到,请先 ReadFile 校验内容",出现多次则要求提供更多上下文使其唯一;
  • 创建新文件请使用 WriteFile,而非 EditFile

3.4 WriteFile:创建或覆写

WriteFileTool 接收 pathcontent 两个必填参数,用于创建新文件或整体重写已有文件。底层 LocalEnvironment.write_file() 会自动创建缺失的父目录,并支持 str(UTF-8 文本)与 bytes(二进制)两种内容类型。

3.5 环境级系统指令注入

除提供工具外,EnvironmentToolset 还会在每次 LLM 调用时通过 process_llm_request() 注入环境级系统指令(模板见 _constants.py),内容大致为:

Your environment is at {working_dir}/
# Environment Rules
DO:
- 用 && 把有依赖关系的连续命令链在单次 Execute 调用中执行
- 读已有文件始终用 ReadFile,改已有文件用 EditFile
DON'T:
- 用 Execute 执行 cat/head/tail(ReadFile 能完成的事)
- 在同一轮响应中把 EditFile/ReadFile 与 Execute 混用(应先调文件工具,下一轮再 Execute)
- 用多个 Execute 并行执行有依赖关系的命令

这套规则把工作目录位置和工具选择策略直接"喂"给模型,是保证 Agent 行为规范、减少工具误用的关键设计。

四、完整示例:从写文件到执行脚本

示例 Agent 定义于 agent.py

from google.adk import Agent
from google.adk.environment import LocalEnvironment
from google.adk.tools.environment import EnvironmentToolset

root_agent = Agent(
    model="gemini-2.5-pro",
    name="local_environment_agent",
    description="A simple agent that demonstrates local environment usage.",
    instruction="""
    You are a helpful AI assistant that can use the local environment to
    execute commands and file I/O. Follow the rules of the environment and the
    user's instructions.
    """,
    tools=[
        EnvironmentToolset(
            environment=LocalEnvironment(),
        ),
    ],
)

可以看到,接入本地环境能力只需要三步:

  1. 构造 LocalEnvironment()(不传 working_dir 时使用自动创建的临时目录,Agent 会话结束即清理);
  2. 将其传入 EnvironmentToolset(environment=...)
  3. 把 toolset 加入 Agenttools 列表。

4.1 示例提示词

用户只需下达一条自然语言指令:

"Write a Python file named hello.py to the working directory that prints 'Hello from ADK!'. Then read the file to verify its contents, and finally execute it using a command."

("在工作目录写一个名为 hello.py 的 Python 文件,内容打印 'Hello from ADK!'。然后读取该文件校验内容,最后用命令执行它。")

4.2 预期行为

Agent 会自主编排出如下三步工具调用链:

  1. Write File:调用 WriteFile 写入 hello.py,内容为 print("Hello from ADK!")
  2. Read File:调用 ReadFile 读取 hello.py 并校验内容是否正确;
  3. Execute Command:调用 Execute 运行 python3 hello.py,返回其执行输出(即 Hello from ADK!)。

这一链路完整覆盖了"创建文件 → 校验 → 运行"三种典型操作,是 Agent 编程(Agentic Coding)场景中最基础的闭环范式。

五、底层执行机制与安全设计

结合 LocalEnvironment 源码,可以进一步理解命令执行的底层原理:

  • 子进程模型:使用 asyncio.create_subprocess_shell() 以 shell 方式执行命令,并传入 cwd=working_dir、合并后的 env 以及 start_new_session=True——命令拥有独立的进程组/会话,超时清理时不会误伤宿主进程;
  • 超时与清理:超时后先对整个进程组发 SIGTERM,等待 5 秒宽限期(_TERMINATE_GRACE_SECONDS)后升级为 SIGKILL;若仍有脱离进程组的后代进程占用输出管道,则放弃读取其输出并记录警告,避免清理流程自身阻塞;
  • 输出解码:stdout/stderr 以 UTF-8 解码(容错 errors='replace'),随后在工具层按 max_output_chars 截断后返回给 LLM;
  • 文件读写read_file / write_file 通过 asyncio.to_thread 将同步 I/O 放入线程池,避免阻塞事件循环。

从源码结构可以推断,EnvironmentToolsetclose()get_tools() 保证了环境初始化/关闭的幂等性(_environment_initialized 标志),确保 Agent 生命周期内环境只初始化一次、结束时资源被正确回收。

六、如何运行与验证

  • 环境要求:示例使用 gemini-2.5-pro 模型,运行前需完成 ADK 的模型凭据配置;命令执行能力依赖本地 python3 与 shell 环境。
  • 运行方式:可直接以脚本方式运行 agent.py 与 Agent 交互,或通过 ADK 自带的 CLI 工具加载该 Agent(仓库中的 CLI 模块 提供了运行与 Web 调试能力)。
  • 样例校验:仓库的 tests/unittests/test_samples.py 会统一校验 contributing 下样例的合法性与可加载性,可作为示例代码正确性的回归保障。

七、总结与延伸

通过本示例,你可以看到 ADK 将"环境能力"抽象成一条清晰的链路:BaseEnvironment(能力契约)→ LocalEnvironment(本地实现)→ EnvironmentToolset(工具聚合 + 指令注入)→ Agent(编排入口)。四个工具各司其职——Execute 跑命令、ReadFile 读文件、WriteFile 建文件、EditFile 改文件——配合注入的环境规则,让模型能够安全、规范地操作真实文件系统。

若你的场景需要更强隔离或云端执行,可以在 src/google/adk/environment 之外探索 ADK 提供的沙箱、容器与云端环境实现,其接口与本文所述完全一致,替换 LocalEnvironment() 即可无缝迁移。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
936
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.02 K
1.03 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
400
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.07 K
538