Bridge Profile 规范(P0 Exec Protocol)
最后更新:2026-06-29
iLink Hub Bridge 的 profile 就是一个可执行的脚本或程序:收到消息 → 做处理 → 把回复写到 stdout。
1. P0 协议契约
P0 仅依赖环境变量 + stdout,完全跨平台(macOS / Linux / Windows)。
输入(bridge 自动注入环境变量)
| 变量名 | 说明 |
|---|---|
AGENT_MESSAGE | 用户消息文本(路由后的净文本,前缀已剥离) |
AGENT_SESSION_ID | Hub 持久化的后端 session UUID(空 = 新会话) |
AGENT_SESSION_NAME | session 可读名称(默认 default) |
AGENT_FROM_USER | 发送消息的用户 ID |
AGENT_CONTEXT_TOKEN | Hub context token |
AGENT_STREAMING | 1(默认)= 流式模式,0 = 一次性回复模式(见下文) |
当 YAML 设置了
stdin: message时,AGENT_MESSAGE同时也会写入 stdin。
输出(profile 写 stdout)
[可选] AGENT_SESSION:<uuid> ← 如需 session 追踪,首行输出这个
<回复给微信用户的文本>Bridge 从进程启动开始实时读取 stdout——profile 一旦写入并刷新缓冲区,bridge 立即处理。
流式输出(AGENT_PARTIAL)
当 profile 需要把 AI 生成的文本逐段发给用户时,可在终端输出中夹杂 AGENT_PARTIAL: 行:
AGENT_PARTIAL:<JSON 编码的字符串>- bridge 读到该行后立即将解码后的文本通过 sendmessage 发给微信用户,无需等进程退出。
<JSON 编码的字符串>是对分块文本调用json.dumps(text)/JSON.stringify(text)的结果,换行等特殊字符会被 JSON 转义,整个标记只占 stdout 的一行。- profile 内部无需感知 iLink 协议——
AGENT_PARTIAL:只是 profile 与 bridge 之间的约定,bridge 负责向 Hub 发消息的全部细节。 - 进程退出 = EOF,bridge 结束读取,若剩余 stdout 非空则作为最终消息发送;若全部内容已通过
AGENT_PARTIAL:发出,最终 body 为空时 bridge 会自动跳过最终 sendmessage。
关闭流式(streaming: false)
如果遇到流式 bug 或需要调试,可以在 YAML profile 里将 streaming 设为 false:
profiles:
claude:
type: claude-code
cwd: /path/to/your/project
streaming: false # 关闭流式,等 AI 完全响应后一次性发送关闭后:
- bridge 忽略所有
AGENT_PARTIAL:行,不实时转发给用户 - 同时向子进程注入
AGENT_STREAMING=0,内置 profile(如claude-code)会自动切换为一次性输出模式 - 进程退出后,完整 stdout 作为最终回复一次性发送
默认值为 true(流式开启)。
Bash 示例:
#!/usr/bin/env bash
# 流式调用示例:用于测试,每秒发一段
echo 'AGENT_PARTIAL:"第一段,1 秒前发出"'
sleep 1
echo 'AGENT_PARTIAL:"第二段,2 秒前发出"'
sleep 1
# 进程退出 → bridge 读到 EOF → 完成退出码
| 退出码 | 含义 |
|---|---|
0 | 成功,stdout 内容作为回复 |
非 0 | 失败,bridge 发送错误提示(若 send_error_reply: true) |
Stderr 记录为 debug 日志,不发给用户(除非 include_stderr_in_reply: true)。
2. bridge 怎么运行你的脚本:script: 字段
在 YAML 里写 script: 即可——bridge 根据文件扩展名自动推断运行时,无需你手动写 command / args:
profiles:
my-bot:
script: ./my_handler.py # bridge 自动调用 python3 my_handler.py
timeout_secs: 60| 扩展名 | 推断运行时 |
|---|---|
.py | python3 <script> |
.js / .mjs | node <script> |
.ts | npx tsx <script> |
.sh / .bash | bash <script> |
.rb | ruby <script> |
| 无 / 其他 | 直接执行(需 chmod +x + shebang) |
如果你需要用特定的 Python 虚拟环境或 Python 路径,设置 command 即可覆盖自动推断:
profiles:
my-bot:
script: ./my_handler.py # 仅作标注,command 优先
command: .venv/bin/python3
args: ["./my_handler.py"]3. 用 SDK 写 profile(推荐)
SDK 把读取环境变量、写 stdout、管理对话历史的样板代码封装掉,让你只写业务逻辑。
Python SDK
pip install agentproc创建 my_handler.py:
from agentproc import create_profile
async def handler(ctx):
# ctx.message, ctx.session_id, ctx.from_user, ctx.context_token
reply = await my_ai_call(ctx.message)
return reply # 直接返回字符串即可
create_profile(handler)流式输出(ctx.send_partial):
from agentproc import create_profile, AgentResult
async def handler(ctx):
new_sid = ctx.session_id
# AI 每产生一段文本,立即发给用户,无需等到全部完成
async for chunk, new_sid in stream_ai(ctx.message, ctx.session_id):
await ctx.send_partial(chunk) # 写 AGENT_PARTIAL: + flush
# 全部已流式发出,response 置空即可
return AgentResult(response="", session_id=new_sid)
create_profile(handler)send_partial 的实现只有两行:写 AGENT_PARTIAL:{json.dumps(text)}\n 并立即 flush()。Bridge 在实时读 stdout 时检测到该前缀,就立即向 Hub 发消息——profile 完全不感知 iLink 协议。
YAML 配置:
profiles:
my-bot:
script: ./my_handler.py
timeout_secs: 60Node.js SDK
npm install agentproc创建 my_handler.js:
const { createProfile } = require('agentproc');
createProfile(async ({ message, sessionId, fromUser }) => {
const reply = await myAICall(message);
return { response: reply };
});YAML 配置:
profiles:
my-bot:
script: ./my_handler.js
timeout_secs: 60多轮对话历史(JSONL,可选)
当你直接调用 LLM API 且需要携带上下文时,SDK 提供历史管理(存储于 ~/.ilink-hub/sessions/<session_id>.jsonl):
from agentproc import create_profile, load_history, append_history, HistoryEntry, AgentResult
async def handler(ctx):
history = load_history(ctx.session_id)
messages = [{"role": e.role, "content": e.content} for e in history]
messages.append({"role": "user", "content": ctx.message})
reply = await call_openai(messages) # 传入完整上下文
append_history(ctx.session_id, [
HistoryEntry(role="user", content=ctx.message),
HistoryEntry(role="assistant", content=reply),
])
return AgentResult(response=reply, session_id=ctx.session_id)
create_profile(handler)4. 不用 SDK 的裸脚本
如果你只需做简单转发或不想引入依赖,直接读 env var、写 stdout 即可。
Bash
#!/usr/bin/env bash
# my_handler.sh
REPLY=$(curl -s https://api.example.com/chat \
-H "Authorization: Bearer $MY_API_KEY" \
--data-urlencode "message=$AGENT_MESSAGE")
echo "$REPLY"YAML:
profiles:
my-bot:
script: ./my_handler.shPython(无 SDK)
#!/usr/bin/env python3
import os, sys
message = os.environ.get('AGENT_MESSAGE', '')
reply = my_ai_call(message)
sys.stdout.write(reply)Node.js(无 SDK)
#!/usr/bin/env node
const message = process.env.AGENT_MESSAGE || '';
async function main() {
const reply = await myAI(message);
process.stdout.write(reply);
}
main().catch(e => { process.stderr.write(String(e)); process.exit(1); });5. 内置 profile:type: claude-code
由 ilink-hub-bridge 自带,无需额外脚本,最简单地接入 Claude Code:
profiles:
claude:
type: claude-code
cwd: /path/to/your/project
timeout_secs: 300bridge 解析时自动展开为(等价于):
profiles:
claude:
command: ilink-hub-bridge
args: [profile, claude-code]
stdin: message
cwd: /path/to/your/project
timeout_secs: 300
cli_session_first_line_prefix: "AGENT_SESSION:"手动测试:
AGENT_MESSAGE="你好" AGENT_SESSION_ID="" ilink-hub-bridge profile claude-code6. 分享与发布
团队内分享:把脚本放进 git 仓库,其他人 clone 后,YAML 填相对路径即可:
profiles:
my-bot:
script: ./scripts/my_handler.py公开发布:发布为 npm 或 PyPI 包,包名约定 agentproc-<type>:
# 发布
npm publish # 或 python -m twine upload dist/*
# 用户安装后,直接用 command 引用profiles:
gemini:
command: agentproc-gemini7. 调试
模拟一次 bridge 调用(不启动完整 bridge):
AGENT_MESSAGE="你好" \
AGENT_SESSION_ID="" \
AGENT_SESSION_NAME="default" \
AGENT_FROM_USER="test" \
AGENT_CONTEXT_TOKEN="test-token" \
python3 ./my_handler.py或用 bridge 内置子命令调用 built-in profile:
AGENT_MESSAGE="你好" AGENT_SESSION_ID="" ilink-hub-bridge profile claude-code调试消息路由:
ILINKHUB_BRIDGE_DUMP_MSG=1 ilink-hub-bridge --config my.yaml