Browse Source

docs: add formal construction plan

SamLee 2 tuần trước cách đây
mục cha
commit
3f6518bc87

+ 59 - 345
docs/acquisition-runtime-boundary.md

@@ -1,368 +1,82 @@
-# Acquisition Demo Runtime Boundary
+# Acquisition Runtime Boundary
 
-本文整理当前 acquisition demo 在 `acquisition/` 目录以外真实使用的输入、输出、配置和外部接口。调查时间:2026-06-30。
+This document records the current formal runtime boundary after the 2026-06-30
+Step6 cleanup. The pre-Step6 local demo boundary was archived at:
 
-## 当前状态
+```text
+archive/2026-06-30-step6/legacy_docs/acquisition-runtime-boundary.md
+```
 
-- 已尝试关闭历史 SubAgent `019f1683-1368-7b20-8d07-8ce7532ab34e`,工具返回 `not found`,当前没有可管理的活跃 SubAgent。
-- 当前前端服务入口是 `creation_knowledge.api:app`,本地通常跑在 `http://localhost:8126/app/#/`。
-- 当前 demo 主链路不是旧的“找帖子”页面,而是“创作 query 正交 demo”:
-  - query 生成:`scripts/build_creation_demo.py`
-  - 真实采集:`scripts/run_creation_search.py`
-  - AI 补判:`scripts/classify_creation_items.py`
-  - API + 静态托管:`creation_knowledge/api.py` + `acquisition/web_api.py`
-  - 前端:`acquisition/web/app/src/App.jsx`、`CreationDemo.jsx`、`CreationQueryDetail.jsx`
+## Current Formal Entrypoints
 
-## 目录外真实依赖
+- API service: `app.api:app`.
+- Frontend source: `app/frontend/src`.
+- Frontend build output: `app/frontend/dist`.
+- Acquisition runner: `scripts/run_acquisition.py`.
+- Decode runner: `scripts/run_decode_content.py`.
 
-### `core/`
+The formal API no longer mounts local `/data` or `/frames` as business data
+sources. Query batches, runs, jobs, candidate items, media assets, and
+classifications are read through the PostgreSQL repository boundary.
 
-`acquisition` 依赖 `core` 作为共享底座:
+## Current State Source
 
-- `core/config.py`
-  - 读取 `.env` 或系统环境变量。
-  - 组装 `Settings`,供搜索、详情、OSS、模型判断、静态目录使用。
-- `core/prompts.py`
-  - 从项目根 `prompts/<name>.txt` 读取 prompt。
-- `core/llm.py`
-  - 走 OpenRouter `/chat/completions`,用于旧 query 生成/部分 query 过滤。
-- `core/models.py`
-  - `Post` / `Card` 数据模型,被 crawler/detail parse 使用。
-- `core/embedding.py`
-  - 走火山 Ark embedding。当前 acquisition demo 主链路不直接用,旧/解构链路和 scope 工具可能用。
-- `core/db.py`
-  - PostgreSQL/Greenplum 连接工具。当前 demo 主链路主要用 SQLite;旧/正式入库链路会用。
+- Formal database config: `CK_DB_*`.
+- Database session and transaction boundary: `core/db_session.py`.
+- Formal repository implementation: `acquisition/repositories/postgres.py`.
+- Formal migration: `db/migrations/001_creation_knowledge_schema.sql`.
 
-### `creation_knowledge/`
+Local SQLite and `data/app.db` are legacy demo state. The old SQLite adapter is
+kept only under:
 
-当前 acquisition demo 借用了 `creation_knowledge` 的服务壳和媒体下载工具:
+```text
+archive/2026-06-30-step6/legacy_sqlite/store.py
+```
 
-- `creation_knowledge/api.py`
-  - FastAPI app。
-  - 挂载 `/app` 到 `acquisition/web/app/dist`。
-  - 挂载 `/data` 到 `Settings.data_dir`,默认 `data`。
-  - 挂载 `/frames` 到 `Settings.frames_dir`,默认 `runtime/frames`。
-  - include `acquisition.web_api.router`。
-- `creation_knowledge/integrations/video_extract.py`
-  - `acquisition.creation_search` 只复用 `_default_download` 下载图片/媒体。
-  - 旧视频解构链路会使用更多 video extract 逻辑。
+## Current Acquisition Boundary
 
-### `scripts/`
+The formal acquisition flow is:
 
-当前 demo 的主要操作入口在 `scripts/`,不在 `acquisition/` 内:
+```text
+query batch -> query x platform job -> detail/media -> coarse classify -> candidate state
+```
 
-- `scripts/build_creation_demo.py`
-  - 读 `scope_trees/trees_index.json`。
-  - 调 `acquisition.query_filter.filter_queries`。
-  - 写 `data/queries/creation_demo.json`。
-- `scripts/run_creation_search.py`
-  - 读 `data/queries/creation_demo.json`。
-  - 写 `data/app.db` 的 `creation_*` 表。
-  - 下载小红书/公众号图片到 `data/media/...`。
-  - 抖音视频调用 OSS 上传接口,保存 CDN URL 到 SQLite。
-- `scripts/classify_creation_items.py`
-  - 从 `data/app.db` 读取未判断/失败的 item。
-  - 调 `acquisition.classify` 使用 Qwen / Ark / OpenRouter。
-  - 写 `creation_item_classifications`。
+Active code paths:
 
-旧链路仍存在但不是当前 demo 主路径:
+- Query generation/filtering: `acquisition/queries/*`.
+- Platform adapters: `acquisition/platforms/*`.
+- Media stabilization: `acquisition/media/*`.
+- Coarse classification: `acquisition/classification/coarse.py`.
+- Run/job orchestration: `acquisition/runner.py`.
 
-- `scripts/decompose.py`
-  - 读 `创作知识提取-skill/extraction/phase1-frame.md`、`phase2-scope.md`。
-  - 读 `prompts/gate_admit.txt`、`gate_refute.txt`、`gate_tiebreak.txt` 等。
-  - 输出 `outputs/`、`web/frameworks*.json`、`web/payloads*.json` 等旧/后续解构产物。
-- `scripts/run_search.py`
-  - 旧“找帖子”链路,写 `data/search_results.json`、`data/search/...`。
+The old SQLite-coupled acquisition runner has been archived under
+`archive/2026-06-30-step6/legacy_acquisition/` and
+`archive/2026-06-30-step6/legacy_scripts/`.
 
-## Prompt 与 Skill
+## Current API And Frontend Boundary
 
-### 当前 demo 直接使用的 prompt
+Active API routes:
 
-- `acquisition/query_filter.txt`
-  - query 机械正交后做 valid/relevant 过滤。
-  - `scripts/build_creation_demo.py` 间接调用。
-  - 前端 `/api/filter-prompt` 会原样展示。
-- `prompts/classify_imgtext.txt`
-  - 小红书、微信公众号图文判断“是不是创作知识”。
-  - `acquisition.classify.classify_imgtext` 读取。
-  - 前端 `/api/judge-prompts` 会展示。
-- `prompts/classify_video.txt`
-  - 抖音视频判断“是不是创作知识”。
-  - `acquisition.classify.classify_video` 读取。
-  - 当前列表里的 `x/10创作知识` 暂不把抖音计入分母。
+- `GET /api/query-batches/{batch_id}`
+- `GET /api/acquisition/runs/{run_id}/summary`
+- `GET /api/acquisition/runs/{run_id}/queries/{query_id}`
 
-### 旧/解构链路使用的 prompt
+The frontend now fetches those formal endpoints. It no longer fetches
+`/data/queries/creation_demo.json`, `/api/creation-search/*`, or
+`/api/filter-prompt`.
 
-- `prompts/extract.txt`
-- `prompts/extract_video.txt`
-- `prompts/gate_admit.txt`
-- `prompts/gate_refute.txt`
-- `prompts/gate_tiebreak.txt`
-- `prompts/gate_how_*`
-- `prompts/gate_why_refute.txt`
-- `prompts/normalize_scope.txt`
-- `prompts/query_gen.txt`
-- `prompts/form_query_gen.txt`
+## Legacy Archive Boundary
 
-### 创作知识提取 skill
+The following are no longer active mainline code:
 
-`创作知识提取-skill/` 当前不是 acquisition demo 主链路的直接运行依赖。它仍然是后续“真实解构/组装 payload”的方法论来源:
+- `creation_knowledge/` top-level package.
+- `acquisition/web_api.py`.
+- `acquisition/store.py`.
+- `acquisition/creation_search.py`.
+- `scripts/run_creation_search.py`.
+- `scripts/classify_creation_items.py`.
+- `scripts/filter_multiaxis.py`.
+- `acquisition/web/app/`.
 
-- `scripts/decompose.py` 直接读取:
-  - `创作知识提取-skill/extraction/phase1-frame.md`
-  - `创作知识提取-skill/extraction/phase2-scope.md`
-- skill 内还包含 schema、taxonomy、lint/ingest/scope-link 工具。
-
-## 数据输入与输出
-
-### Query 输入
-
-- `scope_trees/trees_index.json`
-  - `scripts/build_creation_demo.py` 读取。
-  - 生成 query 的实质/形式/作用/感受/意图树输入。
-- `scope_trees/trees.json`
-  - 当前 demo 主路径不直接读。
-- `scope_trees/trees_embeddings.npy`
-  - 当前 demo 主路径不直接读;scope/embedding 工具可能用。
-
-### Query 输出
-
-- `data/queries/creation_demo.json`
-  - 当前前端首页直接 fetch:`/data/queries/creation_demo.json`。
-  - `scripts/run_creation_search.py` 默认读取它,作为 430 query 输入。
-  - 当前大小约 140KB。
-
-### SQLite
-
-- `data/app.db`
-  - 当前 demo 的结构化结果库。
-  - SQLite,当前约 52MB。
-  - 当前主表:
-    - `creation_search_runs`
-    - `creation_search_jobs`
-    - `creation_search_items`
-    - `creation_item_classifications`
-  - 旧表目前为空:
-    - `queries`
-    - `search_results`
-    - `post_class`
-
-当前调查时行数:
-
-- `creation_search_runs`: 3
-- `creation_search_jobs`: 1302
-- `creation_search_items`: 6009
-- `creation_item_classifications`: 4290
-- `queries`: 0
-- `search_results`: 0
-- `post_class`: 0
-
-`creation_search_items` 存标题、正文、原帖 URL、本地图片 URL、OSS 视频 CDN URL、raw JSON 等;不是巨大 JSON 文件。
-
-### 媒体输出
-
-- `data/media/xiaohongshu/<query_hash>/<content_id>/image_*.webp|jpg|png`
-- `data/media/weixin/<query_hash>/<article_hash>/image_*.webp|jpg|png|gif`
-- 抖音视频不落本地,写 OSS CDN URL 到 SQLite `creation_search_items.video_url`。
-
-当前体量:
-
-- `data/media`: 约 5.4GB
-- `data/app.db`: 约 52MB
-- `data/queries`: 约 140KB
-- `data/` 总计:约 5.5GB
-
-### Runtime 输出
-
-- `runtime/logs/...`
-  - 全量采集、分类、web 服务日志。
-- `runtime/frames`
-  - 旧/视频帧链路使用;FastAPI 会挂载为 `/frames`。
-
-## `.env` 配置
-
-配置读取优先级是:真实环境变量 > `CK_ENV_FILE` 指定文件 > 默认 `.env` > 代码默认值。
-
-当前 demo 主链路实际会读取这些 key:
-
-### 基础
-
-- `CK_ENV_FILE`
-- `CK_DATA_DIR`
-- `CK_FRAMES_DIR`
-- `CK_MAX_CARDS`
-
-### Aiddit 爬虫接口(小红书 / 微信公众号等)
-
-- `AIDDIT_CRAWLER_BASE_URL`
-- `AIDDIT_CRAWLER_TIMEOUT_SECONDS`
-
-### 票圈 TV 抖音独立后端
-
-- `PIAOQUANTV_DOUYIN_BASE_URL`
-- `PIAOQUANTV_DOUYIN_ACCOUNT_ID`
-- `PIAOQUANTV_DOUYIN_COOKIE_BATCH`
-- `CK_DOUYIN_RATIO`
-
-### OSS 转存
-
-- `CRAWLER_OSS_UPLOAD_URL`
-- `CRAWLER_OSS_UPLOAD_TIMEOUT_SECONDS`
-
-### OpenRouter / Gemini
-
-- `OPENROUTER_BASE_URL`
-- `OPENROUTER_API_KEY`
-- `OPENROUTER_MODEL`
-- `OPENROUTER_TIMEOUT_SECONDS`
-
-### 阿里云百炼 / Qwen
-
-- `ALIYUN_BAILIAN_API_KEY`
-- `ALIYUN_BAILIAN_BASE_URL`
-- `ALIYUN_BAILIAN_MODEL`
-
-### Ark / 豆包 / 火山
-
-- `ARK_API_KEY`
-- `ARK_CHAT_URL`
-- `ARK_CHAT_MODEL`
-- `ARK_EMBEDDING_EP`
-- `ARK_EMBEDDING_URL`
-- `ARK_EMBEDDING_DIM`
-
-### 分类限流
-
-- `CLASSIFY_PROVIDER`
-- `CLASSIFY_MODEL`
-- `CLASSIFY_PROVIDER_MIN_INTERVAL_SECONDS`
-- `CLASSIFY_QWEN_MIN_INTERVAL_SECONDS`
-- `CLASSIFY_ARK_MIN_INTERVAL_SECONDS`
-- `CLASSIFY_429_BACKOFF_SECONDS`
-- `CLASSIFY_QWEN_429_BACKOFF_SECONDS`
-- `CLASSIFY_ARK_429_BACKOFF_SECONDS`
-
-### Query 过滤
-
-- `QUERY_FILTER_PROVIDER`
-
-### PG / 后续正式入库
-
-当前 acquisition demo 主要用 SQLite,但 `Settings` 仍要求/读取 PG 配置:
-
-- `OPEN_AIGC_PG_HOST`
-- `OPEN_AIGC_PG_PORT`
-- `OPEN_AIGC_PG_USER`
-- `OPEN_AIGC_PG_PASSWORD`
-- `OPEN_AIGC_PG_DB_NAME`
-- `CK_PG_SCHEMA`
-
-## 外部接口
-
-### 小红书
-
-- 搜索:`POST {AIDDIT_CRAWLER_BASE_URL}/crawler/xiao_hong_shu/keyword`
-- 详情:`POST {AIDDIT_CRAWLER_BASE_URL}/crawler/xiao_hong_shu/detail`
-
-当前 demo:
-
-- 搜索前 10 条。
-- 逐条拉详情。
-- 下载图文图片到本地 `data/media/xiaohongshu/...`。
-
-### 微信公众号
-
-- 搜索:`POST {AIDDIT_CRAWLER_BASE_URL}/crawler/wei_xin/keyword`
-- 详情:`POST {AIDDIT_CRAWLER_BASE_URL}/crawler/wei_xin/detail`
-
-当前 demo:
-
-- 搜索前 10 条。
-- 逐条拉详情。
-- 下载图文图片到本地 `data/media/weixin/...`。
-
-### 抖音
-
-- 搜索:`POST {PIAOQUANTV_DOUYIN_BASE_URL}/crawler/dou_yin/keyword`
-- 详情:`POST {PIAOQUANTV_DOUYIN_BASE_URL}/crawler/dou_yin/detail`
-
-当前 demo:
-
-- 请求体带 `account_id` 和 `cookie_batch`。
-- 搜索前 10 条。
-- 逐条拉详情。
-- 第一个 `video_url` 调 OSS 转存。
-- 本地不保存抖音视频文件。
-
-### OSS 转存
-
-- `POST {CRAWLER_OSS_UPLOAD_URL}`
-- 当前默认:`http://crawler-upload-v2.aiddit.com/crawler/oss/upload_stream`
-- 请求体:
-  - `src_url`
-  - `src_type`: `image` 或 `video`
-- 返回 `oss_object.cdn_url`。
-
-当前 demo 只强制用于抖音视频。文档 `docs/oss-media-transfer.md` 记录过:小红书图片、公众号图片、抖音视频都实测可转存;文字正文不能直接用该接口存,需要另做 JSON/HTML/Markdown 文件上传接口。
-
-### 多模态模型
-
-- OpenRouter:`{OPENROUTER_BASE_URL}/chat/completions`
-- Qwen/DashScope:`{ALIYUN_BAILIAN_BASE_URL}/chat/completions`
-- Ark/豆包:默认 `https://ark.cn-beijing.volces.com/api/v3/chat/completions`
-
-当前 demo 判断逻辑:
-
-- `classify_imgtext`: 标题 + 正文前 1500 字 + 本地图片 base64 data URL。
-- `classify_video`: HTTP(S) CDN video URL 直接传模型;本地 mp4 是兼容 fallback。
-- Qwen / Ark / OpenRouter 按 provider 选择和可用密钥兜底。
-- provider 级 limiter 和 429 backoff 在单进程内生效。
-
-## 前端依赖
-
-前端不是独立后端,它由 FastAPI 静态托管:
-
-- 构建源:`acquisition/web/app`
-- 构建产物:`acquisition/web/app/dist`
-- 入口:`/app`
-- 路由:
-  - `#/`:Query Demo
-  - `#/query/<encoded-query>`:单 query 详情
-
-前端直接请求:
-
-- `/data/queries/creation_demo.json`
-- `/api/creation-search/summary`
-- `/api/creation-search/query?query=...`
-- `/api/filter-prompt`
-- `/api/judge-prompts`
-- `/data/media/...`
-
-## 正式开发迁移建议
-
-如果另起正式目录,不建议把当前 5.5GB `data/` 直接复制为默认输入输出。建议拆成四类可配置路径:
-
-1. `QUERY_SOURCE_PATH`
-   - 当前对应 `data/queries/creation_demo.json`。
-   - 正式版可以换成数据库表、对象存储 JSON、或正式 query 生成服务。
-
-2. `RESULT_DB_PATH`
-   - 当前固定在 `acquisition.store.DB_PATH = data/app.db`。
-   - 正式版应改成 env 可配置,例如 `CK_SQLITE_PATH` 或直接切 PG。
-
-3. `MEDIA_ROOT`
-   - 当前由 `CK_DATA_DIR` 控制,默认 `data`。
-   - 当前媒体实际写死在 `ROOT / data / media/...` 的代码也需要改成使用 `settings.data_dir`,否则迁目录会漏。
-
-4. `PROMPT_ROOT`
-   - 当前 `core/prompts.py` 固定读项目根 `prompts/`。
-   - 正式版建议显式配置 prompt 目录或把 prompt 版本入库。
-
-优先改造点:
-
-- `acquisition.store.DB_PATH` 改成可配置,不再硬绑定 `data/app.db`。
-- `acquisition.creation_search._process_xhs/_process_weixin` 的本地媒体目录改成 `settings.data_dir`。
-- `load_creation_queries` 默认路径改为参数/环境配置。
-- `core.prompts.PROMPTS_DIR` 改成可配置,或在启动时固定 prompt snapshot。
-- 把 crawler/OSS/model provider 封装成正式 adapter,避免 `.env` key 分散。
-- 将旧链路 `scripts/run_search.py`、旧表 `queries/search_results/post_class` 与当前 `creation_*` demo 数据模型分离归档。
+They remain available under `archive/2026-06-30-step6/` for migration evidence
+and local-demo recovery only.

+ 11 - 8
开发文档/产品文档.md

@@ -2,6 +2,7 @@
 
 > 配套:本目录《技术文档.md》(工程实现)、《创作知识拆解框架.md》(方法论设计依据)。
 > 本文讲**产品全貌 + 下一迭代要补的新能力**,不讲代码细节。
+> 2026-06-30 归档说明:本文描述的是旧本地 demo / 单帖解构产品形态,已作为历史说明保留;正式版定位与云端管道看《正式版开发文档.md》《正式版施工文档.md》。
 
 ---
 
@@ -10,7 +11,7 @@
 **一句话**:把一篇创作帖(小红书 / 抖音的图文或视频教程),自动拆成 **N 颗**可被下游单独检索复用的「创作知识」,每颗按自己的类型成形,收敛成入库请求体,并在前端按类型可视化。
 
 - **输入**:一篇创作内容(正文 + 图 / 视频)。
-- **输出**:一个或多个 ingest payload(**一颗知识 = 一个 payload**,一帖通常 1–N 颗,共享同一 `source.id`),落到本地 JSON 供前端查看、供下游入库检索
+- **输出**:一个或多个 ingest payload(**一颗知识 = 一个 payload**,一帖通常 1–N 颗,共享同一 `source.id`)。旧 demo 曾写入本地 JSON 供前端查看;正式版改为云端 payload draft / ingest 状态
 
 ### 单位:颗
 
@@ -40,18 +41,18 @@
 
 ---
 
-## 三、端到端用户流
+## 三、端到端用户流(历史)
 
-### 现状(已落地
+### 旧本地 demo(已归档
 
 ```
 人工选定帖子 ID(写死 9 个) → 取数读懂 → 拆颗+判类型+成形+打标签 → 作用域定位 → 组装 payload
-  → web/frameworks.json + web/payloads.json → 前端一页多颗、按类型渲染
+  → web/frameworks.json + web/payloads.json → 前端一页多颗、按类型渲染
 ```
 
 痛点:**入口靠人肉填 content_id**,无法按主题量产;live 帖媒体受平台防盗链限制,要本地下载中转。
 
-### 新能力(下一迭代)
+### 旧计划中的新能力(已被正式版云端管道取代)
 
 ```
 输入关键词(query) → 自动搜索召回一批帖 → 逐帖取数
@@ -62,6 +63,8 @@
 
 从「我知道是哪几篇 → 填 ID」升级为「我想要某主题的创作知识 → 给关键词 → 自动产出一批」。
 
+正式版不再采用本地 run JSON 作为目标形态,而是进入云端 `query_batches / acquisition_runs / candidate_items / decode_results / payload_drafts`。
+
 ---
 
 ## 四、新能力的价值
@@ -92,11 +95,11 @@
 
 ## 六、现状与路线
 
-**已落地**:9 帖样本 → 24 颗(What 15 / Why 5 / How 4),0 ERROR;前端按类型渲染 + 作用域定位 drawer + 提示词溯源。
+**旧 demo 已落地**:9 帖样本 → 24 颗(What 15 / Why 5 / How 4),0 ERROR;前端按类型渲染 + 作用域定位 drawer + 提示词溯源。
 
-**本次迭代**:query 搜索 + OSS 转存 + run 隔离产出(先上小红书)。
+**历史迭代计划**:query 搜索 + OSS 转存 + run 隔离产出(先上小红书)。正式版已改为云端数据库和管道式状态。
 
 **后续路线**:
 - 抖音 / 更多平台搜索接入(抖音 keyword 接口需鉴权,待打通)。
-- 真正入库 `POST /api/v1/knowledge/ingest`(当前止于本地 JSON)。
+- 真正入库 `POST /api/v1/knowledge/ingest`(正式版通过云端 payload draft/ingest 状态推进)。
 - payload schema 与代码产出对齐(`dim_attributes` 等细节,见技术文档「已知问题」)。

+ 12 - 9
开发文档/技术文档.md

@@ -3,14 +3,15 @@
 > 配套:本目录《产品文档.md》《创作知识拆解框架.md》。
 > 本文 = **A 现有系统架构** + **B 下一迭代详细开发步骤** + **C 测试核验** + **D 实施顺序** + **E 已知问题**。
 > 所有 `文件:行号` 可点开核对(行号对应当前 main,实现时以实际为准)。
+> 2026-06-30 归档说明:本文描述的是旧本地 demo / 单帖解构实现,已经归入正式版改造的历史说明;正式入口与施工步骤以后看《正式版开发文档.md》《正式版施工文档.md》。
 
 ---
 
-# A. 现有系统架构
+# A. 旧本地 Demo 系统架构
 
 ## A.1 总览
 
-一条 Python 编排流水线,把帖子拆成知识颗并组装 ingest payload。入口 `scripts/decompose.py`,方法论真源是 `创作知识提取-skill/`(phase 文档直接当 LLM system prompt 喂),产物给 `web/index.html` 渲染。
+一条 Python 编排流水线,把帖子拆成知识颗并组装 ingest payload。入口 `scripts/decompose.py`,方法论真源是 `创作知识提取-skill/`(phase 文档直接当 LLM system prompt 喂),旧产物给归档静态页渲染。
 
 ```
 ① 读懂 → ①.5 创作闸 → ② 成形 → ②.5 假how根治 → ②.7 why轻闸 → ③ 作用域 → ⑤ 组装
@@ -48,7 +49,7 @@
 
 ## A.4 数据契约
 
-### frameworks.json(前端主数据,[web/frameworks.json](../web/frameworks.json))
+### frameworks.json(前端主数据,[archive/2026-06-30-step1/legacy_web/frameworks.json](../archive/2026-06-30-step1/legacy_web/frameworks.json))
 ```
 { count, posts:[ {
     post_id, source_id, title, platform, url,
@@ -64,7 +65,7 @@
 } ] }
 ```
 
-### payloads.json(入库体,[web/payloads.json](../web/payloads.json))
+### payloads.json(入库体,[archive/2026-06-30-step1/legacy_web/payloads.json](../archive/2026-06-30-step1/legacy_web/payloads.json))
 ```
 [ { source:{id,source_type:"post",title,author,source_metadata:{platform,url}},
     title, content, dim_creations:["创作"], dim_attributes:["how"|"what"|"why"],
@@ -75,11 +76,11 @@
 ### Ingest schema(最终裁判)
 `创作知识提取-skill/format/ingest-payload-{how,what,why}.schema.json` —— 字段约束与受控词表的权威定义。
 
-## A.5 前端数据绑定([web/index.html](../web/index.html))
+## A.5 前端数据绑定([archive/2026-06-30-step1/legacy_web/index.html](../archive/2026-06-30-step1/legacy_web/index.html))
 
-- 加载:[`index.html:362`](../web/index.html) `Promise.all([j("/frameworks.json"), j("/payloads.json")])`,`cache:"no-store"`。
+- 加载:旧 `index.html:362` 使用 `Promise.all([j("/frameworks.json"), j("/payloads.json")])`,`cache:"no-store"`。
 - 渲染:HowTable(工序表)/ WhatCard(构成卡)/ WhyCard(阐述卡);组件颗 cross-ref 跳转;作用域 drawer(候选→top-K→复用/新建);提示词溯源(点开产出该阶段的 prompt)。
-- payload 按 `source.id` 匹配挂到帖子([:363](../web/index.html))。
+- payload 按 `source.id` 匹配挂到帖子(旧静态页对应逻辑)。
 - `web/prompt-read`→`../prompts`、`web/prompt-skill`→`../创作知识提取-skill/extraction` 是**符号链接**(前端静态读 prompt)。
 
 ## A.6 运行环境
@@ -92,6 +93,8 @@
 
 # B. 下一迭代 · 详细开发步骤
 
+> 历史说明:本章是旧 demo 阶段的下一迭代计划,不再作为正式版施工计划。正式施工以《正式版施工文档.md》为准。
+
 **目标**:`query 搜索 → OSS 转存 → 模型直读 CDN → run 隔离产出`。**仅小红书**。已实测:小红书搜索 `code:0`、OSS `status:0` 回 `cdn_url`、Gemini 直读 OSS http 视频链 HTTP 200。新 env 已写入 `.env`(`CRAWLER_OSS_UPLOAD_URL` 等 5 个)。
 
 ## B.0 实测确认的接口契约
@@ -192,8 +195,8 @@ def extract_video(post, *, ..., oss_video_url: Optional[str] = None) -> Extracte
   - 媒体类型:复用现有 `if post.video_urls:` 判定。
 - **run 隔离产出**:`slugify(text)`(中英→`-`,去空);写 `web/runs/<slug>.json`(同名存在则加 `-{timestamp}` 保留历史)+ 配套 `web/runs/<slug>.payloads.json`(前端按 `source.id` 配对,须同行)+ 追加 `web/runs/index.json` 清单 `[{slug,query,count,created_at}]`。**不覆盖 `web/frameworks.json`**。
 
-### ④ `web/index.html` — run 选择器(小改
-- data-load `useEffect`([:361](../web/index.html))加:fetch `/runs/index.json` 填一个 `<select>`/pill 行(近 header)。
+### ④ 旧 `web/index.html` — run 选择器(历史计划
+- data-load `useEffect` 加:fetch `/runs/index.json` 填一个 `<select>`/pill 行(近 header)。
 - 选中某 run → 加载 `/runs/<slug>.json` + `/runs/<slug>.payloads.json`;**默认不选仍加载 `/frameworks.json`**(9 帖样本不动)。
 - 渲染树形状兼容(run posts 与 frameworks posts 同形),卡片组件零改。
 

+ 344 - 0
开发文档/正式版开发文档.md

@@ -0,0 +1,344 @@
+# 创作知识系统|正式版开发文档
+
+> 文档性质:正式版开发文档。
+> 适用范围:后续在 `main` 上重构正式架构时使用。
+> 更新时间:2026-06-30。
+> 核心原则:正式版不沿用本地 SQLite、本地 `data/legacy_data`、旧 `web/index.html`,也不保留 `creation_knowledge/` 作为顶层业务目录名。当前代码只作为证据和迁移来源。
+
+## 1. 正式定位
+
+这个系统的正式定位是:
+
+**创作知识发现、采集、判断与解构工作台。**
+
+它不是普通爬虫,也不是把所有内容先下载入库再慢慢判断的本地 demo。正式版要从一个创作需求出发,生成或读取 query,跨平台召回真实内容,在采集阶段先做创作知识粗筛,再把高价值候选送入单帖解构,最终形成可入库的知识 payload。
+
+这里的“创作知识”只收能指导“怎么想、怎么判断、怎么搭框架”的知识,不收器材、剪辑软件、调色、导出等制作执行知识。这个边界来自现有 `创作知识提取-skill`,后续目录可以改,但这个 skill contract 不能被降级或打散。
+
+证据:`创作知识提取-skill/README.md:20-30` 定义只收创作、不收制作、一帖拆成 N 颗知识;`scripts/decompose.py:176-185` 已有创作判定闸。
+
+## 2. 正式业务链路
+
+正式版主链路是:
+
+```text
+query -> search -> detail/media -> coarse classify -> decode -> scope -> payload -> ingest
+```
+
+每一步的业务含义:
+
+1. **query**:来自云端 query 批次或 query 生成服务,不再以本地 JSON 作为正式输入。
+2. **search**:跨小红书、微信公众号、抖音等平台召回真实内容。
+3. **detail/media**:补齐正文、图片、视频、作者、原链接和媒体可访问地址。
+4. **coarse classify**:先判断候选是否像创作知识,非创作内容不进入重解构。
+5. **decode**:对通过粗筛的候选做单帖读懂、创作闸和知识拆颗。
+6. **scope**:定位实质、形式、感受、作用、意图等作用域。
+7. **payload**:按 skill contract 组装可入库 payload。
+8. **ingest**:写入正式知识库,并记录入库状态。
+
+所以答案很明确:**正式版不需要、也不应该,把所有搜索结果先下载到本地 SQLite 之后再判断。**
+采集阶段只保留必要详情和媒体地址,先粗筛;只有像创作知识的候选才进入深度 decode。
+
+证据:`acquisition/runner.py:150-220` 已按 `query x platform` 创建 run/job 并执行平台采集;`acquisition/runner.py:91-146` 已写入候选 item、媒体和粗分类;`pipeline/creation_pipeline.py:23-66` 已把 acquisition 与 decode stage 串成正式编排骨架;`decode_content/service.py` 是正式单帖解构服务。
+
+## 3. 正式目录结构
+
+正式版目标目录不是当前目录的小修小补,而是下面这套结构:
+
+```text
+core/
+  config.py
+  llm.py
+  embedding.py
+  models.py
+  prompts.py
+  jsonio.py
+
+acquisition/
+  query 生成 / 筛选
+  平台搜索
+  详情抓取
+  媒体稳定化
+  粗分类
+  候选 item 状态
+
+decode_content/
+  单帖读懂
+  创作闸
+  what/how/why 拆颗
+  作用域定位
+  payload 组装
+  skill contract 版本记录
+
+pipeline/
+  query -> search -> classify -> decode -> payload -> ingest 编排
+  run/job 状态
+  断点续跑
+  去重策略
+
+scripts/
+  run_acquisition.py
+  run_decode_content.py
+  run_creation_pipeline.py
+  build_creation_demo.py
+
+app/ 或 web/
+  正式前端
+  正式 API 入口
+```
+
+这意味着:`creation_knowledge/` 不应该作为未来顶层业务目录继续存在。Step6 后它已经从活跃源码树归档,原能力对应迁到新目录:
+
+| 当前位置 | 当前职责 | 正式目标 |
+|---|---|---|
+| `archive/2026-06-30-step6/legacy_api/creation_knowledge_api.py` | 旧 FastAPI 壳,曾挂 `/app`、`/data`、`/frames` | 正式入口已迁到 `app/api.py` |
+| `archive/2026-06-30-step6/legacy_creation_knowledge/creation_knowledge/integrations/extractor.py` | 旧图文/图片读懂 | 正式读懂在 `decode_content/readers/imgtext.py` |
+| `archive/2026-06-30-step6/legacy_creation_knowledge/creation_knowledge/integrations/video_extract.py` | 旧视频读懂、分段卡片 | 正式读懂在 `decode_content/readers/video.py` |
+| `archive/2026-06-30-step6/legacy_creation_knowledge/creation_knowledge/integrations/video_frames.py` | 旧视频帧能力 | 正式能力在 `decode_content/readers/video_frames.py` |
+| `archive/2026-06-30-step6/legacy_creation_knowledge/creation_knowledge/media.py` | 旧本地媒体落盘工具 | 正式媒体稳定化在 `acquisition/media/service.py` |
+
+`创作知识提取-skill/` 不等于 `creation_knowledge/`。前者是拆解方法合同,应该保留;后者是当前 Python package 名,正式版要改名和拆分。
+
+证据:`app/api.py:29-49` 是正式 FastAPI 入口;`decode_content/readers/imgtext.py`、`decode_content/readers/video.py`、`decode_content/readers/video_frames.py` 是正式 reader;旧包已归档到 `archive/2026-06-30-step6/legacy_creation_knowledge/`。
+
+## 4. 分层职责
+
+### 4.1 `core`
+
+`core` 是全系统共用能力层,只放配置、模型、LLM、Embedding、Prompt、JSON 读写等基础能力。它不应该表达 acquisition、decode 或 pipeline 的具体业务流程。
+
+正式版要保留:
+
+- 配置加载和环境区分。
+- LLM JSON 调用和修复。
+- embedding / 作用域定位基础能力。
+- Post、Card、候选 item、decode result、payload 等共享模型。
+- prompt 加载与版本记录。
+
+证据:`core/config.py:43-90` 已承载 PG、爬虫、LLM、媒体目录等配置;`scripts/decompose.py:22-30` 已依赖 `core.config`、`core.llm`、`core.prompts` 和作用域 linker。
+
+### 4.2 `acquisition`
+
+`acquisition` 是候选发现层。它从 query 出发,找到真实内容,补齐可判断的信息,并产出候选 item。
+
+正式职责:
+
+- query 生成与筛选。
+- 平台搜索。
+- 详情抓取。
+- 媒体稳定化,正式版以 OSS/CDN URL 为主。
+- 粗分类,判断候选是否值得深度 decode。
+- 向云端状态源写入 run/job/item/classification。
+
+这里的粗分类不是最终知识入库,它只是为了避免把明显非创作内容送进昂贵的单帖解构。
+
+证据:`acquisition/runner.py:16` 定义正式默认三平台;`acquisition/queries/builder.py` 与 `acquisition/queries/filter.py` 承接 query 生成和筛选;`acquisition/classification/coarse.py:76-130` 做粗分类;`acquisition/repositories/postgres.py` 负责把 run/job/item/classification 写入正式库。
+
+### 4.3 `decode_content`
+
+`decode_content` 是内容解构层。它把一个候选内容从“原帖/文章/视频”推进到“可入库知识颗”。
+
+正式职责:
+
+- 单帖读懂:图文、视频转成可追溯的文字、卡片和媒体索引。
+- 创作闸:再次过滤制作、工具、纯展示和越界内容。
+- What/How/Why 拆颗:一帖可以拆成 N 颗,三类知识对等。
+- 作用域定位:定位实质、形式、感受、作用、意图。
+- payload 组装:一颗知识对应一个 ingest payload。
+- skill contract 版本记录:记录本次拆解用到的 phase/prompt/schema 版本。
+
+这一层要从 `scripts/decompose.py` 抽出来,但不能改坏 `创作知识提取-skill` 的核心规则。
+
+证据:`scripts/decompose.py:1-9` 明确一帖到 N 颗再到 payload;`scripts/decompose.py:102-156` 是图文/视频读懂;`scripts/decompose.py:487-511` 是闸门、拆颗、作用域和组装;`创作知识提取-skill/extraction/phase1-frame.md:11-28` 定义类型中立拆颗;`phase2-scope.md:1-14` 定义作用域定位;`phase3-assemble.md:1-5` 定义每颗一个 payload。
+
+### 4.4 `pipeline`
+
+`pipeline` 是正式编排层,负责把 acquisition、decode_content、ingest 串起来。
+
+正式职责:
+
+- run:一次 query 批次、采集批次或解构批次。
+- job:一个 query x platform 采集任务,或一个候选 item decode 任务。
+- resume:失败或中断后只补未完成任务。
+- dedupe:跨 query、跨平台、跨 run 去重。
+- gate:粗分类、创作闸、质量闸、人工审核闸。
+- ingest status:payload 是否已入正式知识库。
+
+当前代码已有云端 acquisition run/job 的第一版正式状态对象,但还没有完整 pipeline。文档要把 pipeline 写成目标架构,不要写成已完成事实。
+
+证据:`db/migrations/001_creation_knowledge_schema.sql` 已定义正式 run/job 表;`acquisition/repositories/postgres.py` 已提供正式 repository;`acquisition/runner.py` 已串起 query batch、run、job、item、media、classification;`scripts/decompose.py:514-525` 仍是单独 query 搜索召回入口,尚未并入正式 pipeline。
+
+### 4.5 `app` / `web`
+
+正式前端与 API 已从旧 `creation_knowledge/api.py` 迁出。目标是一个正式工作台,不是旧静态 demo。
+
+正式视图:
+
+- Query 批次视图:query 组合方式、采集覆盖、粗分类命中。
+- 候选 item 视图:来源、媒体、正文、分类原因、是否进入 decode。
+- Decode 视图:单帖读懂、创作闸、What/How/Why 知识颗、作用域。
+- Payload/ingest 视图:payload 审核、入库状态、失败重试。
+
+当前 Vite React 看板已迁到 `app/frontend`,列表页和详情页改读正式 API。后续还要补 decode、payload、ingest 的完整审核视图。
+
+证据:`app/api.py:38-49` 注册正式路由并托管 `app/frontend/dist`;`app/frontend/src/pages/CreationDemo.jsx:47-55` 读取 query batch 与 run summary;`app/frontend/src/pages/CreationQueryDetail.jsx:159-162` 读取正式 query detail。
+
+## 5. 数据策略:正式版只认云端数据库
+
+正式版不使用本地 SQLite 作为状态源,也不把本地 `/data`、`legacy_data` 当成默认输入输出。
+
+正式数据库已经新建在“阿里云海外 lsh 开发机”上;本地环境配置目录是:
+
+```text
+/Users/samlee/Documents/工作环境配置/阿里云海外lsh开发机
+```
+
+该目录包含机器账号说明和私钥文件,属于本机环境配置,不进入仓库、不写入本文档细节、不提交到 git。
+
+正式数据策略:
+
+- query 批次进入云端数据库或云端 query 服务;当前第一版已支持写入云端 `query_batches` 和 `queries`。
+- acquisition 写云端 run/job/item/classification。
+- 媒体统一使用 OSS/CDN URL,前端直接消费 URL。
+- decode_content 写云端 decode result、scope result、payload draft。
+- pipeline 记录断点续跑、失败原因、重试次数和 ingest status。
+- 本地 SQLite 只作为 `local-demo` 分支或历史回看手段,不进入 `main` 的正式架构。
+
+当前代码里的 SQLite 只说明“旧实现怎么存过数据”,不再作为正式版设计对象。正式文档不再保留“SQLite 与本地数据”作为主章节。
+
+证据:旧 SQLite adapter 已归档到 `archive/2026-06-30-step6/legacy_sqlite/store.py`;`core/config.py` 已区分 `PgConfig` 和正式 `CreationDbConfig`;`core/db_session.py` 是正式 PG 事务入口;`db/migrations/001_creation_knowledge_schema.sql` 已落正式状态表;`acquisition/repositories/postgres.py` 已实现正式 acquisition repository。
+
+## 6. Legacy 边界
+
+下面这些能力保留为历史证据或 local-demo 回看,不进入正式版默认路径:
+
+- `archive/2026-06-30-step1/legacy_web/index.html`
+- `archive/2026-06-30-step1/legacy_web/frameworks.json`
+- `archive/2026-06-30-step1/legacy_web/payloads.json`
+- `archive/2026-06-30-step1/legacy_web/runs/*`
+- `archive/2026-06-30-step1/legacy_scripts/serve_old_web.py`
+- `legacy_data/app.db`
+- `legacy_data/media`
+- `legacy_data/queries/creation_demo.json`
+
+它们的意义是让旧 demo 可以回看,不是正式版要继续背负的架构。2026-06-30 第一轮归档后,旧静态页、旧脚本、旧抓包证据已经迁到 `archive/2026-06-30-step1/`;如果要继续使用旧 demo,就切回 `local-demo`。
+
+证据:`archive/2026-06-30-step1/legacy_scripts/serve_old_web.py:1-6` 明确它是 legacy decompose demo;`archive/2026-06-30-step1/legacy_scripts/serve_old_web.py:46-66` 服务旧路径并监听 `127.0.0.1:8127`;`archive/2026-06-30-step1/legacy_web/index.html:370-376` 读取旧 `frameworks.json`、`payloads.json` 和 `runs` 产物。
+
+## 7. 保留资产与不可破坏边界
+
+可以大改目录、命名和编排,但不能破坏这些资产:
+
+- `创作知识提取-skill`:保留为正式 skill contract。
+- 三 lane 对等:How、What、Why 都是知识颗,不默认 How 是主角。
+- 组件颗机制:What/Why 可独立成颗,也可作为 How 的组件颗回链。
+- 作用域定位:五类作用域开放值,定位不上才新建。
+- 平台 adapter 差异:小红书、微信、抖音的搜索、详情、媒体处理差异真实存在。
+- 粗分类闸门:不是每条候选都进入深度 decode。
+- run/job/resume:采集批次和任务状态是正式 pipeline 雏形。
+
+证据:`创作知识提取-skill/README.md:24-30`;`创作知识提取-skill/extraction/phase1-frame.md:11-28`;`acquisition/platforms/base.py` 定义平台 adapter 合同;`acquisition/runner.py:150-220` 已按 query x platform 跑正式 run/job,并支持 `resume`、`skip_done`。
+
+## 8. 第一轮重构路线
+
+第一轮重构目标:**把目录、入口、数据源方向改对,同时保留可迁移组件。**
+
+建议顺序:
+
+1. **新建正式目录**
+   - 建 `decode_content/`、`pipeline/`、`app/` 或正式 `web/`。
+   - `core/`、`acquisition/` 保留但清理边界。
+   - 不再新增 `creation_knowledge/` 下的业务代码。
+
+2. **迁移 `creation_knowledge/`**
+   - `api.py` 迁到 `app/api.py`。
+   - `integrations/extractor.py` 迁到 `decode_content/readers/imgtext.py`。
+   - `integrations/video_extract.py` 迁到 `decode_content/readers/video.py`。
+   - `media.py` 的本地落盘逻辑退出正式主链路,媒体以 OSS/CDN URL 为准。
+
+3. **替换状态源**
+   - 不做新的 SQLite adapter。
+   - 基于阿里云海外 lsh 开发机新建云端数据库。
+   - 先定义云端 run/job/item/decode/payload/ingest 状态对象,再改 API 和 pipeline 读写。
+
+4. **抽 `decode_content`**
+   - 从 `scripts/decompose.py` 拆出读懂、创作闸、拆颗、作用域、payload 组装。
+   - 保留 `创作知识提取-skill` 作为合同源,并记录版本。
+
+5. **补 `pipeline`**
+   - 先跑 `query -> search -> classify -> decode -> payload`。
+   - 再接正式 ingest。
+   - 每一步写云端 run/job 状态,支持断点续跑。
+
+6. **升级工作台**
+   - 当前 query 列表和详情页可迁入正式 app。
+   - 去掉本地 query JSON 和 SQLite API 假设。
+   - 增加 decode、payload、ingest 状态视图。
+
+## 9. 验收标准
+
+后续正式实现至少满足:
+
+- 仓库主干不再依赖本地 SQLite 作为默认状态源。
+- 顶层业务目录不再使用 `creation_knowledge/` 命名。
+- 从一个云端 query 批次能追踪到 search、item、classification、decode、payload、ingest。
+- 非创作知识候选不会进入深度 decode,或会被显式标记为跳过。
+- 每个 payload 能追溯到原帖、媒体、skill contract 版本和作用域定位过程。
+- 前端默认进入正式工作台,不进入旧 `web/index.html`。
+- 旧 demo 与本地历史数据只通过 `local-demo` 或归档路径访问。
+
+## 附录 A:代码证据索引
+
+| 判断 | 证据 |
+|---|---|
+| 正式 FastAPI 入口已迁到 `app` | `app/api.py:29-49` |
+| 旧 FastAPI 壳已归档 | `archive/2026-06-30-step6/legacy_api/creation_knowledge_api.py` |
+| 正式图文读懂在 `decode_content/readers` | `decode_content/readers/imgtext.py:29-72`, `:91-120` |
+| 正式视频读懂在 `decode_content/readers` | `decode_content/readers/video.py:54-100`, `:128-150` |
+| 正式视频帧能力在 `decode_content/readers` | `decode_content/readers/video_frames.py:141-172` |
+| 旧本地媒体工具已归档 | `archive/2026-06-30-step6/legacy_creation_knowledge/creation_knowledge/media.py` |
+| 当前主业务 API 已换成正式 acquisition routes | `app/routes/acquisition.py` |
+| 当前前端已迁到 Vite React 正式工作台 | `app/frontend/src/App.jsx`, `app/frontend/src/pages/CreationDemo.jsx`, `app/frontend/src/pages/CreationQueryDetail.jsx` |
+| 列表页读取正式 query batch 与 run summary | `app/frontend/src/pages/CreationDemo.jsx:47-55` |
+| 详情页读取正式 query detail | `app/frontend/src/pages/CreationQueryDetail.jsx:159-162` |
+| acquisition 默认三平台 | `acquisition/runner.py:16` |
+| acquisition 从云端 query batch 读取 keep query | `acquisition/runner.py:168-180` |
+| acquisition 做图文/视频粗分类 | `acquisition/classification/coarse.py:76-130` |
+| `query x platform` job 失败继续、done/partial/failed 收敛 | `acquisition/runner.py:188-275` |
+| 正式采集脚本创建 run/job,支持 resume/skip-done | `scripts/run_acquisition.py`; `acquisition/runner.py:155-165` |
+| 旧 SQLite 表只作为归档证据 | `archive/2026-06-30-step6/legacy_sqlite/store.py` |
+| Open AIGC PG 连接基建仍主要用于分类树 dump | `core/db.py:1-4` |
+| 正式状态库配置与事务入口已存在 | `core/config.py`, `core/db_session.py` |
+| 单帖解构链路是一帖到 N 颗再到 payload | `scripts/decompose.py:1-9` |
+| 图文/视频读懂链路 | `scripts/decompose.py:102-156` |
+| 创作判定闸 | `scripts/decompose.py:176-185` |
+| 作用域候选、名词化、定位、复用/新建 | `scripts/decompose.py:304-392` |
+| payload 组装 | `scripts/decompose.py:395-467` |
+| 单帖取数、读懂、闸、拆颗、作用域、组装主流程 | `scripts/decompose.py:470-511` |
+| skill 是自包含提取合同 | `创作知识提取-skill/README.md:1-18` |
+| 只收创作、不收制作 | `创作知识提取-skill/README.md:20-22` |
+| 一帖 N 颗、三 lane 对等、作用域定位 | `创作知识提取-skill/README.md:24-30` |
+| phase1 类型中立拆颗 | `创作知识提取-skill/extraction/phase1-frame.md:11-28` |
+| phase2 只做作用域定位 | `创作知识提取-skill/extraction/phase2-scope.md:1-14` |
+| phase3 每颗组装一个 payload | `创作知识提取-skill/extraction/phase3-assemble.md:1-5` |
+| 旧静态页和旧 server 是 legacy demo | `archive/2026-06-30-step1/legacy_scripts/serve_old_web.py:1-6`, `:46-66`; `archive/2026-06-30-step1/legacy_web/index.html:370-376` |
+
+## 附录 B:Legacy 本地数据说明
+
+本地核验时间:2026-06-30。
+核验对象:`legacy_data`。
+用途:只用于证明旧 demo 曾经跑通过,不作为正式版输入、状态源或迁移前提。
+
+| 对象 | 当前结果 |
+|---|---|
+| query family 数 | 15 |
+| keep query 数 | 430 |
+| `legacy_data/app.db` | 约 52MB |
+| `legacy_data/media` | 约 5.4GB |
+| `creation_search_runs` | 3 |
+| `creation_search_jobs` | 1302 |
+| job 状态 | `done=1162`, `partial=9`, `failed=131` |
+| `creation_search_items` | 6009 |
+| `creation_item_classifications` | 4290 |
+
+这些数据不再进入正式架构设计。未来如需回看,切 `local-demo` 或访问归档数据;`main` 面向云端数据库和正式目录重构。

+ 1083 - 0
开发文档/正式版施工文档.md

@@ -0,0 +1,1083 @@
+# 创作知识系统|正式版施工文档
+
+> 文档性质:施工文档。
+> 目标:把当前本地 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`
+- `数据接口与来源/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` 替代。
+- `数据接口与来源/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 配置,不进入正式工程 |
+| 立即归档后从主干移除 | `数据接口与来源/captures/*` | 移到 `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` 负责:
+
+- 同平台同 source_id 去重。
+- 跨 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 作为正式运行依赖。