Files
draft-closing-report-default/development/DATA_PATHS.md
chenfuqiang 7b24521b43
All checks were successful
技能自动化发布 / release (push) Successful in 6s
feat: 结题报告初稿助手首版 v1.0.1(draft-closing-report-default)
2026-09-13 16:04:01 +08:00

194 lines
7.8 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.
# 用户数据目录与文件路径黄金标准
> 本文是 skill **读写本地文件** 的权威约定。凡涉及输入、输出、缓存、RPA 存证等路径,复制模板后须遵守本文;Agent 实现业务 skill 时也应先读本文。
---
## 1. 根目录
所有技能持久化数据(配置、数据库、业务文件)默认落在:
```text
{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill_slug}/
```
示例:
```text
D:\jiangchang-data\12500\download-video-baidu-haokan\
```
实现:`util.runtime_paths.get_skill_data_dir()`。
**禁止**把技能自己写入的文件落到:
- Agent / 宿主 **workspace**(进程 CWD)
- 技能安装目录(含 `.openclaw/skills/...`)
- 未在本文声明的任意相对 CWD 路径
---
## 2. 标准子目录树
用户可见 · **文件目录**(数据管理「输入 / 输出」文件浏览器只挂这两个):
| 子目录 | 方向 | 用途 | 默认 helper |
|--------|------|------|-------------|
| `input/` | 上行 | 用户/Agent 放入的待处理材料(urls.txt、batch.json、CSV、产品表…) | `get_input_dir()` |
| `output/` | 下行 | 技能/宿主交给用户拿走的结果(视频、回单、xlsx/csv/json…) | `get_output_dir()` |
用户可见 · **HTML 报告页**(宿主扫描挂到技能下,**不是** input/output 文件浏览器):
| 子目录 | 用途 | 默认 helper |
|--------|------|-------------|
| `html/` | 技能写出的静态 `.html` 报告;宿主一层扫描 `*.html`,侧栏预览 / 右键重新生成 | `get_html_dir()` |
内部 / 可选目录(**不上侧栏**):
| 子目录 | 用途 | 默认 helper |
|--------|------|-------------|
| `uploads/` | 待发布到外部平台的 staging(可选) | `get_uploads_dir()` |
| `cache/` | 可再生缓存(页面快照、解析中间结果) | `get_cache_dir()` |
| `temp/` | 单次任务 scratch,任务结束可删 | `get_temp_dir()` |
| `rpa-artifacts/` | RPA 失败截图、录屏中间产物 | `get_rpa_artifacts_dir(batch_id)` |
| `videos/` | RPA 录屏成片 MP4 | `get_videos_dir()` |
| `{skill_slug}.db` | SQLite | `get_db_path()` |
| `.env` | 用户配置 | `config.get_env_file_path()` |
复制后若业务不需要某目录,可不创建;**但一旦写入该类文件,必须走对应 helper**。
**开发者硬规则:**
- 用户/Agent **放进来**的文件 → `input/`(`get_input_dir()`)
- 技能/宿主 **交给用户拿走**的文件 → `output/`(`get_output_dir()`)
- 可浏览的 **HTML 报告页** → `html/`(`get_html_dir()` / `service.html_page_service`);**不要**把整页报告塞进 `output/` 指望侧栏预览
- 禁止再引入业务「文件浏览器」第三目录(如旧的 `downloads` / `imports` / `exports` 三分法);`html/` 是报告页扫描面,不是第三种文件浏览器
- 缓存、临时、RPA 录屏用 `cache` / `temp` / `videos`,不要当成用户可见的「输出目录」
宿主约定(与本模板对齐):
- 工具栏「导入」**UI 一律**打开宿主「导入数据」面板(默认从本技能 `input/` 选文件);「导出」默认写到 `output/`
- **Action 表单**里 `display_type`/`format` 为 `file` 或 `directory` 时:宿主原生对话框默认打开**当前技能** `{skill_data}/input`;若字段声明可选 **`pathRoot`**(相对技能数据根,如 `input/库存数据`),则打开该子目录。禁止在技能里写用户 ID 或绝对盘符。契约见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「本地路径选择与 pathRoot」
- 若表声明了 `replaces: import`,确认导入后由技能 CLI 写库(见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「`replaces`」);仅有 `replaces: create` 时宿主可按行调 create(兼容)
- 数据管理扫描 `{skill_data}/html/*.html`(一层、非递归);`<title>` → 侧栏显示名;「重新生成」调用 Action id **`render-html`**(见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「HTML 页面报告」)
---
## 3. 两类路径
### A. 技能拥有(skill-owned)
技能**创建或更新**的文件:下载结果、导出报表、失败截图、队列 sidecar 等。
- **默认**必须在 `{skill_data_dir}/<子目录>/` 下。
- 用户可拿走的业务产出 → **`output/`**。
- **必须**通过 `resolve_data_path()` 或 `get_*_dir()` 解析,**禁止** `os.path.abspath(config.get(...))` 相对 CWD。
### B. 用户显式输入(user-owned)
用户在 CLI / Agent 对话中给出的**已有文件**绝对路径,例如「发布这个视频 `D:\素材\x.mp4`」。
- 技能**只读**,路径可以是用户磁盘任意位置。
- 使用 `resolve_input_path()`:绝对路径原样;**相对路径**解析为 `{skill_data_dir}/input/<相对路径>`,**不**相对 workspace/CWD。
---
## 4. 配置项(`.env`)
可选覆盖项(**留空则用默认子目录**):
```ini
# ── 数据目录子路径(见 development/DATA_PATHS.md)──
# 相对路径相对于 {JIANGCHANG_DATA_ROOT}/{USER_ID}/{slug}/,不是 workspace
# SKILL_INPUT_DIR=input
# SKILL_OUTPUT_DIR=output/videos
# SKILL_UPLOAD_DIR=uploads
# SKILL_CACHE_DIR=cache
# SKILL_TEMP_DIR=temp
```
| 规则 | 说明 |
|------|------|
| 未配置 | `{skill_data_dir}/{default_subdir}` |
| 相对值 `output/videos` | `{skill_data_dir}/output/videos` |
| 绝对值 `D:\nas\output` | 高级用户/NAS 挂载,原样使用 |
| **禁止** `./outputs/...` | 会随 CWD 漂移,不得作为 `.env.example` 活跃配置行 |
业务专属路径 env key 须带 **`SKILL_` 前缀**(或技能 slug 前缀),避免污染全局命名空间。
---
## 5. 代码用法
```python
from util.runtime_paths import (
get_input_dir,
get_output_dir,
resolve_data_path,
resolve_input_path,
list_resolved_data_paths,
)
# 下行产出(A 类)——视频、报表等
dest = os.path.join(get_output_dir(), f"{vid}.mp4")
# 或带 env 覆盖
out_dir = resolve_data_path("SKILL_OUTPUT_DIR", "output")
# CLI -i urls.txt(B 类,相对路径 → input/)
manifest = resolve_input_path(args.input_path)
# health 诊断
paths = list_resolved_data_paths()
```
**反模式(禁止):**
```python
# ❌ 相对 CWD,Agent 从 workspace 执行时会写错盘
os.path.abspath(config.get("DOWNLOAD_OUTPUT_DIR"))
# ❌ .env.example 里写 ./outputs/videos
DOWNLOAD_OUTPUT_DIR=./outputs/videos
# ❌ 再拆 downloads / imports / exports 三个用户目录
```
---
## 6. health / config-path
`health` 或 `config-path` 建议输出 `list_resolved_data_paths()` 中的路径,便于排查「文件写到哪里去了」。展示路径须与 `run` 实际使用的一致。
---
## 7. 与 RPA 存证的关系
- 失败截图、录屏中间文件:`rpa-artifacts/{batch_id}/`(见 [`RPA.md`](RPA.md) §5.1)
- 录屏成片:`videos/`(见 [`RPA.md`](RPA.md) §5.3)
业务 `output/` 与 RPA `videos/` **分工不同**:前者是业务产出文件,后者是操作过程录屏。
---
## 8. 测试
- 单元测试须用 `IsolatedDataRoot`,断言路径落在临时数据根下。
- `tests/test_runtime_paths.py` 覆盖 `resolve_data_path` / `resolve_input_path`。
- `POLICY-DATA-PATH-001`:禁止 `.env.example` 中 `./` 路径;禁止业务代码 `abspath(config.get(...))`。
### 卸载技能与数据保留
宿主**卸载技能只删除安装目录**(如 `.openclaw/skills/{slug}`),**不会**删除 `{JIANGCHANG_DATA_ROOT}/{user_id}/{skill_slug}/` 下的 SQLite、输入输出与日志。重装会复用同一用户库;schema 迁移须在 `init_db()` 中幂等完成(见 [`../references/SCHEMA.md`](../references/SCHEMA.md)「表增删改与退役」)。
---
## 9. 相关文档
| 文档 | 内容 |
|------|------|
| [`RUNTIME.md`](RUNTIME.md) | 数据根、共享 runtime、编码 |
| [`CONFIG.md`](CONFIG.md) | `.env` bootstrap 与三层优先级 |
| [`RPA.md`](RPA.md) | rpa-artifacts / videos 存证 |
| [`POLICY_MATRIX.md`](POLICY_MATRIX.md) | POLICY-DATA-PATH-001 |