跳转到内容

异构 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:服务端子 Agent
  • execClientSubAgent / execClientSubAgents:客户端(Desktop)子 Agent

文件变更追踪

对于 Codex 等编码 Agent,LobeHub 实现文件变更追踪:

// 伪代码:Codex 文件变更追踪
class CodexFileChangeTracker {
recordBaseline(dir: string): Promise<void>; // 启动时记录快照
getChangedFiles(): FileChange[]; // 获取变更文件
getFileDiff(path: string): string; // 获取文件 diff
}

问题与规避

问题场景规避策略
Windows .cmd shimspawn("claude") 在 Windows 上失败检测平台,Windows 使用 claude.cmd
CLI 输出缓冲事件延迟到达 UI使用 --streaming 标志
子进程异常退出任务静默失败监控 exitCode,发送 terminal_error 事件
AskUserQuestion auto-declineClaude 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 原理