mcpField
C
concord-engine

concord-engine

本地服务
github.com/harsh-chandakAI 与智能体01 周前更新

关于 concord-engine

concord-engine 是面向并行编码 Agent 团队的协调工具,通过契约和决策日志约束各工作单元,防止它们对同一问题作出冲突选择。它以规则比较而非模型调用来检测矛盾,并提供技能、MCP 服务端和 CLI 三种使用方式。

主要功能

  1. 01编写并验证任务契约
  2. 02将契约决策绑定到追加式日志
  3. 03按契约向工作单元生成简报
  4. 04比较决策并拒绝冲突选择
  5. 05运行验收检查并生成文档和成本报告
  6. 06通过 CLI 检查冲突

适用场景

  1. 01将跨 API、客户端和模式的任务拆分给并行 Agent
  2. 02在冲突决策写入时阻止不一致实现
  3. 03在 CI 中检查决策日志是否存在矛盾
  4. 04为多个工作单元生成简报并运行验收

MCP 相关

MCP 服务端向 Agent 提供契约编写与绑定、工作单元简报、并行计划、验收、文档生成、成本报告、决策记录、冲突查询及状态等工具。决策比较由规则完成,不调用模型。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"harsh-chandak-concord-engine": {
"command": "npx",
"args": [
"-y",
"concord-engine"
],
"env": {
"CONCORD_ENGINE_DIR": "<CONCORD_ENGINE_DIR>"
}
}
}
}
启动命令npx -y concord-engine
需要配置的环境变量
CONCORD_ENGINE_DIRWhere the decision log lives. Defaults to .concord-engine in the working directory. Commit it — the log is the shared contract between your agents.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmconcord-engine0.3.2

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

Concord Engine

扩展并行度。保持一致性。

描述一项任务。一组 Agent 便会构建它。它们交付的成果已完成、经过测试并编写了文档,而且彼此一致,因为它们都不能违背其他 Agent 已经做出的决定。

并行 Agent 无法在没有人工介入的情况下持续扩展。你启动 5 个 Agent 想要提速,其中 4 个做对了,产品却坏了,而唯一能发现问题的人就是你——你得阅读所有内容、掌握全貌,还要重新解释已经定下的选择。

这不是提示词问题。你的 Agent 有记忆,也有验证,但两者都回答不了真正会导致并行运行失败的问题:

这两个 Agent 刚刚是不是对同一个问题做出了不同决定?

Concord Engine 通过比对来回答这个问题,从不依赖模型,并会在第二个答案写入的那一刻将其拒绝。

真实的 Claude Code 会话:两个 Worker 在退款功能上对错误格式做出了不同决定;第二个 Worker 被拒绝,并获知当前生效的决定、做出决定的人及其原因,随后运行验收检查。

实际录制,并非绘制。brag/record.mjs 会运行真实的 CLI 并逐字捕获其输出;brag/video.html 只负责呈现捕获的内容。这个循环展示了拒绝流程——完整运行还会先编写契约、绑定契约并向 4 个 Worker 提供简报。

什么是决定

这一点常常被低估。“决定”不只是一个变量名,而是任何两个 Agent 都可能给出不同答案、且双方都言之有理的问题——代价从来不在问题本身,而在于没有任何地方会报错。

类型实际示例
convention金额以整数的最小货币单位表示,绝不使用浮点数 · 时间戳使用 UTC ISO-8601 字符串 · 分页基于游标 · 所有非 2xx 响应体都采用 {"error":"<message>"} · 重试具有幂等性,且最多重试 3 次 · 迁移分两个可逆阶段发布
api-contractPOST /api/orders 返回 201,而不是 200 · 租户范围由路径确定,而不是由请求头确定
enum订单状态为 pending|paid|shipped · 角色为 owner|admin|member · Webhook 事件使用封闭集合
schemaid 是 uuid 且不可为空 · amount_cents 是整数 · 软删除使用 deleted_at,而不是布尔值
naming标识符为 user_id · 目标字段为 url,而不是 target

4 个 Agent 分别采用 4 种合理的答案来实现“错误响应体应是什么样”这一问题,最终会得到 4 套通过测试的测试套件和一个坏掉的产品。这就是问题的全部范畴,而且范围远不止路由路径。

它能阻止的问题

一项退款功能拆分给后端 Worker 和客户端 Worker。后端确定了错误格式。客户端并行工作,无法查看后端文件,于是得出了不同的答案:

$ concord-engine decide --unit client --kind convention \
    --subject "error response body shape" \
    --value 'every non-2xx body is {"code":"<slug>","detail":"<message>"}' --as client

refused this contradicts a decision already in force.
  d-003  api (lead): error response body shape = every non-2xx body is {"error":"<message>"}
  because: the client shows it to a person
  yours:   every non-2xx body is {"code":"<slug>","detail":"<message>"}

  Adopt the existing value, or supersede it deliberately:
    --supersedes d-003 --layer pm      (an override, recorded as one)
    --force                            (record the disagreement for `check` to surface)

以上内容来自录制视频,未经改动。

Agent 一试图提出不同意见,就会立刻被拦下,并获知当前生效的决定、决定者、原因,以及两种合理的解决方式。不是等到集成时才拦截,也不需要你出面。

三种运行方式

作为技能使用,这是大多数人的选择。 描述任务,Agent 就会运行完整流程——编写契约、绑定契约、向每个 Worker 提供简报、分派任务、把关每项决定、运行验收检查并编写设计文档。

/concord-engine:concord-engine add refunds across the API, the client and the schema
/concord-engine:concord-engine migrate us off the legacy auth tables, five workers
/concord-engine:concord-engine rename the tenant concept to workspace everywhere

任务不必很小,也不必是路由相关的任务。

作为 MCP 服务使用,这样已经开始执行任务的 Agent 无需接收工作流,也能访问把关机制。提供 12 个工具:write_contract、bind_contract、brief_unit、fleet_plan、run_acceptance、generate_docs、cost_report,以及 record_decision、binding_decisions、open_conflicts、record_attempt、status。

作为 CLI 使用,供人类和 CI 使用。concord-engine check 在发现任何矛盾时会以非零状态码退出,可作为流水线中的一个命令步骤。

为什么这不是又一个记忆工具

工具回答的问题无法回答的问题
交接 / 记忆工具“上一个 Agent 知道什么?”“这两者是否一致?”
编排器验证“这个工作进程完成任务了吗?”“这两者是否一致?”
编辑冲突检测“两个 Agent 是否改动了同一个文件?”“这两者是否一致?”

而回答这个问题最显而易见的方法——将决策以散文形式存储,再询问模型两者是否冲突——速度慢,每次检查都要调用一次,而且错误率高到让人们不再信任它。没人信任的协调层比没有协调层更糟,因为它会给原本没有延迟和误报的工作流增加延迟和误报。

Concord Engine 通过比较来判定是否一致,绝不依赖模型。 决策有明确类型且范围狭窄,因此矛盾可以通过比较来判断:主题相同,值不同。决策路径中的任何位置都不会调用 API——有一项测试会在该路径中的任何文件能够访问进程外部时失败;还有第二项测试会在包中任何位置的文件调用模型时失败。

工作循环

上面的门控只是一个器官。v0.2 是围绕它构建的整个身体:你描述一项任务,而负责构建它的 Agent 集群从第一行代码开始之前,就受同一组决策约束。

  describe a task
        |
   [1]  CONTRACT     the agent authors JSON to a published schema;
        |            the package validates it and stores it
        v
   [2]  BIND         the contract's decisions enter the log at PM layer,
        |            binding before any code exists
        v
   [3]  BRIEF        one brief per unit: its slice, the decisions it must
        |            honour, what it may not decide, how it will be judged
        v
   [4]  FAN OUT      the host spawns one worker per unit, each getting a
        |            brief and nothing else
        v
   [5]  GATE         a worker that tries to decide something contradictory
        |            is refused, told what is in force and who may override
        v
   [6]  ACCEPT       every acceptance criterion carries a command; the run
        |            is not done until all of them exit 0
        v
   [7]  DOCS         the design doc is generated from the decision log —
        |            what was decided, by whom, and why
        v
   [8]  COST         what the fan-out sent, in bytes
concord-engine spec --schema              # the contract shape, as JSON Schema
concord-engine spec --from contract.json  # validate and store it
concord-engine bind   --contract add-refunds
concord-engine fleet  --contract add-refunds            # every brief, as JSON
concord-engine brief  --contract add-refunds api        # one worker's brief
concord-engine accept --contract add-refunds            # run every check
concord-engine docs   --contract add-refunds
concord-engine cost   --contract add-refunds

每个循环命令都需要 --contract。MCP 服务通过 write_contract、bind_contract、brief_unit、fleet_plan、run_acceptance、generate_docs 和 cost_report 暴露同一组功能。

合约

一个 JSON 文档。由 Agent 编写;由包判断其是否有效。

openSubjects 是人们常会跳过、但不该跳过的部分。在任何人做出决定之前,先为某个事物命名,才能阻止两个工作进程为它各自创造不同的名称——请参阅诚实的局限,了解这导致问题的那次运行。

{
  "id": "add-refunds",
  "goal": "one sentence a reviewer can judge the result against",
  "scope": { "in": ["..."], "out": ["..."] },
  "decisions": [                      // binding from t=0, at PM layer
    { "kind": "api-contract", "subject": "refund endpoint",
      "value": { "method": "POST", "path": "/api/refunds", "status": 201 },
      "rationale": "why this and not the alternative" }
  ],
  "openSubjects": [                   // known to need deciding, not yet decided
    { "kind": "convention", "subject": "error response body shape",
      "note": "the server and the client both serialise this" }
  ],
  "units": [                          // one worker each
    { "id": "api", "goal": "...", "files": ["src/api/**"],
      "mayDecide": ["convention"],    // everything else must be requested
      "dependsOn": [] }
  ],
  "acceptance": [
    { "id": "a1", "statement": "refunds endpoint returns 201",
      "check": "npm test -- refunds", "unit": "api" }
  ]
}

验证不是走过场。当合约中的两个决策彼此矛盾、某个单元依赖不存在的单元、依赖图存在环、某个单元没有验收标准,或某项标准没有对应命令时,合约都会被拒绝。所有检查一次完成,因为每轮往返只修复一个错误,是找出四个错误最昂贵的方式:

$ concord-engine spec --from contract.json

concord-engine: contract is not valid:
  - unit "api" depends on "ui", which is not a unit in this contract
  - acceptance[a1] has no check — a criterion without a command is a wish

接下来,bind 会将合约中的决策写入 PM 层的日志。如果其中某个决策与已有的生效决策矛盾,绑定就会拒绝——新合约悄然覆盖现行决策,正是这个工具要防止的情况。

单个工作进程会收到什么

一份简报,而不是对话。这就是编写此文件的单元实际收到的简报。原文照录,仅在标注处删节:

$ concord-engine brief --contract concord-engine-v2 readme

You are one worker on "concord-engine-v2". Your unit is "readme".

GOAL
  Rewrite README.md around the loop without losing the v0.1 pitch that the gate is the product.

FILES YOU OWN
  README.md
  Do not edit files outside this list. Another worker owns them.

DECISIONS ALREADY IN FORCE — these are not suggestions
  - [convention] model calls inside the package = no file in the package may call a model or open a network connection; enforced by a test  (the one defensible sentence is that agreement is decided by comparison, never by a model — if authoring moves inside the package that stops being true)
  - [naming] cli flag for selecting a contract = --contract  (every loop command takes it, so it has to be the same word everywhere)
  ... 3 more decisions in force

WHAT YOU MAY DECIDE
  nothing — you write code and decide nothing

WHAT YOU MUST NOT DECIDE ALONE
  naming, convention, api-contract, enum, schema

RECORD A DECISION LIKE THIS — the flags are what enforce the two lists above
  concord-engine decide --contract concord-engine-v2 --unit readme \
    --kind <kind> --subject "<what>" --value '<value>' --as readme --rationale "<why>"

  Omitting --unit skips the check on what you may decide. Use the MCP tool
  record_decision if you have it; it gates the same way.
  A refusal means a decision already covers this: adopt the existing value.
  Do not work around it, and do not --force it.

DONE MEANS
  [a-readme] the README covers the loop and keeps the gate pitch
       $ test -f README.md && grep -q 'spec --schema' README.md && grep -q 'refus' README.md && grep -q -i 'estimate' README.md

fleet 会一次性输出所有简报,并根据 dependsOn 分组为多个波次,让宿主知道哪些任务可以并行运行。

包负责判断什么,不负责判断什么

包绝不会调用模型。 Agent 负责编写;宿主负责启动;包负责判断所有可以通过比较来判断的事项。

由模型编写由包判定
合约的内容✅
合约格式是否正确✅
每个工作进程会收到什么✅
每个工作进程编写的代码✅
某项决策是否与现行决策矛盾✅
验收标准是否通过✅
设计文档的内容✅(由日志生成)

右侧每一项都通过比较、架构检查或子进程退出码来判定。两项测试确保这种分工得到遵守:决策路径中的任何内容都不得以任何方式离开进程;任何文件都不得访问模型,包括用于启动子进程的 accept。

这才是差异化所在,而不是缺陷。扇出只是一个循环加一段提示词;每个编排器都有。没人做到的是让集群的决策彼此具有约束力,而只有确保路径中没有模型,才能让这一机制始终可信。

安装

claude plugin marketplace add harsh-chandak/shadowjoin
claude plugin install concord-engine@harsh-chandak

harsh-chandak marketplace 托管在 shadowjoin repo 中,且包含这两种工具——第一行并没有写错。

然后描述工作内容,让它运行整个循环:

/concord-engine:concord-engine add refunds across the API, the client and the schema

或者不使用 Claude Code:

npx concord-engine --help

使用

负责人和 PM 做决定。工作者编写代码,不做任何决定。

# a decision, gated: --unit enforces what this unit is allowed to settle,
# and the subject must be one the contract declares
concord-engine decide --contract add-refunds --unit api \
  --kind convention --subject "error response body shape" \
  --value 'every non-2xx body is {"error":"<message>"}' \
  --as api --rationale "the client shows it to a person"

concord-engine check     # exits 1 if anything contradicts
concord-engine verify    # are the decided routes actually served?
concord-engine report    # how often did a conflict reach you?

在合同之外,decide 仍会拒绝与生效中决定相矛盾的决定——只是无法检查某个单元获准解决什么问题,因为没有 --unit 就无从得知。

verify 指路由验证,与 v0.1 中一样。验收标准由 accept 运行。

共有五种类型,各自采用不同的比较规则:

类型值比较方式
naming"user_id"精确匹配
convention"snake_case in payloads"精确匹配
api-contract{"method":"POST","path":"/orders","status":201}结构比较,忽略尾部斜杠和参数命名
enum["pending","paid","shipped"]集合相等
schema{"field":"id","type":"uuid","nullable":false}结构比较

日志所在位置

工作目录中的 .concord-engine/decisions.jsonl——仅追加的 JSON Lines 文件,每行一项决定,无需解析器即可读取。合同存放在旁边的 .concord-engine/contracts/ 中。

请提交它。 这份日志是 Agent 之间的合同,因此必须像迁移文件一样随代码一同保存。设置 CONCORD_ENGINE_DIR 或传入 --dir 可将其放在其他位置;CLI 和 MCP 服务以相同方式解析位置,因此 check 始终会审计服务写入的文件。

谁来解决什么问题

worker vs worker   ->  team lead
lead   vs lead     ->  PM
PM can't resolve   ->  you

裁决依据层级确定,而非主观判断。PM 的决定优先于负责人的决定——这是覆盖,而非冲突。两位负责人意见不一时交由 PM 处理。两位 PM 意见不一时则交由你处理,因为没有人的权限高于他们。

每个层级尝试两次,然后升级处理。提交给你的是一份材料包——包含双方立场、各自的提出者、理由以及已经尝试过的方法:

CONFLICT c-01fc0217  naming: user identifier in API payloads
    backend-lead (lead)   user_id  "matches the users table column"
    frontend-lead (lead)  userId   "matches the TypeScript style guide"

    tried: proposed user_id; frontend-lead rejected, 3 call sites already public
    tried: proposed an alias field; rejected as duplication
    owner: you

而不是“出了点问题,去看日志吧”。

没人写的设计文档

docs 根据决定日志生成运行记录:目标、范围内和范围外的内容、每项生效中决定的作者、时间戳和理由,以及证明工作成果的命令表。它由系统生成,因此不会偏离实际作出的决定——这是第一份无需花费任何代价就能保持更新的设计文档,因为根本没人需要写它。

真正重要的数字

$ concord-engine report

  47 conflicts recorded
    settled without you     44
    reached the PM          11
    reached you              3   6.4%

其他所有编排器都会报告启动的 Agent 数量、消耗的令牌数和完成的任务数。没有一个会报告系统是否能自行处理事务——而这才是唯一能说明运行十个 Agent 是否真的比运行一个更省钱的数字。

扇出带来的成本

节省是由结构带来的,而非取巧:工作者收到的是任务简介,而不是对话内容。 任务简介是一个单元的部分内容,加上约束该单元的决定——体量小、固定不变,且在整个运行过程中保持一致。对话记录则不具备这些特点。

该软件包衡量的是字节数,因为字节数是它能够统计的。使用本仓库自身的合同运行——五个单元,五项约束性决定。克隆并运行相同命令即可复现:

$ concord-engine cost --contract concord-engine-v2
  mcp                2333 B brief   vs 4132 B whole contract
  skill              2470 B brief   vs 4132 B whole contract
  tests              2286 B brief   vs 4132 B whole contract
  readme             2297 B brief   vs 4132 B whole contract
  e2e                2214 B brief   vs 4132 B whole contract

  11600 B sent instead of 20660 B — 44% less
  ~2900 vs ~5165 tokens, ESTIMATED at 4 chars/token.
  No tokenizer ships with this package; these are divisions, not counts.

它输出的每个令牌数都标注为估算值,并说明所采用的每令牌字符数假设。基准值是每个工作者收到的完整合同,而非你的真实会话——你的实际节省会更多,该工具不会猜测具体多出多少。

示例详解

examples/linkfold.contract.json 是本文反复引用的那次运行所用的合同:一个由四位工作者构建的 URL 缩短器,他们从未看过彼此的代码,最终却成功运行——包含 55 个测试,以及三个单元之间真实的 HTTP 往返通信;这三个单元遵循相同的线上格式,却从未讨论过该格式。

该合同包含原始运行中缺失的 openSubjects,这就是原始运行会以两个名称解决错误格式问题两次的原因。CI 会在每次推送时重新验证它。

concord-engine spec --from examples/linkfold.contract.json
concord-engine fleet --contract linkfold
concord-engine brief --contract linkfold api

诚实的局限

  • 只会检查已记录的内容。 如果负责人用文字决定了某件事,却从未记录下来,本工具就无从得知。

  • 主题按措辞匹配,而非按含义匹配。 大小写、标点和重音符号都会标准化,因此「User Identifier」和「user identifier」会被视为相同。两个 Agent 即使描述的是同一件事,只要用词确实不同,就不会匹配。这是本工具最明显的短板,而且并非纸上谈兵:在首次将此工作流用于另一个项目时,两名工作者分别以「error response body shape」和「api error response body」为主题确定错误格式,选用了互不兼容的负载,而 check 没有报告矛盾,因为它从未对这两个主题进行比较。

    缓解方法是使用 openSubjects:在任何人作出决定前,先在契约中命名相关事项。这样,decide --contract <id> --unit <id> 遇到契约中未声明的主题时就会拒绝,并提供契约中已声明的写法。如此一来,主题身份便通过查找确定,而非靠猜测。按相似度匹配主题也能弥补这一缺口,但这会让判断重新进入决策流程——而本软件包绝不会这样做。

    残余风险确实存在:未带 --contract 记录的决定,或使用 --new-subject 记录的决定,仍会再次按措辞匹配。

  • verify 只能识别 Express 风格的注册方式。 它会查找 app/router/server/api 后跟方法调用的模式,以及 FastAPI 和 Flask 装饰器。基于 node:http、通过检查 req.url 进行路由分发的普通服务则无法识别。若将匹配范围扩大到任意标识符,cache.get("key") 也会被误认为路由,因此匹配范围仍保持狭窄,报告的结果也会少于实际情况。

  • verify 只读取字面路径。 挂载在前缀下的路由器,或运行时构建的路径,都无法通过静态提取发现。它宁可少报,也不会乱指控,并会显示读取的文件数量,以免误把该数字当成完整结果。

  • 它不会启动工作者。 fleet 会生成任务简报;由你的主机负责启动工作者。若软件包通过 Shell 调用另一个 Agent CLI,就会继承该工具的身份验证方式、速率限制和故障模式,也就不再是一个库。

  • 它不会编写契约。 编写契约需要调用模型,而模型调用发生在软件包之外。

  • 它不会计算令牌数。 软件包不附带分词器。它会测量字节数;令牌数则是估算值,并会明确标注。

  • 它不会验证工作是否完成。 accept 会运行契约中指定的命令。如果契约指定的命令不够严格,它仍会通过。

  • 它不会评判哪项决定更好。 它会报告两项决定无法同时成立,并指出由谁裁定。

  • 工作者仍可绕过关卡。 不带 --unit 使用 decide,会跳过对该单元可作何种决定的检查;使用 --force 则会故意记录矛盾。这两种情况都会显示在日志中,并由 report 统计。关卡是护栏,不是沙箱。

  • 仅支持五种类型,这是有意为之。 dependency 会暂缓支持,直到正确实现 semver 范围交集;如果比较器把 ^1.0.0 和 ^1.2.0 判定为不兼容,那还不如完全没有比较器。

许可证

MIT © Harsh N. Chandak

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。