← 返回博客

并行开发中的阻塞任务分类

编者注(2026 年 9 月更新): 本文描述的是 v0.25 之前的实时管线。Beadbox 已不再运行 WebSocket 服务器,应用完全不开启网络监听端口。变更事件现在通过 sidecar 的 stderr 上的 kkrpc 传递。对用户而言行为没有变化:CLI 写入依然无需刷新就会出现在界面中。改变的只是实现方式。本文中的更新速度数字同样予以撤回:变更多快出现取决于平台,我们不再给出具体数值。 编者注(2026 年 9 月更新): 本文描述的是 v0.25 之前的实时管线。Beadbox 已不再运行 WebSocket 服务器,应用完全不开启网络监听端口。变更事件现在通过 sidecar 的 stderr 上的 kkrpc 传递。对用户而言行为没有变化:CLI 写入依然无需刷新就会出现在界面中。改变的只是实现方式。 现在你可以同时运行 10 个 AI 编程智能体。给每个一个 issue,指向一个共享的 beads 数据库,让它们工作。智能体创建子任务、提交发现的 bug、更新状态、完成后关闭 issue。真正有生产力。

直到某些东西被阻塞。

当人类被阻塞时,会说出来。在 Slack 里说、在站会上提、走到同事桌前。智能体不会做任何这些。智能体遇到无法解决的依赖时,要么默默停滞,要么开始绕路,而绕路往往制造更多问题。三个智能体可能卡在同一个未解决的上游任务上,而你直到四个小时后好奇为什么什么都没交付时才会知道。

这就是智能体开发中的分类问题。不是"如何开更好的站会",而是"如何看到一群不会抱怨的自主工作者中什么卡住了"。这里分享的是我们在构建 Beadbox 过程中学到的经验。Beadbox 是 beads 的实时仪表盘,它让你实时看到智能体在做什么、被什么阻塞、以及什么刚变为可用。

直接跳到你关心的部分:

智能体如何创建阻塞链

智能体产生依赖问题的方式和人类团队不同。理解这些失败模式很重要,因为对应的分类响应也不同。

智能体不会预先建模依赖。 人类架构师会将功能分解为任务并思考顺序。编程智能体接到任务后开始工作,在实现过程中发现需要一些尚不存在的东西。它可能会为那个依赖创建新 issue。可能会试图内联构建并制造混乱。也可能直接停止。除非你在监视 issue 数据库,否则这些结果都不可见。

智能体工作速度快于依赖图更新。 智能体 3 关闭了智能体 7 正在等待的任务,但智能体 7 不知道,因为它在 10 分钟前就检查过阻塞情况了。而此时智能体 7 仍然空闲或在做优先级更低的事。解除阻塞已经发生了,但信息没有传播。

并行分解产生循环依赖。 当多个智能体同时分解工作时,可能创建出单个智能体看不到的循环。智能体 1 创建任务 A 依赖任务 B。智能体 2 创建任务 B 依赖任务 C。智能体 3 创建任务 C 依赖任务 A。每个依赖在局部看都合理。循环只有从上方才能看到。

资源争用是隐形的。 两个智能体都需要修改同一个文件,或都需要 staging 环境,或都需要同一个共享库处于稳定状态。没有依赖被提交,因为两个智能体都不知道对方的存在。它们都变慢了,但都不报告原因。

共同点是:智能体产生阻塞的速度快于它们报告阻塞的速度。supervisor(你)需要自动浮现阻塞的工具,而不是等别人举旗的工具。

自动化依赖检测

解决方案是在创建任务时就产生明确的、可查询的依赖数据,并持续检查。以下是使用 beads 的实际操作。beads 是我们运行智能体集群所使用的 Git 原生 issue 追踪器。

智能体在创建任务时记录依赖:

# 智能体创建任务,发现需要一个不存在的 API
API_TASK=$(bd create \
  --title "Implement /api/v2/orders endpoint" \
  --type task --priority 2 --json | jq -r '.id')

# 智能体创建自己的任务并声明依赖
UI_TASK=$(bd create \
  --title "Build order history page" \
  --type task --priority 2 --json | jq -r '.id')

bd dep add "$UI_TASK" "$API_TASK"

那个 bd dep add 只是一个 CLI 调用。任何 AI 编程智能体(Claude Code、Cursor、Copilot Workspace)都能执行它。不需要 API 客户端库,不需要认证流程。依赖现在是结构化数据,任何其他智能体或脚本都能查询。

循环检测自动运行:

# beads 检查整个依赖图的循环
bd dep cycles

# 存在循环时的输出:
# CYCLE DETECTED: beads-a1b -> beads-c3d -> beads-e5f -> beads-a1b

在 5,000 条依赖的图上,这大约需要 ~70ms。作为 post-commit hook 或 5 分钟 cron 运行。当三个智能体独立创建了一个依赖循环时,你在几分钟内就能发现,而不是几小时后当三个都停滞时才知道。

一条命令浮现所有阻塞任务:

bd blocked --json | jq -r '.[] |
  "BLOCKED: \(.id) \(.title)\n  waiting on: \(.blocked_by | join(", "))\n  assignee: \(.owner // "unassigned")\n"'

输出:

BLOCKED: beads-x7q Build order history page
  waiting on: beads-m2k Implement /api/v2/orders endpoint
  assignee: agent-3

BLOCKED: beads-r4p Deploy staging environment
  waiting on: beads-j9w Fix Docker build, beads-n1c Update TLS certificates
  assignee: agent-7

现在你知道智能体 3 和智能体 7 卡住了,卡在什么上,以及需要做什么来解除阻塞。整个查询在 10K issue 数据库上只需 30ms。

通过分支命名约定检测被阻塞的 PR:

#!/bin/bash
# blocked-prs.sh: find PRs whose dependencies haven't merged

for pr in $(gh pr list --json number,headRefName --jq '.[].headRefName'); do
  ISSUE_ID=$(echo "$pr" | grep -oE 'beads-[a-z0-9]+')
  [ -z "$ISSUE_ID" ] && continue

  BLOCKERS=$(bd show "$ISSUE_ID" --json | jq -r '.blocked_by[]' 2>/dev/null)
  for blocker in $BLOCKERS; do
    BLOCKER_STATUS=$(bd show "$blocker" --json | jq -r '.status')
    if [ "$BLOCKER_STATUS" != "closed" ]; then
      echo "PR ($pr) blocked: $ISSUE_ID waiting on $blocker ($BLOCKER_STATUS)"
    fi
  done
done

二十行 shell。在本地运行,读取本地数据,告诉你智能体的哪些 PR 还不能合并以及原因。

CLI 优先的分类工作流

智能体工作流中的分类不是会议。它是一个循环运行的脚本。supervisor(人类或智能体)查看什么卡住了,对每个项目做出决策。

这是我们实际运行的分类脚本:

#!/bin/bash
# triage.sh: agentic fleet blocker triage

echo "========================================="
echo "TRIAGE REPORT: $(date +%Y-%m-%d %H:%M)"
echo "========================================="

# 1. 什么被阻塞了?
echo -e "\n--- BLOCKED TASKS ---"
bd blocked --json | jq -r '.[] |
  "[\(.priority)] \(.id) \(.title)
    blocked by: \(.blocked_by | join(", "))
    assignee: \(.owner // "unassigned")\n"'

# 2. 有什么可以给智能体认领的?
echo -e "\n--- READY (unblocked, open) ---"
bd ready --json | jq -r '.[] |
  "[\(.priority)] \(.id) \(.title) (\(.owner // "unassigned"))"'

# 3. 哪些智能体变安静了?
echo -e "\n--- STALE IN-PROGRESS (no update in 2h) ---"
CUTOFF=$(date -v-2H +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -d '2 hours ago' --iso-8601=seconds)
bd list --status=in_progress --json | jq -r --arg cutoff "$CUTOFF" '.[] |
  select(.updated_at < $cutoff) |
  "STALE: \(.id) \(.title) (last update: \(.updated_at), assignee: \(.owner // "unknown"))"'

# 4. 依赖健康度
echo -e "\n--- DEPENDENCY CYCLES ---"
bd dep cycles 2>&1 || echo "No cycles detected."

echo -e "\n--- FLEET STATS ---"
bd stats

对人类团队来说,"停滞"意味着 48 小时没有更新。对智能体来说,一个 in-progress 任务 2 小时的沉默就是警示信号。要么智能体卡住了没汇报,要么它崩溃了。无论哪种情况,你都需要去看。

每个阻塞项的决策树:

  1. 其他智能体能解除阻塞吗? 提升阻塞任务的优先级,分配可用智能体。
  2. 依赖是虚假的吗? 智能体有时在规划阶段会声明过于保守的依赖。如果阻塞不是真实的,移除它:bd dep remove beads-x7q beads-m2k(移除 x7q 对 m2k 的依赖,立即解除 x7q 的阻塞)。
  3. 工作可以拆分吗? 让被阻塞的智能体做不需要依赖的部分。为剩余部分创建后续任务。
  4. 是外部阻塞吗? 只有人类能解决的事情(API key、设计决策、权限授予)。标记它,记录预期解决时间,把智能体重新分配到其他就绪工作。

选项 2 在智能体工作中经常发生。它们根据规划时对代码库的理解来建模依赖。一旦实现开始,工作的真实形态往往显示那些依赖中有一半是不必要的。

实时了解智能体工作

每 30 分钟运行一次分类脚本会留下空白。当智能体工作很快时,两次检查之间会发生很多事。问题变成:你能实时看到阻塞的形成吗?

Beadbox 是这样做的:

beads 数据库位于你文件系统的 .beads/ 目录中。智能体运行的每个 bd update、bd create 或 bd close 都会写入该目录。Beadbox 用 fs.watch() 监听它,并在毫秒内通过 WebSocket 推送变化到 UI。

实际效果:智能体 5 在终端运行 bd update beads-x7q --status=closed。Beadbox 面板立即显示该任务为已关闭,任何之前被它阻塞的任务都亮起为新可用。你看到级联效应,不需要运行任何命令。

这很重要,因为智能体工作会产生突发。一个智能体可能在 90 秒内关闭三个任务,每个解除不同下游工作的阻塞。30 秒刷新间隔的轮询式面板会显示令人困惑的中间状态。亚秒级传播让你在全貌发生时就看到它。

如果你不使用 Beadbox,文件系统监听仍然有效:

# Watch the beads database for changes, alert on new blocks
# Note: fswatch fires on every write. In production you'd debounce this
# (e.g., sleep 2 after each trigger) to avoid noise during burst writes.
fswatch -o .beads/ | while read; do
  BLOCKED_COUNT=$(bd blocked --json | jq length)
  if [ "$BLOCKED_COUNT" -gt 0 ]; then
    echo "$(date): $BLOCKED_COUNT tasks currently blocked"
    # Pipe to ntfy, Slack webhook, or any notification system
  fi
done

用 CI 闭环:

# In your CI post-build step: auto-close the issue when the build passes
if [ "$BUILD_STATUS" = "success" ]; then
  ISSUE_ID=$(echo "$BRANCH_NAME" | grep -oE 'beads-[a-z0-9]+')
  if [ -n "$ISSUE_ID" ]; then
    bd update "$ISSUE_ID" --status=closed
    bd comments add "$ISSUE_ID" --author ci \
      "Build passed. Commit: $COMMIT_SHA. Closing automatically."
  fi
fi

当 CI 关闭那个 issue 时,所有被它阻塞的任务都解除阻塞。如果智能体在监听 bd ready 寻找新工作,它会自动认领已解除阻塞的任务。常规解除阻塞不需要人工介入。

这就是追踪状态的工具和传播状态的工具之间的区别。大多数项目管理软件做前者:你更新一张卡片,卡片变色。传播意味着下游效应(解除阻塞依赖者、浮现可用工作、更新进度汇总)自动发生,不需要任何人点击任何东西。

评估智能体工作流的分类工具

如果你在为管理智能体集群挑选工具,需求和人类团队不同。

必须有:智能体可调用的 CLI。 如果你的 issue 追踪器只有 Web UI,智能体用不了。它们需要能运行 shell 命令。bd create、bd update、bd blocked 都是单行命令,任何编程智能体都知道怎么执行。REST API 也行,但需要 auth token、HTTP 客户端和错误处理。Unix 管道更简单。

必须有:可查询的依赖图。 "Blocked"作为状态标签对自动化没用。你需要将"A 依赖 B"作为结构化数据,这样脚本可以遍历图、检测循环、计算什么就绪了。

必须有:亚秒级本地读取。 当智能体查询可用工作时,响应时间很重要。每次查询 2 秒的 API 往返,乘以 10 个智能体每分钟轮询一次,会产生可测量的开销。beads 在 10K issue 数据库上返回 bd ready 结果只需 30ms,因为一切都在本地。

锦上添花:实时变更传播。 如果智能体每小时提交和解决 50 个 issue,你需要看到状态随变化而变,而不是等刷新间隔。

红旗:"AI 驱动的阻塞检测"。 声称通过分析 issue 描述来检测阻塞的工具会产生误报,并遗漏那些从未被写下来的真实阻塞。明确的 bd dep add 声明优于推断。

红旗:需要浏览器才能分类的工具。 通过 Web UI 解除一个任务的阻塞需要 5-15 秒的点击。通过 CLI,bd dep remove 只需 18ms。在 50 个阻塞任务上,那就是 1 分钟对 12 分钟。当你在监督快速移动的智能体时,分类速度就是你的瓶颈。

常见工具如何处理阻塞

能力 Jira Linear GitHub Issues beads + Beadbox
依赖追踪 插件 (Advanced Roadmaps) Relations (部分) Tasklist 引用 一等公民 bd dep add
自动设置阻塞状态 手动 手动 手动 从依赖自动推断
循环检测 无 无 无 内置 (bd dep cycles)
智能体可用的 CLI Jira CLI (第三方) Linear CLI (有限) gh (无依赖功能) 完整 (bd blocked, bd ready)
实时传播 Webhook (服务端) Webhook (服务端) Webhook (服务端) fs.watch (亚秒级,本地)
离线/本地工作 否 否 否 是 (嵌入模式)
智能体可脚本化 API + auth tokens API + auth tokens gh CLI bd CLI (Unix 管道)

Supervisor 循环

这是我们每天管理 10+ AI 智能体在同一项目上的工作流:

  1. 智能体在创建任务时声明依赖。 每个有前置条件的 bd create 立即跟一个 bd dep add。每个任务只是一个额外的 CLI 调用。

  2. Supervisor 智能体每 30 分钟运行 bd blocked。 如果有新的阻塞,它要么自己解决(重新排优先级、重新分配),要么通知人类。

  3. Beadbox 运行在人类的屏幕上。 面板实时显示完整的依赖图,高亮被阻塞的任务。大多数时候,自动化处理常规解除阻塞。当它无法处理时(外部依赖、架构决策、权限授予),人类立即看到问题并介入。

  4. 停滞任务被积极标记。 一个智能体如果 2 小时没有更新其 in-progress 任务,要么卡住了要么崩溃了。supervisor 检查并要么提醒智能体、重新分配工作,或进行调查。

  5. 虚假依赖被持续清理。 智能体在规划阶段过度声明依赖。随着实现揭示工作的真实形态,supervisor(或智能体自己)移除那些被证明不必要的依赖。干净的图才是有用的图。

底层原则:智能体速度快但不具备自我感知。它们不知道其他智能体在做什么,不注意阻塞何时解除,被卡住时也不抱怨。supervisor 的职责是成为连接所有这一切的神经系统。结构化的依赖数据,自动查询并可视化呈现,是让这一切成为可能的关键。


Beadbox 在 beta 期间免费。 它让你实时看到智能体在做什么、什么被阻塞、以及什么刚变为可用。

brew install --cask beadbox/cask/beadbox

如果你已经在使用 beads,Beadbox 直接读取你现有的 .beads/ 目录,无需导入步骤。试试看。