🤝 DSH A2A Agent
让任意 DeepSeek Harness 智能体一键成为 A2A 兼容的 Agent
一个轻量的 Cordis 插件:自带 Agent Card 发现、JSON-RPC 2.0 端点、大模型驱动的对话回复,以及一个实时状态卡片。
Agent-to-Agent · 让智能体之间说同一种语言
✨ 为什么你需要它
A2A(Agent2Agent) 是 Google 于 2025 年推出的开放协议,让不同厂商、不同框架的智能体能够互相发现、协作、对话。而 DeepSeek Harness 原生只支持 MCP(Agent ↔ 工具),缺少对外暴露的 Agent 端点。
这个插件填补了空白 —— 一次定义,你的 DSH 智能体就有了标准化的 A2A 身份,并在对话流里实时看到它的运行状态。
🚀 特性
| 🪪 Agent Card 自动发现 符合 A2A 1.0 规范的 /.well-known/agent.json |
🔌 JSON-RPC 2.0message/send · message/stream · tasks/get · tasks/cancel |
| 🧠 真实大模型回复 复用宿主 llm 服务流式生成 |
🛡️ 优雅降级 模型不可用时自动回退确定性 echo |
| 📊 实时状态卡片 Run 卡片内展示端点 URL、消息数、任务数 |
🔀 零端口冲突 复用宿主 webServer,不抢端口 |
| ♻️ 可逆生命周期 所有副作用挂载在 Cordis fiber 上 |
🌐 CORS 开箱即用 浏览器端 A2A 客户端可直接调用 |
🧭 工作原理
sequenceDiagram
participant C as A2A Client
participant W as DSH webServer
participant H as Host half
participant L as LLM Service
participant U as Client half (状态卡片)
C->>W: GET /.well-known/agent.json
W->>H: 命中 route handler
H-->>C: Agent Card (JSON)
C->>W: POST /a2a · message/send
W->>H: JSON-RPC dispatch
H->>L: llm.stream(prompt)
L-->>H: token 流
H-->>C: Task (completed, 含回复)
U->>H: get-status (每 2s)
H-->>U: { endpointUrl, messageCount, taskCount }
📁 文件结构
dsh-a2a-agent/
├── lib/index.js # npm 包形态(Host 插件,可挂载到 host 组合)
├── host.js # 动态插件 Host half → cordis_define 的 code.host
├── client.js # 动态插件 Client half → cordis_define 的 code.client
├── README.md
├── LICENSE
├── package.json
└── cordis.example.yml # 用法说明
🧩 两种形态
本项目同时提供两种形态,按需选用:
| 形态 | 文件 | 状态卡片 | 适用场景 |
|---|---|---|---|
| npm 包 | lib/index.js |
通过 /a2a/status HTTP 端点轮询 |
挂载到宿主组合,pnpm add dsh-a2a-agent |
| 动态插件 | host.js + client.js |
内置 Run 卡片(每 2s 轮询) | cordis_define 即时加载,零构建 |
💡 为什么 npm 包形态不带内置 Client 卡片:DSH 正式插件的 Client→Host 通信走 Typert Remote(TS 装饰器 + 构建时生成 codec),需要完整构建链。为保持零构建、可直接挂载,npm 包形态用
/a2a/statusHTTP 端点替代,任意前端都能轮询渲染。
🔖 插件市场收录:本仓库带 GitHub topic
dsh-plugin,会被社区插件市场(扫描github.com/topics/dsh-plugin)自动收录;package.json的dsh.bundlemanifest 让市场能识别并一键安装。
📦 快速开始
方式 A:组合包安装(dsh plugin,推荐)
dsh plugin add dsh-a2a-agent # 或 github:fangweixuan26-hash/dsh-a2a-agent
dsh.bundle manifest 会自动把插件层插入宿主组合(默认 enableStreaming: true)。重启后验证:
curl http://127.0.0.1:3080/.well-known/agent.json # Agent Card
curl http://127.0.0.1:3080/a2a/status # 状态 JSON
手动挂载:也可
pnpm add dsh-a2a-agent后在宿主组合加一行- id: a2a-agent+name: dsh-a2a-agent(config.enableStreaming可选)。
方式 B:动态插件形态(含状态卡片)
把 host.js / client.js 的内容作为动态插件的两个 half:
cordis_define(
{ plugin: { kind: "new", idPrefix: "bridge" } },
name: "A2A Protocol Server",
purpose: "Expose this agent over A2A",
code: { host, client }, // host = host.js 内容, client = client.js 内容
)
cordis_run(...)
💡 首次运行含 Client 代码的 Package 需要你在 UI 里批准;批准后 Run 卡片内会出现状态卡片。
🔌 API 参考
HTTP 端点
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/.well-known/agent.json |
A2A 1.0 Agent Card |
GET |
/.well-known/agent-card.json |
旧版(0.2.x)别名,兼容 |
POST |
/a2a |
JSON-RPC 2.0 入口 |
GET |
/a2a/status |
状态 JSON(端点 URL、消息数、任务数) |
JSON-RPC 方法
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
message/send |
{ message } |
Task |
发送消息,同步生成回复并完成 |
message/stream |
{ message } |
SSE 事件流 | 流式返回 token(需启用 streaming) |
tasks/get |
{ id } |
Task |
按 id 查询任务 |
tasks/cancel |
{ id } |
Task |
取消进行中的任务 |
错误码
| Code | 含义 |
|---|---|
-32700 |
JSON 解析错误 |
-32600 |
非法请求 |
-32601 |
方法不存在 |
-32602 |
参数错误 |
-32001 |
任务不存在 |
-32603 |
内部错误 |
📡 使用示例
发送一条消息
curl -s http://127.0.0.1:3080/a2a \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"messageId": "m1",
"role": "user",
"parts": [{ "kind": "text", "text": "用一句话介绍你自己" }]
}
}
}'
返回(节选):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "task-...",
"status": "completed",
"artifacts": [
{ "artifactId": "art-...", "name": "reply",
"parts": [{ "kind": "text", "text": "我是一个基于A2A协议的多功能AI助手…" }] }
]
}
}
查询任务
curl -s http://127.0.0.1:3080/a2a \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tasks/get","params":{"id":"task-..."}}'
流式消息(SSE)
curl -N http://127.0.0.1:3080/a2a \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"message/stream","params":{"message":{"messageId":"m3","role":"user","parts":[{"kind":"text","text":"用一句话介绍你自己"}]}}}'
返回 SSE 事件流,逐 token 推送:
: connected
event: status-update
data: {"kind":"status-update","status":{"state":"working"},...}
event: artifact-update
data: {"kind":"artifact-update","artifact":{"parts":[{"kind":"text","text":"我"}]},"append":true}
...
event: message
data: {"kind":"message","role":"agent","parts":[{"kind":"text","text":"我是一个智能助手…"}]}
event: status-update
data: {"kind":"status-update","status":{"state":"completed"},"final":true}
🧠 回复是如何生成的
- 从
message.parts提取所有text片段 - 读取宿主
agentDefaultModel的当前模型路由 - 调用
llm.stream()流式累积回复 - 模型失败或不可用时,降级为
[A2A echo]确定性回显
任务、消息、工件均存储于进程内存,插件停止时自动清空。
⚙️ 配置
enableStreaming(可选择启用 SSE 流式)
| 形态 | 配置方式 |
|---|---|
| npm 包 | cordis.yml 里 config: { enableStreaming: true } |
| 动态插件 | 编辑 host.js 顶部的 const enableStreaming = true |
开启后:
- Agent Card 声明
capabilities.streaming: true /a2a端点支持message/stream(SSE,逐 token 推送)- 状态卡片显示「SSE 流式」徽标
关闭时,message/stream 返回 -32601 Method not found。
🛣️ 路线图
- npm 包形态 —— 作为 host 组合条目挂载(含
/a2a/status端点) -
message/stream—— SSE 流式返回 token(可选择启用) - 子代理调度 —— 长任务转交
subagents - 任务持久化 —— 跨插件重启保留
- 认证 —— Agent Card
securitySchemes/ Bearer token - npm 包 Client 卡片 —— 走 Typert Remote(需 DSH 构建链)
🤝 贡献
欢迎 Issue / PR!请先阅读 A2A 规范。
No comments yet. Be the first to write one.