异构 Agent 集成在 LobeHub 中的实现
学习目标
理解 LobeHub 如何通过 HeterogeneousAgents 包将 Claude Code、Codex 等外部 CLI Agent 封装为平台可控的 Agent,包括本地子进程、远程平台双模式、事件流适配和 AskUserQuestion 桥接。
前置知识
本章涉及异构 Agent 集成的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 LobeHub 的具体实现。
项目实践
双模式注册表
LobeHub 将异构 Agent 分为本地 CLI 和远程平台两类:
// 伪代码:本地 CLI Agent 配置const HETEROGENEOUS_AGENT_CONFIGS = [ { command: "claude", iconId: "ClaudeCode", title: "Claude Code", type: "claude-code" }, { command: "codex", iconId: "Codex", title: "Codex", type: "codex" },];
// 伪代码:远程平台 Agent 配置const REMOTE_HETEROGENEOUS_AGENT_CONFIGS = [ { title: "OpenClaw", type: "openclaw" }, { title: "Hermes", type: "hermes" }, { title: "Amp", type: "amp" }, { title: "OpenCode", type: "opencode" },];本地 CLI Agent 通过 command 字段指定终端命令,作为 Desktop 子进程运行。远程平台 Agent 通过 agentNotify.notify 回传事件。
事件适配器注册表
// 伪代码:注册表const registry: Record<string, AgentRegistryEntry> = { "claude-code": { createAdapter: () => new ClaudeCodeAdapter() }, "codex": { createAdapter: () => new CodexAdapter() },};每个适配器实现 AgentEventAdapter 接口,负责 CLI 输出到平台事件的转换。
CLI 子进程事件流
本地 CLI Agent 的 spawn 流程:
AskUserQuestion 桥接
LobeHub 的 Claude Code 和 Codex 都支持 AskUserQuestion 工具。在 Desktop 环境中,需要将 CLI 的交互式提示桥接为 UI 弹窗:
// 伪代码:AskUserQuestion 桥接class AskUserMcpServer { // 当 CLI 调用 AskUserQuestion 时 async handleAskUserQuestion(params) { // 1. 向 UI 发送选择弹窗 const response = await ui.showAskUserDialog(params); // 2. 将用户选择注入为工具结果 return this.submitToolResult(response); }}关键实现细节:Claude Code 在无交互模式下会 decline AskUserQuestion。LobeHub 的解决方案是将 AskUserQuestion 作为 MCP 工具调用而非终端交互,通过 AskUserMcpServer 接收和响应。
子 Agent 调度
在 GeneralChatAgent 的 tool_result phase 中,LobeHub 支持子 Agent 调度:
// 伪代码:子 Agent 调度case "tool_result": { if (stop && data?.state) { switch (data.state.type) { case "execSubAgent": return { type: "exec_sub_agent", payload: data.state.task }; case "execSubAgents": return { type: "exec_sub_agents", payload: data.state.tasks }; case "execClientSubAgent": return { type: "exec_client_sub_agent", payload: data.state.task }; case "execClientSubAgents": return { type: "exec_client_sub_agents", payload: data.state.tasks }; } }}四种调度模式:
execSubAgent/execSubAgents:服务端子 AgentexecClientSubAgent/execClientSubAgents:客户端(Desktop)子 Agent
文件变更追踪
对于 Codex 等编码 Agent,LobeHub 实现文件变更追踪:
// 伪代码:Codex 文件变更追踪class CodexFileChangeTracker { recordBaseline(dir: string): Promise<void>; // 启动时记录快照 getChangedFiles(): FileChange[]; // 获取变更文件 getFileDiff(path: string): string; // 获取文件 diff}问题与规避
| 问题 | 场景 | 规避策略 |
|---|---|---|
| Windows .cmd shim | spawn("claude") 在 Windows 上失败 | 检测平台,Windows 使用 claude.cmd |
| CLI 输出缓冲 | 事件延迟到达 UI | 使用 --streaming 标志 |
| 子进程异常退出 | 任务静默失败 | 监控 exitCode,发送 terminal_error 事件 |
| AskUserQuestion auto-decline | Claude Code 无交互模式 decline | 使用 MCP 工具模式而非终端交互 |
设计取舍
为什么区分 server-side 和 client-side 子 Agent?
Server-side 子 Agent 在服务器进程执行,适用于不需要桌面能力的场景。Client-side 子 Agent 在 Desktop 执行,可以访问本地文件系统、本地 CLI 工具等。
典型场景:Codex 需要在本地仓库目录运行,使用 client-side 子 Agent。服务端 Agent(如分析任务)使用 server-side 子 Agent。
为什么远程平台 Agent 不使用子进程?
远程平台 Agent(如 OpenClaw、Hermes)运行在用户自己的设备上,通过 lh connect 命令绑定到 LobeHub 账户。它们通过 agentNotify.notify 回传事件,而非子进程。
优势:用户设备上的 Agent 可以持续运行,不依赖 LobeHub 服务器在线。
参考来源
- LobeHub HeterogeneousAgents —
packages/heterogeneous-agents/src/完整实现 - LobeHub AskUserMcpServer —
packages/heterogeneous-agents/src/askUser/AskUserMcpServer.ts - 异构 Agent 集成 — 通用异构 Agent 原理