agent() 调用与输出契约
agent() 是语言中唯一的非确定点。所有模型能力都从这里进出,
所有输出都必须带契约。本页讲它的全部选项与运行时行为。
完整形式
agent(target) {
task "..." // 必需,且唯一:自然语言任务描述
input { ... } // 结构化输入(对象字面量)
tools [read_file, grep] // 请求的工具能力(tools none = 空集)
write item.writes // 写入范围,类型必须是 path[]
expect WorkItem // 必需,且唯一:输出契约
timeout 5m // 正时长
retry { attempts: 1, on: [timeout, rate_limit, transient] }
}
agent() 表达式的静态类型 = expect 类型——所以 result.work[*].owner
这样的访问在编译期就完成检查。
target 的三种形态
| 形态 | 编译结果 | 语义 |
|---|---|---|
agent(coder)(use agent 的别名) | {kind:'alias'} | 固定的逻辑 agent |
agent(team.main) | {kind:'team.main'} | 团队的主 agent(如负责人) |
agent(team.member(expr)) | {kind:'team.member', member: <expr>} | 动态指定成员;expr 须为 text/member 类型,Runtime 做成员资格校验 |
其余形态 报 invalid-agent-target;team.main(...) 不可调用。
动态路由是这个设计的甜点:
// 把每个工作包派给它的 owner —— 路由是数据,不是硬编码
return parallel map plan.work as item {
agent(crew.member(item.owner)) {
task "Implement the work item"
write item.writes
expect ChangeResult
}
}
expect:从类型到强制契约
expect 的类型必须可生成 JSON Schema(基本类型、member、对象、数组,
可组合)。Verification 与外部类型不允许。
运行时链路:
Flow 类型 → JSON Schema → OutputContract{typeName, schema, repairAttempts}
→ AgentRuntime 调用 → 结构化结果 → Ajv 校验
→ 失败?携带 {previousOutput, errors} 修复重调(默认 1 次)
→ 仍失败 → OutputValidationError
要点:
- 修复回路是默认行为(
repairAttempts = 1)。第一次输出不合规时, Runtime 会带着校验错误再次调用 agent 修正,而不是立刻失败 - 校验失败是结构化错误,不是字符串比对——Ajv 错误清单会进入修复提示
tools 与 write:请求,不是授权
tools [read_file, list_dir, grep] // 请求这三个工具
tools none // 请求空集
write item.writes // 声明写入范围(必须是 path[] 类型)
两件事必须想清楚:
- 请求 ∩ 宿主策略。
tools列表是工作流想要的能力; 实际生效集合 =请求 ∩ host policy.allowedTools。声明得再多也不会更多。 - write 必须是静态的
path[]。数组字面量会推断公共元素类型, 因此[input.kb_path]在字段为path时合法;混入非 path 元素会报write-not-path-array。
retry:按错误码,不按报错文案
retry { attempts: 1 } // on 缺省 = ['transient']
retry { attempts: 2, on: [timeout, rate_limit] }
attempts是额外重试次数:attempts: 1最多执行 2 次- 重试判定依据错误码(
timeout/rate_limit/transient), 不解析 message——文案会变,错误码不会 - 每次调用仍受
timeout与 workflow 剩余期限约束
timeout 与期限
timeout 3m 是单次调用的限时。Runtime 用
min(timeout, workflow 剩余期限) 构造 AbortSignal 与 Promise 竞速:
迟到结果永不生效。90 秒这样的短超时也合法(90s)。
一个完整的例子
节选自 examples/bug-triage.flow:
use team "dev-team" as dev
use agent "repo-investigator" as investigator
stage reproduce -> Reproduction {
let result = agent(investigator) {
task """
Locate the suspect code for the reported bug.
Quote the decisive lines in hypothesis.
"""
input {
bug: input
logs: input.logs
}
tools [read_file, grep, list_dir] // 请求只读工具
expect Reproduction // {reproduced, suspect_files: path[], hypothesis}
timeout 3m
retry { attempts: 1, on: [transient] }
}
require result.suspect_files.count in 0..10
else fail "suspicion list must stay focused"
return result
}
确定性判断(require)在 agent 外面;非确定性产出被 expect
圈住;工具是请求;超时与重试是声明。这就是 Agent Flow 的标准姿势。
下一步
- parallel 与 parallel map —— 批量调用与并发控制
- verify —— 谁来判定"通过"