商业产业 Hacker News Agent 2026-08-19T22:09 正文 ok 2021 words

协作式人类-Agent 协议(CHAP)

英文标题:🔶 Collaborative Human Agent Protocol (CHAP)

CHAP 是一项面向人类与 Agent 协同工作的协议,旨在把 Agent 草稿、人工编辑、审批与拒绝等决策统一记录为可查询、可回放、可验证的哈希链式 envelope。它将人工修改表示为包含 diff、理由和可控标签的结构化 override,使团队无需跨日志、工单和聊天工具追溯决策。项目提供 TypeScript 与 Python 参考实现,覆盖 Core 与全部 11 个可选 profile、共 39 个方法处理器,并支持 MCP 和 A2A 传输。对开发者而言,CHAP 可将人工纠偏沉淀为可聚合的监督数据,用于识别 Agent 的高频错误模式并迭代提示词、检索和任务上下文。
打开原文相关 3 · 机会 1
media9.4 KB media176.9 KB media5.5 KB media45.0 KB

中文

协作式人类-Agent 协议(CHAP)

供人类与 Agent 真正协同完成工作的协议。

当机器人起草内容、而人类进行编辑时,这次编辑存放在哪里?在 CHAP 中,它位于一个 envelope 中;六个月后,你仍可以查询、回放并验证它。

安装 · 90 秒导览 · 十二种场景 · 关于此仓库 · 论文


同一场景,两套技术栈。没有 CHAP:六种工具各自保存同一项决策的碎片(OpenAI 日志已过期、Zendesk 线程、已被刷过去的 Slack 消息、Linear 评论、webhook 尾部日志、Notion 运行手册);要回答“机器人起草了什么,以及为何获批?”需在四个 UI 中花费 45 分钟。有 CHAP:三个由哈希链接的 envelope(task.create → artefact → decide.override)、可查询标签、一次 audit.read 调用、30 秒。


你已经在让 Agent 处理真实工作:起草代码审查、分流工单、提出和解建议、审查合同。人类会批准、编辑或拒绝其中每一项。现在,这项决策分散在你的应用代码、聊天线程、工单评论和你的脑海里。六周后某件事出错时,重建当时经过需要花 45 分钟,而且一半靠猜。

CHAP 为你提供一个存放这些决策的位置,以及统一的记录形态。Agent 的草稿是一项工件(artefact)。人类的编辑是一项结构化 override,其中包含 diff、理由和你可控制的标签。整个过程通过内容哈希串联。你查询这条链,而不是在四个 UI 中 grep 日志。

这就是全部主张。

一名使用 Cursor 审查 pull request 的独立开发者。机器人标记出一个开发者并不同意的“警告”。以下是完整的端到端交互。下方片段以六个标注步骤在约 23 秒内演示完成;对应代码紧随其后。

包含顶部进度条和步骤指示器的六步骤 CHAP Core+Review 演示。步骤 1:设置(工作区、两名参与者、一项任务)。步骤 2:起草(Agent 起草回复)。步骤 3:待审查(携带草稿工件的 review.request)。步骤 4:Override(人类不同意:diff、理由、标签)。步骤 5:审计链(哈希链接回放,prev_hash 连续)。步骤 6:两个月后(override 学习报告显示 framework-pattern 为最高频标签,将下一次提示词修订指向正确问题)。

以下是全部代码。下方叙述以两种语言讲述同一个连续故事;请选择你实际使用的技术栈。

1. 创建工作区。 一个使用 SQLite 持久化的嵌入式协调器、两名参与者和一个工作区:

TypeScript Python
import { Coordinator } from "@brightbeamai/chap-coordinator";
import { SqliteStore } from
  "@brightbeamai/chap-coordinator/storage/sqlite";

const coord = new Coordinator({
  store: new SqliteStore("./chap.db"),
});

coord.api.workspace.create({
  workspace: "wsp_pr_reviews",
  profiles:  ["core/1.0", "review/1.0"],
});

coord.api.participant.join({
  workspace: "wsp_pr_reviews",
  from:      "human:me@local",
  type:      "human",
});

coord.api.participant.join({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  type:      "agent",
});

|

from chap_coordinator import Coordinator
from chap_coordinator.storage.sqlite \
    import SqliteStore

coord = Coordinator(store=SqliteStore("./chap.db"))

def send(method, params):
    return coord.dispatch({
        "jsonrpc": "2.0", "id": method,
        "method": method, "params": params,
    })

send("workspace.create", {
    "workspace": "wsp_pr_reviews",
    "profiles":  ["core/1.0", "review/1.0"],
})

send("participant.join", {
    "workspace": "wsp_pr_reviews",
    "from":      "human:me@local",
    "type":      "human",
})

send("participant.join", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "type":      "agent",
})  

2. 机器人起草,你进行 override。 将现有的 Cursor 集成接入,使其发出 envelope:

TypeScript Python
// The bot's review is the output of a task.
const { task_id } = coord.api.task.create({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  assignee:  "agent:cursor#v1",
  kind:      "code_review",
  input:     { pr_id: "PR-482" },
});

coord.api.task.complete({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  task_id,
  output:    cursorReview,
});

coord.api.review.request({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  task_id,
  artefact:  cursorReview,
  to:        "human:me@local",
});

// You disagree with one comment. Override it.
coord.api.decide.override({
  workspace:        "wsp_pr_reviews",
  from:             "human:me@local",
  task_id,
  intent_preserved: true,
  diff: [{ op: "replace",
           path: "/comments/0/severity",
           value: "info" }],
  rationale: "False positive. Framework " +
             "convention, not a bug.",
  tags: ["false-positive",
         "framework-pattern-misread"],
});

|

# The bot's review is the output of a task.
r = send("task.create", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "assignee":  "agent:cursor#v1",
    "kind":      "code_review",
    "input":     {"pr_id": "PR-482"},
})
task_id = r["result"]["task_id"]

send("task.complete", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "task_id":   task_id,
    "output":    cursor_review,
})

send("review.request", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "task_id":   task_id,
    "artefact":  cursor_review,
    "to":        "human:me@local",
})

# You disagree with one comment. Override it.
send("decide.override", {
    "workspace":        "wsp_pr_reviews",
    "from":             "human:me@local",
    "task_id":          task_id,
    "intent_preserved": True,
    "diff": [{"op":    "replace",
              "path":  "/comments/0/severity",
              "value": "info"}],
    "rationale": "False positive. Framework "
                 "convention, not a bug.",
    "tags": ["false-positive",
             "framework-pattern-misread"],
})  

关于接口层。 TypeScript 提供了带类型的 facade(coord.api.*),因此每个方法都能获得完整的自动补全和编译期检查。Python 则在接口层保留 JSON-RPC envelope 形态(coord.dispatch({...})),使用者可按调用位置的需要自行封装;send() helper 是 Python 测试所使用的惯用方式。两条路径发出的 wire bytes 完全一致;无论调用来自哪个客户端,审计链都逐字节相同。

3. 两个月后,分析你一直在做什么。 这正是该协议开始回报价值之处。参考仓库同时提供两种语言的分析脚本,可读取审计链(通过 HTTP 或直接从 SQLite 文件),并对 override 进行分组:

# TypeScript reference, against the SqliteStore from step 1:
$ npm --prefix reference/core-plus-review run analyze -- --db ./chap.db wsp_pr_reviews

# Python reference, same idea:
$ python3 reference/python/analyze_overrides.py --db ./chap.db wsp_pr_reviews

Override Learning Report
========================
Total overrides: 47

By tag:
  false-positive             ████████████████  31  (66%)
  framework-pattern-misread  ███████████       22  (47%)
  cosmetic-pref              ████              8   (17%)

Top file paths:
  src/handlers/                                    18 overrides
  src/components/                                  9  overrides

你对 Cursor 的下一次提示词修订不再是猜测,而是可以点名引用这一模式。


Override envelope 详解

Override envelope 是 CHAP 中最重要的单一结构。每个字段都有明确职责:

Override envelope 的结构图,各字段均有注释:task_id 连接至 PR 审查链;from 携带可查询身份;logical_id 可跨修订存续;intent_preserved 将细化型 override 与替代型 override 区分开来;diff 为 RFC 6902 JSON Patch;rationale 记录“为何如此”并与“做了什么”并存;tags 为结构化监督数据。

首次阅读时,大多数人会忽略的两个字段是 intent_preserved 和 tags。

intent_preserved 用于区分细化型(refining) override(人类同意 Agent 的决策,但重写了其表达方式)与替代型(substituting) override(人类得出了不同的决策)。这两者是不同的失败模式,需要不同的修复方式。若某一政策条款周围的细化型 override 比率很高,意味着 Agent 的检索存在偏差;若同一条款的替代型 override 比率很高,则说明政策本身可能含糊,或者 Agent 的任务上下文有误。

tags 是团队共同约定的受控词汇表。应保持精简。你放入其中的任何内容,都会成为三个月后你聚合分析的维度,届时可回答诸如*哪些提示词需要改进?或机器人在哪些路径上持续出错?*这样的问题。

TypeScript / Node:

npm install @brightbeamai/chap-coordinator

Python:

pip install chap-coordinator

无论选择哪条路径,你都会获得 Core、review/1.0 profile 以及可运行的参考实现。TypeScript 参考实现位于 reference/;Python 参考实现位于 reference/python/。TypeScript 库位于 packages/coordinator/;Python 库位于 packages/coordinator-py/。

五分钟实操导览:examples/00-five-minute-start.md。

CHAP 0.2 是一份公开草案。具体而言,此仓库包含:

English / 原文

Collaborative Human-Agent Protocol (CHAP)

The protocol for humans and agents doing real work together.

When a bot drafts something and a human edits it, where does that edit live? In CHAP, it lives in an envelope you can query, replay, and verify six months later.

Install · The 90-second tour · Twelve scenarios · About this repo · Paper


Same scenario, two stacks. Without CHAP: six tools holding fragments of one decision (OpenAI logs expired, Zendesk thread, Slack scrolled past, Linear comments, webhook tail, Notion runbook), 45 minutes across four UIs to answer 'what did the bot draft and why did we approve it?'. With CHAP: three hash-linked envelopes (task.create → artefact → decide.override), queryable tags, one audit.read call, 30 seconds.


You have agents doing real work. Drafting code reviews, triaging tickets, suggesting settlements, reviewing contracts. A human approves, edits, or rejects each one. Right now, that decision lives in your application code, your chat threads, your ticket comments, and your head. When something goes wrong six weeks later, reconstructing what happened costs you forty-five minutes and is half guesswork.

CHAP gives you one place to put those decisions and one shape to put them in. The agent's draft is an artefact. The human's edit is a structured override with a diff, a rationale, and tags you control. The whole thing chains together by content hash. You query the chain instead of grepping logs across four UIs.

That's the whole pitch.

A solo developer using Cursor to review pull requests. The bot flags a "warning" the developer disagrees with. Here is the whole exchange, end to end. The clip below runs in about 23 seconds across six labelled steps; the matching code is right underneath.

Six-step CHAP Core+Review walkthrough with a progress bar and step indicator across the top. Step 1: Setup (workspace, two participants, a task). Step 2: Drafting (agent drafts a response). Step 3: Pending review (review.request with the draft artefact). Step 4: Override (human disagrees: diff, rationale, tags). Step 5: Audit chain (hash-linked replay, prev_hash continuous). Step 6: Two months in (override learning report shows framework-pattern as the top tag, pointing the next prompt revision at the right problem).

And here is the code, every line of it. The narrative below is one continuous story in two languages; pick whichever stack you actually use.

1. Spin up a workspace. An embedded coordinator with SQLite persistence, two participants, a workspace:

TypeScript Python
import { Coordinator } from "@brightbeamai/chap-coordinator";
import { SqliteStore } from
  "@brightbeamai/chap-coordinator/storage/sqlite";

const coord = new Coordinator({
  store: new SqliteStore("./chap.db"),
});

coord.api.workspace.create({
  workspace: "wsp_pr_reviews",
  profiles:  ["core/1.0", "review/1.0"],
});

coord.api.participant.join({
  workspace: "wsp_pr_reviews",
  from:      "human:me@local",
  type:      "human",
});

coord.api.participant.join({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  type:      "agent",
});

|

from chap_coordinator import Coordinator
from chap_coordinator.storage.sqlite \
    import SqliteStore

coord = Coordinator(store=SqliteStore("./chap.db"))

def send(method, params):
    return coord.dispatch({
        "jsonrpc": "2.0", "id": method,
        "method": method, "params": params,
    })

send("workspace.create", {
    "workspace": "wsp_pr_reviews",
    "profiles":  ["core/1.0", "review/1.0"],
})

send("participant.join", {
    "workspace": "wsp_pr_reviews",
    "from":      "human:me@local",
    "type":      "human",
})

send("participant.join", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "type":      "agent",
})  

2. The bot drafts, you override. Wire your existing Cursor integration to emit envelopes:

TypeScript Python
// The bot's review is the output of a task.
const { task_id } = coord.api.task.create({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  assignee:  "agent:cursor#v1",
  kind:      "code_review",
  input:     { pr_id: "PR-482" },
});

coord.api.task.complete({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  task_id,
  output:    cursorReview,
});

coord.api.review.request({
  workspace: "wsp_pr_reviews",
  from:      "agent:cursor#v1",
  task_id,
  artefact:  cursorReview,
  to:        "human:me@local",
});

// You disagree with one comment. Override it.
coord.api.decide.override({
  workspace:        "wsp_pr_reviews",
  from:             "human:me@local",
  task_id,
  intent_preserved: true,
  diff: [{ op: "replace",
           path: "/comments/0/severity",
           value: "info" }],
  rationale: "False positive. Framework " +
             "convention, not a bug.",
  tags: ["false-positive",
         "framework-pattern-misread"],
});

|

# The bot's review is the output of a task.
r = send("task.create", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "assignee":  "agent:cursor#v1",
    "kind":      "code_review",
    "input":     {"pr_id": "PR-482"},
})
task_id = r["result"]["task_id"]

send("task.complete", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "task_id":   task_id,
    "output":    cursor_review,
})

send("review.request", {
    "workspace": "wsp_pr_reviews",
    "from":      "agent:cursor#v1",
    "task_id":   task_id,
    "artefact":  cursor_review,
    "to":        "human:me@local",
})

# You disagree with one comment. Override it.
send("decide.override", {
    "workspace":        "wsp_pr_reviews",
    "from":             "human:me@local",
    "task_id":          task_id,
    "intent_preserved": True,
    "diff": [{"op":    "replace",
              "path":  "/comments/0/severity",
              "value": "info"}],
    "rationale": "False positive. Framework "
                 "convention, not a bug.",
    "tags": ["false-positive",
             "framework-pattern-misread"],
})  

About the surfaces. TypeScript ships a typed facade (coord.api.*) so every method gets full autocomplete and compile-time checks. Python keeps the JSON-RPC envelope shape on the surface (coord.dispatch({...})) and consumers wrap it however suits the call site; a send() helper is the idiom the Python tests use. Both paths emit identical wire bytes; the audit chain is byte-for-byte the same regardless of which client made the call.

3. Two months in, analyse what you have been doing. This is where the protocol pays you back. The reference repo ships an analytics script in both languages that reads the audit chain (over HTTP or directly from your SQLite file) and groups overrides:

# TypeScript reference, against the SqliteStore from step 1:
$ npm --prefix reference/core-plus-review run analyze -- --db ./chap.db wsp_pr_reviews

# Python reference, same idea:
$ python3 reference/python/analyze_overrides.py --db ./chap.db wsp_pr_reviews

Override Learning Report
========================
Total overrides: 47

By tag:
  false-positive             ████████████████  31  (66%)
  framework-pattern-misread  ███████████       22  (47%)
  cosmetic-pref              ████              8   (17%)

Top file paths:
  src/handlers/                                    18 overrides
  src/components/                                  9  overrides

Your next prompt revision for Cursor is no longer a guess. It cites the pattern by name.


The override envelope, in detail

The override envelope is the single most important shape in CHAP. Every field has a job:

Anatomy of an override envelope, with each field annotated: task_id links to the PR review chain, from carries queryable identity, logical_id survives revision, intent_preserved separates refining from substituting overrides, diff is RFC 6902 JSON Patch, rationale is the 'why' alongside the 'what', tags are structured supervision data.

The two fields most people miss on first read are intent_preserved and tags.

intent_preserved distinguishes a refining override (the human agreed with the agent's decision but rewrote how it was expressed) from a substituting override (the human reached a different decision). These are two different failure modes and they want different fixes. A high refining rate around one policy clause means the agent's retrieval is off; a high substituting rate on the same clause means the policy itself is ambiguous, or the agent's task context is wrong.

tags is the controlled vocabulary your team agrees on. Keep it small. Whatever you put there is the dimension you will aggregate on three months from now, when you are answering questions like which prompts need work? or which paths is the bot getting consistently wrong?

TypeScript / Node:

npm install @brightbeamai/chap-coordinator

Python:

pip install chap-coordinator

Either path gets you Core plus the review/1.0 profile and a runnable reference. The TypeScript reference is in reference/; the Python reference is in reference/python/. The TypeScript library lives at packages/coordinator/; the Python library at packages/coordinator-py/.

Five-minute hands-on walkthrough: examples/00-five-minute-start.md.

CHAP 0.2 is a public draft. Concretely, this repo contains:

  • The specification. Core (seven methods, one envelope, one wire format) plus eleven optional profiles. Combined into a single document at SPECIFICATION.md, or read individually from core/SPEC.md and profiles/.
  • Two reference implementations. Both cover Core plus every profile, 39 method handlers in total. The TypeScript reference is at packages/coordinator/, with HTTP servers at reference/core/ and reference/core-plus-review/ and a runnable playground with two browser sessions and a local LLM at reference/playground/. The Python reference is at packages/coordinator-py/ with an HTTP server at reference/python/. Both pass the conformance harness on the same JSON-RPC 2.0 wire.
  • A conformance harness. 23 test vectors, signing/canonicalisation/chain checks, in-toto attestation output. Two conformance levels are claimable today (Minimal, Recommended); Full waits on broader interop testing across the two implementations.
  • MCP server transport. A CHAP Coordinator can present itself as an MCP server, exposing every CHAP method as a tool. Point Claude Desktop, Cursor, Claude Code, or any MCP client at it and drive a CHAP workspace from natural language. TypeScript adapter at packages/coordinator-mcp/, Python adapter at chap_coordinator.transports.mcp_server, runnable reference servers at reference/mcp-server-ts/ and reference/mcp-server-py/. Five-minute walkthrough at examples/drive-chap-from-claude-desktop.md.
  • A2A server transport. A CHAP Coordinator can also present itself as an A2A agent, advertising every CHAP method as a discrete skill on its Agent Card. Any A2A-aware orchestrator (Azure AI Foundry, Amazon Bedrock AgentCore, Google ADK, custom multi-agent systems) can register the coordinator by URL and delegate work to it. TypeScript adapter at packages/coordinator-a2a/, Python adapter at chap_coordinator.transports.a2a_server, reference servers at reference/a2a-server-ts/ and reference/a2a-server-py/. Walkthrough at examples/drive-chap-from-an-a2a-orchestrator.md.
  • Inward wrap helpers. Small library utilities that turn an external MCP tool call or A2A exchange into a CHAP task.create + task.complete pair, with hashes of the input/output canonicalisations recorded as citations on the resulting artefact. The library counterpart to the citation patterns in integrations/CHAP-with-{MCP,A2A}.md. Available as wrapMcpToolCall / wrapA2aMessageExchange from @brightbeamai/chap-coordinator, and as wrap_mcp_tool_call / wrap_a2a_message_exchange from chap_coordinator.transports.wrap.
  • Framework bridges. Thin Python adapters that connect a real agent framework's human-in-the-loop mechanism to CHAP's review/decide methods, so an approval, edit, or denial in the framework becomes a decide.approve / decide.override / decide.reject on the audit chain. Five today, each with its own examples and tests, each framework an optional dependency: chap-langgraph (LangGraph), chap-pydantic-ai (Pydantic AI), chap-ag2 (AG2 / AutoGen), chap-llama-index (LlamaIndex Workflows), and chap-google-adk (Google ADK).
  • Twelve worked scenarios. IN_PRACTICE.md walks through real cases from a solo developer with Cursor up to GMP-regulated fill-finish manufacturing. Runnable implementations live in scenarios/, one folder per story (three implemented so far), open to community contributions.

Breaking changes follow Semantic Versioning. Profile surfaces will move faster than Core. Production deployments needing strict stability should wait for 1.0. The longer status statement and the contribution path are in ABOUT.md.

What you get when you adopt this

  • An audit chain that survives key rotation, log expiry, and people leaving. Every envelope links to the previous by content hash. One audit.read call returns the whole thing.

  • Structured supervision data as a side effect of normal work. No separate annotation pipeline. The overrides you are already making become a dataset you would otherwise have to commission.

  • Signed, non-repudiable approvals when you need them. Opt into security-signed/1.0 for OIDC-bound signatures with a signature_meaning you define. Opt into audit-scitt/1.0 for an external transparency-log anchor, verifiable without trusting your servers.

  • Composability with what you have already built. CHAP does not replace MCP or A2A. It sits next to them: your agent uses MCP for tools, A2A for other agents, and CHAP to record the shared work with humans.

  • IN_PRACTICE.md. Twelve real-world scenarios from solo dev to GMP-regulated manufacturing. The most useful next read.

  • ABOUT.md. What is in this repo, how CHAP relates to MCP and A2A, the standards it reuses, and how to contribute.

  • core/SPEC.md. The seven Core methods. The whole protocol surface fits on one screen.

  • Technical report on arXiv. The full paper. Architecture, design rationale, profile semantics, threat model, and a worked appendix with the twelve scenarios as JSON traces. For readers who want the protocol grounded in its design choices.

If you reference CHAP in academic or technical work, please cite the technical report:

@techreport{chap2026,
  author      = {Shahid, Arsalan and Suttie, Gordon and Black, Philip},
  title       = {Collaborative Human-Agent Protocol (CHAP): An open protocol for auditable, structured multi-human and multi-agent collaboration},
  institution = {Brightbeam AI},
  year        = {2026},
  type        = {Technical Report},
  number      = {arXiv:2606.09751},
  url         = {https://arxiv.org/abs/2606.09751}
}

CC-BY 4.0 (specification) · Apache 2.0 (code) · Royalty-free, any language, any deployment.