Meta-Harness 核心 7 设计
把 N 个 agent 框架(Claude Code / Codex / Pi / OpenAI Agents)压成"只需实现 run_turn、吐标准事件"的薄适配器
图 · Meta-Harness 内部:Runner → ExecutorAdapter → Executor 事件流,以及子进程隔离、native/sdk 双形态与横切统一层
下面 7 条是 Omnigent 区别于普通 agent 框架的核心设计决策。每条都给出具体机制和代码位置。
1统一的 Executor 契约
整个 meta-harness 的基石 —— 一个抽象接口,归一化所有异构后端。
class Executor:
async def run_turn(
messages: list[Message], # 异构 JSON dict,带 "role"
tools: list[ToolSpec], # JSON-Schema 工具定义
system_prompt: str,
config: ExecutorConfig,
) -> AsyncIterator[ExecutorEvent]
无论底层是 Claude SDK、codex CLI 还是 OpenAI Agents:
- 输入统一归一化成
(messages, tools, system_prompt, config) - 输出统一归一化成一条
ExecutorEvent事件流
2归一化的事件词汇表
后端再异构,都得把原生对象翻译成这套固定事件 —— 所有 harness 必须"说同一种话"。
| 事件 | 含义 |
|---|---|
TextChunk(text) | 流式文本增量 |
ReasoningChunk(delta, event_type) | 思考过程(Claude reasoning / o1) |
ToolCallRequest(name, args, metadata={call_id}) | 模型要调工具 |
ToolCallComplete(name, status, result, call_id) | 工具执行完(由 Session 发出) |
TurnComplete(response, usage, continue_turn) | 回合结束 + token 用量 |
ExecutorError(message, retryable) | 出错(区分可重试 / 永久) |
run_turn 必须以恰好一个 TurnComplete 或 ExecutorError 结尾;call_id 在 request / complete 之间镜像,用来配对。Claude SDK 把 AssistantMessage/ResultMessage 翻成这些,codex 把它的协议翻成这些 —— 翻译都发生在各自 executor 内部,Session 层零感知。3能力声明(协商差异,不抹平)
后端能力天差地别,用一组布尔标志让上层据此决定控制流。
supports_streaming() # 是否流式
handles_tools_internally() # 自己跑工具循环?(Claude SDK=True)
supports_live_message_queue() # 能否回合中途插话
supports_tool_boundary_interrupt() # 能否在工具边界打断
max_context_tokens() # 上下文窗口
supports_live_message_queue=True 支持中途 steering,不支持的后端就降级处理。4子进程隔离 — 每对话一个 harness 进程
工程上最硬核的一条。契约:一个对话 = 一个 harness 子进程,独立 Unix socket,懒启动。
父进程 ──spawn──> python -m omnigent.runtime.harnesses._runner
--harness codex --socket conv-<id>.sock --parent-pid N
子进程内:import 模块 → create_app() → uvicorn 绑 UDS
父进程 ──httpx over UDS──> 子进程
为什么不在进程内跑?
- 状态隔离:每对话独立 executor / SDK client,互不串
- 崩溃隔离:一个 harness 崩了只死一个对话
- 干净回收:进程退出即彻底清理,无内存泄漏
- native CLI 1:1 对应:每个终端 TUI session 配一个子进程
配套生命周期管理
父死子死:prctl(PDEATHSIG) + 看门狗线程每秒探活 空闲 30min 回收 reaper 启动孤儿清扫:lsof 找占 socket 的 pid → SIGTERM → SIGKILL /model 改模型 → 检测 env 变化 → 重启子进程
5ExecutorAdapter — 把任意 Executor 包成统一 REST 服务
所有 harness 的 create_app() 长一个样,只有 executor_factory 不同。
def create_app() -> FastAPI:
adapter = ExecutorAdapter(executor_factory=_build_xxx_executor)
return adapter.build() # 四种 harness 只有 factory 不同
Adapter 暴露统一 REST 协议(单个 POST /v1/sessions/{id}/events 端点,用 type 区分):
message / interrupt / tool_result / approval / policy_verdict。
它把 HTTP 请求翻成 run_turn() 调用,再把 ExecutorEvent 翻成 SSE 事件。
关键桥接 — 装在 executor 上的 3 个稳定回调(所有 harness 共用)
executor._tool_executor = self._stable_tool_executor # 工具分发桥
executor._policy_evaluator = self._stable_policy_evaluator # 策略往返桥
executor._elicitation_handler = self._stable_elicitation_handler # 审批回调
这三个桥就是横切能力(设计 7)接入每个 harness 的入口。
6native vs sdk 两种 harness 形态
同一个抽象下两种截然不同的接入方式 —— 这是 Omnigent 一个很聪明的设计。
| SDK harness claude-sdk / codex / pi / openai-agents | native harness claude-native / codex-native | |
|---|---|---|
| 模型怎么跑 | 子进程内启动 SDK | CLI/TUI 已在终端跑着,不启动 |
| run_turn 做什么 | 驱动 SDK,流式产事件 | 用 tmux send-keys 注入消息,立即返回 |
| 输出从哪来 | SDK 流式 | transcript forwarder 读 Claude 的 JSONL / Codex 的 rollout |
| 用途 | 后台 / 编程式 | UI 里实时围观甚至接管 |
*_native_bridge.py + *_native_app_server.py + hooks 桥接真实终端 —— 这就是为什么你能"看着 Codex 干活并随时接管"。7横切统一层 — 工具 / 策略 / 多 agent / 成本
抽象不只在接口:这些能力在 server / runner 端统一注入,与底层框架无关,对所有 harness 一致生效。
| 能力 | 机制 | 位置 |
|---|---|---|
| 统一工具 | ToolManager 单一注册表:sys_* 内置、MCP、子 agent、用户 Python 函数,都继承 Tool ABC,以 OpenAI schema 喂给任意 harness;调用经 _stable_tool_executor → ctx.dispatch_tool → server 执行 | tools/manager.py |
| 统一策略 | PolicyEngine 在 server 端,harness 只是瘦中继。4 个评估点(输入 / 工具调用 / 工具结果 / 输出)。native CLI 用同步 hook(Pre/PostToolUse)拦截,翻成同一套 server 格式 | runtime/policies/engine.py · native_policy_hook.py |
| 统一多 agent | sys_session_send / create 创建子会话(runner task),子会话可用与父会话不同的 harness。Claude 编排器派活给 Codex 子 agent,异步,经 inbox drain 收结果 | tools/builtins/spawn.py |
| 统一成本/状态 | token 用量归一化进 PolicyEngine.usage(input/output/cache/cost);AdvisorVerdict 作为 conversation label 持久化;所有 harness 读写同一份 ConversationStore 历史 | cost_plan.py · stores/ |
一句话总结
meta-harness 的核心 = 归一化的 Executor.run_turn() → ExecutorEvent 契约(① ② ③)
+ 每对话子进程隔离(④)+ 统一的 Adapter / REST 包装(⑤)
+ native / sdk 双形态接入(⑥)+ 工具 / 策略 / 多 agent / 成本横切统一(⑦)。
底层 agent 框架被压缩成"只需实现 run_turn、吐标准事件"的薄适配器,其余一切(工具、策略、编排、成本、跨设备、协作)都由 Omnigent 统一层提供 —— 这就是"把 N 个 agent 框架变成可互换零件"的工程实现。