为什么要自托管?
有些公司不能把数据发给第三方服务。合规要求、安全策略,或者只是个人偏好 —— 理由不重要。重要的是,你的 agent 之间依然应该能用开放的 A2A 标准互相通信。
我们做了一个单文件的 A2A provider,完全跑在你自己的基础设施上。不需要平台账号,不需要 WebSocket bridge,没有任何外部依赖。就一个 HTTP server,加上你本地的 CLI agent。
60 秒跑起来
克隆示例,然后启动:
cd ah-cli/examples/self-hosted-a2a
npm install && npm start
就这些。你现在有了一个标准 A2A 端点 http://127.0.0.1:8080/a2a,Agent Card 在 /.well-known/agent.json。
启动时,server 会生成一个随机 API token 并打印到终端。所有发往 /a2a 的请求都需要带上这个 token 作为 Bearer header。
你得到了什么
server 实现了完整的 A2A 1.0 JSON-RPC 接口:
tasks/send —— 同步执行。发消息,等待完整响应。
tasks/sendSubscribe —— SSE 流式。agent 产生内容的同时实时推送 chunk。
tasks/get —— 查询正在运行或已完成的任务状态。
tasks/cancel —— 立即终止一个正在运行的任务。
底层实现是:每次请求都会 spawn 一个本地 CLI 进程(默认是 Claude Code,但任何 CLI 都行)。agent 在你指定的项目目录下运行,有完整的本地访问权限。
安全模型
这个 provider 是为内网设计的,不是面向公网的。但内网不代表不设防:
认证 —— 每次 /a2a 请求都需要 Bearer token。使用常量时间比较,防止时序攻击。
网络隔离 —— 默认绑定 127.0.0.1。只有当你准备好对内网开放时,再设置 HOST=0.0.0.0。
限流 —— 滑动窗口,每分钟最多 30 次请求。防止意外的请求风暴。
请求超时 —— 最长 5 分钟。失控的 agent 进程会被自动 kill 掉。
body 大小限制 —— 1 MB。超大 payload 在解析前就会被拒绝。
审计日志 —— 每条请求都以结构化 JSON 的形式输出到 stderr。直接接入你公司的日志系统。
替换 agent 后端
默认后端是 Claude Code,但只要能从参数读取输入、向 stdout 写输出,什么都可以用:
AGENT_CMD="codex" npm start
AGENT_CMD="node my-bot.js" npm start
AGENT_CMD="python agent.py" npm start
server 会把用户的消息作为 CLI 参数传入,并把 stdout 流式返回作为响应。
在团队内共享
如果要在内网范围内给团队使用:
HOST=0.0.0.0 API_TOKEN=team-secret npm start
持久化部署的话,套一个 systemd service 或者 Docker 容器就行。server 是无状态的 —— 没有数据库,不写磁盘 —— 水平扩展轻而易举。
架构
整个流程都在你的网络内部完成:
内部调用方 → HTTP server → CLI 进程 → 响应
没有任何数据发送给 Agents Hot,没有连接外部 bridge 的 WebSocket,没有遥测。Agent Card 端点是公开的(其他 A2A agent 发现时需要),但里面只有你 agent 的名称和能力描述,没有任何敏感数据。
完整源码是单个 TypeScript 文件,不到 400 行。拿去读,拿去审计,拿去改。这就是它的意义所在。

