Harness 是模型能力的工程外骨骼
Harness 不是另一个“超级 Agent”,也不是 UI 框架。它提供 Runner 与 Loop、Model Adapter、Tool Registry/Executor、Context 与状态、Policy/Approval、Tracing/Eval Hook 和人工中断。具体 Agent 则定义任务目标、领域工具、业务完成条件和风险语义。
OpenAI Agents SDK 将 agent loop、tools、handoffs、guardrails、sessions、human-in-the-loop 和 tracing 作为共性能力;Anthropic 的工程指南也提醒框架抽象可能遮蔽底层 Prompt 与响应。结论不是“必须自研”,而是任何采用都要知道哪些控制被框架拥有、哪些语义会在适配中丢失。
三种常见 Harness 形态
进程内 SDK/Library
Runner 与业务服务同进程,延迟低、调试直接、可使用原生语言类型。代价是语言与发布耦合,策略和观测容易由各团队分叉。适合单团队或尚在验证共性边界的阶段。
独立 Agent Runtime Service
通过 API 接收 Run,统一模型、Tool 执行、状态和观测。它降低多语言接入成本,却引入网络、排队、租户隔离和跨服务 Debug;若把领域 Prompt 与业务编排都塞入中心服务,会形成新的单体。
控制面 + 数据面
控制面管理 Registry、策略、配置、评测和版本,数据面按数据域与延迟要求运行。适合多业务线和强隔离场景,但需要配置分发、兼容协议、租户治理和组织 Owner,不能在需求尚未稳定时一步到位。
| 变量 | Library | Runtime Service | 控制面 + 数据面 |
|---|---|---|---|
| 团队/语言 | 少、相对统一 | 多语言接入 | 多业务域、多执行环境 |
| 治理一致性 | 靠包版本 | 集中执行 | 集中策略、分布执行 |
| 主要风险 | 版本碎片 | 中心瓶颈与领域侵入 | 平台复杂度和协议演进 |
稳定接口围绕责任边界
不要追求“所有模型一个完全相同接口”。稳定层应定义业务需要的最小语义,并通过能力探测保留差异:结构化输出、并行 Tool、reasoning、缓存、流事件和取消不一定跨 Provider 等价。
interface ModelAdapter {
capabilities(): Promise<ModelCapabilities>;
stream(request: CanonicalModelRequest): AsyncIterable<ModelEvent>;
}
interface ToolExecutor {
authorize(call: ToolCall, principal: Principal): Promise<PolicyDecision>;
execute(call: ToolCall, attempt: AttemptContext): Promise<ToolReceipt>;
}
interface RunStore {
append(expectedVersion: number, events: RunEvent[]): Promise<number>;
load(runId: string): Promise<RunSnapshot>;
}
interface ApprovalPort {
request(decision: BoundActionPreview): Promise<ApprovalRef>;
}
接口中的核心对象应可版本化、可序列化并带稳定 ID。异常也要规范化但保留 Provider 原因:统一成 rate_limited 有助于策略,原始状态码、request ID 和重试提示仍要进入受控 Trace。
Hook 是观测和扩展点,不是隐式业务流程
Run/Step/Model/Tool 前后事件可用于 Trace、指标、成本计量、Eval 抽样和审计。OpenAI Agents SDK 的 Agent 与 Runner 生命周期事件就是一种实现。但 Hook 的顺序、并发、超时和失败语义必须明确。
关键状态变更不能依赖 best-effort Hook:例如 Tool 成功回执必须与状态提交形成可靠协议,不能寄希望于 afterTool 异步写库。插件也不能默认获得主进程全部密钥、上下文和网络权限。
全局、租户、Agent 和 Run 配置分层
建议优先级从低到高为平台默认、环境、租户、Agent 版本、Run 覆盖,但安全底线、数据驻留和最大权限不可被下层覆盖。每个 Run 固化有效配置快照与来源,动态变更默认只影响新 Run;在途 Run 是否迁移需要显式策略。
配置快照应记录模型别名解析结果、Prompt 版本、Tool Schema、预算、Feature Flag 和策略版本,秘密只记录引用或指纹。没有快照的 Trace 无法解释“同样输入为何昨天与今天路径不同”。
Harness 失败的常见原因
| 失败 | 后果 | 反向措施 |
|---|---|---|
| 过早提炼最小公分母 | Provider 优势与错误语义丢失 | Canonical Core + capability extension |
| 把业务逻辑放进通用回调 | Owner 不清、升级回归困难 | 领域服务/Agent 持有业务状态机 |
| Tool/Prompt 只有配置没有版本 | 历史 Run 无法复现 | 不可变版本、快照、兼容期 |
| 平台强制唯一 Golden Path | 团队绕过平台形成 Shadow Runtime | 逃逸口有边界,并用采用证据迭代 |
框架升级前应以行为契约回归,而不是只看编译通过。模型消息顺序、Tool 结果回注、终止判定、取消传播和错误分类的细小变化,都可能改变 Agent 行为。
用代表性任务验证 Harness
最小基准应覆盖单 Agent 多 Tool、结构化输出、审批等待、长任务恢复、Subagent、流式取消和 Provider 替换。故障注入包括超时、重复回执、限流、状态冲突、Hook 失败和旧版本 Run 恢复。
契约测试检查的是语义不变量:同一个 Tool call ID 只提交一次业务效果;取消能传播到尚未开始的节点;策略拒绝不能被重试变成允许;Trace 的父子关系完整;恢复后预算和配置版本不丢失。Provider 替换只在任务成功和关键语义保持时才算成功。
平台采用还要看开发者证据:从空项目到首个可观察 Agent 的时间、逃逸 API 使用率、升级失败率、排障平均时间和各团队重复实现下降。P8 级平台决策不是组件图,而是一份明确 Owner、迁移批次、兼容承诺、SLO 与停用条件的 RFC。
主动提取:为什么“所有 Provider 都能返回 text”不是一个充分的 Model Adapter?
架构判断:何时应该从 SDK 演进为 Runtime Service?
资料与事实边界
以下以官方文档、标准、论文和原始工程资料为主。厂商 API、价格、限额与模型能力会变化,落地时仍需按目标版本做能力探测和回归。
- Anthropic:Building Effective AI Agents — 框架抽象的收益、遮蔽风险和保持简单的工程建议
- OpenAI Agents SDK for TypeScript — Runner、Tool、Handoff、Guardrail、Session 和 Tracing 的当前官方能力
- OpenAI Agents SDK:Running agents — Agent loop、RunState、终止和运行配置的官方语义
- OpenAI Agents SDK:Tracing — Run、Agent、Generation、Tool 与 Handoff Trace 的官方实现边界