跳转到内容

异构 Agent 集成:CLI 工具的平台化封装

异构 Agent 集成:CLI 工具的平台化封装

1. 问题定义

市场上已有成熟的 CLI Agent 工具(Claude Code、Codex、OpenCode 等),它们各自有独特的能力和优势。一个 Agent 平台如果只支持自研 Agent,就无法利用这些外部工具的能力。异构 Agent 集成的目标是:将不同来源、不同运行方式的 Agent 统一封装为平台可控的 Agent,使用统一的事件模型进行交互和管理。

2. 两种运行模式

本地 CLI 模式

CLI Agent 作为 Desktop 子进程运行,通过 Electron IPC 与主进程通信。典型代表:

Agent命令运行方式
Claude CodeclaudeNode.js 子进程
CodexcodexNode.js 子进程

远程平台模式

远程 Agent 通过通知机制(如 agentNotify.notify)回传事件,不运行在本地。典型代表:

Agent类型通信方式
OpenClawremoteagentNotify
HermesremoteagentNotify
AmpremoteagentNotify
OpenCoderemoteagentNotify

3. 注册表模式扩展

异构 Agent 通过注册表模式管理,新增 Agent 只需注册一个 entry,无需修改其他代码:

// 伪代码:注册表
const registry: Record<string, AgentRegistryEntry> = {
"claude-code": { createAdapter: () => new ClaudeCodeAdapter() },
"codex": { createAdapter: () => new CodexAdapter() },
// 新增 Agent 只需添加一行
// "kimi-cli": { createAdapter: () => new KimiCLIAdapter() },
};

优势:每个 Agent 的适配器完全独立,互不影响。

4. CLI 子进程事件流

本地 CLI Agent 的核心挑战是解析其终端输出并转换为结构化事件流:

事件类型

事件类型说明数据
stream_startAgent 开始输出
stream_chunk流式文本块text: string
tool_call工具调用开始name, arguments
tool_result工具调用结果name, result
subagent_spawn子 Agent 生成name, type
agent_endAgent 完成exitCode
terminal_error终端错误error

输出解析策略

CLI 工具的输出格式各异:

  • JSON 行模式:每行一个 JSON 对象(如 Codex 的 --output-format json
  • ANSI 文本模式:解析终端 ANSI 转义序列和文本模式
  • 混合模式:部分结构化 + 部分纯文本

陷阱:CLI 工具更新可能改变输出格式,导致解析器失效。对策:

  1. 使用版本化的解析器(如 claude-code-v1claude-code-v2
  2. 在解析器中设置 fallback 逻辑
  3. 通过 E2E 测试验证解析器对 CLI 新版本的兼容性

5. AskUserQuestion 桥接

CLI Agent 在执行过程中可能需要用户交互(如选择方案、确认操作)。在桌面应用中,需要将 CLI 的交互式提示桥接为 UI 弹窗:

陷阱:某些 CLI Agent(如 Claude Code)在无人交互模式下会 decline AskUserQuestion。需要特殊处理:在 Desktop 环境中将 AskUserQuestion 桥接为外部工具调用而非内部交互。

6. 事件适配器模式

每个 CLI Agent 实现一个 AgentEventAdapter,负责双向转换:

// 伪代码:适配器接口
interface AgentEventAdapter {
// CLI 输出 → 平台事件
parseOutput(line: string): HeterogeneousAgentEvent[];
// 平台指令 → CLI 输入
formatInput(event: PlatformEvent): string;
// 获取 Agent 显示名称
getDisplayName(): string;
}

优势

  • 新增 Agent 只需实现适配器,不修改核心逻辑
  • 每个适配器独立测试
  • CLI Agent 与平台 Agent 共享同一事件处理代码路径

7. 文件变更追踪

对于编码类 CLI Agent(如 Codex),需要追踪其对文件系统的修改:

// 伪代码:文件变更追踪
class CodexFileChangeTracker {
private baseline: Map<string, FileHash>;
// 启动时记录文件快照
recordBaseline(dir: string): Promise<void>;
// 周期性检查变更
getChangedFiles(): FileChange[];
}

用途

  • 在 Agent 任务完成后展示代码 diff
  • 检测意外修改(如删除重要文件)
  • 支持撤销 Agent 的更改

8. 陷阱与对策

陷阱后果对策
CLI 输出缓冲导致事件延迟UI 显示延迟使用 --streaming 标志或禁用输出缓冲
子进程异常退出任务静默失败监控 exitCodestderr,发送错误事件
CLI 版本不兼容解析器失效版本化解析器 + E2E 测试
Windows .cmd shim子进程启动失败检测平台并使用 npm.cmd 而非 npm
长时间运行无输出超时误判使用心跳检测而非固定超时

9. 服务端 Adapter 合约模式(Paperclip 贡献)

Paperclip 在异构 Agent 集成的基础上,提出了更正式的服务端 Adapter 合约,将 Adapter 的核心能力分离为三个独立维度:

9.1 三分离架构

维度职责接口
执行(Execute)启动/恢复 Agent 会话,返回执行结果execute(agent, config, context) → ExecutionResult
会话编解码(Session Codec)序列化/反序列化 Agent 会话状态serialize(session) / restore(serialized)
UI 解析器(UI Parser)解析 Agent 输出为 UI 可渲染格式./ui-parser export from adapter package

这种三分离使得:

  • 执行引擎只关心如何启动和监控 Agent
  • 会话状态可以跨心跳恢复(Agent 不丢失上下文)
  • UI 渲染独立于执行逻辑,不同 Adapter 的输出可以统一展示

9.2 外部 Adapter Plugin 加载

Paperclip 的 Adapter 以独立 npm 包形式存在,通过动态 ESM import 加载:

// 伪代码:外部 Adapter 加载
async function loadExternalAdapter(packageName: string) {
const modulePath = resolvePackageEntry(packageName);
const mod = await import(modulePath);
const adapterModule = mod.createServerAdapter();
// 验证最小合同
if (!adapterModule.type) {
throw new Error("Adapter module missing 'type' field");
}
return adapterModule;
}

关键设计

  • Server 核心零硬编码 Adapter 导入(plugin loader 中无 import claude 等)
  • ESM cache busting 支持热重载(开发时不需要重启 server)
  • UI Parser 通过 package.json exports["./ui-parser"] 声明,server 提取源码并在运行时注入前端

9.3 模型配置文件(Model Profiles)

每个 Adapter 声明自己的模型列表和配置文件:

// 伪代码:Adapter 模型声明
const models = [
{ id: "claude-sonnet-4-6", provider: "anthropic", contextWindow: 200000 },
{ id: "claude-opus-4-7", provider: "anthropic", contextWindow: 200000 },
];
const modelProfiles = {
"fast": { model: "claude-sonnet-4-6", maxTokens: 4096 },
"smart": { model: "claude-opus-4-7", maxTokens: 8192 },
"default": { model: "claude-sonnet-4-6", maxTokens: 4096 },
};

这使得同一 Agent 可以在不同任务中使用不同模型,通过 profile key 切换。

9.4 环境检测

每个 Adapter 提供 testEnvironment() 方法,在安装时检测运行环境是否满足:

检测项说明
CLI 是否安装claude 命令是否在 PATH 中
API Key 是否配置环境变量或配置文件中是否存在 Key
版本是否兼容CLI 版本是否满足最低要求

10. 参考来源

  • LobeHub HeterogeneousAgentspackages/heterogeneous-agents/src/ 完整实现
  • OpenAI Codex CLI — CLI Agent 的输出格式和事件模型
  • Anthropic Claude Code — 工具调用和 AskUserQuestion 机制
  • Paperclip Adapter Registry — server/src/adapters/registry.ts(27KB)
  • Paperclip Adapter Plugin Loader — server/src/adapters/plugin-loader.ts
  • Paperclip Adapter Utils — packages/adapter-utils/src/types.ts