agent-compose.yml 配置手册
本文档说明当前代码实际接受的 agent-compose.yml / agent-compose.yaml 字段、默认值、约束和使用方式。解析器采用严格字段校验:未知字段、重复字段以及类型不符都会报错,因此字段名必须与本文一致。
重要:项目级工作区只支持顶层复数键
workspaces。顶层workspace已不受支持,会被当作未知字段拒绝。Agent 选择某个工作区时仍使用单数键agents.<agent>.workspace。
可以在应用配置前进行本地校验:
agent-compose config --quiet
agent-compose -f ./path/to/agent-compose.yml config
第一条命令只校验;第二条还会输出归一化后的配置,并对标记为 secret 的值进行脱敏。默认查找当前目录下的 agent-compose.yml,其次查找 agent-compose.yaml;两者同时存在时应使用 -f/--file 明确选择。
兼容性策略
公开 YAML 编写格式自 v2608.1.0 起保持稳定:该版本或后续任一稳定版本能够接受的配置,在之后的稳定版本中仍应有效。这是向后兼容承诺;新版本仍可增加可选字段,而使用严格解析器的旧版本不保证识别这些新字段。
删除或改名稳定字段、进行不兼容的类型变更,以及把可选字段改为必填字段均属于破坏性变更。收窄已有短写形式或校验约束,以及改变默认值、插值或归一化语义,也必须经过兼容性审查。安全修复可以有意拒绝不安全的历史值,但必须说明影响和迁移方式。
仓库通过机器可读的 v2608.1.0 字段契约以及累积的解析和归一化 fixture 执行该策略。agent-compose config --quiet 仍是用户使用已安装版本校验项目配置的入口;跨版本契约比较属于仓库 CI 职责,不新增独立 CLI 命令。
完整结构速览
下面的配置用于展示字段所在位置。它刻意包含了较多能力,实际项目只需保留需要的部分。
name: review-pipeline
env_file:
- .env
- .env.local
variables:
DISPLAY_NAME: review-pipeline
CONTROL_TOKEN:
value: ${CONTROL_TOKEN}
secret: true
workspaces:
source:
provider: file
path: .
upstream:
provider: git
url: https://github.com/example/project.git
ref: main
target: .
mcp_servers:
local-tools:
type: local
command: npx
args: ["-y", "@example/mcp-server"]
env:
API_TOKEN:
value: ${MCP_API_TOKEN}
secret: true
issue-tracker:
type: remote
transport: http
url: ${ISSUE_TRACKER_MCP_URL}
headers:
Authorization:
value: Bearer ${ISSUE_TRACKER_TOKEN}
secret: true
octobus_servers:
internal:
url: https://octobus.internal.example
token: ${OCTOBUS_INTERNAL_TOKEN}
public:
url: https://octobus.example
token: ${OCTOBUS_PUBLIC_TOKEN}
volumes:
cache:
name: review-cache
driver: local
labels:
purpose: agent-cache
options: {}
agents:
reviewer:
enabled: true
provider: codex
model: ${REVIEW_MODEL}
system_prompt: |
Review changes carefully and report concrete evidence.
image: chaitin/agent-compose-guest:latest
build:
context: .
dockerfile: guest-images/Dockerfile.agent-compose-guest
target: runtime
args:
CHANNEL: stable
platforms: [linux/amd64]
tags: [review-agent:latest]
no_cache: false
pull: true
driver:
docker: {}
env:
LOG_LEVEL: info
SERVICE_TOKEN:
value: ${SERVICE_TOKEN}
secret: true
mcp_servers:
- local-tools
- name: audit-api
type: remote
transport: sse
url: https://mcp.example.com/sse
headers:
Authorization:
value: Bearer ${AUDIT_TOKEN}
secret: true
capset_ids:
- engineering
- internal/code-review
skills:
- ./skills/review
- name: release-check
provider: git
url: https://github.com/example/agent-skills.git
path: skills/release-check
ref: main
volumes:
- cache:/cache
- type: bind
source: ./reports
target: /workspace/reports
read_only: false
workspace:
name: source
scheduler:
enabled: true
sandbox_policy: sticky
triggers:
- name: hourly-review
cron: "0 * * * *"
prompt: Review the current workspace.
sandbox_policy: new
jupyter:
enabled: false
guest_port: 8888
通用规则
严格解析
- 顶层和各对象中的未知字段会报
unknown field,不会被静默忽略。 - 同一映射中的重复字段会报
duplicate field。 - 布尔字段必须是布尔值,列表字段必须是 YAML sequence,映射字段必须是 YAML mapping。
- 项目名必须匹配
^[a-z0-9][a-z0-9_-]*$。Agent 名、顶层 Workspace 键、MCP 名、Volume 键和最终 Skill 名使用更严格的^[a-z][a-z0-9_-]*$格式。 - 路径和 URL 的相对基准因字段而异,具体见对应章节。
环境变量来源和优先级
env_file 决定 ${NAME} 插值时可读取哪些 dotenv 文件。加载顺序如下:
- 按
env_file列表顺序加载,后面的文件覆盖前面的同名值。 - 运行
agent-composeCLI 的进程环境最后覆盖 dotenv 文件。
若未配置 env_file,CLI 先查配置文件所在目录的 .env,不存在时再查当前工作目录的 .env。显式写 env_file: [] 表示不自动加载任何 dotenv 文件。显式文件不存在、不可读或列表中有空路径都会失败。
${NAME} 只支持简单形式,不支持 shell 的 ${NAME:-default}、命令替换或递归展开。引用的变量不存在时配置校验失败。
当前支持插值的位置包括:
variables.*.valueagents.*.modelagents.*.env.*.value- 项目级和 Agent 内联 MCP 的
url、env.*.value、headers.*.value - 项目级 OctoBus 的
url和token - Skill 的
name、source、url、path、ref、username、password和token;其中password、token必须是完整${NAME}引用
其他字符串字段不会自动插值,例如 name、provider、image、system_prompt、Workspace 的其他字段、Build 字段和 Scheduler 字段。
环境值的两种写法
variables、agents.*.env、MCP env 和 MCP headers 都使用同一个值结构:
PLAIN_VALUE: hello
SECRET_VALUE:
value: ${SECRET_VALUE}
secret: true
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
value |
string | "" |
实际值;在受支持的位置执行 ${NAME} 插值。 |
secret |
bool | false |
标记敏感值。规范化配置输出会显示 ********,运行时仍使用真实值。 |
secret 是脱敏元数据,不会自行从环境读取值;仍需在 value 中写 ${NAME}。
顶层字段
| 字段 | 类型 | 必填 | 作用 |
|---|---|---|---|
name |
string | 条件必填 | 项目标识。省略时按照 Docker Compose 规则从配置文件目录推导。 |
env_file |
string 或 string[] | 否 | 指定插值使用的 dotenv 文件。相对路径以配置文件目录为基准。 |
variables |
map | 否 | 项目级命名变量及 secret 元数据,写入规范化项目配置。它们不会自动继承到 Agent 的 env,也不会成为其他 ${NAME} 的变量来源。 |
workspaces |
map | 否 | 可复用的项目级 Workspace 定义。只能使用复数形式。 |
mcp_servers |
map | 否 | 可由 Agent 按名称引用的 MCP Server 定义。 |
octobus_servers |
map | 否 | 由 qualified capset_ids 选择的具名项目级 OctoBus Server。 |
volumes |
map | 否 | 项目管理或引用的持久 Volume。 |
agents |
map | 否 | Agent 定义;map key 是 Agent 名。 |
name
name: code-review
名称必须匹配 ^[a-z0-9][a-z0-9_-]*$。例如 review-v2 和 2-review 合法,Review 和包含空格的名称不合法。省略名称时,目录 basename 会转为小写、移除不支持的字符并裁掉开头的 _ 或 -。最终名称是 daemon 内全局唯一的 project 身份;移动 compose 文件不会创建另一个 project。
env_file
单文件可以写标量:
env_file: .env.production
多文件写列表:
env_file:
- .env
- .env.production
variables
variables:
REGION: cn-hangzhou
RELEASE_TOKEN:
value: ${RELEASE_TOKEN}
secret: true
variables 当前用于保存项目级配置值和脱敏语义。若某个值要传入 sandbox,仍需在对应 Agent 的 env 中声明。
workspaces:项目级工作区
顶层必须使用 workspaces:
workspaces:
source:
provider: file
path: .
以下旧写法无效:
# 错误:顶层 workspace 会被严格解析器拒绝
workspace:
provider: file
path: .
每个 workspaces.<key> 支持:
| 字段 | 类型 | 适用范围 | 作用 |
|---|---|---|---|
name |
string | 兼容字段 | 顶层条目的实际名称由 map key 决定;通常不要重复填写。 |
provider |
string | 必填 | file 或 git。 |
url |
string | git 必填 |
Git clone URL;file 不允许设置。 |
ref |
string | git 可选 |
Git branch、tag 或 commit。 |
path |
string | file 必填 |
相对于 compose 文件目录的来源路径,不可逃逸项目根目录;Git Workspace 不支持仓库内子目录。 |
target |
string | 可选 | sandbox workspace 根目录下的目标目录,默认 .。 |
username |
string | git 可选 |
Git HTTP 用户名。 |
password |
string | git 可选 |
Git 密码,只允许完整环境引用 ${NAME}。 |
token |
string | git 可选 |
Git token,只允许完整环境引用 ${NAME}。 |
本地 Workspace 在每次项目 run 创建时被复制为隔离快照,Agent 对快照的修改不会写回源目录。
workspaces:
source:
provider: file
path: ./src
release-branch:
provider: git
url: https://github.com/example/service.git
ref: release
target: .
Workspace 选择规则:
- 顶层
workspaces只是命名定义,不会自动分配给任何 Agent。 - Agent 省略
workspace时,无论顶层定义了多少个 Workspace,该次 run 都不配置 Workspace。 - Agent 需要使用顶层 Workspace 时,必须通过
workspace.name显式引用,或定义内联 Workspace。 - 显式的空对象
workspace: {}非法;不使用 Workspace 时应省略该 key。
mcp_servers:项目级 MCP
项目级 MCP 是命名映射,名称须符合稳定标识符格式。支持 local 和 remote 两类。
Local MCP
mcp_servers:
filesystem:
type: local
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
env:
NODE_ENV: production
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 必须为 local。 |
command |
string | 是 | sandbox 内启动 MCP Server 的命令。 |
args |
string[] | 否 | 命令参数;空项和重复项会在规范化时去除。 |
env |
map | 否 | 进程环境,支持值对象和 ${NAME} 插值。 |
transport |
string | 禁止 | Local MCP 不接受该字段的非空值。 |
url |
string | 禁止 | Local MCP 不接受 URL。 |
headers |
map | 禁止 | Local MCP 不接受 HTTP headers。 |
Remote MCP
mcp_servers:
docs:
type: remote
transport: sse
url: https://mcp.example.com/sse
headers:
Authorization:
value: Bearer ${MCP_TOKEN}
secret: true
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 必须为 remote。 |
transport |
string | 是 | sse 或 http。 |
url |
string | 是 | 远程 MCP 地址,支持 ${NAME} 插值。 |
headers |
map | 否 | 请求头,支持值对象、secret 和插值。 |
command |
string | 禁止 | Remote MCP 不执行本地命令。 |
args |
string[] | 禁止 | Remote MCP 不接受命令参数。 |
env |
map | 禁止 | Remote MCP 不接受进程环境。 |
项目级 MCP 不会自动注入所有 Agent。Agent 必须在自己的 mcp_servers 中引用或定义需要的 Server。
octobus_servers:项目级 OctoBus Server
项目可以声明多个具名 OctoBus Server:
octobus_servers:
internal:
url: https://octobus.internal.example
token: ${OCTOBUS_INTERNAL_TOKEN}
public:
url: https://octobus.example
token: ${OCTOBUS_PUBLIC_TOKEN}
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
url |
string | 是 | OctoBus Server 的绝对 http 或 https URL;不允许包含用户信息或 fragment。支持 ${NAME} 插值。 |
token |
string | 否 | daemon 连接该 Server 时使用的 Bearer token。支持 ${NAME} 插值;空值表示不配置鉴权 token。 |
map key 是 Server 名称,必须符合稳定标识符格式。Agent 通过 <server>/<capset> 形式限定现有 capset_ids 条目来选择 Server,例如 internal/code-review。仅声明 Server 不会自动向 Agent 授权。
OctoBus token 天然属于敏感信息。应把它保存在环境变量或 dotenv 配置中,并通过 ${NAME} 引用。daemon 会保留解析后的 token 以代理请求,但会在面向用户的规范化输出中将其脱敏,也不会把它注入 sandbox。不要把字面 token 提交到 compose 文件中。
项目 API 或规范化输出中的脱敏值 ******** 并不是凭据本身。ApplyProject 保持原有的完整替换语义,不会把该 marker 解释为“保留”;compose 和 CLI 的 re-apply 流程仍必须从原始、以环境变量为凭据来源的配置中解析并提交真实 secret。
PatchProject 用于基于已脱敏的 GetProject 结果编辑现有项目。请求必须携带完整目标 ProjectSpec、项目引用和当前 spec hash。在现有 secret 的同一稳定位置提交 ******** 会保留已存值;在新增、移动后或非 secret 的位置使用 marker 会被拒绝。提交真实值会替换 secret,省略集合项则会删除该项。Patch 不能创建或重命名项目,也不能修改 source;当前 hash 过期时返回 ABORTED。CLI 仍使用 ApplyProject,这些 Patch 语义不会改变 CLI 行为。
项目 re-apply 与 MCP Server 沿用相同的 managed agent 配置模型。运行中的 sandbox 保留创建时固化的 capset_ids 授权集合,后续调用则从当前 managed agent definition 解析所引用的 Server。因此,更新 Server URL 或 token 无需重建 sandbox 即可生效。新增 capset 只对新 sandbox 可用;如果旧 sandbox 已授权的 capset 所对应 Server 已无法解析,该调用会失败,不会回退到其他 Server。
volumes:项目级 Volume
volumes:
cache: {}
shared-data:
name: existing-data
driver: local
external: true
labels:
owner: platform
options:
tier: fast
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
name |
string | 由项目和 key 派生 | 指定底层 Volume 名。 |
driver |
string | local |
Volume driver。local 使用 daemon 所在主机目录;agent 使用 k8s runtime 时,k8s 会创建 Kubernetes PersistentVolumeClaim。 |
external |
bool | false |
为 true 时引用已经存在的 Volume,而不是由项目创建。 |
labels |
map[string]string | 空 | 附加标签。key/value 会去除首尾空白。 |
options |
map[string]string | 空 | Driver 选项。k8s 支持 size(默认 1Gi)、storage_class、access_mode(默认 ReadWriteOnce)和 namespace(默认 K8S_NAMESPACE/default)。PVC 使用 daemon 所在的 Kubernetes 集群。 |
Volume map key 必须符合稳定标识符格式。Agent 通过 agents.<name>.volumes 挂载项目 Volume。
agents.<name>
agents 是以 Agent 名为 key 的映射:
agents:
reviewer:
provider: codex
image: chaitin/agent-compose-guest:latest
支持的字段如下:
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enabled |
bool | true |
是否启用 Agent。禁用后定义保留但不可按正常流程运行,Scheduler 也不会启用。 |
display_name |
string | 空 | Agent 的可读显示名称。 |
description |
string | 空 | Agent 职责的可读说明。 |
provider |
string | codex |
Agent CLI/provider:codex、claude、gemini、opencode、pi 或 dsh。兼容别名会在持久化边界归一化。 |
model |
string | provider/daemon 默认 | 模型名;Pi 和 dsh 要求使用 <llm-provider-id>/<model-name>;支持 ${NAME} 插值。 |
system_prompt |
string | 空 | 附加的系统提示,适合使用 YAML ` |
image |
string | daemon 默认镜像 | Guest 镜像引用,也会作为 build 的一个输出 tag。 |
build |
string/object | 无 | agent-compose build 使用的镜像构建配置。 |
driver |
object | Docker | 运行时 driver,必须且只能选择一个 key。 |
env |
map | 空 | 注入 sandbox 的环境变量。 |
mcp_servers |
scalar/object/list | 空 | 引用项目级 MCP,或声明 Agent 专属 MCP。 |
capset_ids |
string[] | 空 | 允许该 Agent sandbox 使用的 OctoBus capability set 声明;可用 <server>/<capset> 选择项目 Server。 |
skills |
list | 空 | 注入 Agent 的 Skill 来源。 |
volumes |
list | 空 | Volume 或 bind mount 列表。 |
workspace |
object | 无 | 显式引用一个顶层 workspaces 条目,或定义 Agent 内联 Workspace。 |
sandbox |
object | 删除已停止 runtime | Sandbox 生命周期配置。 |
scheduler |
object | 无 | 自动触发 Agent 的 Scheduler。 |
jupyter |
object | disabled | Agent run 的 Jupyter 默认配置。 |
enabled、provider、model 和 system_prompt
agents:
reviewer:
enabled: true
provider: claude
model: ${CLAUDE_MODEL}
system_prompt: |
Focus on correctness, security, and regression risk.
Provider 支持 codex、claude、gemini、opencode、pi 和 dsh。当前兼容归一化还接受 claude-code / claude_code、gemini-cli / gemini_cli、open-code / open_code、pi-agent / pi_agent、deepseek / deepseek-harness / deepseek_harness,新配置建议使用规范名称。
Pi 和 dsh 是多模型 Agent,因此 model 必须同时标识已配置的 LLM provider 和模型,例如:
agents:
reviewer:
provider: pi
model: openai/gpt-5.4
第一个 / 前的部分是 agent-compose 中配置的 LLM Provider ID,后面的全部内容是发送给上游的字面量 Model ID,Model ID 本身还可以包含 /。Pi 和 dsh 的模型流量都通过 sandbox runtime LLM facade 转发,上游凭据仍只保留在 daemon 中。
Daemon models.json
daemon 在启动时加载一次 $DATA_ROOT/models.json。文件不存在是合法状态:catalog-owned 条目按不存在处理,已有 system 和环境 Provider 的默认值保持不变。修改文件后需要重启 daemon。
{
"default": "gateway/deepseek-v4-flash",
"providers": {
"gateway": {
"baseUrl": "https://gateway.example.com/api/openai",
"protocol": "chat_completions",
"apiKey": "${GATEWAY_API_KEY}",
"models": [
{
"id": "deepseek-v4-flash",
"maxOutputTokens": 8192
}
]
},
"openai": {
"baseUrl": "https://api.openai.com/v1",
"protocol": "responses",
"apiKey": "$OPENAI_API_KEY"
}
}
}
default 是可选的 provider/model 引用。每个 Provider 都必须提供 baseUrl,并选择 responses、chat_completions 或 anthropic_messages。apiKey 和 Header 值可以是字面量,也可以是完整的 $NAME / ${NAME} 引用;daemon 在启动时从自身环境解析。JSON 非法、未知字段、Protocol 不受支持、Token 上限非法或环境变量引用无法解析都会使 daemon 启动失败。
可选的 models 数组只补充模型级元数据和行为,包括 id、name、baseUrl、protocol、headers 和正整数 maxOutputTokens。模型级 protocol 必须与 Provider 的协议族兼容:OpenAI Provider(responses 或 chat_completions)只允许 responses 和 chat_completions,Anthropic Provider(anthropic_messages)只允许 anthropic_messages。这些属性属于具体的 Provider/Model 部署,共享同一 Model ID 的 Provider 不会相互覆盖;该数组也不是白名单。只要 gateway Provider 已配置,gateway/a-model-not-listed-here 仍会使用 Provider 默认配置,把右侧 Model ID 原样发送给上游。
所有兼容的 Coding Agent 和 scheduler.llm 使用这份目录完成 agent-compose 的 Provider 路由和模型选择;它不替代 Agent 自身的模型能力目录。Agent 中完整配置的 LLM_API_ENDPOINT、LLM_API_PROTOCOL 和 LLM_API_KEY 仍是更高优先级的兼容路径;daemon 自身完整的 LLM_* 配置也继续作为默认值,并优先于 models.json.default。Catalog Provider ID 如果与已有非 catalog Provider 冲突,daemon 会在不覆盖原配置的前提下启动失败。
image
agents:
reviewer:
image: chaitin/agent-compose-guest:latest
运行时会确保所选 driver 能使用该镜像。若同时配置 build,image 也会加入构建 tag;若两者均未提供 tag,执行 agent-compose build 会失败。
GitHub CI 会向 Docker Hub 发布以下镜像:
| 镜像 | 用途 | Dockerfile | 平台 |
|---|---|---|---|
chaitin/agent-compose |
控制面 daemon | Dockerfile |
linux/amd64、linux/arm64 |
chaitin/agent-compose-guest |
Sandbox guest runtime | guest-images/Dockerfile.agent-compose-guest |
linux/amd64、linux/arm64 |
chaitin/agent-compose-guest:archlinux |
可选的 Arch Linux sandbox guest runtime | guest-images/Dockerfile.agent-compose-guest-archlinux |
linux/amd64 |
Agent 的 image 可以使用任一 guest 镜像。daemon 镜像用于部署控制面,不能作为 guest 镜像使用。两个 guest 都不绑定某一个 driver;CI 不发布 BoxLite-only、Microsandbox-only 或其他 driver 专用 guest 镜像。
build
短写法只指定 context:
build: ./guest
完整写法:
build:
context: ./guest
dockerfile: Dockerfile
target: runtime
args:
VERSION: "1.2.3"
platforms:
- linux/amd64
tags:
- example/guest:latest
no_cache: false
pull: true
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
context |
string | . |
Build context;相对路径以 compose 文件目录为基准。 |
dockerfile |
string | Dockerfile |
Dockerfile 路径,由镜像构建后端解释。 |
target |
string | 空 | 多阶段构建 target。 |
args |
map[string]string | 空 | Build args;key 去除首尾空白且不能为空。 |
platforms |
string[] | 空 | 目标平台,格式为 os/arch;当前最多一个。 |
tags |
string[] | 空 | 输出镜像 tag,可与 Agent image 以及 CLI --tag 合并。 |
no_cache |
bool | false |
禁用构建缓存。 |
pull |
bool | false |
构建时拉取较新的基础镜像。 |
当前 agent-compose build 使用 Docker daemon image store。CLI 同名选项可以覆盖或追加 YAML 配置。
driver
省略 driver 时默认为:
driver:
docker: {}
必须且只能选择一个 runtime:
driver:
docker:
host: ""
或:
driver:
boxlite:
kernel: ""
rootfs: ""
或:
driver:
microsandbox:
profile: secure
或:
driver:
k8s:
context: production
namespace: agent-compose
| Driver | 子字段 | 当前状态 |
|---|---|---|
docker |
host |
支持的稳定 driver。host 被解析和保留;daemon 的 Docker 边界仍由部署配置决定。 |
boxlite |
kernel, rootfs |
Linux 构建可编译支持;运行时初始化是惰性的。子字段会去除首尾空白。 |
microsandbox |
profile |
Linux 构建可编译支持;运行时初始化是惰性的。profile 会去除首尾空白。 |
k8s |
context, namespace |
通过 Kubernetes 创建 sandbox Pod。context 选择 kubeconfig context;省略时由 client-go 使用 kubeconfig 当前 context 或集群内配置。namespace 覆盖 K8S_NAMESPACE,最终回退到 default。 |
firecracker |
kernel, rootfs |
仅保留在解析 schema 中;当前规范化会明确报 unsupported runtime driver firecracker,不可使用。 |
k8s driver 要求 daemon 运行在目标集群内部。对外支持的安装入口是
charts/agent-compose Helm Chart:
helm install agent-compose ./charts/agent-compose \
--kube-context prod-cluster \
--namespace team-a \
--create-namespace
Helm release 的 namespace 会作为默认 K8S_NAMESPACE;Chart 也会根据这个
namespace 渲染 daemon Service DNS 回调地址和 ClusterRoleBinding subject。Sandbox
Pod 不会挂载 daemon 的数据 PVC,也不使用 hostPath;provision 好的 workspace 和
provider home 配置通过 Kubernetes Exec 流式传入 Pod。
为完整说明 schema,下面的结构可以被解析器识别,但当前会在规范化阶段失败:
# 当前实现中无效。
driver:
firecracker:
kernel: /path/to/kernel
rootfs: /path/to/rootfs
即使 schema 支持某个 driver,当前二进制也必须编译了该 driver。可通过 agent-compose --json version 的 compiled_drivers 查看;这不代表 KVM、Docker daemon 或运行时制品健康。
env
env:
LOG_LEVEL: debug
API_TOKEN:
value: ${API_TOKEN}
secret: true
这些值进入 Agent sandbox。相同名称的空项会在后续边界归一化;secret: true 控制展示脱敏。
mcp_servers
引用一个项目级 MCP:
mcp_servers: filesystem
引用多个:
mcp_servers:
- filesystem
- issue-tracker
定义 Agent 专属 MCP:
mcp_servers:
- name: private-tools
type: local
command: private-mcp
args: ["serve"]
内联对象支持 name、type、transport、command、args、env、url 和 headers,其 local/remote 约束与项目级 MCP 相同。内联 MCP 必须提供 name;同一 Agent 内不能重复声明同名内联 MCP。重复引用同一个项目级 MCP 会去重。
capset_ids
capset_ids:
- legacy-capset
- internal/engineering
- public/ticketing
列表会去除空值和重复值。真实 capset ID 必须符合 OctoBus 的 ^[a-zA-Z][a-zA-Z0-9_-]{0,62}$ 规则。legacy-capset 这类未限定值使用 daemon 全局 OctoBus 配置,从而保持已有项目文件的行为。internal/engineering 这类限定值选择 octobus_servers.internal;internal 仅供 agent-compose 选择上游,OctoBus 收到的 capset ID 仍为 engineering。限定值引用未声明 Server、包含多个 / 或真实 capset ID 不合法时会产生配置校验错误。
限定值和未限定值可以混用。新增 octobus_servers 绝不会改变未限定值的路由。完整声明仍是 sandbox 的授权边界:授权 internal/engineering 不会同时授权 engineering 或 public/engineering。
所选 ID 用于注入 capability gateway 环境和能力说明。未限定值缺少 daemon 全局 gateway 配置、Server 不可达或能力说明获取失败时采用 best-effort 行为并产生 warning,sandbox 仍会继续创建。项目级 OctoBus URL 和 token 始终留在 daemon 中,不会进入 guest metadata 或能力说明。
skills
Skill 目录必须包含有效的 SKILL.md。provider 支持 file、http 和 git;同一 Agent 的最终 Skill 名不可重复。ZIP 是内容格式,不是 provider。
本地目录:
skills:
- name: review
provider: file
path: ./skills/review
Git 来源:
skills:
- name: review
provider: git
url: https://github.com/example/skills.git
path: review
ref: v1.0.0
username: ${GIT_USERNAME}
token: ${GIT_TOKEN}
远程 ZIP:
skills:
- name: review
provider: http
url: https://downloads.example.com/review.zip
format: zip
path: review
| 字段 | 类型 | 作用 |
|---|---|---|
name |
string | Skill 名;省略时从 path/URL 推导,最终必须符合稳定标识符格式。 |
provider |
string | 必填,支持 file、http 或 git。 |
url |
string | http 和 git 必填。 |
path |
string | file 的本地路径;Git 或 ZIP 内容内的子目录。相对 file 路径以 compose 文件目录为基准。 |
ref |
string | Git branch、tag 或 commit。 |
format |
string | 可选内容格式,目前只支持 zip;HTTP Skill 必须设置。 |
username |
string | HTTP/Git 用户名,可插值。 |
password |
string | HTTP/Git 密码,只允许完整环境引用 ${NAME}。 |
token |
string | HTTP/Git token,只允许完整环境引用 ${NAME}。 |
password 和 token 不允许明文。执行 config 或 up 时,CLI 会从项目 dotenv/进程环境解析完整的 ${NAME} 引用,再把项目提交给 daemon;引用对应的变量缺失时保留引用本身而不是报错,并在 clone 时再解析。面向用户的规范化输出和项目 API 会对解析后的凭据脱敏。远程 ZIP 下载限制为 HTTP(S),并执行大小、压缩包和网络地址安全检查。
Git ref 会在各自业务生命周期中解析:Skill 在 Agent run 时解析,Workspace 在 sandbox provisioning 时解析,Scheduler 来源在 config/up 时解析并保存脚本快照。因此 moving branch 在三处可能得到不同 commit;需要严格一致时,应在 ref 中直接填写 commit SHA。
volumes
短写格式是 source:target[:ro|rw]:
volumes:
- cache:/cache
- ./reports:/workspace/reports:ro
长写格式:
volumes:
- type: volume
source: cache
target: /cache
read_only: false
- type: bind
source: ./reports
target: /workspace/reports
read_only: true
| 字段 | 类型 | 必填 | 作用 |
|---|---|---|---|
type |
string | 否 | volume 或 bind。省略时:source 命中顶层 Volume、是绝对路径或以 . 开头时可推断,否则按 Volume。 |
source |
string | 是 | 顶层 Volume key/有效 Volume 名,或 bind 的 host source。 |
target |
string | 是 | Guest 内绝对路径。 |
read_only |
bool | 否 | 只读挂载,默认 false。 |
同一 Agent 不能将多个条目挂到相同 target。短写使用 : 分隔,因此不适合包含冒号的 source/target,此时应使用长写。
Kubernetes(k8s)driver 不支持本地 bind 挂载。bind 的 source 指向 daemon
所在机器上的路径,而该路径在 Pod 内不可用。请改用 driver: k8s 的顶层命名
Volume(由 PVC 提供);配置校验阶段会直接拒绝 bind 挂载。
workspace:Agent 选择或内联工作区
这里是 Agent 内部的单数 workspace,与顶层复数 workspaces 不是同一个层级。
引用顶层条目:
workspace:
name: source
内联本地 Workspace:
workspace:
provider: file
path: ./src
内联 Git Workspace:
workspace:
provider: git
url: https://github.com/example/project.git
ref: main
target: .
若 name 与任一来源字段或 target 同时出现,该对象按内联 Workspace 处理,而不是从顶层继承后局部覆盖。需要复用时只写 name。
sandbox:已停止 runtime 生命周期
默认情况下,停止 sandbox 会在确认 stop 后删除 driver runtime 及其私有可写状态,同时保留 sandbox 元数据、日志、workspace 和声明的持久挂载;resume 会创建新的 runtime。若需要重新启动同一个容器、Box 或 microVM sandbox,请显式保留私有 runtime 状态:
sandbox:
stopped_runtime_policy: retain
stopped_runtime_policy 只接受 retain 或 remove(默认值):
retain保留已停止的 runtime 和私有可写层。Resume 必须使用同一个 runtime;runtime 意外丢失时不会静默重建。remove显式删除已停止的 Docker 容器、BoxLite box/私有磁盘,或 Microsandbox sandbox/私有 qcow2 overlay。Sandbox 元数据、事件、日志、workspace 和声明的持久挂载仍会保留。Resume 会创建新 runtime,因此只存在于私有可写层中的数据会丢失。
Kubernetes driver 不接受 retain:Kubernetes Pod 没有“已停止但仍保留”的状态,停止 sandbox 会删除 Pod。请使用 remove,让 resume 根据镜像和持久挂载重新创建 Pod。若后续调度任务需要复用同一个运行中的 Pod,请使用 sandbox_policy: sticky;sticky 完成后会保持 Pod 运行,它与 stopped-runtime retention 是两种不同的生命周期策略。
有效策略会在 sandbox 创建时生成快照;之后修改项目配置只影响新 sandbox。没有策略快照的历史 sandbox 会继续保留 runtime。agent-compose inspect sandbox <sandbox> --json 通过 stopped_runtime_policy、stopped_runtime_state、stopped_runtime_last_error 和 stopped_runtime_released_at 展示生命周期记录。状态含义如下:
retained:当前没有主动释放 runtime;已停止 sandbox 应继续使用保留的 runtime 恢复。release_pending:释放意图已经持久化,但 stop、runtime 删除或 ownership 更新尚未全部确认完成。Daemon 会在中断后重试该状态;resume 也会先完成待处理的释放,再创建新 runtime。released:runtime 删除和 ownership 更新都已完成;resume 会创建新 runtime。
对于 remove,daemon 会先持久化 release_pending。如果生命周期记录中存在晚于最近一次确认 stop 的启动或启动尝试,即使粗粒度 VM 状态是 failed 而不是 running,也会先确认 driver stop;之后才删除 runtime 并把记录标记为 released。这个顺序可避免部分启动的 runtime 被跳过 stop,或未经确认便被破坏性释放。磁盘上的 ownership record 格式属于内部恢复状态,不是稳定的运维接口。
scheduler
Scheduler 可以使用声明式 triggers,也可以使用 JavaScript script;两者互斥。
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enabled |
bool | true |
是否启用该 Agent 的 Scheduler。禁用 Agent 也会使其 Scheduler 无效。 |
sandbox_policy |
string | new |
Scheduler 默认 sandbox 策略:new 或 sticky。 |
concurrency_policy |
string | skip |
整个 Agent Scheduler 的重叠运行策略:skip 或 parallel。 |
run_timeout |
duration | 空 | 单次 scheduler run 的最大时长。为空时继承 SCHEDULER_RUN_TIMEOUT;支持 30m、2h 等 Go duration。 |
model |
string | Agent 的模型 | scheduler.llm 默认使用的 provider/model;调用参数中的 model 或 LLM_MODEL 优先。 |
triggers |
list | 空 | 声明式触发器。 |
script |
string/object | 空 | 内联 JavaScript,或扁平的 file/http/git 来源配置。不能和 triggers 同时使用。 |
new 为每次调用创建新 sandbox;sticky 允许 Scheduler 绑定并复用 sandbox。单个 Trigger 的 sandbox_policy 可覆盖执行 Agent 时的策略。
concurrency_policy 作用于整个 Agent Scheduler,包括全部声明式或脚本注册的 Trigger,以及手动 Scheduler 调用。skip 会把与同一 Scheduler 既有运行重叠的新 run 记录为 skipped,且不会排队补跑;parallel 允许重叠 run 并行执行。它不是 Trigger 级策略。
脚本型 scheduler 可以用 scheduler.llm.async 和 scheduler.agent.async 并行发起 host 调用。两个 scheduler env 用于限制同时进行的数量:
| Env | 默认值 | 说明 |
|---|---|---|
LLM_MAX_CONCURRENCY |
8 |
同时进行的 scheduler.llm.async 调用数。 |
AGENT_MAX_CONCURRENCY |
3 |
同时进行的 scheduler.agent.async 运行数。 |
agent 的上限明显更低:每个并行 agent 运行都会创建独立 sandbox,而 sandbox 创建本身没有数量限制,且每次运行都要经由 SQLite 连接池写入(SQLITE_MAX_OPEN_CONNS,默认 4 条)。非正整数会被忽略并回退到默认值。scheduler.agent.async 始终使用 new sandbox 策略:并行 agent 无法共享同一个 sandbox。
声明式 Trigger
每个 Trigger 必须且只能写一个 kind 字段:cron、interval、timeout 或 event。
scheduler:
enabled: true
sandbox_policy: sticky
concurrency_policy: parallel
triggers:
- name: nightly
cron: "0 2 * * *"
timezone: Asia/Shanghai
prompt: Run the nightly review.
- name: heartbeat
interval: 30m
prompt: Check service health.
- name: startup-once
timeout: 15s
prompt: Perform the startup check.
sandbox_policy: new
- name: webhook-review
event:
topic: webhook.github.push
prompt: Review the pushed changes.
| 字段 | 类型 | 必填 | 作用 |
|---|---|---|---|
name |
string | 否 | 可读的稳定名称;同一 Scheduler 中非空名称不可重复。省略时 ID 会结合列表位置稳定派生。 |
cron |
string | 四选一 | 5 字段 cron,支持可选秒字段和 robfig/cron descriptor。默认使用 daemon 的本地时区。 |
timezone |
string | 否 | cron trigger 使用的 IANA 时区,例如 UTC 或 Asia/Shanghai。Local 或省略该字段时使用 daemon 本地时区;其他 trigger 类型不能使用。 |
interval |
duration | 四选一 | 周期,例如 30s、5m、2h;必须大于 0,实际注册精度至少 1ms。 |
timeout |
duration | 四选一 | 一次性延迟,例如 15s;必须大于 0,实际注册精度至少 1ms。 |
event.topic |
string | 四选一 | 内层 topic 是订阅的非空 topic,可使用如 webhook.github.push。 |
prompt |
string | 否 | 触发后发送给 Agent 的 prompt;空值默认为 Run agent <name>.。 |
sandbox_policy |
string | 否 | 本次 Agent 调用使用 sticky 或 new;省略时不在生成的调用中显式覆盖。 |
Daemon 本地时区优先取 TZ,未设置时取操作系统的 /etc/localtime。项目提供的 Docker Compose 会以只读方式挂载宿主机 /etc/localtime;仅当 daemon 需要有意使用不同于宿主机的时区时,才在 .env 中设置 TZ。修改时区后需要重启 daemon。持久化时间戳仍统一使用 UTC。
内联脚本
scheduler:
enabled: true
script: |
scheduler.interval("review", async function () {
return scheduler.agent("Review the workspace.");
}, 60000);
脚本由 Scheduler runtime 验证,并从脚本注册结果获取触发器。
外部脚本
本地文件:
scheduler:
enabled: true
script:
provider: file
path: ./scheduler.js
HTTP URL:
scheduler:
enabled: true
script:
provider: http
url: https://example.com/scheduler.js
外部脚本 mapping 使用与 Skill、Workspace 相同的来源字段:
- File:
provider: file配合path,相对路径以 compose 文件目录为基准。 - HTTP:
provider: http配合url,可选认证字段。 - Git:
provider: git配合url、可选ref和必填的仓库内path。
应用项目时 CLI 会读取脚本并将内容快照保存到项目规范,而不是让 daemon 以后重新读取来源。HTTP 读取限制包括 10 秒超时、最大 1 MiB、最多 5 次 redirect、UTF-8 校验;HTTPS 不允许降级 redirect 到 HTTP,URL userinfo 不允许使用。
jupyter
jupyter:
enabled: true
guest_port: 8888
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enabled |
bool | false |
Agent run 未用 CLI 显式覆盖时,是否启用 Jupyter。 |
guest_port |
int | daemon JUPYTER_GUEST_PORT |
Guest 内监听端口。0 使用 daemon 默认;显式值必须在 1–65535。 |
设置 guest_port 但保持 enabled: false 会保留端口配置,但默认不启动 Jupyter。CLI run --jupyter 可以为单次运行启用。
常见错误与迁移提示
升级时存在同名 project
升级不会归并已有的同名 project,因为名称和 compose 路径相同并不能证明它们的历史数据等价。一个 project 保留原名:优先 active project,其次按最近更新时间排序,最后使用创建时间和完整 ID 做确定性排序。其余 project 依次改为下一个可用的 <name>-N,并跳过已经存在的数字后缀。
每个 project 的 ID 以及 revision、agent、scheduler、run、sandbox 和 volume 关联仍属于原 project。升级后使用 agent-compose project ls 查看分配后的名称;后续如需更新带后缀的 project,应在对应 compose 文件中设置该名称。
顶层误写 workspace
错误:
workspace:
provider: file
path: .
正确:
workspaces:
source:
provider: file
path: .
agents:
reviewer:
workspace:
name: source
同时配置 Scheduler script 和 triggers
二者互斥。把所有注册逻辑放入 script,或完全使用声明式 triggers。
以为 variables 会自动进入 sandbox
variables 不会继承到 Agent。需要显式写:
variables:
API_URL: https://api.example.com
agents:
reviewer:
env:
API_URL: https://api.example.com
如果目标是复用部署环境值,可在两个位置都写 ${API_URL},并通过 env_file 或 CLI 进程环境提供。
以为项目 Workspace 会被自动选择
顶层 workspaces 不会被自动选择。Agent 省略 workspace 时,即使项目只定义了一个条目,也不会配置 Workspace。需要使用时应设置 workspace.name 或内联 Workspace;不使用时应省略该 key,而不是写 workspace: {}。
选择未编译或运行条件不满足的 driver
配置 schema 通过不代表 runtime 一定可启动。BoxLite/Microsandbox 只在 Linux 完整构建中编译,并依赖 KVM 和对应运行时制品;Docker 需要可达的 Docker daemon。
最小配置示例
name: docker-minimal
agents:
reviewer:
provider: codex
image: chaitin/agent-compose-guest:latest
driver:
docker: {}
验证并应用:
agent-compose config --quiet
agent-compose up
agent-compose run reviewer --prompt "Review this project."