83439ee623
- scripts/new-agent.sh:建库/建号/授权/写 .env/创建 agent/渲染中文模板/自检,幂等且默认拒绝覆盖 - scripts/agent-templates/:5 个中文模板(IDENTITY/SOUL/AGENTS/MEMORY/USER) - skills/agent-provisioning/:main 创建 agent 的唯一入口与红线(description 覆盖中英触发说法) - workspace/:修复悬空 gitlink,将 9 个配置文档纳入版本控制(含新增创建 agent 与数据库红线) - docs/:设计文档(含附录 A 实测发现)、实现计划(含 sudo 事故修正) - .gitignore:workspace 白名单,避免数据/技能/媒体进入版本库
526 lines
20 KiB
Markdown
526 lines
20 KiB
Markdown
# agent 创建规范与数据库自动化 —— 实现计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use subagent-driven-development to implement this plan task-by-task.
|
||
>
|
||
> **Goal:** 让 main 一条命令创建带专属 MySQL 库与全中文配置的新 agent,并让新建库自动进入每日备份。
|
||
>
|
||
> **Architecture:** 特权操作全部收敛到 `new-agent.sh`(建库 → 建号 → 授权 → 写 .env → 创建 agent → 渲染中文模板 → 自检);skill 只负责触发与约束;备份脚本由硬编码清单改为动态发现。
|
||
>
|
||
> **Spec:** `~/.openclaw/docs/agent-创建规范与自动化-设计.md`(执行者需同时读此文档)
|
||
>
|
||
> **执行环境:** ⚠️ **所有文件位于 bk02,不是本机**。通过 `sshpass -p '<密码>' ssh yangxuan@bk02` 操作;本地写好的文件用 `scp` 传给 bk02 再 `mv` 到目标路径。
|
||
>
|
||
> **Global Constraints**
|
||
> - `agent_id` 必须匹配 `^[a-z][a-z0-9-]{0,23}$`
|
||
> - 库名 = `agent_id`;账号 = `agent_<id>`;**只授权 `<id>.*`**
|
||
> - 展示层(模板与文档内容、`name`、`identity.name`)**全中文**;标识层(id/库名/账号/目录名)**全 ASCII**
|
||
> - 脚本**不硬编码任何密码**;root 凭据从 `~/.openclaw/.env` 的 `MYSQL_PWD_ROOT` 读
|
||
> - **默认拒绝覆盖**:库 / agent 条目 / workspace 任一已存在即失败退出
|
||
> - 改动提交到 `~/.openclaw` 仓库(分支 `314`)并 **push**(该仓库 auto-commit 在无变更时不推送)
|
||
|
||
---
|
||
|
||
## 任务依赖图
|
||
|
||
```
|
||
Task A(脚本+模板)──► Task E(存量改造,串行)
|
||
Task B(skill) ┐
|
||
Task C(备份改造) ├─ 与 A 无文件冲突,可并行
|
||
Task D(红线+索引) ┘
|
||
```
|
||
|
||
---
|
||
|
||
## Task A:provisioning 脚本 + 中文模板
|
||
|
||
**Files**
|
||
- Create `~/.openclaw/scripts/new-agent.sh`(`chmod 755`)
|
||
- Create `~/.openclaw/scripts/agent-templates/IDENTITY.md.tpl`
|
||
- Create `~/.openclaw/scripts/agent-templates/SOUL.md.tpl`
|
||
- Create `~/.openclaw/scripts/agent-templates/AGENTS.md.tpl`
|
||
- Create `~/.openclaw/scripts/agent-templates/MEMORY.md.tpl`
|
||
|
||
**Interfaces(Produces,Task B/E 依赖)**
|
||
- CLI:`new-agent.sh <id> <中文名> [--dry-run] [--reset-password]`
|
||
- 成功时 stdout 末尾打印四行:`DATABASE=` / `USER=` / `PASSWORD_ENV=` / `WORKSPACE=`
|
||
- 审计日志追加一行到 `~/.openclaw/workspace/db/agent-provision.log`
|
||
|
||
**Steps**
|
||
|
||
- [ ] **1. 写四个中文模板**
|
||
|
||
`IDENTITY.md.tpl`:
|
||
```markdown
|
||
# IDENTITY.md —— 我是谁?
|
||
|
||
- **名称:** {{AGENT_NAME}}
|
||
- **编号:** {{AGENT_ID}}
|
||
- **定位:** (一句话说明我负责什么)
|
||
- **创建时间:** {{CREATED_AT}}
|
||
```
|
||
|
||
`SOUL.md.tpl`:
|
||
```markdown
|
||
# SOUL.md —— 我的性格与表达
|
||
|
||
- **风格:** 简洁、直接、先给结论再给依据。
|
||
- **原则:** 不确定时先问,不猜测;做破坏性操作前必须先请示。
|
||
- **语言:** 一律用中文回复。
|
||
- **边界:** 只处理我职责范围内的事,跨范围时转交对应 agent。
|
||
```
|
||
|
||
`AGENTS.md.tpl`:
|
||
```markdown
|
||
# AGENTS.md —— 我的工作规范
|
||
|
||
## 会话启动
|
||
1. 读 `IDENTITY.md`(我是谁)
|
||
2. 读 `MEMORY.md`(长期记忆)
|
||
3. 读 `memory/` 下最近两天的记录
|
||
|
||
## 记忆
|
||
- 每日记录写 `memory/YYYY-MM-DD.md`
|
||
- 长期结论写 `MEMORY.md`
|
||
|
||
## 数据库规范
|
||
- **默认库:** `{{AGENT_ID}}` —— 这就是我自己的库
|
||
- **账号:** `agent_{{AGENT_ID}}`
|
||
- **密码:** 环境变量 `DB_PASSWORD_{{AGENT_NAME}}`
|
||
- **连接:** `mysql -h 127.0.0.1 -u agent_{{AGENT_ID}} -p"${DB_PASSWORD_{{AGENT_NAME}}}" {{AGENT_ID}}`
|
||
- **禁止:** 使用 root、使用 `docker exec`、访问其他 agent 的库、跨库写入
|
||
- **建表前**先确认字段与索引;**破坏性语句**(DROP / TRUNCATE / 无条件 DELETE)必须先请示
|
||
|
||
## 红线
|
||
- 绝不泄露私密数据
|
||
- 未经请示绝不运行破坏性命令
|
||
```
|
||
|
||
`MEMORY.md.tpl`:
|
||
```markdown
|
||
# MEMORY.md —— 长期记忆
|
||
|
||
> 只写经过筛选的、长期有效的结论;原始流水写 `memory/YYYY-MM-DD.md`。
|
||
|
||
## 关于我的职责
|
||
|
||
## 关于我的库 `{{AGENT_ID}}`
|
||
|
||
## 重要决定
|
||
```
|
||
|
||
- [ ] **2. 写脚本骨架(参数校验 + 冲突检查 + dry-run)**
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
# 新建 OpenClaw agent:建库 → 建号 → 授权 → 写 .env → 创建 agent → 渲染中文模板 → 自检
|
||
set -euo pipefail
|
||
|
||
ENV_FILE="$HOME/.openclaw/.env"
|
||
TEMPLATE_DIR="$HOME/.openclaw/scripts/agent-templates"
|
||
OPENCLAW_ROOT="$HOME/.openclaw"
|
||
LOG_DIR="$OPENCLAW_ROOT/workspace/db"
|
||
LOG_FILE="$LOG_DIR/agent-provision.log"
|
||
MYSQL_CONTAINER="mysql"
|
||
|
||
AGENT_ID="${1:-}"; AGENT_NAME="${2:-}"; shift 2 2>/dev/null || true
|
||
DRY_RUN=0; RESET_PASSWORD=0
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--dry-run) DRY_RUN=1 ;;
|
||
--reset-password) RESET_PASSWORD=1 ;;
|
||
*) echo "未知参数: $arg" >&2; exit 2 ;;
|
||
esac
|
||
done
|
||
|
||
usage() { echo "用法: new-agent.sh <id> <中文名> [--dry-run] [--reset-password]"; }
|
||
|
||
# —— ① 入参校验 ——
|
||
[[ -n "$AGENT_ID" && -n "$AGENT_NAME" ]] || { usage; exit 2; }
|
||
[[ "$AGENT_ID" =~ ^[a-z][a-z0-9-]{0,23}$ ]] || { echo "id 非法:只允许小写字母开头、[a-z0-9-]、最长 24 字符"; exit 2; }
|
||
[[ "$AGENT_NAME" =~ ^[^[:space:]]{2,20}$ ]] || { echo "中文名非法:2–20 个字符且不含空白"; exit 2; }
|
||
|
||
WORKSPACE="$OPENCLAW_ROOT/workspace-$AGENT_ID"
|
||
DB_USER="agent_$AGENT_ID"
|
||
PASSWORD_VAR="DB_PASSWORD_$AGENT_NAME"
|
||
|
||
# —— root 凭据:从 .env 读,绝不硬编码 ——
|
||
MYSQL_ROOT_PWD="$(grep -E '^MYSQL_PWD_ROOT=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"'"'"'')"
|
||
[[ -n "$MYSQL_ROOT_PWD" ]] || { echo "无法从 $ENV_FILE 读取 MYSQL_PWD_ROOT"; exit 1; }
|
||
|
||
mysql_root() { docker exec -e MYSQL_PWD="$MYSQL_ROOT_PWD" "$MYSQL_CONTAINER" mysql -uroot -N -B -e "$1"; }
|
||
```
|
||
|
||
- [ ] **3. 冲突检查(默认拒绝覆盖)**
|
||
|
||
```bash
|
||
# —— ② 冲突检查 ——
|
||
DB_EXISTS="$(mysql_root "select count(*) from information_schema.schemata where schema_name='$AGENT_ID';")"
|
||
USER_EXISTS="$(mysql_root "select count(*) from mysql.user where user='$DB_USER';")"
|
||
AGENT_EXISTS="$(python3 -c "
|
||
import json
|
||
d=json.load(open('$OPENCLAW_ROOT/openclaw.json'))
|
||
print(1 if '$AGENT_ID' in (d.get('agents') or {}).get('entries',{}) else 0)
|
||
")"
|
||
WS_EXISTS=$([[ -d "$WORKSPACE" ]] && echo 1 || echo 0)
|
||
|
||
if [[ "$RESET_PASSWORD" == "1" ]]; then
|
||
[[ "$USER_EXISTS" == "1" ]] || { echo "账号 $DB_USER 不存在,无法重置密码"; exit 1; }
|
||
else
|
||
[[ "$DB_EXISTS" == "0" ]] || { echo "❌ 库 $AGENT_ID 已存在 —— 已存在请使用 --reset-password,或改用 new-agent.sh 管理"; exit 1; }
|
||
[[ "$USER_EXISTS" == "0" ]] || { echo "❌ 账号 $DB_USER 已存在 —— 请使用 --reset-password"; exit 1; }
|
||
[[ "$AGENT_EXISTS" == "0" ]] || { echo "❌ agent 条目 $AGENT_ID 已存在"; exit 1; }
|
||
[[ "$WS_EXISTS" == "0" ]] || { echo "❌ 目录 $WORKSPACE 已存在"; exit 1; }
|
||
fi
|
||
```
|
||
|
||
- [ ] **4. 建库建号授权 + 密码生成**
|
||
|
||
```bash
|
||
# —— ③ 生成密码 ——
|
||
NEW_PWD="$(openssl rand -base64 24 | tr -d '/+=' | cut -c1-32)"
|
||
|
||
# —— ④ SQL(dry-run 只打印)——
|
||
SQL_CREATE_DB="CREATE DATABASE \`$AGENT_ID\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;"
|
||
SQL_CREATE_USER="CREATE USER '$DB_USER'@'%' IDENTIFIED BY '$NEW_PWD';"
|
||
SQL_GRANT="GRANT ALL PRIVILEGES ON \`$AGENT_ID\`.* TO '$DB_USER'@'%';"
|
||
|
||
if [[ "$DRY_RUN" == "1" ]]; then
|
||
echo "[dry-run] 将执行:"; echo " $SQL_CREATE_DB"; echo " $SQL_CREATE_USER"; echo " $SQL_GRANT"
|
||
echo "[dry-run] 将写入 $ENV_FILE:$PASSWORD_VAR=<32位随机>"
|
||
echo "[dry-run] 将创建 workspace:$WORKSPACE"
|
||
echo "[dry-run] 将执行:openclaw agents add $AGENT_ID --workspace $WORKSPACE --non-interactive"
|
||
exit 0
|
||
fi
|
||
|
||
mysql_root "$SQL_CREATE_DB" >/dev/null
|
||
if [[ "$RESET_PASSWORD" == "1" ]]; then
|
||
mysql_root "ALTER USER '$DB_USER'@'%' IDENTIFIED BY '$NEW_PWD';" >/dev/null
|
||
else
|
||
mysql_root "$SQL_CREATE_USER" >/dev/null
|
||
fi
|
||
mysql_root "$SQL_GRANT" >/dev/null
|
||
```
|
||
|
||
- [ ] **5. .env 幂等写入**
|
||
|
||
```bash
|
||
# —— ⑤ 写 .env(同键覆盖,不重复追加)——
|
||
if grep -q "^${PASSWORD_VAR}=" "$ENV_FILE"; then
|
||
python3 - "$ENV_FILE" "$PASSWORD_VAR" "$NEW_PWD" <<'PY'
|
||
import sys
|
||
path, key, val = sys.argv[1], sys.argv[2], sys.argv[3]
|
||
lines = open(path, encoding='utf-8').read().splitlines()
|
||
out = [f'{key}={val}' if l.startswith(key + '=') else l for l in lines]
|
||
open(path, 'w', encoding='utf-8').write('\n'.join(out) + '\n')
|
||
PY
|
||
else
|
||
printf '%s=%s\n' "$PASSWORD_VAR" "$NEW_PWD" >> "$ENV_FILE"
|
||
fi
|
||
chmod 600 "$ENV_FILE"
|
||
```
|
||
|
||
- [ ] **6. 创建 agent + 渲染模板 + 同步中文名**
|
||
|
||
```bash
|
||
# —— ⑥ 创建 agent ——
|
||
export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"; nvm use 26 >/dev/null 2>&1
|
||
openclaw agents add "$AGENT_ID" --workspace "$WORKSPACE" --non-interactive >/dev/null
|
||
|
||
# —— ⑦ 渲染中文模板(覆盖 openclaw 生成的英文骨架)——
|
||
CREATED_AT="$(date '+%Y-%m-%d')"
|
||
for tpl in IDENTITY SOUL AGENTS MEMORY; do
|
||
sed -e "s/{{AGENT_ID}}/$AGENT_ID/g" \
|
||
-e "s/{{AGENT_NAME}}/$AGENT_NAME/g" \
|
||
-e "s/{{CREATED_AT}}/$CREATED_AT/g" \
|
||
"$TEMPLATE_DIR/$tpl.md.tpl" > "$WORKSPACE/$tpl.md"
|
||
done
|
||
mkdir -p "$WORKSPACE/memory"
|
||
|
||
# —— ⑧ 中文名同步进配置 ——
|
||
openclaw agents set-identity --agent "$AGENT_ID" --from-identity >/dev/null 2>&1 || \
|
||
openclaw agents set-identity --agent "$AGENT_ID" --name "$AGENT_NAME" >/dev/null
|
||
```
|
||
|
||
- [ ] **7. 自检(核心:用新账号实连)**
|
||
|
||
```bash
|
||
# —— ⑨ 自检:证明授权真的生效 ——
|
||
if docker exec -e MYSQL_PWD="$NEW_PWD" "$MYSQL_CONTAINER" mysql -u"$DB_USER" -N -B -e "SELECT 1;" "$AGENT_ID" >/dev/null 2>&1; then
|
||
SELFCHECK="ok"
|
||
else
|
||
SELFCHECK="FAILED"
|
||
fi
|
||
|
||
mkdir -p "$LOG_DIR"
|
||
printf '[%s] id=%s name=%s user=%s selfcheck=%s dry_run=%s\n' \
|
||
"$(date '+%Y-%m-%d %H:%M:%S')" "$AGENT_ID" "$AGENT_NAME" "$DB_USER" "$SELFCHECK" "$DRY_RUN" >> "$LOG_FILE"
|
||
|
||
cat <<EOF
|
||
|
||
✅ 已创建 agent:$AGENT_NAME($AGENT_ID)
|
||
DATABASE=$AGENT_ID
|
||
USER=$DB_USER
|
||
PASSWORD_ENV=$PASSWORD_VAR
|
||
WORKSPACE=$WORKSPACE
|
||
自检=$SELFCHECK
|
||
EOF
|
||
[[ "$SELFCHECK" == "ok" ]] || { echo "⚠️ 自检失败:请检查授权"; exit 1; }
|
||
```
|
||
|
||
**验收(Task A)**
|
||
```bash
|
||
# 1) 语法与 dry-run
|
||
bash -n ~/.openclaw/scripts/new-agent.sh
|
||
bash ~/.openclaw/scripts/new-agent.sh tprobe 测试探针 --dry-run
|
||
|
||
# 2) 实跑一个临时 agent,验证三件事,然后清理
|
||
bash ~/.openclaw/scripts/new-agent.sh tprobe 测试探针
|
||
# → 库存在? docker exec -e MYSQL_PWD=... mysql mysql -uroot -e "show databases like 'tprobe';"
|
||
# → 账号只对本库有权限? docker exec ... mysql -uroot -e "show grants for 'agent_tprobe'@'%';"
|
||
# → 模板全中文? head -3 ~/.openclaw/workspace-tprobe/AGENTS.md
|
||
# 3) 幂等:再跑一次应被拒绝(提示已存在)
|
||
# 4) 清理:删除测试 agent / 库 / 账号 / workspace / .env 行 / 日志行
|
||
```
|
||
> ⚠️ 清理必须彻底:`openclaw agents delete tprobe`、`DROP DATABASE`、`DROP USER`、删 workspace、删 `.env` 中的 `DB_PASSWORD_测试探针`。
|
||
|
||
---
|
||
|
||
## Task B:skill `agent-provisioning`
|
||
|
||
**Files**
|
||
- Create `~/.openclaw/skills/agent-provisioning/SKILL.md`
|
||
|
||
**Interfaces**
|
||
- Consumes:Task A 的 CLI 契约(`new-agent.sh <id> <中文名>`)
|
||
- Produces:main 在相应语境下加载本 skill 并调用脚本
|
||
|
||
**Steps**
|
||
|
||
- [ ] **1. 写 SKILL.md**
|
||
|
||
```markdown
|
||
---
|
||
name: agent-provisioning
|
||
description: "新建 OpenClaw agent 的唯一正确流程:当用户说「新建 agent」「创建 agent」「加一个 agent」「给某人开一个助手」时使用。负责为 agent 建专属 MySQL 库、专用账号、中文配置与 workspace。禁止绕过本流程手工建库。"
|
||
---
|
||
|
||
# 新建 agent(唯一正确流程)
|
||
|
||
## 何时使用
|
||
用户表达"新建 / 创建 / 增加一个 agent(助手 / 机器人 / 专属助理)"时。
|
||
|
||
## 唯一正确做法
|
||
|
||
```bash
|
||
bash ~/.openclaw/scripts/new-agent.sh <id> <中文名>
|
||
```
|
||
|
||
- `<id>`:ASCII,小写字母开头,只含 `[a-z0-9-]`,≤24 字符(例:`reading`)
|
||
- `<中文名>`:2–20 字符,不含空白(例:`阅读`)
|
||
|
||
先与用户确认这两个值,再执行。
|
||
|
||
## 红线(禁止)
|
||
|
||
- ❌ 禁止自行拼 SQL 建库、建账号
|
||
- ❌ 禁止用 `mysql` / `docker exec` 直接操作数据库来创建 agent
|
||
- ❌ 禁止为建库读取或使用 root 凭据
|
||
- ❌ 禁止手工创建 `workspace-*` 目录或手改 `openclaw.json` 的 agents 条目
|
||
|
||
以上任一被绕过,都会造成"库存在但未被备份登记 / 账号缺失 / 配置中英混杂"的不一致。
|
||
|
||
## 执行后
|
||
|
||
脚本会输出四行结果(`DATABASE` / `USER` / `PASSWORD_ENV` / `WORKSPACE`)。
|
||
把结果转述给用户,并提示:新库将在**次日 03:00 自动进入每日备份**。
|
||
|
||
## 参数
|
||
|
||
| 参数 | 用途 |
|
||
|---|---|
|
||
| `--dry-run` | 只打印将执行的 SQL 与文件操作,不落地(先给用户看过再执行) |
|
||
| `--reset-password` | 仅重置已存在 agent 的账号密码(库/账号已存在时用) |
|
||
```
|
||
|
||
**验收(Task B)**
|
||
```bash
|
||
# 1) frontmatter 合法(name + description 齐备,否则会被跳过)
|
||
head -5 ~/.openclaw/skills/agent-provisioning/SKILL.md
|
||
# 2) 重启后不再出现 Skipping invalid skill,且该 skill 被加载
|
||
export NVM_DIR=$HOME/.nvm; . $NVM_DIR/nvm.sh; nvm use 26 >/dev/null
|
||
openclaw gateway restart && sleep 20
|
||
journalctl --user -u openclaw-gateway --since "2 min ago" | grep -iE 'agent-provisioning|Skipping invalid'
|
||
```
|
||
|
||
---
|
||
|
||
## Task C:每日备份脚本改造(动态发现)
|
||
|
||
**Files**
|
||
- Modify `/usr/local/bin/backup-agent-dbs.sh`(需 `sudo`)
|
||
|
||
**Interfaces**
|
||
- Consumes:MySQL 中由 Task A 创建的 `agent_<id>` 账号与 `<id>` 库
|
||
- Produces:所有 agent 库每日备份,无需人工登记
|
||
|
||
**Steps**
|
||
|
||
- [ ] **1. 先备份原脚本**
|
||
|
||
```bash
|
||
sudo cp /usr/local/bin/backup-agent-dbs.sh /usr/local/bin/backup-agent-dbs.sh.bak-20260916
|
||
```
|
||
|
||
- [ ] **2. 把硬编码 `DB_MAP` 换成动态发现**
|
||
|
||
删除原 `declare -A DB_MAP=(...)`,替换为:
|
||
|
||
```bash
|
||
# ===== 动态发现:agents.entries 的 id ∩ MySQL 实际库(排除系统库)=====
|
||
SYSTEM_DBS="mysql|information_schema|performance_schema|sys"
|
||
AGENT_IDS="$(python3 -c "
|
||
import json
|
||
d=json.load(open('/home/yangxuan/.openclaw/openclaw.json'))
|
||
print('\n'.join(sorted((d.get('agents') or {}).get('entries',{}).keys())))
|
||
")"
|
||
DB_LIST="$(mysql_root "select schema_name from information_schema.schemata where schema_name not regexp '^(${SYSTEM_DBS})\$';")"
|
||
|
||
# 交集:只备份「配置里存在的 agent」且「库真实存在」的
|
||
TARGETS=""
|
||
while IFS= read -r db; do
|
||
[ -n "$db" ] || continue
|
||
echo "$AGENT_IDS" | grep -qx "$db" && TARGETS="$TARGETS $db"
|
||
done <<< "$DB_LIST"
|
||
TARGETS="$(echo $TARGETS)"
|
||
echo "[${TIMESTAMP}] 备份目标:${TARGETS}" >> "$LOG_FILE"
|
||
```
|
||
|
||
- [ ] **3. 目录映射改为按 id 推导**
|
||
|
||
```bash
|
||
ws_dir_for() { # $1 = agent id → workspace 目录名
|
||
[ "$1" = "main" ] && echo "workspace" || echo "workspace-$1"
|
||
}
|
||
```
|
||
|
||
并把原循环改为遍历 `$TARGETS`,备份目录用 `"$BACKUP_ROOT/$(ws_dir_for "$DB")/db"`。
|
||
|
||
- [ ] **4. 不再硬编码密码**
|
||
|
||
```bash
|
||
# 从 .env 读(脚本内不再出现明文)
|
||
ENV_FILE="/home/yangxuan/.openclaw/.env"
|
||
MYSQL_PASS="$(grep -E '^MYSQL_PWD_ROOT=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"'"'"'')"
|
||
PG_PASS="$(grep -E '^PGPASSWORD=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"'"'"'')"
|
||
```
|
||
|
||
- [ ] **5. 30 天清理与 git 提交段改为遍历动态目录**
|
||
|
||
原清理循环 `for WS in workspace workspace-resume ...` 改为遍历 `$TARGETS` 推导出的目录。
|
||
原 `git add "workspace-${WS}/db/"` 同理。
|
||
|
||
**验收(Task C)**
|
||
|
||
> ⚠️ **实测教训(2026-09-16,已造成真实故障)**
|
||
> **绝不要用 `sudo` 跑本脚本**:生产路径是 **yangxuan 的 crontab**(root crontab 为空)。
|
||
> 用 `sudo` 跑会生成 **root 属主**的备份目录/文件,导致**次日 3:00 的 yangxuan cron 写不进去**,
|
||
> 备份从此天天失败。实测证据:`workspace-{juaner,wellness,tab}/db` 曾是 root 属主,
|
||
> 这 3 个库自 **2026-08-26** 起每天 `❌ 备份失败`(日志 `workspace/db/backup.log`)。
|
||
|
||
```bash
|
||
# 0) 前置:修复 root 属主的 db 目录(否则这 3 个库必然继续失败)
|
||
for d in /home/yangxuan/.openclaw/workspace*/db; do
|
||
[ -d "$d" ] && [ "$(stat -c %U "$d")" = "root" ] && sudo -n chown -R yangxuan:yangxuan "$d"
|
||
done
|
||
stat -c '%U:%G %n' /home/yangxuan/.openclaw/workspace*/db # 应全部为 yangxuan:yangxuan
|
||
|
||
# 1) 语法
|
||
bash -n /usr/local/bin/backup-agent-dbs.sh
|
||
# 2) 手动跑一次(以 yangxuan 身份,**不加 sudo**)
|
||
/usr/local/bin/backup-agent-dbs.sh; tail -20 ~/.openclaw/workspace/db/backup.log
|
||
# → 期望日志出现「备份目标:…」且成功的库数 = 库真实存在且已在 agents.entries 中的 agent 数
|
||
# 3) 确认备份文件落在各 workspace 的 db/ 下且已 gz、属主为 yangxuan
|
||
ls -la ~/.openclaw/workspace-*/db/*_$(date +%Y%m%d).sql.gz 2>/dev/null | head
|
||
# 4) 确认脚本内无明文密码
|
||
grep -nE 'MYSQL_PASS="|PG_PASS="' /usr/local/bin/backup-agent-dbs.sh
|
||
```
|
||
|
||
> **另一处必须知道的差异**:`.env` 中**没有** `MYSQL_PWD_ROOT`(只有 `MYSQL_PWD`)。
|
||
> 正确写法是「优先 `MYSQL_PWD_ROOT`、回退 `MYSQL_PWD`」,这样 Task A/E 之后补上该键会自动生效。
|
||
> 另注意:`openclaw` 不在 `agents.entries`(当前只有 11 个 id),**它必须先进入该配置才会被动态发现**(Task E 处理)。
|
||
|
||
---
|
||
|
||
## Task D:main 红线 + 文档索引
|
||
|
||
**Files**
|
||
- Modify `~/.openclaw/workspace/AGENTS.md`(在既有「## 红线」章节内追加)
|
||
- Modify `~/.openclaw/docs/README.md`(登记实现计划)
|
||
|
||
**Steps**
|
||
|
||
- [ ] **1. 在 `workspace/AGENTS.md` 的「## 红线」章节追加两条**
|
||
|
||
```markdown
|
||
- **创建 agent 红线**:新建 agent 必须且只能执行 `bash ~/.openclaw/scripts/new-agent.sh <id> <中文名>`。
|
||
禁止自行拼 SQL、禁止用 `mysql`/`docker exec` 建库、禁止为建库读取任何 root 凭据、禁止手工创建 `workspace-*` 或手改 agents 配置。
|
||
- **数据库红线**:每个 agent 只能访问自己的库(库名 = 自己的 agent id),账号为 `agent_<id>`;禁止使用 root、禁止跨库写入。
|
||
```
|
||
|
||
- [ ] **2. `docs/README.md` 文档表追加一行**
|
||
|
||
```markdown
|
||
| 4 | `agent-创建规范-实现计划.md` | 上述设计的实现计划:任务切分(A 脚本+模板 / B skill / C 备份改造 / D 红线 / E 存量)、关键代码与验收标准 |
|
||
```
|
||
|
||
**验收(Task D)**
|
||
```bash
|
||
grep -n '创建 agent 红线' ~/.openclaw/workspace/AGENTS.md
|
||
grep -c 'agent-创建规范-实现计划' ~/.openclaw/docs/README.md # 期望 1
|
||
```
|
||
|
||
---
|
||
|
||
## Task E:存量改造(12 个 agent,依赖 A)
|
||
|
||
**Files**
|
||
- Modify `~/.openclaw/.env`(补变量)
|
||
- Modify 各 `~/.openclaw/workspace-<id>/AGENTS.md`(补/改数据库段)
|
||
|
||
**Steps**
|
||
|
||
- [ ] **1. 已缺库的 4 个:按 A 的流程补建**
|
||
`note`(笔记)、`pbs`(PBS备份) 直接走 `new-agent.sh` 的**建库建号部分**(因 agent 已存在,需用 `--reset-password` 之外的分支 → 实际用 `--adopt-existing` 或分步执行:建库建号授权 + 手工补文档)。
|
||
`sql`(SQL):保留远程 VPS 业务库定位,另建私有库。
|
||
`openclaw`:先确认纳入 `agents.entries` 与 `agentToAgent.allow` 的影响,再建库建号。
|
||
|
||
- [ ] **2. 已有库的 8 个:建专用账号并改文档**
|
||
`main` `juaner` `wellness` `tab` `finances` `fitness` `resume` `travel`
|
||
- 建 `agent_<id>` 账号 + `GRANT ALL ON \`<id>\`.*` + 写 `DB_PASSWORD_<中文名>`
|
||
- **改各自 `AGENTS.md`**:删掉"用 root 连接"的表述,替换为 §Task A 的数据库段
|
||
|
||
- [ ] **3. 每改一个 agent 立即验证**
|
||
```bash
|
||
docker exec -e MYSQL_PWD="<新密码>" mysql mysql -uagent_<id> -N -B -e "SELECT 1;" <id>
|
||
```
|
||
|
||
**验收(Task E)**
|
||
```bash
|
||
# 全部 12 个 agent 都有库与账号
|
||
docker exec -e MYSQL_PWD="$MYSQL_PWD_ROOT" mysql mysql -uroot -e \
|
||
"select user from mysql.user where user like 'agent\_%';"
|
||
# 各 workspace 的 AGENTS.md 不再出现 root 直连表述
|
||
grep -rn 'u root' ~/.openclaw/workspace*/AGENTS.md
|
||
```
|
||
|
||
---
|
||
|
||
## Self-Review(计划自查)
|
||
|
||
| 检查 | 结果 |
|
||
|---|---|
|
||
| Spec 覆盖 | §2 命名→A 的校验;§3 组件→A/B;§4 九步→A 的 ①–⑨;§5 模板→A Step1;§6 幂等安全→A Step3/5;§7 验证→A 自检;**§8 存量→E**;§10 备份→C;**§11 六条→B(2) + D(1) + A(日志/提示)** ✅ |
|
||
| 占位符扫描 | 无 TBD/TODO;所有代码块均给出可执行内容 |
|
||
| 类型/命名一致 | `agent_<id>`、`DB_PASSWORD_<中文名>`、`$PASSWORD_VAR`、`ws_dir_for()` 在 A/C/E 中一致 ✅ |
|
||
| 已知待现场确认 | ① `openclaw agents add` 生成的文件清单(决定模板是覆盖还是补充)② `set-identity --from-identity` 对 IDENTITY.md 的解析要求 —— 实现时先验证,若不符则用 `--name` 兜底(A Step6 已写兜底分支) |
|