---
title: "像带团队一样管 agent"
description: "「像带团队一样管 agent」：yokoten 使用指南的一章。"
canonical: https://yokoten.app/zh/docs/guide/managing-agents-like-a-team
lang: zh-CN
source: https://github.com/yokoten-labs/yokoten/blob/v0.1.2/docs/i18n/zh-CN/guide.md
ref: "v0.1.2"
---

> For AI agents: the documentation index is at https://yokoten.app/llms.txt. To set up yokoten in a project, follow https://yokoten.app/agents.md.

# 像带团队一样管 agent

这套做法把 AI 当成产线上的员工：每个人分工清楚，权限也有限。

- **主人**（你）决定做什么、什么时候发布。只有你能批准硬线、打发版 tag、改仓库设置。另一个模型的
  区域 lead 可以批准自己区域里的 PR（记录 0026 和 0047）。
- **lead** 是和你对话的那个会话。它做计划、拆任务、做审查，通过 **bot** 开 PR，从不用你的
  账号。
- **worker** 是 lead 为单个任务启动的会话，各自在自己的 worktree 里，带着
  `YOKOTEN_ROLE=worker`。它改代码、交报告，从不推送。如果是修复任务，还会带上
  `YOKOTEN_FIX_TASK=1`，这时守卫和提交检查都不让它改测试文件。
- **bot** 是装在仓库上的 GitHub App，负责开 PR。就算给了它 Workflows 权限，改 CI 的 PR
  也会经 CODEOWNERS 交给你审；发版要在受保护的 `release` 环境里等你批准。规则集要求你审查，
  所以它开的 PR，你不点头就合不进去。

worker 在守卫之下运行命令，所以任务书要照这些规则写：

- 命令以 `cd <worktree> && cmd && cmd` 的形式写。`cd` 要单独成句，用 `&&` 或 `;` 连接：
  不能放在子 shell、管道或后台里。`cd` 之后不要接管道。
- 不要用 `git -C`：先 `cd` 进 worktree。
- 命令前面不要加 `VAR=value`，只有 `TZ`、`CI`、`NODE_ENV`、`NO_COLOR`、`FORCE_COLOR`、
  `DEBUG`、`LANG`、`LC_ALL`，以及 `yokoten.json` 的 `worker.settings` 里列出的名字可以。
- 任务需要别的设置（比如测试的输出文件）时，由 lead 自己做好，把文件交给 worker。

这套方法靠七个技能来做。`dispatch` 给 worker 派活。`inspect` 分三轮审查某一个具体提交。
`rca` 找根因，加检查，再推广到其他项目。`record` 把决定写下来。`release` 定下一个版本号，并准备更新日志。
tag 由主人来打。`merge` 只通过 bot 合并，新提交不是 UTC 就停下。`yokoten` 采用这套做法。
三个 agent 干活：`worker`、`reviewer`（只读）、`verifier`（只跑不修）。

## 谁来审什么

风险越大，越要主人来看。PR 第一行的 tier 说的是：改错了有多难收回（记录 0004）。

| tier | 什么样的改动 | 谁来审 |
|------|--------------|--------|
| C | 日常小改：文档、措辞、样式 | lead 审 |
| B | 普通改动：其他代码，带测试 | 另一家厂商的 agent 审，lead 批准 |
| A | 改错了很难收回：CODEOWNERS 里的文件，或让规则变松的改动 | 主人审；既不是硬线、也没让规则变松的路径，另一个模型的区域 lead 可以批准 |

在 yokoten 自己的仓库里，`.github/CODEOWNERS` 只列出改错了很难收回的文件：核心规则、CODEOWNERS、
会发版或用到密钥的 CI、bot 的令牌、能关掉保护的配置和 `package.json`（记录 0037）。
不是每个文件都列。新项目跑 `init` 时，会按只许 lead 改的路径写好一份。仓库打开「需要 code owner 审查」以后，PR 一改到这些文件，GitHub
就会请主人来审。

`check --pr` 把这两件事接起来。PR 改到 CODEOWNERS 里的文件，第一行却不是 `tier: A`，
检查就不通过。改了哪些路径，决定 tier 最低是多少。作者可以把 tier 往高了写，不能往低了写。

检查读的是目标分支（base）上的 CODEOWNERS，不是 PR 自己改过的那份。这样 PR 没法靠改这个
文件来给自己降级。本地跑、没有 base 的时候，读工作区里的文件，提示里会说读的是哪一份。
没有 CODEOWNERS 文件，就不查。

## 区域 lead 在自己区域里批准

记录 [0026](https://github.com/yokoten-labs/yokoten/blob/v0.1.2/docs/records/0026-the-owner-is-the-board.md) 把非硬线路径的锁从你的 code owner
审查移到 yokoten 的合并检查。区域 lead 用 GitHub 审查来批准。审查正文的最后一行写明 lead、
它的厂商和模型：

```text
Approved by area lead Sonnet (Anthropic, Claude Sonnet 5.5)
```

GitHub 会记下这次审查针对的提交。之后又有新提交，就要重新审查。lead 的模型必须和 `Made with`
行里每个写代码的模型都不一样（记录 [0047](https://github.com/yokoten-labs/yokoten/blob/v0.1.2/docs/records/0047-tier-a-by-another-model.md)）。
Claude Sonnet 可以批准 Claude Opus 写的代码，别家厂商的 lead 也可以。检查比的是名字，不是写法：
`claude-opus-5-5` 和 `Claude Opus 5.5` 算同一个模型。`Made with` 行没写出写代码的人，检查就拒绝。
`.github/CODEOWNERS` 列出的文件，以及放松规则的改动，仍然要你批准。发版的 PR 会改
`package.json`，所以一定要你批准。在差异 tier 检查（#697）完成之前，改动一个已经存在的测试算放松规则；
新增测试不算。

这里不改任何仓库设置。你准备好以后，按这个顺序做。

1. **确认 `.github/CODEOWNERS` 只列硬线。** 这个仓库已经是这样了（记录 0037）：核心规则、
   `.github/CODEOWNERS` 本身、会发版或用到密钥的 CI、bot 的令牌、能关掉保护的配置和
   `package.json`。不是硬线的路径就删掉；删掉的路径就由区域 lead 批准。
2. **保留一个必需批准。** lead 的审查就是 GitHub 审查，所以「Required approvals」保持 1。
   不要让 GitHub Actions 批准 PR。
3. **保留必需的 CI 检查。** 分支规则集仍要要求跑 `yokoten check --pr` 的那个 CI 检查。
   bot 的合并检查是第二道锁：在 `gh pr merge` 之前读 lead 的批准。
4. **硬线安全以后，关掉「Require review from Code Owners」。** 做完第 1 步，这个设置只列
   硬线，开着也只会找你审这些。关掉它，其余路径就交给 bot 的检查。

`yokoten doctor` 现在仍期望 code owner 审查。在它学会新锁之前，这一步会提醒你。合并被拒时，
提示会说明原因和下一步：换一个模型的 lead、请人在 head 上重新审查，或者硬线、放松规则的改动
去找主人。

审的人靠标签找 tier，所以 `check --pr` 也看标签。一个 PR 要有一个类型标签和一个 tier 标签。
类型标签可以直接用标题里的类型（`feat`、`fix`、`docs`、`refactor`、`ci` 等），也可以用常见的叫法：
`fix` 可以叫 `bug`、`hotfix` 或 `incident`，`feat` 可以叫 `feature` 或 `enhancement`，`docs` 可以叫
`documentation`。tier 标签是 `tier A`、`tier B` 或 `tier C`，写成 `tier: B` 也行，而且要跟正文第一行的
`tier:` 一样。少了哪个、哪个不对，各报一条，每条都说怎么改。

服务器怎么设置，一步一步写在 [`docs/server.md`](https://github.com/yokoten-labs/yokoten/blob/v0.1.2/docs/server.md)。

## 在 PR 上会看到的审查机器人

审查机器人会在你开 PR 之后读这个 PR。它把评论发在 PR 页面上。你可能会看到改动的讲解、
风险等级、合并前检查清单，还有修改建议。有的机器人能直接把这些修改推上去。

分两种：

- **商业的。** 机器人是闭源的。公司替你运行，你的代码会发到公司的服务器。很多对公开仓库
  免费，对私有仓库收费。
- **开源的。** 你自己运行，用你自己的模型 API 密钥。模型用的 token 由你向模型厂商付费，
  代码留在你自己的机器上。

价格、许可证和数据的去向会变。本指南不推荐任何一款机器人。打开它之前，先读它自己的页面。
机器人的评论里一般有链接。

yokoten 在 PR 之前工作，在你这台机器上：

- 守卫拦住危险的命令、密钥和个人信息；
- `inspect` skill 在你的 agent 会话里审查改动，不额外花钱；
- 检查和合并规则拦住你没有批准的合并或发版。

审查机器人在 PR 之后工作。两者配合得很好。机器人看到 PR；yokoten 看到写出这个 PR 的会话。

刚开始不需要审查机器人。yokoten 自己的审查就够了。团队想在 PR 页面上多一个读者时，再加
一个机器人。`doctor` 从不叫你装一个：它只说 yokoten 自己的下一步。

yokoten 在 PR 页面上的结果，只来自它自己的检查。它不重复别的机器人的结论，也不再审一遍
代码（#91）。

## 工位

工位就是给一个 AI 做一件任务用的围起来的地方，不管是哪家的 agent，都一样（#637）。
用 issue 编号，或者引号里的一句任务，就能开一个：

```sh
yokoten desk 42
yokoten desk "fix the login bug" --agent codex
```

`yokoten desk` 会建出工位 `../<repo>-desks/<n>/`（它自己的 clone，分支 `desk/<n>`），写一份任务书，
再在围栏里启动 AI。它从不 push；加 `--pr` 才会 push 分支并开 PR。AI 退出后，它在围栏里跑检查和测试，
并给出证据。不带任何参数时，它列出你的工位。

给脚本用的旧写法，可以在你自己建好的围栏 clone 里启动任何命令：

```sh
git clone ../yokoten ../desks/42
yokoten desk claude-code ../desks/42 -- claude -p "$(cat brief.md)"
```

围栏只有一套规矩，每个系统用自己的办法做到：

| 规矩 | 工位里是什么样 |
|---|---|
| 写 | 只能写工位文件夹（包括 `.git`，所以 worker 能提交）和工位给这个 agent 的状态文件夹。`.git/config` 和 `.git/hooks` 只能读 |
| 读 | agent 看得到的其他东西都只能读 |
| 别的项目 | 看不到。你的主目录和工位的上一级文件夹都藏起来了。Linux 上 `/run` 也藏起来：那里的 socket 能通到外面，在 WSL2 里还有一个能直接启动 Windows 程序。Node、agent 程序和 yokoten 会重新放进来，只能读 |
| 你自己的 agent 状态 | 看不到。你的 `~/.claude`、`~/.codex`、`~/.grok` 和 Muse 的文件夹都藏起来：设置、钩子、记忆、MCP 配置、历史记录、会话，一样都不带进来 |
| 子进程 | 能跑，管道也能用，所以测试可以启动程序 |
| 网络 | 命令自己上不了网。外面的代理只放行 agent 的 API 主机。工位里的任何命令也能用这个代理，所以命令也连得到这些主机 |
| 环境变量 | `YOKOTEN_ROLE=worker`、`TZ=UTC`。你 shell 里的 token 和密钥都不带进来（见 [agent 拿到的环境变量](https://yokoten.app/zh/docs/guide/privacy.md#agent-拿到的环境变量)） |
| 团队 | 团队文件夹只能读，只有这个 agent 自己的发件箱能写（见 [agent 之间发消息](https://yokoten.app/zh/docs/guide/managing-agents-like-a-team.md#agent-之间发消息)） |

工位必须是单独 `git clone` 出来的。worktree 的 git 文件夹在另一个检出里，工位会把它藏起来，
所以 worktree 不行，命令会拒绝。

**工位自己的状态文件夹。** 每个工位在旁边有一个给 agent 用的状态文件夹：`<工位>.state/<agent>`。
工位用 agent 自己认的变量把它指到这里。从你那里只复制登录文件。你的登录文件更新了，下次启动时
会再复制一次。删工位的时候，把这个文件夹一起删掉。

| agent | `<agent>` | 用哪个变量指到状态文件夹 | 复制的登录文件 | API 主机 | 在工位里跑的命令 |
|---|---|---|---|---|---|
| Claude Code | `claude-code` | `CLAUDE_CONFIG_DIR` | `.credentials.json` | `api.anthropic.com`，还有续登录用的 `platform.claude.com` | `claude -p "<任务说明>"` |
| Codex，以及通过 Codex 用 DeepSeek | `codex` | `CODEX_HOME` | `auth.json` | `api.openai.com`、`api.deepseek.com` | `codex exec --sandbox danger-full-access "<任务说明>"` |
| Grok | `grok` | `GROK_HOME` | `auth.json` | `api.x.ai` | `grok -p "<任务说明>"` |
| Muse Code | `muse` | `XDG_CONFIG_HOME`、`XDG_DATA_HOME` | `muse/auth.json` | `api.meta.ai` | `muse exec "<任务说明>"` |

- **Claude Code** 到 `platform.claude.com` 续登录。用 `claude setup-token` 拿的 token 放在
  `CLAUDE_CODE_OAUTH_TOKEN` 里，或者用 API key，就不用续，它们会作为登录变量传进去。
- **Codex** 在工位里要用 `--sandbox danger-full-access`。它自己的 `workspace-write` 沙箱会把
  `.git` 设成只读，还会挡住测试要用的管道。工位本身就是围栏，用不着它那一层。你的 `config.toml`
  不带进来：模型提供方用命令里的 `-c` 给。用 ChatGPT 登录的话，还要加 `--host chatgpt.com,auth.openai.com`。
- **Grok**：如果 `--sandbox` 挡住了提交或测试，在工位里把它关掉。
- **加主机**：`--host <主机>` 让 agent 多连一个 API 主机（443 端口）。几个主机用逗号隔开。
- **加文件**：`--read <路径>` 多放一个路径进来，只能读。会露出主目录、藏起来的文件夹或你自己 agent
  状态的路径，命令会拒绝。

**代理。** 四个 agent 的程序里都认 `HTTPS_PROXY`。工位会把 `HTTPS_PROXY`、`HTTP_PROXY`、
`ALL_PROXY` 的大写和小写都设好，还设 `NODE_USE_ENV_PROXY=1`，这样 Node 自带的 `fetch` 也走代理。
代理只接受 `CONNECT`，只放行名单上的主机，从不看传的内容。它同时最多开 64 条通道，一条通道 10 分钟
没动静就关掉。它拒绝了哪个主机，工位结束时会说出来。

**在外面审。** 工位里的东西是 worker 写的，要当成不可信的。检查之前，别在工位里跑 git 或别的命令。
先把分支取到你自己的检出里，再在那里审：

```sh
git fetch ../desks/42 HEAD:refs/desks/42
git diff main refs/desks/42
```

工位里的 `.git/config` 和 `.git/hooks` 只能读，worker 没法在那里埋钩子或别名。upstream 之类的
设置，在启动工位之前从外面设好。

**提交检查和你的私有词表。** 提交检查要读你的私有词表。agent 能读这个文件，就能把它打印出来。
所以工位默认把它藏起来，工位里的检查看不到你的词。在词表改成存哈希之前（#665），请你自己在工位
外面检查：在你自己的检出里切到取回来的分支，跑 `yokoten check`，它会查整个目录树。
`--marks` 会把这个文件放进工位，只能读。只有你放心让它看到这些词的 agent，才这样用。

**系统。**

围栏用 Anthropic 的 [sandbox-runtime](https://github.com/anthropics/sandbox-runtime)（`srt`），
就是 Claude Code 自己用的沙箱。PATH 上的 `srt` 是钉死的 0.0.81 版时直接用它；不是的话，`npx`
第一次把这个版本下载到 npm 的缓存里。它不是 yokoten 的依赖。沙箱起不来时，工位用一句话说明原因，
什么都不启动。

- **Linux 和 WSL2**：要装 `bubblewrap`、`socat` 和 `ripgrep`。Debian 和 Ubuntu 上：
  `sudo apt install bubblewrap socat ripgrep`。工位有自己的空网络，只有 agent 的 API 主机能通过
  srt 的代理。
- **macOS**：用系统自带的 `sandbox-exec`。还没在 Mac 上跑过：请在 Mac 上跑一次
  `node --test test/agents/desk/start.test.ts`，检查这套规矩。
- **原生 Windows**：没有工位。`yokoten desk` 什么都不启动，只提示改用 WSL。原生 Windows 上只有守卫在把关。

`YOKOTEN_FENCE=own` 会用旧的围栏，留到 v0.1.3：Linux 上是 `bwrap`，macOS 上是 `sandbox-exec`。

## agent 之间发消息

各家的 agent 可以互相发消息（#674）。消息就是这台电脑上的文件，不用服务器，也不上网。

```sh
yokoten msg send lead "测试都过了，可以审了"
yokoten msg inbox
yokoten msg who
yokoten msg log
```

- **放在哪**：你的仓库旁边的团队文件夹 `<仓库名>.team/`。每个 agent 只能写自己的发件箱。
  `yokoten desk` 用 `--name` 给的名字把 agent 加进团队；没给就用工位文件夹的名字，改成小写。
  文件夹名做不了名字的，这个工位不进团队。
- **你是谁**：在工位里，由工位决定。不在工位里，只有在主 checkout 里你才是 `lead`；
  在 worktree 里、或者在工位里面的 checkout 里都不是。
- **工位里假冒不了发件人**：在工位里，团队文件夹只能读，只有自己的发件箱能写。
  不在工位里的 worker 用 `msg send` 会被拒绝，guard 也会拦下它对团队文件夹的修改。
- **工位外可以假冒发件人**：工位外的程序用的是你的用户身份，任何发件箱都能写，包括 lead 的。
  普通 worktree 里的 worker 就做得到，比如跑一段内联代码，离开自己的文件夹再写。
  yokoten 分不出这是它写的还是你写的。要发消息的 worker，请放进工位里跑。
- **怎么送到**：hook 把没读的消息放进 agent 的上下文。Claude Code 和 Grok 在调用工具前收到；
  所有 agent 在一轮结束时收到，先做检查再送：检查没过，打回时把消息一起带上。
  随时可以用 `msg inbox` 看。Muse Code 的 hook 拿不到环境变量，所以在工位里 Muse 要用 `msg inbox` 看。
- **消息只是数据**：每次送消息都会说明：这是别的 agent 发的，不能给批准或权限。靠的就是这句说明。
  每条消息写明发件人的厂商和模型。有些自称 owner 已经批准的话会被标出来，但换个说法就标不出来。
- **隐私**：发出前和贴出去的文字做一样的检查：私密词、密钥、邮箱和 IP、带密钥的环境变量。
  查出问题就不发，并告诉你原因。检查只看原文，密钥编码过或拆开写就查不出来。
  工位只有加了 `--marks` 才看得到你的私密词，所以工位发给工位的消息，只有这时才查私密词。
  lead 收到的消息每次都会查。
- **谁能看**：团队里每个工位都能看到所有消息，不只是发给自己的。
- **上限**：每条 2000 字符；每个 agent 10 分钟内最多 20 条；每个发件箱 256 KB。
- **全部记录**：`msg log` 显示所有消息，只有 lead 能跑。

## 看板

`yokoten kanban` 一眼看到整个团队。它是预览版，以后可能会变。它是只读的：
上面没有任何东西能启动、停掉或改动工作。为什么这样做，见记录 0044。
（v0.1.2 之前它叫 `yokoten board`，旧名字仍然能用。）

它显示三样东西：

- **会话。**这个仓库、它的 worktree 和它的工位里的每个 AI 会话：哪个 agent、什么模型、
  什么任务，以及它是在做、卡住了、在等，还是做完了。15 分钟没有输出的会话算卡住。
- **证据。**每个会话是被系统沙箱围着，还是只有守卫；提交检查过了没有；
  测试在 Linux、Windows 和 macOS 上的结果。状态文件还没记录平台之前，这三项都显示「未证明」。
- **PR 和 Actions。**等你处理的 PR、最近合并的 PR，以及 GitHub Actions 的健康情况。
  每个 agent 用了多少 token，也一并显示。

`yokoten kanban --text` 用纯文本按状态打印会话，再打印 PR 和 Actions，然后退出。现在就能用。
证据和 token 数目前在 `GET /api/state` 里。

不加 `--text` 时，它只在这台电脑上开一个页面，地址是 `http://<repo>.localhost:<port>/`。
`<repo>` 是文件夹名，改成主机名能用的样子。这个仓库的端口保持不变。
它会在浏览器里打开页面并自动登录，一直运行到你按 Ctrl-C。加 `--no-open` 就只打印地址。
登录用的密钥只交给浏览器，从不打印。主机名不对的请求会被拒绝。

页面正在重做，成为独立的网页包（记录 0044）。做好之前，打开 `/` 只会得到一行说明，
`GET /api/state` 照常可用。它以 JSON 返回会话、PR 和 Actions 的一份快照。
`GET /api/sessions/<id>/stream` 用服务器推送发出一个会话的实时输出。
id 是会话所在文件夹的哈希，不是路径。GET 和 HEAD 以外的请求一律回 405。

看板通过 `gh` 在后台读 GitHub，同一时间只读一次。哪次没读到，就留着上一次的列表。
每个 agent 都过 guard，push 和合并只走 bot。

agent 会自己出现在看板上。在这个仓库、它的某个 worktree，或者一个工位（`yokoten desk`）里
打开 agent，yokoten 的钩子就会把它的状态写进那个目录根部的 `.yokoten-agent.json`。
每家 vendor 都一样：

- 会话一开始，写上 `vendor`、`model`、`role`（主目录里是 `lead`，worker 是 `engineer`，
  和 guard 用同一条规则判断）、`worktree`、`state: working`，还有 `started`。
  `started` 只写一次。Grok 没有会话开始的钩子，由它的第一次调用来写。
- 每次放行的工具调用会更新 `doing` 和 `updated`，最多每 5 秒写一次。
- 被拦下的调用会写上 `blocked`。下一次放行的调用会清掉它。
- 结束时写上 `state`：worker 是 `done`，并带上 `checks`；负责人是 `waiting`。

钩子只在有 `yokoten.json` 的仓库里写这个文件。worker 从不写主目录里的那份：那份是负责人的。
第一次写的时候，会把这个文件加进仓库自己的 `.git/info/exclude`，所以 `git status` 里永远看不到它。
工位是普通的 clone，`git worktree list` 列不出来，所以 `yokoten desk` 会把它记在仓库的 git 目录里，
看板也就能显示它。

agent 也可以自己写这个文件，比如 `task`；钩子不碰它没设的字段。
删掉这个文件就离开。格式是公开的，别的工具也可以读。一个 worktree 一个文件。

| 字段 | 意思 |
|------|------|
| `name` | agent 的名字。没写就用 worktree 的文件夹名 |
| `vendor` | 跑的是哪家的 agent，比如 `claude-code`、`codex` 或 `grok` |
| `model`、`effort` | 模型和力度，比如 `opus` 和 `medium` |
| `role` | `lead`、`engineer`、`reviewer`、`verifier` 或 `helper` |
| `task` | 在做什么任务，比如 `PR #615 Run rules locally` |
| `worktree` | 在哪个 worktree 干活 |
| `doing` | 现在在干什么，一句话 |
| `state` | `working`、`waiting`（需要你）或 `done` |
| `checks` | `passing` 或 `failing`，来自它上一次跑测试。它在 `/api/state` 里显示成提交检查 |
| `updated` | 上次写这个文件的时间，比如 `2026-10-22T10:00:00Z`。安静 15 分钟就显示为卡住 |
| `started` | 这件事什么时候开始的，比如 `2026-10-22T08:00:00Z`。没写就用文件的创建时间，再没有就用 `updated` |
| `blocked` | 需要决定的 guard 拦截，一句话 |

每个字段都是文本，每个字段都可以不写。不是 JSON 对象的文件会被跳过。`state` 或 `checks` 写了上面以外的词，会被忽略。

agent 写的内容只当文本显示，从不当页面代码，并且像会话一样先遮住敏感内容。
看板不从互联网加载任何东西。它的接口是 `/api/state`，页面只是其中一种展示。

---

上一章: [起名字](https://yokoten.app/zh/docs/guide/naming.md)
下一章: [隐私](https://yokoten.app/zh/docs/guide/privacy.md)
