项目

Agora:去中心化的 Agent 点对点沟通网络

一个由中心消息服务和命令行客户端组成的通信原型,处理 Agent 发现、消息转发和临时离线收件。

更新于

目录

两个开发者分别让 Agent 修改 SDK 和后端。SDK 改了接口,修改的原因、提交位置、兼容性要求却仍要由人复制到聊天软件,再交给另一边的 Agent。两个 Agent 都能理解代码,也都能执行命令,协作却在环境之间断开了。

Agora 从这个具体问题出发:提供一个足够小的通信层,让不同人、不同机器、不同工具上的 Agent 发现彼此、交换消息。它不接管各个 Agent 的执行循环,也不要求所有人迁移到同一种开发工具。这里介绍的是 2026 年 4 月留下的设计与通信原型。

注册的是一个正在工作的 Agent

规格将 Agora 称为“agent 局域网”。它不要求机器位于同一物理网络,而是让一组参与者连接到共同可达的中心服务。服务保存在线注册表,客户端通过 CLI 接入;能调用 shell 的 Agent 工具就可以使用这层接口。

注册信息描述的是一个协作角色:谁拥有它、它叫什么、在哪个项目里工作、当前负责什么。owner/name 构成标识,例如 alice/sdk-agent;项目字段用于筛选,职责说明帮助其他 Agent 判断该向谁询问。这里的项目标签不是访问控制,也不是 Git 仓库的自动同步关系。

设备和 Agent 分成两层。设备先配对,取得连接服务的凭证;一个具体的 Agent 再注册自己的名称与工作描述。这样,机器的接入和一次工作中的协作身份不必捆成同一个对象。相应地,协议中的 owner 只是注册信息,原型没有把这个名字变成经过独立核验的个人身份。

在线状态由连接维护。注册后,Agent 获得其他在线成员的列表,其他成员收到上线广播;连接断开则从注册表移除并广播离线。同名 Agent 再连接时,代码会关闭旧连接、替换注册项,并检查旧连接退出时不要误删新连接。这处理的是运行中的连接归属,不是持久任务状态。

一条消息怎样跨过两台机器

Agora 拓扑:两台机器各有 Agent、CLI 与本地收件箱,中心服务只转发和暂存消息
图 1. Agora 的通信结构。中心服务传送上下文;读取消息和决定行动属于接收方工作环境。

以下是一条按协议构造的示意消息,不是真实项目记录:

{
  "to": "bob/backend-agent",
  "content": "刷新令牌接口已拆分,请检查旧调用点。变更说明和签名见所附提交。",
  "context": {
    "commit": "example-revision",
    "files_changed": ["src/auth.ts"]
  }
}

自然语言正文解释变化,context 补充提交、文件等结构化信息。协议没有要求先定义复杂的任务交接格式,也不把代码仓库内容整体上传。附上一个文件路径,并不代表另一台机器自动拥有该文件;接收方仍需在自己有权限访问的资料中核对。

实现设计把客户端操作分成两类。connect 是持续运行的前台进程,负责 WebSocket 认证、注册与接收;send 和 list 是一次性 HTTP 请求,分别发送消息和查询在线列表。长连接不必兼任一个供其他命令调用的本地后台服务,发送命令也不需要自己维持第二套连接生命周期。

消息到达客户端后,一份写到标准输出,另一份追加到本地 inbox.jsonl。标准输出适合运行环境实时读取,本地文件则让 Agent 可以在稍后的工作节点检查收件箱。inbox 命令读取的是本地已收到的内容,而不是每次向服务器拉取全部历史。

服务端给消息增加时间戳,并确定发送者。WebSocket 路径使用当前注册的 Agent 标识;HTTP 路径检查所声明的 from 是否属于当前设备。若设备只注册了一个 Agent,可以自动推断;没有注册则拒绝发送,注册了多个而未指定发送者也会拒绝。初版规格允许“不先 connect 就直接 HTTP 发送”,后续代码收紧了这个条件:HTTP 是独立的发送通道,但不是匿名的协作身份。

这条链路止于接收。Agora 不把收到的自然语言当作自动执行指令,也不为另一台机器批准文件修改。后端 Agent 需要读取说明、核对提交,再决定如何继续自己的任务。通信层因此可以保持很小,而任务接受、冲突处理和人的确认仍属于各自工具的工作流。

离线暂存解决的是短暂错过,不是可靠队列

中心 Hub 查找接收方:在线就尝试经 WebSocket 推送,不在线就放入内存中的暂存队列。Agent 再注册时,服务先返回注册结果,再补发暂存消息。

队列按接收方保存,最多 100 条,超过上限丢弃较早内容;取出时过滤超过 24 小时的消息。选择内存意味着部署不必附带数据库,但服务器重启后,暂存消息会消失。

这里没有完整的投递确认协议。当前代码取出离线消息时便清空队列,在线发送失败也没有形成持久重试记录。因此“服务器接受了消息”“客户端收到消息”“Agent 阅读了消息”“任务完成”是四件不同的事,原型只处理前两者之间的基础传输,不能给出恰好一次投递或工作完成的保证。

这个取舍与最初的使用场景一致:让正在协作的开发者少搬运一次上下文,而不是立即建立一套企业消息基础设施。如果未来要承载不能丢失的交接,持久存储、消息标识、确认与重试就会变成协议本身的职责,不能仅通过提示 Agent“记得回复”补齐。

从共享密钥改为设备配对

最早的产品规格用一个 shared secret 限定参与范围。后续实现设计改成邀请码换设备令牌:管理员生成一次性邀请码,默认一小时有效;设备用它配对,取得由随机字节生成的令牌,之后的 HTTP 和 WebSocket 认证使用该令牌。凭证保存在客户端本地,设备可以单独撤销。

这个调整减少了所有参与者长期共用同一密钥的耦合。设备退出协作时,设计上可以撤销该设备,而不必重新分发所有人的密钥。但设备记录同样在内存中,服务器重启后需要重新配对;令牌也不是端到端消息加密机制。

文档将范围限定在受信任的小组,细粒度发送权限、联邦和消息级加密均不在 v0.1 内。

为什么只留下一个通信层

实现选择 Go,将服务器与 CLI 构建为两个程序,共享协议类型。中心服务承担注册、转发、暂存,客户端承担连接和本地收件;没有模型服务、网页控制台或跨 Agent 自动编排。这使协议的适配点落在命令调用,而不是某个模型 SDK 或特定 Agent 的内部实现。

回到 SDK 与后端的例子,Agora 传送的是“这次改了什么、对应哪份材料”。它不试图决定谁拥有接口的最终解释权,也不推断另一方必须立即接受。这个原型保留下来的核心想法,是先让原本隔离的工作环境能够交换足够明确的上下文,再由参与者定义更高层的协作约定。

所有项目

AI 助手与对话数据

团子是 AI 角色,不是本人。掷骰子等开场互动使用预先整理的公开资料,不调用模型。实时对话是否可用,以聊天框中的状态说明为准。

启用实时对话后,发送问题会将问题、当前文章的标识,以及服务器保存的近期对话与早期对话目录发送到本站后端;后端会将这些内容和检索到的公开资料发送给阿里云百炼生成回答。首次建立会话时使用 Cloudflare Turnstile 验证访客,无需注册。不要输入密钥或私人信息。模型与验证服务的数据处理适用各服务商的政策。

浏览器使用当前标签页的会话存储保存最近十条互动,便于在文章和对话间返回;清空对话会清除这些本地记录。服务器会话有效期为一小时,最多保存二十轮已完成对话;最近三轮直接作为上下文,较早记录按需读取。过期记录在后续请求时清理。“重新开始”会放弃当前会话,但不会立即删除服务器上的记录。在同一标签页的 HTTPS 页面间切换时,一小时会话凭证保存在该标签页的会话存储中,供后端恢复近期上下文和早期对话目录;它不是模型服务的 API 密钥。凭证过期或存储不可用时,需要重新验证,之前显示的记录不会自动进入新会话。

服务器另保存额度、预算和短期请求限制记录;连接标识经过带密钥的哈希处理。应用不额外记录问题日志,但服务器会话存储中包含上述对话文本。模型请求失败时,仅记录随机请求编号、错误类别、处理阶段、调用次数及是否已有部分输出,不记录问题、回答、IP 地址或密钥。AI 只能使用网站提供的公开资料工具,不能访问个人文件、私人聊天或密钥。来源链接不保证每句话准确;请检查原文,并区分作者观点、引用观点与模型推断。

嗨,我在。

不知道从哪里开始?我们可以掷个骰子。

我是 AI 角色,不是煜欣本人。当前可以玩开场互动;自由对话尚未接通。