|
|
@@ -0,0 +1,350 @@
|
|
|
+# mode_workflow · 工序(Workflow)接口文档
|
|
|
+
|
|
|
+> 来源:`examples/mode_workflow`(`server.py` + `db.py`)。本文档只整理**工序方向**(`mode=process`)相关接口。
|
|
|
+> 工具方向(`mode=tools`)多数接口同形,仅把 `process→tools`、`mode_process→mode_tools`、`search_process→search_tools` 替换即可,文末附差异说明。
|
|
|
+
|
|
|
+## 0. 基础约定
|
|
|
+
|
|
|
+- **Base URL**:`http://<host>:8772`(`server.py` 的 `PORT=8772`)。
|
|
|
+- **数据源**:MySQL 四表为唯一事实源,工序方向用其中两张:
|
|
|
+ - `search_process`:每行 = 一个 `(query, 帖子)`,搜索 + LLM 评估结果。
|
|
|
+ - `mode_process`:每行 = 一个解构出的**工序**(一帖可解出多个工序;`steps` 等嵌套结构存 JSON 列)。
|
|
|
+- **方向参数**:凡带 `mode` 的接口,工序方向传 `mode=process`(缺省即 `process`)。
|
|
|
+- **响应格式**:统一 `application/json; charset=utf-8`。出错返回 `{"error": "<msg>"}` + 对应 HTTP 状态码(400/404/500)。
|
|
|
+- **缓存**:部分 GET 带 `ETag`,命中返回 `304`;`/api/dashboard`、`/api/queries` 另有 60s 内存缓存(解构任务完成时作废)。
|
|
|
+- **解构是异步的**:发起解构/搜索/评分的 POST 立即返回 `task_id`,真正的计算在 `server.py` 起的子进程里跑;用 `GET /api/task_status` 轮询。
|
|
|
+- **版本**:工序解构按版本号 `v_MMDDHHMM` 保留历史;同版本重跑幂等覆盖,跨版本共存。`link_*` 前缀是**跨 query 复制版**(同一帖被多个 query 搜到时只真实解构一次,其余用 `link_` 复制,`cost=0`),“最新版本”默认排除 `link_*`。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 数据看板
|
|
|
+
|
|
|
+### GET /api/dashboard
|
|
|
+工序 + 工具方向的全量聚合(覆盖度 / 成本 / 进度)。带 ETag。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "result": {
|
|
|
+ "collected_by_platform": [["xhs", 120], ["gzh", 80]], // 采集帖子数 by 平台 [name,count]
|
|
|
+ "extracted_by_platform": [["xhs", 60], ["gzh", 30]], // 已解构帖子数 by 平台
|
|
|
+ "matrix_covered": 42, // 内容树命中的有效节点数(steps 的 action叶 × 输入/输出 type ∩ tier≥1)
|
|
|
+ "matrix_valid": 643, // 内容树有效(tier≥1)节点总数
|
|
|
+ "matrix_cells": [[3, 12], [5, 7]], // 命中的 [action_index, type_index] 列表
|
|
|
+ "matrix_actions": ["获取", "提取", "生成", ...], // 动作维度名(行)
|
|
|
+ "matrix_types": ["指令", "参数", ...], // 类型维度名(列)
|
|
|
+ "substance_count": 18, "substance_top": [["产品外观", 9], ...], // 实质维度覆盖
|
|
|
+ "form_count": 12, "form_top": [["纪实记录", 5], ...], // 形式维度覆盖
|
|
|
+ "post_count": 200, // 采集帖子总数
|
|
|
+ "extracted_post_count": 90, // 已解构帖子总数
|
|
|
+ "tool_count": 25, // 去重工具数(工具方向)
|
|
|
+ "via_top10": [["nano_banana", 30], ["klingai", 12]] // steps[].via 工具使用 Top10
|
|
|
+ },
|
|
|
+ "process_data": {
|
|
|
+ "run_count": 90, // 解构调用次数(按 case+version 去重)
|
|
|
+ "avg_cost": 0.0512, "total_cost": 4.61, // 成本(USD)
|
|
|
+ "avg_duration": 14.3, "total_duration": 1287.0, // 耗时(秒)
|
|
|
+ "cost_trend": [["06-17", 1.2], ["06-18", 0.9]], // 每日成本趋势
|
|
|
+ "process_progress": {"done": 60, "total": 80}, // 工序解构进度(分母=采纳帖)
|
|
|
+ "tools_progress": {"done": 30, "total": 50} // 工具解构进度
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. Query 与帖子
|
|
|
+
|
|
|
+### GET /api/queries?mode=process
|
|
|
+某方向搜索表派生的 query 列表(含工序解构进度)。带 ETag。
|
|
|
+
|
|
|
+**响应**:`Array<Query>`
|
|
|
+```jsonc
|
|
|
+[{
|
|
|
+ "query_id": "q0020",
|
|
|
+ "query_text": "人物 姿势 精准控制 怎么做",
|
|
|
+ "post_count": 20, // 该 query 搜到的帖子数
|
|
|
+ "hit_count": 8, // 达到“采纳”口径的帖子数
|
|
|
+ "process_done": 5, // 已工序解构的帖子数(distinct case)
|
|
|
+ "tools_done": 2 // 已工具解构的帖子数
|
|
|
+}]
|
|
|
+```
|
|
|
+
|
|
|
+### GET /api/posts?query_id=q0020&mode=process
|
|
|
+某 query 下的帖子列表(瘦身列,不含正文/评估大字段)。
|
|
|
+
|
|
|
+**Query 参数**:`query_id`(必填)、`mode`(缺省 `process`)。
|
|
|
+
|
|
|
+**响应**:`Array<PostListItem>`
|
|
|
+```jsonc
|
|
|
+[{
|
|
|
+ "id": 123, "query_id": "q0020", "query_text": "...",
|
|
|
+ "case_id": "xhs_abc123", // 帖子物理身份 = platform_channelContentId
|
|
|
+ "platform": "xhs", "channel_content_id": "abc123",
|
|
|
+ "title": "...", "url": "https://...", "content_type": "normal",
|
|
|
+ "images": ["https://..."], "like_count": 1024, "publish_time": "2026-06-12",
|
|
|
+ "quality_score": 8.6, "quality_grade": "A",
|
|
|
+ "found_by": ["人物 姿势 精准控制"], // 命中的措辞
|
|
|
+ "knowledge_type": ["工序", "能力"], // 评估判定的知识类型子集
|
|
|
+ "overall_score": 8.2, // (相关均值+质量均值)/2
|
|
|
+ "adopted": true, // 是否“采纳”(is_adopted_rel 口径)
|
|
|
+ "has_process": true, // 该帖是否已有工序解构
|
|
|
+ "has_tools": false // 该帖是否已有工具解构
|
|
|
+}]
|
|
|
+```
|
|
|
+
|
|
|
+### GET /api/all_posts?mode=process
|
|
|
+某方向**全部帖子**(跨所有 query),分页返回。瘦身列同 `/api/posts`(不含正文/评估大字段)。
|
|
|
+与 `/api/posts` 的区别:后者限定单个 `query_id`,本接口取该方向全表。
|
|
|
+
|
|
|
+**Query 参数**
|
|
|
+| 参数 | 必填 | 缺省 | 说明 |
|
|
|
+|---|---|---|---|
|
|
|
+| `mode` | 否 | `process` | 方向(`process` / `tools`) |
|
|
|
+| `page` | 否 | `1` | 页码(从 1 起) |
|
|
|
+| `page_size` | 否 | `100` | 每页条数(上限 500) |
|
|
|
+| `adopted` | 否 | `0` | `1`/`true` 只返回采纳帖(`is_adopted_rel` 口径) |
|
|
|
+| `distinct` | 否 | `0` | `1`/`true` 按 `case_id` 去重(同帖被多 query 搜到时只保留 `overall_score` 最高的一行) |
|
|
|
+
|
|
|
+> `page`/`page_size` 非整数返回 `400 {"error":"page/page_size 须为整数"}`。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "total": 1197, // 过滤(+去重)后的总条数(用于算总页数)
|
|
|
+ "page": 1,
|
|
|
+ "page_size": 100,
|
|
|
+ "posts": [ /* Array<PostListItem>,字段同 /api/posts(含 adopted/has_process/has_tools) */ ]
|
|
|
+}
|
|
|
+```
|
|
|
+> 默认(不去重)`total` = 该方向 `(query, 帖子)` 行总数;同一帖被 N 个 query 搜到即计 N 行。
|
|
|
+> 需“物理帖子数”时传 `distinct=1`。`has_process`/`has_tools` 为**跨 query 全局**判定(该帖在 `mode_process`/`mode_tools` 中是否有解构)。
|
|
|
+
|
|
|
+### GET /api/post?query_id=q0020&case_id=xhs_abc123&mode=process
|
|
|
+单帖完整详情(含正文 `body`、`videos`、`llm_evaluation` 全量)。带 ETag;无帖返回 `404 {"error":"无此帖"}`。
|
|
|
+
|
|
|
+**响应**:`search_process` 的整行(在 `PostListItem` 基础上额外含)
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "...": "(含 PostListItem 全部列)",
|
|
|
+ "body": "帖子正文全文",
|
|
|
+ "videos": ["https://..."],
|
|
|
+ "llm_evaluation": { /* 评估全量 blob */ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 工序解构结果
|
|
|
+
|
|
|
+### GET /api/extract?mode=process&case_id=xhs_abc123&version=
|
|
|
+**一次取**版本列表 + 指定版本的工序解构详情(前端少一次往返)。`version` 省略取最新版。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "versions": [ // 该帖的所有解构版本(link_* 排在最后)
|
|
|
+ {"version": "v_06181530", "n": 3, "model": "anthropic/claude-sonnet-4-6"}
|
|
|
+ ],
|
|
|
+ "data": { /* 见下 ProcessPayload;无解构记录时为 null */ },
|
|
|
+ "missing": false // data 为 null 时 true
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### GET /api/process_versions?case_id=xhs_abc123
|
|
|
+仅版本列表。**响应**:`Array<{version, n, model}>`(同上 `versions`)。
|
|
|
+
|
|
|
+### GET /api/process?case_id=xhs_abc123&version=
|
|
|
+单帖工序解构结果(`version` 省略取最新真实版)。无记录返回 `404 {"error":"无解构记录"}`。
|
|
|
+
|
|
|
+**响应**:`ProcessPayload`
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "case_id": "xhs_abc123",
|
|
|
+ "version": "v_06181530",
|
|
|
+ "platform": "xhs",
|
|
|
+ "title": "帖子标题",
|
|
|
+ "model": "anthropic/claude-sonnet-4-6",
|
|
|
+ "cost_usd": 0.0512,
|
|
|
+ "duration_s": 14.3,
|
|
|
+ "source": { /* 解构返回的 source 块:帖子来源信息 */ },
|
|
|
+ "procedures": [ // 一帖可含多个工序
|
|
|
+ {
|
|
|
+ "id": "p1",
|
|
|
+ "name": "AI商品主图生成",
|
|
|
+ "purpose": "生成电商可用的高质量商品主图",
|
|
|
+ "category": "产物创造", // 产物创造/资产建设/自动化/分析/学习
|
|
|
+ "declarations": { /* 工序级声明:inputs/outputs 等 */ },
|
|
|
+ "type_registry": { /* 类型注册表 */ },
|
|
|
+ "tools_used": ["nano_banana", "klingai"], // 从 steps[].via 去重
|
|
|
+ "steps": [ // 步骤数组(核心结构,见下)
|
|
|
+ {
|
|
|
+ "id": "s1",
|
|
|
+ "kind": "block", // block / nested
|
|
|
+ "group": "s5", // nested 时所属父步
|
|
|
+ "via": "nano_banana", // 外部工具;human/- 表示无工具
|
|
|
+ "effect": "主体生成",
|
|
|
+ "action": "生成/元素生成", // 动作(叶子用于覆盖度统计)
|
|
|
+ "inputs": [{"type": "提示词", "name": "风格提示词", "value": "...", "anchor": "← 工序输入"}],
|
|
|
+ "outputs": [{"id": "s1o1", "type": "成品图", "value": "<画面描述>", "anchor": "→ 交付"}]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> `steps[].action` 的叶子(按 `/` 取最后一段)× `inputs/outputs[].type` 用于 Dashboard 内容树覆盖度命中统计。
|
|
|
+
|
|
|
+### GET /api/extract_prompt?mode=process
|
|
|
+取当前工序解构 system prompt 原文(供“重新解构·编辑 Prompt”弹框预填)。
|
|
|
+
|
|
|
+**响应**:`{"prompt": "<prompts/procedure_extract_system.md 全文>"}`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 发起工序解构(异步)
|
|
|
+
|
|
|
+### POST /api/extract_process
|
|
|
+对指定 query 下的若干帖子发起**工序解构**,起子进程 `stages/procedure_extract.py`。
|
|
|
+
|
|
|
+**请求体**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "query_id": "q0020", // 必填
|
|
|
+ "case_ids": ["xhs_abc", "gzh_def"], // 必填,待解构帖子的 case_id 列表
|
|
|
+ "model": "anthropic/claude-sonnet-4-6", // 选填,覆盖默认模型
|
|
|
+ "prompt": "<临时 system prompt>", // 选填,仅本次生效,不改 prompts/*.md
|
|
|
+ "force": false // 选填,true 跳过“按 case 全局去重”,强制重解构
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**行为 / 去重**
|
|
|
+- 默认按 `case_id` **全局去重**:同一帖跨 query 只真实解构一次,已解构过的用 `db.link_process` 复制关联(`cost=0`)。换 prompt/模型要对比时传 `force:true`。
|
|
|
+- **认领锁**:剔除正在解构中的 `case`,防并发重复解构(白花 LLM 钱)。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "task_id": "proc_xxx", // 轮询用;全部被跳过时为 null
|
|
|
+ "skipped": ["gzh_def"], // 因正在解构中被跳过的 case
|
|
|
+ "note": "..." // 仅当全部跳过时出现
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 发起搜索(新增 query · 工序方向)
|
|
|
+
|
|
|
+### POST /api/run_search
|
|
|
+跑一次搜索 + 评估,起子进程 `stages/search_eval.py`。新 `query_id` 即等于“新增一个 query”。
|
|
|
+
|
|
|
+**请求体**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "query": "人物 姿势 精准控制 怎么做", // 必填,原始组合词(存入 query 列表)
|
|
|
+ "query_id": "q0021", // 选填,缺省服务端 _next_query_id() 自动分配
|
|
|
+ "query_text": "...", // 选填,实际搜索用的改写词
|
|
|
+ "synonyms": "...", // 选填,近义词
|
|
|
+ "mode_type": "工序", // 选填,"工序" / "工具";落表方向(工序→search_process)
|
|
|
+ "platforms": "xhs,gzh", // 选填,渠道;默认 xhs,gzh 各 20
|
|
|
+ "max_count": 20 // 选填,每渠道条数
|
|
|
+}
|
|
|
+```
|
|
|
+> 注:评估会按帖子的知识类型标签自动路由落表(工序/能力→`search_process`,工具→`search_tools`,两者都含则写两表),`mode_type` 是搜索方向倾向。
|
|
|
+
|
|
|
+**响应**:`{"task_id": "search_xxx", "query_id": "q0021"}`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 任务状态轮询
|
|
|
+
|
|
|
+### GET /api/task_status?task_id=proc_xxx
|
|
|
+轮询异步任务(解构 / 搜索 / 评分)状态。未知 `task_id` 返回 `404 {"error":"未知 task_id"}`。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "status": "running", // running / done / failed(见 server 任务管理)
|
|
|
+ "log_tail": "...最后 3000 字符日志..."
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### GET /api/search_progress?mode=process
|
|
|
+搜索**执行进度**:回答“1000 个 query 词要搜,已经执行了多少”。两个口径互补——
|
|
|
+- `executed`:**durable**,= 搜索表里 distinct `query_id` 数(已产出结果的 query),服务重启不丢。
|
|
|
+ 有 N 个词要搜时,进度 ≈ `executed / N`(分母 N 由调用方掌握,服务端不持久化“计划清单”)。
|
|
|
+- `session_tasks`:**本进程**生命周期内发起的 search 任务统计,重启清零(任务记录仅存内存,见 `server.py:TASKS`)。批量连搜时用来实时看“正在跑/已跑完几个”。
|
|
|
+
|
|
|
+**Query 参数**:`mode`(缺省 `process`)。
|
|
|
+
|
|
|
+**响应**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "executed": 29, // 已执行(已产出结果)的 distinct query 数 —— durable
|
|
|
+ "session_tasks": { // 本进程内 search 任务计数(重启清零)
|
|
|
+ "total": 5, "running": 1, "done": 3, "failed": 1
|
|
|
+ },
|
|
|
+ "running_queries": ["q0042"], // 正在搜索中的 query_id
|
|
|
+ "recent": [ // 最近 ≤50 个 search 任务明细(新任务在前)
|
|
|
+ {"task_id": "search_0624...", "query_id": "q0042", "query": "人物 姿势 怎么做", "status": "running"}
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+> **口径说明**:`executed` 统计的是“已写入结果的 query”,一次搜索若 **0 命中**不写行、不计入。
|
|
|
+> 若需精确的“计划 1000 → 已发起 X”进度,按 `session_tasks`(同一进程内连续 `POST /api/run_search`)统计;
|
|
|
+> 跨重启的精确批次追踪需另建“搜索计划台账”(当前未实现,按需扩展)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. Query 规则组织器(铺词 → 评分 → 搜索)
|
|
|
+
|
|
|
+用内容树维度系统化铺 query → Sonnet 评分 → 高亮达标 → 一键搜。生成的工序方向 query 走第 5 节落库。
|
|
|
+
|
|
|
+### GET /api/query_matrix
|
|
|
+正交表底图:`动作 × 类型` 整张矩阵(来自 `reference/judged_matrix.json`)。带 ETag。
|
|
|
+
|
|
|
+### GET /api/category_tree?source_type=实质
|
|
|
+分类树(`实质` / `形式`),用于维度 chips 逐级下钻(仅作 Sonnet 领域上下文)。带 ETag。
|
|
|
+
|
|
|
+### POST /api/query_score
|
|
|
+对当前维度上下文下的 tier≥1 格子用 Sonnet 打分(natural/findable/useful + keep + rewrite + reason)。
|
|
|
+
|
|
|
+**请求体**
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "tool_type": "AI", "modality": "图片", "suffix": "怎么做",
|
|
|
+ "substance_path": ["表象", "视觉"], "form_path": ["呈现", "视觉"],
|
|
|
+ "model": "anthropic/claude-sonnet-4-6",
|
|
|
+ "force": false // true 跳过缓存重评
|
|
|
+}
|
|
|
+```
|
|
|
+**响应**:`{"sel": "<16位hex>", "task_id": "score_xxx", "cached": false}`(命中缓存时 `cached:true` 且无 `task_id`)。
|
|
|
+
|
|
|
+### GET /api/query_score?sel=<16位hex>
|
|
|
+取某次评分结果(按选择哈希缓存于 `.cache/query_score/<sel>.json`)。`sel` 必须是 16 位十六进制,否则 `400`;结果未就绪返回 `202 {"pending": true}`。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 工序知识导入(反代知识库后端)
|
|
|
+
|
|
|
+### `* /api/v1/knowledge*`
|
|
|
+同源反代到 `.env` 的 `KNOWLEDGE_API_BASE`(明文后端),GET/POST 原样透传,本服务不解析 JSON。
|
|
|
+工序解构采纳后由 `stages/import_process_knowledge.py` 调此接口导入知识库;`knowledge_ingest_log` 台账按 `(case_id, proc_index)` 防重复上传,导入时记录 `mode_process` 版本,版本变了应重导。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附:工具方向(`mode=tools`)差异
|
|
|
+
|
|
|
+| 工序方向 | 工具方向 |
|
|
|
+|---|---|
|
|
|
+| `GET /api/posts?mode=process` | `GET /api/posts?mode=tools` |
|
|
|
+| `GET /api/all_posts?mode=process` | `GET /api/all_posts?mode=tools` |
|
|
|
+| `GET /api/search_progress?mode=process` | `GET /api/search_progress?mode=tools` |
|
|
|
+| `GET /api/extract?mode=process` | `GET /api/extract?mode=tools` |
|
|
|
+| `GET /api/process_versions` `GET /api/process` | `GET /api/tools_versions` `GET /api/tools` |
|
|
|
+| `POST /api/extract_process` | `POST /api/extract_tools` |
|
|
|
+| 表 `search_process` / `mode_process` | 表 `search_tools` / `mode_tools` |
|
|
|
+| 台账 `knowledge_ingest_log` | 台账 `tools_ingest_log` |
|
|
|
+
|
|
|
+工具方向 payload 字段不同(`tool_name` / `substance_scope` / `form_scope` / `creation_layer` / `cases_json` / `defects_json` 等),不在本工序文档展开。
|