开发智能体适配器
为 OpenAgents 智能体连接器开发新的编码智能体适配器:BaseAdapter 契约、registry 注册、进程派生、配置、测试,以及一个最小的端到端示例。
开发智能体适配器
**适配器(adapter)**是智能体连接器(packages/agent-connector/,即 agn CLI 背后的 Node.js 包)中负责驱动某一种编码智能体的组件——Claude Code、Codex、Aider、Goose 等等。守护进程为每个已配置的智能体派生一个适配器;适配器轮询工作空间中发给该智能体的消息,驱动智能体 CLI,并把结果流式回传。
本指南介绍为一个新的编码智能体添加支持所需的全部内容。
适配器如何运行 (How it runs)
agn up
└─ daemon (src/daemon.js)
└─ one adapter per workspace-connected agent (src/adapters/*.js)
├─ polls GET /v1/events (messages targeted at this agent)
├─ spawns the coding-agent CLI (per message, or persistent)
└─ posts POST /v1/events (thinking / status / todos / chat)基类 BaseAdapter(src/adapters/base.js)已经实现了整个外层循环——加入工作空间、30 秒心跳、自适应消息轮询(活跃时 2s → 温态 5s → 冷态 15s)、按事件去重、带排队的按频道分发、控制事件轮询器(stop/restart/mode/skill 等动作,工作进行中每 250ms 轮询一次)以及优雅断开。子类只需要提供与它所驱动的编码智能体相关的部分。
BaseAdapter 契约 (Contract)
适配器是继承 BaseAdapter 的普通 CommonJS 类。构造函数接收:
{ workspaceId, channelName, token, agentName, endpoint,
agentEnv, agentType, workingDir, onStatus }必须实现——只有这一个方法:
| 方法 | 用途 |
|---|---|
async _handleMessage(msg) | 处理一条收到的消息。若不覆盖此方法,基类会直接抛出异常。msg.content 是消息文本;msg.sessionId 是频道。 |
可选覆盖:
| 方法 | 用途 |
|---|---|
preflight() | 守护进程在加入工作空间之前调用。返回 { ok: true } 或 { ok: false, reason, message }(例如 reason: 'runtime_missing')——preflight 失败时,错误信息会显示在 agn status 中,而不是陷入崩溃重启循环。 |
stop() | 扩展它来清理你维护的所有子进程(遵循 super.stop() 的语义:设置基类的标志位)。 |
_onControlAction(action, payload) | 扩展它来处理额外的控制事件(基类已处理 status、routines、skill.install、skill.uninstall)。 |
继承的发送方法——在 _handleMessage 中用它们把结果流式回传;每个方法对应一种工作空间 UI 认识的 message_type:
| 方法 | 渲染为 |
|---|---|
sendResponse(channel, content) | 最终的聊天回答 |
sendThinking(channel, content) | 可折叠的“中间步骤” |
sendStatus(channel, content, extraMeta) | 工具调用 / 进度提示行 |
sendTodos(channel, todos) | 待办组件 |
sendError(channel, error) | 会话中的一条错误消息 |
两种进程模型 (Process models)
两种模型在代码库中都存在;选择与你的智能体 CLI 匹配的那一种。
持久进程——src/adapters/claude.js。为每个频道维护一个长期存活的 claude -p --input-format stream-json 进程,后续消息通过 stdin 喂入,并带有空闲超时与看门狗。这省去了每条消息的启动时间,但需要更多的簿记工作(进程表、进程死亡后重启、stop() 清理)。
每消息一个进程——src/adapters/codex.js 以及其他大多数适配器(cursor、gemini、opencode、aider、goose、copilot、cline、amp、hermes、deepseek、pi、mini-swe-agent)。每条消息都派生一次全新的 CLI 运行(codex exec --json --full-auto …),进程退出即完成。对话的连续性来自按频道持久化的 session/thread ID,下一次派生时传回(例如 Codex 的 --resume)。
也有完全不启动本地进程的适配器(kimi 通过 LlmDirectAdapter 直接调用 OpenAI 兼容的 HTTP API),但一个新的基于 CLI 的智能体几乎总是上面两种模型之一。
派生智能体进程 (Spawning)
遵循现有适配器的约定——它们凝结了大量来之不易的 Windows 与 Unix 修复经验:
const { spawn } = require('child_process');
const { getEnhancedEnv } = require('../paths');
const proc = spawn(cmd[0], cmd.slice(1), {
cwd: this.workingDir, // the agent's project directory
env: getEnhancedEnv(this.agentEnv),
stdio: ['pipe', 'pipe', 'pipe'],
windowsHide: true,
detached: process.platform !== 'win32', // Unix: killable process group
shell: process.platform === 'win32', // Windows: resolve .cmd shims
});- 工作目录。 绝不要用
process.cwd()。守护进程会传入workingDir,来自智能体配置的path(agn create <name> --path <dir>,默认为你运行agn create时所在的目录),兜底为~/.openagents/workspaces/<agent-name>。每次派生进程都把它作为cwd传入。 - 环境变量。
getEnhancedEnv()(src/paths.js)会把探测到的 bin 目录(nvm/fnm/volta、Homebrew、npm 全局目录、pipx、隔离的~/.openagents/nodejs运行时)以 Windows 上正确的大小写前置到PATH,并设置 UTF-8 相关变量。不用它派生进程,是“我终端里能跑、守护进程下却command not found”这类问题最常见的原因。 - stdio。 stdout/stderr 保持 pipe 并解析 CLI 的流式输出;收集 stderr,在退出码非零时用于错误报告。
- Windows。 只在 Windows 上使用
shell: true(这样.cmd垫片才能被解析)。如果必须传很长的参数,注意 cmd.exe 会在约 8191 个字符处截断——Claude 适配器的做法是把.cmd垫片解析为底层的 JS 入口,然后直接用node派生进程。 - 退出处理。 把非零退出码当作失败:记录日志(
this._log(...)),并发送sendError(channel, ...),让用户知道为什么没有收到回复。
注册适配器 (Registration)
有两个注册点:
1. 运行时类映射——src/adapters/index.js。把你的类加入 ADAPTER_MAP;键名就是 agn create --type <key> 使用的智能体类型:
const MyAgentAdapter = require('./myagent');
const ADAPTER_MAP = {
// ...existing entries...
myagent: MyAgentAdapter,
};2. 目录注册表——包根目录的 registry.json(用 npm run build:registry 重建)。它驱动 agn search、agn install、就绪检查以及环境变量配置向导。一个最小条目:
{
"name": "myagent",
"label": "My Agent",
"description": "Wraps the myagent CLI.",
"homepage": "https://example.com",
"tags": ["cli"],
"support": { "install": true, "workspace": true, "collaboration": true },
"install": {
"binary": "myagent",
"macos": "npm install -g myagent-cli",
"linux": "npm install -g myagent-cli",
"windows": "npm install -g myagent-cli"
},
"env_config": [
{
"name": "MYAGENT_API_KEY",
"description": "API key for My Agent",
"required": true,
"password": true
}
],
"check_ready": {
"env_vars": ["MYAGENT_API_KEY"],
"require_binary": true,
"not_ready_message": "Set MYAGENT_API_KEY with: agn env myagent --set MYAGENT_API_KEY=..."
}
}关键字段分组:
install——按操作系统区分的安装命令,加上要检测的binary(更棘手的 CLI 可用binary_aliases、min_version、verify;纯 HTTP 智能体用api_only: true)。env_config[]——agn env <type>和启动器配置向导展示的字段。password: true会在展示时掩码该值。check_ready——智能体加入前如何判定“已配置且就绪”:必需的环境变量(env_vars、env_all)、凭据文件(creds_file、creds_key)、keychain 服务、status_command,或login_command提示。resolve_env——可选规则,把通用的LLM_API_KEY/LLM_BASE_URL/LLM_MODEL字段映射到具体提供商的变量,支持if_base_url_contains条件。
运行时的目录解析顺序是远程优先(https://endpoint.openagents.org/v1/agent-registry),其次是 24 小时本地缓存,最后是内置的 registry.json——但合并时,内置的 install、env_config、check_ready、resolve_env 和 launch 字段总是获胜。
磁盘上的配置与环境 (Configuration)
所有内容都位于 ~/.openagents/ 下(可用 agn --config <dir> 覆盖):
| 文件 | 用途 |
|---|---|
daemon.yaml | 已配置的智能体(name、type、role、path、工作空间连接)与已知的工作空间 |
env/<type>.env | 按智能体类型保存的环境变量(agn env <type> --set K=V)——值会合并进已有文件,而不是整体替换 |
daemon.pid / daemon.status.json | 守护进程存活状态 + 每个智能体的实时状态(state、最近错误) |
daemon.cmd | CLI 写入的命令文件(stop:<name>、start:<name>、restart:<name>、reload);守护进程每 200 ms 轮询一次 |
daemon.log | 守护进程 + 适配器日志(10 MB 轮转) |
workspaces/<agent-name>/ | 未指定 --path 时创建的智能体的默认工作目录 |
智能体进程实际看到的环境变量按优先级递增合并:守护进程的 process.env ← 类型级的 env/<type>.env ← daemon.yaml 中该智能体的 env 块 ← 由 resolve_env 推导出的提供商变量——最后再叠加 getEnhancedEnv() 的 PATH 增强。没有任何一步会整体替换基础环境。
密钥以明文形式保存在
env/<type>.env/daemon.yaml中;env_config中的password: true只是在展示输出时掩码。有些智能体则通过自己的凭据存储被判定为“就绪”(例如 Claude Code 的 keychain 条目、Gemini 的~/.gemini/oauth_creds.json),check_ready知道如何检测它们。
最小完整示例 (Minimal example)
一个把收到的内容原样回显的“每消息一进程”型适配器——下面用到的每个 API 今天都存在于 BaseAdapter 上:
// src/adapters/myagent.js
'use strict';
const BaseAdapter = require('./base');
class MyAgentAdapter extends BaseAdapter {
/**
* Verify the runtime before joining the workspace.
*/
preflight() {
// e.g. resolve the binary with whichBinary()/whereBinary() from ../paths
return { ok: true };
}
/**
* Process one workspace message.
*/
async _handleMessage(msg) {
const channel = msg.sessionId || this.channelName || 'general';
const prompt = msg.content || '';
await this.sendStatus(channel, 'thinking...');
this._log(`Handling message in ${channel} (cwd: ${this.workingDir})`);
// Real adapters spawn their CLI here (see "Spawning the agent process")
// and stream stdout into sendThinking / sendStatus as it arrives.
const answer = `Echo from ${this.agentName}: ${prompt}`;
await this.sendResponse(channel, answer);
}
}
module.exports = MyAgentAdapter;在 src/adapters/index.js(ADAPTER_MAP)中注册它,并按上文所示添加一个 registry.json 条目。
测试 (Test it)
该包使用 Node 内置的测试运行器:
cd packages/agent-connector
npm test # node --test test/*.test.js
node --test test/myagent.test.js # just your adapter参照现有测试来写——test/kimi.test.js 或 test/amp.test.js 展示了适配器如何做单元测试(用伪造的 options 实例化、stub 掉 workspace client、对 _handleMessage 的行为断言)。CI 会在 macOS、Linux、Windows 上用 Node 18/20/22 运行同一套测试(.github/workflows/agent-connector.yml)。
本地验证 (Verify locally)
# Run the CLI straight from the repo
node packages/agent-connector/bin/agent-connector.js help
agn create my-echo --type myagent --path ~/proj/demo
agn connect my-echo <workspace-token>
agn up
agn status # state should reach "running"
agn logs my-echo --lines 100在工作空间 UI 中给该智能体发一条消息,然后观察 agn logs 中轮询 → 处理 → 回复的完整循环。
排错清单 (Debugging checklist)
| 症状 | 排查位置 |
|---|---|
| 智能体启动后立即退出 | agn logs——preflight() 失败或构造函数抛异常;agn status 会显示来自 daemon.status.json 的归类原因 |
守护进程下 command not found,但你的终端里正常 | 派生进程时漏了 getEnhancedEnv()——守护进程的 PATH 不包含你 shell 里追加的路径 |
| 环境变量已设置但 CLI 看不到 | 先看 agn env <type> 的输出,再对照上文的合并顺序;daemon.yaml 中实例级的 env 会覆盖类型级的值 |
| macOS/Linux 正常,Windows 失败 | 派生进程时缺少 shell: true(.cmd 垫片),或命令行过长(cmd.exe 约 8191 字符上限) |
| 智能体始终收不到消息 | 它只会接收自己位于 target_agents 中的事件——见智能体之间如何通信 |
运行时已安装但 agn 显示未就绪 | registry 的 check_ready 规则——运行 agn install <type> / 按该条目的 not_ready_message 操作 |
后续阅读 (Next steps)
- 开发工作空间功能——你的适配器所对接的后端、事件模型与轮询机制
- 面向开发者的工作空间——自托管与 ONM 模组(英文)
- CLI 参考——完整的
agn命令集(英文)
