# 创作知识系统｜正式版施工文档

> 文档性质：施工文档。
> 目标：把当前本地 Demo 项目改造成“云端数据库 + OSS/CDN + acquisition -> decode_content -> pipeline -> app”的正式管道。
> 更新时间：2026-06-30。
> 本轮边界：本文记录正式版施工步骤；已完成的步骤会附执行记录，未完成的步骤仍按计划表述。
> 默认方案：在阿里云海外 lsh 开发机上新建 PostgreSQL 正式库；现有 Open AIGC PG/Greenplum 只作为上游分类树或历史连接，不作为本系统状态库。

## 0. 施工总原则

这次不是给旧 demo 打补丁，而是把主干改成正式工程。

核心原则：

- `main` 面向正式云端架构；旧本地数据和旧 demo 只由 `local-demo` 或归档路径负责。
- 本地 SQLite、`data/`、`legacy_data/` 不再是正式状态源。
- `creation_knowledge/` 不再作为正式顶层业务目录；其中可复用能力要迁到 `app/`、`decode_content/`、`acquisition/`。
- `创作知识提取-skill/` 是正式 skill contract，必须保留。
- 媒体正式链路统一以 OSS/CDN URL 为准，前端不再依赖本地 `/data/media/...`。
- 数据库先按可理解的业务对象建模，不在第一轮堆非常细的字段。

施工顺序不能反过来：先归档、再建云端状态源、再建本地模型和仓储、再迁 acquisition、decode、pipeline、app。

每个大步骤完成后，都要过一次“清理闸门”：

1. 查正式代码是否还引用旧入口。
2. 查测试是否还依赖旧路径。
3. 能删的从 `main` 移除。
4. 还需要回看的移到 `archive/` 或留在 `local-demo`。
5. 还没完全替代的，只能降级为 legacy shim，并在下一步清理清单里继续追踪。

## 1. 先清理与归档

### 1.1 先保留什么

这些目录和文件是正式版要继续用的资产：

| 保留对象 | 原因 |
|---|---|
| `core/` | 配置、LLM、Embedding、Prompt、PG 基建等共享底座 |
| `acquisition/` 主体 | 平台搜索、详情抓取、OSS、粗分类已经有真实代码 |
| `prompts/` | 创作闸、分类、读懂、scope normalize 等提示词仍被代码使用 |
| `tests/fixtures/` | crawler/search 离线测试仍依赖 |
| `创作知识提取-skill/` | 正式解构合同，不等于旧 `creation_knowledge/` 包名 |
| `app/frontend/src/` | 当前 Vite 前端已经迁为正式工作台起点 |

`acquisition/store.py` 已在第 6 步归档到 `archive/2026-06-30-step6/legacy_app/acquisition/store.py`；正式代码不能继续依赖它。

### 1.2 第一轮归档或删除什么

第一轮只动纯 legacy，不要急着拆核心业务代码。

建议从 `main` 移除或迁到归档位置：

- `web/index.html`
- `web/frameworks.json`
- `web/payloads.json`
- `web/runs/*`
- `scripts/serve_old_web.py`
- `scripts/rebuild_payloads.py`
- `scripts/import_to_db.py`
- `scripts/run_search.py`
- `.claude/launch.json`
- 原始抓包证据已归档到 `archive/2026-06-30-step1/data_interface_captures/`

本轮执行路径：已先迁到 `archive/2026-06-30-step1/`，不是直接物理删除；真实 `.env` 从 git tracking 移除，本地文件保留，提交 `.env.example` 作为模板。

处理方式：

- `web/*` 是旧单帖静态 demo 产物，正式前端不再使用。
- `scripts/serve_old_web.py` 只服务旧静态 demo，正式 API 不再需要。
- `scripts/rebuild_payloads.py` 与 `scripts/decompose.py` 里 payload 组装重复，后续统一到 `decode_content/payloads.py`。
- `scripts/import_to_db.py` 是本地 SQLite 迁移工具，正式版不再需要。
- `scripts/run_search.py` 是旧搜索和本地媒体链路，正式链路由 `run_acquisition.py` 替代。
- 原始抓包证据已归档到 `archive/2026-06-30-step1/data_interface_captures/`，不再放主干活跃区。

### 1.2.1 删除、归档、保留清单

这一节专门回答“到底删哪些文件和文件夹”。原则是：能确定只是旧 demo 产物的，先归档后从主干移除；还可能被正式链路复用的，先迁移能力，再删旧入口；真实数据、密钥和云端状态不跟代码一起删。

| 类型 | 对象 | 处理方式 | 说明 |
|---|---|---|---|
| 立即归档后从主干移除 | `web/index.html` | 移到 `archive/2026-06-30-step1/legacy_web/` | 根目录旧单帖静态 demo，不是正式前端 |
| 立即归档后从主干移除 | `web/frameworks.json` | 移到 `archive/2026-06-30-step1/legacy_web/` | 旧 demo 的本地 payload 展示数据 |
| 立即归档后从主干移除 | `web/payloads.json` | 移到 `archive/2026-06-30-step1/legacy_web/` | 旧 demo 的本地 payload 展示数据 |
| 立即归档后从主干移除 | `web/runs/*` | 移到 `archive/2026-06-30-step1/legacy_web/runs/` | 旧 demo run 结果 |
| 立即归档后从主干移除 | `scripts/serve_old_web.py` | 移到 `archive/2026-06-30-step1/legacy_scripts/` | 只服务旧静态 demo |
| 立即归档后从主干移除 | `scripts/rebuild_payloads.py` | 移到 `archive/2026-06-30-step1/legacy_scripts/` | 后续统一由 `decode_content/payloads.py` 负责 payload 组装 |
| 立即归档后从主干移除 | `scripts/import_to_db.py` | 移到 `archive/2026-06-30-step1/legacy_scripts/` | 本地 SQLite 导入工具，正式链路不用 |
| 立即归档后从主干移除 | `scripts/run_search.py` | 移到 `archive/2026-06-30-step1/legacy_scripts/` | 旧搜索入口，正式入口改为 `scripts/run_acquisition.py` |
| 立即归档后从主干移除 | `tests/test_legacy_paths.py` | 移到 `archive/2026-06-30-step1/legacy_tests/` | 测的是旧 demo 路径，不再代表正式验收 |
| 立即归档后从主干移除 | `.claude/launch.json` | 移到 `archive/2026-06-30-step1/local_agent/` | 个人本地 agent 配置，不进入正式工程 |
| 已归档后从主干移除 | 原始抓包证据 | 已归档到 `archive/2026-06-30-step1/data_interface_captures/` | 原始抓包证据保留为归档，不放主干活跃区 |
| 从 git 移除但本地保留 | `.env` | `git rm --cached .env`，新增 `.env.example` | 真实密钥不进入仓库 |
| Step6 已归档 | `acquisition/store.py` | 移到 `archive/2026-06-30-step6/legacy_sqlite/` | SQLite adapter 只供 local-demo/历史参考，正式代码不再依赖 |
| Step6 已归档 | `scripts/run_creation_search.py` | 移到 `archive/2026-06-30-step6/legacy_scripts/` | 旧 SQLite 采集 runner，正式入口改为 `scripts/run_acquisition.py` |
| Step6 已归档 | `acquisition/creation_search.py` | 移到 `archive/2026-06-30-step6/legacy_acquisition/` | 平台处理经验已迁到 adapters/media/classification/runner |
| Step5 已降级为 legacy/debug | `scripts/decompose.py` | 不删 | 正式能力已迁到 `decode_content/`，旧脚本只作调试入口 |
| Step6 已归档 | `creation_knowledge/` | 移到 `archive/2026-06-30-step6/legacy_creation_knowledge/` | API、读懂、媒体能力已迁到 `app/`、`decode_content/`、`acquisition/media/` |
| 保留 | `创作知识提取-skill/` | 永久保留 | 正式知识拆解合同层 |
| 保留 | `prompts/` | 保留 | 正式分类、闸门、读懂、scope prompt 仍使用 |
| 保留 | `tests/fixtures/` | 保留 | 离线测试证据 |
| Step6 已迁移 | `acquisition/web/app/` | 源码迁到 `app/frontend/`，旧 dist 归档 | 当前 Vite React 看板已成为正式前端起点 |

第二轮删除条件：

- `scripts/run_creation_search.py`：已在 Step6 归档。
- `acquisition/creation_search.py`：已在 Step6 归档。
- `acquisition/store.py`：已在 Step6 归档。
- `scripts/decompose.py`：已在 Step5 降级为 legacy/debug；正式 pipeline 不调用它。
- `creation_knowledge/`：已在 Step6 整体归档。

### 1.3 同步改哪些引用

删除或归档前必须同步处理引用：

- `scripts/decompose.py`
  - 旧 `web/frameworks*.json`、`web/payloads*.json` 写盘逻辑改成 legacy adapter。
  - 正式 decode 不再直接写 `web/`。
- `tests/test_legacy_paths.py`
  - 删除，或改成归档测试。
- `开发文档/产品文档.md`、`开发文档/技术文档.md`
  - 所有 `web/index.html`、SQLite、本地 JSON、`creation_knowledge` 作为正式入口的说法改成历史说明。
- `.env`
  - 从 git 移除。
  - 新增 `.env.example`，只保留键名和说明，不写真实密钥。
  - 真实 `.env` 只放本地或云机环境。

### 1.4 本步骤验收

- `git status` 只显示预期的删除、归档和新增文档。
- 没有正式代码继续引用 `web/frameworks.json`、`web/payloads.json`。
- 没有测试继续 import `scripts.serve_old_web`。
- `.env` 不再作为 tracked secret 进入后续提交。

## 2. 在云服务器新建正式数据库

### 2.1 云机事实

本地云机配置目录：

```text
/Users/samlee/Documents/工作环境配置/阿里云海外lsh开发机
```

只读检查确认：

- 目录内有 `account.md` 和 `ali-denet.pem`。
- 云机可访问。
- 云端已有 repo：`/home/sam/Create-knowledge-find-decode`。
- 云端 repo 与本地 `main` 不完全同步，施工前必须以本地新分支或新提交为准重新部署。

安全要求：

- 不把云机 IP、账号、私钥、数据库密码写入仓库。
- 不把真实 `.env` 提交。
- 新库密码只放云机环境变量或安全密钥管理位置。

### 2.2 安装与初始化 PostgreSQL

施工动作：

1. 在云机安装 PostgreSQL server 和 client。
2. 新建数据库，例如：
   - `creation_knowledge_prod`
3. 新建应用用户，例如：
   - `ck_app`
4. 新建 schema，例如：
   - `creation_knowledge`
5. 只给应用用户访问该 schema 的必要权限。
6. 配置备份策略。
7. 配置远程访问策略：本地开发可以访问云端库，但不能开放无边界公网访问。

注意：

- 现有 `.env` 里的 `OPEN_AIGC_PG_*` 是上游 Open AIGC PG/Greenplum 配置，不要混用成本系统正式库。
- 正式库新增 `CK_DB_*` 配置。

建议新增环境变量：

```text
CK_DB_HOST=
CK_DB_PORT=5432
CK_DB_NAME=creation_knowledge_prod
CK_DB_USER=ck_app
CK_DB_PASSWORD=
CK_DB_SCHEMA=creation_knowledge
```

### 2.3 新增迁移文件

新增：

```text
db/migrations/001_creation_knowledge_schema.sql
```

第一版表不追求字段极细，先覆盖业务闭环。

建议业务对象：

| 对象 | 业务意义 |
|---|---|
| `query_batches` | 一批 query 的来源、目标、生成方式、状态 |
| `queries` | 单条 query 及其业务轴、是否保留、筛选原因 |
| `acquisition_runs` | 一次采集批次 |
| `acquisition_jobs` | 单条 query 在单个平台上的任务 |
| `candidate_items` | 候选帖子、文章、视频 |
| `media_assets` | 图片、封面、视频、原始 URL、OSS/CDN URL |
| `item_classifications` | 粗分类结果：是否创作知识、原因、模型版本 |
| `decode_jobs` | 单帖解构任务 |
| `decode_results` | 读懂文本、创作闸结果、拆颗摘要 |
| `knowledge_particles` | What/How/Why 知识颗 |
| `scope_results` | 作用域定位：实质、形式、感受、作用、意图 |
| `payload_drafts` | 待审核或待入库 payload |
| `ingest_records` | 入正式知识库记录 |
| `contract_snapshots` | skill、prompt、schema 版本快照 |

字段原则：

- 每张表都有业务 id、状态、来源、错误、创建时间、更新时间。
- 大型证据、原始平台回包、模型返回用 JSONB 保存。
- 媒体不要塞进 item 的字符串数组，拆到 `media_assets`。
- query 文本可能重复，正式 API 应优先用 query id。

### 2.4 本步骤验收

- 空库执行 migration 成功。
- migration 可重复执行或有明确版本记录。
- 本地通过 `CK_DB_*` 能连到云端正式库。
- 能写入一个 query batch、一个 query、一个 acquisition run。
- 不依赖 `CK_SQLITE_PATH`。

### 2.4.1 本步骤清理闸门

第二步完成后，重点不是删业务代码，而是防止云端施工副产物进入仓库：

| 对象 | 处理方式 | 说明 |
|---|---|---|
| 云机真实 `.env`、数据库密码、连接串 | 不进仓库 | 只放本地或云机安全路径 |
| SSH 私钥、云机账号文件 | 不进仓库 | 继续留在本机配置目录 |
| 数据库备份文件 | 不进仓库 | 只留云机备份目录 |
| 临时 SQL、psql 输出、连接测试日志 | 删除或外部归档 | 不作为项目源代码 |
| `OPEN_AIGC_PG_*` 配置 | 保留但降级用途 | 只用于上游分类树/历史连接，不再当本系统状态库 |
| `CK_SQLITE_PATH` | 不新增正式引用 | 旧 demo 可以继续用，正式代码不能继续接入 |

本步骤不删除 `acquisition/store.py` 和旧 SQLite runner，因为正式 repository 还没有接管业务流。

### 2.5 2026-06-30 执行记录

本步骤已真实在阿里云海外 lsh 开发机执行，记录非敏感事实如下：

- 已安装 PostgreSQL server/client，版本为 PostgreSQL 18.4。
- 已新建正式库 `creation_knowledge_prod`。
- 已新建应用角色 `ck_app`。
- 已新建 schema `creation_knowledge`。
- 已执行 `db/migrations/001_creation_knowledge_schema.sql`。
- migration 已重放验证，可以重复执行。
- `ck_app` 默认 `search_path` 已设置为 `creation_knowledge, public`。
- 云机上真实 `CK_DB_*` 写入 `/home/sam/.config/create-knowledge/creation_knowledge_prod.env`，权限为用户私有；仓库不保存密码。
- 本地 `.env` 已写入 `CK_DB_*`，通过 SSH tunnel 连接云端数据库；`.env` 已被 `.gitignore` 忽略。
- 云端 PostgreSQL 只监听本机地址，本地开发通过 SSH tunnel 访问，不开放无边界公网 5432。
- 已配置每日备份：`/usr/local/sbin/backup_creation_knowledge_pg.sh`，cron 位于 `/etc/cron.d/creation_knowledge_pg_backup`，备份目录为 `/var/backups/creation_knowledge`，当前策略保留 14 天。
- 已做本地写入回滚验证：可以通过应用用户写入 `query_batches`、`queries`、`acquisition_runs` 并回滚。

## 3. 本地建立模型、Schema、Repository

当前依赖已经有 `pydantic`、`psycopg2-binary`、`fastapi`，没有 SQLAlchemy/Alembic。第一版采用：

```text
SQL migration + Pydantic model/schema + psycopg2 repository
```

### 3.1 新建目录

新增：

```text
db/migrations/
app/
app/routes/
app/frontend/
acquisition/repositories/
acquisition/queries/
acquisition/platforms/
acquisition/media/
acquisition/classification/
decode_content/
decode_content/readers/
pipeline/
```

### 3.2 新建核心文件

新增：

| 文件 | 负责什么 |
|---|---|
| `core/db_session.py` | 正式 PG 连接、事务、读写边界 |
| `acquisition/domain.py` | Query、Run、Job、Item、MediaAsset、Classification |
| `acquisition/repositories/base.py` | AcquisitionRepository 接口 |
| `acquisition/repositories/postgres.py` | 云端 PG 实现 |
| `decode_content/models.py` | ReadResult、Knowledge、Scope、PayloadDraft |
| `decode_content/repository.py` | decode 结果、知识颗、payload draft 写库 |
| `pipeline/models.py` | PipelineRun、PipelineJob、Resume 状态 |
| `pipeline/repository.py` | pipeline 状态读写 |
| `app/schemas.py` | 前端 API 返回结构 |

`core/db.py` 保留给上游分类树读取。
`core/db_session.py` 负责本系统正式状态库。两者不要混在一起。

### 3.3 Repository 约定

正式业务代码只依赖接口，不直接操作 psycopg2 cursor。

`AcquisitionRepository` 至少提供：

- 创建 query batch。
- 读取待跑 query。
- 创建 acquisition run。
- 创建或更新 job。
- 写 candidate item。
- 写 media asset。
- 写 item classification。
- 查询 summary/detail 给前端。

`DecodeRepository` 至少提供：

- 领取待 decode item。
- 写 read result。
- 写 gate result。
- 写 knowledge particles。
- 写 scope results。
- 写 payload draft。

`PipelineRepository` 至少提供：

- 创建 pipeline run。
- 标记 step 状态。
- 记录失败和重试。
- 查询断点续跑位置。

### 3.4 本步骤验收

- 单元测试可用 fake repository 跑 acquisition/decode，不需要真实 DB。
- 集成测试可用临时 PG schema 跑 migration。
- 正式代码没有新增 SQLite 依赖。

### 3.4.1 本步骤清理闸门

第三步完成后，要清理的是“重复状态源”和“错误引用”，不是急着删旧业务能力：

| 对象 | 处理方式 | 说明 |
|---|---|---|
| 新正式代码里对 `acquisition.store` 的引用 | 必须删除 | 正式模型、repository、schema 不允许依赖 SQLite |
| 新正式代码里对 `CK_SQLITE_PATH` 的引用 | 必须删除 | 正式状态源只能走 `CK_DB_*` |
| `core/db.py` | 保留 | 仍用于上游 Open AIGC/分类树连接 |
| `core/db_session.py` | 保留 | 本系统正式状态库连接入口 |
| `acquisition/store.py` | 暂时保留为 legacy | 等 Step4/Step6 替代旧 runner/API 后再归档 |
| 只为新目录占位的空文件 | 保留或删除都可以 | 只要不误导为已实现业务即可 |

检查命令建议：

```text
rg "acquisition.store|CK_SQLITE_PATH|data/app.db" core acquisition decode_content pipeline app scripts
```

如果命中发生在 legacy 文件里，可以保留；如果命中发生在正式新模块里，必须改掉。

### 3.5 2026-06-30 执行记录

本步骤已完成第一版本地骨架，记录如下：

- 已新增 `core/db_session.py`，作为正式 `CK_DB_*` 云端 PostgreSQL 连接入口。
- 已在 `core/config.py` 新增 `CreationDbConfig`，明确区别于仍用于上游 Open AIGC 的 `PgConfig`。
- 已新增 `acquisition/domain.py`，覆盖 QueryBatch、Query、AcquisitionRun、AcquisitionJob、CandidateItem、MediaAsset、ItemClassification。
- 已新增 `acquisition/repositories/base.py` 和 `acquisition/repositories/postgres.py`，提供正式 acquisition repository 接口与 PostgreSQL 实现。
- 已新增 `decode_content/models.py` 和 `decode_content/repository.py`，先定义读懂、创作闸、知识颗、作用域、payload draft、contract snapshot 的模型与仓储合同，不迁移执行逻辑。
- 已新增 `pipeline/models.py` 和 `pipeline/repository.py`，先定义 orchestration/resume 的状态对象与仓储合同；是否新建 pipeline 专用表留到后续 migration 决策。
- 已新增 `app/schemas.py`，作为正式工作台 API DTO 起点。
- 已新增正式目录占位：`app/routes/`、`app/frontend/`、`decode_content/readers/`、`acquisition/queries/`、`acquisition/platforms/`、`acquisition/media/`、`acquisition/classification/`。
- 没有让正式代码新增 SQLite 依赖，旧 `acquisition/store.py` 仍只作为 legacy/local-demo adapter。
- 已用云端正式库做 repository 回滚验证：创建 query batch、query、run、job、candidate item、media asset、classification，查询 summary/detail 后 rollback。
- 当前未迁移 `scripts/decompose.py` 的旧 `web/` 写盘，也未修 `dim_attributes` 合同漂移；这些按计划留到 `decode_content/contracts.py` 与 `payloads.py` 阶段。

## 4. 改造 Acquisition

### 4.1 Query 生成与筛选

移动：

- `scripts/build_creation_demo.py` 核心逻辑 -> `acquisition/queries/builder.py`
- `acquisition/query_filter.py` -> `acquisition/queries/filter.py`

变化：

- 不再默认写 `data/queries/creation_demo.json`。
- 生成结果写入云端 `query_batches` 和 `queries`。
- 记录 query 生成方式、筛选结果、筛选 prompt 版本。

保留：

- 当前正交组合思路。
- `keep/reason/valid/relevant` 这类业务判断。

### 4.2 平台搜索与详情抓取

拆分：

```text
acquisition/platforms/base.py
acquisition/platforms/xiaohongshu.py
acquisition/platforms/weixin.py
acquisition/platforms/douyin.py
```

从现有文件迁移：

- `acquisition/search.py`
- `acquisition/crawler.py`
- `acquisition/creation_search.py` 中 `_process_xhs/_process_weixin/_process_douyin`

统一接口：

- `search(query) -> Candidate[]`
- `fetch_detail(candidate) -> PostLike`
- `normalize(raw) -> CandidateItem`

不要强行把三个平台字段抹平到失真：

- 小红书和微信偏图文/文章。
- 抖音偏视频，需要 account/cookie batch 和视频 URL。
- 平台差异进入 adapter 内部，业务层只看统一候选对象。

### 4.3 媒体转 OSS/CDN

移动：

- `acquisition/oss.py` -> `acquisition/media/oss_client.py`

新增：

- `acquisition/media/service.py`

正式行为：

- 小红书图片转 OSS/CDN。
- 微信图片转 OSS/CDN。
- 抖音视频转 OSS/CDN。
- DB 只保存媒体资产记录和 CDN URL。
- 不再把正式媒体写到本地 `/data/media/...`。

注意：

- 旧本地落盘只作为 local-demo 或测试辅助。
- 图文粗分类要支持直接使用 HTTP(S) 图片 URL。

### 4.4 粗分类

移动：

- `acquisition/classify.py` -> `acquisition/classification/coarse.py`

改造：

- 图文支持 CDN 图片 URL，不再依赖本地 `/data/...` 转 base64。
- 视频继续支持 CDN URL。
- 粗分类结果写 `item_classifications`。
- 记录模型、prompt 版本、失败原因。

### 4.5 Runner 与 CLI

新增：

```text
acquisition/runner.py
scripts/run_acquisition.py
```

替代：

- `scripts/run_creation_search.py`
- `acquisition/creation_search.py` 中直接操作 SQLite 的部分

正式 runner 做这些事：

1. 从云端读取 query batch。
2. 创建 acquisition run。
3. 为每条 query 和每个平台创建 job。
4. 搜索、详情、媒体转存、粗分类。
5. 写 candidate item、media asset、classification。
6. 更新 job 状态：pending、running、done、partial、failed。
7. 支持 resume 和 skip done。

### 4.6 本步骤验收

- 可以用一个 query batch 跑三平台 mock。
- 不创建 `data/app.db`。
- 不读取 `/data/queries/creation_demo.json`。
- item 媒体全是 CDN/OSS URL 或明确失败状态。
- 粗分类写入云端 DB。

### 4.6.1 本步骤清理闸门

第四步完成后，Acquisition 相关旧入口要分三类处理：

| 对象 | 本步骤处理方式 | 后续删除条件 |
|---|---|---|
| `scripts/build_creation_demo.py` | 保留为正式薄入口 | 不删；它已不再默认写本地 JSON |
| `acquisition/query_filter.py` | 保留为 shim | 等旧脚本全部改 import 后，可移除 |
| `scripts/run_acquisition.py` | 保留 | 正式采集 CLI |
| `acquisition/runner.py` | 保留 | 正式采集 runner |
| `scripts/run_creation_search.py` | 暂时保留为 legacy | 正式 API/前端不再读 legacy summary，真实 query batch 跑通后归档 |
| `acquisition/creation_search.py` | 暂时保留为 legacy/迁移来源 | 平台 adapter、media service、coarse classifier 的真实平台测试覆盖后归档 |
| `acquisition/store.py` | 暂时保留为 legacy SQLite adapter | API、runner、前端全不依赖 SQLite 后归档 |
| `scripts/classify_creation_items.py` | 暂时保留为 legacy 补判脚本 | 正式 item classification 重判脚本补齐后归档 |
| `data/queries/creation_demo.json` | 如果由新流程生成，删除或外部归档 | 正式 query batch 写云端 DB，不再靠本地 JSON |
| `data/media/*` 新增正式媒体 | 不允许新增 | 正式媒体进入 OSS/CDN 和 `media_assets` |

检查命令建议：

```text
rg "creation_demo.json|data/app.db|CK_SQLITE_PATH|acquisition.store" acquisition scripts app tests
```

正式新模块不应命中这些旧状态源；legacy 文件命中可以保留，但要在下一步继续追踪。

### 4.7 2026-06-30 执行记录

本轮已完成第一版 Acquisition 正式链路拆分，没有删除 legacy SQLite runner：

- 已新增 `acquisition/queries/builder.py`，承接原 `scripts/build_creation_demo.py` 的正交 query 生成核心。
- 已新增 `acquisition/queries/filter.py`，`acquisition/query_filter.py` 退为兼容 shim。
- 已将 `scripts/build_creation_demo.py` 改成薄入口：默认只打印摘要；只有显式 `--export-json` 才导出本地 JSON，只有显式 `--persist` 才写正式 PG。
- 已新增三平台 adapter：`acquisition/platforms/xiaohongshu.py`、`weixin.py`、`douyin.py`。
- 已新增 `acquisition/media/service.py`，统一把图片/视频转成可入库的媒体记录；OSS 失败时保留原 URL，不丢候选 item。
- 已新增 `acquisition/classification/coarse.py`，正式粗分类支持 HTTP(S) 图片 URL；同时补了旧 `acquisition.classify.classify_imgtext`，避免 legacy 补判在 CDN 图片下丢视觉输入。
- 已新增 `acquisition/runner.py` 和 `scripts/run_acquisition.py`，正式入口从云端 query batch 读取 query，创建 run/job，写候选 item、media asset、classification，并支持 skip done。
- 明确保留 `scripts/run_creation_search.py`、`acquisition/creation_search.py`、`acquisition/store.py` 为 legacy/local-demo 路径，本轮不改其 SQLite 语义。

交叉验证结论：

- Query 生成/筛选、平台/媒体/粗分类、正式 runner/CLI 三条 SubAgent 只读检查均确认：新链路应走 repository，不再从默认本地 query JSON 或 SQLite 取正式状态。
- 新增测试覆盖 fake repository 跑 acquisition batch、skip done、媒体 service、HTTP 图片粗分类、query batch 持久化合同、CLI 薄委托。
- 本地验证：`.venv/bin/pytest -q` 通过，`96 passed`；`.venv/bin/python -m compileall acquisition scripts core app decode_content pipeline tests` 通过；`git diff --check` 通过。

### 4.8 2026-06-30 清理闸门执行记录

按 Step1-4 清理闸门做了第二轮实际清理：

- 已删除本地运行产物：`runtime/`、空的 `web/runs/`、Python `__pycache__/`。
- 已把 `acquisition/platforms/weixin.py` 中对 `acquisition.creation_search._hash` 的依赖改为 adapter 内部实现，避免正式平台 adapter import legacy runner。
- 已将默认测试里的旧 SQLite/query demo/creation search 测试迁到 `archive/2026-06-30-step4/legacy_tests/`：
  - `tests/test_store.py`
  - `tests/test_creation_search.py`
  - `tests/test_query.py`
- 已保留 `tests/test_decompose_helpers.py`，因为 Step5 decode_content 迁移尚未执行，暂时仍需要它保护旧 decode helper 行为。
- 已脱敏 `db/README.md` 的 SSH tunnel 示例，不保留本机私钥绝对路径。
- 已把旧《产品文档》《技术文档》进一步标注为历史 demo 说明，避免继续把本地 JSON / 旧 `web/index.html` 写成正式入口。

本轮 SubAgent 交叉验证结论：

- Step1 原始 legacy 文件已不在原路径。
- Step2 未发现真实 DB 密码、私钥、psql 日志进入仓库；`.env.example` 只保留模板。
- Step3/4 正式 runner、CLI、query builder、repository 不读 SQLite、不读 `creation_demo.json`、不写 `data/app.db`。
- 仍按计划暂留为 legacy/迁移来源的文件：`acquisition/store.py`、`acquisition/creation_search.py`、`scripts/run_creation_search.py`、`acquisition/web_api.py`、`acquisition/web/app/`。这些会在 Step5/6 后继续清理。

## 5. 改造 Decode Content

改造前，读懂、创作闸、拆颗、作用域和 payload 主要压在 `scripts/decompose.py` 与 `creation_knowledge/integrations/`。

### 5.1 新建合同层

新增：

```text
decode_content/contracts.py
```

职责：

- 加载 `创作知识提取-skill/extraction/phase1-frame.md`。
- 加载 `phase2-scope.md`。
- 加载 `phase3-assemble.md`。
- 加载 schema、taxonomy、prompt。
- 计算合同版本 hash。
- 写入 `contract_snapshots`。

原则：

- 保留 `创作知识提取-skill/`。
- 不把它散落复制到别的目录。
- 每次 decode 都能追溯当时使用的合同版本。

### 5.2 读懂层

新增：

```text
decode_content/readers/imgtext.py
decode_content/readers/video.py
decode_content/readers/video_frames.py
decode_content/readers/service.py
```

迁移：

- `creation_knowledge/integrations/extractor.py` -> `imgtext.py`
- `creation_knowledge/integrations/video_extract.py` -> `video.py`
- `creation_knowledge/integrations/video_frames.py` -> `video_frames.py`
- `scripts/decompose.py::read_one` -> `readers/service.py`

正式行为：

- 输入：candidate item + media assets。
- 输出：read text、cards、media refs、is_empty。
- 不负责写 web 文件。
- 不负责 payload。

### 5.3 创作闸与质量闸

新增：

```text
decode_content/gates.py
```

迁移：

- `creation_gate`
- how admit/refute/tiebreak
- why refute

正式行为：

- 创作闸只看读懂结果，不靠标题关键词硬判。
- How 闸防止假流程。
- Why 轻闸只过滤空泛废话，不随意丢失信息。

### 5.4 拆颗与作用域

新增：

```text
decode_content/framing.py
decode_content/scoping.py
```

`framing.py` 负责：

- 调 phase1。
- 产出 What/How/Why 知识颗。
- 假 How 重拆。
- 受控值守卫。
- 组件颗 parent 校验。

`scoping.py` 负责：

- 调 phase2。
- 候选 scope。
- 名词化。
- 连接词拆原子。
- 调 `ScopeLinker`。
- 决定复用或新建。

### 5.5 Payload 组装

新增：

```text
decode_content/payloads.py
```

目标：

- 成为唯一 payload assembler。
- 取代 `scripts/decompose.py` 与 `scripts/rebuild_payloads.py` 的重复逻辑。
- 一颗知识生成一个 payload draft。
- payload 写入云端 `payload_drafts`。

同时修正合同漂移：

- 统一 `dim_attributes` 口径。
- 统一 How step 的 `input/directive/output`。
- What/Why content 统一为正式 ingest 接口可接受的字符串形态。

### 5.6 Decode Service

新增：

```text
decode_content/service.py
scripts/run_decode_content.py
```

`decode_content/service.py` 提供：

```text
decode_item(item_id) -> DecodeResult
```

它串起：

```text
load candidate -> read -> creation gate -> frame -> scope -> payload draft -> write db
```

`scripts/decompose.py` 最终只保留为 legacy/调试入口。

### 5.7 本步骤验收

- 用 fixture item 可以跑完整 decode。
- 每次 decode 都写 contract version。
- payload 组装只有一处实现。
- 不再从正式链路写 `web/frameworks.json`。

### 5.7.1 本步骤清理闸门

第五步完成后，Decode 相关旧入口要开始真正降级：

| 对象 | 本步骤处理方式 | 后续删除条件 |
|---|---|---|
| `scripts/decompose.py` | 改成 legacy/调试入口，或迁入归档 | `decode_content/service.py` 能跑完整 fixture 后归档 |
| `scripts/rebuild_payloads.py` | 保持归档，不再恢复到主干 | `decode_content/payloads.py` 是唯一 payload assembler |
| `creation_knowledge/integrations/extractor.py` | 迁到 `decode_content/readers/imgtext.py` 后归档 | 新 reader 测试通过且无正式 import |
| `creation_knowledge/integrations/video_extract.py` | 迁到 `decode_content/readers/video.py` 后归档 | 新 video reader 测试通过且无正式 import |
| `creation_knowledge/integrations/video_frames.py` | 迁到 `decode_content/readers/video_frames.py` 后归档 | 抽帧测试通过且无正式 import |
| 旧 `web/frameworks*.json` 写盘逻辑 | 删除 | 正式 decode 只写云端 decode/payload 表 |
| 旧 payload 组装函数副本 | 删除 | payload 逻辑只能留在 `decode_content/payloads.py` |

检查命令建议：

```text
rg "web/frameworks|web/payloads|creation_knowledge.integrations|rebuild_payloads" scripts creation_knowledge decode_content tests
```

正式链路里不允许再写旧 `web/` JSON；如果旧脚本还需要保留，只能明确标为 legacy。

### 5.8 2026-06-30 执行记录

本轮已完成 Step5 第一轮正式迁移：

- 新增 `decode_content/contracts.py`，集中加载 `创作知识提取-skill` 的 README、phase、schema、taxonomy、gate/normalize prompts 和 `scope_trees` cache 元信息，生成合同 hash 与 `ContractSnapshot`。
- 新增 `decode_content/readers/`，图文、整段视频、抽帧兜底 reader 已迁入正式包；正式 reader 入口支持 `CandidateItem + MediaAsset[] -> ReadResult`，不再依赖旧 `src["from"]`、本地 `/data/demo` 或 web JSON。
- 新增 `decode_content/gates.py`、`framing.py`、`scoping.py`、`payloads.py`、`service.py`，把创作闸、How/Why 质量闸、What/How/Why 拆颗、作用域定位、payload 组装和单帖 decode 编排拆到正式模块。
- `decode_content/payloads.py` 已统一正式 `dim_attributes`：`how工序 / what构成 / why原理`；What/Why content 按正式 ingest 边界输出字符串。
- `DecodeService` 每次 decode 会写合同快照；空内容或创作闸未通过会短路，只保存 read/gate/decode 状态，不生成 particles/payload drafts。
- 新增 `core/media_download.py`，把原来散在旧视频 reader 里的下载字节能力上提为共享工具；正式 acquisition/decode 不再 import `creation_knowledge.integrations`。
- 新增 `scripts/run_decode_content.py` 作为薄 CLI；正式 DB item 加载留到 Step6 pipeline/API 继续接。
- `scripts/decompose.py` 已标为 `LEGACY/debug`，保留旧 demo 调试能力，但不再作为正式链路。

本步骤清理结果：

- 已归档 `tests/test_decompose_helpers.py` 到 `archive/2026-06-30-step5/legacy_tests/`。
- 默认测试已迁到正式模块：`tests/test_decode_contracts.py`、`tests/test_decode_readers_service.py`、`tests/test_decode_payloads.py`、`tests/test_decode_gates_framing_scoping.py`、`tests/test_decode_service.py`。
- `tests/test_extractor.py`、`tests/test_video_extract.py`、`tests/test_video_frames.py` 已改为从 `decode_content.readers.*` 导入，继续保护 reader 行为。

交叉验证结论：

- SubAgent 确认 reader 不应继承旧 `src["from"]`、本地 `/data/demo`、reader 内 OSS 转存或 web 写盘行为；本轮已按 `CandidateItem + MediaAsset[]` 建正式 reader 输入。
- SubAgent 确认 `scripts/decompose.py::process_one/_run/write_run/main` 不应进入正式 service；本轮仅迁移业务能力，旧脚本保留为 legacy/debug。
- SubAgent 确认合同层要记录 phase/schema/taxonomy/prompt/scope cache 与漂移；本轮已在 `contracts.py` 记录正式常量与漂移说明。

本地验证：

```text
.venv/bin/pytest -q tests/test_decode_contracts.py tests/test_decode_readers_service.py tests/test_decode_payloads.py tests/test_decode_gates_framing_scoping.py tests/test_decode_service.py tests/test_extractor.py tests/test_video_extract.py tests/test_video_frames.py
23 passed
.venv/bin/pytest -q
84 passed
rg "creation_knowledge\\.integrations|web/frameworks|web/payloads|web/runs" decode_content tests scripts app pipeline acquisition -n
只剩 scripts/decompose.py 这个 legacy/debug 入口命中
```

## 6. 补 Pipeline 与正式 API

### 6.1 Pipeline

新增：

```text
pipeline/acquisition_runner.py
pipeline/decode_runner.py
pipeline/creation_pipeline.py
pipeline/dedupe.py
```

`creation_pipeline.py` 负责完整编排：

```text
query batch -> acquisition -> coarse classify -> decode candidates -> payload drafts -> ingest
```

`dedupe.py` 负责：

- 候选内容优先按 `unique_key` 去重，兼容缺失时再退回平台原始 ID 或 URL。
- 跨 query 重复 URL 去重。
- 同 item 不重复 decode。
- 已 ingest payload 不重复写入。

Pipeline 必须记录：

- 当前 step。
- run/job 状态。
- 错误原因。
- 重试次数。
- 断点续跑位置。

### 6.2 正式 API

新增：

```text
app/api.py
app/routes/acquisition.py
app/routes/decode.py
app/routes/payloads.py
app/routes/runs.py
```

迁移：

- `creation_knowledge/api.py` 的 FastAPI 壳 -> `app/api.py`
- `acquisition/web_api.py` 的业务 API -> `app/routes/acquisition.py`

正式 API 替换：

| 旧接口 | 新接口 |
|---|---|
| `/api/creation-search/summary` | `/api/acquisition/runs/{run_id}/summary` |
| `/api/creation-search/query?query=...` | `/api/acquisition/runs/{run_id}/queries/{query_id}` |
| `/data/queries/creation_demo.json` | `/api/query-batches/{batch_id}` |
| `/data/media/...` | DB 中的 OSS/CDN URL |

API 原则：

- 不再默认挂本地 `/data`。
- 不再创建本地 media 目录。
- 不再每请求打开 SQLite。
- CORS 不再无脑 `*`，按部署环境配置。

### 6.3 前端

迁移：

```text
acquisition/web/app/src -> app/frontend/src
```

保留：

- query 列表页骨架。
- query 详情页骨架。
- 三平台候选展示。
- 创作知识/非创作知识分组思路。

改造：

- 去掉 `/data/queries/creation_demo.json`。
- 去掉 `/api/creation-search/*`。
- 改用正式 API。
- 增加 pipeline run 状态。
- 增加 decode 结果视图。
- 增加 payload draft / ingest 状态视图。

### 6.4 本步骤验收

- `uvicorn app.api:app` 能启动。
- 前端不再请求 `/data/queries/creation_demo.json`。
- 前端媒体全部来自 CDN/OSS URL。
- API 不依赖 SQLite。

### 6.4.1 本步骤清理闸门

第六步完成后，旧 API、旧前端和顶层旧包要进入最终清理：

| 对象 | 本步骤处理方式 | 后续删除条件 |
|---|---|---|
| `creation_knowledge/api.py` | 迁到 `app/api.py` 后归档 | `uvicorn app.api:app` 通过且前端改用正式 API |
| `acquisition/web_api.py` | 迁到 `app/routes/acquisition.py` 后归档 | 新 acquisition API summary/detail 测试通过 |
| `creation_knowledge/media.py` | 本步骤随旧顶层包归档 | 正式媒体只走 `acquisition/media/` 和 OSS/CDN |
| `creation_knowledge/` 顶层包 | 本步骤整体归档 | 无正式 import，API/reader/media 都已迁完 |
| `acquisition/store.py` | 本步骤归档 | 正式状态源改为 PostgreSQL repository |
| `acquisition/creation_search.py` | 本步骤归档 | 平台搜索、媒体、分类、runner 能力已拆到正式模块 |
| `scripts/run_creation_search.py` | 本步骤归档 | 正式采集入口改为 `scripts/run_acquisition.py` |
| `scripts/classify_creation_items.py` | 本步骤归档 | 后续补正式 repository-backed 重判脚本 |
| `scripts/filter_multiaxis.py` | 本步骤归档 | query 生成/筛选迁到 `acquisition/queries/*` |
| `acquisition/backfill_weixin.py` | 本步骤归档 | 旧 SQLite 补抓链路退出主干 |
| `acquisition/web/app/` | 迁到 `app/frontend/` 后归档旧位置 | 新前端能 build，且不再 fetch 本地 JSON |
| 根目录 `web/` | 删除空目录或保持归档说明 | 正式前端不再使用 |
| `/data` 静态挂载 | 从正式 API 删除 | 媒体和 query 都来自 DB/OSS/CDN |
| `/api/creation-search/*` | 删除或只在 legacy 服务保留 | 前端、测试、文档都改成正式 API |

检查命令建议：

```text
rg "creation_knowledge.api|acquisition.web_api|/api/creation-search|/data/queries|StaticFiles|CK_SQLITE_PATH" app acquisition pipeline tests
```

如果这一步后 `creation_knowledge/` 仍被正式代码引用，不能直接删；要先把最后的能力迁到 `app/` 或 `decode_content/`。

### 6.5 2026-06-30 执行记录

本步骤已按正式版方向落地第一轮代码改造：

- 新增 `app/api.py`、`app/dependencies.py` 和 `app/routes/*`，正式入口切到 `app.api:app`。
- 新增 acquisition 正式接口：query batch、run summary、query detail，不再走 `/api/creation-search/*`。
- 新增 decode、payload、pipeline run 的正式 API 占位路由，未接仓储的部分明确返回未实现，而不是伪装成已完成。
- 新增 `pipeline/acquisition_runner.py`、`pipeline/decode_runner.py`、`pipeline/creation_pipeline.py`、`pipeline/dedupe.py`，先把 acquisition 到 decode 的编排骨架接上。
- 前端从 `acquisition/web/app` 迁到 `app/frontend`，列表页和详情页改为读取正式 API。
- 前端不再请求 `/data/queries/creation_demo.json`，详情页媒体读取 item 里的 OSS/CDN URL。
- 根 `.gitignore` 的前端构建产物例外从旧路径切到 `app/frontend/dist/`。
- `creation_knowledge/api.py`、`acquisition/web_api.py` 归档到 `archive/2026-06-30-step6/legacy_api/`。
- 旧 `acquisition/web/app/dist` 归档到 `archive/2026-06-30-step6/legacy_frontend/`，旧 `acquisition/web/app` 空目录已清理。
- 顶层 `creation_knowledge/` 包整体归档到 `archive/2026-06-30-step6/legacy_creation_knowledge/`；`scripts/decompose.py` 这个 legacy/debug 入口改为引用 `decode_content/readers/*`。
- `acquisition/store.py`、`acquisition/creation_search.py`、`acquisition/backfill_weixin.py` 归档到 `archive/2026-06-30-step6/legacy_sqlite/` 与 `legacy_acquisition/`。
- `scripts/run_creation_search.py`、`scripts/classify_creation_items.py`、`scripts/filter_multiaxis.py` 归档到 `archive/2026-06-30-step6/legacy_scripts/`。
- `acquisition/classify.py` 保留为正式粗分类可复用的模型判定函数，但移除旧 SQLite 批处理入口。
- 旧本地 `/data` 媒体落盘测试归档到 `archive/2026-06-30-step6/legacy_tests/`。

本地验证：

```text
.venv/bin/pytest -q
90 passed

cd app/frontend && npm ci && npm run build
Vite build passed

rg "(/data/queries|/api/creation-search|/api/filter-prompt|CK_SQLITE_PATH|creation_knowledge\\.api|acquisition\\.web_api)" app pipeline tests
只剩 tests/test_app_api.py 里的反向断言命中
```

## 7. 测试计划

### 7.1 数据库测试

- migration 能在空库建表。
- migration 重跑有明确处理方式。
- 能插入 query batch、query、run、job。
- 能从 repository 查询 summary/detail。

### 7.2 Acquisition 测试

- 三平台 adapter 用 mock 回包测试。
- OSS client 解析 CDN URL。
- 媒体 service 能把图片/视频写成 media assets。
- 粗分类支持 HTTP 图片 URL 和视频 CDN URL。
- resume、skip done、partial、failed 状态可测。

### 7.3 Decode 测试

- 图文读懂离线测试。
- 视频读懂离线测试。
- 创作闸 admit/refute/tiebreak 测试。
- 假 How 重拆测试。
- 作用域复用/新建测试。
- payload draft 结构测试。

### 7.4 Pipeline 测试

- 从 query batch 到 acquisition run。
- 从候选 item 到 decode result。
- 从 knowledge particle 到 payload draft。
- 失败任务可重试。
- 已完成任务不会重复跑。

### 7.5 API 与前端测试

- API 合同测试不依赖 `CK_SQLITE_PATH`。
- API 合同测试不依赖本地 `/data`。
- 前端不再 fetch 本地 query JSON。
- 前端能展示 run、item、classification、decode、payload、ingest 状态。

### 7.6 Legacy 回归

- 删除旧 demo 后，不再有测试 import `scripts.serve_old_web`。
- `local-demo` 可作为旧数据回看来源。

### 7.7 每轮清理回归

每一轮改造结束，都要补一轮“反向测试”：确认正式路径没有重新长出旧依赖。

固定检查：

- 正式代码不 import `acquisition.store`。
- 正式代码不读取 `data/queries/creation_demo.json`。
- 正式代码不创建 `data/app.db`。
- 正式 API 不挂本地 `/data` 作为业务数据源。
- 前端不 fetch `/data/queries/*`。
- 新测试不再断言 legacy 路径是正式入口。

建议命令：

```text
rg "acquisition.store|CK_SQLITE_PATH|data/app.db|creation_demo.json|/data/queries|/api/creation-search|creation_knowledge.api" core acquisition decode_content pipeline app scripts tests
```

命中结果要分三类标注：

- `formal`：必须修掉。
- `legacy`：可以保留，但必须在文件或文档里说明。
- `archive`：不参与正式测试和运行。

### 7.8 2026-06-30 执行记录

本步骤已把测试计划落成一批可离线运行的正式回归测试，不连接真实云库、不读本地 SQLite、不依赖 `/data`：

- 新增 `tests/test_db_migration_contract.py`，检查正式 migration 包含 query、acquisition、decode、payload、ingest、contract snapshot 等业务表，且使用 PostgreSQL/JSONB/可重放写法，不退回 SQLite 形态。
- 新增 `tests/test_postgres_repository_contract.py`，用 fake repository 边界验证 `PostgresAcquisitionRepository` 的 summary、query detail、creation candidate 查询都走正式 PG 表和正式 domain model。
- 新增 `tests/test_platform_adapters.py`，用 mock 回包直接覆盖小红书、微信、抖音三平台 adapter 的 search/detail 映射。
- 新增 `tests/test_decode_stage_full_chain.py`，把 candidate item、media asset、decode service、skill contract、payload draft 串成一条最小离线闭环。
- 新增 `tests/test_legacy_cleanup_contract.py`，把正式源码不得依赖 SQLite、本地 `/data`、旧 API、旧 `creation_knowledge` 包的规则固化为测试；`scripts/decompose.py` 仅作为已标注的 legacy/debug 入口保留。
- 扩展 `tests/test_acquisition_runner.py`，覆盖 skip done、partial、failed 三类采集状态。
- 扩展 `tests/test_pipeline_formal.py`，覆盖 acquisition stage 成功/失败、decode stage 去重、完整 `creation_pipeline` 编排骨架。
- 扩展 `tests/test_app_api.py`，确认旧顶层 `creation_knowledge/` 已归档，活跃源码不 import legacy store/package；decode、payload、pipeline route 在仓储未实现时明确返回 501，且在注入正式仓储后存在成功返回路径，不伪装成旧本地数据接口。
- 继续保留前端 build 与 legacy 反向扫描作为每轮验收命令。

本地验证：

```text
.venv/bin/pytest -q tests/test_db_migration_contract.py tests/test_postgres_repository_contract.py tests/test_acquisition_runner.py tests/test_pipeline_formal.py tests/test_app_api.py
22 passed
```

仍然明确留到后续步骤：

- `decode_content`、payload、pipeline 的正式 PostgreSQL repository 实现。
- decode/payload/pipeline API 从 501 占位切到真实仓储读写。
- 真实云库集成测试或部署烟测，必须在有明确环境变量和测试库隔离后再执行。

## 8. 推荐提交顺序

建议拆成这些提交，降低风险：

1. `docs: add formal construction plan`
2. `chore: archive legacy static demo`
3. `chore: remove tracked env and add env example`
4. `feat(db): add cloud postgres migration and session`
5. `feat(acquisition): add domain and postgres repository`
6. `feat(acquisition): migrate query and platform adapters`
7. `feat(decode): add decode_content contract and readers`
8. `feat(decode): add framing scoping payload service`
9. `feat(pipeline): add cloud pipeline runners`
10. `feat(app): add formal api and migrate frontend`
11. `test: replace sqlite/local-data assumptions`

## 9. 风险与处理

| 风险 | 处理 |
|---|---|
| 云机 repo 与本地 main 不一致 | 施工前以本地新分支/新提交重新部署云机 |
| `.env` 已被 tracked | 先 `git rm --cached .env`，再补 `.env.example` |
| 旧 demo 删除影响回看 | 保留 `local-demo`，必要时外部归档 `web/*` |
| `creation_knowledge/` 迁移影响 import | 分阶段迁移，先加新模块，再替换 import，最后删旧包 |
| payload 合同漂移 | 先统一 `decode_content/contracts.py` 和 `payloads.py` |
| 图文分类依赖本地图片 | 改成支持 HTTP(S) 图片 URL |
| 云端数据库误用 Open AIGC PG | `CK_DB_*` 与 `OPEN_AIGC_PG_*` 明确分离 |

## 10. 最终验收

正式版完成后，应满足：

- 主干不依赖本地 SQLite。
- 主干不依赖 `legacy_data/`。
- 主干不依赖旧 `web/index.html`。
- 顶层正式业务目录不再使用 `creation_knowledge/`。
- 一个云端 query batch 可以追踪到 search、item、classification、decode、payload、ingest。
- 非创作知识候选不会进入深度 decode。
- 每个 payload 都能追溯到原帖、媒体、skill contract 版本和作用域定位。
- 前端默认进入正式工作台。
- 旧 demo 只通过 `local-demo` 或归档路径访问。

最终清理清单：

- 主干没有根目录旧 `web/index.html`。
- 主干没有旧 `web/frameworks.json`、`web/payloads.json`、`web/runs/*`。
- 主干没有正式代码使用 `acquisition/store.py`。
- 主干没有正式代码使用 `scripts/run_creation_search.py`。
- 主干没有正式代码使用 `scripts/decompose.py` 作为主入口。
- 主干没有顶层 `creation_knowledge/` 作为正式业务包。
- 主干没有真实 `.env`、数据库密码、云机私钥。
- 主干没有新产生的本地媒体、SQLite、query JSON 作为正式运行依赖。
