Hive 源码分析:Queen、Tracker 与 Playbook 如何组织长期 Agent 工作
AI 参与说明(Agent:Codex):本文依据 Hive
1.0.11、固定提交022f455c9c889b608e7318d32a151124cc134639的官方文档与源码整理,资料核验于 2026-10-22。离线验证覆盖工具分层、Tracker 与 Playbook;选型和取舍属于本文的工程判断。执行入口为 Codex Desktop;完整模型标识与 reasoning effort 未取得任务级运行记录。
Hive 值得研究的地方,是它怎样把一次探索性的 Agent 工作变成可以批量执行、查询进度和再次运行的业务过程。当前内置主路径围绕 Queen、Worker 和 SQLite Tracker 展开:Queen 先做一个样本,把有效方法写成 Skill,再用 Playbook 派工;Worker 把结果落到 Tracker,Queen 查表检查剩余工作。
这套设计适合线索研究、资料整理、账户检查等包含许多相似工作单元的任务。它的价值集中在执行过程的组织:把方法、工作状态、模型上下文分别保存,让任务不必始终依赖一段越来越长的对话。
先认识几个术语
| 英文术语 | 中文名称 | 简要解释 |
|---|---|---|
| Agent Harness | 智能体运行支架 | 包围模型的工具执行、状态保存、预算与交互控制机制 |
| AgentLoop | 智能体循环 | Hive 中请求模型、执行工具、接收结果并继续运行的执行类 |
| Queen | 蜂后 | 与用户交互、管理任务和派工的长期 Agent 角色 |
| Worker | 工作 Agent | 接受具体任务、使用受限工具并向 Queen 报告的执行角色 |
| Colony | 蜂群 | 围绕一个过程组织的 Queen、Worker、数据与可复用方法 |
| Tracker | 进度台账 | Colony 共享的 SQLite 数据库,保存每个工作单元的结果与状态 |
| Skill | 技能 | 可按需加载的操作说明,保存已经试过的方法和注意事项 |
| Playbook | 执行手册 | 调用 Hive 派工接口的 Python 脚本,查询未完成记录并推动处理 |
| Compaction | 上下文压缩 | 清理或概括旧消息,使长期会话能继续放进模型上下文 |
版本边界:先放下旧版 graph evolution 的印象
本文分析的 core/pyproject.toml 标记版本为 1.0.11,源码提交于 2026-10-20(Asia/Shanghai)。后文源码链接均固定到该提交,避免 main 更新后论据漂移。版本定义
当前官方文档明确说明,早期“由 Coding Agent 跨代改写工作流图”的 evolution 叙事已经退场,改进改由会话反馈、长期记忆、Skill 与 Playbook 承担。How a Colony Improves
The built-in Queen and Colony path uses AgentLoop instances and runtime tool calls for orchestration. Queen 在运行过程中决定派出哪些 Worker,无须先编译一张固定工作流图。Queen 构造入口,Worker 构造入口
但仓库仍保留声明式 graph 的 Orchestrator、NodeWorker 和加载兼容代码。阅读时应区分“当前内置产品主路径”和“仓库还支持哪些执行接口”,不能把文档中的“没有 graph”理解成源码已删除所有 graph 模块。GraphSpec 加载,Orchestrator
功能、代码组织与运行架构
Hive 同时提供可直接使用的应用和可嵌入其他项目的 Python 执行原语。应用包含本地服务、React 界面、Queen、Colony、工具与持久状态;开发者可以从 hive 包导入 AgentLoop、AgentSpec、Provider 等原语。framework 是内部实现,tools 包承载内置工具与外部服务集成。公开导出,Building on Hive
| 功能 | 主要代码入口 | 实际职责 |
|---|---|---|
| 用户会话与 Queen | core/framework/server/queen_orchestrator.py | 构造循环、注入工具和记忆、把 Worker 报告送回会话 |
| 模型与工具循环 | core/framework/agent_loop/ | 流式请求、调度工具、等待输入、压缩和恢复 |
| Worker 编排 | core/framework/host/colony_runtime.py、worker.py | 派工、并发排队、停止、恢复和预算调整 |
| 共享业务状态 | core/framework/host/tracker_db.py、tools/tracker_tools.py | SQLite 表、可写列注册、查询和 upsert |
| 批量执行 | core/framework/host/playbook/runner.py、tools/playbook_tools.py | 运行脚本、查询剩余项、派工、重试、限速与失败清单 |
| 经验与能力发现 | core/framework/agents/queen/、skills/、tools/tool_tiers.py | 记忆提取、召回、Skill 加载和工具按需展开 |
| 模型与工具适配 | core/framework/llm/、tools/harness_tools.py、仓库根 tools/src/ | Provider 协议、进程内工具以及外部 MCP 接入 |
| 用户界面与服务 | core/frontend/、core/framework/server/ | 展示会话、Tracker、计划和 Worker,接收实时事件 |
下图画的是 Queen/Colony 主路径。箭头分别表示执行调用、业务数据写入和报告回流。
%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 48}}}%%
flowchart TB
User[用户] -->|目标与决策| Queen[Queen / AgentLoop]
Queen -->|run_worker| Runtime[ColonyRuntime<br/>创建或排队]
Queen -->|run_playbook| Playbook[PlaybookRun]
Playbook -->|dispatch_one| Runtime
Runtime --> Workers[Worker / AgentLoop]
Queen -->|建表、注册可写列<br/>与查询| Tracker[(Tracker / SQLite)]
Workers -->|tracker_upsert| Tracker
Tracker -->|查询未完成项| Playbook
Workers -->|SUBAGENT_REPORT| Bus[EventBus]
Bus -->|注入报告| Queen
Queen -->|write_skill| Skills[Colony Skills]
Skills -->|按需加载| Workers
依据:架构总览、Playbook 接线。
几个核心数据对象
| 对象 | 保存什么 | 为什么需要单独存在 |
|---|---|---|
AgentSpec | 身份、提示词、工具策略、输出键、报告格式与可选成功标准 | 声明一个 Agent 能做什么 |
AgentContext | 本次执行的输入、模型、工具、目标和运行标识 | 把声明配置与实际执行资源分开 |
ColonyBinding | 不可变的 Colony 名称、目录和 Tracker 路径 | 避免会话 ID 与业务目录混用,读写到错误数据库 |
| Tracker 业务行 | 工作单元的主键、结果、证据和完成字段 | 让业务进度可以查询并跨运行保存 |
| 会话 cursor | 当前轮次、等待状态、Stop、工具累计消耗与反循环状态 | 恢复执行控制,超出普通聊天记录的范围 |
PlaybookRun | 本次派工计数、lane、日志与 dead letter | 管理一次执行;已完成业务记录仍以 Tracker 为准 |
来源:AgentSpec / AgentContext、ColonyBinding、cursor、PlaybookRun。
一次任务怎样从输入变成结果
以“核对一批产品的公开发布记录”为例,Queen 先确认哪些记录算有效,定义并注册 Tracker 的业务行和完成条件,再亲自处理一个产品,把证据与结果写入首行。这个 pilot 用于发现筛选规则和工具路径。随后 Queen 保存 Skill,编写 Playbook,派出 Worker 处理余项。
这段“先做一个,再固化方法”的过程主要写在 Queen 的操作提示中,并不是一个保证 pilot 已成功的强制状态机。真正的运行检查要看工具实现:例如新派工的 run_worker 会要求存在 ColonyBinding,且 Tracker 至少有一张已注册的 Worker 可写表,否则直接返回错误。pilot 操作规程,派工前检查
沿一条实际调用链看,run_worker 先过滤工具范围、附加 ColonyBinding,再调用 ColonyRuntime.spawn_batch()。Runtime 创建 Worker 的 AgentLoop、独立会话存储与 Context,并决定立即运行还是排队。Worker 调用 tracker_upsert 写结果,以 report_to_parent 报告;终止报告经 SUBAGENT_REPORT 进入 Queen 的会话。run_worker,spawn_batch,报告注入
%%{init: {"sequence": {"width": 150, "actorMargin": 16, "wrap": true, "diagramMarginX": 10}}}%%
sequenceDiagram
participant Q as Queen / AgentLoop
participant R as ColonyRuntime
participant W as Worker / AgentLoop
participant T as Tracker
Q->>R: run_worker → spawn_batch
R-->>Q: 返回 Worker 标识<br/>与派工状态
Note over Q,W: Queen 可继续响应<br/>Worker 按并发限额运行
R->>W: 独立 Context<br/>工具与预算
W->>T: tracker_upsert<br/>保存结构化结果
W-->>Q: report_to_parent,经 EventBus<br/>注入 WORKER_REPORT
Q->>T: 查询结果与缺口
Note over Q,T: 报告 success 仍需检查业务行
A Worker reuses the same loop implementation, with its own context, tools and budget. 文档中的 clone 应理解为复用执行原语;Queen 与 Worker 并非共享同一段完整对话,也不必使用同一模型。派工时会过滤 Queen 专属工具,Worker 也不继承 Queen 的 persona。Worker 构造,工具过滤
直接派工的默认并发数为 4,多出来的任务进入队列。Worker 默认有 3 个工作 turn 和 1 个收尾 turn;默认每 turn 工具预算为 30、硬倍数为 3,累计工具预算为 200。收尾阶段只开放报告、Tracker 写入和任务更新等少量工具。这些默认值及可调边界共同限制一个卡住的 Worker 能继续消耗多少资源。并发与可调字段,Worker 默认配置
Tracker:把“做到了哪里”变成数据
Tracker 是这套架构最关键的协作接口。Queen 用 tracker_sql 建表,再用 tracker_register_writable 声明 Worker 可以写哪些列、用哪些键识别业务行。Worker 通过 tracker_upsert 写结果,共享的 tracker_query 则只允许读取。Tracker 工具契约
例如,下面的 SQL 是本文的业务建模示例:每个产品占一行,核验结束时写入 checked_at,outcome 则区分找到与未找到。未找到有效记录也可以是已完成的研究结果。
CREATE TABLE launches (
product TEXT PRIMARY KEY,
outcome TEXT,
source_url TEXT,
checked_at TEXT
);
SELECT product
FROM launches
WHERE checked_at IS NULL;
这里的完成条件是 checked_at IS NOT NULL。只用 source_url IS NOT NULL 会把“已核对但未找到”的产品不断派回去。Hive 提供的是查询和写入原语,业务完成条件仍要由 Queen 或使用者正确建模。
实现中还有两处值得借鉴:
ColonyBinding把 Colony 身份和数据库路径一起传递。正常 Colony 路径下,工具取不到 binding 就拒绝执行,避免通过一个含义不清的会话 ID 猜路径。tracker_query不只是检查 SQL 是否以SELECT开头;它拆分语句、识别 CTE 的有效主语句,并结合底层禁用规则拒绝写入、多语句脚本和数据库附加等操作。
来源:binding 解析、读取约束。
A persisted row makes progress inspectable; it does not make the row correct. Queen 仍需核对证据、业务规则和异常行。数据库解决了状态持久化和查询问题,内容真实性还需要单独检查。
Playbook:用确定代码推动不确定的 Worker
自由探索适合交给模型;“再查一次还有谁没完成、该并发多少、失败后是否重试”适合写成普通程序。Playbook 把这两部分接在一起。
PlaybookRun 只依赖两个注入接口:dispatch_one 派出一个 Worker 并等待报告,query_rows 查询 Tracker。脚本调用 converge() 后,每轮重新获取未完成记录,派工,再查询;达到轮数上限或触发失败比例阈值时停止,最后把仍未完成的记录列入 dead letter。Runner 依赖边界,converge 实现
Playbook completion is derived from the pending query, not from a successful Worker receipt. 一个 Worker 即使报告 success,只要业务行仍满足 pending 条件,后续轮次就仍会看到它。反过来,重新运行时已经完成的行不再进入派工清单。
还要分清两种重试:worker(retries=...) 根据失败的 Worker 生命周期报告重试;converge(max_rounds=...) 则通过重新查询业务缺口决定下一轮工作。Runner 不会因为成功回执的形状不合预期,就在 Queen 侧盲目重做已经完成的外部操作。worker 重试与回执说明
lane 同时提供并发信号量和起始速率限制:前者限制多少 Worker 同时执行,后者限制多快开始下一次派工。它能控制派工节奏,但不能保证 Worker 内部每次第三方 API 调用的速率。RateGate,lane 调度
离线实验:success 之后忘写 Tracker 会怎样
下面的实验运行真实 run_playbook_script,只把 Worker 和查询接到本地 SQLite。a 正常完成,b 第一次声称成功却忘记写表,c 在修复前持续失败。每次运行都新建 Runner,只复用同一个数据库文件。
在固定源码检出中,将代码保存为 playbook_demo.py,以 Python 3.11+ 和 uv 执行:
PYTHONPATH=core uv run --no-project python playbook_demo.py
展开完整示例:无需模型、网络或 API Key
import asyncio
import sqlite3
import tempfile
from collections import Counter
from pathlib import Path
from framework.host.playbook.runner import run_playbook_script
SCRIPT = '''
meta = {"name": "receipt-vs-tracker"}
async def run(args):
return await converge(
pending=lambda: tracker_query("SELECT id FROM jobs WHERE done=0 ORDER BY id"),
dispatch=lambda row, i: worker(task=row["id"], data=row, retries=0),
max_rounds=2, chunk=1,
)
'''
async def main():
with tempfile.TemporaryDirectory() as tmp:
db = Path(tmp) / "tracker.db"
attempts, repaired = Counter(), False
with sqlite3.connect(db) as con:
con.execute("CREATE TABLE jobs(id TEXT PRIMARY KEY, done INTEGER)")
con.executemany("INSERT INTO jobs VALUES (?, 0)", [(x,) for x in "abc"])
def query_rows(sql):
with sqlite3.connect(db) as con:
con.row_factory = sqlite3.Row
return [dict(row) for row in con.execute(sql)]
async def dispatch_one(task, *, data, profile, timeout, schema=None):
key = data["id"]
attempts[key] += 1
if key == "c" and not repaired:
return {"status": "failed", "error": "blocked", "data": {}}
if not (key == "b" and attempts[key] == 1):
with sqlite3.connect(db) as con:
con.execute("UPDATE jobs SET done=1 WHERE id=?", (key,))
return {"status": "success", "data": {"id": key}}
async def run(label):
out = await run_playbook_script(
SCRIPT, dispatch_one=dispatch_one, query_rows=query_rows, run_id=label
)
print(label, out["dispatched"], out["result"])
return out
first = await run("first")
assert first["dispatched"] == 5 and first["result"]["remaining"] == 1
repaired = True
second = await run("repaired")
assert second["dispatched"] == 1 and second["result"]["converged"]
third = await run("done")
assert third["dispatched"] == 0 and third["result"]["converged"]
assert attempts == {"a": 1, "b": 2, "c": 3}
asyncio.run(main())
实测结果如下;dispatched 是各次新 Runner 的计数,不是数据库里的累计任务数。
| 运行 | dispatched | rounds | remaining | dead_lettered | 原因 |
|---|---|---|---|---|---|
first | 5 | 2 | 1 | 1 | 第二轮再调度漏写的 b,c 仍失败 |
repaired | 1 | 1 | 0 | 0 | 新 Runner 只处理数据库里剩下的 c |
done | 0 | 0 | 0 | 0 | 已无 pending 行,无须派工 |
这验证了新 Runner 如何从已有业务状态继续处理;没有启动真实 Colony,也没有模拟进程在外部写操作中途崩溃。上游另有 Tracker 为准的测试 和 重新运行的测试。
AgentLoop:可靠性机制放进共同执行原语
Queen 和 Worker 共用循环,因此工具调度、压缩、恢复、预算与等待行为不必在两套执行器里重复实现。AgentLoop 的主文件已经很大,阅读时最好沿具体状态变化进入 internals/,而不是逐行从头读完。
流式输出期间就启动安全工具
Hive 对声明了 concurrency_safe 的工具做提前调度:完整参数的 ToolCallEvent 一到,就先写 partial checkpoint,再创建执行任务,与模型后续输出重叠。没有安全标记的工具等待流式响应完成,再按串行路径执行。后续汇总阶段复用已有任务,避免同一轮重复调用。提前执行,checkpoint 与 task,结果复用和串行执行
这能减少读取型任务等待模型输出结束的时间。不过 concurrency_safe 是工具声明,不能自动证明工具没有副作用。源码中普通并行批次的信号量上限为 10,流式提前启动则有自己的路径,不能概括成“所有工具共享一个全局 10 并发上限”。批次分区
保存执行位置,也保存等待与停止的含义
FileConversationStore 使用消息 part 文件、meta、cursor 和流式 partial 保存会话,写入采用临时文件替换等原子写机制。cursor 还保存 pending_input、user_stopped、tool_calls_used 和最近响应、工具指纹。文件存储,恢复状态
这些字段让恢复能够分清“正在等待回答”“用户已停止”和“可以继续工作”,也防止工具累计预算因恢复而简单归零。它们仍无法与外部服务的写操作组成共同事务:若外部动作成功,进程却在写完成标记前崩溃,后续查询仍可能把该行认作未完成。
Crash recovery does not imply exactly-once external side effects. 对发消息、创建记录等动作,应按业务使用幂等键、外部操作 ID 或再次查询核验;这是应用侧需要补齐的协议。
Compaction 尽量保留重新取回信息的路径
大工具结果可以落盘,模型接收预览与文件路径。microcompact 清理旧结果时,会跳过错误、Skill 内容和没有恢复路径的结果。之后再按预算裁剪并尝试 LLM 摘要。压缩后,Queen 及启用工具分层的 Worker 会重新收到可搜索工具目录,Queen 还会收到 Skill 目录,避免模型记得任务却忘了可用能力。microcompact,压缩流程,工具目录提醒,Skill 目录提醒
这个版本有一处容易读错:模块注释仍提到 emergency deterministic summary,但实际分支已经移除该 fallback。LLM 摘要失败或压缩不足时,代码保留当时会话并告警,交给后续上下文超限路径处理。不能根据注释声称“压缩总能把预算降下来”。实际失败分支
预算还会参考成功 Worker 的实际消耗
ColonyRuntime 除了固定上限,还会从成功、未受预算截断的 Worker 中收集工具调用消耗。样本足够时,结合中位数、较高分位的分散程度和历史最大成功成本,调整 Colony 的预算参考值;差异过大的任务会阻止简单收紧预算。样本筛选与计算
这是把观测反馈到执行控制的一个具体设计。其统计状态在内存中,成功样本依据 Worker 报告;它既不证明结果正确,也不等同于按金额设置的费用上限。
工具与 Skill:既控制权限,也控制模型看到多少
工具管理要区分两个问题:Agent 是否有权调用某工具,以及是否需要把完整参数 schema 一开始就塞进模型上下文。
Hive 用 allowlist 解决前者,用 eager/searchable 分层解决后者。常用工具直接提供 schema;searchable 表示已允许、尚未加载完整 schema 的工具,先以目录形式出现,经 search_tools 提升后再加载完整定义,提升状态还可以持久化恢复。工具分层实现,提升与恢复
内置 terminal、file、chart、memory 等工具走进程内调用。harness_tools.py 用收集器复用工具注册装饰器并生成参数 schema,省去启动内置工具 MCP 子进程的步骤;外部 MCP 仍是单独接入路径。内置工具加载,schema 与调用适配
Skill 负责保存方法。write_skill 写入 Colony 的 Skill 目录后会同步重新加载 catalog,使同一 turn 随后派出的 Worker 能立即发现它。Worker 按任务中的名称引用加载具体正文。写入与同步加载
因此,pilot 的价值是让 Skill 里留下真实遇到的边界,例如“旧产品名对应的帖子也要检查,但普通提及不算发布”,而不只是泛泛要求“仔细搜索”。具体批次如何重试和限速则由 Playbook 承担,操作说明与调度代码各有位置。
“持续改进”具体发生在哪里
当前实现包含三类可观察的经验复用:
- 会话内使用工具错误和可选 Judge 反馈调整下一步。
- Queen 将经验写入有作用域的 Markdown 记忆,并在后续输入时选择相关内容召回。
- 经 pilot 验证的方法写成 Skill,再以 Playbook 重复应用到相似工作单元。
记忆部分的具体入口是 reflection_agent.py、queen_memory_v2.py 和 recall_selector.py。它们处理何时提取、写到什么作用域、如何选择相关记忆,属于可检查的文本记忆机制。反思触发,记忆作用域,召回选择
Judge 也要按条件理解:内置 Queen 设置 skip_judge=True;通用循环可检查输出键,也可在配置成功标准时调用语义评价。不能把 Hive 描述成每一步都有独立模型严格验收。通用 conversation judge 对空响应或调用异常还存在默认接受的分支。Judge pipeline,异常分支
Hive reuses experience through context, memory, Skills and Playbooks; these mechanisms do not train model weights. “改进”在这里主要意味着让已经遇到的问题有更明确的方法和可重复的处理过程。
人工介入与隔离边界
用户交互集中到 Queen。Queen 可以 ask_user 后等待,等待会写入持久状态。当前并行 Worker 没有中途问答或 escalate 通道:遇到阻塞应保存已有结果,再以失败或部分完成的报告结束,由 Queen 决定重新派工或接手。兼容的旧 Worker 路径仍可能提供 escalate,但不能把它套到 run_worker 派出的并行 Worker 上。交互角色判断,等待持久化,并行 Worker 的工具暴露条件
不过,允许调用什么工具与操作系统隔离是两回事。当前 terminal 可以指定任意路径的 cwd 并启动本机进程;Playbook 使用进程内 Python exec,明确保留完整 builtins,信任 Queen 编写的脚本。浏览器 Bridge 也使用用户自己的 Chrome,Worker 的 tab group 用于分组,不能当成账户隔离。terminal 执行,Playbook 信任边界,Browser 架构
因此,用 Hive 管理可信个人自动化和把它开放给不可信租户,需要的外部隔离方案不同。工具白名单、SQL 可写列和模型提示中的约束不能代替容器、独立身份或服务端权限校验。
验证范围与源码阅读路线
本文在固定源码上完成了以下离线验证:
test_toolset_tier_invariants.py、test_worker_tool_tiers.py、test_tracker_tools.py:pytest 共 71 项通过,覆盖工具目录、schema 提升与恢复、允许范围、Tracker 写入和作用域。test_playbook_runner.py:通过runpy执行其中 20 个测试函数,全部通过,覆盖重新查询、重试、限速、失败清单和脚本错误等行为;未经过全套 pytest 的 conftest。- 上文 SQLite 示例:真实 Runner、新建 Runner 后继续、全部完成后零派工,均通过断言。
这些检查没有调用真实模型、浏览器账户或第三方业务 API,不能替代长时运行与业务写入的验收。源码阅读可以按这条顺序进行:
queen_orchestrator.py:看 Queen 的 Context、模型、工具和存储如何组装。queen_lifecycle_tools.py → colony_runtime.py → worker.py:追踪派工、排队、报告和恢复。tracker_tools.py → tracker_db.py:理解业务完成条件落在什么数据上。playbook_tools.py → host/playbook/runner.py:理解确定调度怎样调用不确定执行。agent_loop.py → internals/cursor_persistence.py / compaction.py:检查长期运行的状态边界。tool_tiers.py / tool_skill_reminders.py / reflection_agent.py:理解能力发现与经验复用。
如果要借鉴它的 harness,我会优先考虑三处设计:用结构化数据表示工作进度;先试跑再保存操作方法;把批量收敛、限速与重试留给确定代码。它们能独立用于已有 Agent 产品,不必照搬 Queen 的完整产品形态。
采用完整 Hive 则更适合这样的需求:任务包含许多相似单元、需要持续或反复运行、结果必须能查询,并且有人负责定义和检查完成条件。一次短问答或简单脚本通常不需要这套组织成本。
关联阅读
- Pi Agent 框架架构:包边界、Agent Loop 与接入选择:对照模型接口、循环和完整 Session 的复用边界。
- Mastra 项目原理:Agent Loop、Workflow 与状态恢复:对照显式 Workflow 与 Hive 的运行时派工。
- Rakazo 源码分析:持久 Agent、任务恢复与多端协作:进一步区分保存状态、恢复执行和外部动作。