开发工具 (adk-devtools)

adk-devtools 是编码 agent 所需的内循环工具集——读取、编辑、 搜索和运行——并且每个操作都限定在工作区目录内。它是一个独立、可发布的 crate,只依赖于 adk-core,因此它可以与任何 LlmAgent 组合使用(CodingAgent harness 会为你把它接好)。

这些工具

DevToolset 是一个 Toolset,捆绑了六个工具:

工具参数行为
read_filepath, offset?, limit?返回文件内容,带行号
write_filepath, content创建/覆盖文件(创建父目录)
edit_filepath, old_string, new_string, replace_all?精确字符串替换
globpattern, path?列出与 glob 匹配的文件(例如 src/**/*.rs
greppattern, path?, glob?, case_insensitive?正则内容搜索
bashcommand, timeout_secs?在工作区根目录运行 shell 命令

值得了解的两个安全行为:

  • edit_file 需要在会话中先对该文件进行一次 read_file,并且 默认情况下目标字符串必须恰好出现一次(可用 replace_all 覆盖)。这可防止盲目覆盖。
  • grep 会跳过常见的构建/VCS 目录(target.gitnode_modules,…) 以及二进制/过大的文件。

bash 工具会在命令运行时通过 ToolContext::emit_progress 按行流式输出其 stdout/stderr, 因此 UI 可以显示实时终端。每个 chunk 都会作为代理的 EventStream 上的部分事件到达 (可通过 event.tool_progress_stream() 检测);完整输出仍会作为工具的最终结果返回。另见 streaming_bash 示例来自工具的流式进度

Workspace

Workspace 会把每次操作都限定在某个目录下,并强制执行一小组策略:

use adk_devtools::Workspace;
use std::time::Duration;

let ws = Workspace::new("./my-repo");              // read-write, bash enabled
let ws = Workspace::read_only("./my-repo");        // explore/plan: no writes, no bash
let ws = Workspace::new("./my-repo")
    .allow_bash(false)                              // file edits, but no shell
    .bash_timeout(Duration::from_secs(60))
    .max_output_bytes(512 * 1024);
  • 路径包含性 — 任何解析到根目录之外的路径都会被拒绝,因此 代理无法读取或写入 ../../etc/...。包含性是基于解析后的路径来强制执行的,而不只是字面路径: 指向根目录外部的符号链接即使在字面上位于根目录内也会被拒绝。该规则同时覆盖 作为最终组件的符号链接和作为父目录的符号链接,因此通过重定向目录进行创建也会被拒绝。 如果符号链接的目标仍位于工作区内,则会继续工作,因为仓库中合法地会包含内部链接。

    该检查不是锁。若在检查与随后 open 之间植入一个符号链接,系统仍会跟随它; 要关闭这个窗口,需要使用相对文件描述符的遍历,并结合平台的 no-follow 语义。 应把这些文件工具视为对游走中的代理的包含性约束,而不是对能够并发写入工作区的对手的隔离。

  • 只读模式Workspace::read_only(..) 会完全隐藏会修改内容的工具 (模型只会看到 read_file/glob/grep)。

  • bash 环境已清空 — 命令只会收到 PATHHOMELANGLC_ALLTMPDIRTERMUSERSHELL,因此代理进程持有的提供方 API 密钥 不能通过 env 读取。Workspace::inherit_env(true) 会恢复旧的全量传递行为,而 env_allowlist 会替换这组环境变量。

  • bash 超时 + 输出上限 — 过长或输出过多的命令会被限制。超时的命令会作为进程组被杀掉, 因此它启动的任何进程也会被杀掉;此前只有直接子进程会收到信号,而后代进程会幸存。

直接使用它

将工具集附加到任意代理:

use adk_devtools::{DevToolset, Workspace};
use adk_agent::LlmAgentBuilder;
use std::sync::Arc;

let agent = LlmAgentBuilder::new("coder")
    .model(model)
    .toolset(Arc::new(DevToolset::new(Workspace::new("./my-repo"))))
    .build()?;

DevToolset 只会暴露工作区允许的工具,因此只读 工作区会自动得到一个只读代理。

沙箱模型

第 1 阶段在 bash 上以宿主机本地方式运行(sh -c,工作目录固定到根目录),并带有 超时和清空后的环境。这样做以及不能做到什么:

强制不强制
文件工具无法解析根目录之外的路径,包括通过符号链接bash 仍然可以使用绝对路径——工作目录不是操作系统边界
命令无法读取 agent 的环境变量命令可以访问网络
超时会终止命令及其子进程没有任何机制限制内存或 CPU

因此,它是路径内含、环境隔离且有边界的,但不是操作系统隔离的。该策略词汇与 adk-codeSandboxPolicy 一致;要实现强隔离,请将 bash 运行在容器化执行器后面(见设计文档)。 将其与 adk-guardrail(命令白名单、密钥脱敏)以及用于带令牌工具的 adk-auth 配合使用(例如 GitHub)。

下一步:测试执行器 →

开发工具 (`adk-devtools`) - ADK-Rust 文档 | ADK-Rust