Skip to content
预计 1 分钟右侧目录可快速定位

Agent、Workflow 与 Harness:控制权、执行权与系统边界 ​

前置:无需前置;从这里建立最小 Harness 心智模型
核心问题:当模型可以提出下一步动作时,谁拥有真正的控制权和执行权,Workflow、Agent、Harness、Runtime 分别负责什么?

最小控制模型 ​

核心边界:模型可以决定“建议做什么”,但不能自己把建议变成系统事实。

1. Workflow、Agent、Harness、Runtime 先分清 ​

  • Workflow:下一步主要由程序预先写好的控制流决定。适合顺序固定、规则明确的任务。
  • Agent:模型根据当前 Observation 选择下一步动作。它扩大的是决策空间,不是执行权限。
  • Harness:承载 Model、Tool、Context、Policy、State、Trace 等能力的软件系统。
  • Runtime:Harness 中真正推进 Run 的执行内核。它验证动作、调用 Tool、提交状态,并产生下一轮可依赖的 Observation。

最重要的边界不是“有没有 LLM”,而是:谁决定下一步,谁有权把结果提交为事实。

2. 最小运行闭环 ​

text
User Request
   ↓
Harness 组装 Context
   ↓
Model 提出 Action
   ↓
Runtime validate
   ↓
Runtime execute Tool
   ↓
Tool Result
   ↓
Runtime normalize / commit
   ↓
Observation
   ↓
Model 决定下一步或结束

在这条链路里:

  • Action 是模型提出的意图;
  • Tool Result 是外部执行返回的原始结果;
  • Observation 是 Runtime 整理后可用于下一步决策的新事实;
  • “Tool 成功”“Run 完成”“允许执行”都属于 Runtime 事实,不能只由模型文本决定。

3. Run / Turn / Action / Observation ​

先只掌握四个词:

  • Run:一次完整任务执行。
  • Turn:模型得到 Context 并做一次决策的回合。
  • Action:这一回合模型提出的下一步,例如调用 Tool 或给出 Final Answer。
  • Observation:执行后进入下一回合的新事实。

一个 Run 可以有多个 Turn;一个 Turn 可以产生一个或多个 Action;Action 只有经过 Runtime 校验和执行,才可能产生 Observation。

4. Model proposes → Runtime commits ​

假设模型返回:

json
{"tool": "apply_patch", "path": "auth.py", "patch": "..."}

这并不表示文件已经修改。Runtime 至少还要判断:

  1. apply_patch 是否在当前 Tool Registry;
  2. 参数是否满足 schema;
  3. 当前 policy 是否允许修改该文件;
  4. Tool 实际执行结果是什么;
  5. 哪些结果可以提交为新的 Run state / Observation。

因此可以固定一句话:

Model 可以提出动作;Runtime 才拥有验证、执行与提交系统事实的权力。

5. 一个完整最小案例 ​

用户要求“找出认证失败原因并给出修复建议”。

text
Turn 1
Model → search_code("token")
Runtime → 校验并执行
Observation → 找到 auth/token.py

Turn 2
Model → read_file("auth/token.py")
Runtime → 执行
Observation → token expiry 比较使用了错误时区

Turn 3
Model → Final Answer:指出原因与修复方式
Runtime → 结束 Run

这个例子已经具备 Agent 的最小特征:下一步不是程序提前写死,而是模型根据 Observation 决定;但搜索和读文件仍然由 Runtime 真正执行。

Advanced / Canonical 深入内容

1. 为什么第一章先讲“谁拥有控制权” ​

学习 Agent 最容易走偏的地方,是先记框架名、Prompt 技巧或 Tool Calling API,却没有先建立一个最基本的系统模型:

模型可以决定什么?程序必须保证什么?真正的副作用由谁执行?系统如何知道一件事“真的发生了”?

如果这几个问题没有分清,后面学习 Context、Tool、Planning、State、Checkpoint、HITL、Sandbox、Tracing 时会反复混乱。

例如用户要求:

“分析这个代码仓库,修复登录失败的问题,并运行测试确认修复有效。”

模型可能提出:

  1. 搜索登录相关代码;
  2. 阅读 auth.py;
  3. 修改 token 校验逻辑;
  4. 运行测试;
  5. 根据测试结果决定是否继续修改。

这里确实存在“模型自主选择下一步”的成分。

但下面这些事情不能只因为模型说了就成立:

  • 文件是否真的被修改;
  • 当前 Agent 是否有权限修改这个目录;
  • pytest 是否真的执行成功;
  • Tool 是否超时;
  • 修改是否已经持久化;
  • 任务是否满足结束条件;
  • 失败后是否应该重试;
  • 一个有副作用的动作是否已经执行过。

这些都是程序需要验证和记录的系统事实(system facts)。


2. Workflow、Agent、Harness 与 Runtime 的执行链 ​

可以把执行链表示为:

text
                    ┌────────────────────────────┐
                    │          Harness           │
                    │                            │
User Goal ─────────▶│  Context / Tools / Policy  │
                    │  State / Trace / Memory    │
                    │  Approval / Sandbox ...    │
                    └────────────┬───────────────┘
                                 │
                                 ▼
                        ┌─────────────────┐
                        │      Model      │
                        │  propose next   │
                        │    decision     │
                        └────────┬────────┘
                                 │
                            Action Intent
                                 │
                                 ▼
                        ┌─────────────────┐
                        │     Runtime     │
                        │ validate        │
                        │ authorize       │
                        │ execute         │
                        │ persist         │
                        │ schedule        │
                        └────────┬────────┘
                                 │
                              Result
                                 │
                                 ▼
                           Observation
                                 │
                                 └──────────▶ next Turn

这张图里最重要的不是组件名称,而是两条边界:

2.1 模型拥有的是“决策建议权” ​

模型可以根据当前 Context 判断:

  • 下一步需要搜索什么;
  • 选择哪个 Tool;
  • 是否还缺信息;
  • 如何拆解开放任务;
  • 当前结果是否值得继续探索。

但模型输出本身只是一个候选决策。

2.2 Runtime 拥有的是“执行事实的提交权” ​

真正改变系统状态的动作,需要经过程序:

text
Model proposes
    ↓
Runtime validates
    ↓
Policy authorizes
    ↓
Executor performs
    ↓
State/Trace records

因此:

“模型认为已经完成”与“系统证明已经完成”不是一回事。

这是 Agent Engineering 最重要的边界之一。


3. Workflow:控制路径主要由程序预先决定 ​

Anthropic 在 Building effective agents 中给出的一个实用区分是:

  • Workflow:LLM 和 Tool 沿预定义的代码路径进行编排;
  • Agent:模型会根据当前情况动态决定自己的过程和 Tool 使用方式。

这里的关键不是“有没有 LLM”。

一个 Workflow 完全可以包含多个 LLM 调用:

text
User Input
   ↓
Classify with LLM
   ↓
Retrieve
   ↓
Generate with LLM
   ↓
Validate
   ↓
Return

虽然用了两个模型调用,但:

text
Classify → Retrieve → Generate → Validate

这个顺序由代码确定,所以它仍然是 Workflow。

3.1 Workflow 擅长什么 ​

Workflow 适合:

  • 路径明确;
  • 业务规则稳定;
  • 错误成本较高;
  • 每个阶段可以定义清晰输入输出;
  • 希望行为高度可预测。

例如:

text
订单创建
→ 库存校验
→ 支付
→ 写账
→ 发货事件

即使其中某一步使用 LLM,也不应该让模型自由重排支付和写账顺序。

3.2 Workflow 的工程优势 ​

Workflow 的最大优势是控制流显式。

程序通常可以回答:

text
当前在哪一步?
下一步是什么?
为什么执行这一步?
失败应该跳到哪里?
哪些步骤已经完成?

因此它:

  • 更容易单元测试;
  • 更容易做状态机;
  • 更容易估算成本与 latency;
  • 更容易保证 transaction / idempotency;
  • 更容易审计。

3.3 Workflow 的限制 ​

当任务本身缺少固定路径时,Workflow 会迅速膨胀。

例如:

“调查为什么这个大型代码仓库最近性能下降,并给出修复方案。”

你很难提前写出完整的:

text
if A → B
if C → D
if E → ...

因为真正需要检查什么,往往取决于前一步发现的 Observation。

这就是 Agentic Control 开始有价值的地方。


4. Agent:模型根据 Observation 动态选择下一步 ​

Agent 的关键不是“多轮对话”,也不是“能调用 Tool”。

更有用的工程定义是:

在任务的一部分控制流上,允许模型依据当前 Context 和 Observation 动态决定下一 Action。

例如修复代码:

text
Turn 1
Model:
  "先搜索 login/token 相关实现"
       ↓
search_code
       ↓
Observation:
  "auth/service.py 中存在 verify_token"

Turn 2
Model:
  "读取 verify_token 以及它的调用方"
       ↓
read_file
       ↓
Observation:
  "发现 expiry 单位转换错误"

Turn 3
Model:
  "修改代码并运行相关测试"
       ↓
apply_patch + pytest

这里的:

text
search → read → patch → test

不是程序在任务开始时写死的,而是模型根据新事实逐步决定的。

这就是 Agentic Control。

4.1 Agent 不是“模型直接操作世界” ​

即使模型决定:

json
{
  "tool": "apply_patch",
  "arguments": {
    "file": "auth.py",
    "patch": "..."
  }
}

它也只是输出了一份结构化 Action Intent。

真正执行前仍需要:

text
Tool 是否存在?
参数是否合法?
路径是否允许访问?
当前 workspace 是否可信?
该动作是否需要人工审批?
是否超出 deadline?

然后才真正调用执行环境。

所以 Agent 的“自主性”不应该被理解成:

模型绕过程序直接拥有系统权限。

更合理的理解是:

模型在程序允许的 capability boundary 中拥有一定的决策自由度。


5. Harness:让模型能力变成一个可运行的软件系统 ​

Harness 并不是所有框架都使用完全相同定义的标准术语。

本文采用一个面向工程的定义:

Harness 是包围模型的整套运行环境与工程机制,它负责把模型的推理能力连接到 Context、Tools、State、Policy、Execution、Security 和 Observability。

Anthropic 2026 年介绍 Managed Agents 时,将 session、harness 和 sandbox 作为可以独立演化的接口,其中 harness 负责循环调用模型并把 Tool Call 路由到相关基础设施。这说明 Harness 更接近“模型周围的执行系统”,而不是某个 Prompt 文件。

一个成熟 Harness 可能包含:

text
Harness
├── Model Layer
│   ├── provider adapter
│   ├── structured output
│   └── streaming
│
├── Context
│   ├── instructions
│   ├── history
│   ├── retrieval
│   ├── memory
│   └── compaction
│
├── Tool System
│   ├── registry
│   ├── schema
│   ├── permission
│   └── result policy
│
├── Runtime
│   ├── run loop
│   ├── state machine
│   ├── scheduler
│   ├── retry
│   └── resume
│
├── State
│   ├── run state
│   ├── checkpoint
│   └── durable state
│
├── Security
│   ├── approval
│   ├── capability
│   ├── sandbox
│   └── credentials
│
└── Observability
    ├── logs
    ├── traces
    ├── metrics
    └── eval

5.1 Harness 的价值不是“让模型更聪明” ​

Harness 的目标更接近:

把概率性的模型能力包装成可测试、可恢复、可观测、受权限约束的软件能力。

同一个模型,在不同 Harness 下可能表现差异很大,因为 Harness 决定了:

  • 模型能看到什么;
  • 有哪些 Tool;
  • Tool 如何设计;
  • 失败以后怎么反馈;
  • Context 怎么维护;
  • 是否可以恢复长任务;
  • 是否能验证工作结果。

5.2 Harness 不是越复杂越好 ​

Anthropic 的 Agent 工程经验反复强调:先使用简单、可组合的 pattern,再根据任务需要增加复杂度。

如果单次模型调用 + Retrieval 已经能稳定完成任务,就没有必要增加:

text
Planner
→ Manager
→ Critic
→ Reviewer
→ Multi-Agent

复杂度本身会产生:

  • 更多 token;
  • 更多 latency;
  • 更多状态;
  • 更多故障点;
  • 更困难的 debugging。

6. Runtime:真正推进 Run 的执行内核 ​

Runtime 同样不是所有 Agent 框架中的统一标准名词。

在 Shadow Harness 中,我们用它表示:

负责推进一次 Run 生命周期,并对状态迁移、Tool Execution、终止、错误传播、暂停和恢复拥有程序控制权的执行内核。

可以把 Harness 和 Runtime 的关系理解成:

text
Harness = 整个工程外壳
Runtime = 其中负责“跑起来”的执行核心

6.1 Runtime 典型职责 ​

Runtime 通常负责:

  • 开始一个 Run;
  • 构建或请求 Context;
  • 调用 Model;
  • 解释结构化输出;
  • 找到对应 Tool;
  • 参数校验;
  • 权限/审批检查;
  • 调度 Tool;
  • timeout / cancellation;
  • 写入 Observation;
  • 更新 RunState;
  • 判断是否继续下一 Turn;
  • 判断是否成功、失败或等待;
  • checkpoint;
  • resume。

OpenAI Agents SDK 当前的 Runner 就体现了这种 runtime-loop 思路:Runner 调用模型,如果模型产生 Tool Calls 就执行 Tool 并把结果加入下一轮;如果产生 handoff 则切换 Agent;产生最终输出则结束;同时可以由 max_turns 等配置限制循环。它是一个具体实现,不代表行业唯一 Runtime 设计,但很适合作为参考。

6.2 为什么模型不能直接成为 Runtime ​

因为 Runtime 管的是程序事实。

例如模型输出:

“刚才已经成功删除文件。”

Runtime 不能因此更新:

text
file_deleted = true

正确流程应当是:

text
Model proposes delete_file
       ↓
Runtime validates permission
       ↓
Tool executes
       ↓
OS/filesystem returns result
       ↓
Runtime records observation
       ↓
State becomes DELETE_SUCCEEDED

State 的来源必须是可验证执行结果,而不是模型自述。


7. Agent Loop:Turn、Action、Observation 到底是什么 ​

理解 Agent Loop,需要把“模型思考”和“系统执行”拆开。

一个最小循环可以表示为:

text
          ┌─────────────────────────────┐
          │                             │
          ▼                             │
Build Context                           │
    ↓                                   │
Model Inference                         │
    ↓                                   │
Decision                                │
    ↓                                   │
Final? ─── yes ──▶ Finish               │
    │ no                                │
    ▼                                   │
Action Intent                           │
    ↓                                   │
Runtime Validation                      │
    ↓                                   │
Execute Tool                            │
    ↓                                   │
Observation                             │
    ↓                                   │
Update State / Context ─────────────────┘

7.1 Turn ​

本文把 Turn 定义为:

一次模型获得当前输入并产生输出,然后 Runtime 处理该输出所形成的一个推理周期。

Turn 的精确定义可能因框架不同而略有差异,所以内部实现必须自己固定术语。

至少应该能够记录:

python
TurnRecord(
    turn_id=7,
    model_request=...,
    model_response=...,
    started_at=...,
    ended_at=...,
)

7.2 Action ​

Action 是模型提出的下一步意图,例如:

text
调用 search_code
调用 shell
请求 handoff
读取 artifact
返回 final output

Action 不是已经执行的事实。

例如:

python
ToolCall(
    name="delete_file",
    arguments={"path": "..."},
)

只能证明:

模型请求调用这个 Tool。

不能证明:

文件已经删除。

7.3 Observation ​

Observation 是执行层返回给 Agent 的新事实。

例如:

text
exit_code = 1
stderr = "3 tests failed"

或者:

text
patch applied successfully

Observation 应经过 Runtime 的 normalize,再进入:

  • State;
  • Trace;
  • 下一轮 Context。

7.4 Loop 为什么必须由程序限制 ​

一个纯粹的:

python
while True:
    response = await model(...)

不是合格 Runtime。

至少需要考虑:

text
max_turns
deadline
token budget
cost budget
cancellation
approval
tool timeout
retry budget

否则模型完全可能形成:

text
search
→ search
→ search
→ search
→ ...

系统却没有可靠停止机制。


8. Deterministic Control 与 Agentic Control 如何分工 ​

生产系统通常不是“Workflow 或 Agent 二选一”,而是 Hybrid。

最重要的问题是:

哪些决策值得交给模型,哪些规则必须由程序保证?

8.1 适合 Deterministic Control 的事情 ​

这些事情通常能够形式化,并且错误成本较高:

  • authentication / authorization;
  • schema validation;
  • transaction;
  • state transition;
  • DAG dependency;
  • retry budget;
  • timeout;
  • concurrency limit;
  • idempotency;
  • audit;
  • approval requirement;
  • resource quota。

例如:

text
如果 Tool risk_level = HIGH
→ 必须进入 WAITING_APPROVAL

这是规则,不需要让模型判断。

8.2 适合 Agentic Control 的事情 ​

模型更擅长处理:

  • 开放任务拆解;
  • Tool 选择;
  • 搜索策略;
  • 判断当前证据是否足够;
  • 根据 Observation 改变探索方向;
  • 处理自然语言意图;
  • 在多个合理方案之间做语义选择。

例如:

“为了定位性能下降,我应该先检查数据库慢查询,还是先分析应用 Trace?”

这种问题存在大量上下文判断,适合 Agentic Decision。

8.3 一个实用判断方法 ​

可以连续问三个问题:

问题 A:这个规则能否用确定性代码准确描述?

如果可以,优先代码。

问题 B:错误执行是否会产生高风险副作用?

风险越高,越应该把最终权限保留在程序/Human Approval。

问题 C:下一步是否依赖开放世界中的新 Observation?

如果是,模型动态决策的价值更高。


9. 如何选择 Workflow、Agent 或 Hybrid ​

9.1 选择 Workflow ​

适合:

text
路径稳定
+ 强一致性
+ 可枚举异常
+ 需要高可预测性

例如:

  • 文档固定审批流程;
  • 财务对账 pipeline;
  • 明确 ETL;
  • 结构化报告生成流水线。

9.2 选择 Agent ​

适合:

text
开放任务
+ 路径未知
+ 信息需要逐步发现
+ 多种工具组合方式都可能成立

例如:

  • 深度研究;
  • 大型代码仓问题定位;
  • 开放式数据分析;
  • 多来源调查。

9.3 生产中更常见的是 Hybrid ​

例如 Coding Agent:

text
Agentic:
决定搜索哪些文件
决定下一步调查方向
决定修改策略

Deterministic:
路径权限
patch apply
shell timeout
test exit code
state transition
checkpoint
approval

因此可以把 Hybrid 理解为:

模型负责探索空间,Runtime 负责守住系统不变量。

9.4 不要为了“更 Agent”而扩大模型控制权 ​

一个系统不是模型自由度越高越先进。

合理目标是:

在能够获得任务收益的地方使用 Agentic Control,在必须稳定的地方保留 Deterministic Control。


10. 一个真实执行案例:修复代码并验证 ​

把前面的概念放进一条完整执行链。

用户:

“修复仓库中登录 token 过期判断错误,并确保测试通过。”

Step 1:Harness 准备运行环境 ​

Harness 提供:

text
instructions
workspace
read/search/patch/shell tools
permission policy
context manager
trace

Step 2:Runtime 创建 Run ​

python
run = RunState(
    status="RUNNING",
    turn=0,
)

这是程序事实。

Step 3:模型做 Agentic Decision ​

模型根据目录和用户目标输出:

json
{
  "action": "search_code",
  "query": "token expiry verify"
}

Step 4:Runtime 校验并执行 ​

Runtime 检查:

text
search_code 是否注册?
arguments 是否符合 schema?
workspace 是否允许?

然后执行 Tool。

Step 5:产生 Observation ​

json
{
  "matches": [
    "auth/service.py:82"
  ]
}

Runtime 将 Observation 写入 Trace,并让 ContextManager 在下一 Turn 使用。

Step 6:模型继续动态决策 ​

模型读取文件,发现:

text
milliseconds / seconds conversion error

于是提出:

text
apply_patch

Step 7:Runtime 守住副作用边界 ​

如果 apply_patch 需要 workspace write permission:

text
Policy → allow / deny / require approval

真正修改成功以后,Runtime 才记录:

text
PATCH_SUCCEEDED

Step 8:模型请求测试 ​

text
shell: pytest tests/test_auth.py

Runtime 执行,获得:

text
exit_code = 0

Step 9:Runtime 判断系统事实 ​

模型可以说:

“测试已经通过。”

但最终可验证事实来自:

text
ToolExecutionResult.exit_code == 0

Step 10:结束 Run ​

模型输出 final response。

Runtime 检查:

  • 是否存在未完成 Tool;
  • 是否满足终止规则;
  • 是否写入最终 State / Trace。

然后:

text
RUNNING → SUCCEEDED

这条执行链就是:

text
Agentic Decision
        +
Deterministic Runtime
        +
Harness Capabilities

组合后的真实 Agent 系统。


11. 最小 Shadow Harness 应该怎么实现 ​

第一阶段不要做复杂 Multi-Agent。

建议最小目录:

text
shadow_harness/
├── model/
│   └── client.py
├── context/
│   └── manager.py
├── tools/
│   ├── registry.py
│   └── executor.py
├── runtime/
│   └── runner.py
├── state/
│   └── models.py
├── trace/
│   └── recorder.py
└── tests/

11.1 AgentDefinition:声明“这个 Agent 是什么” ​

python
from dataclasses import dataclass

@dataclass(frozen=True)
class AgentDefinition:
    name: str
    instructions: str
    tool_names: tuple[str, ...]

它不应该保存:

text
current_turn
pending_tool_call
retry_count
checkpoint_id

因为这些是运行事实。

11.2 RunState:保存“这次运行现在是什么状态” ​

python
from dataclasses import dataclass, field

@dataclass
class RunState:
    run_id: str
    status: str = "RUNNING"
    turn: int = 0
    observations: list = field(default_factory=list)

11.3 Runtime:拥有循环 ​

python
async def run_agent(agent, state, model, tools, limits):
    while state.status == "RUNNING":
        if state.turn >= limits.max_turns:
            state.status = "FAILED"
            raise RuntimeError("max turns exceeded")

        request = build_context(agent, state)
        response = await model.generate(request)

        if response.final_output is not None:
            state.status = "SUCCEEDED"
            return response.final_output

        for call in response.tool_calls:
            tool = tools.require(call.name)
            args = tool.validate(call.arguments)

            result = await tool.execute(args)

            state.observations.append(
                normalize_tool_result(call, result)
            )

        state.turn += 1

这个例子故意保持简单。

它还没有解决:

  • 并行 Tool Calls;
  • timeout;
  • retry;
  • approval;
  • durable checkpoint;
  • cancellation;
  • context compaction;
  • trace spans。

这些正是后续章节要逐步加入的能力。


12. Failure Modes:这一层最常见的架构错误 ​

12.1 把“会调用 Tool”直接等同于 Agent ​

错误表现

系统只有:

text
User → LLM → one tool → response

却默认它已经拥有完整 Agent Runtime。

问题

Tool Calling 只提供一种模型→程序的结构化交互机制,并没有自动提供:

  • loop;
  • state;
  • retry;
  • timeout;
  • approval;
  • resume;
  • termination。

修复

明确区分:

text
Tool Calling capability
≠
Agent control loop
≠
Harness

验证

用 Scripted Model 只返回一次 Tool Call,断言系统不会因为“支持 Tool Calling”就自动拥有 loop、checkpoint、approval 或 resume;这些能力必须由 Runtime/Harness 的独立 contract test 证明。

12.2 让模型决定程序事实 ​

错误表现

模型输出:

“任务完成。”

程序立即:

python
run.status = "SUCCEEDED"

问题

模型可能:

  • 误判;
  • hallucinate;
  • 忘记某个未完成步骤;
  • 忽略 Tool Failure。

修复

成功条件应尽量来自:

text
validated output
+ execution result
+ deterministic completion rule

验证

让 Fake Tool 返回失败,同时让下一轮 Fake Model 声称“任务已完成”;断言 Runtime 不得进入 SUCCEEDED,并以 ToolExecutionResult 与确定性验收条件作为事实来源。

12.3 把权限、重试和状态迁移写进 Prompt ​

错误表现

Prompt:

“如果失败最多重试三次;没有权限时不要执行;成功以后把状态变成完成。”

问题

这些规则本应由程序强制保证。

模型理解并遵守 Prompt 不是 security/reliability guarantee。

修复

分别落到:

text
RetryPolicy
PermissionPolicy
StateMachine

验证

构造 Prompt 要求越权执行或无限重试,同时配置 PermissionPolicy 拒绝、RetryBudget=2;断言 Executor 不被越权调用,attempt 数也不能突破程序策略。

12.4 Agent Loop 没有资源边界 ​

错误表现

python
while not model_says_done:
    ...

后果

可能产生:

  • runaway loop;
  • token 无限增长;
  • API 成本失控;
  • Tool 重复调用;
  • 长时间占用资源。

修复

至少配置:

text
max_turns
deadline
token/cost budget
tool timeout
cancellation

验证

让 Scripted Model 永远返回“继续搜索”,分别触发 max_turns、deadline 与 cancellation,断言 Run 在边界内停止,child Tool/Task 不继续泄漏执行。

12.5 一开始就构建复杂 Multi-Agent ​

错误表现

任务只有一个清晰工作流,却加入:

text
manager
planner
researcher
critic
reviewer

问题

你无法判断性能提升来自哪里,却必然增加:

  • coordination cost;
  • context duplication;
  • token;
  • latency;
  • failure surface。

修复

先建立:

text
single-call baseline
→ workflow baseline
→ single-agent baseline

有明确 Eval 证据后再增加复杂度。


验证

在同一版本化 Eval Set 上比较 Single Agent baseline 与 Multi-Agent 方案,记录成功率、token、latency 和 coordination failure;没有可重复净收益就不保留额外拓扑。

12.6 Harness 与具体模型能力耦合过深 ​

错误表现。 Harness 写死大量“因为当前模型做不到 X,所以必须 Y”的 workaround,例如固定分步、过频 summary、重复 critic 或僵化的 Tool 顺序。

为什么危险。 这些规则通常不是业务不变量,而是对某一代模型能力的临时假设。模型升级后,它们可能从帮助变成 dead weight:继续增加 token 和 latency,甚至限制模型已经具备的新能力。Anthropic 2026 年 Managed Agents 的经验明确提醒,Harness 会编码对模型能力的假设,因此需要持续重新验证。

修复

把 workaround 做成:

text
replaceable policy
+ capability detection
+ versioned eval

不要让临时模型缺陷变成永久架构。

验证

每次模型或 provider 发生显著升级时,在同一版本化 Eval Set 上对 workaround 做 ON/OFF ablation,同时比较成功率、token、latency 和 failure rate。只有仍有稳定净收益的策略才继续保留。


13. Runtime Contract 的确定性测试 ​

这一层主要验证 Runtime contract,而不是语义质量;大量边界可以用 deterministic testing 直接证明。

13.1 测试一:模型不能绕过 Tool Registry ​

Scripted Model 输出:

json
{
  "tool": "delete_everything"
}

如果 Registry 没有该 Tool:

text
Runtime 必须拒绝

而不是“模型已经要求了,所以执行”。

13.2 测试二:达到 max_turns 必须终止 ​

让 Fake Model 永远返回:

text
search again

断言:

text
turn == max_turns
→ Run stops

13.3 测试三:Tool Failure 不能变成成功事实 ​

Fake Tool:

text
exit_code = 1

即使模型下一轮说:

“执行成功。”

Runtime 也不应该把 ToolExecution 标记为成功。

13.4 测试四:Workflow 与 Agentic 节点边界明确 ​

给模型不同的 Scripted Decision:

text
A → search_code
B → read_file

验证只有声明为 Agentic 的节点控制流发生变化。

确定性的状态迁移、权限和 retry policy 不随模型输出改变。

13.5 真正需要 Eval 的部分 ​

例如:

模型选择的 Tool 是否合理?

这是语义质量问题,可以建立 Eval Set:

text
task
expected useful tools
actual tool trajectory
task success
token
latency

因此要区分:

text
Testing → 程序契约是否正确
Evaluation → Agent 决策质量是否足够好

14. 常见问题(Q&A) ​

Q1:只要 LLM 能调用 Tool,就算 Agent 吗? ​

不一定。

Tool Calling 只是模型输出结构化 Action 的一种能力。

如果 Tool 调用路径仍然完全由程序预定义,例如:

text
永远先 search
→ 再 summarize
→ 再 return

那它更接近带 Tool 的 Workflow。

Agent 的关键在于:模型是否拥有一部分动态控制流决策权。


Q2:Agent 和 Workflow 最核心的区别是什么? ​

看的是:下一步主要由谁决定,以及这种决定是否会随着新的 Observation 动态变化。

Workflow 的主要控制路径由代码预先定义。即使某一步内部调用 LLM,只要程序仍然决定 A → B → C 的顺序,它仍然属于 Workflow。

Agent 则把一部分控制流交给模型:模型读取当前 Context 和 Observation 后,可以选择不同 Tool、改变调查方向、决定是否继续探索。真实生产系统通常采用 Hybrid——开放语义决策交给模型,权限、状态机、retry、deadline 等系统不变量仍由确定性程序控制。


Q3:Harness 和 Runtime 是同一个东西吗? ​

这里区分为:

text
Harness = 模型周围完整工程外壳
Runtime = Harness 中负责推进 Run 的执行核心

Runtime 负责 loop、Tool Execution、状态迁移、终止、恢复等。

Harness 还包含:

  • Context;
  • Model Adapter;
  • Memory;
  • Security;
  • Observability;
  • Sandbox 等。

需要注意,这两个术语在不同团队/框架里可能边界不同,所以项目内部必须先固定自己的 Domain Language。


Q4:为什么模型不能直接决定“任务成功”? ​

因为模型输出是概率性的语义判断,而任务成功通常依赖可以被外部验证的系统事实。

例如模型可能根据一段截断日志说“测试通过了”,但真正可以证明测试进程成功结束的是 exit_code = 0;模型可能说“文件已经修改”,真正的证据则是 filesystem result、diff 或 artifact record。

因此 Runtime 应把 completion criteria 尽量落到可验证条件上,再让模型负责那些无法完全形式化的语义判断。一个可靠的边界是:

text
Model judges meaning
Runtime verifies facts

只有两者都满足,系统才应该把 Run 推进到 SUCCEEDED。


Q5:是不是应该把所有控制权都放到代码里? ​

也不是。

如果一切都由代码预定义,就失去了 Agent 对开放任务的适应能力。

合理边界是:

代码保证系统不变量

例如:

  • 权限;
  • transaction;
  • timeout;
  • state transition;
  • retry limit。

模型处理开放语义决策

例如:

  • 搜索什么;
  • 使用哪个 Tool;
  • 信息是否足够;
  • 下一步调查方向。

Q6:什么时候没必要使用 Agent? ​

当:

  • 路径明确;
  • 输入输出结构稳定;
  • 错误成本较高;
  • Workflow 已经能高质量解决;

就没有必要为了“Agent 化”增加动态循环。

先使用最简单能满足目标的架构,通常更容易维护。


Q7:为什么说 Harness 会随着模型升级而变化? ​

Harness 经常承担“补偿当前模型不足”的职责。

例如某代模型可能需要:

  • 更强的显式分步;
  • 特定 context reset;
  • 更多 Tool 描述;
  • 特定纠错 Prompt。

模型升级后,这些策略可能不再必要。

因此 Harness 应尽量:

text
稳定接口
+
可替换 Policy
+
版本化 Eval

而不是把临时 workaround 写死成永久架构。


Q8:Shadow Harness 第一阶段为什么不直接做 Multi-Agent? ​

因为首先要证明单 Agent Runtime 的基本机制正确:

  • Context;
  • Tool;
  • Loop;
  • State;
  • timeout;
  • retry;
  • Trace;
  • Test。

如果这些底层机制都没有稳定,多 Agent 只会把同样的问题复制多份。

正确顺序是:

text
single agent
→ reliable runtime
→ measurable baseline
→ only then multi-agent when justified

15. 执行边界总图 ​

text
                    ┌──────── Harness ────────┐
                    │                         │
User Goal ─────────▶│ Context / Capability    │
                    │ Policy / State / Trace  │
                    │                         │
                    │      ┌───────────┐      │
                    │      │   Model   │      │
                    │      └─────┬─────┘      │
                    │            │            │
                    │      Action Intent      │
                    │            │            │
                    │      ┌─────▼─────┐      │
                    │      │ Runtime   │      │
                    │      │ validate  │      │
                    │      │ execute   │      │
                    │      │ persist   │      │
                    │      └─────┬─────┘      │
                    │            │            │
                    │       Observation       │
                    │            │            │
                    └────────────┼────────────┘
                                 │
                              next Turn

这张图对应六个稳定边界:

  1. Workflow:程序预定义主要控制路径;
  2. Agent:模型拥有部分动态控制流决策权;
  3. Harness:把模型、上下文、能力、状态、安全和可观测性组成完整运行环境;
  4. Runtime:真正执行并提交系统事实的内核;
  5. Agent Loop:模型决策与真实 Observation 之间的反馈循环;
  6. Hybrid Design:模型负责开放决策,程序保证系统不变量。

Agent 可以决定下一步“想做什么”;Runtime 决定这件事“能不能做、怎么做,以及做完之后哪些事实真正成立”。


16. Sources ​

  1. Anthropic — Building effective agents
    https://www.anthropic.com/engineering/building-effective-agents

  2. Anthropic — Scaling Managed Agents: Decoupling the brain from the hands
    https://www.anthropic.com/engineering/managed-agents

  3. Anthropic — Building agents with the Claude Agent SDK
    https://www.anthropic.com/engineering/building-agents-with-the-claude-agent-sdk

  4. OpenAI Agents SDK — Agents
    https://openai.github.io/openai-agents-python/agents/

  5. OpenAI Agents SDK — Running agents
    https://openai.github.io/openai-agents-python/running_agents/

  6. OpenAI Agents SDK — Testing
    https://openai.github.io/openai-agents-python/testing/

可选源码深挖:Codex Turn Lifecycle / Granularity

Codex 源码对照:Turn Lifecycle / Granularity ​

要回答的问题 ​

Codex 里的一个 Turn 到底对应多大的 Runtime 生命周期?为什么不能把 Codex Turn 直接等同于本知识库 canonical Turn?

精确定位 ​

Tag: rust-v0.150.1

File:

Symbols:

  • run_turn(...)
  • run_pre_sampling_compact(...)
  • run_sampling_request(...)
  • SamplingRequestResult

最小调用链 ​

text
run_turn(input)
→ run_pre_sampling_compact(...)
→ build / record context
→ loop
   → clone_history().for_prompt(...)
   → run_sampling_request(...)
   → model ResponseItem(s)
   → tool execution when requested
   → tool result enters history
   → post-sampling token status
   → optional mid-turn compact
   → continue when follow-up is required
→ final assistant message / stop condition
→ return last_agent_message

Source Fact ​

run_turn() 的源码注释明确写出:每次 sampling 后,模型可能返回 function call 或 assistant message;如果返回 function call,Runtime 执行工具并把结果放进 下一次 sampling request;当只得到 assistant message 且没有 follow-up 时,Turn 才结束。

因此 Codex 的一个 run_turn() 可以包含多次:

text
model sampling
→ tool execution
→ tool result
→ next sampling

这与本知识库 canonical Turn 的粒度不同。本知识库 canonical Turn 是一次逻辑上的:

text
Model Decision
→ Action / Observation

一个 Codex Turn 因而可能覆盖多个 canonical logical Turn;同时也不能用 sampling count 强行做 1:1 映射,因为一次 model response 可以包含多个 output item。

Design Inference ​

不按类型名直接建立概念映射,应比较 lifecycle、ownership 和 state transition。

对 Harness 来说,更稳定的 invariant 是:一次 user-facing operation 内部可以包含多个 model decision / tool continuation cycle;Runtime 必须拥有循环推进、终止条件、tool result 回灌和 budget 检查,而不是让模型文本决定“Turn 是否已经完成”。

Codex-specific Trade-off ​

Codex rust-v0.150.1 把这段多-sampling 生命周期命名为 Turn,并在同一个 run_turn() 中处理 pending input、hooks、compaction 和 continuation。这是 Codex 当前的产品/runtime 组织方式,不代表所有 Harness 都应把同样的粒度叫 Turn。

Shadow Harness Delta ​

  1. 保留 canonical Turn = one logical Model Decision → Action/Observation,不要为迎合 Codex 类型名改 Domain Language。
  2. 在 Run/Task 层增加一个能容纳多个 canonical Turn 的 outer loop owner。
  3. trace 中分别记录 run_id / canonical_turn_id / model_sample_id / tool_call_id,避免把一次 user request 内多次 sampling 压成一个不可分析事件。
  4. 增加测试:sample → tool → observation → sample → final 时,只能在最终 completion 条件成立后关闭 Run。

不要过度推导 ​

这段源码不能证明“Codex Turn 就是行业标准 Turn”,也不能证明每次 sampling 必然只产生一个 tool call。源码注释反而明确提示一次 sampling 可能返回多个 item。

完整映射见 OpenAI Codex Source Map。

可选源码深挖:Codex Public SDK Boundary

Codex 源码对照:Public SDK Boundary ​

要回答的问题 ​

内部 Session / Turn / Tool / Compaction / Sandbox 很复杂时,为什么外部 SDK 不应该把这些内部对象全部暴露出去?

精确定位 ​

Tag: rust-v0.150.1

File:

Symbols:

  • Codex.startThread(...)
  • Codex.resumeThread(...)
  • Thread.run(...)
  • Thread.runStreamed(...)
  • Turn
  • ThreadOptions
  • CodexExec.run(...)

最小调用链 ​

text
Codex.startThread(options)
        or
Codex.resumeThread(id, options)
→ Thread
→ run(input, turnOptions)
→ runStreamedInternal(...)
→ CodexExec.run(... threadId / sandboxMode / approvalPolicy / ...)
→ thread / item / turn events
→ Turn { items, finalResponse, usage }

Source Fact ​

TypeScript SDK 的 public surface 主要围绕 Codex → Thread → run/runStreamed → Turn result。resumeThread(id, ...) 通过 thread id 恢复;ThreadOptions 暴露 sandboxMode、approvalPolicy、working directory、network access 等配置。CodexExec.run() 再把这些稳定参数翻译成 CLI argument / config(例如 --sandbox、resume <threadId>、approval_policy=...)并启动 Codex executable,而不会要求 SDK 调用方直接操纵内部 SessionState、TurnState、ToolRegistry、compaction loop 或 process manager。

Design Inference ​

Harness 内部复杂度与 public API 复杂度应解耦。 外部 API 应围绕稳定业务语义暴露少量对象,把易变的 Runtime plumbing 隐藏在 abstraction boundary 后面。

Codex-specific Trade-off ​

Thread / Turn 的命名、TypeScript API 形状、sandboxMode 的三个枚举值,以及通过本地 Codex executable 驱动 SDK,都是 Codex 当前产品选择,不是通用 Harness 标准。

Shadow Harness Delta ​

  1. 为 Shadow Harness 增加独立 public facade,不直接向调用方暴露内部 Scheduler / Store / ToolExecutor 对象。
  2. 对外优先保留稳定操作:start/resume/run/stream/cancel + 明确 configuration。
  3. 内部 domain schema 升级时,增加 adapter test,保证 public API 不因内部状态对象重构而大面积变化。

不要过度推导 ​

SDK 暴露 Thread 和 Turn 不能反向证明内部 Runtime 必须使用同名对象,也不能证明 sandboxMode 足以表达 Codex 内部所有 permission / approval / sandbox 状态。

完整映射见 OpenAI Codex Source Map。

自测 ​

  1. 模型输出“apply_patch 已成功”时,为什么这句话还不能成为系统事实?从 Action Intent 到 Observation,中间至少需要哪些 Runtime 步骤?
  2. 同一个代码修复需求,什么条件下适合写成固定 Workflow,什么条件下才需要让 Agent 根据 Observation 决定下一步?
  3. 在 User → Model → Action → Runtime → Observation 中,哪一层拥有真实副作用的提交权?如果把这项权力交给模型文本,会破坏什么边界?

AI Engineering · Agent · Harness · Backend Systems