a2a-engine
v0.2.1
Published
A2A 社交决策注入器:host-agnostic 的 OpenClaw 插件,在 agent 看到消息前注入老板上下文 + 决策预提示词;同时通过 host 标准 hook (before_prompt_build / agent_end) 自维护双向对话流水。
Maintainers
Readme
a2a-engine
OpenClaw 通用插件:A2A 社交决策注入器。Host-agnostic,兼容 OpenClaw / QClaw / 其他 OpenClaw-family host。
做什么
在 agent 看到入站消息前,把「老板最近对话上下文 + 决策预提示词」注入到 system context;agent 一轮结束后,把它发出去的话回写到插件自维护的对话流水里。零额外 LLM 调用,零规则匹配,所有判断由 agent 主回合的 LLM 完成。
不做什么
- 不发任何 IM 消息(通信由对应的 channel 插件负责,比如
multi-openim-channel) - 不调任何 skill 命令(命令模板由配置注入,插件本身 skill-agnostic)
- 不做任何 regex 分类 / 评分(决策全交 LLM)
- 不读其他 skill / 插件的 STATE 文件
- 不依赖任何特定 host 的目录/字段名(多个 host 的 sessionKey / payload 形态都能识别)
架构
inbound IM → channel plugin → host pipeline
↓
[before_prompt_build] hook
↓
a2a-engine:
- 通过 compat 层解析 envelope(sessionKey / payload 多源)
- record inbound to log
- read boss/peer context from log
- return { prependSystemContext: "<提示词>" }
↓
agent LLM 主回合(决策)
↓
(调命令:skill / channel send tool)
↓
[agent_end] hook
↓
a2a-engine: record outbound to logHost 兼容(compat.js)
所有 host-specific 的取值都集中在 dist/compat.js:
| 维度 | 解析顺序 |
|---|---|
| 插件配置 | api.pluginConfig → api.config.plugins.entries["a2a-engine"].config → api.config.plugins["a2a-engine"].config → api.config.plugins["a2a-engine"] → globalThis.A2A_ENGINE_CONFIG |
| stateDir | api.stateDir → api.runtime.stateDir → api.config.stateDir → $OPENCLAW_STATE_DIR → $QCLAW_STATE_DIR → ~/.openclaw |
| sessionKey | ctx.sessionKey / ctx.SessionKey / ctx.session / 同名 ctxPayload / 同名 event 字段 |
| channelId | channelId / messageChannel / channel / messageProvider / provider / source(PascalCase / camelCase 都认) |
| peerId | peerId / conversationPeerId,缺失时回落到 senderId / fromId / userId / sendID |
| groupId | groupId / roomId / chatId / conversationId |
| chatType | chatType / peerKind / kind / conversationType |
| 昵称 | requesterSenderName / senderName / displayName / nickname / fromName / name |
| inbound body | event.prompt → event.body → event.text → event.content → payload Body/Text/Content/messageText → 最后一条 user message |
SessionKey 解析支持多种 OpenClaw-family 格式:
agent:<agentId>:<channel>:direct:<peerId> // OpenClaw v2026.x(首选)
agent:<agentId>:<channel>:group:<groupId>
<channel>:direct:<peerId> // 简化 / legacy host
<channel>:group:<groupId>解析不出来时不会报错,而是回落到 payload 取 senderId/peerId,再不行就跳过当前事件。
配置
在 host 的 openclaw.json(或对应 host 的同名文件)下:
{
"plugins": {
"allow": ["a2a-engine"],
"entries": {
"a2a-engine": {
"config": {
"enabled": true,
"bossId": "user_752311134680",
"channels": [],
"bossContextN": 5,
"peerContextN": 3,
"commands": {
"notify": "multi_openim_send_text(accountId=\"main\", target=\"user:{bossId}\", text=\"{text}\")",
"ask": "multi_openim_send_text(accountId=\"main\", target=\"user:{bossId}\", text=\"[问] {text}\")",
"send": "multi_openim_send_text(accountId=\"main\", target=\"user:{peerId}\", text=\"{text}\")",
"replyPeer": "multi_openim_send_text(accountId=\"main\", target=\"user:{peerId}\", text=\"好的,我跟老板确认下再回复您\")"
}
}
}
}
}
}字段说明:
| 字段 | 必填 | 默认 | 用途 |
|---|---|---|---|
| bossId | 是 | — | 老板的 IM ID。peer.id == bossId 时走「老板入站」分支 |
| channels | 否 | [](=任意 channel) | 仅在这些 channel 上注入;小写匹配 |
| logPath | 否 | <host.stateDir>/a2a-engine/conversations.jsonl | 对话流水文件,按行 JSON append-only |
| bossContextN | 否 | 5 | peer 入站时注入老板最近几句;boss 入站时同样用作「你最近对老板说过的话」上限 |
| peerContextN | 否 | 3 | boss 入站时注入最近几个 peer 的一来一回 |
| commands.* | 否 | "" | 注入模板里的命令占位。支持 {peerId} / {bossId} / {text} 占位符;空串时模板隐藏该行。模板纯文本提示,插件不执行——可以是 channel 插件的工具调用(multi_openim_send_text(...))或任意 shell 命令 |
上面示例用
multi_openim_send_text作为通信出口;要换成别的 channel 或自家 skill 命令,只改commands这个对象即可,插件代码不动。
注入模板(两种)
peer 入站(peer != boss):三步判断 → notify / ask + replyPeer / 自主回 peer
boss 入站(peer == boss):两步判断 → 命中近期向老板的发问就用 send 把回话转给对应 peer;推断不出来按字面执行老板指令。LLM 自己用「你最近对老板说过的话」+「peer 最近互动」完成关联,无 escalation 状态机。
具体模板见 dist/prompts.js。命令模板中 {peerId} / {bossId} 已替换为真实 ID,{text} 由 LLM 自己填。
失败模式
bossId没配 → 插件加载,所有 inbound 跳过注入(agent 退化为通用对话),warn 一次- 配置的
channels没命中 → 跳过该入站(不影响其他 channel 的工作) - sessionKey 解不出来 + payload 也没 peerId → 跳过当前事件,不报错
- log 写失败 → 只 warn 一次,不中断 agent;下次写成功后恢复
- 任何 hook 回调内部异常 → 全套 try/catch 兜底,最坏情况「这一轮没注入」
文件
a2a-engine/
├── package.json
├── openclaw.plugin.json
├── README.md
└── dist/
├── index.js # 注册 before_prompt_build / agent_end
├── compat.js # host 兼容层(config/host/envelope 解析)
├── config.js # 配置归一化 + 命令模板渲染
├── peer-parse.js # sessionKey → {channel, kind, id}
├── recorder.js # conversations.jsonl 写入
├── context.js # 读取 boss / peer 上下文
└── prompts.js # 两个注入模板License
MIT
