回应 Fasthei 终审三点: 1) [P1 文档/代码冲突 + 死代码] 删除从不被调用的 *_enabled() helper(autonomous_tasks.proposals_enabled / task_competition.task_competition_enabled / convergence.convergence_report_enabled)及其 import os;模块 docstring 与四份协议文档(autonomous-task-generation / task-competition-protocol / review-loop-protocol / convergence-protocol)从"默认关/未接入/待 PR/cutover 转无条件"全部改为 "无条件接入(无开关)",删除引用死 helper 的过时集成代码样例;同步删除三个模块单测里的 "flag default OFF" 断言。 2) [P1 验收] #6 "Closes" 降为 "Refs":#6 DoD 需 ARB 决策记录链接,当前只有 owner 指示断言、无链接。 product-positioning.md 改为如实记录决策来源(owner 指示 + 本 PR + 文档)并把"补 ARB 记录链接(或 owner 明确接受断言)"列为关闭 #6 的前置;纠正其"flag 门控、默认行为不变"的过时表述(重构已无条件)。 3) [P2 契约卫生] assess_swarm_health 不再 emit_event("swarm.health")(避免向订阅全部的 Manager 回调 投递未注册事件);改为存 run.metadata["health"] + 内部 health_log。test-swarm-guard 相应断言 "无 swarm.health 外发 + 内部 health_log 已记"。 本地受影响 11 套全绿。影响范围:agent_swarm(orchestrator 模块/文档/测试);不改 Manager↔Swarm 契约。 Refs #6 Refs #7 Refs #8 Refs #11 Refs #12 Refs #18 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.0 KiB
Agent 任务竞争协议(Task Competition Protocol)
解决 issue #8:「Agent 自主竞争机制缺失:任务无法被抢占、竞价、协商或重新接管」。
本文档是该机制的唯一入口。机制实现于 orchestrator/task_competition.py(纯仲裁),已接入 WS 主循环(去中心化重构 P4):main.py 新增 task_bid/task_yield/task_takeover_request 分支与 handle_task_bid/arbitrate_and_assign/handle_task_yield/handle_task_takeover——竞价收集→确定性仲裁(τ 加权)→择优 finalize_dispatch 分派;让渡复用 release_task;接管须 decisive 胜出方可重分派。仲裁/竞价/让渡审计存 run.metadata(内部遥测)。feature flag ENABLE_TASK_COMPETITION(构建期;cutover 转无条件)。集成测试 scripts/test-swarm-competition.py。模块单测 scripts/test-task-competition.py。
1. 背景与缺口
当前派发是单向拉取:空闲 Agent 拉取就绪任务(task_queue.get_ready_pending_task),由到达顺序固定 Agent 一侧;ACO 决策引擎(decision_engine.py)在 ENABLE_ACO_DISPATCH=1 时按 P_i = τ^α·η^β / Σ 采样任务,但仍是「Agent 挑任务」的单边匹配。缺失的是 Agent 之间围绕同一个任务的主动博弈:
- 竞价(bid):多个 Agent 同时声明「我能做这个任务」,附上自评置信度、成本、耗时、风险。
- 让渡(yield):持有任务的 Agent 主动释放,给出理由,并可推荐接手者。
- 接管(takeover):另一个 Agent 请求从当前持有者手中接过任务(持有者卡住、或请求者更合适)。
- 仲裁(arbitrate):一个确定性、可审计的裁决器在竞价者中选出赢家,并记录理由与落败者。
2. 接入现状(已无条件接入,无开关)
竞争协议是蜂群固有行为,无 ENABLE_* 开关(本仓即 swarm 运行时)。main.py 始终生效的 WS 分支 task_bid / task_yield / task_takeover_request → handle_task_bid(累积竞价到 run.metadata["bids"])/ arbitrate_and_assign(τ 加权确定性仲裁 → finalize_dispatch 择优分派)/ handle_task_yield(复用 release_task)/ handle_task_takeover(须 decisive 胜出方可重分派)。仲裁/竞价/让渡审计存 run.metadata(内部遥测,非 Manager 事件)。task_competition.py 模块本身仍是纯逻辑 + 数据模型(不碰 Redis/WS/计费/审批链)。
3. 消息 / 数据模型(orchestrator/task_competition.py)
| 模型 | 字段 | 含义 |
|---|---|---|
TaskBid |
task_id, agent_id, confidence, estimated_cost, estimated_time, risk_score, reason, capabilities, current_load |
一次竞价。confidence/risk_score∈[0,1];estimated_cost=占 run 预算比例;estimated_time=秒;current_load=在飞任务数 |
TaskYield |
task_id, agent_id, release_with_reason, recommend_agent? |
主动让渡 + 理由 + 可选推荐接手者 |
TaskTakeoverRequest |
task_id, requesting_agent_id, current_agent_id?, reason, bid? |
接管请求,可携带 bid 以便与持有者在同一标准下被仲裁 |
ArbitrationScore |
agent_id, total, components{} |
单个竞价者的逐项打分明细(审计) |
TaskArbitrationResult |
task_id, winner_agent_id, reason, decisive, scores[], losers[], policy, arbitrated_at |
裁决结果:赢家、人读理由、是否「明确」、全量分数、落败者、所用策略 |
4. 仲裁算法(arbitrate(bids, policy, *, required_capabilities, historical_success))
纯函数,确定性。 每个竞价独立打分(无共享可变状态),然后按 (total 降序, agent_id 升序) 稳定排序,头部即赢家。agent_id 兜底排序消除了对输入顺序和字典迭代顺序的依赖 —— 相同输入恒得相同赢家、相同分数、相同有序落败列表。
每项分量先归一化到 [0,1] 再乘策略权重;「越低越好」的字段(成本/耗时/风险/负载)转为余量 1 - 归一值,使「越高越好」统一成立:
| 分量 | 来源 | 默认权重 |
|---|---|---|
capability |
竞价 capabilities 对任务 required_capabilities 的覆盖率 |
0.25 |
historical_success(τ) |
复用 ACO 信息素 trail 的「挣来的声誉」pheromone:{role}:{id};缺失 → 文档化中性 0.5(对所有竞价同值,不扭曲排序,同 decision_engine 对缺失 confidence 的处理) |
0.25 |
confidence |
竞价自评置信度 | 0.20 |
budget |
成本余量(越便宜越高) | 0.10 |
risk |
1 - risk_score |
0.10 |
time |
速度余量(越快越高) | 0.05 |
load |
current_load 余量(越闲越高) |
0.05 |
权重由 ArbitrationPolicy 提供,非负、无需归一(相对比较)。decisive_margin(默认 0.02):当头两名分差低于它时,结果标记 decisive=False,理由中建议转 Manager 审核而非自动分配 —— 仲裁仍是确定的(赢家=稳定排序头部),但把「过于接近的平局」交回人工/审批链,符合 heicodeDocs 安全与审批约束。
τ 是真实输入:测试断言「其余完全相同、仅声誉不同」时高 τ 者胜,证明 historical_success 真正改变结果而非装饰字段。
5. 事件载荷构建器(builders)
下列函数只构建 payload dict,不发射。形状对齐既有 HM 事件(task_id + 角色/id + 人读 summary):
| 函数 | event_type |
|---|---|
bid_submitted_event(bid) |
task.bid_submitted |
yielded_event(yield_msg) |
task.yielded |
takeover_requested_event(req) |
task.takeover_requested |
arbitrated_event(result) |
task.arbitrated(携带全量 scores 与 losers 审计) |
6. 测试
scripts/test-task-competition.py:模块级、无 Redis / WS / 模型(task_competition 无副作用,直接测真实仲裁数学)。覆盖:两 Agent 同任务竞价→更优者胜并记录可审计理由与落败者;输入乱序 / 重复调用结果字节一致(确定性);τ 单因子决胜;死平局不 decisive 且兜底确定 + 建议审核;空竞价;让渡(带理由 + 推荐);接管请求并与持有者仲裁;自定义策略权重改变结果;全部事件 builder。
运行(在 agent_swarm_v6 下,先装依赖):
pip install -r orchestrator/requirements.txt
..\.venv\Scripts\python.exe scripts/test-task-competition.py
结果:35/35 PASS(ALL PASSED)。
集成现状(已接入)
已落地于 orchestrator/main.py(无开关):
- WS 入站分支(紧邻
peer_message/handoff_request):task_bid→handle_task_bid(按 agent 去重累积到run.metadata["bids"]);task_yield→handle_task_yield(复用task_queue.release_task,不增 retry_count);task_takeover_request→handle_task_takeover(请求者 bid 与持有者中性 bid 一并送arbitrate)。 - 仲裁分派:
arbitrate_and_assign(run, task_id)取run.metadata["bids"]→arbitrate→ 赢家经finalize_dispatch分派;审计存run.metadata["arbitrations"]/["yields"]。 - τ 注入:
historical_success由decision_engine.get_tau(...)逐竞价者取值(经normalize_tau)传入arbitrate,与自选/ACO 共用同一声誉源。 - 事件不进 Manager 流:
task.bid_submitted/yielded/takeover_requested/arbitrated的 builder 已实现但不经emit_event外发(未在agent_callback.go注册;与peer_message/swarm.health同策略——仅内部遥测)。登记后方可启用 Manager 侧发送。 - 审批 / 计费 / 审计(后续硬约束):
decisive=False(接近平局)或接管已分配任务时,按 heicodeDocs 应走 Manager 审批链而非自动改派;当前实现仅在 decisive 胜出时改派,平票不改派(记录待 review)。任何改派保留usage与X-Agent/X-Agnet归属。专用「竞价窗口」收集期(定时触发arbitrate_and_assign)为后续优化。