Files
dsh-superagent/docs/006-部署.md
T

275 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 006 部署
用这份指南安装、配置并运行 dsh-superagent。它覆盖账号、启动检查、升级与排障,建立在 [001 领域模型](001-领域模型.md) 至 [005 面板](005-面板.md) 之上。
## 运行的是什么
- **一个专用的 DeepSeek Harness profile**,本指南称 `superagent`。从 Web profile 模板创建。它加载 bundle,并在 `127.0.0.1` 上提供它的面板。把它与你日常干活的 profile 分开:插件会改动进程级的 `PATH` 与 git 设置,它的进程也需要独立重启与升级。
- **一个 agent GitHub 账号。** agent 以这个账号发评论、推分支、开 pull request,通过一个只登录了该账号的 GitHub CLI。
- **可选:** 一个用于聊天请求的 Feishu bot。
插件配置持有状态目录与机器限额。其余一切用面板:Sources 管仓库与账号,Policy 管允许名单,Execution 管 worker 目录与命令。
### 权限与隔离
bundle 为 Executors 定义了 `dsh-superagent-worker`,为 main Session 定义了 `dsh-superagent-router`。它们会出现在 DeepSeek Harness 的预设列表里,但只应给 dsh-superagent 用。不要给个人 Sessions 选它们,也不要禁用它们的配置行。应用需要它们来创建和恢复自己的 Sessions。
在 Web UI 里打开一个空白的受管 Session 时,可能选中 profile 的默认预设。dsh-superagent 会在第一个回合之前恢复它的角色专属预设。
两个预设都以操作者的 OS 用户身份、用 `danger-full-access` 运行,并从被订阅的 Threads 读取消息。账号包装器默认挑 agent 的凭据;它们不隔离进程。worker 可以按路径执行程序、读取操作者的 home 目录。要隔离,就用一个只装 agent 凭据的专用 OS 账号。
## 前提
- Node.js 22.19+ 或 24+、pnpm、git 2.32+、Python 3.8+ 与 tmux
- GitHub CLI(`gh`)2.48 或更新。Source 的 CLI 字段必须指向 `gh` 或 `lark-cli` 包装器;host 依赖它们的子命令与输出格式。
- github.com 上的一个仓库;不支持 GitHub Enterprise Server。要为每个 Ticket 建一个 checkout,保留一份本地 clone,让 prepare command 能在其中添加 worktrees。那个仓库若用 Git LFS,安装 `git-lfs`。
- 为 DeepSeek Harness 配置好的 model provider。dsh-superagent 使用 profile 的默认模型,除非 Eval 或 Execution 设置点名了另一个(可以带 reasoning effort);被复用的 Session 会在下一次派发时被重新指到其角色配置的模型。
- 用 Feishu 则需要:`lark-cli`(测试于 1.0.90),以及一个带 bot 的 Feishu 或 Lark 应用
## 账号
### agent GitHub 账号
1. 为 agent 建一个 GitHub 用户账号,授予它对仓库的写权限。GitHub App 不能当 agent,因为 dsh-superagent 读取该账号的 notifications。
2. 给它一个只用自己凭据的 GitHub CLI:
```sh
mkdir -p ~/.local/bin
cat > ~/.local/bin/gh-agent <<'EOF'
#!/bin/sh
GH_CONFIG_DIR="$HOME/.config/gh-agent" exec gh "$@"
EOF
chmod +x ~/.local/bin/gh-agent
gh-agent auth login
gh-agent api user --jq .login
```
以 agent 账号登录;最后一条命令会打印它的 login。dsh-superagent 用同样的方式读 login。
### Feishu bot(可选)
1. 创建一个带 bot 能力的自建应用。授予它接收消息事件、读取与发送 chat 消息、读取 chat 成员的权限,然后发布应用版本。
2. 给 bot 一份它自己的 `lark-cli` 配置,限定 bot 身份,外加一个包装器:
```sh
LARKSUITE_CLI_CONFIG_DIR="$HOME/.lark-cli-dsh-superagent" lark-cli config init --app-id <cli_…> --app-secret-stdin
LARKSUITE_CLI_CONFIG_DIR="$HOME/.lark-cli-dsh-superagent" lark-cli config strict-mode bot --global
cat > ~/.local/bin/lark-bot <<'EOF'
#!/bin/sh
export LARKSUITE_CLI_CONFIG_DIR="$HOME/.lark-cli-dsh-superagent"
exec lark-cli "$@"
EOF
chmod +x ~/.local/bin/lark-bot
```
第一条命令从标准输入读 app secret。用一个独立的配置目录:`lark-cli` 每个 app id 只允许一个 profile,而你的个人配置里可能已经有该应用的用户登录。
host 会拒绝一个能扮演用户的 CLI。它从 bot 配置读取 app id、Feishu/Lark 品牌与 bot 的 `open_id`。
两份配置共享 keychain 里 app secret 的那个条目。要只移除 bot 配置,删它的目录即可;不要运行 `lark-cli config remove`,那会影响共享的 secret。
## 安装
在还没有 `superagent` profile 的机器上,把这个块跑一遍:
```sh
npm install -g @deepseek-ai/dsh@0.2.1-rc.3
git clone <this repository> dsh-superagent && cd dsh-superagent
pnpm install && pnpm build
dsh superagent --from-default-profile web --dump-config > /dev/null
dsh plugin --profile superagent add "link:$PWD"
```
上面钉住的是公开版本;其他版本必须满足 `package.json` 里的 `engines.dsh`。
第四条命令从 DeepSeek Harness Web 模板创建 `~/.dsh/profiles/superagent`。第五条安装本 bundle。用 `DSH_HOME` 可以改变默认的 `~/.dsh` 目录位置。
profile 创建命令只在新安装时运行。若该 profile 已存在,那条命令会替换它并删掉它的 `cordis.patch.yml`,机器配置也一并没了。不要拿它做升级。
## 机器配置
把 [examples/profile.cordis.patch.yml](../examples/profile.cordis.patch.yml) 复制为 `~/.dsh/profiles/superagent/cordis.patch.yml`(替换那个文件),并替换每一个 `<placeholder>`。要用 Feishu 或本 fork 的 Gitea Source,就同时启用那里的 `dsh-superagent-feishu` / `dsh-superagent-gitea` 条目。`webserver` 条目设定面板的端口。
| 键 | 必填 | 含义 |
| --- | --- | --- |
| `stateDir` | 是 | 绝对路径:数据库、锁、`worker-bin` 里的命令垫片、agent 的 git 配置,默认还包括 `work` 下的 Ticket 目录。升级前先备份 |
| `promptMaxChars`、`admissionContextMaxPages`、`admissionContextTimeoutMs` | 否 | 可保存的提示词配置的最大体积;一次研判在多长的时间窗内读多少个 Thread 分页 |
首次启动之后,机器限额到 **Settings → Deployment** 或 DeepSeek Harness Plugin Manager 里改。改动无需重启即生效。`stateDir` 不能在那里改。非法值或未知配置键会阻止启动;日志会指出是哪个键。
## 启动与停止
```sh
scripts/dsh-superagent start
scripts/dsh-superagent status
```
`start` 在 tmux 会话 `dsh-superagent` 里运行 `dsh --profile superagent`,其他 profile 则是 `dsh-<profile>`。它至多等一分钟,等 runtime 就绪或报告一个设置需求。首次运行会打印缺少的设置信息。它拒绝重复实例,包括在本脚本之外启动的实例。
`stop` 与 `restart` 请求优雅关闭,并至多给 Executors 五分钟。它们从不强杀进程,也不碰其他 tmux 会话。
`status` 打印进程号、健康状况与设置问题。要换 profile 或端口,用 `--profile` 与 `--url`,或设置 `DSH_SUPERAGENT_PROFILE` 与 `DSH_SUPERAGENT_URL`。
`status`、`start` 与 `restart` 以四种退出码之一结束:
| 退出码 | 含义 |
| --- | --- |
| 0 | 服务正常运行 |
| 1 | 它没在跑、跑了双份、不回应、或其 runtime 报错;重启可能修好 |
| 2 | 它在跑,但 runtime 之外的某个部件——如某个 Source 或某次 Policy 同步(`sync:<source id>`)——在报错;重启修不好 |
| 3 | 它在跑,但在等待设置;重启修不好 |
还在启动中的 Source 算正常运行。设置检查没能检查到的 Feishu bot 也算。
`status` 在 `errors` 下返回失败的部件及其细节,在 `setup` 下返回设置需求。`start` 与 `restart` 把这些问题逐行打印;服务始终起不来时它们以 1 退出。只有刻意的 `stop` 会暂停自动恢复。
每个 profile 把日志与控制文件存放在 `.artifacts/` 下:
- 启动日志:`dsh-superagent-<profile>-<date>-<time>-<pid>.log`,其中 pid 属于脚本。保留最新 20 个。
- 控制文件:`control/profiles/<profile>/`,内含停止标记、锁与进程号。
打开日志里打印的 URL,然后在侧边栏选 **Superagent**。
### 每小时检查
每小时检查建在你平时使用的 DeepSeek Harness profile 里,不要建在 `superagent` 里。agent 服务宕机时它必须还能运行。在那个 profile 里让一个 agent 用下面这段提示词创建一个每小时计划,并把 checkout 路径填进去:
```text
Check dsh-superagent: run `scripts/dsh-superagent --profile superagent status` in <checkout>. Exit 0: do nothing and say nothing. Exit 2: do not restart; tell me each part under `errors` with its detail, unless you already told me about the same ones. Exit 3: do not restart; tell me what `setup` says setup needs, unless you already told me. If it reports manually_stopped: true, leave it stopped. Otherwise (exit 1) run `scripts/dsh-superagent --profile superagent --respect-stop restart`, which exits the same way: 0 means it recovered, so say nothing; 2 and 3 are reported as above; if it exits 1 or fails otherwise, read the newest .artifacts/dsh-superagent-superagent-*.log and the Troubleshooting section of docs/006-部署.md, fix what you can, and tell me what was wrong and what you did.
```
`--respect-stop` 保留一次刻意的停止。只有 `stop` 写那个标记;`start` 清除它。`restart` 不写标记,所以这个检查能恢复一次因关闭超时而中断的重启。
## 设置
设置必须成功,轮询与工作才会开始。Overview 列出缺失的需求并链接到对应设置。一次设置改动会重新运行设置检查。若你在应用之外修好了什么——例如 CLI 认证——点 **Check again**。
1. **Sources。** 设置 GitHub 仓库(`owner/name`)与 agent CLI,例如 `/home/me/.local/bin/gh-agent`。请求通常经 agent 账号的 notifications 到达。新 Issue 或提到该账号的评论会在下一次轮询到达;靠编辑已有 Issue 正文补一个 @ 是不会到的——请改发一条新评论。用 Feishu 时,设置 bot CLI、chats、每个 chat 的全量接收或仅 @ 模式、以及是否接收私聊。
2. **Policy。** 两条示例规则可以编辑或删除。在 **On each Source** 下,添加可以请求工作的 GitHub 账号与 Feishu 用户;在名单里有了人之前,任何请求都不可能成为 Ticket。用 Feishu 时,可以选一个 chat 作为名单同步的来源,在启动时与点 **Sync now** 时同步。
3. **Execution。** 目录根与一条 prepare command。要让每个 Ticket 得到一份仓库的 checkout:
```sh
git -C /home/me/src/widgets fetch --no-tags "https://github.com/$DSH_SUPERAGENT_GITHUB_REPOSITORY.git" "${DSH_SUPERAGENT_GITHUB_PR_HEAD:-HEAD}:refs/dsh-superagent/$DSH_SUPERAGENT_TICKET" &&
git -C /home/me/src/widgets worktree add -b "agent/$DSH_SUPERAGENT_NAME" "$DSH_SUPERAGENT_DIR" "refs/dsh-superagent/$DSH_SUPERAGENT_TICKET"
```
把个人 CLI——例如登录了你自己账号的那些——列为被屏蔽的命令。
4. **Prompts。** 设置团队分节:GitHub 披露句、review bots、commit 作者与 auto-merge 条件。
### Management API
面板使用一个本地 HTTP API,agent 也可以调用它。`设置.md` 用它保存设置,服务脚本读取 `GET state`。
向 `http://127.0.0.1:<port>/orchestrator/api/` 发送请求,带上 `x-orchestrator-request: 1`。host 必须是 `127.0.0.1:<port>` 或 `localhost:<port>`。缺少请求头、或 `Origin` 来自别的站点,都会得到 403。
API 保存拥有面板的全部权限。与通过 main Session 工具做的保存不同,它可以修改执行环境与设置里的账号字段。
agent 可以依赖这些路由与字段。`src/app/api.ts` 里的其他路由服务于面板,并随面板一起变化。
| 请求 | 应答 |
| --- | --- |
| `GET state` | 每个页面都会读的内容:`setup`,其中 `problem` 与 `feishuProblem` 各为 `null`、或一个带 `place`、`code`、`values` 与 `detail` 的设置问题,设置解析出账号后还有 `accounts`;`health`,每个部件一个条目(见下),带 `state`(`starting`、`ready`、`paused` 或 `error`)、`detail` 与 `at`;`load`,即 Executor Sessions 的 `running`、Tickets 的 `queued` 与 `maxWorkers`;`paused`;以及 `masterId` |
| `GET settings` | `blocks`:`policy`、`eval` 与 `executor`,各含其 `value` 与其 `revision`;`sources`:各 Source 实例,各含 `id`、`kind`、`installed`、其 block 的 `value`(`{"enabled": …, "config": {…}}`)与 `revision`、其 kind 的表单,以及有设置问题时的 `problem`;`policyVersion`;以及 `environment`,即执行环境的 `root` 与 `variables` |
| `GET sources` | 每个 Source 的状态:`state`(`missing`、`off`、`error`、`setup`、`starting`、`paused` 或 `ready`)、其各部件的 `health`、其设置 `problem`、最近一天的 `stats`、当天它 `filtered` 的 Triggers、当天它 `dropped` 的消息(最新丢弃在前,每条含其 `channel`、`preview`、属于某 Ticket 时的 `ticket`(`id` 与 `title`)、存储里有它时的 `thread`、以及 `dropped.at` 与 `dropped.reason`——`channelGone` 或 `channelIgnored`) |
| `PUT settings/<block>`,体为 `{"value": …, "revision": n}` | 保存一个 block:`saved`、该 block 的新 `revision`、`policy_changed` 与 `restarting`。`value` 是完整的 block,如同 `GET settings` 返回的那样;缺失的键取默认值 |
| `POST executor/pause`,体为 `{"paused": true}` 或 `false` | 暂停或恢复处理:`paused` |
| `POST setup/check` | 通过重启 dsh-superagent 重新运行设置:`restarting: true`。设置无可修复时会被拒绝 |
| `POST policy-sync/<source id>` | 立刻同步该 Source 的允许名单:`policy`,即已保存的该 Source 的部分 |
| `GET prompts`、`PUT prompts`,体为 `{"config", "baseRevision"}` | 读取提示词配置:`current`(含其 `revision` 与 `config`)、`defaults`、`sections`(团队分节按 Sessions 读取的顺序)、`skillName`(团队技能名的模式)、`builtin`(团队技能不能占用的名字),以及 `skillPrefix`(Sessions 在该前缀下加载技能名)。或保存它:返回新 `revision` |
| `POST tickets/<id>/retry`、`POST tickets/<id>/stop` | 重试或停止一个 Ticket,与面板一致 |
健康条目按职责分组:
| 条目 | 报告 |
| --- | --- |
| `runtime` | 应用自身 |
| `models` | 正在等模型预算重试的 Sessions |
| `evals` | 最近一小时内、没有更晚判断也没有被 dismiss 的 unavailable Evals |
| `assessment`、`launch`、`delivery`、`relay`、`notice`、`reply` | [003 执行](003-执行.md) 所述的 stored work;delivery 统计排队中的工作,reply 覆盖 Channel 消息 |
| `worker` | Executor Session 的事件处理;在其首次失败之后出现 |
| `github`、`feishu`、`history` | Source 轮询、事件与历史恢复 |
| `sync:<source id>` | 允许名单同步 |
一次拒绝有一个状态码与 `error`,即它的英文文本:
- 400:非法值(`issues`,每条含其 `path`)、失败的账号检查(`setup`)、固定的拒绝理由(`code` 与 `values`)、或不是 JSON / 过大的请求体。
- 404:不存在的东西。
- 409:过期的 revision。
- 500:其他一切。
各 block:
- `source:github`、`source:feishu`:`{"enabled": true, "config": {…}}`。GitHub 的 config 有 `repository` 与 `cli`,以及默认关闭的开关 `observeIssues`、`observePulls`、`observeComments` 与 `observeCi`——它们分别接收仓库中每个新 Issue、新 pull request、评论与默认分支的 CI;Feishu 的有 `cli`、`chats`(形如 `[{"chatId": "oc_…", "name": "…", "listen": "mentions"}]`,或 `"all"`)、`directMessages`,以及取 `"none"` 或 `"mentions"` 的 `otherChats`。
- `policy`:`{"rules": […], "sources": {"github": {"allowed": [{"id": "<login>"}], "bots": [{"id": "dependabot[bot]"}]}, "feishu": {"allowed": [{"id": "ou_…", "name": "…"}], "syncChat": "oc_…", "admission": {"oc_…": {"unlistedAddressed": "explicit"}}}}}`。`bots` 列出消息可以成为 inferred 候选的 bots。`admission` 的行以 chat id 为键——Feishu 还有 `other` 或 `direct`——GitHub 按 `issues`、`pulls`、`comments` 或 `ci`;未设置的单元格取其默认(见 [002 授权](002-授权.md))。
- `eval`:`mode`、`timeoutSeconds` 与 `model`。
- `executor`:`model`、`maxWorkers`、`directory`、`prepare` 与 `blockedCommands`。处理暂停是它自己的一项设置 `paused`,由 `POST executor/pause` 设定。
修改仓库或 CLI 会先运行对应的设置检查。检查失败返回 400,细节在 `setup` 里。一次成功的账号变更、或设置未就绪期间的任何保存,返回 `restarting: true`。应用重启期间 API 暂时不可用。
```sh
api=http://127.0.0.1:3082/orchestrator/api
rev=$(curl -s -H 'x-orchestrator-request: 1' $api/settings | jq '.sources[] | select(.id == "github") | .revision')
curl -s -X PUT -H 'x-orchestrator-request: 1' -H 'content-type: application/json' $api/settings/source:github \
-d "{\"value\": {\"enabled\": true, \"config\": {\"repository\": \"acme/widgets\", \"cli\": \"$HOME/.local/bin/gh-agent\"}}, \"revision\": $rev}"
```
## 设置检查
设置检查使用 DeepSeek Harness 的 subprocess service,与 worker shell 相同。它会移除名字里含 KEY、PASSWORD、SECRET 或 TOKEN 的环境变量。
设置检查会验证 GitHub 仓库与 agent 的推送权限、写入 worker 环境,并确认 git 对 github.com 只使用 agent 的凭据。Feishu 的设置检查失败只禁用 Feishu Source。
设置检查写出的进程环境:
- **命令垫片**在 `<stateDir>/worker-bin`,位于 `PATH` 最前:`gh` 运行 agent 的 CLI;`lark-cli` 运行 bot 的 CLI;被屏蔽的命令失败。一个垫片在运行它的目标之前,先从 `PATH` 里移除自己所在的目录。
- **git 配置**在 `<stateDir>/worker-gitconfig`:替换操作者的全局与系统 git 配置。它选择 agent CLI 作为 github.com 的凭据 helper,把 GitHub 的 SSH 地址重写为 HTTPS,并定义 Git LFS 过滤器。个人 git 设置——例如 HTTP 代理——不生效。这里不设提交身份,git 从不弹凭据提示,`GH_TOKEN` 与 `GITHUB_TOKEN` 会被移除。
`worker-bin` 或 `worker-gitconfig` 里有一个并非 dsh-superagent 写入的文件,设置检查就会停住。一个处在 git 仓库之内的 Ticket 目录,若该仓库自己的配置添加了凭据,这个 Ticket 就失败。
## 试一把
用一个被允许的 GitHub 账号,开一个 Issue,或发一条包含 `@<agent login> <request>` 的评论。在一个轮询间隔内(默认一分钟),Overview 应该出现一个 Ticket。agent 会确认请求、在它自己的 Session 与目录里干活,并在原始对话里交付。用 Feishu 时,在配置好的 chat 里 @ bot。
## 升级
1. 等到没有 Executor 正处于回合中间。
2. 备份 `<stateDir>/orchestrator.sqlite`。
3. 运行 `git pull && pnpm install && pnpm build`。
4. 运行 `scripts/dsh-superagent restart` 并检查它的状态。
一次重启会结束运行中的 Assignments,并带着恢复备注启动接替者。团队文本与被覆盖的提示词部件会保留;没有覆盖的部件跟随新默认值。升级后检查标了 **Default updated** 的部件。
被更新版本迁移过的数据库无法被旧版本打开。因此回滚需要升级前的那份备份。升级 DeepSeek Harness 本身之后,重启运行中的实例,让面板加载相匹配的构件。
### agent 预设
worker 与 router 预设会把 shell 命令在 5 秒后移入后台。一个 Session 没有它的预设就无法加载。只要它们的 Sessions 还在使用,就保留 `cordis.patch.yml` 里的 `dsh-superagent-worker` 与 `dsh-superagent-router` 行;否则恢复会因 `agent-preset/not-found` 失败。
## 排障
设置问题带着修复链接出现在 overview 上,用面板的语言;日志与 API 显示下面这些英文文本。
| 症状 | 原因与修复 |
| --- | --- |
| `The agent GitHub CLI is not set.` | 在 GitHub 卡片上设置 agent 的 CLI |
| `The agent GitHub CLI was not found.` | 设置包装器的绝对路径,或一个在 `PATH` 上的命令名 |
| `The agent GitHub CLI cannot read its account` | 用 `auth login` 给包装器登录 |
| `@… cannot read …` 或 `@… cannot push to …` | 给 agent 账号授予该仓库的写权限 |
| `The GitHub Source names no repository.` | 在 GitHub 卡片上设置仓库,`owner/name` |
| Ticket 因 `The prepare command failed: …` 失败 | 输出会点名原因,例如 clone 路径不对;修好命令,然后重试这个 Ticket |
| `git does not read the github.com credential configuration dsh-superagent writes` | 安装 git 2.32 或更新 |
| Ticket 因 `The git configuration of the repository in … sets …` 失败 | 从 clone 的 `.git/config` 里移除点名的凭据或 `http.*.extraheader` 设置 |
| `… was not written by dsh-superagent` | 把点名的文件移出 `<stateDir>/worker-bin` 或 `<stateDir>/worker-gitconfig`,或另选一个 `stateDir` |
| `The bot lark-cli allows user identities` | 把 Feishu Source 指向一个仅 bot 的配置,如上文 Feishu bot 所述 |
| `The bot lark-cli does not work.` 或 `The bot identity of app … is …` | 检查配置里的 app id 与 secret |
| `The Feishu bot is not a member of the chat.` | 在该 chat 的设置里把 bot 拉进群;下一次尝试就会成功 |
| `status` 退出码 2 | 某个部件在报错,重启修不好:`errors` 点名每个部件(如 Management API 一节所列)及其细节;每个部件恢复正常后清掉自己的条目 |
| `status` 退出码 3 | 设置在等待:`setup` 说明缺什么,overview 链接到能修复它的设置 |
| `status` 退出码 1 | 服务没在跑、跑了双份、不回应、或其 runtime 报错;输出与最新的日志会说明是哪一种 |
| `… timed out after 90 s`、`… was stopped by …`、`… exited with code …` 或 `… could not run: …` | 某个 CLI 失败而没说明原因;以操作者的用户手动运行被点名的程序,看它自己的报错 |
| 面板说 dsh-superagent 不回应 | 它正在重启,或因配置问题没起来;DeepSeek Harness 日志会点名原因 |
| `dsh-superagent configuration has keys it does not use`(日志) | 移除点名的键;消息会说每个值现在在哪里 |
| 日志说插件与运行中的 dsh 不兼容 | 安装一个落在 `engines.dsh` 范围内的 DeepSeek Harness 版本 |
| DeepSeek Harness 升级后,面板显示 "Failed to load plugins" | 重启运行中的实例 |
| @ 之后什么都没发生 | 检查作者是否在该 Source 的允许名单上、Source 是否开着、处理是否被暂停。GitHub 上,靠编辑 Issue 正文补的 @ 不会被收到;请在评论里 @ agent |