Files
openclaw-config/docs/agent-创建规范与自动化-设计.md
T
openclaw-314 31c9958741 feat(agent-db): 引入 db-conn.sh 统一数据库入口,各 agent 改用专用账号
- 新增 scripts/db-conn.sh:按 agent id 反查中文名,读 .env 后经 MYSQL_PWD 转发给 docker exec,口令不进 argv/stdout,--print 只输出占位符
- new-agent.sh 新增 --adopt 模式(为既有 agent 补建库/账号),与 --reset-password 互斥
- AGENTS.md.tpl 与 11 个 workspace-*/AGENTS.md 改用 db-conn.sh,禁用 root 与 docker exec 直连
- mysql-schema-sync 技能改用 MYSQL_PWD_SQL 环境变量,不再硬编码密码
- 设计文档补附录 A 第 8 条:中文键名无法注入 agent 环境的三条实测依据
2026-09-16 11:30:55 +08:00

238 lines
14 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.
# agent 创建规范与数据库自动化(设计)
> **状态**:设计已确认(2026-09-16)· **待实现**
> **适用**bk02 上的 OpenClaw(前置阅读:`bk02-openclaw-系统说明.md`
> **决策记录**:见 §12
>
> ⚠️ 本文不含明文口令。
---
## 1. 目标与范围
**目标**
1. **main agent 在对话中即可创建新 agent**,且新 agent 的配置(`IDENTITY.md`/`SOUL.md`/`AGENTS.md`/`MEMORY.md``openclaw.json``name`/`identity.name`**全部为中文**。
2. **每个 agent 自动获得专属 MySQL 库**(库名 = `agent_id`+ **专用账号**(只授权自己的库,不再用 root)。
3. 新建 agent 的库**次日自动进入每日备份**,无需人工登记。
**范围**
- 新增:`new-agent.sh`provisioning 脚本)、中文模板一组、`agent-provisioning` skill
- 改造:既有每日备份脚本 `backup-agent-dbs.sh`(硬编码 → 动态发现)
- **不修改** OpenClaw 自身(纯配置/文件层扩展)
**明确不做**:见 §9。
---
## 2. 命名与标识规范
| 概念 | 形式 | 示例 |
|---|---|---|
| `agent_id` | ASCII slug,正则 `^[a-z][a-z0-9-]{0,23}$` | `reading` |
| 中文名 | 26 个汉字 | `阅读` |
| MySQL 库 | **= `agent_id`** | `reading` |
| MySQL 账号 | `agent_<id>` | `agent_reading` |
| 密码变量 | `DB_PASSWORD_<中文名>`(写入 `~/.openclaw/.env` | `DB_PASSWORD_阅读` |
| workspace | `~/.openclaw/workspace-<id>` | `workspace-reading` |
| agentDir | `~/.openclaw/agents/<id>/agent` | — |
**中文只用于展示层**`name``identity.name`、文档正文、`.env` 变量名后缀)。
`id` / 库名 / 账号 / 目录名一律 ASCII —— 依据:既有的 `Obsidian``obsidian`(仅大小写不同)曾导致 `openclaw backup create --verify`*portable path collision* 失败。
---
## 3. 组件与职责
| 组件 | 路径 | 职责 |
|---|---|---|
| provisioning 脚本 | `~/.openclaw/scripts/new-agent.sh` | **唯一特权入口**:建库、建账号、授权、写 `.env`、创建 agent、渲染模板、设置中文名、自检 |
| 中文模板 | `~/.openclaw/scripts/agent-templates/{IDENTITY,SOUL,AGENTS,MEMORY}.md.tpl` | 新建 agent 的文档骨架 |
| skill | `~/.openclaw/skills/agent-provisioning/SKILL.md` | 中文;定义触发词、参数、约束、输出格式;约束 main **只能调用脚本** |
| 审计日志 | `~/.openclaw/workspace/db/agent-provision.log` | 脚本每次执行追加一行(时间/参数/结果),供事后对账 |
| 备份脚本(改造) | `/usr/local/bin/backup-agent-dbs.sh` | 见 §10 |
**设计要点**
- 特权动作(root 建库)只存在于脚本内;main 的 skill 里写死"只允许调用脚本"。
- skill 与 `~/.openclaw` 同处一个 git 仓库,规范可版本化、可追溯。
---
## 4. 数据流(main 创建 agent
```
用户:“新建一个 agent,id 用 reading,中文名 阅读”
main 读 skills/agent-provisioning/SKILL.md(约束 + 流程)
main 执行:bash ~/.openclaw/scripts/new-agent.sh reading 阅读
脚本(幂等,逐步输出):
① 入参校验:id 正则 / 中文名非空且长度合法
② 冲突检查:库、agent 条目、workspace 目录任一已存在 → 失败退出(见 §6)
③ 生成 32 位随机密码
④ MySQLroot 凭据取自 ~/.openclaw/.env 的 MYSQL_PWD_ROOT):
CREATE DATABASE `<id>` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CREATE USER 'agent_<id>'@'%' IDENTIFIED BY '<随机密码>';
GRANT ALL PRIVILEGES ON `<id>`.* TO 'agent_<id>'@'%';
⑤ 写入 ~/.openclaw/.envDB_PASSWORD_<中文名>='<随机密码>'
(同键则替换,不重复追加;.env 已在 .gitignore
⑥ openclaw agents add <id> --workspace ~/.openclaw/workspace-<id> --non-interactive
⑦ 渲染中文模板到 workspace-<id>/(覆盖 openclaw 生成的英文骨架)
⑧ openclaw agents set-identity --agent <id> --from-identity
(从 IDENTITY.md 读中文名写入配置)
⑨ 自检(§7)并输出汇总:库名 / 账号 / 凭据变量 / workspace / 下一步
```
**为什么用 `--from-identity`**:中文名先落在 `IDENTITY.md`(人可读),再一条命令同步进 `openclaw.json`,避免两处手工维护不一致。
---
## 5. 中文模板
四件套(全中文,`.tpl` 内用占位符 `{{AGENT_ID}}``{{AGENT_NAME}}`):
- **`IDENTITY.md`** —— 我是谁(含中文名、emoji)
- **`SOUL.md`** —— 性格与表达风格
- **`AGENTS.md`** —— 工作规范(**核心是数据库段**)
- **`MEMORY.md`** —— 长期记忆骨架
`AGENTS.md.tpl` 的数据库段(沿用 `workspace-sql` 已验证的范式):
```markdown
## 数据库规范
- 默认库:`{{AGENT_ID}}` —— 这就是你自己的库
- 账号:`agent_{{AGENT_ID}}`
- 连接(推荐):`~/.openclaw/scripts/db-conn.sh {{AGENT_ID}}`(交互式)或 `db-conn.sh {{AGENT_ID}} --sql "select database()"`
- 口令:由该脚本自动从 `~/.openclaw/.env` 读取(键名 `DB_PASSWORD_{{AGENT_NAME}}`),口令不会出现在命令行中
- 禁止:使用 root、使用 docker exec、访问其他 agent 的库、跨库写入
- 破坏性语句(DROP / TRUNCATE / 无条件 DELETE)必须先请示
```
---
## 6. 幂等、错误处理与安全
| 场景 | 行为 |
|---|---|
| id 非法 / 中文名为空 | 立即失败并打印用法 |
| 库或 agent 或 workspace **已存在** | **默认失败**(不覆盖、不改动);仅 `--reset-password` 才重置账号密码 |
| 中途失败 | 已完成的步骤保留;重跑时明确提示"已完成到第 N 步",不静默改写 |
| `.env` 写入 | 幂等替换(同键覆盖,避免重复行) |
| SQL 注入 | id 必须通过白名单正则后才参与拼接 |
| 凭据 | 脚本**不硬编码**密码,从 `~/.openclaw/.env` 读;生成的密码只写 `.env` |
---
## 7. 验证方式
- `new-agent.sh --dry-run`:打印将执行的 SQL 与文件操作,**不落地**
- 创建后自动自检:
1. 库存在
2. **用新账号实连一次 `SELECT 1`**(证明授权真的生效)
3. `workspace-<id>/` 四件套齐全
4. 输出可直接复制给用户的凭据行
---
## 8. 存量处置(所有 agent
**范围:全部 12 个 agent 都要有库 + 专用账号**(含 `openclaw`)。
| 分组 | agent | 动作 |
|---|---|---|
| 已有库,需改造 | `main` `juaner` `wellness` `tab` `finances` `fitness` `resume` `travel` | 建 `agent_<id>` 账号 + 授权 + 改各自 `AGENTS.md` 数据库段(由 root 直连改为专用账号) |
| 缺库需新建 | `note`(笔记) `pbs`(PBS备份) | 建库建号 + 补文档 |
| 特例 | `sql`(SQL) | 保留其**远程 VPS 业务库**定位,另给一个私有库做工作区 |
| 待纳入 | `openclaw` | 当前不在 `agents.entries``agentToAgent.allow`;按「所有 agent」口径**纳入并建库建号**(纳入前确认不影响既有行为) |
> 存量改造是**一次性迁移**,建议作为独立步骤执行(含改 `.env` 变量与各 agent 文档),与新建流程分开验证。
---
## 9. 非目标(YAGNI
- 不做可视化界面 / 批量重建工具
- 不做库结构版本迁移工具
- 不去改 skill 机制使其按 agent 隔离 —— OpenClaw 的 `skills.entries` 是**全局开关(52 项,仅 `enabled`)**,无法按 agent 配置;权限隔离由 **MySQL 账号**承担(这也是"专用账号"从建议变为必需的原因)
---
## 10. 每日备份改造
**现状**(已存在,非自建部分):`crontab 0 3 * * * /usr/local/bin/backup-agent-dbs.sh`
- 逐库 `mysqldump --single-transaction --routines --triggers --events`**用 root**
- 备份到各自 workspace 的 `db/`(如 `workspace-resume/db/resume_20260916.sql.gz`),另留一份 `*_latest.sql`
- 清理 30 天前备份
- 结束后 `git commit` + **push 到自建 Gitea**
**问题**:库清单是**硬编码的 8 条 `DB_MAP`** ⇒ **新建 agent 不会被备份**
**改造目标**
1. **动态发现**:备份目标 = `agents.entries` 的 id ∩ `information_schema.schemata`(排除 `mysql`/`information_schema`/`performance_schema`/`sys`),这样 provisioning 建的库**次日自动纳入**
2. **不硬编码密码**:改为从 `~/.openclaw/.env` 读(而非脚本内明文)
3. 保留策略(30 天)与 **push 到 Gitea 保持现状**Gitea 为自建)
4. PostgreSQL`openclaw` 库)分支保持不动
---
## 11. 技能强制性 = 档 ①(纯规范,不改环境)
**已明确接受的前提**:不剥夺凭据、不收窄 sudo、不启用 exec 审批。因此 main **技术上仍可能**从 `.env` / 备份脚本取得 root 自行建库 —— 规范能大幅降低概率,但**不能归零**
在纯规范档下,把约束强化到六点:
1. **写入 main 的「红线」章节**`workspace/AGENTS.md` 已有该章节):
> 创建 agent 必须且只能执行 `bash ~/.openclaw/scripts/new-agent.sh <id> <中文名>`
> 禁止自行拼 SQL、禁止用 `mysql`/`docker exec` 建库、禁止为建库读取任何 root 凭据。
2. **SKILL.md 的 `description` 精确化**(skill 触发的唯一依据):写明触发场景与关键词("新建 agent""创建 agent""加一个 agent")。
3. **让正确路径比绕过更省事**(行为引导):脚本一条命令完成 9 步;绕过则需自行拼 SQL、处理 `.env`、写四个中文模板 —— 复杂度差本身就是约束。
4. **脚本内不硬编码密码**(见 §6)。
5. **脚本自带轻量审计**:每次执行追加一行到 `workspace/db/agent-provision.log`;**不改环境**,但事后可对账 —— 库中多出的账号若不在日志里,即为绕过证据。
6. **阻断态显式提示**:出现"库已存在但账号缺失"等异常时,输出"请改用 `new-agent.sh`"的明确指引。
---
## 12. 决策记录
| # | 决策点 | 结论 | 日期 |
|---|---|---|---|
| 1 | 落地形态 | **方案 B**:脚本 + skill,由 main 调用(否决纯人工脚本、否决把 root 交给 main 自行拼 SQL | 2026-09-16 |
| 2 | 目标库与账号 | **本机 docker MySQL**;**每个 agent 专用账号**(停用 root 直连) | 2026-09-16 |
| 3 | 存量范围 | **所有 agent**(含 `openclaw`)都要有库与账号 | 2026-09-16 |
| 4 | 中文范围 | 展示层全中文;`id`/库名/账号/目录名保持 ASCII | 2026-09-16 |
| 5 | 备份 | 改造为动态发现;**保持 push 到自建 Gitea** | 2026-09-16 |
| 6 | 技能强制性 | **档 ①(纯规范)**:不改凭据、不收窄 sudo、不加 exec 审批 | 2026-09-16 |
| 7 | 授权模型 | skill 层无法按 agent 隔离(`skills.entries` 全局)⇒ 隔离必须由 MySQL 账号承担 | 2026-09-16 |
---
## 附:实现时需现场确认的两点
1. `openclaw agents add` 生成的骨架文件清单与内容 —— 决定模板是**覆盖**还是**补充**
2. `set-identity --from-identity``IDENTITY.md` 格式的具体要求(字段名/解析规则)
---
## 附录 A:实现期间的实测发现(2026-09-16)
> 本节回答 §「附:实现时需现场确认的两点」,并记录实现中撞出的偏差与处置。
| # | 发现 | 处置 |
|---|---|---|
| 1 | `.env` 中**没有** `MYSQL_PWD_ROOT`(真实键名是 `MYSQL_PWD`);且计划里的 `grep` 写法在 `set -euo pipefail` 下会因无匹配**静默退出**(连报错都没有) | 脚本抽出 `read_env_var()` 并做 `MYSQL_PWD_ROOT → MYSQL_PWD` 回退 |
| 2 | `openclaw agents add` 生成 `AGENTS/BOOTSTRAP/IDENTITY/SOUL/USER.md` **+ 一个 `.git` 仓库**,且**不生成** `MEMORY.md`/`memory/`;存量 12 个 workspace 都没有 `.git` | 脚本渲染 5 个中文模板(补齐 MEMORY/USER),并删除 `BOOTSTRAP.md`(身份已定,避免首启"起名仪式")与 `.git`(避免嵌套仓库) |
| 3 | `set-identity --from-identity` **只认英文标签** `- Name: xxx`,对中文模板的 `- **名称:**` 解析失败(`No identity data found` | 设计目标(配置中存中文名)由 `--name` 兜底达成;脚本保留兜底分支。**这不是 bug,是既定偏差** |
| 4 | 存量 `.env` 里 6 行 `DB_PASSWORD_<中文名>` 的**值与 MySQL root 密码相同** | 意味着现状下"每 agent 一账号"并未真正隔离。Task E 将生成**独立随机口令**并替换存量行 |
| 5 | 同机 3 个 `workspace-{juaner,tab,wellness}/db` 属主为 root → 这 3 个库自 2026-08-26 起**每天备份失败** | 已 `chown` 修复;备份脚本验收明确改为**不加 `sudo`**(生产路径是 yangxuan 的 crontab;用 sudo 跑正是该事故成因) |
| 6 | `agents delete` 会连 workspace 一起删,但留下 `~/.openclaw/agents/<id>/` 空壳 | 清理流程需手工 `rm -rf`(已写入脚本注释与计划文档) |
| 7 | 中文名含 `&` 时,调用侧若不加引号会被 shell 解析;实测已导致一次意外创建 | 已回灌进 `agent-provisioning` skill**必须写成 `<id> '<中文名>'`** |
| 8 | **中文键名不可能作为环境变量暴露给 agent** —— 三条实测依据:(a) gateway 进程 `/proc/<pid>/environ` 里只有 `MYSQL_PWD_ROOT`/`DB_PASSWORD_ROOT`**没有** `DB_PASSWORD_<中文名>`(b) service 的 `OPENCLAW_SERVICE_MANAGED_ENV_KEYS` 是白名单,只列 9 个键、不含 `DB_PASSWORD_*`,而键数随 agent 数量增长,维护白名单不现实;(c) 官方把凭据注入 agent 执行环境的机制 `openclaw secrets store --kind env` 要求名称匹配 `^[A-Z][A-Z0-9_]{0,127}$`(**纯大写 ASCII**),中文键名语法上就不合法 | ⇒ 以「环境变量」形式暴露中文键名**不可行**,改用统一入口 `~/.openclaw/scripts/db-conn.sh <agent_id> [--sql "<SQL>"] [--print]`:按 id 反查 `agents.entries.<id>.name` → 读 `.env``DB_PASSWORD_<中文名>` → 经 `MYSQL_PWD` 环境变量注入 `docker exec`(口令不进 argv、不进 stdout`--print` 只打印占位符)。已同步:11 个 `AGENTS.md``AGENTS.md.tpl``new-agent.sh` 汇总输出、本文 §5 示例。**§2 命名规范里的「密码变量 `DB_PASSWORD_<中文名>`」依然成立,但它的定位是 `.env` 内的存储键名,不再是 agent 的环境变量** |
**审计日志格式(Task E 解析用)**
`[时间] id=… name=… user=… mode=create|reset-password selfcheck=… dry_run=…`