Talegorithm 1 неделя назад
Родитель
Сommit
4675d90155

+ 5 - 0
agent/tools/builtin/content/__main__.py

@@ -0,0 +1,5 @@
+"""CLI 入口:`python -m agent.tools.builtin.content <cmd> [...]`"""
+from agent.tools.builtin.content.tools import cli_main
+
+if __name__ == "__main__":
+    cli_main()

+ 10 - 8
agent/tools/builtin/content/tools.py

@@ -248,31 +248,33 @@ def _parse_args(argv: list) -> dict:
     return kwargs
     return kwargs
 
 
 
 
-if __name__ == "__main__":
+def cli_main(argv: Optional[list] = None) -> None:
+    """CLI 入口:通过 `python -m agent.tools.builtin.content` 调用(见 __main__.py)"""
     import sys
     import sys
     import asyncio
     import asyncio
 
 
-    COMMANDS = {
+    argv = sys.argv if argv is None else argv
+
+    commands = {
         "platforms": content_platforms,
         "platforms": content_platforms,
         "search": content_search,
         "search": content_search,
         "detail": content_detail,
         "detail": content_detail,
         "suggest": content_suggest,
         "suggest": content_suggest,
     }
     }
 
 
-    if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
-        print(f"Usage: python {sys.argv[0]} <{'|'.join(COMMANDS)}> [--key=value ...]")
+    if len(argv) < 2 or argv[1] not in commands:
+        print(f"Usage: python -m agent.tools.builtin.content <{'|'.join(commands)}> [--key=value ...]")
         sys.exit(1)
         sys.exit(1)
 
 
-    cmd = sys.argv[1]
-    kwargs = _parse_args(sys.argv[2:])
+    cmd = argv[1]
+    kwargs = _parse_args(argv[2:])
 
 
     # trace_id:CLI 参数 > 环境变量 > 自动生成
     # trace_id:CLI 参数 > 环境变量 > 自动生成
     trace_id = kwargs.pop("trace_id", None) or os.getenv("TRACE_ID") or f"cli-{uuid.uuid4().hex[:8]}"
     trace_id = kwargs.pop("trace_id", None) or os.getenv("TRACE_ID") or f"cli-{uuid.uuid4().hex[:8]}"
     os.environ["TRACE_ID"] = trace_id
     os.environ["TRACE_ID"] = trace_id
 
 
-    result = asyncio.run(COMMANDS[cmd](**kwargs))
+    result = asyncio.run(commands[cmd](**kwargs))
 
 
-    # 输出 JSON(与 toolhub CLI 格式一致)
     out = {"trace_id": trace_id, "output": result.output, "error": result.error}
     out = {"trace_id": trace_id, "output": result.output, "error": result.error}
     if result.metadata:
     if result.metadata:
         out["metadata"] = result.metadata
         out["metadata"] = result.metadata

+ 38 - 0
knowhub/docs/capability_strategy_fields.csv

@@ -0,0 +1,38 @@
+entity,field,type,nullable,status,group,description
+capability,id,VARCHAR PK,N,existing,标识,"格式 ""CAP-XXX"",跨 version 唯一"
+capability,name,VARCHAR,N,existing,标识,"能力名称"
+capability,version,VARCHAR(32),N,existing,标识,"多租户版本,DEFAULT 'v0'"
+capability,description,TEXT,N,existing,语义,"能力是什么(综合定位描述),用于人类阅读与 embedding 输入"
+capability,method,TEXT,N,new,语义,"去参数化的做法概述(核心机理层),用于跨 case 聚类与语义召回;具体参数下沉到 capability_tool.parameters_template"
+capability,category,TEXT,Y,new,组织,"树状分类,例 ""image/portrait/identity"""
+capability,effects,TEXT[],N,new,召回,"满足的原子需求描述列表,每条形如""实现 XX 效果"";需求-能力匹配的主信号(统一了原 applicable_to 概念)"
+capability,inputs,JSONB,N,new,执行契约,"输入契约:素材类型、参数 schema、约束;agent 调用前用于校验"
+capability,outputs,JSONB,N,new,执行契约,"产出契约:格式、规格、可校验属性"
+capability,preconditions,TEXT,Y,new,执行契约,"前置条件,例 ""需先有角色 LoRA""、""≥5 张参考图"""
+capability,criterion,TEXT,Y,existing,执行,"产出评估标准(提炼结果),用于质检和 agent 重试判断"
+capability,failure_modes,TEXT,Y,new,边界,"不适用的边界,反向过滤防误召回"
+capability,maturity,VARCHAR,Y,new,置信,"experimental / validated / production"
+capability,validation_count,INTEGER,Y,new,置信,"被多少 case 验证过"
+capability,last_verified_at,BIGINT,Y,new,置信,"最近一次验证时间戳(秒)"
+capability,embedding,float4[],N,modified,向量,"name + description + method + effects 拼接的向量(原为 name + description)"
+strategy,id,VARCHAR PK,N,existing,标识,"格式 ""strategy-XXX"""
+strategy,name,VARCHAR,N,existing,标识,"工序名称"
+strategy,version,VARCHAR(32),N,existing,标识,"多租户版本"
+strategy,description,TEXT,N,existing,语义,"工序概览描述"
+strategy,body,TEXT,Y,existing,语义,"人类可读的工作流叙述(设计审阅、故障排查入口);与 steps 并存——人写 body 整理设计,agent 读 steps grounding"
+strategy,category,TEXT,Y,new,组织,"平台/介质/形态分类"
+strategy,effects,TEXT[],N,new,召回,"满足的复合需求描述列表(统一了原 goal_pattern + applicable_to)"
+strategy,inputs,JSONB,N,new,执行契约,"工序整体输入契约"
+strategy,outputs,JSONB,N,new,执行契约,"工序整体产出契约"
+strategy,preconditions,TEXT,Y,new,执行契约,"前置条件"
+strategy,steps,JSONB,N,new,执行,"结构化步骤数组:[{order, type: capability|sub_strategy|human_review|conditional, ref_id, inputs, outputs, params, quality_check, on_fail, on_success, optional}];agent 执行 grounding 的权威源;与 strategy_capability/strategy_composition junction 同步写入"
+strategy,success_metrics,JSONB,Y,new,执行,"strategy 级整体成功判据"
+strategy,expected_cost,JSONB,Y,new,执行,"估算时长/费用,多候选排序用"
+strategy,coverage_estimate,FLOAT,Y,new,边界,"对需求的覆盖度估计(0-1,业务文档明确强调)"
+strategy,maturity,VARCHAR,Y,new,置信,"experimental / validated / production"
+strategy,validation_count,INTEGER,Y,new,置信,"被多少 case 验证过"
+strategy,last_verified_at,BIGINT,Y,new,置信,"最近一次验证时间戳"
+strategy,status,VARCHAR,N,existing,管理,"draft / approved / deprecated 等"
+strategy,created_at,BIGINT,N,existing,管理,"创建时间戳(秒)"
+strategy,updated_at,BIGINT,N,existing,管理,"更新时间戳(秒)"
+strategy,embedding,float4[],N,modified,向量,"name + description + effects 拼接的向量(原为 name + description)"

+ 228 - 0
knowhub/docs/extract_capability_strategy_prompts.md

@@ -0,0 +1,228 @@
+# 能力 / 工序 提炼 Prompt(首次入库)
+
+提供两个版本:
+1. **单帖版**(Prompt 1)— 从一篇原帖产出 1 个 strategy + N 个 capabilities
+2. **批次版**(Prompt 2)— 从一批相关原帖产出多个 strategy + 跨帖合并的 capabilities
+
+何时用哪个见文末"使用建议"。
+
+---
+
+## 字段范围
+
+### 首次提炼覆盖(核心字段)
+
+| 实体 | 必填字段 | 可空字段 |
+|---|---|---|
+| capability | name, method, effects, body, inputs, outputs | criterion |
+| strategy | name, method, effects, steps(每步含 order/summary/body), inputs, outputs | criterion |
+
+### 留二轮补
+
+- `description`:基于 effects + method/body 自动合成
+- `steps[].ref_id`:对齐到正式 capability 库后绑定
+- `category`:跨 case 视角下分类
+- capability:`failure_modes`、`preconditions`
+- strategy:顶层 `body`(全局叙述/原则/坑)、`preconditions`、`expected_cost`、`coverage_estimate`
+
+入库时系统填:`id`、`version`、`created_at`、`updated_at`、`embedding`、`maturity`(默认 `experimental`)、`validation_count`(默认 1,批次版按合并的来源帖数)。
+
+每个 prompt 块都自包含、可直接复制使用。占位符由调用方填充:单帖版用 `{post_content}`,批次版用 `{posts_block}`(每篇原帖以 `[POST id=p?]\n{content}\n\n` 起头)。
+
+---
+
+## Prompt 1:单帖版
+
+```
+你是 AI 内容制作能力沉淀助手。从下方一篇原帖中,提炼能入库的"能力(capability)"和"工序(strategy)",输出严格 JSON,无任何额外文字。
+
+# 概念
+
+**capability(能力)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。
+- ✅ 例:人像角色一致性生成、三段式排版、图像超分
+- ❌ 单一 prompt 词(太细)/ "做一篇穿搭分享"(太粗)/ "发到小红书"(是 tool action)
+
+**strategy(工序)**:端到端制作流程,由能力组合而成。
+- ✅ 例:小红书穿搭分享首图制作流程
+
+判定:能在另一个工序里复用 → 能力;只能整体用 → 工序内部步骤。
+
+# 字段要点
+
+1. **method 严格去参数化**:写做法机理("用 LoRA + IP-Adapter 维持一致性"),不写具体参数(rank/alpha/weight)。具体参数留 body
+2. **body** 写具体做法:关键 prompt 文本、关键参数值、关键调用顺序
+3. **effects** 是需求/效果描述,每条形如"实现 XX 效果",不写工具名也不写做法
+4. **strategy.steps** 每步含 {order, summary, body}:summary 一句话骨架,body 该步具体做法
+5. **inputs / outputs** 用自然语言简述
+6. **criterion** 原帖有质量描述就填,没就 null
+7. **strategy 可为 null**:原帖只是单一技法分享、没有端到端流程时
+8. capability 数量参考 2-6 个,>8 可能粒度过细
+
+# 输入
+
+原帖如下:
+---
+{post_content}
+---
+
+# 输出(严格 JSON)
+
+{{
+  "skip": false,
+  "skip_reason": "",
+  "strategy": null,
+  "capabilities": []
+}}
+
+非跳过时,strategy 与 capabilities 各项填充:
+
+strategy(如有端到端流程则填,否则 null):
+{{
+  "name": "工序名",
+  "method": "工序整体方法论概述(去具体步骤序列),保留核心技术路线",
+  "effects": ["实现 XX 效果", "..."],
+  "steps": [
+    {{"order": 1, "summary": "骨架一句话", "body": "该步具体做法(prompt/参数/tip)"}}
+  ],
+  "inputs": "整体输入需要什么(自然语言)",
+  "outputs": "最终产出形态(自然语言)",
+  "criterion": null
+}}
+
+capabilities(数组,每项一个能力):
+{{
+  "name": "能力名",
+  "method": "去参数化做法概述",
+  "effects": ["实现 XX 效果", "..."],
+  "body": "具体做法(关键 prompt / 参数 / 步骤)",
+  "inputs": "输入(自然语言)",
+  "outputs": "产出(自然语言)",
+  "criterion": null
+}}
+
+# 跳过条件(满足任一设 skip=true 并写 skip_reason)
+
+- 原帖纯营销,没有具体方法 / 步骤 / 参数
+- 信息密度过低,连一个 capability 都凑不出
+- 只是结果展示,没有任何"怎么做"的描述
+```
+
+---
+
+## Prompt 2:批次版
+
+```
+你是 AI 内容制作能力沉淀助手。从下方一批相关原帖中,横向归纳出可入库的"能力(capability)"和"工序(strategy)",输出严格 JSON,无任何额外文字。
+
+# 概念
+
+**capability(能力)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。
+- ✅ 例:人像角色一致性生成、三段式排版、图像超分
+- ❌ 单一 prompt 词(太细)/ "做一篇穿搭分享"(太粗)/ "发到小红书"(是 tool action)
+
+**strategy(工序)**:端到端制作流程,由能力组合而成。
+- ✅ 例:小红书穿搭分享首图制作流程
+
+判定:能在另一个工序里复用 → 能力;只能整体用 → 工序内部步骤。
+
+# 批次提炼的核心要求
+
+1. **跨帖合并 capability**:相同或相似的能力(叫法、参数可能不同)在多帖出现 → 合并为一条
+   - method 取多帖参数的共性,去参数化要更彻底
+   - effects 合并多帖视角,覆盖更全面
+   - body 选最完整的;如各帖在参数 / prompt 上有有意义的差异,把差异点合并写进 body(标注来源帖)
+2. **strategy 通常按帖产**:每帖对应一个 strategy 草稿
+   - 例外:两帖步骤序列相似度 ≥ 70% 时合并为同一 strategy(多源)
+3. **来源标注**:每条 capability / strategy 都标 `source_post_ids`,列出来自哪些帖子(用于追溯和置信度评估)
+
+# 字段要点
+
+1. **method 严格去参数化**:批次场景下尤其重要——多帖参数不同,正好用来抽共性机理
+2. **body** 写具体做法:合并时保留多帖优点,差异参数标注来源
+3. **effects** 多帖视角合并,每条形如"实现 XX 效果"
+4. **strategy.steps** 每步含 {order, summary, body}
+5. **inputs / outputs** 自然语言简述
+6. **criterion** 原帖有就填、没就 null
+
+# 提炼步骤建议(内部思考流程,不必输出)
+
+1. 逐帖识别 strategy 草稿(端到端流程)
+2. 横向扫描所有帖子,识别重复出现的 capability 候选
+3. 合并候选 → 最终 capability:method 去参数化、effects 合并、body 选最完整
+4. 跨帖 strategy 合并:步骤序列高度相似(≥70%)才合并,否则各自保留
+5. 每条产出都填 source_post_ids
+
+# 输入
+
+原帖列表(每篇带 id 标识):
+---
+[POST id=p1]
+{post_1_content}
+
+[POST id=p2]
+{post_2_content}
+
+[POST id=p3]
+{post_3_content}
+(按需续)
+---
+
+# 输出(严格 JSON)
+
+{{
+  "skipped_posts": [
+    {{"post_id": "p?", "reason": ""}}
+  ],
+  "strategies": [
+    {{
+      "name": "工序名",
+      "source_post_ids": ["p1", "p2"],
+      "method": "工序整体方法论概述",
+      "effects": ["实现 XX 效果", "..."],
+      "steps": [
+        {{"order": 1, "summary": "骨架一句话", "body": "该步具体做法"}}
+      ],
+      "inputs": "整体输入(自然语言)",
+      "outputs": "最终产出形态",
+      "criterion": null
+    }}
+  ],
+  "capabilities": [
+    {{
+      "name": "能力名",
+      "source_post_ids": ["p1", "p3", "p5"],
+      "method": "去参数化做法概述(多帖共性)",
+      "effects": ["实现 XX 效果", "..."],
+      "body": "合并后的具体做法(差异参数可标注来源帖)",
+      "inputs": "输入(自然语言)",
+      "outputs": "产出(自然语言)",
+      "criterion": null
+    }}
+  ]
+}}
+
+# 跳过条件(per post)
+
+某帖满足任一时进 skipped_posts,不进入提炼:
+- 纯营销,无方法/步骤/参数
+- 信息密度过低
+- 只是结果展示
+```
+
+---
+
+**两步法默认建议**(适合大多数场景):
+
+1. 用单帖版跑全量原帖 → capability 标 `maturity=experimental` 进 candidate 状态,strategy 直接入库为 `status=draft`
+2. capability candidates 按 `method + effects` embedding 聚类后,再喂"合并 prompt"(独立 prompt,本文件不含)→ 簇内 ≥2 个独立 case 升级 `validated`
+
+**直接批次法**(仅当一批高度相似时):直接用批次版一次喂入相似帖子组,产出已经合并,跳过 candidate 阶段。但聚类质量决定结果,不是普适首选。
+
+---
+
+## 二轮处理 TODO(本文件不含)
+
+- description 自动合成 prompt:基于 effects + method/body 生成一句话定位
+- capability 合并 prompt:candidate 池聚类后,每簇喂入合并产稳定 capability
+- steps[].ref_id 对齐 prompt:把 strategy.steps 绑到正式 capability 库
+- 边缘字段补全:category(跨 case)、failure_modes / preconditions(人工 review)

+ 235 - 0
knowhub/docs/group_search_results_prompts copy.md

@@ -0,0 +1,235 @@
+# 搜索结果分组 Prompt(实验三档)
+
+为后续"批次提炼 capability / strategy"做前置分组,三种维度对照实验:
+1. **Prompt A** — 按 effects(解决的需求/效果)分组
+2. **Prompt B** — 按 method(核心技术机理)分组
+3. **Prompt C** — LLM 自主选维度分组
+
+每个 prompt 都自包含、可直接复制使用。占位符 `{posts_block}` 由调用方拼接,每篇原帖以 `[POST id=p?]\n{content}\n\n` 起头。
+
+---
+
+## Prompt A:按 effects 分组
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次按 effects(解决的需求 / 实现的效果)维度分组**:同组内的帖子要解决的内容制作需求相近,**即使做法、工具完全不同**也归一组。
+
+# 判据
+
+- ✅ 同组:核心要解决的需求/效果相近
+  例:以下三篇都属于"维持角色一致性"组——
+  - 用 LoRA 训练实现角色面部一致
+  - 用 IP-Adapter + reference image 锁定特征
+  - 用 ControlNet + 多角度参考图保面孔
+- ❌ 不要按工具或做法分组(那是 method 维度,不是当前任务)
+
+# effects_label 写法
+
+每组的 `effects_label` 写一句"实现 XX 效果"形式的需求描述,不写工具名、不写做法。
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "groups": [
+    {{
+      "group_id": "g1",
+      "effects_label": "实现 XX 效果(一句话需求描述)",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组(multi-effect 帖)→ member_post_ids 重复出现
+- 单成员组允许(孤儿效果,不强行合并)
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider
+- 边界 case 进 ungrouped + 写 reason,不硬塞进组
+```
+
+---
+
+## Prompt B:按 method 分组
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次按 method(核心技术机理)维度分组**:同组内的帖子用了相似的核心技术路径或机理,**即使解决的需求、应用领域完全不同**也归一组。
+
+# 判据
+
+- ✅ 同组:核心做法机理相似(不看具体参数)
+  例:以下三篇都属于"LoRA 训练 + reference image 复合"组——
+  - LoRA 训练 + reference image 维持角色一致性
+  - LoRA 训练 + reference image 维持画风一致性
+  - LoRA 训练 + reference image 控制构图
+- ✅ 同组判定看机理层:rank/alpha/weight 不同 = 同一种 method(参数差异不影响)
+- ❌ 不要按需求/效果分组(那是 effects 维度,不是当前任务)
+
+# method_label 写法
+
+每组的 `method_label` 写一句去参数化的机理概述("用 LoRA + IP-Adapter 维持一致性"),不写 rank/alpha 等具体参数。
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "groups": [
+    {{
+      "group_id": "g1",
+      "method_label": "一句话去参数化机理概述",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组(用了多种机理的帖子)→ member_post_ids 重复出现
+- 单成员组允许(孤儿机理,不强行合并)
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider
+- 边界 case 进 ungrouped + 写 reason,不硬塞进组
+```
+
+---
+
+## Prompt C:LLM 自主选维度分组
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次不指定分组维度**——你自主选择最有助于下游"批次提炼 capability / strategy"的分组方式,并在输出里说明选择理由。
+
+# 候选维度(可选其一、可组合、可自创)
+
+- effects(解决的需求 / 效果)
+- method(核心技术机理)
+- 应用领域(人像 / 风景 / 视频 / 文案 ...)
+- 平台场景(小红书 / 抖音 / 知乎 ...)
+- 工具栈(SD 系 / Flux 系 / 视频生成系 ...)
+- 内容形态(图文 / 短视频 / 长文 ...)
+- 复合维度(如 "effects + 应用领域")
+- 其他你认为更合适的
+
+# 决策原则
+
+1. 选能让"组内帖子可以横向归纳出共同 capability / strategy"的维度
+2. 跟随数据本身的内在结构——不要硬塞维度
+3. 复合维度可以但要明确写出("effects + 平台",标清楚每个维度的取值)
+4. 优先选能让组内 capability 数量集中、组间 capability 重叠少的维度
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "chosen_dimension": "你选择的分组维度 + 1-2 句理由(为何这个维度对当前数据集最合适)",
+  "groups": [
+    {{
+      "group_id": "g1",
+      "label": "一句话概括这组共性",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组 → member_post_ids 重复出现
+- 单成员组允许
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider
+- 边界 case 进 ungrouped + 写 reason
+- 如果数据集天然不适合分组(例如全部独立、各讲各的),可以输出近乎"每篇一组",并在 chosen_dimension 里说明此判断
+```
+
+---
+
+## 实验对比建议
+
+跑完三种分组后,把每组分别送给"批次提炼 prompt",按下表收集对比指标:
+
+| 指标 | 含义 | 怎么读 |
+|---|---|---|
+| 总 capability 数 | 三种分组下产出的 capability 总数 | 数量越少 + 复用越高 = 抽象越彻底 |
+| 平均 source_post_ids 长度 | 每条 capability 平均合并了几个帖子 | 越大 = 跨帖归纳越成功 |
+| 组内 capability 一致性 | 同组内 capability 的命名 / method 相似度 | 高 = 分组维度收敛性好 |
+| 组间 capability 重叠率 | 不同组产出 capability 之间的语义相似比例 | 高 = 分组维度漏抽共性,需后置 dedup |
+| LLM 自主版选维度 | Prompt C 选了什么 | 看 LLM 对数据的内在判断 |
+
+**预期假设**(待数据验证,不是结论):
+- effects 分组:组内 method 多样 → 抽出的 capability "机理通用",但数量可能偏多
+- method 分组:组内做法收敛 → capability "做法稳定",但可能错过跨方法的需求归纳
+- LLM 自主:可能选择复合维度(如 effects + 平台),适合作 baseline 反推合理维度
+
+**建议样本量**:实验阶段 15–30 篇原帖足够看出趋势;少于 10 篇分组没意义。
+
+---
+
+## 摘要降级(可选)
+
+如果原帖数量大、上下文超长,可先用"单帖提炼 prompt"产出每帖的 `name + method + effects` 摘要,再以摘要为输入跑分组(把 `[POST id=p?]` 后面的 `{content}` 替换为摘要文本即可,prompt 本体不变)。

+ 308 - 0
knowhub/docs/group_search_results_prompts.md

@@ -0,0 +1,308 @@
+# 搜索结果分组 Prompt(实验三档)
+
+为后续"批次提炼 capability / strategy"做前置分组,三种维度对照实验:
+1. **Prompt A** — 按 effects(解决的需求/效果)分组
+2. **Prompt B** — 按 method(核心技术机理)分组 ← 当前重点
+3. **Prompt C** — LLM 自主选维度分组
+
+每个 prompt 都自包含、可直接复制使用。占位符 `{posts_block}` 由调用方拼接,每篇原帖以 `[POST id=p?]\n{content}\n\n` 起头。
+
+**两条全局要求**(已写进每个 prompt):
+- **跨组帖子重复是预期**:一篇帖子如果用了多种 method / 涵盖多种 effects,应同时进入多个组;不是漏写、不是问题
+- **粒度合理**:每个组既不能过大(无法横向归纳出可复用 capability),也不能过小(绑定单一参数/场景/工具,无法迁移)
+
+---
+
+## Prompt A:按 effects 分组
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次按 effects(解决的需求 / 实现的效果)维度分组**:同组内的帖子要解决的内容制作需求相近,**即使做法、工具完全不同**也归一组。
+
+# 粒度判据
+
+每组的"合理粒度"标准——组送入下游批次提炼时应能产出 1–3 个稳定可复用的 capability。
+
+- ❌ **过大**:例如"做小红书内容"——需求太泛,组内 capability 无法收敛
+- ❌ **过小**:例如"为某博主做 4 月某条首图"——绑定单一具体场景,无法迁移
+- ✅ **合理**:例如"维持角色一致性"、"做穿搭分享首图"、"商品白底图制作"——明确的需求类型,跨账号 / 跨平台可套用
+
+# 跨组帖子重复(重要)
+
+一篇帖子如果同时涵盖多种 effects(例如教程同时讲"角色一致性"和"光影氛围渲染"),应同时进入多个组。member_post_ids 在不同组里重复出现是**预期、不是问题**。不要为了"每帖只属一组"硬切。
+
+# effects_label 写法
+
+每组的 `effects_label` 写一句"实现 XX 效果"形式的需求描述,不写工具名、不写做法(那是 method 维度的事)。
+
+# 判据细则
+
+- ✅ 同组:核心要解决的需求/效果相近
+  例:以下三篇都属于"维持角色一致性"组——
+  - 用 LoRA 训练实现角色面部一致
+  - 用 IP-Adapter + reference image 锁定特征
+  - 用 ControlNet + 多角度参考图保面孔
+- ❌ 不要按工具或做法分组(那是 method 维度,不是当前任务)
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "groups": [
+    {{
+      "group_id": "g1",
+      "effects_label": "实现 XX 效果(一句话需求描述)",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组(multi-effect 帖)→ member_post_ids 在多组中重复出现,预期行为
+- 单成员组允许(孤儿效果,不强行合并)
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider
+- 边界 case 进 ungrouped + 写 reason,不硬塞进组
+```
+
+---
+
+## Prompt B:按 method 分组 ← 重点
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次按 method(核心技术机理)维度分组**——method 分组是这次工作的重点。同组内的帖子用了相似的核心技术路径或机理,**即使解决的需求、应用领域、具体工具实例完全不同**也归一组。
+
+# 粒度判据(核心要求,请严格遵守)
+
+method 分组的"合理粒度"——分组结果送入下游"批次提炼 prompt"时,**每组应能产出 1–3 个稳定可复用的 capability**。粒度过大或过小都直接破坏下游提炼。
+
+## 过大(请避免)
+
+- ❌ "用 SD 生图"——SD 生态下做法多样(LoRA / IP-Adapter / ControlNet / textual inversion 等),混在一组无法收敛出统一机理
+- ❌ "用 LLM 做内容"——太宽泛,CoT / Agent / RAG 等不同机理混在一起
+- ❌ "图像生成"——按工具类别分,不是按机理分
+
+**过大特征**:组下含多种本质不同的机理路径,无法横向归纳出 1 个或几个共同的 capability。
+
+## 过小(请避免)
+
+- ❌ "用 LoRA rank=16 alpha=32 训练角色一致性"——参数化粒度
+- ❌ "针对小红书首图的 LoRA + IP-Adapter 复合"——绑定单一应用场景
+- ❌ "用 wanxiang 模型 + 某 prompt 模板生成人像"——绑定单一工具实例和具体 prompt
+
+**过小特征**:绑定单一参数 / 单一场景 / 单一工具实例,删去这些约束后机理就和别组重合,无法独立成组也无法跨场景迁移。
+
+## 合理(目标)
+
+- ✅ "LoRA + reference image 复合维持一致性"——明确机理 + 跨场景适用(角色一致 / 画风一致 / 构图控制)
+- ✅ "ControlNet + 参考图控制构图"——明确机理 + 跨内容形态适用
+- ✅ "多 prompt 链 + 评分 reranker 选优"——明确机理 + 跨任务适用
+- ✅ "图像分割 + 局部重绘"——明确机理 + 跨场景适用
+
+**合理特征**:
+1. method_label 描述"可独立调用的核心机理"——去参数化(不写 rank/alpha)、去场景化(不绑定小红书/抖音)、去工具实例化(不绑定某具体模型)
+2. 删去组内任意一篇帖子,剩下的帖子仍能归纳出同一 method(鲁棒性)
+3. 包含明确的"做法骨架"——不是"用 AI"这种空泛,也不是"某具体 prompt"这种死绑
+
+# 跨组帖子重复(重要)
+
+一篇帖子通常会用到多种 method(例如一个完整教程同时用了 LoRA、IP-Adapter、ControlNet、后期色调统调),**应同时进入多个 method 组**。member_post_ids 在不同组里重复出现是**预期、不是问题**。
+
+为提炼充分覆盖该帖的全部机理信号,请尽量把帖子分配到所有它能贡献的组——分组宁愿冗余覆盖,不要漏。
+
+# method_label 写法
+
+每组的 `method_label` 写一句去参数化的机理概述:
+- ✅ "用 LoRA + IP-Adapter 复合维持一致性"
+- ❌ "用 LoRA rank=16 alpha=32 训练"(含参数)
+- ❌ "用 SD"(太大,未到机理层)
+- ❌ "用 LoRA 维持小红书博主角色一致"(绑定场景)
+
+# 判据细则
+
+- ✅ 同组:核心做法机理相似(不看具体参数、不看应用场景、不看具体工具实例)
+  例:以下三篇都属于"LoRA 训练 + reference image 复合"组——
+  - LoRA 训练 + reference image 维持角色一致性(人像场景)
+  - LoRA 训练 + reference image 维持画风一致性(插画场景)
+  - LoRA 训练 + reference image 控制构图(电商场景)
+- ✅ 同组判定看机理层:rank/alpha/weight 不同 = 同一种 method,参数差异不影响分组
+- ❌ 不要按需求/效果分组(那是 effects 维度,不是当前任务)
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "groups": [
+    {{
+      "group_id": "g1",
+      "method_label": "一句话去参数化机理概述",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组(绝大多数有信息密度的帖子会涉及多种 method)→ member_post_ids 在多组中重复出现,预期行为
+- 单成员组允许(孤儿机理,不强行合并)
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider;如果数据集机理多样,可以多于此范围
+- 边界 case 进 ungrouped + 写 reason,不硬塞进组
+- 自检:写完每组后回头看 method_label——它如果含参数、含具体场景名、含具体工具实例(如某模型名),需要重写抽象到机理层
+```
+
+---
+
+## Prompt C:LLM 自主选维度分组
+
+```
+你是 AI 内容制作研究员,正在帮一个内容制作能力知识库做调研结果的预处理分组。
+
+# 项目背景
+
+我们在沉淀一个 AI 内容制作能力知识库,目标是从大量"如何用 AI 做出 X 内容"的原帖中提炼可复用的「能力(capability)」和「工序(strategy)」入库,未来供 agent 自主调用工具完成内容制作任务。
+
+- **能力(capability)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。例:人像角色一致性生成、三段式排版、图像超分。
+- **工序(strategy)**:端到端制作流程,由能力组合而成。例:小红书穿搭分享首图制作流程。
+
+# 当前任务
+
+你做的是"分组"步骤——分组结果会送给下游"批次提炼 prompt",每组独立提炼出多个 capability / strategy。**分组维度直接影响下游产出质量**:组内能横向归纳的共性越多,提炼出的 capability 越稳定、复用性越强。
+
+**本次不指定分组维度**——你自主选择最有助于下游"批次提炼 capability / strategy"的分组方式,并在输出里说明选择理由。
+
+# 候选维度(可选其一、可组合、可自创)
+
+- effects(解决的需求 / 效果)
+- method(核心技术机理)
+- 应用领域(人像 / 风景 / 视频 / 文案 ...)
+- 平台场景(小红书 / 抖音 / 知乎 ...)
+- 工具栈(SD 系 / Flux 系 / 视频生成系 ...)
+- 内容形态(图文 / 短视频 / 长文 ...)
+- 复合维度(如 "effects + 应用领域")
+- 其他你认为更合适的
+
+# 决策原则
+
+1. 选能让"组内帖子可以横向归纳出共同 capability / strategy"的维度
+2. 跟随数据本身的内在结构——不要硬塞维度
+3. 复合维度可以但要明确写出("effects + 平台",标清楚每个维度的取值)
+4. 优先选能让组内 capability 数量集中、组间 capability 重叠少的维度
+
+# 粒度判据
+
+每组的"合理粒度"——组送入下游批次提炼时应能产出 1–3 个稳定可复用的 capability。
+
+- ❌ **过大**:组内涵盖多种本质不同的内容/做法,无法收敛出共同 capability。例:"用 AI 生图"、"做小红书内容"
+- ❌ **过小**:绑定单一参数 / 单一场景 / 单一工具实例,无法跨场景迁移。例:"用 wanxiang 模型某 prompt 模板"、"为某博主做某条首图"
+- ✅ **合理**:明确的中层抽象——可独立成 capability 单元、跨场景可迁移、有具体做法骨架
+
+# 跨组帖子重复(重要)
+
+一篇帖子如果在你选的维度上能贡献到多个组(例如同时涉及多种 method、多种 effects),**应同时进入多个组**。member_post_ids 重复出现是**预期、不是问题**。
+
+# 输入
+
+每篇原帖以 `[POST id=p?]` 开头标识,content 跟随其后:
+
+---
+{posts_block}
+---
+
+# 输出(严格 JSON,无任何额外文字)
+
+{{
+  "chosen_dimension": "你选择的分组维度 + 1-2 句理由(为何这个维度对当前数据集最合适)",
+  "groups": [
+    {{
+      "group_id": "g1",
+      "label": "一句话概括这组共性",
+      "member_post_ids": ["p1", "p3"]
+    }}
+  ],
+  "ungrouped": [
+    {{"post_id": "p?", "reason": "为什么不属任何组"}}
+  ]
+}}
+
+# 通用规则
+
+- 一篇可属多组 → member_post_ids 重复出现,预期行为
+- 单成员组允许
+- 组数参考:N 篇期望 ⌈N/8⌉ ~ ⌈N/3⌉ 组,过密过稀都需 reconsider
+- 边界 case 进 ungrouped + 写 reason
+- 如果数据集天然不适合分组(例如全部独立、各讲各的),可以输出近乎"每篇一组",并在 chosen_dimension 里说明此判断
+```
+
+---
+
+## 实验对比建议
+
+跑完三种分组后,把每组分别送给"批次提炼 prompt",按下表收集对比指标:
+
+| 指标 | 含义 | 怎么读 |
+|---|---|---|
+| 总 capability 数 | 三种分组下产出的 capability 总数 | 数量越少 + 复用越高 = 抽象越彻底 |
+| 平均 source_post_ids 长度 | 每条 capability 平均合并了几个帖子 | 越大 = 跨帖归纳越成功 |
+| 组内 capability 一致性 | 同组内 capability 的命名 / method 相似度 | 高 = 分组维度收敛性好 |
+| 组间 capability 重叠率 | 不同组产出 capability 之间的语义相似比例 | 高 = 分组维度漏抽共性,需后置 dedup |
+| 帖子跨组分配率 | 平均每帖出现在多少个组 | 1.0 偏低(可能漏分),1.5–3.0 健康 |
+| 组规模分布 | 每组 member_post_ids 长度的分布 | 单峰均匀 = 粒度统一;长尾 = 有些组过大 |
+| LLM 自主版选维度 | Prompt C 选了什么 | 看 LLM 对数据的内在判断 |
+
+**预期假设**(待数据验证):
+- effects 分组:组内 method 多样 → 抽出的 capability "机理通用",但数量可能偏多
+- method 分组:组内做法收敛 → capability "做法稳定",但需重点防止粒度过大/过小(已在 prompt 中加严判据)
+- LLM 自主:可能选择复合维度(如 effects + 平台),适合作 baseline 反推合理维度
+
+**建议样本量**:实验阶段 15–30 篇原帖足够看出趋势;少于 10 篇分组没意义。
+
+---
+
+## 摘要降级(可选)
+
+如果原帖数量大、上下文超长,可先用"单帖提炼 prompt"产出每帖的 `name + method + effects` 摘要,再以摘要为输入跑分组(把 `[POST id=p?]` 后面的 `{content}` 替换为摘要文本即可,prompt 本体不变)。

+ 95 - 0
knowhub/docs/prompts/batch.md

@@ -0,0 +1,95 @@
+你是 AI 内容制作能力沉淀助手。从下方一批相关原帖中,横向归纳出可入库的"能力(capability)"和"工序(strategy)",输出严格 JSON,无任何额外文字。
+
+# 概念
+
+**capability(能力)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。
+- ✅ 例:人像角色一致性生成、三段式排版、图像超分
+- ❌ 单一 prompt 词(太细)/ "做一篇穿搭分享"(太粗)/ "发到小红书"(是 tool action)
+
+**strategy(工序)**:端到端制作流程,由能力组合而成。
+- ✅ 例:小红书穿搭分享首图制作流程
+
+判定:能在另一个工序里复用 → 能力;只能整体用 → 工序内部步骤。
+
+# 批次提炼的核心要求
+
+1. **跨帖合并 capability**:相同或相似的能力(叫法、参数可能不同)在多帖出现 → 合并为一条
+   - method 取多帖参数的共性,去参数化要更彻底
+   - effects 合并多帖视角,覆盖更全面
+   - body 选最完整的;如各帖在参数 / prompt 上有有意义的差异,把差异点合并写进 body(标注来源帖)
+2. **strategy 通常按帖产**:每帖对应一个 strategy 草稿
+   - 例外:两帖步骤序列相似度 ≥ 70% 时合并为同一 strategy(多源)
+3. **来源标注**:每条 capability / strategy 都标 `source_post_ids`,列出来自哪些帖子(用于追溯和置信度评估)
+
+# 字段要点
+
+1. **method 严格去参数化**:批次场景下尤其重要——多帖参数不同,正好用来抽共性机理
+2. **body** 写具体做法:合并时保留多帖优点,差异参数标注来源
+3. **effects** 多帖视角合并,每条形如"实现 XX 效果"
+4. **strategy.steps** 每步含 {order, summary, body}
+5. **inputs / outputs** 自然语言简述
+6. **criterion** 原帖有就填、没就 null
+
+# 提炼步骤建议(内部思考流程,不必输出)
+
+1. 逐帖识别 strategy 草稿(端到端流程)
+2. 横向扫描所有帖子,识别重复出现的 capability 候选
+3. 合并候选 → 最终 capability:method 去参数化、effects 合并、body 选最完整
+4. 跨帖 strategy 合并:步骤序列高度相似(≥70%)才合并,否则各自保留
+5. 每条产出都填 source_post_ids
+
+# 输入
+
+原帖列表(每篇带 id 标识):
+---
+[POST id=p1]
+{post_1_content}
+
+[POST id=p2]
+{post_2_content}
+
+[POST id=p3]
+{post_3_content}
+(按需续)
+---
+
+# 输出(严格 JSON)
+
+{{
+  "skipped_posts": [
+    {{"post_id": "p?", "reason": ""}}
+  ],
+  "strategies": [
+    {{
+      "name": "工序名",
+      "source_post_ids": ["p1", "p2"],
+      "method": "工序整体方法论概述",
+      "effects": ["实现 XX 效果", "..."],
+      "steps": [
+        {{"order": 1, "summary": "骨架一句话", "body": "该步具体做法"}}
+      ],
+      "inputs": "整体输入(自然语言)",
+      "outputs": "最终产出形态",
+      "criterion": null
+    }}
+  ],
+  "capabilities": [
+    {{
+      "name": "能力名",
+      "source_post_ids": ["p1", "p3", "p5"],
+      "method": "去参数化做法概述(多帖共性)",
+      "effects": ["实现 XX 效果", "..."],
+      "body": "合并后的具体做法(差异参数可标注来源帖)",
+      "inputs": "输入(自然语言)",
+      "outputs": "产出(自然语言)",
+      "criterion": null
+    }}
+  ]
+}}
+
+# 跳过条件(per post)
+
+某帖满足任一时进 skipped_posts,不进入提炼:
+- 纯营销,无方法/步骤/参数
+- 信息密度过低
+- 只是结果展示

+ 118 - 0
knowhub/docs/prompts/strategy&capability.md

@@ -0,0 +1,118 @@
+你是 AI 内容制作能力沉淀助手。从下方一篇原帖中,提炼能入库的"能力(capability)"和"工序(strategy)",并把每条能力 / 工序挂到给定**内容树**上对应的"实质"和"形式"节点;输出严格 JSON,无任何额外文字。
+
+# 概念
+
+**capability(能力)**:能独立交付产出 + 能在多个工序中复用的最小动作单元。
+- ✅ 例:人像角色一致性生成、三段式排版、图像超分
+- ❌ 单一 prompt 词(太细)/ "做一篇穿搭分享"(太粗)/ "发到小红书"(是 tool action)
+
+**strategy(工序)**:端到端制作流程,由能力组合而成。
+- ✅ 例:小红书穿搭分享首图制作流程
+
+判定:能在另一个工序里复用 → 能力;只能整体用 → 工序内部步骤。
+
+# 字段要点
+
+1. **method 严格去参数化**:写做法机理("用 LoRA + IP-Adapter 维持一致性"),不写具体参数(rank/alpha/weight)。具体参数留 body
+2. **body** 写具体做法:关键 prompt 文本、关键参数值、关键调用顺序
+3. **effects** 是需求/效果描述,每条形如"实现 XX 效果",不写工具名也不写做法
+4. **strategy.steps** 每步含 {{order, summary, body}}:summary 一句话骨架,body 该步具体做法
+5. **inputs / outputs** 用自然语言简述
+6. **criterion** 原帖有质量描述就填,没就 null
+7. **strategy 可为 null**:原帖只是单一技法分享、没有端到端流程时
+8. capability 数量参考 2-6 个,>8 可能粒度过细
+
+# apply_to(必填):把每条能力 / 工序挂到内容树
+
+每个 capability、以及非空 strategy,都必须给出 `apply_to`,回答"这件事在做什么 / 怎么做"对应到下方内容树上的具体节点。
+
+- **实质(substantive)**:内容**关于什么**,主体 / 题材 / 场景 / 情境(来自树中 `source_type=实质`)
+- **形式(formal)**:内容**怎么呈现**,叙事 / 修辞 / 排版 / 镜头 / 构图等(来自树中 `source_type=形式`)
+
+要求:
+- 实质 1–3 个,形式 1–3 个;按"最匹配"程度排序,前面的最贴
+- 只能从下方"内容树参考"中真实出现的节点选;不要编造 id;不要选意图(intent)类节点
+- **优先选叶子或近叶子的具体节点**;只有当所有候选都明显跨多个二级类时,才允许向上选到聚合节点
+- `rationale` 一句话说清"这条能力 / 工序在该节点上落地了什么"
+
+格式:
+```
+"apply_to": {{
+  "实质": [
+    {{"id": <int>, "path": "<节点路径>", "rationale": "为什么这条能力落在该节点上"}}
+  ],
+  "形式": [
+    {{"id": <int>, "path": "<节点路径>", "rationale": "..."}}
+  ]
+}}
+```
+
+# 内容树参考
+
+下方是当前内容树(execution_id=56)中所有可选的实质 / 形式节点。每行格式:`id  L<level>  <path>  | <description>`。请只从这里选。
+
+---
+{{tree_ref}}
+---
+
+# 输入:原帖
+
+---
+{{post_content}}
+---
+
+# 输出(严格 JSON)
+
+{{
+  "skip": false,
+  "skip_reason": "",
+  "strategy": null,
+  "capabilities": []
+}}
+
+非跳过时,strategy 与 capabilities 各项填充:
+
+strategy(如有端到端流程则填,否则 null):
+{{
+  "name": "工序名",
+  "method": "工序整体方法论概述(去具体步骤序列),保留核心技术路线",
+  "effects": ["实现 XX 效果", "..."],
+  "steps": [
+    {{"order": 1, "summary": "骨架一句话", "body": "该步具体做法(prompt/参数/tip)"}}
+  ],
+  "inputs": "整体输入需要什么(自然语言)",
+  "outputs": "最终产出形态(自然语言)",
+  "criterion": null,
+  "apply_to": {{
+    "实质": [{{"id": 0, "path": "...", "rationale": "..."}}],
+    "形式": [{{"id": 0, "path": "...", "rationale": "..."}}]
+  }}
+}}
+
+capabilities(数组,每项一个能力):
+{{
+  "name": "能力名",
+  "method": "去参数化做法概述",
+  "effects": ["实现 XX 效果", "..."],
+  "body": "具体做法(关键 prompt / 参数 / 步骤)",
+  "inputs": "输入(自然语言)",
+  "outputs": "产出(自然语言)",
+  "criterion": null,
+  "apply_to": {{
+    "实质": [{{"id": 0, "path": "...", "rationale": "..."}}],
+    "形式": [{{"id": 0, "path": "...", "rationale": "..."}}]
+  }}
+}}
+
+# 跳过条件(满足任一设 skip=true 并写 skip_reason)
+
+- 原帖纯营销,没有具体方法 / 步骤 / 参数
+- 信息密度过低,连一个 capability 都凑不出
+- 只是结果展示,没有任何"怎么做"的描述
+
+# JSON 输出硬规则(违反会被丢弃,必须遵守)
+
+- 字符串值内**禁止**出现 ASCII 双引号 `"`;如果你想表达引号,请改用全角引号 `"` `"` 或中文书名号 `《》`
+- 不要把 JSON 包在 ```json``` 代码块里
+- 不要任何前言、解释、Markdown 标题
+- 数字 id 用整数,不要加引号

+ 12 - 10
skills4claude/content-search/SKILL.md

@@ -16,20 +16,20 @@ description: 通过 API 快速搜索指定内容平台(可选择小红书/B站
 
 
 ```bash
 ```bash
 # 查看所有平台
 # 查看所有平台
-python -m agent.tools.builtin.content.tools platforms
+python -m agent.tools.builtin.content platforms
 
 
 # 查看指定平台的参数(支持模糊匹配:ID/中文名/别名)
 # 查看指定平台的参数(支持模糊匹配:ID/中文名/别名)
-python -m agent.tools.builtin.content.tools platforms --platform=小红书
+python -m agent.tools.builtin.content platforms --platform=小红书
 
 
 # 搜索
 # 搜索
-python -m agent.tools.builtin.content.tools search --platform=xhs --keyword=胶片摄影
-python -m agent.tools.builtin.content.tools search --platform=youtube --keyword=Claude
+python -m agent.tools.builtin.content search --platform=xhs --keyword=胶片摄影
+python -m agent.tools.builtin.content search --platform=youtube --keyword=Claude
 
 
 # 查看详情(index 来自搜索结果)
 # 查看详情(index 来自搜索结果)
-python -m agent.tools.builtin.content.tools detail --platform=xhs --index=3
+python -m agent.tools.builtin.content detail --platform=xhs --index=3
 
 
 # 搜索建议词(仅 xhs/toutiao/douyin/bili/zhihu)
 # 搜索建议词(仅 xhs/toutiao/douyin/bili/zhihu)
-python -m agent.tools.builtin.content.tools suggest --platform=xhs --keyword=摄影
+python -m agent.tools.builtin.content suggest --platform=xhs --keyword=摄影
 ```
 ```
 
 
 ## 平台专属参数
 ## 平台专属参数
@@ -37,7 +37,7 @@ python -m agent.tools.builtin.content.tools suggest --platform=xhs --keyword=摄
 通过 `--extras` 传 JSON,如小红书筛选:
 通过 `--extras` 传 JSON,如小红书筛选:
 
 
 ```bash
 ```bash
-python -m agent.tools.builtin.content.tools search \
+python -m agent.tools.builtin.content search \
   --platform=xhs --keyword=摄影 --extras='{"sort_type":"最新发布","publish_time":"近7天"}'
   --platform=xhs --keyword=摄影 --extras='{"sort_type":"最新发布","publish_time":"近7天"}'
 ```
 ```
 
 
@@ -49,11 +49,13 @@ python -m agent.tools.builtin.content.tools search \
 
 
 ```bash
 ```bash
 export TRACE_ID=my-session
 export TRACE_ID=my-session
-python -m agent.tools.builtin.content.tools search --platform=github --keyword=agent
-python -m agent.tools.builtin.content.tools detail --platform=github --index=1
+python -m agent.tools.builtin.content search --platform=github --keyword=agent
+python -m agent.tools.builtin.content detail --platform=github --index=1
 ```
 ```
 
 
 ## 注意
 ## 注意
 
 
+- 小红书等平台可能有限流措施,要完成一次搜索、拿到结果之后再启动下一次搜索
 - 磁盘缓存(用于 search → detail 复用)默认落在运行时的临时目录,由 cyber-agent 内部管理
 - 磁盘缓存(用于 search → detail 复用)默认落在运行时的临时目录,由 cyber-agent 内部管理
-- 为什么不像 toolhub 那样把脚本复制过来:content-search 是 8 个文件的子包(tools.py + registry.py + cache.py + 3 个 platform 模块 + 图片工具),复制出去维护成本高,所以保留 `-m` 形式依赖 editable install
+
+<!-- 为什么不像 toolhub 那样把脚本复制过来:content-search 是 8 个文件的子包(tools.py + registry.py + cache.py + 3 个 platform 模块 + 图片工具),复制出去维护成本高,所以保留 `-m` 形式依赖 editable install -->

+ 5 - 2
skills4claude/toolhub/SKILL.md

@@ -19,11 +19,14 @@ python <this_skill_dir>/toolhub.py health
 # 搜索可用工具(不填 keyword 返回全量)
 # 搜索可用工具(不填 keyword 返回全量)
 python <this_skill_dir>/toolhub.py search --keyword=image
 python <this_skill_dir>/toolhub.py search --keyword=image
 
 
-# 调用工具
+# 调用工具(必须指定 --output_dir 把产物落到项目内可见目录)
 python <this_skill_dir>/toolhub.py call \
 python <this_skill_dir>/toolhub.py call \
-  --tool_id=flux_gen --params='{"prompt":"a cat sitting on the moon"}'
+  --tool_id=flux_gen --params='{"prompt":"a cat sitting on the moon"}' \
+  --output_dir=/path/to/project/outputs/topic-v1
 ```
 ```
 
 
+不传 `--output_dir` 时产物会埋到 `<cwd>/.cache/toolhub_outputs/...`(隐藏目录)。调用 call 时务必传,结束后把 `saved_files` 路径告知用户。
+
 ## 图片参数
 ## 图片参数
 
 
 ### 输入(给 ToolHub)
 ### 输入(给 ToolHub)

+ 11 - 2
skills4claude/toolhub/toolhub.py

@@ -94,10 +94,15 @@ def set_trace_context(trace_id: str):
 
 
 
 
 def _get_output_dir(tool_id: str) -> Path:
 def _get_output_dir(tool_id: str) -> Path:
-    """获取图片输出目录:outputs/{trace_id}/,无 trace_id 时用时间戳"""
+    """优先级:TOOLHUB_OUTPUT_DIR 环境变量 > trace_id 子目录 > 时间戳"""
+    explicit = os.getenv("TOOLHUB_OUTPUT_DIR")
+    if explicit:
+        out_dir = Path(explicit).expanduser()
+        out_dir.mkdir(parents=True, exist_ok=True)
+        return out_dir
+
     trace_id = _trace_id_var.get("")
     trace_id = _trace_id_var.get("")
     if trace_id:
     if trace_id:
-        # trace_id 可能含 @ 等特殊字符,取前段作为目录名
         safe_id = trace_id.split("@")[0][:12] if "@" in trace_id else trace_id[:12]
         safe_id = trace_id.split("@")[0][:12] if "@" in trace_id else trace_id[:12]
         out_dir = OUTPUT_BASE_DIR / safe_id
         out_dir = OUTPUT_BASE_DIR / safe_id
     else:
     else:
@@ -737,6 +742,10 @@ if __name__ == "__main__":
 
 
     kwargs = _parse_args(sys.argv[2:])
     kwargs = _parse_args(sys.argv[2:])
 
 
+    # --output_dir CLI 参数优先于环境变量,统一通过 TOOLHUB_OUTPUT_DIR 被 _get_output_dir 消费
+    if "output_dir" in kwargs:
+        os.environ["TOOLHUB_OUTPUT_DIR"] = kwargs.pop("output_dir")
+
     # trace_id:CLI 参数 > 环境变量 > 自动生成(用于图片输出目录)
     # trace_id:CLI 参数 > 环境变量 > 自动生成(用于图片输出目录)
     trace_id = kwargs.pop("trace_id", None) or os.getenv("TRACE_ID") or f"cli-{uuid.uuid4().hex[:8]}"
     trace_id = kwargs.pop("trace_id", None) or os.getenv("TRACE_ID") or f"cli-{uuid.uuid4().hex[:8]}"
     set_trace_context(trace_id)
     set_trace_context(trace_id)