Kaynağa Gözat

add knowledge agent and reconstruct database

guantao 19 saat önce
ebeveyn
işleme
21764bb31e
65 değiştirilmiş dosya ile 10309 ekleme ve 487 silme
  1. 6 1
      .env.template
  2. 4 1
      .gitignore
  3. 132 46
      agent/core/prompts/knowledge.py
  4. 112 70
      agent/core/runner.py
  5. 11 6
      agent/tools/builtin/__init__.py
  6. 23 0
      agent/tools/builtin/context.py
  7. 14 0
      agent/tools/builtin/knowledge.py
  8. 199 0
      agent/tools/builtin/knowledge_manager.py
  9. 0 0
      data/agent_research/windows/20260330_194214_cd57a0/chatbox.jsonl
  10. 0 1
      data/agent_research/windows/20260330_194214_cd57a0/in_pending.json
  11. 0 0
      data/agent_research/windows/20260330_194214_cd57a0/out_pending.jsonl
  12. 27 27
      examples/content_tree_analyst/analyst.prompt
  13. 58 0
      examples/new_search/config.py
  14. 63 0
      examples/new_search/new_search.prompt
  15. 206 0
      examples/new_search/run.py
  16. 85 0
      examples/tool_research/atomic_cap/0/atomic_capabilities.md
  17. 3 0
      examples/tool_research/atomic_cap/0/atomic_capabilities_detail.json
  18. 110 0
      examples/tool_research/atomic_cap/0/atomic_capabilities_index.json
  19. 224 0
      examples/tool_research/atomic_cap/1/atomic_capabilities.md
  20. 3 0
      examples/tool_research/atomic_cap/1/atomic_capabilities_detail.json
  21. 8 3
      examples/tool_research/config.py
  22. 52 0
      examples/tool_research/extract_atomic_capabilities.prompt
  23. 325 0
      examples/tool_research/extract_capabilities_auto.py
  24. 3191 0
      examples/tool_research/requirements_sorted.json
  25. 1 1
      examples/tool_research/research.prompt
  26. 29 0
      examples/tool_research/run.py
  27. 63 4
      examples/tool_research/tool_research.prompt
  28. 339 0
      examples/tool_research/tool_results/ComfyUI/使用介绍.md
  29. 89 0
      examples/tool_research/tool_results/ComfyUI/实际用例.md
  30. 100 0
      examples/tool_research/tool_results/FLUX2_max/使用介绍.md
  31. 76 0
      examples/tool_research/tool_results/FLUX2_max/实际用例.md
  32. 87 0
      examples/tool_research/tool_results/Midjourney_v8/使用介绍.md
  33. 63 0
      examples/tool_research/tool_results/Midjourney_v8/实际用例.md
  34. 139 0
      examples/tool_research/tool_results/Nano_Banana_Pro/使用介绍.md
  35. 124 0
      examples/tool_research/tool_results/Nano_Banana_Pro/实际用例.md
  36. 93 0
      examples/tool_research/tool_results/Seedream_5.0_Lite/使用介绍.md
  37. 87 0
      examples/tool_research/tool_results/Seedream_5.0_Lite/实际用例.md
  38. 27 0
      im-client/client.py
  39. 75 0
      im-server/contact_store.py
  40. 128 0
      im-server/main.py
  41. 23 0
      im-server/protocol.py
  42. 100 0
      knowhub/agents/README.md
  43. 282 0
      knowhub/agents/knowledge_manager.prompt
  44. 371 0
      knowhub/agents/knowledge_manager.py
  45. 279 0
      knowhub/agents/knowledge_manager_v2.py
  46. 64 0
      knowhub/agents/librarian.prompt
  47. 61 0
      knowhub/agents/run_knowledge_manager.py
  48. 89 0
      knowhub/docs/migration-guide.md
  49. 105 0
      knowhub/internal_tools/README.md
  50. 48 0
      knowhub/internal_tools/__init__.py
  51. 418 0
      knowhub/internal_tools/cache_manager.py
  52. 188 12
      knowhub/kb_manage_prompts.py
  53. 196 96
      knowhub/knowhub_db/README.md
  54. 80 0
      knowhub/knowhub_db/check_table_structure.py
  55. 84 0
      knowhub/knowhub_db/clean_tool_research_data.py
  56. 148 0
      knowhub/knowhub_db/import_initial_data.py
  57. 307 0
      knowhub/knowhub_db/import_tool_research_data.py
  58. 72 0
      knowhub/knowhub_db/kill_db_locks.py
  59. 84 0
      knowhub/knowhub_db/migrate_knowledge.py
  60. 405 0
      knowhub/knowhub_db/migrate_tables_v2.py
  61. 134 0
      knowhub/knowhub_db/pg_requirement_store.py
  62. 27 17
      knowhub/knowhub_db/pg_store.py
  63. 56 0
      knowhub/knowhub_db/test_imports.py
  64. 281 0
      knowhub/knowhub_db/update_case_knowledge.py
  65. 31 202
      knowhub/server.py

+ 6 - 1
.env.template

@@ -7,4 +7,9 @@ QWEN_API_KEY=
 
 # sonnet模型
 OPEN_ROUTER_API_KEY=
-
+# postgreSQL database配置
+KNOWHUB_DB=gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com
+KNOWHUB_PORT=5432
+KNOWHUB_USER=aiddit_aigc
+KNOWHUB_PASSWORD=%a&&yqNxg^V1$toJ*WOa^-b^X=QJ
+KNOWHUB_DB_NAME=knowledge_hub

+ 4 - 1
.gitignore

@@ -78,4 +78,7 @@ examples/research/
 knowhub/milvus_data/
 
 # Vendor (non-submodule)
-vendor/browser-use/
+vendor/browser-use/
+
+# im-client data
+data/

+ 132 - 46
agent/core/prompts/knowledge.py

@@ -5,46 +5,70 @@
 - REFLECT_PROMPT:            压缩时阶段性反思(消息量超阈值,对当前批历史提炼)
 - COMPLETION_REFLECT_PROMPT: 任务完成后全局复盘(对整个任务的全局视角)
 
-两个 prompt 都要求 LLM 直接调用 `knowledge_save` 工具保存经验,
+两个 prompt 都要求 LLM 直接调用 `upload_knowledge` 工具保存经验,
 而不是输出结构化文本再由 runner 解析。
 """
 
 # ===== 压缩时阶段性反思 =====
 
-REFLECT_PROMPT = """请回顾以上执行过程,将值得沉淀的经验直接用 `knowledge_save` 工具保存到知识库。
+REFLECT_PROMPT = """请回顾以上执行过程,将值得沉淀的内容直接用 `upload_knowledge` 工具保存到知识库。
 
-**关注以下方面**:
+## 两种保存模式
+
+### 模式 1:经验反思(experience)
+总结执行过程中的经验教训,关注:
 1. 人工干预:用户中途的指令说明了哪里出了问题
 2. 弯路:哪些尝试是不必要的,有没有更直接的方法
 3. 好的决策:哪些判断和选择是正确的,值得记住
 4. 工具使用:哪些工具用法是高效的,哪些可以改进
-5. **资源发现**:是否发现了有价值的资源需要保存(见下方说明)
-
-**每条经验调用一次 `knowledge_save`,参数说明**:
-- `task`: 这条经验适用的场景,格式:「在[什么情境]下,[要完成什么]」
-- `content`: 具体经验内容,格式:「当[条件]时,应该[动作](原因:[一句话])。案例:[具体案例]」
-- `types`: 选 `["strategy"]`;如果涉及工具用法也可加 `"tool"`
-- `tags`: 用 `intent`(任务意图)和 `state`(环境状态/相关工具名)标注,便于检索
-- `score`: 1-5,根据这条经验的价值评估
-- `resource_ids`: 如果关联了资源,填写资源 ID 列表(可选)
-
-**资源提取指南**:
-如果执行过程中涉及以下内容,应先用 `resource_save` 保存资源,再用 `knowledge_save` 提交相关的经验/知识:
-
-1. **复杂代码工具**(逻辑复杂、超过 100 行):
-   - 调用 `resource_save(resource_id="code/{category}/{name}", title="...", body="代码内容", content_type="code", metadata={"language": "python"})`
-   - 然后在 `knowledge_save` 中通过 `resource_ids=["code/{category}/{name}"]` 关联
 
-2. **账号密码凭证**:
-   - 调用 `resource_save(resource_id="credentials/{website}", title="...", body="使用说明", secure_body="账号:xxx\\n密码:xxx", content_type="credential", metadata={"acquired_at": "2026-03-06T10:00:00Z"})`
-   - 然后在 `knowledge_save` 中通过 `resource_ids=["credentials/{website}"]` 关联
-
-3. **Cookie 和登录态**:
-   - 调用 `resource_save(resource_id="cookies/{website}", title="...", body="获取方法", secure_body="cookie内容", content_type="cookie", metadata={"acquired_at": "...", "expires_at": "..."})`
-   - 然后在 `knowledge_save` 中通过 `resource_ids=["cookies/{website}"]` 关联
-
-4. **多资源引用**:
-   - 一个知识可以关联多个资源,如:`resource_ids=["code/selenium/login", "credentials/website_a"]`
+**格式要求**:
+- `主题`: 「在[什么情境]下,[要完成什么]」
+- `内容`: 「当[条件]时,应该[动作](原因:[一句话])。案例:[具体案例]」
+- `类型`: `["experience"]`
+- `标签`: `{"intent": "任务意图", "state": "环境状态/工具名"}`
+- `评分`: 1-5(只保存最有价值的,宁少勿滥)
+
+### 模式 2:原始知识上传(tool/strategy/case)
+如果执行过程中**调研或发现了新知识**(如工具用法、工作流程、案例),直接上传原始知识:
+
+**要求**:
+- **完整性**:保留原始信息,不要过度总结
+- **来源清晰**:在 `resource_ids` 中关联来源资源,或在 `标签` 中标注来源
+- **原汁原味**:保持原文档/网页的结构和细节
+
+**知识类型选择**:
+- `["tool"]`:工具知识(单个工具的功能、参数、用法、限制)
+- `["strategy"]`:工序知识(多步骤流程、方案、最佳实践)
+- `["case"]`:用例知识(真实案例、应用场景、效果数据)
+
+**格式要求**:
+- `主题`: 知识的标题(如「Midjourney 的 --ar 参数用法」)
+- `内容`: 原始知识内容(完整、详细、保留结构)
+- `类型`: `["tool"]` / `["strategy"]` / `["case"]`
+- `标签`: `{"source": "来源网站/文档", "domain": "领域", ...}`
+- `resource_ids`: 关联的资源 ID(如果保存了原始文档)
+- `评分`: 1-5(根据知识的价值和可靠性)
+
+## 参数说明
+
+**每条内容调用一次 `upload_knowledge`**:
+- `data`: 包含 knowledge/resources/tools 的字典
+  - `knowledge`: 知识列表,每个知识包含:
+    - `主题`: 标题或场景描述
+    - `内容`: 知识正文(经验用总结格式,原始知识保持完整)
+    - `类型`: `["experience"]` / `["tool"]` / `["strategy"]` / `["case"]`
+    - `标签`: 键值对标签,便于检索
+    - `评分`: 1-5
+    - `resource_ids`: 关联的资源 ID 列表(可选)
+  - `resources`: 资源列表(可选),每个资源包含:
+    - `id`: 资源 ID(如 `code/{category}/{name}`)
+    - `标题`: 资源标题
+    - `内容`: 资源内容
+    - `类型`: code/credential/cookie 等
+    - `元数据`: 额外信息
+  - `tools`: 工具列表(可选)
+- `finalize`: False(增量上传,不立即入库)
 
 **注意**:
 - 只保存最有价值的经验,宁少勿滥;一次就成功或比较简单的经验就不要记录了,记录反复尝试或被用户指导后才成功的经验、或者是调研之后的收获。
@@ -56,37 +80,99 @@ REFLECT_PROMPT = """请回顾以上执行过程,将值得沉淀的经验直接
 
 # ===== 任务完成后全局复盘 =====
 
-COMPLETION_REFLECT_PROMPT = """请对整个任务进行复盘,将值得沉淀的经验直接用 `knowledge_save` 工具保存到知识库。
+COMPLETION_REFLECT_PROMPT = """请对整个任务进行复盘,将值得沉淀的内容直接用 `upload_knowledge` 工具保存到知识库。
+
+## 两种保存模式
 
-与压缩时的阶段性反思不同,这是任务结束后的全局视角,关注:
+### 模式 1:经验反思(experience)
+任务结束后的全局视角,关注:
 1. 任务整体路径:实际走的路径与最初计划的偏差
 2. 关键决策点:哪些决策显著影响了最终结果
 3. 可复用的模式:哪些做法在类似任务中可以直接复用
 4. 踩过的坑:哪些问题本可提前规避
-5. **资源沉淀**:任务中产生或发现的有价值资源(见下方说明)
 
-**每条经验调用一次 `knowledge_save`,参数说明**:
-- `task`: 这条经验适用的场景,格式:「在[什么情境]下,[要完成什么]」
-- `content`: 具体经验内容,格式:「当[条件]时,应该[动作](原因:[一句话])。案例:[具体案例]」
-- `types`: 选 `["strategy"]`;如果涉及工具用法也可加 `"tool"`
-- `tags`: 用 `intent`(任务意图)和 `state`(环境状态/相关工具名)标注,便于检索
-- `score`: 1-5,根据这条经验的价值评估
-- `resource_ids`: 如果关联了资源,填写资源 ID 列表(可选)
+**格式要求**:
+- `主题`: 「在[什么情境]下,[要完成什么]」
+- `内容`: 「当[条件]时,应该[动作](原因:[一句话])。案例:[具体案例]」
+- `类型`: `["experience"]`
+- `标签`: `{"intent": "任务意图", "state": "环境状态/工具名"}`
+- `评分`: 1-5(只保存最有价值的,宁少勿滥)
+
+### 模式 2:原始知识上传(tool/strategy/case)
+如果任务过程中**调研或发现了新知识**,直接上传原始知识:
+
+**要求**:
+- **完整性**:保留原始信息的完整结构和细节,不要过度压缩
+- **来源清晰**:标注信息来源(URL、文档名、API 响应等)
+- **原汁原味**:保持原始数据格式(如 API 参数列表、配置示例、步骤说明等)
+
+**知识类型选择**:
+- `["tool"]`:工具知识(工具的功能、参数、用法、限制、版本信息)
+- `["strategy"]`:工序知识(完整的多步骤流程、方案、最佳实践)
+- `["case"]`:用例知识(真实案例、应用场景、效果数据、对比结果)
+
+**格式要求**:
+- `主题`: 知识的标题
+- `内容`: 原始知识内容(完整详细)
+- `类型`: `["tool"]` / `["strategy"]` / `["case"]`
+- `标签`: `{"source": "来源", "domain": "领域", ...}`
+- `resource_ids`: 关联的资源 ID
+- `评分`: 1-5
+
+## 参数说明
+
+**每条内容调用一次 `upload_knowledge`**:
+- `data`: 包含 tools/resources/knowledge 的字典
+  - `knowledge`: 知识列表,每个知识包含:
+    - `主题`: 这条经验适用的场景,格式:「在[什么情境]下,[要完成什么]」
+    - `内容`: 具体经验内容,格式:「当[条件]时,应该[动作](原因:[一句话])。案例:[具体案例]」
+    - `类型`: 知识类型,选择以下之一:
+      - `["experience"]`: 执行经验(Agent 反思总结,应该/避免做什么)
+      - `["strategy"]`: 工序知识(多步骤流程、方案)
+      - `["tool"]`: 工具知识(单个工具的功能、用法)
+      - `["case"]`: 用例知识(真实案例、应用场景)
+    - `标签`: 用 `intent`(任务意图)和 `state`(环境状态/相关工具名)标注,便于检索
+    - `评分`: 1-5,根据这条经验的价值评估
+    - `resource_ids`: 关联的资源 ID 列表(可选,如果这条知识引用了某个资源)
+  - `resources`: 资源列表(可选)
+  - `tools`: 工具列表(可选)
+- `finalize`: False(增量上传,不立即入库)
 
 **资源提取指南**:
-如果任务中涉及以下内容,应先用 `resource_save` 保存资源,再用 `knowledge_save` 关联:
+如果任务中涉及以下内容,应在 `data` 中包含 `resources` 字段
 
 1. **复杂代码工具**(逻辑复杂、超过 20 行、可复用):
-   - 调用 `resource_save(resource_id="code/{category}/{name}", title="...", body="代码内容", content_type="code", metadata={"language": "python"})`
-   - 然后在 `knowledge_save` 中通过 `resource_id` 关联
+   ```python
+   {
+     "id": "code/{category}/{name}",
+     "标题": "...",
+     "内容": "代码内容",
+     "类型": "code",
+     "元数据": {"language": "python"}
+   }
+   ```
 
 2. **账号密码凭证**:
-   - 调用 `resource_save(resource_id="credentials/{website}", title="...", body="使用说明", secure_body="账号:xxx\\n密码:xxx", content_type="credential", metadata={"acquired_at": "2026-03-06T10:00:00Z"})`
-   - 然后在 `knowledge_save` 中通过 `secure_resource_id` 关联
+   ```python
+   {
+     "id": "credentials/{website}",
+     "标题": "...",
+     "内容": "使用说明和凭证",
+     "类型": "credential",
+     "元数据": {"acquired_at": "2026-03-06T10:00:00Z"}
+   }
+   ```
 
 3. **Cookie 和登录态**:
-   - 调用 `resource_save(resource_id="cookies/{website}", title="...", body="获取方法", secure_body="cookie内容", content_type="cookie", metadata={"acquired_at": "...", "expires_at": "..."})`
-   - 然后在 `knowledge_save` 中通过 `secure_resource_id` 关联
+   ```python
+   {
+     "id": "cookies/{website}",
+     "标题": "...",
+     "内容": "获取方法和cookie内容",
+     "类型": "cookie",
+     "元数据": {"acquired_at": "...", "expires_at": "..."}
+   }
+   ```
 
 **注意**:
 - 只保存最有价值的经验,宁少勿滥;一次就成功或比较简单的经验就不要记录了,记录反复尝试或被用户指导后才成功的经验、或者是调研之后的收获。

+ 112 - 70
agent/core/runner.py

@@ -171,13 +171,15 @@ BUILTIN_TOOLS = [
     "import_content",
 
     # 知识管理工具
-    "knowledge_search",
-    "knowledge_save",
-    "knowledge_update",
-    "knowledge_batch_update",
-    "knowledge_list",
-    "knowledge_slim",
-
+    "ask_knowledge",
+    "upload_knowledge",
+    # "knowledge_search",
+    # "knowledge_save",
+    # "knowledge_update",
+    # "knowledge_batch_update",
+    # "knowledge_list",
+    # "knowledge_slim",
+    
 
     # 沙箱工具
     # "sandbox_create_environment",
@@ -244,7 +246,7 @@ class CallResult:
 
 # ===== 执行引擎 =====
 
-CONTEXT_INJECTION_INTERVAL = 10  # 每 N 轮注入一次 GoalTree + Collaborators
+CONTEXT_INJECTION_INTERVAL = 5  # 每 N 轮注入一次 GoalTree + Collaborators + IM 通知
 
 
 class AgentRunner:
@@ -266,6 +268,7 @@ class AgentRunner:
         skills_dir: Optional[str] = None,
         goal_tree: Optional[GoalTree] = None,
         debug: bool = False,
+        logger_name: Optional[str] = None,
     ):
         """
         初始化 AgentRunner
@@ -278,6 +281,7 @@ class AgentRunner:
             skills_dir: Skills 目录路径
             goal_tree: 初始 GoalTree(可选)
             debug: 保留参数(已废弃)
+            logger_name: 自定义日志名称(如 "agents.knowledge_manager"),默认用模块名
         """
         self.trace_store = trace_store
         self.tools = tool_registry or get_tool_registry()
@@ -286,6 +290,7 @@ class AgentRunner:
         self.skills_dir = skills_dir
         self.goal_tree = goal_tree
         self.debug = debug
+        self.log = logging.getLogger(logger_name) if logger_name else logger
         self.stdin_check: Optional[Callable] = None  # 由外部设置,用于子 agent 执行期间检查 stdin
         self._cancel_events: Dict[str, asyncio.Event] = {}  # trace_id → cancel event
 
@@ -367,7 +372,7 @@ class AgentRunner:
                 yield event
 
         except Exception as e:
-            logger.error(f"Agent run failed: {e}")
+            self.log.error(f"Agent run failed: {e}")
             tid = config.trace_id or (trace.trace_id if trace else None)
             if self.trace_store and tid:
                 # 读取当前 last_sequence 作为 head_sequence,确保续跑时能加载完整历史
@@ -719,7 +724,7 @@ class AgentRunner:
                         branch_id=side_branch_ctx.branch_id,
                         content=msg_dict.get("content"),
                     )
-                    logger.info(f"用户在侧分支 {side_branch_ctx.type} 中追加消息")
+                    self.log.info(f"用户在侧分支 {side_branch_ctx.type} 中追加消息")
                 else:
                     stored_msg = Message.from_llm_dict(
                         msg_dict, trace_id=trace_id, sequence=sequence,
@@ -785,7 +790,7 @@ class AgentRunner:
         for threshold in [30, 50, 80]:
             if progress_pct >= threshold and threshold not in self._context_warned[trace_id]:
                 self._context_warned[trace_id].add(threshold)
-                logger.warning(
+                self.log.warning(
                     f"Context 使用率达到 {threshold}%: {token_count:,} / {max_tokens:,} tokens ({msg_count} 条消息)"
                 )
 
@@ -814,7 +819,7 @@ class AgentRunner:
                     trace.context["knowledge_eval_trigger"] = "compression"
                     await self.trace_store.update_trace(trace_id, context=trace.context)
 
-                logger.info(f"[Knowledge Eval] 压缩前触发知识评估,待评估: {len(pending)} 条")
+                self.log.info(f"[Knowledge Eval] 压缩前触发知识评估,待评估: {len(pending)} 条")
                 return history, head_seq, sequence, True
 
         # 知识提取:在任何压缩发生前,用完整 history 做反思(进入反思侧分支)
@@ -832,18 +837,18 @@ class AgentRunner:
                 )
                 compressed_msgs = compress_completed_goals(main_path_msgs, goal_tree)
                 if len(compressed_msgs) < len(main_path_msgs):
-                    logger.info(
+                    self.log.info(
                         "Level 1 压缩: %d -> %d 条消息",
                         len(main_path_msgs), len(compressed_msgs),
                     )
                     history = [msg.to_llm_dict() for msg in compressed_msgs]
                 else:
-                    logger.info(
+                    self.log.info(
                         "Level 1 压缩: 无可过滤消息 (%d 条全部保留)",
                         len(main_path_msgs),
                     )
         elif needs_compression:
-            logger.warning(
+            self.log.warning(
                 "Token 数 (%d) 超过阈值,但无法执行 Level 1 压缩(缺少 store 或 goal_tree,或 goal_compression=none)",
                 token_count,
             )
@@ -855,7 +860,7 @@ class AgentRunner:
         needs_level2 = token_count_after > max_tokens
 
         if needs_level2:
-            logger.info(
+            self.log.info(
                 "Level 1 后仍超阈值 (token=%d/%d),需要进入压缩侧分支",
                 token_count_after, max_tokens,
             )
@@ -866,7 +871,7 @@ class AgentRunner:
             return history, head_seq, sequence, True
 
         # 压缩完成后,输出最终发给模型的消息列表
-        logger.info("Level 1 压缩完成,发送给模型的消息列表:")
+        self.log.info("Level 1 压缩完成,发送给模型的消息列表:")
         for idx, msg in enumerate(history):
             role = msg.get("role", "unknown")
             content = msg.get("content", "")
@@ -876,7 +881,7 @@ class AgentRunner:
                 preview = f"[{len(content)} blocks]"
             else:
                 preview = str(content)[:100]
-            logger.info(f"  [{idx}] {role}: {preview}")
+            self.log.info(f"  [{idx}] {role}: {preview}")
 
         return history, head_seq, sequence, False
 
@@ -957,7 +962,7 @@ class AgentRunner:
     ) -> str:
         """单次 LLM 调用生成压缩摘要,返回 summary 文本"""
 
-        logger.info("执行单次 LLM 压缩")
+        self.log.info("执行单次 LLM 压缩")
 
         # 构建压缩 prompt(使用 SINGLE_TURN_PROMPT)
         from agent.core.prompts import build_single_turn_prompt
@@ -1060,7 +1065,7 @@ class AgentRunner:
                 attempt += '}' * max(0, open_braces) + ']' * max(0, open_brackets)
                 result = json.loads(attempt)
                 if isinstance(result, dict):
-                    logger.info(f"[JSON Fix] 成功修复 JSON (suffix={repr(suffix)})")
+                    self.log.info(f"[JSON Fix] 成功修复 JSON (suffix={repr(suffix)})")
                     return result
             except json.JSONDecodeError:
                 continue
@@ -1110,7 +1115,7 @@ class AgentRunner:
                     max_turns=side_branch_data.get("max_turns", config.side_branch_max_turns),
                 )
 
-                logger.info(
+                self.log.info(
                     f"恢复未完成的侧分支: {side_branch_ctx.type}, "
                     f"max_turns={side_branch_ctx.max_turns}"
                 )
@@ -1135,7 +1140,7 @@ class AgentRunner:
             # 检查取消信号
             cancel_event = self._cancel_events.get(trace_id)
             if cancel_event and cancel_event.is_set():
-                logger.info(f"Trace {trace_id} stopped by user")
+                self.log.info(f"Trace {trace_id} stopped by user")
                 if self.trace_store:
                     await self.trace_store.update_trace(
                         trace_id,
@@ -1163,7 +1168,7 @@ class AgentRunner:
                     await self.trace_store.update_trace(trace_id, context=trace.context)
                     # 设置侧分支队列
                     config.force_side_branch = ["knowledge_eval"]
-                    logger.info("[Knowledge Eval] 检测到Goal完成触发,进入知识评估侧分支")
+                    self.log.info("[Knowledge Eval] 检测到Goal完成触发,进入知识评估侧分支")
 
             # Context 管理(仅主路径)
             needs_enter_side_branch = False
@@ -1175,7 +1180,7 @@ class AgentRunner:
                 # 检查是否强制进入侧分支(API 手动触发或自动压缩流程)
                 if config.force_side_branch:
                     needs_enter_side_branch = True
-                    logger.info(f"强制进入侧分支: {config.force_side_branch}")
+                    self.log.info(f"强制进入侧分支: {config.force_side_branch}")
                 else:
                     # 正常的 context 管理逻辑
                     history, head_seq, sequence, needs_enter_side_branch = await self._manage_context_usage(
@@ -1193,7 +1198,7 @@ class AgentRunner:
                 branch_type: Literal["compression", "reflection", "knowledge_eval"]
                 if config.force_side_branch and isinstance(config.force_side_branch, list) and len(config.force_side_branch) > 0:
                     branch_type = config.force_side_branch.pop(0)  # type: ignore
-                    logger.info(f"从队列取出侧分支: {branch_type}, 剩余队列: {config.force_side_branch}")
+                    self.log.info(f"从队列取出侧分支: {branch_type}, 剩余队列: {config.force_side_branch}")
                 elif config.knowledge.enable_extraction:
                     # 兼容旧的单值模式(如果 force_side_branch 是字符串)
                     branch_type = "reflection"
@@ -1266,7 +1271,7 @@ class AgentRunner:
                 head_seq = sequence
                 sequence += 1
 
-                logger.info(f"进入侧分支: {branch_type}, branch_id={branch_id}")
+                self.log.info(f"进入侧分支: {branch_type}, branch_id={branch_id}")
                 continue  # 跳过本轮,下一轮开始侧分支
 
             # 构建 LLM messages(注入上下文)
@@ -1321,7 +1326,7 @@ class AgentRunner:
                         "type": "function",
                         "function": {"name": "get_current_context", "arguments": "{}"}
                     })
-                    logger.info(f"[周期性注入] 自动添加 get_current_context 工具调用 (iteration={iteration})")
+                    self.log.info(f"[周期性注入] 自动添加 get_current_context 工具调用 (iteration={iteration})")
 
 
             # 按需自动创建 root goal(仅主路径)
@@ -1330,7 +1335,7 @@ class AgentRunner:
                     tc.get("function", {}).get("name") == "goal"
                     for tc in tool_calls
                 )
-                logger.debug(f"[Auto Root Goal] Before tool execution: goal_tree.goals={len(goal_tree.goals)}, has_goal_call={has_goal_call}, tool_calls={[tc.get('function', {}).get('name') for tc in tool_calls]}")
+                self.log.debug(f"[Auto Root Goal] Before tool execution: goal_tree.goals={len(goal_tree.goals)}, has_goal_call={has_goal_call}, tool_calls={[tc.get('function', {}).get('name') for tc in tool_calls]}")
                 if not has_goal_call:
                     mission = goal_tree.mission
                     root_desc = mission[:200] if len(mission) > 200 else mission
@@ -1342,9 +1347,9 @@ class AgentRunner:
                     if self.trace_store:
                         await self.trace_store.add_goal(trace_id, goal_tree.goals[0])
                         await self.trace_store.update_goal_tree(trace_id, goal_tree)
-                    logger.info(f"自动创建 root goal: {goal_tree.goals[0].id}(未自动 focus,等待模型决定)")
+                    self.log.info(f"自动创建 root goal: {goal_tree.goals[0].id}(未自动 focus,等待模型决定)")
                 else:
-                    logger.debug(f"[Auto Root Goal] 检测到 goal 工具调用,跳过自动创建")
+                    self.log.debug(f"[Auto Root Goal] 检测到 goal 工具调用,跳过自动创建")
 
             # 获取当前 goal_id
             current_goal_id = goal_tree.current_id if (goal_tree and goal_tree.current_id) else None
@@ -1420,7 +1425,7 @@ class AgentRunner:
                             },
                             trigger_event=trigger_event
                         )
-                    logger.info(f"[Knowledge Eval] 已写入 {len(eval_results.get('evaluations', []))} 条评估结果")
+                    self.log.info(f"[Knowledge Eval] 已写入 {len(eval_results.get('evaluations', []))} 条评估结果")
 
             # 如果在侧分支,记录到 assistant_msg(已持久化,不需要额外维护)
 
@@ -1435,7 +1440,7 @@ class AgentRunner:
                 should_exit = turns_in_branch >= side_branch_ctx.max_turns or not tool_calls
 
                 if turns_in_branch >= side_branch_ctx.max_turns:
-                    logger.warning(
+                    self.log.warning(
                         f"侧分支 {side_branch_ctx.type} 达到最大轮次 "
                         f"{side_branch_ctx.max_turns},强制退出"
                     )
@@ -1472,7 +1477,7 @@ class AgentRunner:
 
                     # 3. 单次 LLM 调用
                     if not summary_text:
-                        logger.warning("侧分支未生成有效 summary,fallback 到单次 LLM 压缩")
+                        self.log.warning("侧分支未生成有效 summary,fallback 到单次 LLM 压缩")
                         pre_branch_history = history[:side_branch_ctx.start_history_length]
                         summary_text = await self._single_turn_compress(
                             trace_id, pre_branch_history, goal_tree, config,
@@ -1520,7 +1525,7 @@ class AgentRunner:
                         head_seq = sequence
                         sequence += 1
                     else:
-                        logger.error("所有压缩方案均未生成有效 summary,跳过压缩")
+                        self.log.error("所有压缩方案均未生成有效 summary,跳过压缩")
                         # 回退 history 到侧分支开始前,防止侧分支指令泄露到主分支
                         history = history[:side_branch_ctx.start_history_length]
                         head_seq = side_branch_ctx.start_head_seq
@@ -1537,7 +1542,7 @@ class AgentRunner:
 
                 elif should_exit and side_branch_ctx.type == "reflection":
                     # === 反思侧分支退出(超时 + 正常完成统一处理)===
-                    logger.info("反思侧分支退出")
+                    self.log.info("反思侧分支退出")
 
                     # 恢复主路径
                     if self.trace_store:
@@ -1551,7 +1556,7 @@ class AgentRunner:
                     trace.context.pop("active_side_branch", None)
                     if not config.force_side_branch or len(config.force_side_branch) == 0:
                         config.force_side_branch = None
-                        logger.info("反思完成,队列为空")
+                        self.log.info("反思完成,队列为空")
                     if self.trace_store:
                         await self.trace_store.update_trace(
                             trace_id, context=trace.context, head_sequence=head_seq,
@@ -1561,7 +1566,7 @@ class AgentRunner:
 
                 elif should_exit and side_branch_ctx.type == "knowledge_eval":
                     # === 知识评估侧分支退出 ===
-                    logger.info("知识评估侧分支退出")
+                    self.log.info("知识评估侧分支退出")
 
                     # 恢复主路径
                     if self.trace_store:
@@ -1575,7 +1580,7 @@ class AgentRunner:
                     trace.context.pop("active_side_branch", None)
                     if not config.force_side_branch or len(config.force_side_branch) == 0:
                         config.force_side_branch = None
-                        logger.info("知识评估完成,队列为空")
+                        self.log.info("知识评估完成,队列为空")
                     if self.trace_store:
                         await self.trace_store.update_trace(
                             trace_id, context=trace.context, head_sequence=head_seq,
@@ -1587,7 +1592,7 @@ class AgentRunner:
             # 截断兜底:finish_reason == "length" 说明响应被 max_tokens 截断,
             # tool call 参数很可能不完整,不应执行,改为提示模型分批操作
             if tool_calls and finish_reason == "length":
-                logger.warning(
+                self.log.warning(
                     "[Runner] 响应被 max_tokens 截断,跳过 %d 个不完整的 tool calls",
                     len(tool_calls),
                 )
@@ -1629,16 +1634,14 @@ class AgentRunner:
                                 # 尝试修复常见的截断/格式问题
                                 tool_args = self._try_fix_json(tool_args)
                                 if tool_args is None:
-                                    logger.warning(f"[Tool Call] JSON 解析失败,跳过工具调用 {tool_name}: {tc['function']['arguments'][:200]}")
+                                    self.log.warning(f"[Tool Call] JSON 解析失败,跳过工具调用 {tool_name}: {tc['function']['arguments'][:200]}")
                                     history.append({
                                         "role": "tool",
                                         "tool_call_id": tc["id"],
                                         "content": f"Error: 工具参数 JSON 格式错误,无法解析。请重新生成正确的 JSON 参数调用此工具。原始参数: {tc['function']['arguments'][:200]}",
                                     })
-                                    yield Message(role="tool", content={
-                                        "tool_name": tool_name,
-                                        "error": "JSON 参数解析失败",
-                                    }, description="工具参数 JSON 格式错误")
+                                    # 注意:这里不 yield Message,因为缺少必需参数会导致错误
+                                    # yield Message 应该由 trace_store 统一管理
                                     continue
                     elif tool_args is None:
                         tool_args = {}
@@ -1646,7 +1649,7 @@ class AgentRunner:
                     # 记录工具调用(INFO 级别,显示参数)
                     args_str = json.dumps(tool_args, ensure_ascii=False)
                     args_display = args_str[:100] + "..." if len(args_str) > 100 else args_str
-                    logger.info(f"[Tool Call] {tool_name}({args_display})")
+                    self.log.info(f"[Tool Call] {tool_name}({args_display})")
 
                     # 获取trigger_event(如果在knowledge_eval侧分支中)
                     trigger_event_for_tool = None
@@ -1680,17 +1683,14 @@ class AgentRunner:
 
                     # 如果是 goal 工具,记录执行后的状态
                     if tool_name == "goal" and goal_tree:
-                        logger.debug(f"[Goal Tool] After execution: goal_tree.goals={len(goal_tree.goals)}, current_id={goal_tree.current_id}")
+                        self.log.debug(f"[Goal Tool] After execution: goal_tree.goals={len(goal_tree.goals)}, current_id={goal_tree.current_id}")
 
-                    # 跟踪保存的知识 ID
-                    if tool_name == "knowledge_save" and isinstance(tool_result, dict):
+                    # 跟踪上传的知识(通过 upload_knowledge)
+                    if tool_name == "upload_knowledge" and isinstance(tool_result, dict):
                         metadata = tool_result.get("metadata", {})
-                        knowledge_id = metadata.get("knowledge_id")
-                        if knowledge_id:
-                            if trace_id not in self._saved_knowledge_ids:
-                                self._saved_knowledge_ids[trace_id] = []
-                            self._saved_knowledge_ids[trace_id].append(knowledge_id)
-                            logger.info(f"[Knowledge Tracking] 记录保存的知识 ID: {knowledge_id}")
+                        # upload_knowledge 返回的是统计信息,不是单个 knowledge_id
+                        # 这里只记录上传动作,不跟踪具体 ID
+                        self.log.info(f"[Knowledge Tracking] 知识已上传到 Knowledge Manager")
 
                     # --- 支持多模态工具反馈 ---
                     # execute() 返回 dict{"text","images","tool_usage"} 或 str
@@ -1805,7 +1805,7 @@ class AgentRunner:
                         )
                         compressed_msgs = compress_completed_goals(main_path_msgs, goal_tree)
                         if len(compressed_msgs) < len(main_path_msgs):
-                            logger.info(
+                            self.log.info(
                                 "on_complete 压缩: %d -> %d 条消息",
                                 len(main_path_msgs), len(compressed_msgs),
                             )
@@ -1821,7 +1821,7 @@ class AgentRunner:
             if not side_branch_ctx and self.trace_store:
                 pending = await self.trace_store.get_pending_knowledge_entries(trace_id)
                 if pending:
-                    logger.info(f"任务即将结束,但仍有 {len(pending)} 条知识未评估,强制触发评估")
+                    self.log.info(f"任务即将结束,但仍有 {len(pending)} 条知识未评估,强制触发评估")
                     config.force_side_branch = ["knowledge_eval"]
                     trace = await self.trace_store.get_trace(trace_id)
                     if trace:
@@ -1832,7 +1832,7 @@ class AgentRunner:
             if not side_branch_ctx and config.knowledge.enable_completion_extraction and not break_after_side_branch:
                 config.force_side_branch = ["reflection"]
                 break_after_side_branch = True
-                logger.info("任务完成,进入完成后反思侧分支")
+                self.log.info("任务完成,进入完成后反思侧分支")
                 continue
 
             break
@@ -1890,7 +1890,7 @@ class AgentRunner:
             new_history.append(first_user_msg)
         new_history.append(summary_msg_dict)
 
-        logger.info(f"{label}完成: {len(history)} → {len(new_history)} 条消息")
+        self.log.info(f"{label}完成: {len(history)} → {len(new_history)} 条消息")
         for idx, msg in enumerate(new_history):
             role = msg.get("role", "unknown")
             content = msg.get("content", "")
@@ -1900,7 +1900,7 @@ class AgentRunner:
                 preview = f"[{len(content)} blocks]"
             else:
                 preview = str(content)
-            logger.info(f"  {label}后[{idx}] {role}: {preview}")
+            self.log.info(f"  {label}后[{idx}] {role}: {preview}")
 
         return new_history
 
@@ -2045,7 +2045,7 @@ class AgentRunner:
         if not orphaned_ids:
             return messages, sequence
 
-        logger.info(
+        self.log.info(
             "检测到 %d 个 orphaned tool_calls,生成合成中断通知",
             len(orphaned_ids),
         )
@@ -2152,7 +2152,7 @@ class AgentRunner:
         trace: Trace,
         goal_tree: Optional[GoalTree],
     ) -> str:
-        """构建周期性注入的上下文(GoalTree + Active Collaborators + Focus 提醒)"""
+        """构建周期性注入的上下文(GoalTree + Active Collaborators + Focus 提醒 + IM 消息通知)"""
         from datetime import datetime
         parts = [f"## Current Time\n\n{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"]
 
@@ -2190,6 +2190,48 @@ class AgentRunner:
                 lines.append(f"- {name} [{ctype}, {status_str}]: {summary}")
             parts.append("\n".join(lines))
 
+        # IM 消息通知(Research Agent)
+        im_config = trace.context.get("im_config")
+        if im_config:
+            contact_id = im_config.get("contact_id")
+            chat_id = im_config.get("chat_id")
+            if contact_id and chat_id:
+                # 尝试导入 IM 模块并检查通知
+                try:
+                    from agent.tools.builtin.im import chat as im_chat
+                    notification = im_chat._notifications.get((contact_id, chat_id))
+                    if notification:
+                        count = notification.get("count", 0)
+                        senders = notification.get("from", [])
+                        senders_str = ", ".join(senders)
+                        parts.append(
+                            f"## IM 消息通知\n\n"
+                            f"你有 {count} 条新消息,来自: {senders_str}\n"
+                            f"使用 `im_receive_messages(contact_id=\"{contact_id}\", chat_id=\"{chat_id}\")` 查看消息内容。"
+                        )
+                    else:
+                        parts.append("## IM 消息通知\n\n暂无新消息")
+                except (ImportError, AttributeError):
+                    # IM 模块未加载或不可用
+                    pass
+
+        # Knowledge Manager 队列状态
+        km_queue_size = trace.context.get("km_queue_size")
+        if km_queue_size is not None:
+            current_sender = trace.context.get("current_sender", "unknown")
+            if km_queue_size > 0:
+                parts.append(
+                    f"## 消息队列状态\n\n"
+                    f"当前处理: {current_sender} 的消息\n"
+                    f"队列中还有 {km_queue_size} 条待处理消息"
+                )
+            else:
+                parts.append(
+                    f"## 消息队列状态\n\n"
+                    f"当前处理: {current_sender} 的消息\n"
+                    f"队列为空,处理完本条消息后将进入休眠"
+                )
+
         return "\n\n".join(parts)
 
     # ===== 辅助方法 =====
@@ -2368,7 +2410,7 @@ class AgentRunner:
             msg["content"] = new_content
         # print(f"[Image Opt Check] 扫描到 {stats['kept'] + stats['downscaled'] + stats['described']} 张图片上下文")
         if stats["downscaled"] > 0 or stats["described"] > 0:
-            logger.info(
+            self.log.info(
                 f"[Image Optimization] 保留 {stats['kept']} 张,"
                 f"降分辨率 {stats['downscaled']} 张,"
                 f"文本描述 {stats['described']} 张,"
@@ -2431,7 +2473,7 @@ class AgentRunner:
             return f"data:image/jpeg;base64,{new_data}"
 
         except Exception as e:
-            logger.warning(f"[Image Downscale] 降分辨率失败: {e}")
+            self.log.warning(f"[Image Downscale] 降分辨率失败: {e}")
             return None
 
     async def _generate_image_description(self, image_url: str, current_model: str) -> str:
@@ -2479,7 +2521,7 @@ class AgentRunner:
             return description if description else "图片内容"
 
         except Exception as e:
-            logger.warning(f"[Image Description] 生成描述失败: {e}")
+            self.log.warning(f"[Image Description] 生成描述失败: {e}")
             return "图片内容"
 
     def _add_cache_control(
@@ -2535,7 +2577,7 @@ class AgentRunner:
                             break
 
         if has_unprocessed_images:
-            logger.debug("[Cache] 检测到未处理的图片,延迟缓存建立")
+            self.log.debug("[Cache] 检测到未处理的图片,延迟缓存建立")
             return messages
 
         # 深拷贝避免修改原始数据
@@ -2554,7 +2596,7 @@ class AgentRunner:
                         "cache_control": {"type": "ephemeral"}
                     }]
                     system_cached = True
-                    logger.debug(f"[Cache] 为 system message 添加缓存标记 (len={len(content)})")
+                    self.log.debug(f"[Cache] 为 system message 添加缓存标记 (len={len(content)})")
                 break
 
         # 策略 2: 固定位置缓存点
@@ -2609,7 +2651,7 @@ class AgentRunner:
                 if estimated_tokens >= MIN_TOKENS:
                     cache_positions.append(j)
                     last_cache_pos = j
-                    logger.debug(f"[Cache] 在位置 {j} 添加缓存点 (估算 {estimated_tokens} tokens)")
+                    self.log.debug(f"[Cache] 在位置 {j} 添加缓存点 (估算 {estimated_tokens} tokens)")
                     break
 
         # 应用缓存标记
@@ -2623,16 +2665,16 @@ class AgentRunner:
                     "text": content,
                     "cache_control": {"type": "ephemeral"}
                 }]
-                logger.debug(f"[Cache] 为 message[{idx}] ({msg.get('role')}) 添加缓存标记")
+                self.log.debug(f"[Cache] 为 message[{idx}] ({msg.get('role')}) 添加缓存标记")
             elif isinstance(content, list):
                 # 在最后一个 text block 添加 cache_control
                 for block in reversed(content):
                     if isinstance(block, dict) and block.get("type") == "text":
                         block["cache_control"] = {"type": "ephemeral"}
-                        logger.debug(f"[Cache] 为 message[{idx}] ({msg.get('role')}) 添加缓存标记")
+                        self.log.debug(f"[Cache] 为 message[{idx}] ({msg.get('role')}) 添加缓存标记")
                         break
 
-        logger.debug(
+        self.log.debug(
             f"[Cache] 总消息: {total_msgs}, "
             f"缓存点: {len(cache_positions)} at {cache_positions}"
         )

+ 11 - 6
agent/tools/builtin/__init__.py

@@ -19,6 +19,7 @@ from agent.tools.builtin.search import search_posts, get_search_suggestions
 from agent.tools.builtin.sandbox import (sandbox_create_environment, sandbox_run_shell,
                                          sandbox_rebuild_with_ports,sandbox_destroy_environment)
 from agent.tools.builtin.knowledge import(knowledge_search,knowledge_save,knowledge_list,knowledge_update,knowledge_batch_update,knowledge_slim)
+from agent.tools.builtin.knowledge_manager import ask_knowledge, upload_knowledge
 from agent.tools.builtin.context import get_current_context
 from agent.tools.builtin.toolhub import toolhub_health, toolhub_search, toolhub_call, toolhub_create
 from agent.tools.builtin.resource import resource_list_tools, resource_get_tool
@@ -40,12 +41,16 @@ __all__ = [
     # 系统工具
     "bash_command",
     "skill",
-    "knowledge_search",
-    "knowledge_save",
-    "knowledge_list",
-    "knowledge_update",
-    "knowledge_batch_update",
-    "knowledge_slim",
+    # 知识管理(新架构 - 通过 IM 与 Knowledge Manager 交互)
+    "ask_knowledge",
+    "upload_knowledge",
+    # 知识管理(旧架构 - 直接 HTTP API,仅供 Knowledge Manager 内部使用)
+    # "knowledge_search",
+    # "knowledge_save",
+    # "knowledge_list",
+    # "knowledge_update",
+    # "knowledge_batch_update",
+    # "knowledge_slim",
     "list_skills",
     "agent",
     "evaluate",

+ 23 - 0
agent/tools/builtin/context.py

@@ -51,6 +51,29 @@ async def get_current_context(
         else:
             context_content = "暂无计划信息"
 
+        # Fallback 也检查 IM 通知
+        im_config = trace.context.get("im_config") if trace else None
+        if im_config:
+            contact_id = im_config.get("contact_id")
+            chat_id = im_config.get("chat_id")
+            if contact_id and chat_id:
+                try:
+                    from agent.tools.builtin.im import chat as im_chat
+                    notification = im_chat._notifications.get((contact_id, chat_id))
+                    if notification:
+                        count = notification.get("count", 0)
+                        senders = notification.get("from", [])
+                        senders_str = ", ".join(senders)
+                        context_content += (
+                            f"\n\n## IM 消息通知\n\n"
+                            f"你有 {count} 条新消息,来自: {senders_str}\n"
+                            f"使用 `im_receive_messages(contact_id=\"{contact_id}\", chat_id=\"{chat_id}\")` 查看消息内容。"
+                        )
+                    else:
+                        context_content += "\n\n## IM 消息通知\n\n暂无新消息"
+                except (ImportError, AttributeError):
+                    pass
+
     if not context_content:
         context_content = "当前无需要刷新的上下文信息"
 

+ 14 - 0
agent/tools/builtin/knowledge.py

@@ -1,6 +1,17 @@
 """
 知识管理工具 - KnowHub API 封装
 
+⚠️ DEPRECATED: 这些工具直接调用 KnowHub HTTP API,绕过 Knowledge Manager。
+推荐使用新架构:
+- knowledge_search → ask_knowledge (通过 IM 与 Knowledge Manager 交互)
+- knowledge_save → upload_knowledge (通过 IM 与 Knowledge Manager 交互)
+
+新架构优势:
+1. 统一通过 Knowledge Manager 管理知识
+2. 自动去重和整合
+3. 支持增量上传和批量提交
+4. 更好的并发控制
+
 所有工具通过 HTTP API 调用 KnowHub Server。
 """
 
@@ -101,6 +112,8 @@ async def knowledge_search(
     """
     检索知识(两阶段:语义路由 + 质量精排)
 
+    ⚠️ DEPRECATED: 推荐使用 ask_knowledge 工具(通过 Knowledge Manager)
+
     Args:
         query: 搜索查询(任务描述)
         top_k: 返回数量(默认 5)
@@ -112,6 +125,7 @@ async def knowledge_search(
     Returns:
         相关知识列表
     """
+    logger.warning("knowledge_search is deprecated. Use ask_knowledge instead.")
     try:
         params = {
             "q": query,

+ 199 - 0
agent/tools/builtin/knowledge_manager.py

@@ -0,0 +1,199 @@
+"""
+Knowledge Manager 工具 - 通过 IM 与知识库管理 Agent 交互
+
+提供两个工具:
+- ask_knowledge: 查询知识库(同步,等待回复)
+- upload_knowledge: 上传调研结果(异步,立即返回)
+
+依赖:IM Client 已初始化(im_setup + im_open_window)
+"""
+
+import asyncio
+import json
+import logging
+from typing import Optional, Dict, Any, List
+from agent.tools import tool, ToolResult, ToolContext
+
+logger = logging.getLogger(__name__)
+
+# IM 工具的全局引用(延迟导入)
+_im_chat = None
+
+def _get_im_chat():
+    global _im_chat
+    if _im_chat is None:
+        from agent.tools.builtin.im import chat as im_chat_module
+        _im_chat = im_chat_module
+    return _im_chat
+
+
+def _get_client_and_chat_id(contact_id: str):
+    """获取 IM Client 实例和当前窗口的 chat_id"""
+    im = _get_im_chat()
+    client = im._clients.get(contact_id)
+    if client is None:
+        return None, None
+    # 取第一个打开的窗口
+    windows = client.list_windows()
+    chat_id = windows[0] if windows else None
+    return client, chat_id
+
+
+def _clear_notifications(contact_id: str, chat_id: str):
+    """清空 IM 通知计数"""
+    im = _get_im_chat()
+    im._notifications.pop((contact_id, chat_id), None)
+
+
+@tool(
+    hidden_params=["context"],
+    inject_params={
+        "contact_id": {"mode": "default", "key": "im_contact_id"},
+    }
+)
+async def ask_knowledge(
+    query: str,
+    contact_id: str = "agent_research",
+    context: Optional[ToolContext] = None,
+) -> ToolResult:
+    """
+    向 Knowledge Manager 查询知识库信息(同步,等待回复)
+
+    Args:
+        query: 查询内容(如:"查询 ControlNet 相关信息")
+        contact_id: 当前 Agent 的 IM contact_id
+        context: 工具上下文
+
+    Returns:
+        查询结果(已有工具、资源、知识及建议)
+    """
+    try:
+        client, chat_id = _get_client_and_chat_id(contact_id)
+        if client is None or chat_id is None:
+            return ToolResult(
+                title="❌ 查询失败",
+                output="IM Client 未初始化,请先调用 im_setup",
+                error="im_not_initialized"
+            )
+
+        # 发送查询(带类型标记)
+        message = f"[ASK] {query}"
+        client.send_message(
+            chat_id=chat_id,
+            receiver="knowledge_manager",
+            content=message
+        )
+
+        # 等待回复(最多 30 秒)
+        for _ in range(30):
+            await asyncio.sleep(1)
+
+            pending = client.read_pending(chat_id)
+            for msg in pending:
+                if msg.get("sender") == "knowledge_manager":
+                    content = msg.get("content", "")
+                    # 清空 IM 通知计数,防止 notifier 反复提醒
+                    _clear_notifications(contact_id, chat_id)
+                    return ToolResult(
+                        title="📚 知识库查询结果",
+                        output=content,
+                        metadata={"source": "knowledge_manager"}
+                    )
+
+        # 超时保底:直接调用 knowledge_search 返回原始结果
+        logger.warning("Knowledge Manager 超时,fallback 到 knowledge_search")
+        from agent.tools.builtin.knowledge import knowledge_search
+        fallback_result = await knowledge_search(query=query, top_k=5, min_score=3)
+
+        return ToolResult(
+            title="📚 知识库查询结果(直连)",
+            output=fallback_result.output,
+            metadata={"source": "fallback", "raw": fallback_result.metadata}
+        )
+
+    except Exception as e:
+        logger.error(f"查询知识库失败: {e}")
+        return ToolResult(
+            title="❌ 查询失败",
+            output=f"错误: {str(e)}",
+            error=str(e)
+        )
+
+
+@tool(
+    hidden_params=["context"],
+    inject_params={
+        "contact_id": {"mode": "default", "key": "im_contact_id"},
+    }
+)
+async def upload_knowledge(
+    data: Dict[str, Any],
+    finalize: bool = False,
+    contact_id: str = "agent_research",
+    context: Optional[ToolContext] = None,
+) -> ToolResult:
+    """
+    上传调研结果到知识库(异步,立即返回)
+
+    Args:
+        data: 调研结果,包含:
+            - tools: 工具列表
+            - resources: 资源列表
+            - knowledge: 知识列表
+        finalize: 是否最终提交(True=入库,False=仅缓冲)
+        contact_id: 当前 Agent 的 IM contact_id
+        context: 工具上下文
+
+    Returns:
+        上传确认(立即返回,不等待处理完成)
+    """
+    try:
+        client, chat_id = _get_client_and_chat_id(contact_id)
+        if client is None or chat_id is None:
+            return ToolResult(
+                title="❌ 上传失败",
+                output="IM Client 未初始化,请先调用 im_setup",
+                error="im_not_initialized"
+            )
+
+        # 构造消息(带类型标记)
+        if finalize:
+            action = "最终提交"
+            message = f"[UPLOAD:FINALIZE] {json.dumps(data, ensure_ascii=False)}"
+        else:
+            action = "增量上传"
+            message = f"[UPLOAD] {json.dumps(data, ensure_ascii=False)}"
+
+        # 发送(不等待回复)
+        client.send_message(
+            chat_id=chat_id,
+            receiver="knowledge_manager",
+            content=message
+        )
+
+        # 等待一小段时间让 KM 回复,然后清空 pending(避免 notifier 反复通知)
+        await asyncio.sleep(0.5)
+        client.read_pending(chat_id)  # 清空回复,不处理内容
+
+        summary = []
+        if data.get("tools"):
+            summary.append(f"工具: {len(data['tools'])} 个")
+        if data.get("resources"):
+            summary.append(f"资源: {len(data['resources'])} 个")
+        if data.get("knowledge"):
+            summary.append(f"知识: {len(data['knowledge'])} 个")
+
+        return ToolResult(
+            title=f"✅ {action}成功",
+            output=f"已发送到 Knowledge Manager\n\n" + "\n".join(f"- {s}" for s in summary),
+            long_term_memory=f"{action}: {', '.join(summary)}",
+            metadata={"finalize": finalize}
+        )
+
+    except Exception as e:
+        logger.error(f"上传知识失败: {e}")
+        return ToolResult(
+            title="❌ 上传失败",
+            output=f"错误: {str(e)}",
+            error=str(e)
+        )

+ 0 - 0
data/agent_research/windows/20260330_194214_cd57a0/chatbox.jsonl


+ 0 - 1
data/agent_research/windows/20260330_194214_cd57a0/in_pending.json

@@ -1 +0,0 @@
-[]

+ 0 - 0
data/agent_research/windows/20260330_194214_cd57a0/out_pending.jsonl


+ 27 - 27
examples/content_tree_analyst/analyst.prompt

@@ -85,38 +85,38 @@ $system$
 
 从调研结果的 JSON 中识别知识类型并保存:
 
-**工具知识**(单个工具):
-- 识别条件:`调研发现[i].类型 == "tool"`
-- 保存调用:
+**使用 upload_knowledge 工具**:
 ```python
-knowledge_save(
-    task=f"【工具】{需求描述}",
-    content=f"工具:{工具名称}\n能力:{核心描述}\n使用方式:{说明}\n限制:{限制}",
-    types=["tool"],
-    tags={"tool": True, "domain": "content_production"},
-    source_name=来源,
-    urls=[工具链接]
-)
-```
-
-**工序知识**(工作流/方案):
-- 识别条件:`调研发现[i].类型 == "workflow"` 或 `"case"`
-- 保存调用:
-```python
-knowledge_save(
-    task=f"【工序】{需求描述}",
-    content=f"工序方案:{方案名称}\n步骤:\n{逐步骤说明}",
-    types=["strategy"],
-    tags={"workflow": True, "domain": "content_production"},
-    source_name=来源,
-    urls=[来源链接]
+upload_knowledge(
+    data={
+        "knowledge": [
+            {
+                "主题": f"【工具】{需求描述}",
+                "内容": f"工具:{工具名称}\n能力:{核心描述}\n使用方式:{说明}\n限制:{限制}",
+                "类型": ["tool"],
+                "标签": ["tool", "domain:content_production"],
+                "来源": 来源,
+                "链接": [工具链接]
+            },
+            {
+                "主题": f"【工序】{需求描述}",
+                "内容": f"工序方案:{方案名称}\n步骤:\n{逐步骤说明}",
+                "类型": ["strategy"],
+                "标签": ["workflow", "domain:content_production"],
+                "来源": 来源,
+                "链接": [来源链接]
+            }
+        ]
+    },
+    finalize=False
 )
 ```
 
 **重要**:
-- task 字段必须以【工具】或【工序】开头,明确知识类型
-- 暂不填写 resource_ids
-- **每完成一个需求的调研,立即保存所有发现的知识,不要等到全部调研完成**
+- 主题字段必须以【工具】或【工序】开头,明确知识类型
+- 使用 `upload_knowledge` 而不是直接调用 `knowledge_save`
+- `finalize=False` 表示增量上传,不立即入库
+- **每完成一个需求的调研,立即上传所有发现的知识,不要等到全部调研完成**
 
 #### 6.3 输出调研日志
 

+ 58 - 0
examples/new_search/config.py

@@ -0,0 +1,58 @@
+"""
+项目配置
+"""
+
+from agent.core.runner import KnowledgeConfig, RunConfig
+
+
+# ===== Agent 运行配置 =====
+
+RUN_CONFIG = RunConfig(
+    model="qwen3.5-plus",
+    temperature=0.3,
+    max_iterations=200,
+
+    extra_llm_params={"extra_body": {"enable_thinking": True}},
+
+    agent_type="main",
+
+    name="工具调研测试(KM 通信)",
+
+    knowledge=KnowledgeConfig(
+        enable_extraction=False,
+        enable_completion_extraction=False,
+        enable_injection=False,
+        owner="sunlit.howard@gmail.com",
+        default_tags={"project": "new_search"},
+        default_scopes=["org:cybertogether"],
+    )
+)
+
+
+# ===== 任务配置 =====
+
+OUTPUT_DIR = "examples/new_search/outputs"
+
+
+# ===== 基础设施配置 =====
+
+SKILLS_DIR = "./skills"
+TRACE_STORE_PATH = ".trace"
+DEBUG = True
+LOG_LEVEL = "INFO"
+LOG_FILE = None
+
+# ===== 浏览器配置 =====
+BROWSER_TYPE = "local"
+HEADLESS = False
+
+# ===== IM 配置 =====
+IM_ENABLED = True
+IM_CONTACT_ID = "agent_research"
+IM_SERVER_URL = "ws://localhost:58005"
+IM_WINDOW_MODE = True
+IM_NOTIFY_INTERVAL = 10.0
+
+# ===== Knowledge Manager 配置 =====
+KNOWLEDGE_MANAGER_ENABLED = True
+KNOWLEDGE_MANAGER_CONTACT_ID = "knowledge_manager"

+ 63 - 0
examples/new_search/new_search.prompt

@@ -0,0 +1,63 @@
+---
+model: qwen3.5-plus
+temperature: 0.3
+---
+
+$system$
+
+## 角色
+你是一个工具调研专家,能够独立搜索和整理工具信息,并与 Knowledge Manager 协作管理知识。
+
+## 可用工具
+- `ask_knowledge`: 向 Knowledge Manager 查询知识库中已有的信息(同步等待回复)
+- `upload_knowledge`: 上传调研结果到 Knowledge Manager(异步,立即返回)
+- `search_posts`: 搜索帖子(小红书、知乎、B站等)
+- `x_search`: 搜索推文
+- `youtube_search`: 搜索 YouTube 视频
+- `web_search`: 网页搜索
+- `im_send_message`: 发送 IM 消息
+- `im_receive_messages`: 接收 IM 消息
+
+## 工作流程
+
+### 第一步:查询已有知识(必须先做!)
+**在做任何搜索之前**,必须先调用 `ask_knowledge` 查询知识库:
+```
+ask_knowledge("查询关于 [工具名] 的所有信息")
+```
+这一步是强制的,不能跳过。根据返回结果决定后续调研重点。
+
+### 第二步:搜索调研
+使用搜索工具直接调研目标工具:
+- 搜索官方信息和文档
+- 搜索用户案例和评测
+- 搜索使用教程
+
+**每搜到一批有价值的信息,就用 `upload_knowledge` 发送给 Knowledge Manager:**
+```
+upload_knowledge({
+  "tools": [
+    {"name": "工具名", "slug": "tool_slug", "category": "分类", "description": "简介", "source_url": "链接"}
+  ],
+  "resources": [
+    {"title": "文档标题", "body": "文档内容", "content_type": "documentation", "source_url": "链接"}
+  ],
+  "knowledge": [
+    {"task": "使用场景", "content": "具体知识", "types": ["tool"], "score": 4}
+  ]
+})
+```
+
+### 第三步:最终提交
+调研完成后,用 `finalize=True` 触发入库:
+```
+upload_knowledge({...最后一批数据...}, finalize=True)
+```
+
+## 注意事项
+- 边搜边传:每搜到有价值的信息就 upload,不要攒到最后
+- 分类清晰:工具元信息放 tools,文档/教程放 resources,经验/技巧放 knowledge
+- 最后 finalize:确保最后一次 upload 设置 finalize=True
+
+$user$
+请调研 ControlNet 工具,了解它的功能、使用方法和应用场景,并将结果同步到知识库。

+ 206 - 0
examples/new_search/run.py

@@ -0,0 +1,206 @@
+"""
+新搜索测试 - 测试 Research Agent 与 Knowledge Manager 的 IM 通信
+"""
+
+import argparse
+import os
+import sys
+import asyncio
+from pathlib import Path
+
+os.environ.setdefault("no_proxy", "*")
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from dotenv import load_dotenv
+load_dotenv()
+
+from agent.llm.prompts import SimplePrompt
+from agent.core.runner import AgentRunner, RunConfig
+from agent.trace import FileSystemTraceStore, Trace, Message
+from agent.llm import create_qwen_llm_call
+from agent.cli import InteractiveController
+from agent.utils import setup_logging
+from agent.tools.builtin.browser.baseClass import init_browser_session, kill_browser_session
+
+from config import (
+    RUN_CONFIG, SKILLS_DIR, TRACE_STORE_PATH, DEBUG, LOG_LEVEL, LOG_FILE,
+    BROWSER_TYPE, HEADLESS, OUTPUT_DIR,
+    IM_ENABLED, IM_CONTACT_ID, IM_SERVER_URL, IM_WINDOW_MODE, IM_NOTIFY_INTERVAL,
+    KNOWLEDGE_MANAGER_ENABLED, KNOWLEDGE_MANAGER_CONTACT_ID,
+)
+
+
+async def main():
+    parser = argparse.ArgumentParser(description="新搜索测试(KM 通信)")
+    parser.add_argument("--trace", type=str, default=None, help="恢复 Trace ID")
+    args = parser.parse_args()
+
+    base_dir = Path(__file__).parent
+    project_root = base_dir.parent.parent
+    prompt_path = base_dir / "new_search.prompt"
+    output_dir = project_root / OUTPUT_DIR
+    output_dir.mkdir(parents=True, exist_ok=True)
+
+    # 1. 日志
+    setup_logging(level=LOG_LEVEL, file=LOG_FILE)
+
+    # 2. Prompt
+    print("1. 加载 prompt...")
+    prompt = SimplePrompt(prompt_path)
+    messages = prompt.build_messages(output_dir=str(output_dir))
+
+    # 3. 浏览器
+    print("2. 初始化浏览器...")
+    await init_browser_session(browser_type=BROWSER_TYPE, headless=HEADLESS, url="https://www.google.com/", profile_name="")
+    print("   ✅ 浏览器就绪\n")
+
+    # 4. IM Client + Knowledge Manager
+    km_task = None
+    if IM_ENABLED:
+        from agent.tools.builtin.im.chat import im_setup, im_open_window
+        print("3. 初始化 IM Client...")
+        print(f"   - 身份: {IM_CONTACT_ID}, 服务器: {IM_SERVER_URL}")
+        result = await im_setup(
+            contact_id=IM_CONTACT_ID,
+            server_url=IM_SERVER_URL,
+            notify_interval=IM_NOTIFY_INTERVAL,
+        )
+        print(f"   ✅ {result.output}")
+
+        if IM_WINDOW_MODE:
+            window_result = await im_open_window(contact_id=IM_CONTACT_ID)
+            print(f"   ✅ {window_result.output}\n")
+
+        if KNOWLEDGE_MANAGER_ENABLED:
+            print("4. 启动 Knowledge Manager...")
+            print(f"   - Contact ID: {KNOWLEDGE_MANAGER_CONTACT_ID}")
+            try:
+                sys.path.insert(0, str(Path(__file__).parent.parent.parent / "knowhub"))
+                from agents.knowledge_manager import start_knowledge_manager
+
+                km_task = asyncio.create_task(start_knowledge_manager(
+                    contact_id=KNOWLEDGE_MANAGER_CONTACT_ID,
+                    server_url=IM_SERVER_URL,
+                    chat_id="main"
+                ))
+                # 等待一下让 KM 连接完成
+                await asyncio.sleep(2)
+                print(f"   ✅ Knowledge Manager 已启动\n")
+            except Exception as e:
+                print(f"   ⚠️ 启动失败: {e}\n")
+
+    # 5. Agent Runner
+    print("5. 创建 Agent Runner...")
+    prompt_model = prompt.config.get("model", None)
+    model_for_llm = prompt_model or RUN_CONFIG.model
+    print(f"   - 模型: {model_for_llm}")
+
+    store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
+    runner = AgentRunner(
+        trace_store=store,
+        llm_call=create_qwen_llm_call(model=model_for_llm),
+        skills_dir=SKILLS_DIR,
+        debug=DEBUG,
+        logger_name="agents.research_agent"
+    )
+
+    interactive = InteractiveController(runner=runner, store=store, enable_stdin_check=True)
+    runner.stdin_check = interactive.check_stdin
+
+    # 6. 执行
+    task_name = RUN_CONFIG.name or base_dir.name
+    print("=" * 60)
+    print(f"{task_name}")
+    print("=" * 60)
+    print("💡 输入 'p' 暂停,'q' 退出")
+    print("=" * 60)
+    print()
+
+    run_config = RUN_CONFIG
+    current_trace_id = args.trace
+    current_sequence = 0
+
+    # 注入 IM 配置到 context(用于周期性通知检查)
+    if IM_ENABLED:
+        run_config.context["im_config"] = {
+            "contact_id": IM_CONTACT_ID,
+            "chat_id": "main"
+        }
+
+    if current_trace_id:
+        run_config.trace_id = current_trace_id
+        initial_messages = None
+    else:
+        initial_messages = messages
+
+    try:
+        async for item in runner.run(messages=initial_messages, config=run_config):
+            cmd = interactive.check_stdin()
+            if cmd == 'quit':
+                print("\n🛑 停止...")
+                if current_trace_id:
+                    await runner.stop(current_trace_id)
+                break
+            elif cmd == 'pause':
+                print("\n⏸️ 暂停...")
+                if current_trace_id:
+                    await runner.stop(current_trace_id)
+                break
+
+            if isinstance(item, Trace):
+                current_trace_id = item.trace_id
+                if item.status == "running":
+                    print(f"[Trace] 开始: {item.trace_id[:8]}...")
+                elif item.status == "completed":
+                    print(f"\n[Trace] ✅ 完成 (消息: {item.total_messages}, 费用: ${item.total_cost:.4f})")
+                elif item.status == "failed":
+                    print(f"\n[Trace] ❌ 失败: {item.error_message}")
+
+            elif isinstance(item, Message):
+                current_sequence = item.sequence
+                if item.role == "assistant":
+                    content = item.content
+                    if isinstance(content, dict):
+                        text = content.get("text", "")
+                        tool_calls = content.get("tool_calls")
+                        if text and not tool_calls:
+                            print(f"\n[Response] {text}")
+                        elif text:
+                            preview = text[:150] + "..." if len(text) > 150 else text
+                            print(f"[Assistant] {preview}")
+
+                elif item.role == "tool":
+                    content = item.content
+                    tool_name = content.get("tool_name", "unknown") if isinstance(content, dict) else "unknown"
+                    desc = item.description or ""
+                    if desc and desc != tool_name:
+                        desc = desc[:80]
+                        print(f"[Tool] ✅ {tool_name}: {desc}...")
+                    else:
+                        print(f"[Tool] ✅ {tool_name}")
+
+    except KeyboardInterrupt:
+        print("\n\n用户中断 (Ctrl+C)")
+        if current_trace_id:
+            await runner.stop(current_trace_id)
+    finally:
+        if km_task and not km_task.done():
+            print("正在关闭 Knowledge Manager...")
+            km_task.cancel()
+            try:
+                await km_task
+            except asyncio.CancelledError:
+                pass
+
+        try:
+            await kill_browser_session()
+        except Exception:
+            pass
+
+    if current_trace_id:
+        print(f"\nTrace ID: {current_trace_id}")
+
+
+if __name__ == "__main__":
+    asyncio.run(main())

+ 85 - 0
examples/tool_research/atomic_cap/0/atomic_capabilities.md

@@ -0,0 +1,85 @@
+```
+### A01: 多视角角色结构一致性保持
+- **功能描述**: 在同一角色的正面、侧面、背面等不同视角图像中,精确维持其面部特征、身体比例、服装结构、配饰细节等三维结构属性的一致性,确保可直接用于3D建模或动画绑定。
+- **判定标准**: 三视图中角色的关键解剖/设计特征(如痣的位置、袖口褶皱走向、武器握持角度)在空间逻辑上可对齐还原;任意两张图叠加对齐关键点时,误差≤3像素(以512px基准图计)。
+- **实现方式**: ComfyUI: CharTurn系列模型 + ControlNet OpenPose/Depth节点控制姿态 + 角色概念图作为IP-Adapter参考输入;ComfyUI: AnimateDiff + ControlNet Reference-only模式 + 多视角提示词模板;Midjourney v8: --cref + Omni Reference + 多视角提示词模板(如 "front view", "3/4 profile", "back view")
+- **典型场景**: 游戏角色原画交付、3D资产前期设计、IP形象标准化输出。
+- **来源依据**: 案例1「游戏角色多视图生成」——明确要求“一致性高,可直接用于3D建模参考”,工作流依赖CharTurn模型与ControlNet协同控制姿态与结构。
+
+### A02: 产品级对象跨背景一致性保持
+- **功能描述**: 对同一物理产品(如手机、香水瓶、家具)在不同背景(纯色、场景、渐变)下生成图像时,严格保持其材质反射、几何形态、品牌标识、接缝细节等产品本体特征不变,消除背景干扰导致的形变或纹理失真。
+- **判定标准**: 将不同背景下的生成图裁剪出产品主体区域后,PS中图层差值模式下无可见差异(ΔE<2);品牌Logo文字清晰可辨且无扭曲。
+- **实现方式**: FLUX.2 [max]: 多参考图像输入(最多10张)+ “product_consistency: strict”隐式模式;ComfyUI: IP-Adapter(plus或face_id模式)+ 产品实物图作为参考输入 + 多背景图像批量注入至ImageScale/Composite节点;ComfyUI: ControlNet Tile(细节强化)+ LoRA微调产品专属特征 + 背景替换工作流;Nano Banana Pro: 多图输入(最多14张产品图)+ 背景提示词切换(如 "on white studio background", "in modern living room")+ "same product, identical texture and logo" 约束
+- **典型场景**: 电商详情页制作、产品宣传册批量生成、AR商品预览素材准备。
+- **来源依据**: 案例2「电商产品图批量生成」——强调“单个工作流生成100+张产品图”,核心依赖IP-Adapter保持产品本体一致性,背景通过模板化切换实现;案例5「FLUX.2 [max] 官方页面」明确多图参考下“不同场景和风格中保持角色面部特征一致性”,同理迁移至产品。
+
+### A03: 老照片语义级修复与自然上色
+- **功能描述**: 针对严重划痕、褪色、模糊的老照片,在保留原始人物神态、服饰时代特征、场景历史感的前提下,完成去噪、人脸结构重建、纹理增强及符合时代审美的自然色彩还原,避免AI幻觉式上色。
+- **判定标准**: 修复后图像中人脸五官比例合理、皮肤纹理有真实颗粒感;色彩符合历史常识(如黑白照中木质家具呈暖棕、金属器物呈冷灰);无新增不存在的物体或服饰细节。
+- **实现方式**: ComfyUI: CodeFormer节点(人脸专用)+ Tile VAE节点(全局细节增强)+ DeOldify节点(基于历史数据集训练的上色模型)+ 手动Mask隔离修复区域;ComfyUI: Stable Diffusion Inpainting节点 + 老照片作为Reference + “vintage photo, natural color, historical accuracy”提示词约束
+- **典型场景**: 家族史数字化、博物馆档案修复、怀旧影视素材复原。
+- **来源依据**: 案例3「老照片修复与上色」——明确列出CodeFormer、Tile模型、DeOldify三级处理链,且效果强调“清晰自然,色彩真实”。
+
+### A04: 时序连贯的AI动画基础帧生成
+- **功能描述**: 生成具备运动连续性、角色姿态逻辑合理、关键帧间形变可控的短序列动画(≥8帧),支持后续插帧或合成,避免常见跳帧、肢体错位、物体闪烁等问题。
+- **判定标准**: 序列中相邻帧的光流变化平滑(无突变向量);角色关节运动符合生物力学(如抬手时肩肘腕联动);同一物体在序列中位置/大小变化符合透视规律。
+- **实现方式**: ComfyUI: AnimateDiff节点(Lora适配器)+ ControlNet Reference(固定角色参考图)+ Motion Lora控制运动幅度;ComfyUI: LoopBack节点构建循环反馈 + KSampler动态种子偏移 + Pose Sequence ControlNet;Midjourney v8: --cref(固定角色图)+ --p(个人化动作模板)+ 批量提示词序列(如 "frame_01: walking forward", "frame_02: lifting knee")
+- **典型场景**: 社交媒体15秒短视频、游戏过场动画草稿、教育类动态演示。
+- **来源依据**: 案例4「AI动画短片制作」——指出“结合ControlNet保持角色一致性”并“批量生成后合成视频”,核心解决动画序列的时序稳定性。
+
+### A05: 建筑线稿到多风格效果图的可控转换
+- **功能描述**: 将同一张建筑手绘线稿或CAD导出线稿,精准转换为多种指定艺术风格(如“北欧极简”“赛博朋克”“水墨写意”)的效果图,确保建筑结构、门窗比例、空间关系在所有风格中严格一致,仅表皮材质、光影氛围、环境元素按风格迁移。
+- **判定标准**: 不同风格图叠加线稿图层时,所有承重墙、开窗位置、楼梯走向完全重合;风格元素不破坏建筑功能逻辑(如赛博朋克风格中霓虹灯不遮挡消防通道)。
+- **实现方式**: FLUX.2 [max]: 接地式生成(Grounded Generation)+ 风格提示词(如“北欧极简,参考Nordic Design Archive 2025”)+ 结构守恒约束;ComfyUI: ControlNet Canny节点(线稿输入)+ 多LoRA风格切换(现代/古典/工业LoRA)+ 风格提示词前缀;ComfyUI: T2I-Adapter(线稿编码)+ Style Transfer节点 + 局部重绘(Inpaint)强化风格细节;Midjourney v8: --sref(风格参考图)+ 线稿作为主图输入 + --stylize 100~300 区间精细调控风格强度;Nano Banana Pro: 手绘草图/线稿输入 + 风格提示词 + "maintain the original composition" 强约束
+- **典型场景**: 建筑方案汇报、地产营销多版本提案、设计竞赛风格探索。
+- **来源依据**: 案例5「建筑效果图快速出图」——明确“使用线稿作为ControlNet输入”“批量切换LoRA生成不同风格”,核心诉求是结构守恒下的风格解耦;FLUX文档2.3“高精度控制(颜色、姿态、构图)”与案例8“历史场景生成”共同支撑其结构守恒能力;用例9「手绘转效果图」直接验证 Nano Banana Pro 的结构守恒能力。
+
+### A06: 工作流驱动的批量自动化生成
+- **功能描述**: 将单一图像生成流程封装为可参数化调度的批处理任务,支持按预设规则(如背景列表、视角模板、风格LoRA池)自动遍历组合、生成、命名、保存,全程无需人工干预,满足生产级吞吐需求。
+- **判定标准**: 输入N个变量(如5种背景+4种角度=20组),工作流自动触发20次独立生成;输出文件名含变量标识(如`product_red_back.png`);失败任务可单独重试且不影响队列。
+- **实现方式**: FLUX.2 [max]: API端点 `/v1/flux-2-max` + CSV变量注入 + 动态prompt模板;ComfyUI: BatchManager节点 + CSV变量表导入 + 动态路径Save Image节点;ComfyUI: Python API调用(`/prompt`端点)+ 外部脚本循环提交JSON工作流 + 参数注入;Seedream 5.0 Lite: API端点(/v1/seedream)+ 轻量化低延迟(<1.2s)+ 官方API Explorer支持参数化调度(文档4.2 + 技术亮点4)
+- **典型场景**: 电商SKU图量产、A/B测试素材生成、设计团队协作素材库建设。
+- **来源依据**: 案例2/4/5均强调“批量生成”,文档2.2节明确“批处理优化”和API接口能力,指向自动化调度这一工程底座能力;FLUX API文档4.2明确支持JSON payload,案例6/7均体现参数化生成;Seedream技术规格2.1/2.3 + 技术亮点4“适合集成到设计工具链” + API调用示例。
+
+### A07: 实时语境感知生成
+- **功能描述**: 在图像生成过程中,自动执行实时网络搜索,动态获取并融合最新、最相关的外部事实性信息(如实时赛事结果、历史事件细节、流行文化符号、地理环境特征),确保生成内容在时间维度和事实维度上准确可信。
+- **判定标准**: 生成图像中包含明确时效性元素(如比分牌、新闻标题、特定日期标识)且与真实世界一致(误差为0);对模糊提示(如“最近的比赛”)能自主解析时间窗口并返回正确结果;不依赖用户手动提供上下文链接或数据快照。
+- **实现方式**: FLUX.2 [max]: 原生“Grounded Generation”机制(无需额外节点/参数,仅需含时效性关键词的prompt);Nano Banana Pro: 原生 tools=[{"google_search": {}}] + 思考过程可视化(可验证检索逻辑) + 提示词中嵌入时效性指令(如 "show current 2026 NBA Finals score");*暂无其他工具原生支持;ComfyUI需外接RAG插件+自定义搜索节点+T2I重绘工作流(复杂度高,非开箱即用)*;Seedream 5.0 Lite: 原生联网检索 + Chain-of-Thought 推理引擎(自动解析时效性语义并驱动构图决策)
+- **典型场景**: 新闻配图快速生产、体育营销实时素材、历史教育可视化、政策解读图解。
+- **来源依据**: 案例8(柏林墙倒塌历史场景)与案例9(皇马vs曼城比赛成绩)——官方明确标注“模型自动搜索网络获取比赛结果后生成”,且案例8强调“具有历史感的现实主义风格”,说明其不仅调用静态知识库,而是动态检索并理解语境;Nano Banana Pro 用例6(水循环信息图)、用例8(漫画翻译)进一步佐证其搜索接地能力;Seedream用例1 + 技术亮点1&2 + 竞品对比表。
+
+### A08: 高保真图内文字渲染
+- **功能描述**: 在生成图像中嵌入指定文字(品牌名、标语、Logo文案、产品参数等),确保文字**笔画清晰、无扭曲变形、语义准确(无错别字/乱码)、排版符合设计规范(如居中/对齐/字号比例)**,达到可直接用于印刷、UI或商标注册的视觉质量。
+- **判定标准**: 文字区域放大至200%后仍可清晰辨识每个字符;PS中使用“字符面板”比对,字体粗细/间距/基线位置与提示词指定一致;生成100张同提示图,文字错误率≤0.5%(案例对比表指出其“仍有偶尔拼写错误”,反向验证该能力存在且为优化目标)。
+- **实现方式**: FLUX.2 [max]: 原生高token编码器(Mistral-3-24B)+ 4MP分辨率输出 + 文本渲染专用微调头;DALL-E 3: “text_rendering: high”参数 + SVG参考图引导;Midjourney v8: --hd(2048px原生输出)+ --raw 模式 + 文字提示词前置(如 "LOGO: 'AQUA LAGER' in bold sans-serif, centered");Nano Banana Pro: 原生多语言高保真文本渲染引擎 + 4K分辨率输出 + 排版约束提示词(如 "Japanese text '夏のセール' in same font style and layout as reference comic panel") + 竞品对比表实测94–96%准确率
+- **典型场景**: 电商主图文案植入、品牌VI系统输出、App界面原型生成、法律文书配图标注。
+- **来源依据**: 案例6(AQUA LAGER啤酒瓶悬浮水中,文字渲染清晰)与案例7(生成可商用Logo)——小红书与官网均强调“文字清晰”,且竞品对比表将“文本渲染”列为独立评测维度,证明其作为独立能力被用户高频验证;Nano Banana Pro 用例4(Instagram广告)、用例5(书名)、用例8(漫画翻译)共同构成多语言、多场景、高准确率的文字保真证据链。
+
+### A09: 几何守恒型重纹理
+- **功能描述**: 对输入图像中的指定物体或区域,在**严格保持其原始三维几何结构、全局光照方向、表面曲率与阴影投射关系的前提下**,仅替换其表面材质表现(如将哑光塑料变为镜面金属、木质变为大理石、布料变为碳纤维),杜绝因材质变更引发的形变、透视错误或光照逻辑冲突。
+- **判定标准**: 替换前后物体轮廓、边缘高光位置、阴影长度/角度、反射内容(如镜面中映出的天花板)完全一致;使用MeshLab等工具导入深度图,两图深度误差<0.5%;材质变化不导致物体“浮起”或“凹陷”等Z轴幻觉。
+- **实现方式**: FLUX.2 [max]: 原生“High-fidelity Retexturing”模式(文档2.3明确命名,输入原图+材质描述即可);ComfyUI: ControlNet Depth + Inpainting节点 + 材质LoRA(需手动对齐深度图);Stable Diffusion XL: T2I-Adapter(depth)+ IP-Adapter(材质参考图)+ 局部重绘mask;Midjourney v8: --cref(原始材质图)+ --sref(目标材质图)+ 材质描述提示词(如 "retextured with brushed stainless steel, same lighting and curvature");Nano Banana Pro: 手绘草图/线稿输入 + 材质描述提示词 + 4K输出保障曲率细节
+- **典型场景**: 家具材质方案比选、汽车外观定制预览、工业设计表面工艺验证、虚拟试衣间布料模拟。
+- **来源依据**: 案例1(台灯替换,强调“对角度进行了调整”——说明模型理解原始台灯的3D姿态并保持)与案例2(白天转夜间亮灯,需维持灯具结构与光源位置关系)——知乎用户实测反馈聚焦于“角度调整”“亮灯效果自然”,指向几何与光照逻辑的联合守恒,而非简单贴图;Nano Banana Pro 用例9(手绘转效果图)隐含对原始几何的深度理解。
+
+### A10: 单图解剖结构可信锚定
+- **功能描述**: 在单张生成图像中,确保角色/生物/复杂人造物的关键解剖或机械结构符合真实世界物理与生物学约束(如五指完整且比例合理、肘关节弯曲方向符合骨骼联动、齿轮咬合无穿模、翅膀骨骼支撑逻辑自洽),杜绝AI常见幻觉性结构错误。
+- **判定标准**: 图像中所有可辨识的生物关节(手/足/脊柱/下颌)、机械连接点(铰链/轴承/传动轴)、拓扑关键区域(耳垂与颅骨连接、指甲与指腹过渡)在放大至200%后,结构连续性无断裂、穿插、数量错误或反向扭曲;经专业解剖/机械设计师目检,无需标注即判定为“结构可信”。
+- **实现方式**: Midjourney v8: --cref(角色参考图)+ Omni Reference 全向特征对齐机制(自动提取并锚定输入图中的3D结构先验);Midjourney v8: --raw + --stylize 0(抑制风格化干扰,强化结构忠实度);ComfyUI: ControlNet OpenPose + Depth + Normal三节点联合约束(需手动配准,非开箱即用)
+- **典型场景**: 角色原画终稿交付、医学教育插图生成、工业设备概念图验证、动画绑定前结构审查。
+- **来源依据**: V8 vs V7 对比评测明确指出“V8 在解剖结构(手部)方面有明显优势”,且案例1(荒漠游侠)与案例2(水下居民)虽用v6.1生成,但用户反馈“v8生成同一提示词时,手部不再出现六指或熔融状”,说明该能力是v8的核心突破点;官方教程视频《THE BEST TIP for Generating Text in Midjourney V8》中亦强调“Omni Reference 可让模型理解‘手臂不是一根棍子’”。
+
+### A11: 多图协同角色一致性锚定
+- **功能描述**: 在单次生成任务中,利用最多14张异构参考图(不同角度、表情、光照、服饰、姿态),自动提取并融合其中共享的身份特征(面部ID、体型比例、标志性配饰、行为习惯),构建统一、鲁棒的角色身份表征,并在目标提示词约束下稳定输出符合该身份的所有变体,解决多源参考间的特征冲突与歧义。
+- **判定标准**: 同一提示词下生成的多张图(如正面/侧脸/半身/全身)中,关键身份标识(如左眉痣、耳垂形状、手表表带纹路、惯用手姿势)在像素级(≤2px误差)和语义级(如“always holds coffee cup in right hand”)均保持一致;任意两张参考图输入后,生成结果不出现“特征漂移”(如某图强调圆脸,另一图强调长脸,输出却呈现第三种脸型)。
+- **实现方式**: Nano Banana Pro: 原生多图输入(最多14张)+ `tools=[{"google_search": {}}]`(可选,用于校准时代/文化特征)+ 提示词中显式声明身份约束(如 "same person as all reference images, consistent facial structure and body proportions");ComfyUI: Multi-IP-Adapter节点组(需手动配置权重与冲突消解逻辑,非开箱即用);Seedream 5.0 Lite: 原生多图输入(最多14张)+ 自动特征融合与冲突消解机制(文档3.3节“一致性达92%” + 用例3“游戏角色多场景展示”)
+- **典型场景**: 虚拟网红全平台形象统一(TikTok/Instagram/官网头图)、跨国品牌代言人多语言广告一致性、影视预演中主角多状态快照生成、法律文书中的当事人形象存证。
+- **来源依据**: 使用介绍3.5节“最多14张图片输入,保持角色和品牌一致性”;用例1(AI Influencer创作)强调“full-body photo of a 25-year-old female fashion influencer”需与多张参考图身份对齐;用例10(8人团队照)要求“these 8 people”在合成中各自身份不混淆,反向验证其多图身份分离与锚定能力;Seedream用例3 + 3.3节 + 竞品对比表。
+
+### A12: 实时动态数据可视化
+- **功能描述**: 将实时、结构化的外部动态数据(如当前城市气温/湿度、国际金价每盎司报价、单日电影票房TOP3榜单)自动解析为符合人类认知习惯的视觉符号(温度计/箭头/柱状图/徽章),并按设计逻辑(极简/信息图/海报式)无缝嵌入目标图像构图中,确保数据准确、符号可读、布局专业、风格统一。
+- **判定标准**: 生成图像中数据数值与真实世界完全一致(误差为0);所有可视化元素(如温度刻度、金价单位、票房数字字体)符合领域规范且无歧义;同一提示词下重复生成10次,数据符号位置、比例、配色一致性≥95%;无需后期PS即可直接用于新闻资讯页或数据看板。
+- **实现方式**: Seedream 5.0 Lite: 原生联网检索 + Chain-of-Thought 推理引擎(自动选择最优图表类型与空间布局)+ 数据符号微调头(文档3.1/技术亮点1&2);*暂无其他工具原生支持端到端数据→图表→图像闭环;FLUX.2/Nano Banana Pro 需额外人工设计图表模板并作为参考图输入,非自动解析*
+- **典型场景**: 新闻客户端实时天气卡片、金融App金价行情图、影视平台票房日榜海报、教育类APP实时科学数据演示。
+- **来源依据**: 用例1「实时信息可视化」——明确列出“当前天气预报可视化”“实时金价走势图”“最新票房数据图表”三类典型,并强调“业界首创的联网检索能力”;竞品对比表将“实时联网”单列为核心特性;技术亮点2再次确认“可生成包含实时天气、新闻、金价等动态信息的图像”,且区别于简单文字叠加,强调其“可视化”本质。
+```

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 0
examples/tool_research/atomic_cap/0/atomic_capabilities_detail.json


+ 110 - 0
examples/tool_research/atomic_cap/0/atomic_capabilities_index.json

@@ -0,0 +1,110 @@
+[
+  {
+    "id": "A01",
+    "name": "多视角角色结构一致性保持",
+    "description": "保持同一角色多视角下三维结构属性一致,支持3D建模。",
+    "criteria": "关键特征空间可对齐;关键点叠加误差≤3像素。",
+    "tools": ["ComfyUI", "Midjourney v8"],
+    "scenarios": ["游戏角色原画交付", "3D资产前期设计"],
+    "source_summary": "案例1要求一致性高、可直用于3D建模,依赖CharTurn与ControlNet协同。"
+  },
+  {
+    "id": "A02",
+    "name": "产品级对象跨背景一致性保持",
+    "description": "同一产品在不同背景下保持材质、形态、标识等本体特征不变。",
+    "criteria": "裁剪主体后图层差值无可见差异(ΔE<2);Logo清晰无扭曲。",
+    "tools": ["FLUX.2 [max]", "ComfyUI", "Nano Banana Pro"],
+    "scenarios": ["电商详情页制作", "产品宣传册批量生成"],
+    "source_summary": "案例2强调单工作流生成100+张图,依赖IP-Adapter保产品本体;FLUX官方迁移至产品。"
+  },
+  {
+    "id": "A03",
+    "name": "老照片语义级修复与自然上色",
+    "description": "修复划痕褪色并自然上色,保留神态、时代特征与历史感。",
+    "criteria": "五官比例合理、皮肤有颗粒感;色彩符合历史常识;无新增虚构细节。",
+    "tools": ["ComfyUI"],
+    "scenarios": ["家族史数字化", "博物馆档案修复"],
+    "source_summary": "案例3明确使用CodeFormer、Tile VAE、DeOldify三级链,强调清晰自然与色彩真实。"
+  },
+  {
+    "id": "A04",
+    "name": "时序连贯的AI动画基础帧生成",
+    "description": "生成≥8帧运动连续、姿态合理、形变可控的短动画序列。",
+    "criteria": "相邻帧光流平滑;关节运动符合生物力学;物体透视变化合理。",
+    "tools": ["ComfyUI", "Midjourney v8"],
+    "scenarios": ["社交媒体15秒短视频", "游戏过场动画草稿"],
+    "source_summary": "案例4指出结合ControlNet保角色一致性,批量生成后合成视频,解决时序稳定性。"
+  },
+  {
+    "id": "A05",
+    "name": "建筑线稿到多风格效果图的可控转换",
+    "description": "将同一建筑线稿精准转为多风格效果图,结构严格一致。",
+    "criteria": "所有风格图与线稿叠加时承重墙/开窗/楼梯完全重合;不破坏功能逻辑。",
+    "tools": ["FLUX.2 [max]", "ComfyUI", "Midjourney v8", "Nano Banana Pro"],
+    "scenarios": ["建筑方案汇报", "地产营销多版本提案"],
+    "source_summary": "案例5明确用线稿+ControlNet+LoRA切换风格;FLUX文档与用例9共同支撑结构守恒。"
+  },
+  {
+    "id": "A06",
+    "name": "工作流驱动的批量自动化生成",
+    "description": "封装图像流程为参数化批处理任务,全程自动调度执行。",
+    "criteria": "N变量自动触发N次独立生成;文件名含变量标识;失败可单独重试。",
+    "tools": ["FLUX.2 [max]", "ComfyUI", "Seedream 5.0 Lite"],
+    "scenarios": ["电商SKU图量产", "A/B测试素材生成"],
+    "source_summary": "案例2/4/5均强调批量生成;FLUX API与Seedream技术亮点4明确支持参数化调度。"
+  },
+  {
+    "id": "A07",
+    "name": "实时语境感知生成",
+    "description": "自动生成并融合最新外部事实信息,确保内容时效与事实准确。",
+    "criteria": "时效元素(如比分)与真实世界完全一致;模糊提示能自主解析时间窗口。",
+    "tools": ["FLUX.2 [max]", "Nano Banana Pro", "Seedream 5.0 Lite"],
+    "scenarios": ["新闻配图快速生产", "体育营销实时素材"],
+    "source_summary": "案例8/9验证自动搜索比赛结果与历史事件;Nano Banana与Seedream用例佐证接地能力。"
+  },
+  {
+    "id": "A08",
+    "name": "高保真图内文字渲染",
+    "description": "嵌入指定文字,确保笔画清晰、无错字、排版规范、达印刷级质量。",
+    "criteria": "放大200%仍清晰可辨;字符面板比对字体/间距/基线一致;错误率≤0.5%。",
+    "tools": ["FLUX.2 [max]", "DALL-E 3", "Midjourney v8", "Nano Banana Pro"],
+    "scenarios": ["电商主图文案植入", "品牌VI系统输出"],
+    "source_summary": "案例6/7强调文字清晰;竞品对比表将文本渲染列为独立评测维度。"
+  },
+  {
+    "id": "A09",
+    "name": "几何守恒型重纹理",
+    "description": "替换物体表面材质,严格保持原始几何结构、光照与阴影关系。",
+    "criteria": "轮廓/高光/阴影/反射完全一致;深度图误差<0.5%;无Z轴幻觉。",
+    "tools": ["FLUX.2 [max]", "ComfyUI", "Stable Diffusion XL", "Midjourney v8", "Nano Banana Pro"],
+    "scenarios": ["家具材质方案比选", "汽车外观定制预览"],
+    "source_summary": "案例1/2聚焦角度调整与亮灯效果自然;Nano Banana用例9隐含几何深度理解。"
+  },
+  {
+    "id": "A10",
+    "name": "单图解剖结构可信锚定",
+    "description": "确保角色/生物/机械结构符合真实物理与生物学约束。",
+    "criteria": "关键关节/连接点放大200%无断裂穿插;专业目检即判结构可信。",
+    "tools": ["Midjourney v8", "ComfyUI"],
+    "scenarios": ["角色原画终稿交付", "医学教育插图生成"],
+    "source_summary": "V8 vs V7评测指出解剖(尤其手部)明显提升;Omni Reference教程强调理解三维结构。"
+  },
+  {
+    "id": "A11",
+    "name": "多图协同角色一致性锚定",
+    "description": "融合最多14张异构参考图,构建统一鲁棒角色身份并稳定输出变体。",
+    "criteria": "关键身份标识像素级(≤2px)与语义级均一致;无特征漂移。",
+    "tools": ["Nano Banana Pro", "ComfyUI", "Seedream 5.0 Lite"],
+    "scenarios": ["虚拟网红全平台形象统一", "跨国品牌代言人广告一致性"],
+    "source_summary": "使用介绍3.5节明确14图输入保一致性;用例1/10及Seedream 3.3节验证多图身份锚定。"
+  },
+  {
+    "id": "A12",
+    "name": "实时动态数据可视化",
+    "description": "自动解析实时结构化数据,生成专业、准确、风格统一的视觉图表。",
+    "criteria": "数据数值零误差;符号符合领域规范;重复生成布局一致性≥95%。",
+    "tools": ["Seedream 5.0 Lite"],
+    "scenarios": ["新闻客户端天气卡片", "金融App金价行情图"],
+    "source_summary": "用例1明确三类实时数据可视化;竞品对比表与技术亮点2强调端到端联网→图表→图像闭环。"
+  }
+]

+ 224 - 0
examples/tool_research/atomic_cap/1/atomic_capabilities.md

@@ -0,0 +1,224 @@
+### CAP-001: 文本到图像生成
+- **功能描述**: 根据文字描述(正向/反向提示词)生成对应图像,是最基础的 AI 图像生成能力
+- **判定标准**: 生成图像在内容、风格、构图上与提示词描述一致;反向提示词中的元素未出现在图像中
+- **实现方式**:
+  - ComfyUI: CheckpointLoader + CLIPTextEncode(正/负)+ EmptyLatentImage + KSampler + VAEDecode + SaveImage 标准文生图工作流
+  - FLUX.2 [max]:直接输入提示词,支持最高 32K tokens、4MP 输出,照片级真实感突出(案例 4 老渔夫肖像)
+  - Midjourney v8: `/imagine prompt: [描述] --v 8`,支持最多 4000 字符提示词,多语言输入(英文效果最佳),默认输出 4 张 1024x1024 PNG;提示词遵循性强,默认偏向摄影写实风格,生成速度约 10-15 秒
+  - Nano Banana Pro (Gemini 3 Pro Image):直接输入提示词,支持 1K/2K/4K 分辨率输出,多种宽高比(1:1 至 21:9),生成速度 8-12 秒,照片级真实感突出(用例 1-3 虚拟网红、专业头像、产品 Mockup)
+  - Seedream 5.0 Lite:直接输入提示词,Chain-of-Thought 推理架构先进行逻辑解析再生成像素,提升提示词遵循性(MagicBench 基准 Prompt Following 维度显著提升);生成速度 <1.2s(1080p);支持 PNG/JPEG 输出;定价 $0.035/张
+- **典型场景**: 从零开始创作数字艺术、生成设计素材、制作博客配图、游戏概念图生成、照片级人像生成、室内设计效果图、角色概念设计
+- **来源依据**: ComfyUI 使用介绍第 2.2 节「文生图工作流搭建(6 步)」;案例 5「建筑效果图快速出图」;FLUX.2 [max] 案例 4「老渔夫肖像」;Midjourney v8 案例 1-5;Nano Banana Pro 使用介绍 5.1 节基础图像生成示例;用例 1「AI Influencer 创作」、用例 2「专业头像生成」、用例 3「电商产品 Mockup」;Seedream 5.0 Lite 使用介绍 1.2 节、2.1 节、实际用例「技术亮点」第 3-4 条
+
+---
+
+### CAP-002: 结构/姿态控制生成
+- **功能描述**: 以线稿、深度图、姿态骨架、法线图等结构信息为约束条件,控制生成图像的构图、姿态或空间结构
+- **判定标准**: 生成图像的主体姿态/空间结构与输入的控制图高度吻合;在保持结构约束的同时,图像内容/风格可自由变化
+- **实现方式**: ComfyUI: ControlNet 节点(Advanced-ControlNet 自定义节点)+ 预处理器节点(OpenPose/Canny/Depth 等)+ KSampler 工作流
+- **典型场景**: 游戏角色多视图生成(控制正/侧/背面姿态)、建筑线稿转效果图、人物姿态指定生成
+- **来源依据**: 案例 1「游戏角色多视图生成」使用 ControlNet 控制角色姿态;案例 5「建筑效果图快速出图」使用线稿作为 ControlNet 输入;使用介绍 4.5 节列出 Advanced-ControlNet 为必备自定义节点
+
+---
+
+### CAP-003: 图像主体一致性保持
+- **功能描述**: 以参考图像为输入,在生成新图像时保持参考图中主体(产品、角色、物体)的外观特征不变,使其出现在不同背景、场景或构图中
+- **判定标准**: 生成图像中的主体与参考图在颜色、形状、纹理、关键特征上高度一致;背景/场景可以自由变化
+- **实现方式**:
+  - ComfyUI: IP-Adapter 节点(IPAdapter 自定义节点包)+ 参考图输入 + KSampler 工作流
+  - FLUX.2 [max]:原生多图参考(最多 10 张),稳定保留人物面部特征、身体比例、表情特点(案例 5 角色一致性保持)
+  - Midjourney v8 `--cref`:角色参考参数,上传角色参考图后生成保持该角色外观特征的新图像;v8 相比 v7 在解剖结构(尤其手部)和角色一致性方面有明显改进
+  - Nano Banana Pro (Gemini 3 Pro Image):原生多图参考(最多 14 张),可同时上传多张角色/产品参考图,稳定保留主体外观特征;技术规格 3.5 节明确说明「保持角色和品牌一致性」
+  - Seedream 5.0 Lite:原生多图参考(最多 14 张),人脸特征、色调、风格等高度稳定,官方标注一致性达 92%;适用于游戏角色多场景展示、品牌营销素材系列、故事板连续画面
+- **典型场景**: 电商产品多背景展示图生成、角色在不同场景中保持外观一致、品牌物料批量生成、游戏角色系列图生成
+- **来源依据**: ComfyUI 案例 2「电商产品图批量生成」使用 IP-Adapter;FLUX.2 [max] 案例 5「角色一致性保持」;Midjourney v8 使用介绍 2.3 节高级功能 `--cref`;Nano Banana Pro 使用介绍 3.5 节「多图像混合」及用例 1「AI Influencer 创作」;Seedream 5.0 Lite 使用介绍 1.2 节、3.3 节;实际用例第 3 条「多图一致性创作」
+
+---
+
+### CAP-004: 风格切换与风格控制
+- **功能描述**: 通过加载不同的风格模型或风格参数,在保持内容/结构基本不变的前提下,将图像渲染为指定的艺术风格;或通过风格参考图引导生成图像的整体视觉风格
+- **判定标准**: 生成图像的视觉风格与目标风格(LoRA/参考图/参数描述)一致;可在同一内容基础上批量切换多种风格并输出
+- **实现方式**:
+  - ComfyUI: LoRA Loader 节点 + CheckpointLoader + KSampler 工作流;批量切换时使用循环/批处理节点遍历多个 LoRA
+  - Midjourney v8 `--sref`:风格参考参数,上传风格参考图,生成图像的视觉风格向参考图靠拢
+  - Midjourney v8 `--stylize [值]`:控制风格化程度(如 `--stylize 250`),值越高越具艺术风格化;`--raw` 参数获得最低风格化的原始输出
+- **典型场景**: 建筑效果图多风格出图(现代/古典/工业)、角色多风格概念图、品牌视觉风格探索、插画风格指定生成
+- **来源依据**: ComfyUI 案例 5「建筑效果图快速出图」中准备多个风格 LoRA;Midjourney v8 使用介绍 2.2 节参数表(--stylize、--raw);2.3 节高级功能(--sref);案例 4「男士西装设计」使用 `--stylize 250`;案例 5「童话城堡插画」通过提示词描述水彩风格
+
+---
+
+### CAP-005: 人脸修复与增强
+- **功能描述**: 对图像中的人脸区域进行专项修复和增强,解决人脸模糊、变形、细节缺失等问题
+- **判定标准**: 修复后人脸清晰度显著提升;五官比例自然,无明显 AI 变形痕迹;与图像其他区域融合自然
+- **实现方式**: ComfyUI: CodeFormer 节点 / Impact-Pack 中的 FaceDetailer 节点 + 图像输入工作流;ReActor 节点用于换脸场景
+- **典型场景**: 老照片人脸修复、AI 生成图中人脸细节增强、批量人像后期处理
+- **来源依据**: 案例 3「老照片修复与上色」使用 CodeFormer 节点进行人脸修复;使用介绍 4.5 节列出 Impact-Pack 和 ReActor 为必备自定义节点
+
+---
+
+### CAP-006: 图像细节增强与高清放大
+- **功能描述**: 对已生成的图像进行分辨率提升和细节增强,在放大的同时补充高频细节(后处理路径,区别于生成阶段直接高清输出的 CAP-016)
+- **判定标准**: 放大后图像分辨率显著提升(如 2x/4x);细节更丰富,无明显模糊或锯齿;整体风格与原图一致
+- **实现方式**: ComfyUI: Ultimate SD Upscale 自定义节点 + Tile ControlNet 模型 + VAEDecode 工作流;大图使用 Tiled VAE 节点避免显存溢出
+- **典型场景**: 低分辨率草图放大为高清成品、建筑效果图细节增强、打印级别图像输出、对已生成图像进行后期放大
+- **来源依据**: 案例 3「老照片修复与上色」使用 Tile 模型进行细节增强;案例 5「建筑效果图快速出图」使用高清修复提升细节;使用介绍 4.5 节列出 Ultimate SD Upscale 为必备自定义节点
+
+---
+
+### CAP-007: 图像上色
+- **功能描述**: 将黑白或低饱和度的图像自动上色,生成色彩自然、符合语义的彩色图像
+- **判定标准**: 上色结果色彩自然,符合图像内容的语义(皮肤、天空、植物颜色合理);无明显色块错误或颜色溢出
+- **实现方式**: ComfyUI: DeOldify 模型节点或类似上色模型节点 + 图像输入 + VAEDecode 工作流
+- **典型场景**: 老照片上色、历史图像彩色化、黑白素描上色
+- **来源依据**: 案例 3「老照片修复与上色」使用 DeOldify 或类似模型对老照片进行上色处理
+
+---
+
+### CAP-008: 批量图像生成
+- **功能描述**: 在单次工作流执行中,通过批处理机制自动循环生成大量图像(不同背景、不同参数、不同种子等),并自动命名保存
+- **判定标准**: 单次执行可生成 N 张图像(N 可配置);每张图像按预设规则变化;自动保存并按规则命名,无需人工干预
+- **实现方式**:
+  - ComfyUI: 批处理循环节点 + EmptyLatentImage(batch_size 参数)+ 多模板输入列表 + SaveImage 自动命名工作流
+  - Nano Banana Pro REST API:Batch API 模式(50% 折扣),程序化批量提交生成任务
+- **典型场景**: 电商产品多背景批量出图、数据集批量生成、多参数对比实验
+- **来源依据**: ComfyUI 案例 2「电商产品图批量生成」通过批处理节点循环生成 100+ 张产品图;使用介绍 4.2 节提及 batch_size 性能优化技巧;Nano Banana Pro 使用介绍第 6 节定价信息中提及「Batch API: 50% 折扣」
+
+---
+
+### CAP-009: AI 动画帧序列生成
+- **功能描述**: 基于文本描述或参考图像,生成具有时间连贯性的动画帧序列,使内容产生动态运动效果
+- **判定标准**: 生成的帧序列在内容上连贯,运动自然无明显跳变;帧与帧之间的角色/场景保持一致性;可导出为视频或 GIF
+- **实现方式**: ComfyUI: AnimateDiff 自定义节点 + ControlNet(保持角色一致性)+ KSampler + 视频合成节点工作流
+- **典型场景**: 短视频动画制作、角色动作演示、AI 动态壁纸生成
+- **来源依据**: 案例 4「AI 动画短片制作」使用 AnimateDiff 生成基础动画,结合 ControlNet 保持角色一致性;使用介绍 4.5 节列出 AnimateDiff 为必备自定义节点
+
+---
+
+### CAP-010: 动画帧插值
+- **功能描述**: 在已有动画帧之间插入过渡帧,提升动画的流畅度和帧率,使运动更加平滑自然
+- **判定标准**: 插值后帧率显著提升(如从 8fps 到 24fps);插入的过渡帧与相邻帧在内容和运动上自然衔接;无明显鬼影或模糊
+- **实现方式**: ComfyUI: RIFE 节点(或类似帧插值节点)+ 帧序列输入 + 视频输出工作流
+- **典型场景**: AI 动画流畅度提升、低帧率视频补帧、动态效果增强
+- **来源依据**: 案例 4「AI 动画短片制作」使用 RIFE 或类似节点进行帧插值,使动画更流畅
+
+---
+
+### CAP-011: 工作流自动化与 API 集成
+- **功能描述**: 通过 REST API 将图像生成能力集成到外部系统或自动化流程中,实现程序化调用、任务队列管理和结果获取
+- **判定标准**: 外部程序可通过 API 提交生成任务并获取结果;支持异步任务状态查询;可集成到业务系统或 CI/CD 流程中
+- **实现方式**:
+  - ComfyUI REST API:`POST /prompt` 提交任务 + `GET /history/{id}` 查询结果 + `GET /queue` 队列管理 + Python/任意语言客户端
+  - FLUX.2 [max] REST API:`POST /v1/flux-2-max`(x-key Header 认证),支持文生图、图像编辑、填充等任务类型的程序化调用;使用介绍 4.2 节提供完整端点示例
+  - Nano Banana Pro REST API:使用 `google-genai` SDK(Python ≥1.52.0 / JS/TS ≥1.30),通过 `client.models.generate_content()` 调用,模型 ID `gemini-3-pro-image-preview`;支持 Batch API(50% 折扣);使用介绍 4.3-4.4 节提供完整 SDK 安装和初始化示例
+  - Seedream 5.0 Lite REST API:`POST https://api.byteplus.com/v1/seedream`(Bearer Token 认证),JSON 请求体传入 prompt 及参数;支持 BytePlus、Replicate、Together AI、fal.ai 等多平台接入;生成延迟 <1.2s,适合实时集成场景;定价 $0.035/张
+- **典型场景**: 电商平台自动生成产品图、内容平台批量配图、游戏资产自动化生产流水线
+- **来源依据**: ComfyUI 使用介绍第 3.4 节「API 接口」;FLUX.2 [max] 使用介绍第 4 节「API 接入」及 4.2 节端点示例;Nano Banana Pro 使用介绍 4.3 节「安装 SDK」、4.4 节「初始化客户端」、5.1 节基础调用示例;Seedream 5.0 Lite 使用介绍 4.2 节「API 调用」Python 示例、1.4 节「平台支持」、2.1 节「轻量化部署」
+
+---
+
+### CAP-012: 图像局部重绘
+- **功能描述**: 对图像的指定区域进行重新生成,保持其余区域不变,实现局部内容的替换、修复或扩展
+- **判定标准**: 重绘区域内容按提示词生成,与周围区域在光照、风格、边缘上自然融合;非重绘区域像素保持不变
+- **实现方式**:
+  - ComfyUI: VAE Encode(inpaint 模式)+ 蒙版输入(Mask)+ KSampler(denoise 参数控制重绘强度)+ VAEDecode 工作流
+  - FLUX.2 [max]:图像编辑模式,输入原图 + 文字描述目标变化,无需手动绘制蒙版,支持物体替换(案例 1:台灯替换,自动调整角度)、物体移除(案例 3:去除吊灯并自然修复背景)
+  - Seedream 5.0 Lite:高级编辑模式,支持单图编辑,包括局部元素删除/修改、背景随机替换;通过文字描述指定编辑目标,无需手动绘制蒙版
+- **典型场景**: 修复图像中的瑕疵区域、替换背景中的特定元素、为产品图更换颜色/材质、老照片破损区域修复、物体移除与背景补全
+- **来源依据**: ComfyUI 使用介绍第 1.3 节功能介绍;FLUX.2 [max] 案例 1「物体替换」和案例 3「物体移除」;Seedream 5.0 Lite 使用介绍 3.4 节「高级编辑」;实际用例第 4 条「高级图像编辑」,示例包含局部元素删除/修改、背景随机替换
+
+---
+
+### CAP-013: 实时语境融合生成
+- **功能描述**: 在生成图像时,模型自动执行实时网络搜索,获取当前最新的事件、数据、状态等语境信息,并将其融入生成结果,使图像内容与现实世界的最新状态保持一致
+- **判定标准**: 生成图像中包含了提示词所指向的真实世界最新信息(如比赛结果、当前事件、实时数据);若无网络搜索能力,同样的提示词将无法生成准确内容
+- **实现方式**:
+  - FLUX.2 [max](Grounded Generation,仅此模型独有):直接在提示词中描述需要实时信息的场景,模型自动触发网络搜索并融合结果
+  - Nano Banana Pro (Gemini 3 Pro Image):在生成配置中启用 `tools=[{"google_search": {}}]`(Search Grounding),连接 Google 搜索获取实时数据,可生成准确的信息图表和时事相关图像;使用介绍 3.3 节、5.3 节
+  - Seedream 5.0 Lite:实时联网检索(官方称「业界首创」),在提示词中描述需要实时信息的场景,模型自动触发网络搜索获取天气、金价、票房等实时数据并融入图像生成;使用介绍 3.1 节
+- **典型场景**: 生成包含最新体育赛事结果的图像、反映当前新闻事件的视觉化内容、需要引用实时数据的信息图、基于当前市场数据的图表生成、天气预报可视化、金融数据图表
+- **来源依据**: FLUX.2 [max] 案例 9「足球比赛结果生成」;FLUX.2 [max] 使用介绍 2.1 节「接地式生成」;Nano Banana Pro 使用介绍 3.3 节「搜索接地(Search Grounding)」及 5.3 节代码示例;用例 6「信息图与教育内容」;Seedream 5.0 Lite 使用介绍 1.2 节核心特性「实时联网检索」、3.1 节详细说明;实际用例第 1 条「实时信息可视化」(天气预报、金价走势图、票房数据图表)及「技术亮点」第 2 条
+
+---
+
+### CAP-014: 图像内文字渲染
+- **功能描述**: 在生成的图像中嵌入清晰、可读、拼写正确的指定文字内容,使文字作为图像视觉元素的一部分自然呈现(如产品标签、Logo 文字、标牌、广告语等)
+- **判定标准**: 图像中的文字内容与提示词指定的文字一致;文字清晰可读,无明显拼写错误;文字与图像整体风格和场景自然融合;多语言文字(中/日/西班牙文等)可正确渲染
+- **实现方式**:
+  - FLUX.2 [max]:在提示词中直接指定文字内容(如 `labeled 'AQUA LAGER'`),模型原生支持文字渲染;注意仍有偶尔拼写错误,建议对关键文字进行人工校验
+  - Midjourney v8:相比前代版本有「改进的文本渲染」(核心特性之一),在提示词中直接描述需要渲染的文字内容;官方教程视频专门讲解文字渲染技巧
+  - Nano Banana Pro (Gemini 3 Pro Image):多语言高保真文本渲染(英文、中文、日文、西班牙文等),支持复杂排版布局,文本准确率 94-96%(竞品对比表数据),显著优于 Midjourney V7(71%)和 DALL-E 3(76-78%);使用介绍 3.1 节「完美文本渲染」
+- **典型场景**: 产品包装/标签图生成、Logo 设计、含文字的广告海报生成、标牌/招牌场景生成、多语言版本图像生成、书籍封面标题渲染
+- **来源依据**: FLUX.2 [max] 案例 6「AQUA LAGER 啤酒产品摄影」;案例 7「Logo 设计」;Midjourney v8 使用介绍 1.2 节核心特性「改进的文本渲染」;Nano Banana Pro 使用介绍 3.1 节「完美文本渲染」;竞品对比表文本准确率数据;用例 4「社交媒体广告」(含粗体排版文字)、用例 5「书籍封面设计」(含标题文字)
+
+---
+
+### CAP-015: 场景光照/时段转换
+- **功能描述**: 对已有图像进行全局语义级的光照条件或时段氛围转换(如白天→夜晚、晴天→阴雨、自然光→人工照明),在保持场景结构和主体不变的前提下,整体改变图像的光照氛围
+- **判定标准**: 转换后图像的光照/时段氛围与目标描述一致;场景的空间结构、家具布局、主体形态保持不变;光照变化在整个画面中物理上自洽(如夜间场景中灯光投影合理)
+- **实现方式**: FLUX.2 [max]:图像编辑模式,输入原图 + 描述目标光照/时段的提示词(如 `"将图像做成夜间亮灯的效果"`),模型自动完成全局光照语义转换
+- **典型场景**: 建筑/室内效果图的昼夜版本生成、同一场景不同天气氛围展示、摄影后期光照氛围调整
+- **来源依据**: FLUX.2 [max] 案例 2「夜间效果转换」中用户输入「将图像做成夜间亮灯的效果」,成功将白天室内场景转换为夜间亮灯效果,场景结构保持不变
+
+---
+
+### CAP-016: 生成阶段原生高分辨率输出
+- **功能描述**: 在图像生成阶段直接输出高分辨率图像(而非先生成低分辨率再后期放大),使生成结果在细节密度、结构完整性上优于先生成后放大的路径
+- **判定标准**: 生成图像在原始输出阶段即达到 2K(2048px)级别或以上分辨率;图像细节(如面料纹理、建筑线条、人物五官)在原生尺寸下清晰完整,无放大插值痕迹
+- **实现方式**:
+  - Midjourney v8: `--hd` 参数,原生输出 2048px,最大宽高比 4:1(HD 模式),GPU 成本为标准的 4 倍;可与 `--q 4` 组合使用(总计 16x GPU 成本)
+  - FLUX.2 [max]:支持最高 4MP 输出(约 2000x2000),在生成阶段直接输出高分辨率
+  - Nano Banana Pro (Gemini 3 Pro Image):支持原生 4K 超高分辨率输出($0.24/张),在生成配置中指定 `image_size="4K"`;使用介绍 3.4 节「4K 超高分辨率」及 5.4 节代码示例
+- **典型场景**: 需要直接输出印刷级/展示级图像而无需后期放大处理、时装设计细节展示(面料纹理)、建筑效果图高清直出、产品摄影级图像生成
+- **来源依据**: Midjourney v8 使用介绍 2.2 节参数表(`--hd` 原生 2048px);3.2 节输出规格;3.4 节定价说明(HD 模式 4x GPU 成本);案例 4「男士西装设计」展示面料纹理细节;FLUX.2 [max] 使用介绍;Nano Banana Pro 使用介绍 2.2 节分辨率选项、3.4 节「4K 超高分辨率」、5.4 节 4K 生成代码示例;用例 3「电商产品 Mockup」使用 `"4K resolution"`
+
+---
+
+### CAP-017: 全向参考(Omni Reference)
+- **功能描述**: 以单张或多张参考图像作为综合性参考输入,同时影响生成图像的内容主体、风格氛围、角色特征等多个维度,而非单一维度的参考约束
+- **判定标准**: 生成图像能够综合体现参考图的多维度特征(如同时保留角色外观 + 场景氛围 + 风格倾向);与单一维度参考(仅风格或仅角色)相比,生成结果在多个维度上与参考图的吻合度更高
+- **实现方式**:
+  - Midjourney v8: Omni Reference 功能(Alpha 阶段),在提示词中上传参考图并指定参考类型权重,可同时作用于角色、风格、内容等多个维度;与 `--cref`(专项角色参考)和 `--sref`(专项风格参考)形成能力互补
+- **典型场景**: 基于概念图生成保持整体氛围和角色特征的系列图、以情绪板(mood board)为参考生成符合整体视觉方向的图像、复杂参考场景下需要多维度约束的创作
+- **来源依据**: Midjourney v8 使用介绍 2.3 节高级功能列表中明确列出「全向参考(Omni Reference)」为独立功能,与 `--sref` 和 `--cref` 并列
+
+---
+
+### CAP-018: 个人化风格持久化
+- **功能描述**: 系统学习并记忆用户的历史审美偏好(如对特定色调、构图、风格的倾向),在后续生成中自动应用这些个人化偏好,使生成结果持续符合用户的个人审美风格,无需每次重复描述
+- **判定标准**: 启用个人化后,生成结果在风格倾向上与用户历史偏好一致,无需每次在提示词中重复描述风格偏好;不同用户使用相同提示词时,因个人化设置不同而产生风格差异化的输出
+- **实现方式**:
+  - Midjourney v8: `--p` 参数(个人化系统),基于用户在 Midjourney 平台上的历史生成记录和偏好数据,自动调整生成风格倾向
+- **典型场景**: 长期创作者希望保持个人风格一致性、品牌设计师希望所有生成图像符合品牌视觉调性、摄影师希望 AI 生成图像符合其个人摄影风格
+- **来源依据**: Midjourney v8 使用介绍 2.3 节高级功能列表中明确列出「个人化系统(--p)」为独立功能
+
+---
+
+### CAP-019: 草图/手绘转效果图
+- **功能描述**: 将用户提供的手绘草图、线稿或粗略示意图转换为精美的效果图或写实渲染图,在保持原始构图和空间关系的同时,大幅提升视觉质量和细节丰富度
+- **判定标准**: 输出效果图的构图、空间布局与输入草图保持一致;视觉质量从草图级提升至效果图/写实级;主要结构元素与草图对应,无随意添加或删除
+- **实现方式**:
+  - Nano Banana Pro (Gemini 3 Pro Image):输入手绘草图图片 + 描述目标风格的提示词(如 `"Convert this hand-drawn sketch into a photorealistic architectural rendering, maintain the original composition"`),模型自动完成风格升级转换
+  - ComfyUI: ControlNet(Canny/Lineart 预处理器)+ KSampler 工作流(侧重从草图到效果图的质量跃升)
+- **典型场景**: 建筑/室内设计草图转效果图、产品设计手稿转渲染图、概念艺术草图转精细插画、快速将创意草图可视化为可展示的效果图
+- **来源依据**: Nano Banana Pro 用例 9「手绘转效果图」,场景描述为「将草图转换为精美效果图」,提示词明确要求保持原始构图并转换为写实建筑渲染风格;来源标注为「小红书用户案例」
+
+---
+
+### CAP-020: 多主体场景合成
+- **功能描述**: 将多个独立的人物、角色或物体参考图像合成到同一个场景中,生成包含所有指定主体且各自外观特征保持一致的群体场景图
+- **判定标准**: 生成图像中包含所有指定的主体(人物/物体);每个主体的外观特征与其对应参考图一致;各主体在场景中的位置、比例、光照协调自然,整体构图合理
+- **实现方式**:
+  - Nano Banana Pro (Gemini 3 Pro Image):支持最多 14 张图片输入,可同时上传多个人物/主体参考图 + 场景描述提示词,模型将所有主体合成到指定场景中(如 `"Create a team photo of these 8 people in a modern office setting"`)
+  - FLUX.2 [max]:原生多图参考(最多 10 张),可输入多个主体参考图进行合成
+- **典型场景**: 团队合影生成(将多人照片合成为统一场景)、多角色群像插画、家庭/朋友群体场景合成、多产品组合展示图
+- **来源依据**: Nano Banana Pro 用例 10「多角色场景合成」,场景描述为「将多个人物合成到一个场景中」,示例为将 8 人合成到现代办公室团队照中;技术规格 3.5 节「多图像混合:最多支持 14 张图片输入,保持角色和品牌一致性」
+
+---
+
+### CAP-021: 图像内文字翻译与替换
+- **功能描述**: 识别图像(如漫画、海报、标牌)中已有的文字内容,将其翻译为目标语言,并在保持原有字体风格、排版布局和图像其余部分不变的前提下,将翻译后的文字替换回图像中
+- **判定标准**: 翻译后的文字内容语义准确;替换后的文字在字体风格、大小、位置上与原文字高度一致;图像中非文字区域(背景、人物、图形元素)保持不变;整体视觉效果自然,无明显拼接痕迹
+- **实现方式**:
+  - Nano Banana Pro (Gemini 3 Pro Image):输入含文字的图像 + 翻译指令提示词(如 `"Translate all text in this comic panel from English to Japanese, maintain the original font style and layout"`),模型同时完成识别、翻译和视觉替换
+- **典型场景**: 漫画/图文内容多语言本地化、海报/广告素材的多语言版本生成、产品包装文字多语言适配、教育材料的语言版本转换
+- **来源依据**: Nano Banana Pro 用例 8「漫画与插画翻译」,场景描述为「将漫画中的文字翻译成其他语言」,来源标注为「知乎变现案例」,提示词明确要求保持原字体风格和布局

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 0
examples/tool_research/atomic_cap/1/atomic_capabilities_detail.json


+ 8 - 3
examples/tool_research/config.py

@@ -40,7 +40,7 @@ RUN_CONFIG = RunConfig(
 
 # ===== 任务配置 =====
 
-OUTPUT_DIR = "examples/tool_research/outputs/flux_1"  # 输出目录
+OUTPUT_DIR = "examples/tool_research/outputs/nanobanana_2"  # 输出目录
 
 
 # ===== 基础设施配置 =====
@@ -57,8 +57,13 @@ BROWSER_TYPE = "local"
 HEADLESS = False
 
 # ===== IM 配置 =====
-IM_ENABLED = False                         # 是否启动 IM Client
+IM_ENABLED = True                          # 是否启动 IM Client
 IM_CONTACT_ID = "agent_research"           # Agent 在 IM 系统中的身份 ID
-IM_SERVER_URL = "ws://localhost:8005"      # IM Server WebSocket 地址
+IM_SERVER_URL = "ws://localhost:58005"     # IM Server WebSocket 地址
 IM_WINDOW_MODE = True                      # 窗口模式(True=每次运行消息隔离,推荐)
 IM_NOTIFY_INTERVAL = 10.0                  # 新消息检查间隔(秒)
+
+# ===== Knowledge Manager 配置 =====
+KNOWLEDGE_MANAGER_ENABLED = True           # 是否启动 Knowledge Manager(作为后台 IM Client)
+KNOWLEDGE_MANAGER_CONTACT_ID = "knowledge_manager"  # Knowledge Manager 的 IM 身份 ID
+KNOWLEDGE_MANAGER_ENABLE_DB_COMMIT = False # 是否允许入库(False=只缓存,True=可入库)

+ 52 - 0
examples/tool_research/extract_atomic_capabilities.prompt

@@ -0,0 +1,52 @@
+---
+name: extract_atomic_capabilities
+model: anthropic/claude-sonnet-4.6
+temperature: 0.3
+max_tokens: 16000
+---
+
+$system$
+你是一个专业的能力分析师。你的任务是从工具的使用介绍和实际用例中提取**原子能力**。
+
+## 什么是原子能力?
+
+原子能力是一种**面向需求的、跨工具的高维能力**。它不是某个工具的具体技术实现细节,而是一种独立完整的、可直接面对用户需求的能力单元。
+
+### 核心定义
+- 它是**面向需求**的:每个原子能力都直接对应用户的某一类创作需求
+- 它是**跨工具**的:同一个原子能力可以由不同工具以不同方式实现
+- 它是**不可分割**的:拆分后将无法独立满足任何需求
+- 它是**可组合**的:多个原子能力按顺序组合可形成完整的工序/流水线
+
+### 关于工具的自由度差异
+
+工具分为两类,提取原子能力时请注意区分:
+
+**端到端工具**(如 Midjourney、DALL-E):输入 prompt → 输出图像,能力边界清晰。
+→ 从其参数/功能中直接提取原子能力。
+
+**编排平台型工具**(如 ComfyUI、HTML):内部高度自由,可任意组合节点/模块,能力边界开放。
+→ 不要试图原子化平台本身,而是从其**具体的工作流和用例**中提取原子能力。
+→ 在「实现方式」中标注具体的工作流/方案,而非泛泛地写"ComfyUI"。
+→ 例如:ComfyUI 实现「角色一致性」的方式是「IP-Adapter 节点 + 参考图」,这就是一种实现方案。
+
+### 举例说明
+✅ **正确的原子能力**:「保持角色一致性」— 跨多张图保持同一角色的面部/身体/服装特征不变。这个能力可由 ControlNet、IP-Adapter、--cref 参数、多图参考等不同工具/方式实现,但核心需求是一样的。
+✅ **正确的原子能力**:「图内文字渲染」— 在生成的图像中嵌入清晰可读的指定文字。
+❌ **错误(太底层)**:「使用 KSampler 采样」— 这是具体技术操作,不是面向需求的能力。
+❌ **错误(太底层)**:「设置 --ar 16:9」— 这是参数设置,不是独立能力。
+❌ **错误(可再分)**:「制作电商产品图」— 这可以分解为「背景替换」+「产品一致性保持」+「光照调整」等多个原子能力的组合。
+
+## 原子能力的格式
+
+每个原子能力应包含:
+
+### [能力ID]: [能力名称]
+- **功能描述**: [做什么,满足什么需求]
+- **判定标准**: [怎样算做到了,怎样算没做到]
+- **实现方式**: [列举可实现的工具/方案。端到端工具直接写工具名+参数;编排平台写具体工作流,如「ComfyUI: IP-Adapter节点+参考图输入」]
+- **典型场景**: [什么时候需要用到这个能力]
+- **来源依据**: [从哪些具体用例/文档提炼出来的,简述来源帖子或用例的大概内容]
+
+$user$
+{user_prompt}

+ 325 - 0
examples/tool_research/extract_capabilities_auto.py

@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+"""
+原子能力提取工作流 - 自动化版本
+使用 openrouter (claude-sonnet) 逐个读取工具文档,迭代式提取和融合原子能力
+"""
+
+import asyncio
+import json
+import os
+import re
+import sys
+from pathlib import Path
+
+# 添加项目根目录
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from dotenv import load_dotenv
+load_dotenv()
+
+from agent.llm.openrouter import openrouter_llm_call
+
+# ===== 配置 =====
+BASE_DIR = Path(__file__).parent
+TOOL_RESULTS_DIR = BASE_DIR / "tool_results"
+OUTPUT_FILE = BASE_DIR / "atomic_capabilities.md"
+PROMPT_FILE = BASE_DIR / "extract_atomic_capabilities.prompt"
+
+
+# ===== Prompt 加载(复用 match_nodes.py 的模式) =====
+
+def load_prompt(filepath: str) -> dict:
+    """加载 .prompt 文件,解析 frontmatter 和 $role$ 分段"""
+    text = Path(filepath).read_text(encoding="utf-8")
+
+    config = {}
+    if text.startswith("---"):
+        _, fm, text = text.split("---", 2)
+        for line in fm.strip().splitlines():
+            if ":" in line:
+                k, v = line.split(":", 1)
+                k, v = k.strip(), v.strip()
+                if v.replace(".", "", 1).isdigit():
+                    v = float(v) if "." in v else int(v)
+                config[k] = v
+
+    messages = []
+    parts = re.split(r'^\$(\w+)\$\s*$', text.strip(), flags=re.MULTILINE)
+    for i in range(1, len(parts), 2):
+        role = parts[i].strip()
+        content = parts[i + 1].strip() if i + 1 < len(parts) else ""
+        messages.append({"role": role, "content": content})
+
+    return {"config": config, "messages": messages}
+
+
+def render_messages(prompt_data: dict, variables: dict) -> list[dict]:
+    """用变量替换 prompt 模板中的 {var} 占位符"""
+    rendered = []
+    for msg in prompt_data["messages"]:
+        content = msg["content"]
+        for k, v in variables.items():
+            content = content.replace(f"{{{k}}}", str(v))
+        rendered.append({"role": msg["role"], "content": content})
+    return rendered
+
+
+# ===== 文件读取 =====
+
+def get_all_tool_dirs():
+    """获取所有工具目录"""
+    dirs = sorted([d for d in TOOL_RESULTS_DIR.iterdir() if d.is_dir()])
+    return dirs
+
+
+def read_file(file_path):
+    """读取文件内容"""
+    with open(file_path, 'r', encoding='utf-8') as f:
+        return f.read()
+
+
+def read_tool_files(tool_dir):
+    """读取工具的使用介绍和实际用例"""
+    usage_file = tool_dir / "使用介绍.md"
+    case_file = tool_dir / "实际用例.md"
+
+    content = ""
+    if usage_file.exists():
+        content += "# 使用介绍\n\n" + read_file(usage_file) + "\n\n"
+    if case_file.exists():
+        content += "# 实际用例\n\n" + read_file(case_file)
+
+    return content
+
+
+# ===== 构建 user prompt =====
+
+def build_user_prompt(file_content, tool_name, existing_capabilities=""):
+    """构建每轮迭代的 user prompt"""
+
+    if existing_capabilities:
+        user_prompt = f"""## 当前状态
+
+### 已提取的原子能力
+
+{existing_capabilities}
+
+## 你的工作
+
+1. 仔细阅读下面的工具文档(使用介绍 + 实际用例)
+2. 从中识别出**面向需求的原子能力**(注意:不是工具的技术操作)
+3. 与已有能力对比:
+   - 如果是全新的能力 → 添加,并说明来源
+   - 如果已有能力可由新工具实现 → 融合,在「实现方式」中补充该工具
+   - 如果是多个已有能力的组合 → 不添加,但在「发现的能力组合」中记录
+4. 对于来源依据,要说明从哪个用例/帖子/文档章节提炼的,并简述其大概内容
+"""
+    else:
+        user_prompt = """## 当前状态
+
+这是第一次提取,当前没有已有能力。
+
+## 你的工作
+
+1. 仔细阅读下面的工具文档(使用介绍 + 实际用例)
+2. 从中识别出**面向需求的原子能力**(注意:不是工具的技术操作)
+3. 对于来源依据,要说明从哪个用例/帖子/文档章节提炼的,并简述其大概内容
+"""
+
+    user_prompt += f"""
+## 当前要处理的工具
+
+**工具名称**: {tool_name}
+
+**文档内容**(包含使用介绍和实际用例):
+
+{file_content}
+
+## 输出要求
+
+请按以下格式输出:
+
+# 原子能力清单(更新后)
+
+## 本轮分析
+简要说明从 {tool_name} 中发现了哪些能力,哪些是新的,哪些与已有能力融合了。
+
+## 新增能力
+[列出本次新增的能力,使用上述格式,每个能力都要有来源依据]
+
+## 融合能力
+[列出本次融合/更新的能力,说明新增了哪些实现方式]
+
+## 发现的能力组合
+[列出发现的能力组合关系,例如:能力A + 能力B + 能力C = 完成「电商产品图批量生成」]
+
+## 完整能力清单
+[输出完整的、更新后的原子能力清单,包含所有能力(新增 + 已有 + 融合后的)]
+"""
+
+    return user_prompt
+
+
+# ===== LLM 调用 =====
+
+async def extract_capabilities_from_tool(prompt_data, tool_dir, existing_capabilities=""):
+    """从工具目录提取原子能力"""
+    tool_name = tool_dir.name
+    print(f"\n📖 正在处理: {tool_name}")
+
+    # 读取使用介绍和实际用例
+    content = read_tool_files(tool_dir)
+
+    # 构建 user prompt
+    user_prompt = build_user_prompt(content, tool_name, existing_capabilities)
+
+    # 渲染 prompt 模板
+    messages = render_messages(prompt_data, {"user_prompt": user_prompt})
+
+    # 从 prompt 文件读取配置
+    model = prompt_data["config"].get("model", "anthropic/claude-sonnet-4-20250514")
+    temperature = prompt_data["config"].get("temperature", 0.3)
+    max_tokens = prompt_data["config"].get("max_tokens", 16000)
+
+    try:
+        result = await openrouter_llm_call(
+            messages, model=model, temperature=temperature, max_tokens=max_tokens
+        )
+        response = result["content"]
+
+        # 打印 token 用量
+        pt = result.get("prompt_tokens", 0)
+        ct = result.get("completion_tokens", 0)
+        cost = result.get("cost", 0)
+        print(f"   tokens: {pt} prompt + {ct} completion | cost: ${cost:.4f}")
+
+        # 提取"完整能力清单"部分
+        if "## 完整能力清单" in response:
+            complete_list = response.split("## 完整能力清单")[1].strip()
+        else:
+            complete_list = response
+
+        print(f"✅ {tool_name} 处理完成")
+        return response, complete_list
+
+    except Exception as e:
+        print(f"❌ {tool_name} 处理失败: {e}")
+        return None, existing_capabilities
+
+
+async def generate_json_index(prompt_data, capabilities_md):
+    """把 markdown 格式的能力清单转成简洁 JSON"""
+    prompt = f"""请把以下原子能力清单转成 JSON 数组,每个能力包含以下字段:
+
+```json
+[
+  {{
+    "id": "能力ID",
+    "name": "能力名称",
+    "description": "一句话功能描述",
+    "criteria": "判定标准(简洁)",
+    "tools": ["支持的工具/方案1", "支持的工具/方案2"],
+    "scenarios": ["典型场景1", "典型场景2"],
+    "source_summary": "来源依据的简要概括"
+  }}
+]
+```
+
+要求:
+- 只输出 JSON,不要任何其他文字
+- 保持所有能力,不要遗漏
+- description 控制在 30 字以内
+- criteria 控制在 30 字以内
+
+原子能力清单:
+
+{capabilities_md}
+"""
+
+    model = prompt_data["config"].get("model", "anthropic/claude-sonnet-4-20250514")
+
+    messages = [{"role": "user", "content": prompt}]
+    try:
+        result = await openrouter_llm_call(messages, model=model, temperature=0.1, max_tokens=8000)
+        content = result["content"].strip()
+        # 清理 markdown 代码块包裹
+        if content.startswith("```"):
+            content = content.split("\n", 1)[1]
+            content = content.rsplit("```", 1)[0]
+        # 验证是合法 JSON
+        json.loads(content)
+        return content
+    except Exception as e:
+        print(f"❌ JSON 索引生成失败: {e}")
+        return None
+
+
+# ===== 主流程 =====
+
+async def main():
+    print("🚀 开始提取原子能力...")
+    print()
+
+    # 加载 prompt 模板
+    prompt_data = load_prompt(PROMPT_FILE)
+    model = prompt_data["config"].get("model", "anthropic/claude-sonnet-4-20250514")
+    print(f"🤖 使用模型: {model}")
+    print()
+
+    # 获取所有工具目录
+    tool_dirs = get_all_tool_dirs()
+    print(f"📁 找到 {len(tool_dirs)} 个工具:")
+    for d in tool_dirs:
+        files = list(d.glob("*.md"))
+        print(f"   - {d.name} ({len(files)} 个文件)")
+    print()
+
+    # 迭代处理每个工具
+    existing_capabilities = ""
+    all_responses = []
+
+    for i, tool_dir in enumerate(tool_dirs, 1):
+        print(f"{'='*60}")
+        print(f"进度: [{i}/{len(tool_dirs)}]")
+
+        response, complete_list = await extract_capabilities_from_tool(
+            prompt_data, tool_dir, existing_capabilities
+        )
+
+        if response:
+            all_responses.append({
+                "tool": tool_dir.name,
+                "response": response
+            })
+            existing_capabilities = complete_list
+
+    # 保存最终结果
+    print(f"\n{'='*60}")
+    print("💾 保存结果...")
+
+    # 保存完整能力清单(markdown)
+    OUTPUT_FILE.write_text(existing_capabilities, encoding='utf-8')
+    print(f"✅ 原子能力清单已保存到: {OUTPUT_FILE}")
+
+    # 保存详细过程
+    detail_file = BASE_DIR / "atomic_capabilities_detail.json"
+    with open(detail_file, 'w', encoding='utf-8') as f:
+        json.dump(all_responses, f, ensure_ascii=False, indent=2)
+    print(f"✅ 详细过程已保存到: {detail_file}")
+
+    # 最终一轮:让 LLM 把完整能力清单转成简洁 JSON
+    print(f"\n{'='*60}")
+    print("📋 生成简洁 JSON 索引...")
+    json_result = await generate_json_index(prompt_data, existing_capabilities)
+    if json_result:
+        json_index_file = BASE_DIR / "atomic_capabilities_index.json"
+        with open(json_index_file, 'w', encoding='utf-8') as f:
+            f.write(json_result)
+        print(f"✅ JSON 索引已保存到: {json_index_file}")
+
+    print("\n🎉 所有文件处理完成!")
+
+
+if __name__ == "__main__":
+    os.environ.setdefault("no_proxy", "*")
+    asyncio.run(main())

+ 3191 - 0
examples/tool_research/requirements_sorted.json

@@ -0,0 +1,3191 @@
+{
+  "requirements": [
+    {
+      "requirement_id": "REQ_055",
+      "requirement_text": "生成人物局部特写画面,如放大呈现嘴巴咬食物、手持物品、耳朵佩戴饰品、鼻子、指甲等身体局部细节,画面填充感强,细节清晰可见",
+      "source_subtree": {
+        "parent_node": "景别角度",
+        "parent_id": 15366,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度"
+      },
+      "source_nodes": [
+        "特写近景",
+        "景别角度"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "664c38f0000000001303c21f",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "672ed3b6000000003c017f82",
+        "67862d98000000001a01f443",
+        "67bee0df000000002802acd1",
+        "67d55ec7000000000e004e69",
+        "67e6398f000000001d005ebb",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "6911532d000000000503bd18",
+        "692c3402000000000d03b7b7",
+        "692d3b99000000001e022295",
+        "69535514000000001e032b26",
+        "6964d4bf000000001a031a54",
+        "69672e2d000000001a026263",
+        "697638e8000000001a025711",
+        "697a20e9000000001a033338",
+        "697b64c5000000001a021517"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001 通过提示词描述指定身体局部部位(如'extreme close-up of fingers holding a ring, filling the frame')控制景别和构图,生成局部特写画面;CAP-016 配合高分辨率输出确保局部细节(纹理、质感)清晰可见,满足画面填充感强且细节突出的要求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_056",
+      "requirement_text": "生成人物近景半身或胸部以上的画面,突出人物面部表情和情绪,背景适当虚化,让观看者能清楚看到人物的神态与互动感",
+      "source_subtree": {
+        "parent_node": "景别角度",
+        "parent_id": 15366,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度"
+      },
+      "source_nodes": [
+        "特写近景",
+        "景别角度"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "664c38f0000000001303c21f",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "672ed3b6000000003c017f82",
+        "67862d98000000001a01f443",
+        "67bee0df000000002802acd1",
+        "67d55ec7000000000e004e69",
+        "67e6398f000000001d005ebb",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "6911532d000000000503bd18",
+        "692c3402000000000d03b7b7",
+        "692d3b99000000001e022295",
+        "69535514000000001e032b26",
+        "6964d4bf000000001a031a54",
+        "69672e2d000000001a026263",
+        "697638e8000000001a025711",
+        "697a20e9000000001a033338",
+        "697b64c5000000001a021517"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-005"
+      ],
+      "capability_combination": "CAP-001 通过提示词描述近景半身构图(如'half-body portrait, chest up, shallow depth of field, blurred background, expressive facial emotion')控制景别、背景虚化和情绪氛围;CAP-005 对生成图像中的人脸区域进行增强修复,确保面部表情细节清晰自然、五官比例准确,强化神态与互动感的呈现。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_057",
+      "requirement_text": "生成产品或物品的极近距离特写图,如食物截面、商品细节、小物件放大展示,画面主体占满画幅,质感和纹理清晰突出",
+      "source_subtree": {
+        "parent_node": "景别角度",
+        "parent_id": 15366,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度"
+      },
+      "source_nodes": [
+        "特写近景",
+        "景别角度"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "664c38f0000000001303c21f",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "672ed3b6000000003c017f82",
+        "67862d98000000001a01f443",
+        "67bee0df000000002802acd1",
+        "67d55ec7000000000e004e69",
+        "67e6398f000000001d005ebb",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "6911532d000000000503bd18",
+        "692c3402000000000d03b7b7",
+        "692d3b99000000001e022295",
+        "69535514000000001e032b26",
+        "6964d4bf000000001a031a54",
+        "69672e2d000000001a026263",
+        "697638e8000000001a025711",
+        "697a20e9000000001a033338",
+        "697b64c5000000001a021517"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016",
+        "CAP-006"
+      ],
+      "capability_combination": "CAP-001 通过提示词描述极近距离微距构图(如'macro close-up of food cross-section, filling the entire frame, sharp texture detail')控制景别和主体占满画幅;CAP-016 在生成阶段直接输出高分辨率图像,确保质感和纹理在原生尺寸下清晰完整;CAP-006 作为补充后处理手段,对已生成图像进行细节增强和放大,进一步突出纹理质感。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_091",
+      "requirement_text": "生成拟人化动物角色表情包:用AI生成具有丰富表情和情绪的卡通动物形象(如毛茸茸的红色马、灰色驴),能够呈现出沮丧、无奈、委屈等多种情绪状态,配合不同场景背景(办公室、草地、室内),整体风格介于3D皮克斯动画和水彩插画之间,适合搭配幽默文案使用",
+      "source_subtree": {
+        "parent_node": "泛化概括",
+        "parent_id": 15904,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/泛化概括"
+      },
+      "source_nodes": [
+        "构成单元",
+        "表现载体",
+        "风格基调",
+        "表达技法"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "6634a322000000001e01bcd5",
+        "673c37610000000007029ced",
+        "67e224cc000000000602a6c5",
+        "685b68c300000000150226bd",
+        "6865ec61000000000b02c53b",
+        "6880a7a7000000000b02f5a6",
+        "6892d47c0000000025018c4f",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68bf8639000000001c03efd2",
+        "68ca143d000000001202c3de",
+        "68f5976e000000000700dd28",
+        "6913cafd000000000703402b",
+        "692cc7ab000000001b030110",
+        "692d3b99000000001e022295",
+        "693f425a000000001e00ed26",
+        "6960924b000000001a037a1c",
+        "6964ab0e000000001a035c04"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)作为核心能力,通过详细提示词描述角色外观、情绪状态和场景背景生成基础图像;CAP-004(风格切换与风格控制)通过风格参考图或LoRA控制3D皮克斯与水彩插画混合风格;CAP-003(图像主体一致性保持)确保同一角色(红色马、灰色驴)在不同情绪和场景中保持外观一致性,实现系列表情包的角色统一",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_092",
+      "requirement_text": "制作图文混排的知识科普长图:以深青色/蓝绿色为底色背景,将心理学等知识内容拆分为多个板块,每个板块搭配风格统一的插画小图(奇幻风格人物、动物等),文字与插图穿插排布,整体呈现出版式清晰、视觉层次丰富的杂志风格科普图文效果",
+      "source_subtree": {
+        "parent_node": "泛化概括",
+        "parent_id": 15904,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/泛化概括"
+      },
+      "source_nodes": [
+        "组织方式",
+        "表现载体",
+        "风格基调",
+        "表达技法"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "6634a322000000001e01bcd5",
+        "673c37610000000007029ced",
+        "67e224cc000000000602a6c5",
+        "685b68c300000000150226bd",
+        "6865ec61000000000b02c53b",
+        "6880a7a7000000000b02f5a6",
+        "6892d47c0000000025018c4f",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68bf8639000000001c03efd2",
+        "68ca143d000000001202c3de",
+        "68f5976e000000000700dd28",
+        "6913cafd000000000703402b",
+        "692cc7ab000000001b030110",
+        "692d3b99000000001e022295",
+        "693f425a000000001e00ed26",
+        "6960924b000000001a037a1c",
+        "6964ab0e000000001a035c04"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 生成各板块所需的奇幻风格插画小图;CAP-004 控制所有插画保持统一的奇幻视觉风格;CAP-014 在图像中渲染文字内容",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以生成单张插画(CAP-001)、控制风格统一(CAP-004)、渲染图内文字(CAP-014),但核心缺口在于:1)多板块图文混排的版式编排能力——将多张插画与多段文字按杂志排版逻辑组织为一张完整长图,现有原子能力表中没有专项的版式/排版合成能力;2)长图画布的整体构图控制——如何将深青色底色、多个文字板块、多张插图统一排布在一张长图中,超出了单纯文生图的能力范围。需要调研:是否有支持多元素版式合成的AI工具(如支持模板化排版的图像生成工具),或是否需要结合设计工具(如Canva、Figma)进行后期排版合成。"
+    },
+    {
+      "requirement_id": "REQ_093",
+      "requirement_text": "生成室内空间效果图:用AI渲染出具有温暖奶油色调的室内场景,包含拱形门洞、藤编家具、自然光影等元素,整体呈现出地中海或法式复古风格的高质感室内设计效果,光线柔和、色调统一,适合作为家居内容的视觉展示",
+      "source_subtree": {
+        "parent_node": "泛化概括",
+        "parent_id": 15904,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/泛化概括"
+      },
+      "source_nodes": [
+        "构成单元",
+        "风格基调",
+        "表现载体"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "6634a322000000001e01bcd5",
+        "673c37610000000007029ced",
+        "67e224cc000000000602a6c5",
+        "685b68c300000000150226bd",
+        "6865ec61000000000b02c53b",
+        "6880a7a7000000000b02f5a6",
+        "6892d47c0000000025018c4f",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68bf8639000000001c03efd2",
+        "68ca143d000000001202c3de",
+        "68f5976e000000000700dd28",
+        "6913cafd000000000703402b",
+        "692cc7ab000000001b030110",
+        "692d3b99000000001e022295",
+        "693f425a000000001e00ed26",
+        "6960924b000000001a037a1c",
+        "6964ab0e000000001a035c04"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述室内场景元素(拱形门洞、藤编家具、自然光影)、色调(温暖奶油色)和风格(地中海/法式复古)直接生成室内效果图,Midjourney v8或FLUX.2 [max]均有室内设计效果图典型场景支持;CAP-004(风格切换与风格控制)通过风格LoRA或--sref参数精确控制地中海/法式复古风格呈现;CAP-016(生成阶段原生高分辨率输出)确保输出高质感、细节丰富的展示级效果图",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_061",
+      "requirement_text": "生成具有强烈色彩对比的艺术插画,整体画面以高饱和度的红色与蓝色为主色调,两种颜色形成鲜明的冷暖对撞,背景大面积纯色铺底,视觉冲击力极强",
+      "source_subtree": {
+        "parent_node": "色彩强化",
+        "parent_id": 15896,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化"
+      },
+      "source_nodes": [
+        "色彩强化",
+        "对比度调节"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "684e2d44000000002100cca7",
+        "68538f7c000000002400805b",
+        "689b158f000000001b03e512",
+        "6964be3900000000210282a4",
+        "6964beb3000000002103361a",
+        "6968ef250000000021033c7c",
+        "6969068e000000000d008b48",
+        "696b537f00000000220398ad",
+        "696b658e000000001a01d2ef",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "6971ec6f000000001a02d248",
+        "6975b361000000002202d7e8",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过详细提示词描述高饱和度红蓝对比色调、冷暖对撞、大面积纯色背景等视觉要素直接生成目标插画;CAP-004 可通过风格 LoRA 或 --sref 风格参考图进一步强化特定艺术插画风格,确保色彩饱和度和视觉冲击力符合预期。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_062",
+      "requirement_text": "在以暗色或单色为主的画面中,用局部的高饱和亮色(如红色心脏、橙色暖光窗口、金黄色星光)作为点睛之笔,让视线自然聚焦到这个色彩亮点上,形成强烈的视觉引导",
+      "source_subtree": {
+        "parent_node": "色彩强化",
+        "parent_id": 15896,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化"
+      },
+      "source_nodes": [
+        "局部点缀",
+        "色彩强化"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "684e2d44000000002100cca7",
+        "68538f7c000000002400805b",
+        "689b158f000000001b03e512",
+        "6964be3900000000210282a4",
+        "6964beb3000000002103361a",
+        "6968ef250000000021033c7c",
+        "6969068e000000000d008b48",
+        "696b537f00000000220398ad",
+        "696b658e000000001a01d2ef",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "6971ec6f000000001a02d248",
+        "6975b361000000002202d7e8",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过精细提示词描述暗色/单色主基调 + 局部高饱和亮色元素(如红色心脏、橙色窗口、金黄星光)及其位置关系,直接生成具有视觉引导效果的画面;CAP-004 可通过风格参考图或 LoRA 强化特定的明暗对比插画风格,辅助实现局部色彩点缀的艺术效果。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_063",
+      "requirement_text": "生成整体色调统一、饱和度偏高的场景图,例如全画面笼罩在深蓝色夜光氛围或浓郁的赤红土地色调中,让单一主色调主导整个画面,营造出沉浸式的强烈色彩氛围感",
+      "source_subtree": {
+        "parent_node": "色彩强化",
+        "parent_id": 15896,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化"
+      },
+      "source_nodes": [
+        "色彩强化",
+        "对比度调节"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "684e2d44000000002100cca7",
+        "68538f7c000000002400805b",
+        "689b158f000000001b03e512",
+        "6964be3900000000210282a4",
+        "6964beb3000000002103361a",
+        "6968ef250000000021033c7c",
+        "6969068e000000000d008b48",
+        "696b537f00000000220398ad",
+        "696b658e000000001a01d2ef",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "6971ec6f000000001a02d248",
+        "6975b361000000002202d7e8",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过提示词明确描述单一主色调(深蓝夜光/赤红土地)、高饱和度、全画面色调统一等要素,直接生成沉浸式氛围场景图;CAP-004 可通过色调风格 LoRA 或 --sref 风格参考图进一步锁定特定色彩氛围风格,确保主色调在整个画面中的主导性和一致性。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_028",
+      "requirement_text": "将猫咪表情包图片与各种场景素材(办公室、食物、产品、背景环境等)合成拼贴在一起,让猫咪看起来自然地处于这些场景中,形成多格并排的拼贴版式",
+      "source_subtree": {
+        "parent_node": "素材重组",
+        "parent_id": 15888,
+        "context_path": "/root/呈现/视觉/影像制作/剪辑组接/素材重组"
+      },
+      "source_nodes": [
+        "拼贴并置",
+        "混剪重组"
+      ],
+      "source_posts": [
+        "68737e97000000000d027b81",
+        "68789450000000000b01d4a4",
+        "688366bd000000000d024147",
+        "68946e0d000000002500ef6e",
+        "68a43a11000000001c03cc96",
+        "68be928b000000001c0361ea",
+        "68c3933e000000001d00a902",
+        "68d1ebb8000000001203fd96",
+        "68d610800000000012023282",
+        "68d76cd100000000120165e4",
+        "68e0f5750000000007015ff9",
+        "68ec9d6400000000070389be",
+        "68f0b8140000000007008b05",
+        "68f1b573000000000702052e",
+        "68fa029e0000000007022932",
+        "68ff53770000000007000d54"
+      ],
+      "matched_capabilities": [
+        "CAP-020"
+      ],
+      "capability_combination": "CAP-020 可将猫咪参考图与场景描述合成,但多格并排拼贴版式布局能力存在缺口",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-020 可将猫咪图片与场景合成为单张图,CAP-003 可保持猫咪外观一致性出现在不同场景中。但需求的核心是'多格并排拼贴版式'——即将多张合成结果排列为拼贴画布的版式编排能力,现有原子能力表中没有覆盖图像拼版/画布排列/多图网格布局的能力。需要调研:是否有 AI 工具支持直接生成多格拼贴版式,或是否有图像拼版/排版工具(如 ComfyUI 中的图像拼接节点)可实现多图网格排列输出。"
+    },
+    {
+      "requirement_id": "REQ_029",
+      "requirement_text": "把猫咪图片与各类装扮道具(帽子、眼镜、服装、假发等)或其他卡通/玩具素材叠加合成,让不同来源的素材无缝融合成一张完整的搞笑图",
+      "source_subtree": {
+        "parent_node": "素材重组",
+        "parent_id": 15888,
+        "context_path": "/root/呈现/视觉/影像制作/剪辑组接/素材重组"
+      },
+      "source_nodes": [
+        "拼贴并置",
+        "混剪重组"
+      ],
+      "source_posts": [
+        "68737e97000000000d027b81",
+        "68789450000000000b01d4a4",
+        "688366bd000000000d024147",
+        "68946e0d000000002500ef6e",
+        "68a43a11000000001c03cc96",
+        "68be928b000000001c0361ea",
+        "68c3933e000000001d00a902",
+        "68d1ebb8000000001203fd96",
+        "68d610800000000012023282",
+        "68d76cd100000000120165e4",
+        "68e0f5750000000007015ff9",
+        "68ec9d6400000000070389be",
+        "68f0b8140000000007008b05",
+        "68f1b573000000000702052e",
+        "68fa029e0000000007022932",
+        "68ff53770000000007000d54"
+      ],
+      "matched_capabilities": [
+        "CAP-020",
+        "CAP-012",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-020 可将猫咪与道具素材参考图合成到同一画面;CAP-003 可保持猫咪外观特征不变;CAP-012 可对合成结果进行局部修复使边缘融合更自然",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-020 支持多图合成,CAP-003 可保持猫咪主体一致性,CAP-012 可做局部修复融合。但需求强调'不同来源素材无缝融合'——即将现有的猫咪照片与现有的道具素材图像进行精确叠加合成(类似图层合成/抠图贴合),而非重新生成。现有能力更偏向'生成'而非'精确像素级叠加合成'。需要调研:是否有 AI 工具支持将两张现有图像进行精确的前景/背景分离后叠加合成,以及是否有自动抠图+图层合成的 AI 能力(如 ComfyUI 中的背景移除节点 + 图层合并节点)。"
+    },
+    {
+      "requirement_id": "REQ_030",
+      "requirement_text": "在同一张图中将多只猫咪或同一只猫咪的不同姿态照片拼接组合,配合文字标注形成对话或对比效果的多格拼图",
+      "source_subtree": {
+        "parent_node": "素材重组",
+        "parent_id": 15888,
+        "context_path": "/root/呈现/视觉/影像制作/剪辑组接/素材重组"
+      },
+      "source_nodes": [
+        "拼贴并置",
+        "混剪重组"
+      ],
+      "source_posts": [
+        "68737e97000000000d027b81",
+        "68789450000000000b01d4a4",
+        "688366bd000000000d024147",
+        "68946e0d000000002500ef6e",
+        "68a43a11000000001c03cc96",
+        "68be928b000000001c0361ea",
+        "68c3933e000000001d00a902",
+        "68d1ebb8000000001203fd96",
+        "68d610800000000012023282",
+        "68d76cd100000000120165e4",
+        "68e0f5750000000007015ff9",
+        "68ec9d6400000000070389be",
+        "68f0b8140000000007008b05",
+        "68f1b573000000000702052e",
+        "68fa029e0000000007022932",
+        "68ff53770000000007000d54"
+      ],
+      "matched_capabilities": [
+        "CAP-014",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-003 可保持猫咪外观一致性;CAP-014 可在图像中渲染文字标注内容;但多格拼图版式编排能力缺失",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-003 可处理多张猫咪参考图保持一致性,CAP-014 可渲染对话文字/标注文字。但需求核心是'多格拼图'版式——将多张独立照片排列为多格对比/对话版式(类似漫画分格或表情包多格布局),现有原子能力表中没有覆盖多图网格排版/画布拼接的能力。需要调研:是否有 AI 工具或 ComfyUI 节点支持将多张图像自动排列为多格拼图版式,并支持在格间添加文字标注或对话气泡。"
+    },
+    {
+      "requirement_id": "REQ_075",
+      "requirement_text": "生成具有强烈光影对比的场景图,画面中光源明显(如阳光折射、水面反光、彩虹色光晕),暗部极深、亮部极亮,整体呈现出戏剧性的明暗反差和光线质感",
+      "source_subtree": {
+        "parent_node": "光影质感",
+        "parent_id": 15897,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感"
+      },
+      "source_nodes": [
+        "后期技法",
+        "材质纹理"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "68843a4d000000001c037591",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6960924b000000001a037a1c",
+        "6964573a000000000d00800e",
+        "6964ab0e000000001a035c04",
+        "696b528900000000210333ea",
+        "696b52dd000000002202c60a",
+        "696d7ac4000000000e03e459",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "697569b0000000001a02448c",
+        "6975b32c000000002102bc55"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成):通过在提示词中精确描述戏剧性光影效果(如 chiaroscuro、dramatic lighting、god rays、caustics、rainbow lens flare、deep shadows、blown highlights 等专业光影词汇),利用 FLUX.2 [max] 或 Midjourney v8 的照片级真实感生成能力,直接生成具有强烈明暗对比和光线质感的场景图。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 可通过提示词描述光影风格来生成具有一定光影对比的图像,但对于'暗部极深、亮部极亮'的精确曝光控制、彩虹色光晕/水面反光等复杂物理光学效果的精准还原,仅靠文本提示词的控制精度存在不确定性。现有能力表中缺少专项的光照物理渲染控制能力(如 HDR 色调映射控制、光线追踪参数调节)。需要调研:1)各模型对极端明暗对比(HDR 风格)的提示词响应效果;2)是否有专门的光效控制 LoRA 或 ComfyUI 节点(如光线追踪、焦散效果节点)可实现更精准的光影控制。"
+    },
+    {
+      "requirement_id": "REQ_076",
+      "requirement_text": "生成带有明显颗粒感或纸张纹理的插画风格图片,画面整体像是印刷在粗糙介质上,物体表面有细腻的颗粒噪点或手工绘制的笔触肌理",
+      "source_subtree": {
+        "parent_node": "光影质感",
+        "parent_id": 15897,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感"
+      },
+      "source_nodes": [
+        "材质纹理",
+        "后期技法"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "68843a4d000000001c037591",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6960924b000000001a037a1c",
+        "6964573a000000000d00800e",
+        "6964ab0e000000001a035c04",
+        "696b528900000000210333ea",
+        "696b52dd000000002202c60a",
+        "696d7ac4000000000e03e459",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "697569b0000000001a02448c",
+        "6975b32c000000002102bc55"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)+ CAP-004(风格切换与风格控制):使用 CAP-001 生成基础插画内容,同时通过 CAP-004 加载专项风格 LoRA(如 grain texture LoRA、risograph LoRA、screen print LoRA)或在 Midjourney v8 中使用 --sref 上传带颗粒纸张纹理的风格参考图,将整体画面渲染为印刷粗糙介质风格;提示词中补充 film grain、paper texture、risograph、letterpress、hand-drawn brush strokes 等关键词强化效果。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 + CAP-004 组合可以在风格层面生成带颗粒感和笔触肌理的插画,但'细腻的颗粒噪点'和'粗糙介质印刷感'属于后期叠加效果,仅靠生成阶段的提示词和风格 LoRA 可能无法精确控制颗粒密度、纸张纹理强度等参数。现有能力表中缺少专项的后期纹理叠加能力(如将噪点/纸张纹理图层以特定混合模式叠加到生成图像上的 ComfyUI 节点)。需要调研:1)ComfyUI 中是否有图像后期纹理叠加节点(如 Image Blend、Texture Overlay);2)专项颗粒感/印刷风格 LoRA 的可用性和效果;3)是否可通过 img2img 将生成图像与纹理素材合成来精确控制肌理效果。"
+    },
+    {
+      "requirement_id": "REQ_077",
+      "requirement_text": "生成室内场景时,能真实还原不同材质的质感细节,如木地板的纹路光泽、布艺沙发的绒毛感、大理石茶几的光滑反射、藤编家具的编织纹理等",
+      "source_subtree": {
+        "parent_node": "光影质感",
+        "parent_id": 15897,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感"
+      },
+      "source_nodes": [
+        "材质纹理"
+      ],
+      "source_posts": [
+        "681c64ce000000002200554c",
+        "68843a4d000000001c037591",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6960924b000000001a037a1c",
+        "6964573a000000000d00800e",
+        "6964ab0e000000001a035c04",
+        "696b528900000000210333ea",
+        "696b52dd000000002202c60a",
+        "696d7ac4000000000e03e459",
+        "696ede36000000001a028e03",
+        "6970693f000000002102bec2",
+        "697069b7000000002202d264",
+        "69706a0600000000210282bd",
+        "697569b0000000001a02448c",
+        "6975b32c000000002102bc55"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)+ CAP-016(生成阶段原生高分辨率输出):使用 CAP-001 通过详细的材质描述提示词(如 oak wood floor with visible grain and glossy finish、velvet sofa with soft pile texture、polished marble table with reflective surface、rattan weave pattern 等)生成室内场景;结合 CAP-016 使用 Midjourney v8 --hd 参数或 Nano Banana Pro 4K 输出,在高分辨率下确保木纹、绒毛、大理石纹路、藤编等细节清晰可见。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 + CAP-016 组合可以生成具有一定材质质感的高清室内场景,但对于多种不同材质在同一场景中同时精准还原(尤其是绒毛感的微观纤维细节、藤编的精细编织结构、大理石的真实反射高光)存在挑战,单纯依赖提示词描述的控制精度有限。现有能力表中缺少专项的材质 PBR 渲染控制能力。需要调研:1)专项材质 LoRA(木纹、大理石、布料、藤编)在 ComfyUI 中的可用性;2)FLUX.2 [max] 或 Midjourney v8 对复杂多材质室内场景的实际还原效果;3)是否可通过 ControlNet 材质图或法线图输入来增强特定材质的质感表现。"
+    },
+    {
+      "requirement_id": "REQ_085",
+      "requirement_text": "生成具有统一色调风格的插画场景,整体画面使用高度协调的单一色系(如全蓝紫色调的火车风景、全粉紫色调的奇幻海洋),让画面中所有元素的颜色都偏向同一个色相,营造出梦幻沉浸的视觉氛围",
+      "source_subtree": {
+        "parent_node": "饱和度调节",
+        "parent_id": 15941,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化/饱和度调节"
+      },
+      "source_nodes": [
+        "单色饱和度",
+        "饱和度调节"
+      ],
+      "source_posts": [
+        "67bee0df000000002802acd1",
+        "67e37ff8000000001c008b5e",
+        "686f606c00000000120167b5",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68c15181000000001b01c358",
+        "68e8cac8000000000700da88",
+        "69002ba70000000007008bcc",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6964573a000000000d00800e",
+        "6964be3900000000210282a4",
+        "696ede36000000001a028e03",
+        "69756d90000000001a020c81",
+        "6975b2d7000000002200b4ae",
+        "6975b3c50000000022020356"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过提示词直接描述目标色调(如'全蓝紫色调'、'monochromatic blue-purple palette')引导生成统一色系画面;CAP-004 可通过风格参考图(--sref)或 LoRA 进一步强化特定色调风格,两者组合可实现单一色系的梦幻插画场景生成",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_086",
+      "requirement_text": "生成色彩鲜艳、多色并置的视觉冲击画面,画面中同时出现多种高饱和度的颜色搭配(如复古拼贴风格中的粉色、蓝色、橙色并置,或彩色条纹波浪地形),让整体色彩浓烈饱满、视觉张力强烈",
+      "source_subtree": {
+        "parent_node": "饱和度调节",
+        "parent_id": 15941,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化/饱和度调节"
+      },
+      "source_nodes": [
+        "配色饱和度",
+        "饱和度调节"
+      ],
+      "source_posts": [
+        "67bee0df000000002802acd1",
+        "67e37ff8000000001c008b5e",
+        "686f606c00000000120167b5",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68c15181000000001b01c358",
+        "68e8cac8000000000700da88",
+        "69002ba70000000007008bcc",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6964573a000000000d00800e",
+        "6964be3900000000210282a4",
+        "696ede36000000001a028e03",
+        "69756d90000000001a020c81",
+        "6975b2d7000000002200b4ae",
+        "6975b3c50000000022020356"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过提示词明确描述多种高饱和度颜色组合(如'vibrant pink, blue, orange, highly saturated, bold color palette')及风格关键词(如'retro collage'、'colorful stripes')直接引导生成高饱和多色画面;CAP-004 可通过风格参考图或 LoRA 强化复古拼贴等特定高饱和风格,两者组合可实现视觉张力强烈的多色并置效果",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_087",
+      "requirement_text": "生成低饱和度或去色风格的极简画面,整体色彩纯度降低,呈现出克制、安静的视觉质感(如黑白灰调的海洋孤舟场景,或接近无彩色的素雅插画),与高饱和度画面形成鲜明对比",
+      "source_subtree": {
+        "parent_node": "饱和度调节",
+        "parent_id": 15941,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性/色彩强化/饱和度调节"
+      },
+      "source_nodes": [
+        "单色饱和度",
+        "配色饱和度"
+      ],
+      "source_posts": [
+        "67bee0df000000002802acd1",
+        "67e37ff8000000001c008b5e",
+        "686f606c00000000120167b5",
+        "689b158f000000001b03e512",
+        "68b15f32000000001d00ef75",
+        "68c15181000000001b01c358",
+        "68e8cac8000000000700da88",
+        "69002ba70000000007008bcc",
+        "696078f70000000022038479",
+        "696079a10000000022031521",
+        "6964573a000000000d00800e",
+        "6964be3900000000210282a4",
+        "696ede36000000001a028e03",
+        "69756d90000000001a020c81",
+        "6975b2d7000000002200b4ae",
+        "6975b3c50000000022020356"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001 通过提示词描述低饱和度或去色风格(如'desaturated, monochrome, black and white, muted tones, minimalist')引导生成极简低饱和画面;CAP-004 可通过风格参考图(--sref)或黑白/素雅风格 LoRA 进一步精确控制去色程度和极简视觉质感,两者组合可稳定实现低饱和度插画效果",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_046",
+      "requirement_text": "制作图文卡片式科普内容:每张卡片包含统一的标题样式、编号序列、配套插图(卡通/示意图风格)和说明文字,多张卡片拼成一组,整体风格统一、排版清晰,适合健康养生、步骤教程类内容展示",
+      "source_subtree": {
+        "parent_node": "标注图示",
+        "parent_id": 15886,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示"
+      },
+      "source_nodes": [
+        "图示说明",
+        "标注图示"
+      ],
+      "source_posts": [
+        "669b52720000000025003596",
+        "672de546000000001b02cfeb",
+        "6732f52f000000001b013fdb",
+        "6735b1a0000000001b0137f5",
+        "673d9a58000000000702450b",
+        "67e224cc000000000602a6c5",
+        "67e27e6e000000000b017c96",
+        "6810596c000000002301d1a6",
+        "68f9e8400000000005033268",
+        "69048be90000000005033c79",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "69394a0b000000001f006ce6",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 生成卡通/示意图风格的配套插图;CAP-004 通过风格控制保证多张卡片插图风格统一;CAP-014 在图像中渲染标题文字和说明文字。但三者组合仍无法解决:多张卡片的统一排版布局(编号序列、标题样式、文字与插图的精确位置关系)、多卡片拼合为一组的版式设计,这些属于图文排版合成能力,超出现有原子能力范围。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以生成单张卡通风格插图(CAP-001)、控制风格统一性(CAP-004)、在图像内渲染文字(CAP-014),但缺少以下关键能力:1)多元素精确排版布局能力——将标题、编号、插图、说明文字按固定模板精确排列在卡片内;2)多卡片批量生成并保持版式一致性的模板化能力;3)将多张卡片拼合为一组长图/组图的图像合成排版能力。需要调研是否有支持模板化图文排版合成的AI工具(如支持固定版式的图文生成工具),或是否需要结合设计工具(如Canva API、Adobe Express等)实现排版部分。"
+    },
+    {
+      "requirement_id": "REQ_047",
+      "requirement_text": "制作数据报告类图文内容:包含柱状图、饼图、折线图、词云图、环形图等多种数据可视化图表,配合标题、要点文字说明,整体呈现专业研究报告的视觉风格,色彩搭配统一(如蓝紫色系或橙色系)",
+      "source_subtree": {
+        "parent_node": "标注图示",
+        "parent_id": 15886,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示"
+      },
+      "source_nodes": [
+        "图示说明",
+        "标注图示"
+      ],
+      "source_posts": [
+        "669b52720000000025003596",
+        "672de546000000001b02cfeb",
+        "6732f52f000000001b013fdb",
+        "6735b1a0000000001b0137f5",
+        "673d9a58000000000702450b",
+        "67e224cc000000000602a6c5",
+        "67e27e6e000000000b017c96",
+        "6810596c000000002301d1a6",
+        "68f9e8400000000005033268",
+        "69048be90000000005033c79",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "69394a0b000000001f006ce6",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-013",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-013 可通过实时联网获取真实数据并生成信息图表类图像;CAP-014 可在图像中渲染标题和文字说明。但核心问题在于:AI图像生成工具生成的图表是'看起来像图表的图像',而非基于真实数据精确绘制的数据可视化图表,数值、比例、刻度均不可靠,无法满足专业数据报告的准确性要求。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有AI图像生成能力(CAP-001、CAP-014)可以生成'外观像数据报告'的图像,CAP-013可融合实时数据,但存在根本性缺陷:AI生成的柱状图、饼图、折线图等图表中的数据比例、数值标注、坐标轴刻度均为视觉近似,不能保证数据准确性,不适合真实数据报告场景。需要调研:1)是否有AI工具支持输入真实数据后生成精确的数据可视化图表(如基于代码生成图表的AI工具);2)是否可通过代码生成方式(Python matplotlib/echarts等)结合AI进行风格美化来实现;3)若需求仅为'视觉风格展示'而非真实数据,则CAP-001+CAP-014组合可部分满足外观需求,但需明确需求方对数据准确性的要求。"
+    },
+    {
+      "requirement_id": "REQ_048",
+      "requirement_text": "制作流程图/架构示意图:用箭头、方框、层级结构或立体堆叠图形展示系统架构、业务流程或概念层级关系,配合文字标注说明各模块功能,视觉上清晰呈现逻辑关系",
+      "source_subtree": {
+        "parent_node": "标注图示",
+        "parent_id": 15886,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示"
+      },
+      "source_nodes": [
+        "图示说明",
+        "标注图示"
+      ],
+      "source_posts": [
+        "669b52720000000025003596",
+        "672de546000000001b02cfeb",
+        "6732f52f000000001b013fdb",
+        "6735b1a0000000001b0137f5",
+        "673d9a58000000000702450b",
+        "67e224cc000000000602a6c5",
+        "67e27e6e000000000b017c96",
+        "6810596c000000002301d1a6",
+        "68f9e8400000000005033268",
+        "69048be90000000005033c79",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "69394a0b000000001f006ce6",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成外观上类似流程图/架构图的图像;CAP-014 可在图像中渲染文字标注。但AI图像生成工具无法保证箭头指向逻辑正确、方框层级关系准确、各模块文字标注与对应图形精确对应,生成结果在逻辑结构准确性上不可控。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有AI图像生成能力可以生成'外观像流程图'的图像,但存在根本性问题:1)逻辑结构准确性无法保证——箭头连接关系、层级从属关系、模块间逻辑流向均由模型视觉生成,不能保证与需求描述的逻辑一致;2)文字标注与图形元素的精确对应关系难以控制——CAP-014虽能渲染文字,但无法保证文字精确出现在对应方框内;3)复杂架构图(多层级、多分支)的结构完整性难以通过提示词精确控制。需要调研:1)是否有AI工具支持通过结构化描述(如JSON/YAML/自然语言)生成逻辑准确的流程图/架构图(如基于Mermaid、PlantUML的AI生成工具);2)是否有图表生成类AI工具(如Eraser AI、Whimsical AI等)可满足此需求;3)若需求侧重视觉美化而非逻辑准确性,CAP-001+CAP-014可满足外观需求。"
+    },
+    {
+      "requirement_id": "REQ_007",
+      "requirement_text": "生成真实人物在户外或特定场景中的生活记录照片,画面自然真实,包含儿童在公园、农场等户外环境中玩耍的多角度抓拍效果,光线自然,氛围温馨",
+      "source_subtree": {
+        "parent_node": "实景拍摄",
+        "parent_id": 15908,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄"
+      },
+      "source_nodes": [
+        "拍摄概述",
+        "实景拍摄"
+      ],
+      "source_posts": [
+        "67316440000000001b02e75e",
+        "675c19320000000002017d1f",
+        "6776b27d0000000013018545",
+        "67b2a7f7000000002802a0d7",
+        "682ede8f000000002202bff2",
+        "685f974300000000120144db",
+        "6911532d000000000503bd18",
+        "691d3112000000001e036559",
+        "692d3b99000000001e022295",
+        "692fa7e0000000001e039786",
+        "693d0b1d000000001e02ba36",
+        "69535514000000001e032b26",
+        "69672e2d000000001a026263"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001 负责根据提示词生成户外场景中儿童玩耍的照片级真实感图像(FLUX.2 [max] 或 Nano Banana Pro 均支持照片级真实感输出,可描述公园/农场场景、自然光线、温馨氛围);若需要保持特定儿童人物外观一致性,可结合 CAP-003 上传参考图保持人物特征;多角度抓拍效果可通过多次生成不同构图角度的提示词实现",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 可生成照片级真实感的户外儿童场景图像,CAP-003 可保持特定人物外观一致性。但核心问题在于:需求强调的是'真实人物'的生活记录照片,即基于真实存在的特定儿童人物进行场景生成,这涉及真实人脸的高度还原与多角度一致性保持。现有 CAP-003 虽支持角色一致性,但对真实儿童人脸在多角度自然抓拍场景下的还原精度是否足够,以及是否存在伦理/合规限制(真实儿童人脸生成),需要进一步调研各工具的实际表现和使用限制。"
+    },
+    {
+      "requirement_id": "REQ_008",
+      "requirement_text": "制作将真实人物照片合成到趣味场景中的创意图片,例如把人物缩小放入超市肉类托盘包装内、或与冰雕翅膀等道具结合形成视觉错位的幽默效果",
+      "source_subtree": {
+        "parent_node": "实景拍摄",
+        "parent_id": 15908,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄"
+      },
+      "source_nodes": [
+        "拍摄概述",
+        "实景拍摄"
+      ],
+      "source_posts": [
+        "67316440000000001b02e75e",
+        "675c19320000000002017d1f",
+        "6776b27d0000000013018545",
+        "67b2a7f7000000002802a0d7",
+        "682ede8f000000002202bff2",
+        "685f974300000000120144db",
+        "6911532d000000000503bd18",
+        "691d3112000000001e036559",
+        "692d3b99000000001e022295",
+        "692fa7e0000000001e039786",
+        "693d0b1d000000001e02ba36",
+        "69535514000000001e032b26",
+        "69672e2d000000001a026263"
+      ],
+      "matched_capabilities": [
+        "CAP-020",
+        "CAP-003",
+        "CAP-001",
+        "CAP-012"
+      ],
+      "capability_combination": "CAP-020 支持将真实人物参考图与场景合成(如将人物放入超市托盘场景);CAP-003 保持人物外观特征一致性;CAP-001 生成趣味背景场景(超市托盘包装、冰雕翅膀等);CAP-012 可对合成结果进行局部调整修复,使人物与场景融合更自然",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力组合(CAP-020 + CAP-003 + CAP-001)可以实现将人物参考图合成到新场景中,并保持人物外观。但该需求的核心难点在于:1)视觉错位/比例操控(人物缩小放入托盘)需要精确控制人物与场景的相对比例和透视关系,现有能力表中没有明确支持'比例/透视精确控制合成'的能力;2)真实人物照片的高保真合成(非卡通/插画风格)在复杂错位场景中的融合自然度是否达标;3)冰雕翅膀等道具与人物的光影融合效果。需要调研 Nano Banana Pro 或 FLUX.2 [max] 在复杂比例错位合成场景下的实际表现,以及是否有专项的图像合成/抠图工具可配合使用。"
+    },
+    {
+      "requirement_id": "REQ_009",
+      "requirement_text": "生成真实场景的多图拼贴展示图,将同一地点或主题的多张实拍照片拼合为一张图文并茂的内容图,适合用于地点打卡、产品展示或生活记录类帖子",
+      "source_subtree": {
+        "parent_node": "实景拍摄",
+        "parent_id": 15908,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄"
+      },
+      "source_nodes": [
+        "拍摄概述",
+        "实景拍摄"
+      ],
+      "source_posts": [
+        "67316440000000001b02e75e",
+        "675c19320000000002017d1f",
+        "6776b27d0000000013018545",
+        "67b2a7f7000000002802a0d7",
+        "682ede8f000000002202bff2",
+        "685f974300000000120144db",
+        "6911532d000000000503bd18",
+        "691d3112000000001e036559",
+        "692d3b99000000001e022295",
+        "692fa7e0000000001e039786",
+        "693d0b1d000000001e02ba36",
+        "69535514000000001e032b26",
+        "69672e2d000000001a026263"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成单张照片级真实感场景图像作为拼贴素材;CAP-014 可在图像中嵌入文字说明(地点标注、标题等)实现图文并茂效果",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力表中缺少专项的'多图拼贴排版合成'能力。该需求的核心是将多张图片按照特定版式(九宫格、横排、杂志风等)拼合为一张展示图,并添加文字说明。CAP-001 只能生成单张图像,CAP-014 支持图内文字渲染,但两者组合无法实现多图拼贴排版的核心功能。需要调研:1)是否有 AI 工具支持输入多张图片并自动生成拼贴排版图;2)Nano Banana Pro 的多图输入能力(CAP-020 相关)是否可以用于拼贴排版场景;3)是否需要结合 Canva、Photoshop 等设计工具完成排版,而非纯 AI 生成工具。"
+    },
+    {
+      "requirement_id": "REQ_037",
+      "requirement_text": "生成同一人物在同一场景中多角度、多姿态的即时抓拍效果图,画面呈现自然随意的动态感,如行走、转身、低头、仰望等非摆拍状态,整体风格真实生活化",
+      "source_subtree": {
+        "parent_node": "拍摄方式",
+        "parent_id": 15892,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/拍摄方式"
+      },
+      "source_nodes": [
+        "即时捕捉",
+        "拍摄方式"
+      ],
+      "source_posts": [
+        "68077d02000000001c02dd81",
+        "69003bb30000000004015797",
+        "691acd15000000000402134e",
+        "693f94d80000000019025898",
+        "6964d4bf000000001a031a54",
+        "6965d491000000000e00f9b0",
+        "6969068e000000000d008b48",
+        "696b658e000000001a01d2ef",
+        "6971878d000000001a01e6cc",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003",
+        "CAP-002"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)确保同一人物在多张图中外观一致;CAP-002(结构/姿态控制生成)通过OpenPose骨架控制行走、转身、低头、仰望等不同姿态;CAP-001(文本到图像生成)通过提示词描述抓拍风格、真实生活化氛围和场景",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以做到:用CAP-003保持人物一致性、用CAP-002控制姿态、用CAP-001描述抓拍风格。但核心缺口在于'即时抓拍/非摆拍'的视觉质感——包括运动模糊、随机构图偏移、快门感、画面轻微倾斜等模拟真实抓拍的视觉特征,现有能力表中没有明确支持此类摄影风格模拟的能力。需要调研:1)主流AI图像生成工具是否支持通过提示词或参数模拟抓拍/街拍摄影风格(如运动模糊、随机构图);2)ComfyUI是否有专门的运动模糊或摄影风格LoRA可实现此效果"
+    },
+    {
+      "requirement_id": "REQ_038",
+      "requirement_text": "生成动物(如马)在运动瞬间被捕捉的高动态画面,鬃毛飞扬、肢体伸展,呈现出强烈的瞬间张力和动感,背景简洁以突出主体动态",
+      "source_subtree": {
+        "parent_node": "拍摄方式",
+        "parent_id": 15892,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/拍摄方式"
+      },
+      "source_nodes": [
+        "即时捕捉",
+        "拍摄方式"
+      ],
+      "source_posts": [
+        "68077d02000000001c02dd81",
+        "69003bb30000000004015797",
+        "691acd15000000000402134e",
+        "693f94d80000000019025898",
+        "6964d4bf000000001a031a54",
+        "6965d491000000000e00f9b0",
+        "6969068e000000000d008b48",
+        "696b658e000000001a01d2ef",
+        "6971878d000000001a01e6cc",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述马匹运动瞬间、鬃毛飞扬、肢体伸展、简洁背景等视觉要素,利用FLUX.2[max]或Midjourney v8的照片级写实能力生成高动态画面",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以做到:用CAP-001通过提示词描述马匹运动瞬间的视觉特征(鬃毛飞扬、肢体伸展、简洁背景),FLUX.2[max]和Midjourney v8的写实能力可生成较高质量的动物动态图。但核心缺口在于:1)'高动态瞬间张力'涉及运动模糊、动态拖影等物理摄影效果,现有能力表未明确说明哪个工具能精准控制这类动态摄影效果;2)动物姿态的精准控制(如特定奔跑步态、肢体伸展角度)——CAP-002的姿态控制主要针对人物OpenPose,对动物姿态控制的支持情况未在能力表中说明。需要调研:AI图像生成工具对动物运动姿态的控制能力,以及模拟高速摄影动态效果的实现方式"
+    },
+    {
+      "requirement_id": "REQ_039",
+      "requirement_text": "生成人物在真实日常场景(街头、公园、机场等)中被随手拍下的多张图片拼贴效果,画面构图不刻意、视角多变(含俯拍脚部、镜中自拍、远景抓拍等),整体呈现出碎片化的生活记录感",
+      "source_subtree": {
+        "parent_node": "拍摄方式",
+        "parent_id": 15892,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/拍摄方式"
+      },
+      "source_nodes": [
+        "即时捕捉",
+        "拍摄方式"
+      ],
+      "source_posts": [
+        "68077d02000000001c02dd81",
+        "69003bb30000000004015797",
+        "691acd15000000000402134e",
+        "693f94d80000000019025898",
+        "6964d4bf000000001a031a54",
+        "6965d491000000000e00f9b0",
+        "6969068e000000000d008b48",
+        "696b658e000000001a01d2ef",
+        "6971878d000000001a01e6cc",
+        "697638e8000000001a025711"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)确保多张拼贴图中同一人物外观一致;CAP-001(文本到图像生成)通过提示词分别描述不同场景(街头/公园/机场)和不同视角(俯拍脚部/镜中自拍/远景抓拍)生成多张单图;拼贴排版需额外处理",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以做到:用CAP-003+CAP-001生成多张保持人物一致性的不同场景、不同视角单图。但存在两个核心缺口:1)'多张图片拼贴效果'——将多张图像排列为拼贴版式(如照片墙、胶片条、随机散落感)是一种图像合成/排版能力,现有能力表中没有明确的拼贴版式生成能力;2)'碎片化生活记录感'的视觉质感——包括随机构图、画面倾斜、边缘暗角、胶片颗粒等模拟随手拍的风格特征,能力表未明确支持。需要调研:1)AI图像生成工具是否支持直接生成多图拼贴版式效果(如在单张图像中呈现多张照片拼贴的视觉效果);2)模拟随手拍/生活记录风格的提示词策略或专用LoRA/风格模型是否存在"
+    },
+    {
+      "requirement_id": "REQ_067",
+      "requirement_text": "生成具有强烈视觉冲击力的超现实主义风格图像:画面以大地色系(深红、赭石、深蓝)为主调,将白马、牛仔等元素置于极简的荒漠/盐湖场景中,营造出孤寂、神秘、如油画般的电影感氛围",
+      "source_subtree": {
+        "parent_node": "艺术风格",
+        "parent_id": 15889,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格"
+      },
+      "source_nodes": [
+        "视觉冲击",
+        "绘画艺术"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "65eea166000000000d00c6d8",
+        "661dbf91000000001a0119b6",
+        "669b52720000000025003596",
+        "677b5460000000000b00d33e",
+        "686cd3a5000000000d0180b0",
+        "68a4107f000000001c00e8e9",
+        "68f9e8400000000005033268",
+        "692a535f0000000019026d5b",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述超现实主义风格、大地色系色调、白马牛仔荒漠场景等内容生成基础图像;CAP-004(风格切换与风格控制)通过加载油画/超现实主义风格LoRA或使用Midjourney --sref参数引导整体视觉风格,确保电影感油画氛围的精准呈现。两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_068",
+      "requirement_text": "生成3D卡通风格的拟人化动物角色,角色具有毛绒质感和丰富的表情神态,能够在不同生活场景(办公室、卧室、户外)中呈现出喜怒哀乐等情绪状态,整体风格类似皮克斯动画",
+      "source_subtree": {
+        "parent_node": "艺术风格",
+        "parent_id": 15889,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格"
+      },
+      "source_nodes": [
+        "绘画艺术",
+        "视觉冲击"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "65eea166000000000d00c6d8",
+        "661dbf91000000001a0119b6",
+        "669b52720000000025003596",
+        "677b5460000000000b00d33e",
+        "686cd3a5000000000d0180b0",
+        "68a4107f000000001c00e8e9",
+        "68f9e8400000000005033268",
+        "692a535f0000000019026d5b",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述3D卡通风格、毛绒质感、皮克斯风格拟人化动物角色生成基础图像;CAP-004(风格切换与风格控制)通过加载皮克斯/3D卡通风格LoRA确保风格一致性;CAP-003(图像主体一致性保持)使用IP-Adapter或--cref参数,在切换不同场景(办公室/卧室/户外)和不同表情时保持角色外观特征一致。三者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_069",
+      "requirement_text": "制作融合插画风格的信息图文海报:以卡通机器人/科技感插图作为视觉主体,搭配醒目的彩色标题文字和数据图表,整体呈现出活泼又专业的视觉效果",
+      "source_subtree": {
+        "parent_node": "艺术风格",
+        "parent_id": 15889,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格"
+      },
+      "source_nodes": [
+        "绘画艺术",
+        "极简风格"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "65eea166000000000d00c6d8",
+        "661dbf91000000001a0119b6",
+        "669b52720000000025003596",
+        "677b5460000000000b00d33e",
+        "686cd3a5000000000d0180b0",
+        "68a4107f000000001c00e8e9",
+        "68f9e8400000000005033268",
+        "692a535f0000000019026d5b",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词生成卡通机器人/科技感插画风格的视觉主体;CAP-004(风格切换与风格控制)通过LoRA或风格参数确保插画风格的一致性和活泼专业感;CAP-014(图像内文字渲染)在生成图像中嵌入清晰可读的彩色标题文字。但数据图表部分(如柱状图、折线图等精确数据可视化元素)在现有能力中缺乏专项支持,AI图像生成对精确数据图表的还原度和准确性存在不确定性。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以实现:插画风格卡通机器人主体生成(CAP-001+CAP-004)、标题文字渲染(CAP-014)。缺少的是:精确数据图表(柱状图、饼图、折线图等)的生成能力——AI图像生成工具对精确数值可视化的支持较弱,图表中的数字、刻度、比例可能不准确。需要调研:1)是否有AI工具能在图像中生成精确的数据图表元素;2)是否可以通过CAP-013(实时语境融合)结合数据生成准确图表;3)或者是否需要结合外部图表生成工具(如代码生成图表后与AI插画合成)来完整实现该需求。"
+    },
+    {
+      "requirement_id": "REQ_049",
+      "requirement_text": "将整个帖子内容拆分为多个独立小格子并排列成网格或矩阵布局,每个格子承载一个独立的场景或信息单元,格子之间有明显的分隔边界,整体看起来像一张由多张小图拼合而成的大图",
+      "source_subtree": {
+        "parent_node": "版面结构",
+        "parent_id": 15382,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/版面结构"
+      },
+      "source_nodes": [
+        "空间分割",
+        "内容组织",
+        "载体类型"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "669b52720000000025003596",
+        "6711d712000000001b012783",
+        "6731b884000000001901b8d3",
+        "677b5460000000000b00d33e",
+        "68789450000000000b01d4a4",
+        "68a8241a000000001c011403",
+        "68e6ecb90000000003021e34",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001 可以通过提示词描述网格/矩阵布局来生成包含多格子分割的图像,但这属于对构图的语义描述,生成结果的精确性和可控性存疑",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力中 CAP-001 可通过提示词尝试生成网格布局图像,CAP-002 可通过结构控制约束构图,但两者均无法精确控制格子数量、边界粗细、每格内容的独立性和精确排列。核心缺口是:没有专门的版式/排版控制能力,无法像设计软件那样精确定义网格结构。需要调研:1)是否有 AI 工具支持精确的网格版式生成(如指定 3x3、2x4 等格数);2)是否可通过图像拼接后处理工具(非 AI 生成)实现,再结合 AI 生成各格内容;3)ComfyUI 是否有专门的图像拼接/网格排列节点"
+    },
+    {
+      "requirement_id": "REQ_050",
+      "requirement_text": "在同一张图中混合使用多种内容载体形式,例如将真实照片、插画角色、产品图、文字说明、图表等不同类型的视觉元素组合排布在同一个版面内,形成图文混排的丰富视觉层次",
+      "source_subtree": {
+        "parent_node": "版面结构",
+        "parent_id": 15382,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/版面结构"
+      },
+      "source_nodes": [
+        "载体类型",
+        "内容组织"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "669b52720000000025003596",
+        "6711d712000000001b012783",
+        "6731b884000000001901b8d3",
+        "677b5460000000000b00d33e",
+        "68789450000000000b01d4a4",
+        "68a8241a000000001c011403",
+        "68e6ecb90000000003021e34",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014",
+        "CAP-020"
+      ],
+      "capability_combination": "CAP-001 可生成包含多种视觉元素的图像,CAP-014 可在图像中嵌入文字说明,CAP-020 可将多个主体合成到同一场景;三者组合可部分实现图文混排效果",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以:通过 CAP-001 生成混合风格图像、通过 CAP-014 嵌入文字、通过 CAP-020 合成多主体。但核心缺口是:1)无法精确控制不同类型视觉元素(照片风格 vs 插画风格 vs 图表)在同一画面中的独立性和精确位置排布;2)图表类元素(柱状图、折线图等)的精确生成能力不明确;3)版面排布的精确控制(指定某区域放产品图、某区域放文字说明)超出现有提示词控制的精度范围。需要调研:是否有 AI 工具支持分区域指定不同内容类型的版面合成能力,或是否需要结合设计工具(如 Canva、Figma)进行后期排版"
+    },
+    {
+      "requirement_id": "REQ_051",
+      "requirement_text": "以统一的视觉主角(如同一个卡通角色、同一个人物、同一主题场景)贯穿多个分格画面,每个格子呈现该主角在不同场景或状态下的样子,配合文字说明形成系列感强的多格叙事版式",
+      "source_subtree": {
+        "parent_node": "版面结构",
+        "parent_id": 15382,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/版面结构"
+      },
+      "source_nodes": [
+        "内容组织",
+        "空间分割"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "669b52720000000025003596",
+        "6711d712000000001b012783",
+        "6731b884000000001901b8d3",
+        "677b5460000000000b00d33e",
+        "68789450000000000b01d4a4",
+        "68a8241a000000001c011403",
+        "68e6ecb90000000003021e34",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-003",
+        "CAP-014",
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-003 保持主角在不同场景中的外观一致性,CAP-001 生成各格不同场景内容,CAP-014 在图像中嵌入文字说明;但多格叙事版式的整体排版合成仍是缺口",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以:通过 CAP-003(IP-Adapter/--cref/多图参考)保持主角跨场景一致性,通过 CAP-001 生成各场景图像,通过 CAP-014 添加文字说明。但核心缺口是:1)将多张独立生成的分格图像拼合为统一的多格叙事版式(含分隔边界、文字区域、整体排版)的能力不在现有原子能力表中;2)单张图内精确划分多格并在每格内保持主角一致性的能力超出现有工具的直接支持范围。需要调研:1)是否有 AI 工具支持直接生成多格漫画/故事板版式;2)ComfyUI 是否有图像拼接节点可将多张图合并为网格版式;3)是否需要结合设计工具完成最终版式合成"
+    },
+    {
+      "requirement_id": "REQ_064",
+      "requirement_text": "生成具有强烈氛围感的插画风场景图,整体画面以深蓝色调为主,室内外场景都笼罩在宁静的夜色中,窗户透出暖黄色灯光形成冷暖对比,画面质感接近油画或数字绘画风格,传达出静谧、沉思、略带忧郁的情绪氛围",
+      "source_subtree": {
+        "parent_node": "氛围基调",
+        "parent_id": 15890,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/氛围基调"
+      },
+      "source_nodes": [
+        "柔和舒适",
+        "庄重宏大"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "676f8eac000000000902f53e",
+        "67fd299a000000001c00cf5d",
+        "681c64ce000000002200554c",
+        "6880a7a7000000000b02f5a6",
+        "68f568a1000000000301053d",
+        "69003bb30000000004015797",
+        "6960e87a000000000e00c216",
+        "6964beb3000000002103361a"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述深蓝色调、夜色氛围、冷暖对比光效等内容要素生成基础图像;CAP-004(风格切换与风格控制)通过加载油画/数字绘画风格LoRA或使用Midjourney --sref风格参考,将画面渲染为指定插画艺术风格,两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_065",
+      "requirement_text": "制作色彩鲜艳、视觉冲击力强的宣传海报,背景使用渐变色块(蓝紫、橙红等高饱和度色彩),搭配几何抽象图形装饰,文字排版醒目大气,整体呈现出热烈、充满活力的欢庆氛围",
+      "source_subtree": {
+        "parent_node": "氛围基调",
+        "parent_id": 15890,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/氛围基调"
+      },
+      "source_nodes": [
+        "欢庆热闹",
+        "庄重宏大"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "676f8eac000000000902f53e",
+        "67fd299a000000001c00cf5d",
+        "681c64ce000000002200554c",
+        "6880a7a7000000000b02f5a6",
+        "68f568a1000000000301053d",
+        "69003bb30000000004015797",
+        "6960e87a000000000e00c216",
+        "6964beb3000000002103361a"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述高饱和度渐变色块背景、几何抽象图形装饰、欢庆活力氛围等视觉要素生成海报底图;CAP-014(图像内文字渲染)利用Nano Banana Pro或Midjourney v8的文字渲染能力,在海报中嵌入醒目大气的排版文字,两者组合可完整实现该宣传海报需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_066",
+      "requirement_text": "生成暖色调的室内空间效果图,以米白、浅棕、焦糖色为主色调,光线柔和自然,空间布置温馨舒适,整体画面传达出放松、治愈、生活化的温暖氛围",
+      "source_subtree": {
+        "parent_node": "氛围基调",
+        "parent_id": 15890,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/氛围基调"
+      },
+      "source_nodes": [
+        "柔和舒适"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "676f8eac000000000902f53e",
+        "67fd299a000000001c00cf5d",
+        "681c64ce000000002200554c",
+        "6880a7a7000000000b02f5a6",
+        "68f568a1000000000301053d",
+        "69003bb30000000004015797",
+        "6960e87a000000000e00c216",
+        "6964beb3000000002103361a"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述米白/浅棕/焦糖色主色调、柔和自然光线、温馨舒适室内布置等要素,使用ComfyUI、Midjourney v8或Nano Banana Pro等工具直接生成室内空间效果图,完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_010",
+      "requirement_text": "制作多格宫格式信息图,将同类内容(如多种食材搭配方案)拆分为统一风格的小卡片,每格包含标题、食材图片和文字说明,整体排列整齐、色块鲜明,适合一图展示多个并列条目",
+      "source_subtree": {
+        "parent_node": "版面设计",
+        "parent_id": 15906,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计"
+      },
+      "source_nodes": [
+        "辅助元素",
+        "文字动效"
+      ],
+      "source_posts": [
+        "67299a19000000001901483f",
+        "6732f52f000000001b013fdb",
+        "675c0669000000000600cfd7",
+        "6776b27d0000000013018545",
+        "678ce28d000000001603e3a8",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成统一风格的食材图片素材,CAP-014 可在图像中嵌入标题和文字说明;但两者组合仍无法直接实现多格宫格排版布局、色块分割、多卡片整齐排列等版式设计能力",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力中 CAP-001 可生成图像内容,CAP-014 可渲染图内文字,但缺少专门的多格宫格版式排版能力——即将多个卡片(含图片+标题+文字)按网格结构整齐排列、色块分明地组合为一张信息图的能力。需要调研:1)AI 图像生成工具是否支持通过提示词直接生成宫格信息图版式;2)是否有专门的信息图/排版生成工具(如 Canva AI、Adobe Firefly 等)支持此类结构化多格布局;3)Nano Banana Pro 或 FLUX.2 的复杂排版提示词能力是否能覆盖此场景。"
+    },
+    {
+      "requirement_id": "REQ_011",
+      "requirement_text": "在图片上叠加标注元素,如用红色圆点、箭头或emoji符号指向图中特定位置,配合说明文字,实现在真实照片上直观标记关键信息的视觉效果",
+      "source_subtree": {
+        "parent_node": "版面设计",
+        "parent_id": 15906,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计"
+      },
+      "source_nodes": [
+        "辅助元素"
+      ],
+      "source_posts": [
+        "67299a19000000001901483f",
+        "6732f52f000000001b013fdb",
+        "675c0669000000000600cfd7",
+        "6776b27d0000000013018545",
+        "678ce28d000000001603e3a8",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-012",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-012 可对图像局部区域进行重绘以叠加视觉元素,CAP-014 可在图像中渲染说明文字;但两者组合无法精准控制在指定坐标位置叠加箭头、圆点、emoji等标注符号",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力中 CAP-012 局部重绘可在图像特定区域添加内容,CAP-014 可渲染文字,但缺少精准坐标定位叠加标注元素的能力——即在图像指定像素位置精确放置红色圆点、箭头、emoji等标注符号并与说明文字配合的能力。AI 图像生成工具通常无法保证标注元素出现在精确指定的位置。需要调研:1)是否有 AI 工具支持通过坐标或区域描述精准叠加标注元素;2)Nano Banana Pro 或 FLUX.2 的图像编辑模式是否能通过提示词在指定位置添加箭头/圆点标注;3)是否需要结合非 AI 的图像编辑工具(如 Python PIL、Figma)来实现精准标注叠加。"
+    },
+    {
+      "requirement_id": "REQ_012",
+      "requirement_text": "制作图文混排的长图文内容,将大段文字与人物照片、数据表格、流程图等多种视觉元素组合排布在同一版面中,形成类似杂志或报告的专业排版风格",
+      "source_subtree": {
+        "parent_node": "版面设计",
+        "parent_id": 15906,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计"
+      },
+      "source_nodes": [
+        "辅助元素",
+        "文字动效"
+      ],
+      "source_posts": [
+        "67299a19000000001901483f",
+        "6732f52f000000001b013fdb",
+        "675c0669000000000600cfd7",
+        "6776b27d0000000013018545",
+        "678ce28d000000001603e3a8",
+        "69200dec000000001f00b884",
+        "692e7ccf000000001f00a137",
+        "694a6caf000000001f00e112"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成人物照片等图像素材,CAP-014 可在图像中渲染文字内容;但两者组合无法实现大段文字+多种视觉元素(表格、流程图、照片)在同一版面中的专业排版布局",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力中 CAP-001 可生成图像素材,CAP-014 可渲染图内文字,但缺少长图文专业排版能力——即将大段文字、人物照片、数据表格、流程图等异质元素按杂志/报告风格进行多栏布局、层次排版、精确对齐的能力。AI 图像生成工具不擅长处理结构化的长版面排版,尤其是数据表格和流程图的精确渲染。需要调研:1)是否有 AI 工具支持生成包含多种元素混排的长图文版面;2)Nano Banana Pro 的复杂排版布局能力(使用介绍 3.1 节提及支持复杂排版布局)是否能覆盖此场景;3)是否需要结合专业排版工具(如 Adobe InDesign AI 功能、Canva AI)来实现此类专业图文混排效果。"
+    },
+    {
+      "requirement_id": "REQ_013",
+      "requirement_text": "生成以暖黄/米棕色为背景底色的图文排版内容,整体画面呈现温暖、复古的暖色调氛围,适合健康养生、生活方式类主题",
+      "source_subtree": {
+        "parent_node": "色彩调性",
+        "parent_id": 15907,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性"
+      },
+      "source_nodes": [
+        "背景底色",
+        "色调倾向"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "6837f1270000000012006c8e",
+        "6874c80e000000000d027767",
+        "68b10b46000000001c00ca6c",
+        "69535514000000001e032b26",
+        "696078f70000000022038479",
+        "6964573a000000000d00800e",
+        "6969068e000000000d008b48"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过在提示词中详细描述暖黄/米棕色背景、温暖复古暖色调氛围及健康养生主题,可直接生成符合要求的图像。Midjourney v8、FLUX.2 [max]、Nano Banana Pro、Seedream 5.0 Lite 均支持通过提示词精确控制色调和氛围。若需图文排版中含有文字元素,可结合 CAP-014(图像内文字渲染)实现。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_014",
+      "requirement_text": "生成以深色(黑色/深蓝/深紫)为背景底色的海报,搭配霓虹感彩色光效(橙、紫、青等),营造出科技感强烈的冷暖对比配色效果",
+      "source_subtree": {
+        "parent_node": "色彩调性",
+        "parent_id": 15907,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性"
+      },
+      "source_nodes": [
+        "背景底色",
+        "配色组合",
+        "色调倾向"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "6837f1270000000012006c8e",
+        "6874c80e000000000d027767",
+        "68b10b46000000001c00ca6c",
+        "69535514000000001e032b26",
+        "696078f70000000022038479",
+        "6964573a000000000d00800e",
+        "6969068e000000000d008b48"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述深色背景(黑色/深蓝/深紫)、霓虹光效(橙/紫/青)、科技感冷暖对比配色,可直接生成符合要求的海报图像;若海报含文字内容,结合 CAP-014(图像内文字渲染)在生成阶段嵌入清晰可读的文字元素。Midjourney v8 和 FLUX.2 [max] 对科技感霓虹风格的提示词遵循性强,效果尤为突出。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_015",
+      "requirement_text": "生成整体色调偏粉紫、薄荷绿、浅蓝等低饱和度冷色系的插画或场景图,画面呈现出梦幻、静谧的冷色调氛围,颜色搭配柔和克制",
+      "source_subtree": {
+        "parent_node": "色彩调性",
+        "parent_id": 15907,
+        "context_path": "/root/呈现/视觉/视觉气质/色彩调性"
+      },
+      "source_nodes": [
+        "色调倾向",
+        "配色组合"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "6837f1270000000012006c8e",
+        "6874c80e000000000d027767",
+        "68b10b46000000001c00ca6c",
+        "69535514000000001e032b26",
+        "696078f70000000022038479",
+        "6964573a000000000d00800e",
+        "6969068e000000000d008b48"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述粉紫、薄荷绿、浅蓝等低饱和度冷色系、梦幻静谧氛围,可直接生成符合要求的插画或场景图;CAP-004(风格切换与风格控制)可通过 --sref 风格参考图或 LoRA 模型进一步精确控制插画风格和色调倾向,确保低饱和度冷色系的柔和克制感稳定输出。两者结合可高度还原目标视觉效果。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_016",
+      "requirement_text": "生成手部持握或展示物品的特写画面,手势自然,物品清晰呈现,如用手托举饺子、手持卡片、手握手机等,突出手与物品的互动关系",
+      "source_subtree": {
+        "parent_node": "动作姿态",
+        "parent_id": 15901,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现/动作姿态"
+      },
+      "source_nodes": [
+        "手部动作",
+        "创意动作"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "682086dc0000000012003cbd",
+        "683d8695000000001200012a",
+        "68708544000000000d026732",
+        "68e9b94d0000000007036a6a",
+        "692c3402000000000d03b7b7"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002"
+      ],
+      "capability_combination": "CAP-001 通过详细提示词描述手部姿态与物品互动关系生成基础图像;CAP-002 使用 OpenPose 骨架控制手部姿态精度,确保手势自然;Midjourney v8 在 CAP-001 实现中对手部解剖结构有明显改进,可优先选用",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "手部生成是 AI 图像生成的传统难点。CAP-001 可通过提示词描述手部动作,CAP-002 的 OpenPose 可提供骨架约束,但手部细节(手指数量、弯曲角度、与物品的接触关系)在现有能力中仍难以精确控制。Midjourney v8 虽声称改进了手部解剖结构,但尚无明确的手部特写专项能力说明。需要调研:1)是否有专门针对手部生成优化的 ControlNet 模型(如 HandRefiner);2)Midjourney v8 手部改进的实际效果是否满足特写级别的精度要求;3)是否需要结合 CAP-012 局部重绘对手部区域进行修复"
+    },
+    {
+      "requirement_id": "REQ_017",
+      "requirement_text": "生成多人或多只动物同框的协同姿态画面,如两只猫并排躺着穿睡衣、两人一起摆造型抱花束,呈现出同步、对称或互动的视觉效果",
+      "source_subtree": {
+        "parent_node": "动作姿态",
+        "parent_id": 15901,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现/动作姿态"
+      },
+      "source_nodes": [
+        "协同动作",
+        "行为概念"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "682086dc0000000012003cbd",
+        "683d8695000000001200012a",
+        "68708544000000000d026732",
+        "68e9b94d0000000007036a6a",
+        "692c3402000000000d03b7b7"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-020"
+      ],
+      "capability_combination": "CAP-020 支持多主体合成到同一场景;CAP-002 使用 OpenPose 骨架同时控制多个主体的姿态,实现同步/对称/互动的协同姿态;CAP-001 通过提示词描述协同动作语义;三者组合:先用 CAP-002 定义多主体骨架姿态,再用 CAP-001 生成内容,CAP-020 确保多主体外观一致性",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可部分支持:CAP-001 可通过提示词描述多主体协同场景,CAP-020 可合成多主体,CAP-002 可控制姿态骨架。但核心挑战在于:1)多主体同框时各自姿态的精确协同控制(如两只猫完全对称并排)在 OpenPose 多人骨架控制中存在主体混淆风险;2)动物(猫)的姿态骨架控制与人体 OpenPose 不同,需要调研是否有动物姿态 ControlNet 模型;3)多主体协同的对称/同步视觉效果是否能通过现有工具稳定复现,需要调研实际效果"
+    },
+    {
+      "requirement_id": "REQ_018",
+      "requirement_text": "生成创意性的嘴部动作特写,如用嘴唇衔住花朵茎部形成'嘴唇花'的视觉效果,将身体部位与物品结合产生趣味创意画面",
+      "source_subtree": {
+        "parent_node": "动作姿态",
+        "parent_id": 15901,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现/动作姿态"
+      },
+      "source_nodes": [
+        "创意动作",
+        "手部动作"
+      ],
+      "source_posts": [
+        "6649dbe3000000000c018112",
+        "6687d458000000000a026f91",
+        "66daeddb000000002603ea42",
+        "682086dc0000000012003cbd",
+        "683d8695000000001200012a",
+        "68708544000000000d026732",
+        "68e9b94d0000000007036a6a",
+        "692c3402000000000d03b7b7"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001 通过详细提示词描述嘴部与物品的创意互动关系生成图像;可结合 Midjourney v8 或 FLUX.2 [max] 的照片级真实感能力生成特写画面",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有 CAP-001 可通过提示词尝试生成此类创意特写,但存在以下不确定性:1)嘴部特写中嘴唇与细长物体(花茎)的精确接触关系难以通过提示词精确控制,AI 模型容易生成解剖不自然的结果;2)CAP-002 的 OpenPose 主要针对全身/上半身骨架,对嘴部局部动作的精细控制能力不明确;3)此类创意合成画面(身体部位与物品融合产生视觉双关效果)对模型的创意理解和精确执行能力要求较高。需要调研:1)是否有面部/嘴部局部 ControlNet 控制方案;2)FLUX.2 [max] 或 Midjourney v8 对此类创意提示词的实际执行效果;3)是否需要结合 CAP-012 局部重绘对嘴部区域进行精细调整"
+    },
+    {
+      "requirement_id": "REQ_052",
+      "requirement_text": "将多张图片按网格或分区方式拼贴成一张图,每个区域展示不同角度或不同场景,整体画面有清晰的分割感和节奏感",
+      "source_subtree": {
+        "parent_node": "元素编排",
+        "parent_id": 15883,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/元素编排"
+      },
+      "source_nodes": [
+        "排列节奏",
+        "组合关系",
+        "布局规划"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "6732cd8a000000001b02f948",
+        "6746fb5600000000070260ce",
+        "68737e97000000000d027b81",
+        "693f94d80000000019025898",
+        "696f2f97000000000e00e33c"
+      ],
+      "matched_capabilities": [],
+      "capability_combination": "",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力表中没有直接支持多图网格拼贴/分区排版的能力。CAP-001文本到图像生成理论上可以通过提示词描述网格布局来生成类似效果,但无法精确控制每个区域放置指定的已有图片内容。CAP-008批量图像生成只能批量产出独立图像,不能将多张图合并为网格布局。需要调研:1)是否有AI工具支持将多张已有图片自动排列为网格/分区拼贴图的能力;2)ComfyUI是否有图像拼接/网格合成节点;3)是否可通过提示词精确控制多分区画面布局并在每个分区生成不同内容。"
+    },
+    {
+      "requirement_id": "REQ_053",
+      "requirement_text": "在同一画面中合理安排主体与背景的空间关系,让主体(人物、动物、物品)在画面中有明确的视觉焦点,背景简洁或有层次地衬托主体",
+      "source_subtree": {
+        "parent_node": "元素编排",
+        "parent_id": 15883,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/元素编排"
+      },
+      "source_nodes": [
+        "视线焦点",
+        "疏密比例",
+        "空间拓展"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "6732cd8a000000001b02f948",
+        "6746fb5600000000070260ce",
+        "68737e97000000000d027b81",
+        "693f94d80000000019025898",
+        "696f2f97000000000e00e33c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001通过提示词描述主体与背景的构图关系(如主体居中/前景突出/背景虚化等)来控制视觉焦点;CAP-002通过ControlNet结构控制确保主体在画面中的位置和姿态符合预期构图;CAP-003在需要保持特定主体外观的场景下,确保主体特征一致的同时配合背景生成,三者组合可实现主体突出、背景衬托的构图效果。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_054",
+      "requirement_text": "生成包含多个独立小格子的图文排版画面,每个格子内有图片和文字说明,格子之间疏密有致、整齐排列,整体呈现信息图表或内容合集的视觉效果",
+      "source_subtree": {
+        "parent_node": "元素编排",
+        "parent_id": 15883,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/元素编排"
+      },
+      "source_nodes": [
+        "布局规划",
+        "疏密比例",
+        "排列节奏"
+      ],
+      "source_posts": [
+        "61bdc28b0000000001024896",
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "6732cd8a000000001b02f948",
+        "6746fb5600000000070260ce",
+        "68737e97000000000d027b81",
+        "693f94d80000000019025898",
+        "696f2f97000000000e00e33c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001可通过提示词描述信息图表/内容合集的整体视觉风格来生成类似布局;CAP-014支持在图像中渲染清晰可读的文字内容,可在格子内嵌入文字说明。但两者组合仍难以精确控制每个格子的独立内容(图片+文字的精确对应关系)。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力中CAP-001+CAP-014可以生成整体上具有图文排版感的画面,CAP-014支持文字渲染。但核心缺口在于:无法精确控制多个独立格子中每个格子的图片内容与文字说明的精确对应关系,也无法保证格子数量、间距、疏密的精确排版控制。需要调研:1)AI图像生成工具是否支持结构化多格子图文排版的精确布局控制;2)是否有专门的信息图表/卡片合集生成能力;3)ComfyUI是否有支持图文混排网格布局的节点方案。"
+    },
+    {
+      "requirement_id": "REQ_088",
+      "requirement_text": "生成超现实浪漫场景图:将人物置于不可能存在的宏大环境中,如站在地球边缘俯瞰星空、坐在云端长椅上漂浮、在星海上骑行,画面充满梦幻感和史诗级视觉冲击力",
+      "source_subtree": {
+        "parent_node": "审美取向",
+        "parent_id": 15903,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/审美取向"
+      },
+      "source_nodes": [
+        "幻想虚构",
+        "创意表现"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6727171b000000001b01114b",
+        "681c64ce000000002200554c",
+        "689b158f000000001b03e512",
+        "68e6ecb90000000003021e34",
+        "69535514000000001e032b26",
+        "6965d491000000000e00f9b0",
+        "696b5332000000002103c497"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)是核心能力,通过详细的提示词描述超现实场景(地球边缘、云端、星海等)直接生成图像,FLUX.2 [max]、Midjourney v8 或 Nano Banana Pro 均可胜任此类幻想场景生成;CAP-004(风格切换与风格控制)可通过 --sref 或 LoRA 进一步强化梦幻/史诗视觉风格,确保画面氛围符合预期。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_089",
+      "requirement_text": "制作科技感强烈的活动宣传海报:使用深色背景配合橙色、蓝色等高对比度霓虹色调,融合未来感城市或科技场景插图,搭配大号粗体标题文字,整体呈现出硬核、前沿的视觉气质",
+      "source_subtree": {
+        "parent_node": "审美取向",
+        "parent_id": 15903,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/审美取向"
+      },
+      "source_nodes": [
+        "概念气质",
+        "创意表现",
+        "风格融合"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6727171b000000001b01114b",
+        "681c64ce000000002200554c",
+        "689b158f000000001b03e512",
+        "68e6ecb90000000003021e34",
+        "69535514000000001e032b26",
+        "6965d491000000000e00f9b0",
+        "696b5332000000002103c497"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)生成深色背景+霓虹色调+未来感城市场景的海报底图;CAP-004(风格切换与风格控制)通过风格参考图或 LoRA 强化赛博朋克/科技感视觉风格;CAP-014(图像内文字渲染)在海报中嵌入大号粗体标题文字,Nano Banana Pro 文本准确率最高(94-96%),Midjourney v8 也有改进的文本渲染能力,三者组合可完整实现该海报需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_090",
+      "requirement_text": "生成融合东方传统与现代简约的室内空间效果图:以米白、暖棕为主色调,加入拱形门洞、藤编元素、中式花卉装饰画等传统细节,整体呈现温润雅致的新中式生活美学氛围",
+      "source_subtree": {
+        "parent_node": "审美取向",
+        "parent_id": 15903,
+        "context_path": "/root/呈现/视觉/视觉气质/视觉风格/艺术风格/审美取向"
+      },
+      "source_nodes": [
+        "民俗传统",
+        "风格融合",
+        "概念气质"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6727171b000000001b01114b",
+        "681c64ce000000002200554c",
+        "689b158f000000001b03e512",
+        "68e6ecb90000000003021e34",
+        "69535514000000001e032b26",
+        "6965d491000000000e00f9b0",
+        "696b5332000000002103c497"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)是核心能力,通过详细提示词描述室内空间构成要素(米白暖棕色调、拱形门洞、藤编元素、中式花卉装饰画等)直接生成效果图,ComfyUI 案例5「建筑效果图快速出图」和 FLUX.2 [max] 均支持室内设计效果图生成;CAP-004(风格切换与风格控制)通过新中式风格 LoRA 或风格参考图精准控制东方传统与现代简约融合的视觉风格,确保氛围准确。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_034",
+      "requirement_text": "生成真实户外场景中的人物活动照片,画面要呈现自然光线下的街道、公园、游乐场等具体地点环境,人物动作自然生动,背景环境细节丰富真实",
+      "source_subtree": {
+        "parent_node": "场景人物",
+        "parent_id": 15891,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/场景人物"
+      },
+      "source_nodes": [
+        "场景取景",
+        "环境取景"
+      ],
+      "source_posts": [
+        "66f51b90000000002a036660",
+        "6732cd8a000000001b02f948",
+        "677b5460000000000b00d33e",
+        "68a4107f000000001c00e8e9",
+        "68abe632000000001c0348c0",
+        "6913cafd000000000703402b",
+        "69647323000000001a01ef60"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-006"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述户外场景、自然光线、具体地点和人物动作,利用 FLUX.2 [max] 或 Midjourney v8 的照片级真实感能力生成基础图像;CAP-002(结构/姿态控制生成)通过 ControlNet + OpenPose 控制人物动作姿态使其自然生动;CAP-006(图像细节增强与高清放大)对背景环境细节进行增强,使场景细节更丰富真实。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_035",
+      "requirement_text": "生成多人聚集的活动现场图,如会议、展览、户外聚会等场景,画面中需要呈现多个人物同框、有组织的群体互动氛围,背景有明显的活动标识或场地特征",
+      "source_subtree": {
+        "parent_node": "场景人物",
+        "parent_id": 15891,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/场景人物"
+      },
+      "source_nodes": [
+        "群像合影",
+        "集体演出"
+      ],
+      "source_posts": [
+        "66f51b90000000002a036660",
+        "6732cd8a000000001b02f948",
+        "677b5460000000000b00d33e",
+        "68a4107f000000001c00e8e9",
+        "68abe632000000001c0348c0",
+        "6913cafd000000000703402b",
+        "69647323000000001a01ef60"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-020",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述多人聚集场景、活动类型和群体互动氛围生成基础图像;CAP-020(多主体场景合成)在需要特定人物出现时,将多个人物参考图合成到同一活动场景中,保持各人物外观一致;CAP-014(图像内文字渲染)在背景中渲染活动标识、横幅、标牌等文字元素,强化活动场地特征。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_036",
+      "requirement_text": "生成真实物品的特写或陈列展示图,物品摆放清晰、细节可辨,适合用于产品展示或场景道具呈现,画面构图干净突出主体",
+      "source_subtree": {
+        "parent_node": "场景人物",
+        "parent_id": 15891,
+        "context_path": "/root/呈现/视觉/影像制作/实景拍摄/场景人物"
+      },
+      "source_nodes": [
+        "实物呈现"
+      ],
+      "source_posts": [
+        "66f51b90000000002a036660",
+        "6732cd8a000000001b02f948",
+        "677b5460000000000b00d33e",
+        "68a4107f000000001c00e8e9",
+        "68abe632000000001c0348c0",
+        "6913cafd000000000703402b",
+        "69647323000000001a01ef60"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003",
+        "CAP-006",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述物品特写、陈列方式、干净构图和产品展示风格生成基础图像,利用 Nano Banana Pro 或 FLUX.2 [max] 的照片级真实感能力;CAP-003(图像主体一致性保持)在有参考物品图时,确保生成图中物品外观与参考图一致;CAP-016(生成阶段原生高分辨率输出)直接输出高分辨率图像使物品细节清晰可辨;CAP-006(图像细节增强与高清放大)对物品纹理、材质等细节进行后期增强。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_004",
+      "requirement_text": "生成带有特定道具装扮的人物场景图,道具需与人物自然融合,例如猫咪戴假发穿衣服手持书本、人物手持购物篮抱着玩偶玩具等,道具细节清晰可辨",
+      "source_subtree": {
+        "parent_node": "符号元素",
+        "parent_id": 15913,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素"
+      },
+      "source_nodes": [
+        "道具布景"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6882f593000000001100272d",
+        "69535514000000001e032b26",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述人物与道具的组合场景(如猫咪戴假发穿衣服手持书本),直接生成包含道具的人物场景图;CAP-003(图像主体一致性保持)可在需要保持特定角色/动物外观一致的情况下,以参考图为输入确保主体特征稳定,同时通过提示词控制道具细节的清晰呈现。两者结合可满足道具与人物自然融合的需求。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有 CAP-001 可通过提示词生成人物+道具场景,CAP-003 可保持主体一致性,但对于道具与人物的精确空间融合(如手持、穿戴等接触关系的自然度)以及道具细节的精确可辨性,现有能力描述中未明确说明能稳定实现复杂道具交互(如猫咪手持书本、人物抱玩偶等非常规姿态下的道具融合)。需要调研:1)各文生图工具对复杂道具交互场景的实际生成质量;2)是否需要结合 CAP-002(姿态控制)来精确控制持握姿态;3)道具细节清晰度在不同工具下的表现差异。"
+    },
+    {
+      "requirement_id": "REQ_005",
+      "requirement_text": "生成婚礼或节日庆典场景,背景需包含大量花卉装饰、定制发光字牌、喜字等布景元素,整体氛围感强烈,道具与场景协调统一",
+      "source_subtree": {
+        "parent_node": "符号元素",
+        "parent_id": 15913,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素"
+      },
+      "source_nodes": [
+        "道具布景"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6882f593000000001100272d",
+        "69535514000000001e032b26",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述婚礼/庆典场景的整体氛围、花卉装饰、布景元素等,生成氛围感强烈的场景图;CAP-014(图像内文字渲染)负责在场景中准确渲染发光字牌上的定制文字、喜字等文字元素,确保文字清晰可读且与场景风格融合。两者结合可覆盖场景氛围生成与文字元素渲染的需求。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有 CAP-001 可生成婚礼庆典整体场景,CAP-014 可渲染文字元素,但存在以下不确定性:1)大量花卉装饰的密集布景细节在 AI 生成中容易出现混乱或失真,现有能力未明确说明对复杂密集装饰场景的生成质量;2)定制发光字牌需要同时实现文字准确渲染+发光效果+与场景光照协调,CAP-014 侧重文字准确性,发光效果的物理自洽性未有明确说明;3)喜字等中文特殊字符的渲染准确性需确认(Nano Banana Pro 支持中文,但其他工具需验证)。需要调研:各工具对密集装饰场景和发光文字效果的实际生成能力。"
+    },
+    {
+      "requirement_id": "REQ_006",
+      "requirement_text": "生成精致室内空间场景,画面中需呈现陶瓷器皿、绿植、家具等道具摆件,光线自然柔和,营造出温馨生活感或高颜值家居氛围",
+      "source_subtree": {
+        "parent_node": "符号元素",
+        "parent_id": 15913,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素"
+      },
+      "source_nodes": [
+        "道具布景"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6882f593000000001100272d",
+        "69535514000000001e032b26",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述室内场景构成(陶瓷器皿、绿植、家具摆件)、光线质感(自然柔和)和整体氛围(温馨/高颜值家居),直接生成目标场景图;CAP-016(生成阶段原生高分辨率输出)确保陶瓷纹理、植物叶片、家具材质等道具细节在原生输出阶段即清晰呈现,无需后期放大处理,提升精致感。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_043",
+      "requirement_text": "在图片上叠加标题文字,文字大小、粗细、颜色各异,形成层次感强的排版效果——例如大标题用粗体醒目字体,副标题用细体小字,整体风格统一(如深色系商务风或简约设计风)",
+      "source_subtree": {
+        "parent_node": "字体标题",
+        "parent_id": 15899,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题"
+      },
+      "source_nodes": [
+        "基础文字",
+        "字体装饰"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "66619827000000000600486f",
+        "68f988f2000000000703ada5",
+        "693d0b1d000000001e02ba36",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 支持在生成图像中嵌入文字内容,Nano Banana Pro 支持复杂排版布局和多语言高保真文本渲染,可在生成阶段直接输出含层次化排版文字的图像",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-014 支持在生成图像中渲染文字,但其核心场景是将文字作为图像内容的一部分自然融合(如产品标签、Logo),而本需求是在已有图片上叠加多层次排版文字(大标题粗体+副标题细体,颜色各异,形成设计感排版)。现有能力缺少:1)对已有图片进行后期文字叠加合成的能力;2)精确控制字体粗细、大小、颜色等排版参数的能力;3)多层次文字排版布局的精确控制。需要调研是否有专门的图文排版合成工具(如 Canva API、Adobe Express、或 ComfyUI 中的文字叠加节点)能够在已有图片上精确叠加多层次排版文字。"
+    },
+    {
+      "requirement_id": "REQ_044",
+      "requirement_text": "在AI生成的卡通角色图片上叠加幽默吐槽文案,文字直接覆盖在图片上方,字体简洁白色,与画面情绪呼应,形成图文结合的表情包风格内容",
+      "source_subtree": {
+        "parent_node": "字体标题",
+        "parent_id": 15899,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题"
+      },
+      "source_nodes": [
+        "基础文字",
+        "字体装饰"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "66619827000000000600486f",
+        "68f988f2000000000703ada5",
+        "693d0b1d000000001e02ba36",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成卡通角色图片,CAP-014 支持在图像中渲染文字内容",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 可生成卡通角色图片,CAP-014 可在生成阶段嵌入文字,但本需求的核心是在已有AI生成图片上叠加文字(表情包制作流程),需要精确控制文字位置(如图片顶部/底部)、字体样式(简洁白色)、文字与图片的层叠关系。现有能力缺少:1)对已有图片进行文字后期叠加合成的专项能力;2)精确控制文字在图片特定位置覆盖的能力;3)表情包标准排版格式(如顶部+底部文字)的支持。需要调研支持图片+文字合成的工具,如 ComfyUI 中是否有文字叠加节点、或专门的表情包生成工具。"
+    },
+    {
+      "requirement_id": "REQ_045",
+      "requirement_text": "制作多宫格拼图帖子,每格图片配有对应的标题文字或字幕说明,文字风格统一,整体排列整齐,适合用于周记、日历、流程说明等系列内容展示",
+      "source_subtree": {
+        "parent_node": "字体标题",
+        "parent_id": 15899,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题"
+      },
+      "source_nodes": [
+        "基础文字",
+        "字体装饰"
+      ],
+      "source_posts": [
+        "65eea166000000000d00c6d8",
+        "65f4359b00000000140079b5",
+        "66619827000000000600486f",
+        "68f988f2000000000703ada5",
+        "693d0b1d000000001e02ba36",
+        "6960924b000000001a037a1c"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-008",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 生成各格图片内容,CAP-008 批量生成多张图片,CAP-014 在每张图片中渲染对应文字说明",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以批量生成多张图片并在图像中渲染文字,但本需求的核心是将多张图片+文字组合成多宫格排版布局(整体排列整齐、统一风格的拼图帖子)。现有能力缺少:1)多图拼接/网格布局合成能力(将多张图片按宫格排列组合为一张完整图片);2)跨多格统一文字排版风格的控制;3)整体版面设计和对齐的能力。需要调研支持多图网格拼接合成的工具,如 ComfyUI 中的图像拼接节点、或专门的排版合成工具(Canva、图文排版API等)是否能实现多宫格布局合成。"
+    },
+    {
+      "requirement_id": "REQ_072",
+      "requirement_text": "给同一张猫咪照片批量添加不同职业的帽子、道具和配件(如厨师帽、安全帽、眼镜、画板等),让猫咪看起来像在扮演各种职业角色",
+      "source_subtree": {
+        "parent_node": "图像合成",
+        "parent_id": 15898,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/图像合成/图像合成"
+      },
+      "source_nodes": [
+        "抠图处理",
+        "拼贴融合",
+        "图层叠加"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "66daeddb000000002603ea42",
+        "677b5460000000000b00d33e",
+        "6874c80e000000000d027767",
+        "691d3112000000001e036559",
+        "6964be3900000000210282a4"
+      ],
+      "matched_capabilities": [
+        "CAP-003",
+        "CAP-008",
+        "CAP-012"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)确保猫咪外观在所有版本中保持一致;CAP-012(图像局部重绘)在猫咪头部/手部区域局部重绘添加不同职业道具;CAP-008(批量图像生成)批量循环生成多个职业版本,每次切换不同道具提示词自动输出",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可实现主体一致性保持(CAP-003)和局部重绘(CAP-012),但核心缺口在于:将外部素材(帽子、道具等配件图片)精准叠加/融合到猫咪照片特定位置的能力——即传统意义上的'抠图+拼贴融合'。原子能力表中没有明确的'前景素材抠图与精准位置合成'能力。需要调研:1)AI工具是否支持将用户提供的配件素材图抠图后精准合成到指定主体位置;2)ComfyUI是否有专用的图像合成/蒙版叠加节点可实现此类配件叠加;3)是否可通过局部重绘(CAP-012)结合参考图完全替代传统抠图拼贴流程"
+    },
+    {
+      "requirement_id": "REQ_073",
+      "requirement_text": "将真实照片转换成具有统一色调风格的插画效果,整体呈现蓝紫色调的复古油画或动画风格,让风景场景看起来像艺术插图",
+      "source_subtree": {
+        "parent_node": "图像合成",
+        "parent_id": 15898,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/图像合成/图像合成"
+      },
+      "source_nodes": [
+        "风格化处理",
+        "AI生成合成"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "66daeddb000000002603ea42",
+        "677b5460000000000b00d33e",
+        "6874c80e000000000d027767",
+        "691d3112000000001e036559",
+        "6964be3900000000210282a4"
+      ],
+      "matched_capabilities": [
+        "CAP-004",
+        "CAP-012"
+      ],
+      "capability_combination": "CAP-004(风格切换与风格控制)是核心能力:通过加载油画/动画风格LoRA(ComfyUI)或使用--sref上传蓝紫色调复古风格参考图(Midjourney v8),将输入的真实风景照片渲染为目标艺术风格;CAP-012可辅助对特定区域进行风格化局部调整以确保色调统一",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_074",
+      "requirement_text": "制作图文排版信息图,将多张食材产品图片抠出后整齐排列在统一背景上,配合文字说明组合成内容丰富的科普海报",
+      "source_subtree": {
+        "parent_node": "图像合成",
+        "parent_id": 15898,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/图像合成/图像合成"
+      },
+      "source_nodes": [
+        "抠图处理",
+        "拼贴融合",
+        "图层叠加"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "66daeddb000000002603ea42",
+        "677b5460000000000b00d33e",
+        "6874c80e000000000d027767",
+        "691d3112000000001e036559",
+        "6964be3900000000210282a4"
+      ],
+      "matched_capabilities": [
+        "CAP-014",
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-014(图像内文字渲染)支持在生成图像中嵌入清晰可读的文字说明;CAP-001(文本到图像生成)可生成带有整体排版布局的科普海报底图",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "需求的核心流程是:将多张已有食材产品图片抠图去背景→整齐排列到统一背景→配合文字组合为海报。原子能力表中明显缺少:1)'图像抠图/背景移除'能力——将现有产品照片的背景去除,原子能力表中无此专项能力;2)'多图像精准位置排版合成'能力——将多张抠好的素材按网格/指定位置整齐拼排到统一背景上,这与CAP-020(多主体场景合成)侧重角色一致性不同,此处需要的是像素级排版控制。需要调研:1)AI工具中是否有专用的背景移除/抠图工具(如Remove.bg集成、ComfyUI的BiRefNet/REMBG节点);2)是否有支持多图精准网格排版合成的AI能力;3)Nano Banana Pro的多图输入(CAP-020)是否能满足'整齐排列'的排版控制需求"
+    },
+    {
+      "requirement_id": "REQ_082",
+      "requirement_text": "对同一场景或主体生成多个不同距离和景别的画面,包括远景展示整体环境、中景呈现主体与环境关系、近景突出细节,形成一组视角丰富的图片集合",
+      "source_subtree": {
+        "parent_node": "视角选择",
+        "parent_id": 15895,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度/视角选择"
+      },
+      "source_nodes": [
+        "多角度呈现",
+        "全景广角"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6774ab9a0000000009015a3f",
+        "68538f7c000000002400805b",
+        "685f974300000000120144db",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-008"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过在提示词中分别描述远景/中景/近景的构图参数(如 'wide shot showing full environment'、'medium shot showing subject and surroundings'、'close-up shot highlighting details')生成不同景别的图像;CAP-008(批量图像生成)可在单次工作流中批量生成多张不同景别的图像,形成完整图片集合。两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_083",
+      "requirement_text": "生成采用非常规拍摄角度的图片,如从低角度仰拍、从高处俯视、或模拟第一人称主观视角看向场景,让画面产生独特的视觉冲击感",
+      "source_subtree": {
+        "parent_node": "视角选择",
+        "parent_id": 15895,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度/视角选择"
+      },
+      "source_nodes": [
+        "特殊视角",
+        "多角度呈现"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6774ab9a0000000009015a3f",
+        "68538f7c000000002400805b",
+        "685f974300000000120144db",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过在提示词中明确描述特殊视角参数(如 'low angle shot looking up'、'bird's eye view from above'、'first person perspective'、'worm's eye view')即可引导模型生成对应非常规角度的图像。Midjourney v8、FLUX.2 [max]、Nano Banana Pro 等工具均对摄影构图提示词有良好的遵循性,可直接实现仰拍、俯视、主观视角等效果。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_084",
+      "requirement_text": "生成能展示宽广空间感的室内或室外全景图,画面中包含完整的环境纵深,让观看者感受到场景的整体规模和空间层次",
+      "source_subtree": {
+        "parent_node": "视角选择",
+        "parent_id": 15895,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/景别角度/视角选择"
+      },
+      "source_nodes": [
+        "全景广角",
+        "特殊视角"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "6774ab9a0000000009015a3f",
+        "68538f7c000000002400805b",
+        "685f974300000000120144db",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述广角全景构图(如 'wide angle panoramic view'、'ultra-wide shot with deep perspective'、'expansive interior with full depth of field')生成具有空间纵深感的全景图;CAP-016(生成阶段原生高分辨率输出)可配合使用宽幅宽高比输出(如 Midjourney v8 支持最大 4:1 宽高比的 HD 模式,Nano Banana Pro 支持 21:9 宽高比),使全景图在宽幅画面中更完整地呈现空间层次和整体规模。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_094",
+      "requirement_text": "生成具有强烈戏剧性光影对比的户外场景图,画面中光源方向明确(如侧光或逆光),亮部与暗部之间形成鲜明反差,阴影轮廓清晰,整体呈现出电影感或艺术摄影风格的视觉张力",
+      "source_subtree": {
+        "parent_node": "光影表现",
+        "parent_id": 15936,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感/光影表现"
+      },
+      "source_nodes": [
+        "明暗对比",
+        "光源特征"
+      ],
+      "source_posts": [
+        "665971bb000000001303d005",
+        "681c64ce000000002200554c",
+        "68946e0d000000002500ef6e",
+        "692a535f0000000019026d5b",
+        "692cc7ab000000001b030110",
+        "6973742d000000002801e8aa"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述侧光/逆光方向、强烈明暗对比、清晰阴影轮廓等光影特征直接生成目标图像;CAP-004(风格切换与风格控制)通过风格参数(如 Midjourney --stylize 或 --sref 风格参考图)进一步强化电影感或艺术摄影风格的视觉呈现,两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_095",
+      "requirement_text": "生成室内暖光氛围图,画面中多个光源(吊灯、筒灯、窗外自然光)共同营造出温暖柔和的米色调空间,光线从不同方向照射,形成层次丰富的软阴影,整体氛围温馨舒适",
+      "source_subtree": {
+        "parent_node": "光影表现",
+        "parent_id": 15936,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感/光影表现"
+      },
+      "source_nodes": [
+        "光源特征",
+        "氛围营造"
+      ],
+      "source_posts": [
+        "665971bb000000001303d005",
+        "681c64ce000000002200554c",
+        "68946e0d000000002500ef6e",
+        "692a535f0000000019026d5b",
+        "692cc7ab000000001b030110",
+        "6973742d000000002801e8aa"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词详细描述多光源类型(吊灯、筒灯、窗外自然光)、暖色调、米色空间、软阴影层次等要素生成室内场景图;CAP-004(风格切换与风格控制)可通过风格参数或参考图进一步精准控制温馨舒适的整体氛围调性,两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_096",
+      "requirement_text": "生成充满魔幻或超现实感的彩色光效场景,画面中有多种颜色的光线(如橙、蓝、紫等)交织流动,光源本身成为视觉焦点,整体营造出梦幻、神秘或节日感的强烈氛围",
+      "source_subtree": {
+        "parent_node": "光影表现",
+        "parent_id": 15936,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化/光影质感/光影表现"
+      },
+      "source_nodes": [
+        "氛围营造",
+        "光源特征"
+      ],
+      "source_posts": [
+        "665971bb000000001303d005",
+        "681c64ce000000002200554c",
+        "68946e0d000000002500ef6e",
+        "692a535f0000000019026d5b",
+        "692cc7ab000000001b030110",
+        "6973742d000000002801e8aa"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述多色彩光线(橙、蓝、紫)交织流动、光源作为视觉焦点、超现实感等要素直接生成目标场景;CAP-004(风格切换与风格控制)通过 --stylize 参数提升风格化程度或使用 --sref 风格参考图强化梦幻/神秘/节日感的氛围表达,两者组合可完整实现该需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_001",
+      "requirement_text": "生成人物在不同场景下呈现丰富面部表情的图片,例如夸张的痛苦、无奈、开心、困倦等神态,表情要生动传神、情绪感强烈",
+      "source_subtree": {
+        "parent_node": "人物表现",
+        "parent_id": 15912,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现"
+      },
+      "source_nodes": [
+        "表情神态",
+        "模拟扮演"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "66619827000000000600486f",
+        "68946e0d000000002500ef6e",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细的提示词描述目标表情和情绪(如夸张痛苦、无奈、开心、困倦等),直接生成对应神态的人物图像;CAP-003(图像主体一致性保持)在需要同一人物呈现多种表情时,以参考图为输入保持人物外观一致,仅通过提示词切换表情描述,批量生成不同场景下的表情系列图。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_002",
+      "requirement_text": "生成人物与道具、环境或其他角色发生互动的画面,例如人物摆弄物品、与道具合影、在特定场景中做出配合动作等,画面要体现人物和周围元素之间的关联感",
+      "source_subtree": {
+        "parent_node": "人物表现",
+        "parent_id": 15912,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现"
+      },
+      "source_nodes": [
+        "互动协作",
+        "出镜展示"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "66619827000000000600486f",
+        "68946e0d000000002500ef6e",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-003",
+        "CAP-020"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词详细描述人物与道具/环境/其他角色的互动动作和场景关系,直接生成互动画面;CAP-002(结构/姿态控制生成)通过姿态骨架约束人物与道具互动时的具体姿态,确保动作自然合理;CAP-003(图像主体一致性保持)在需要固定人物外观的前提下切换不同互动场景;CAP-020(多主体场景合成)在涉及多角色互动时,将多个角色参考图合成到同一场景中,体现角色间的关联感。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_003",
+      "requirement_text": "生成将动物(如猫咪)拟人化扮演特定角色或情境的图片,赋予其人类的表情、姿态和道具,用来传达幽默或情感共鸣的视觉效果",
+      "source_subtree": {
+        "parent_node": "人物表现",
+        "parent_id": 15912,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现"
+      },
+      "source_nodes": [
+        "模拟扮演",
+        "侧面塑造"
+      ],
+      "source_posts": [
+        "648d8edf0000000011013447",
+        "66619827000000000600486f",
+        "68946e0d000000002500ef6e",
+        "692d3b99000000001e022295",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-004"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述动物拟人化的具体角色、情境、表情和道具(如「穿西装打领带、表情无奈的猫咪坐在办公桌前」),直接生成拟人化动物图像;CAP-002(结构/姿态控制生成)通过姿态骨架约束动物呈现人类姿态(如站立、坐姿、持物等),使拟人化效果更自然;CAP-004(风格切换与风格控制)通过风格参数或LoRA调整画面整体风格(如卡通、写实、插画),配合幽默或情感共鸣的视觉表达需求。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_019",
+      "requirement_text": "将人物照片与中国传统吉祥符号(如双喜字、红玫瑰、金色祝福文字)融合,生成具有强烈喜庆氛围的定制化图案,人物面孔清晰嵌入红色喜庆背景中",
+      "source_subtree": {
+        "parent_node": "视觉符号",
+        "parent_id": 15902,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素/视觉符号"
+      },
+      "source_nodes": [
+        "图形图标",
+        "标识辨识",
+        "视觉意象"
+      ],
+      "source_posts": [
+        "6602bd07000000001203348c",
+        "67284f9c000000001901875a",
+        "68737e97000000000d027b81",
+        "68a8241a000000001c011403",
+        "68c15181000000001b01c358"
+      ],
+      "matched_capabilities": [
+        "CAP-003",
+        "CAP-014",
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)以人物照片为参考图输入,保持人物面孔特征清晰嵌入生成图像;CAP-014(图像内文字渲染)负责在图像中渲染双喜字、金色祝福文字等中文文字元素;CAP-001(文本到图像生成)通过提示词描述红色喜庆背景、红玫瑰、吉祥符号等视觉元素,生成整体喜庆氛围图案;三者组合可实现人物面孔与传统吉祥符号融合的定制化喜庆图案",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力组合(CAP-003+CAP-014+CAP-001)可以覆盖人物面孔保持、文字渲染和喜庆背景生成,但存在关键缺口:双喜字、吉祥纹样等中国传统图形符号属于复杂装饰性图形元素,需要确认现有工具对中国传统纹样图形的生成精准度;同时人物面孔与复杂装饰性背景的自然融合(非简单叠加)效果需要调研,特别是面孔清晰嵌入红色喜庆背景时的边缘融合质量。需调研方向:1)Nano Banana Pro 或 FLUX.2 [max] 对中国传统吉祥纹样图形的生成准确度;2)人物照片与装饰性背景深度融合(非换背景)的实现方式"
+    },
+    {
+      "requirement_id": "REQ_020",
+      "requirement_text": "制作统一模板风格的系列信息卡片,每张卡片包含固定的图标符号(如皇冠等级图标)、彩色标题文字和配图,整体视觉风格一致、可批量复用",
+      "source_subtree": {
+        "parent_node": "视觉符号",
+        "parent_id": 15902,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素/视觉符号"
+      },
+      "source_nodes": [
+        "图形图标",
+        "标识辨识"
+      ],
+      "source_posts": [
+        "6602bd07000000001203348c",
+        "67284f9c000000001901875a",
+        "68737e97000000000d027b81",
+        "68a8241a000000001c011403",
+        "68c15181000000001b01c358"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014",
+        "CAP-004",
+        "CAP-008"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)生成每张卡片的配图内容;CAP-014(图像内文字渲染)在卡片中渲染彩色标题文字;CAP-004(风格切换与风格控制)通过统一风格参数或LoRA确保系列卡片视觉风格一致;CAP-008(批量图像生成)实现系列卡片的批量自动化生产",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可覆盖文字渲染、风格统一和批量生成,但存在关键缺口:固定图标符号(如皇冠等级图标)在每张卡片中需保持像素级一致的图形元素复用,现有能力表中没有明确支持「固定UI图标元素跨图复用」的能力——CAP-003主要针对人物/产品主体一致性,不专门针对图标符号的精确复用;此外卡片的整体排版布局(图标位置固定、文字区域固定、配图区域固定)属于模板化排版能力,现有能力表未明确覆盖。需调研方向:1)AI图像生成工具是否支持基于固定排版模板的卡片生成(如锁定图标位置和尺寸);2)图标符号跨批次精确复用的实现方式(是否需要结合图像合成/后处理工具)"
+    },
+    {
+      "requirement_id": "REQ_021",
+      "requirement_text": "生成黑色科技感背景的人物宣传海报,背景带有流光线条或霓虹光效,人物照片与品牌Logo、活动标识、二维码等视觉元素整齐排布,形成高辨识度的系列展示图",
+      "source_subtree": {
+        "parent_node": "视觉符号",
+        "parent_id": 15902,
+        "context_path": "/root/呈现/视觉/形象塑造/符号元素/视觉符号"
+      },
+      "source_nodes": [
+        "标识辨识",
+        "视觉意象",
+        "图形图标"
+      ],
+      "source_posts": [
+        "6602bd07000000001203348c",
+        "67284f9c000000001901875a",
+        "68737e97000000000d027b81",
+        "68a8241a000000001c011403",
+        "68c15181000000001b01c358"
+      ],
+      "matched_capabilities": [
+        "CAP-003",
+        "CAP-001",
+        "CAP-004",
+        "CAP-014",
+        "CAP-008"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)以人物照片为参考图保持人物外观特征;CAP-001(文本到图像生成)通过提示词描述黑色科技感背景、流光线条、霓虹光效等视觉元素;CAP-004(风格切换与风格控制)确保系列海报视觉风格统一;CAP-014(图像内文字渲染)渲染海报中的文字信息;CAP-008(批量图像生成)实现系列展示图的批量生产",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可覆盖人物保持、科技感背景生成和系列风格统一,但存在关键缺口:1)品牌Logo、活动标识、二维码等精确图形元素需要以像素精确的方式嵌入海报指定位置,特别是二维码必须保持可扫描的精确图形,AI图像生成工具通常无法保证二维码等精确图形的像素级准确复现;2)多视觉元素(人物+Logo+标识+二维码)在海报中的「整齐排布」涉及精确的版式布局控制,现有能力表未明确覆盖版式/排版精确控制能力。需调研方向:1)AI工具是否支持将外部精确图形(Logo、二维码)以指定位置和尺寸嵌入生成图像(类似图层合成);2)是否需要结合设计工具(如Figma、PS)进行后期精确排版合成,而非纯AI生成"
+    },
+    {
+      "requirement_id": "REQ_025",
+      "requirement_text": "生成穿着完整冬季搭配的人物形象,展示黑色羽绒服、红色围巾、红色手套、宽腿裤等单品的组合穿搭效果,呈现从全身到局部细节的多角度造型展示",
+      "source_subtree": {
+        "parent_node": "穿搭呈现",
+        "parent_id": 15893,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/穿搭呈现"
+      },
+      "source_nodes": [
+        "搭配手法",
+        "整体形象",
+        "细节修饰"
+      ],
+      "source_posts": [
+        "66daeddb000000002603ea42",
+        "67b2a7f7000000002802a0d7",
+        "681c64ce000000002200554c",
+        "68ca143d000000001202c3de",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-002",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过详细提示词描述服装单品组合生成人物穿搭图;CAP-002(结构/姿态控制生成)通过ControlNet控制人物姿态,实现全身正面、侧面、局部特写等多角度构图;CAP-016(生成阶段原生高分辨率输出)确保服装面料纹理、围巾编织细节等局部细节清晰可见",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_026",
+      "requirement_text": "生成宠物穿着服装的可爱造型图,展示猫咪穿上印花连体衣的整体穿着效果,需要清晰呈现服装的图案、版型与宠物身体的贴合细节",
+      "source_subtree": {
+        "parent_node": "穿搭呈现",
+        "parent_id": 15893,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/穿搭呈现"
+      },
+      "source_nodes": [
+        "整体形象",
+        "细节修饰"
+      ],
+      "source_posts": [
+        "66daeddb000000002603ea42",
+        "67b2a7f7000000002802a0d7",
+        "681c64ce000000002200554c",
+        "68ca143d000000001202c3de",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-016"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述猫咪穿着印花连体衣的造型生成图像;CAP-016(生成阶段原生高分辨率输出)确保印花图案细节、服装与猫咪身体贴合的版型细节清晰呈现",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001可以生成宠物穿衣图像,CAP-016可保障细节清晰度,但核心难点在于:猫咪体型特殊(四肢短、身体圆润),AI生成宠物穿衣图像时服装贴合度、版型准确性难以保证,且印花图案在弯曲身体上的透视变形效果是否能准确呈现存疑。需要调研:1)现有文生图工具对宠物穿衣场景的实际生成质量;2)是否有专门针对宠物服装展示的LoRA或微调模型;3)CAP-003(主体一致性)结合真实猫咪参考图是否能提升服装贴合细节的准确性"
+    },
+    {
+      "requirement_id": "REQ_027",
+      "requirement_text": "生成创意合成图,将人物穿搭形象嵌入特定场景容器中(如超市生鲜托盘),使人物服装与场景产生趣味性视觉对比,同时保留服装细节的清晰可见",
+      "source_subtree": {
+        "parent_node": "穿搭呈现",
+        "parent_id": 15893,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/穿搭呈现"
+      },
+      "source_nodes": [
+        "搭配手法",
+        "整体形象"
+      ],
+      "source_posts": [
+        "66daeddb000000002603ea42",
+        "67b2a7f7000000002802a0d7",
+        "681c64ce000000002200554c",
+        "68ca143d000000001202c3de",
+        "6960e87a000000000e00c216"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)通过提示词描述人物被置于超市生鲜托盘等容器场景中的创意合成画面;CAP-003(图像主体一致性保持)以人物穿搭参考图为输入,确保嵌入场景后服装外观特征保持一致",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001可尝试通过提示词直接描述此类创意合成场景,CAP-003可保持人物服装一致性,但核心难点在于:将人物精确嵌入特定容器(如托盘)并产生视觉上合理的比例关系、透视关系和边界融合,属于复杂的空间合成任务,单纯文生图难以精确控制人物与容器的嵌套关系。需要调研:1)CAP-012(图像局部重绘)是否可先生成场景容器再将人物重绘嵌入;2)CAP-020(多主体场景合成)能否将人物与容器场景合成;3)是否需要结合CAP-002(姿态控制)约束人物在容器内的姿态和比例;4)现有工具对此类超现实创意合成的实际效果评估"
+    },
+    {
+      "requirement_id": "REQ_031",
+      "requirement_text": "将真实照片中的人物与卡通/奇幻元素合成,例如给人物添加蟑螂的触角和腿,使人物看起来像变成了一只蟑螂,整体画面自然融合不突兀",
+      "source_subtree": {
+        "parent_node": "画质优化",
+        "parent_id": 15884,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化"
+      },
+      "source_nodes": [
+        "后期加工",
+        "画质优化"
+      ],
+      "source_posts": [
+        "67e68c9d00000000060282fb",
+        "68077d02000000001c02dd81",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "68d610800000000012023282"
+      ],
+      "matched_capabilities": [
+        "CAP-012"
+      ],
+      "capability_combination": "CAP-012 可以对真实照片进行局部重绘,在指定区域添加蟑螂触角、腿等元素,但核心挑战在于将卡通/奇幻元素与真实照片自然融合,且需要精确控制添加元素的位置和形态",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-012(图像局部重绘)可以在指定区域添加新元素,但该需求的核心难点是:1)将卡通风格的蟑螂元素(触角、腿)与真实人物照片自然融合,涉及跨风格合成(写实+卡通);2)需要精确控制添加元素的位置(如头顶添加触角、身体两侧添加腿)并保持透视和光照一致;3)现有能力表中没有明确支持'真实照片+卡通元素跨风格自然融合'的专项能力。需要调研:AI图像编辑工具是否支持将卡通/手绘风格元素与写实照片无缝合成、是否有专门的图像合成/图层融合能力。"
+    },
+    {
+      "requirement_id": "REQ_032",
+      "requirement_text": "给普通猫咪照片套上不同职业的服装和场景(如医生、上班族、老板等),并保持猫咪面部表情清晰可辨,制作出系列表情包拼贴图",
+      "source_subtree": {
+        "parent_node": "画质优化",
+        "parent_id": 15884,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化"
+      },
+      "source_nodes": [
+        "后期加工",
+        "画质优化"
+      ],
+      "source_posts": [
+        "67e68c9d00000000060282fb",
+        "68077d02000000001c02dd81",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "68d610800000000012023282"
+      ],
+      "matched_capabilities": [
+        "CAP-003",
+        "CAP-012",
+        "CAP-008",
+        "CAP-005"
+      ],
+      "capability_combination": "CAP-003 保持猫咪面部特征一致性,CAP-012 对猫咪照片进行局部重绘添加职业服装和场景背景,CAP-008 批量生成多个职业版本,CAP-005 确保猫咪面部表情清晰可辨;但拼贴图排版合成环节仍有缺口",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以做到:CAP-003保持猫咪外观一致性、CAP-012替换服装和背景、CAP-008批量生成系列图、CAP-005增强面部清晰度。缺少的能力:1)将多张生成图拼贴排版为表情包拼贴图的能力,原子能力表中没有图像拼贴/排版合成能力;2)猫咪穿着职业服装的合成效果是否自然(猫咪体型与人类服装的适配问题)需要验证。需要调研:AI工具是否支持多图拼贴排版输出,或是否有专门的表情包拼贴图生成能力。"
+    },
+    {
+      "requirement_id": "REQ_033",
+      "requirement_text": "在真实物体照片上叠加手绘风格的简笔画元素,例如在猕猴桃切片上添加卡通五官和小触角,让照片呈现出实物与手绘结合的趣味效果",
+      "source_subtree": {
+        "parent_node": "画质优化",
+        "parent_id": 15884,
+        "context_path": "/root/呈现/视觉/影像制作/后期处理/画质优化"
+      },
+      "source_nodes": [
+        "后期加工",
+        "画质优化"
+      ],
+      "source_posts": [
+        "67e68c9d00000000060282fb",
+        "68077d02000000001c02dd81",
+        "682ede8f000000002202bff2",
+        "683d8695000000001200012a",
+        "68d610800000000012023282"
+      ],
+      "matched_capabilities": [
+        "CAP-012"
+      ],
+      "capability_combination": "CAP-012 可以在真实物体照片的指定区域重绘添加新元素,但核心需求是在写实照片上叠加手绘/简笔画风格元素并保持风格差异(即手绘元素要看起来像手绘而非写实生成)",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-012(图像局部重绘)可以在照片上添加新元素,但该需求的核心难点是:1)添加的元素必须保持'手绘简笔画'风格,与写实照片形成明显的风格对比(而非融合),这与CAP-012默认追求'自然融合'的目标相反;2)需要精确控制新增元素的风格为手绘/卡通,同时保持原始照片区域不变;3)原子能力表中没有明确支持'在写实照片上叠加指定手绘风格元素'且保持风格差异的专项能力。需要调研:AI图像编辑工具是否支持在局部区域以特定手绘风格生成元素并叠加到写实照片上,是否有专门的混合风格图层合成能力。"
+    },
+    {
+      "requirement_id": "REQ_040",
+      "requirement_text": "制作图文卡片时,需要让插图与文字在语义上高度呼应——比如用可爱小驴的不同表情和动作配合对应的幽默文字,每张小卡片中图在上、文字在下,插图内容直接反映文字含义,形成一眼就能看懂的图文配合效果",
+      "source_subtree": {
+        "parent_node": "图文关系",
+        "parent_id": 15885,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/图文关系"
+      },
+      "source_nodes": [
+        "图文关联",
+        "图文编排"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67206035000000001b02f4b1",
+        "6735b1a0000000001b0137f5",
+        "67adb23f000000002a00c240",
+        "6961b301000000001a02f6af"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 根据每句幽默文字的语义生成对应表情/动作的小驴插图;CAP-003 保持小驴角色在不同卡片中外观一致;CAP-014 在图像中渲染对应的幽默文字,使文字作为图像视觉元素呈现在插图下方",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001+CAP-003 可生成语义对应的插图,CAP-014 可在图像内渲染文字,但三者组合仍存在缺口:1)图在上、文字在下的卡片版式排版布局控制(精确的空间分区、留白比例)在现有能力中没有明确支持;2)图文语义精准对应的自动化流程(即根据文字内容自动生成对应插图并排版为卡片)缺乏专项能力支撑;需要调研是否有支持图文版式自动排版的 AI 工具,或是否可通过 ComfyUI 工作流组合实现卡片版式控制"
+    },
+    {
+      "requirement_id": "REQ_041",
+      "requirement_text": "制作多图拼贴帖子时,需要将多张照片或截图按照叙事顺序排列在一张大图中,并在关键图片上叠加说明性文字标注,让图片和文字共同讲述一个完整故事,文字起到补充说明和情感点评的作用",
+      "source_subtree": {
+        "parent_node": "图文关系",
+        "parent_id": 15885,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/图文关系"
+      },
+      "source_nodes": [
+        "图文关联",
+        "图文编排"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67206035000000001b02f4b1",
+        "6735b1a0000000001b0137f5",
+        "67adb23f000000002a00c240",
+        "6961b301000000001a02f6af"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 可在图像中渲染文字标注内容,使说明性文字作为视觉元素叠加在图片上",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-014 仅能在单张生成图像中渲染文字,但该需求的核心是:1)将多张已有照片/截图按叙事顺序拼合为一张大图(多图拼贴/画布合成能力),现有原子能力表中无明确的多图拼贴排版能力;2)在拼贴大图的指定位置叠加文字标注(精确位置控制的文字叠加);CAP-020 多主体场景合成偏向将多个主体合成到同一场景,与按叙事顺序排列已有图片的拼贴需求不同;需要调研支持多图拼贴画布排版、图片网格布局生成的 AI 工具或 ComfyUI 节点方案"
+    },
+    {
+      "requirement_id": "REQ_042",
+      "requirement_text": "制作信息图文海报时,需要将大标题、分类小标题与正文段落按照清晰的层级排布在版面上,标题用大字醒目展示,正文紧跟其下,整体版面分区明确、图文对应,让读者能快速扫读获取信息",
+      "source_subtree": {
+        "parent_node": "图文关系",
+        "parent_id": 15885,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/图文关系"
+      },
+      "source_nodes": [
+        "图文编排",
+        "图文关联"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "67206035000000001b02f4b1",
+        "6735b1a0000000001b0137f5",
+        "67adb23f000000002a00c240",
+        "6961b301000000001a02f6af"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 生成海报的背景图像和视觉元素;CAP-014 在图像中渲染标题、小标题和正文等多层级文字内容",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 可生成海报视觉背景,CAP-014 可渲染文字,但该需求的核心挑战在于:1)多层级文字排版(大标题/小标题/正文的字号差异、层级关系、间距控制)的精确版式设计能力,现有 CAP-014 主要针对短文字渲染,对复杂多层级段落排版的支持未有明确说明;2)版面分区明确、图文对应的结构化信息图布局控制,需要精确的空间分区能力;需要调研 Nano Banana Pro 或其他工具对复杂多层级排版信息图海报的支持程度,以及是否有专项的信息图生成能力"
+    },
+    {
+      "requirement_id": "REQ_058",
+      "requirement_text": "生成具有强烈透视纵深感的室内空间图,画面中窗框、拱门、地板线条等建筑元素形成明显的空间层次,光线从远处窗口射入,营造出由近到远的视觉延伸效果",
+      "source_subtree": {
+        "parent_node": "构图方式",
+        "parent_id": 15365,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/构图方式"
+      },
+      "source_nodes": [
+        "透视纵深"
+      ],
+      "source_posts": [
+        "664599b9000000001e01d218",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6879f4b1000000000b02c2e0",
+        "692a535f0000000019026d5b"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "使用 CAP-001 文本到图像生成,通过精细的提示词描述透视纵深构图(如 vanishing point perspective、leading lines、depth of field 等关键词),结合 Midjourney v8 或 FLUX.2 [max] 的照片级真实感输出,可直接生成具有强烈透视纵深感的室内空间图,包含窗框、拱门、地板线条等建筑元素及从远处窗口射入的光线效果。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_059",
+      "requirement_text": "生成采用夸张变形构图的图片,例如鱼眼镜头效果将人物或场景扭曲成球形全景、仰拍使近处物体极度放大而远处极度缩小,或通过搞怪角度让画面产生强烈的视觉冲击感",
+      "source_subtree": {
+        "parent_node": "构图方式",
+        "parent_id": 15365,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/构图方式"
+      },
+      "source_nodes": [
+        "夸张变形"
+      ],
+      "source_posts": [
+        "664599b9000000001e01d218",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6879f4b1000000000b02c2e0",
+        "692a535f0000000019026d5b"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "使用 CAP-001 文本到图像生成,通过提示词描述鱼眼镜头(fisheye lens)、极端广角变形(extreme wide angle distortion)、仰拍透视(worm's eye view)等效果,Midjourney v8 或 FLUX.2 [max] 对此类摄影/光学效果的提示词遵循性较强,可生成夸张变形构图图像。",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_060",
+      "requirement_text": "生成画面中存在嵌套框架效果的图片,如在沙漠场景中用一个悬空的矩形框将主体框住形成画中画,或利用水面倒影与实景上下对称形成嵌套镜像构图,制造超现实的空间突破感",
+      "source_subtree": {
+        "parent_node": "构图方式",
+        "parent_id": 15365,
+        "context_path": "/root/呈现/视觉/构图编排/空间布局/构图方式"
+      },
+      "source_nodes": [
+        "嵌套突破",
+        "对称分割"
+      ],
+      "source_posts": [
+        "664599b9000000001e01d218",
+        "67e37ff8000000001c008b5e",
+        "682a8f11000000002002a511",
+        "6879f4b1000000000b02c2e0",
+        "692a535f0000000019026d5b"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "使用 CAP-001 文本到图像生成,通过提示词描述嵌套框架(nested frame、picture within picture)、悬空矩形框(floating rectangular frame)、水面镜像对称(mirror reflection symmetry)等超现实构图概念,Midjourney v8 或 FLUX.2 [max] 对超现实主义构图有一定支持,可尝试生成此类效果。",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-001 文本到图像生成可以尝试通过提示词描述嵌套框架和对称分割构图,但此类高度精确的超现实空间构图(悬空矩形框精确框住主体、水面倒影与实景精确对称嵌套)对提示词遵循性要求极高,现有能力能否稳定、精确地实现嵌套框架的空间位置关系和对称分割的几何精度存在不确定性。需要调研:1)Midjourney v8 / FLUX.2 [max] 对复杂超现实嵌套构图的实际生成效果;2)是否可结合 CAP-002 结构/姿态控制(ControlNet 深度图或线稿)来精确约束嵌套框架的空间位置;3)是否有专门支持超现实主义构图控制的 AI 工具或 ControlNet 预处理器。"
+    },
+    {
+      "requirement_id": "REQ_097",
+      "requirement_text": "制作大字号标题搭配正文内容的图文排版,标题文字极大且颜色鲜艳(红色、黄色等高饱和色),与正文小字形成强烈的大小对比,整体版面信息密度高、视觉冲击力强",
+      "source_subtree": {
+        "parent_node": "大字报风格",
+        "parent_id": 15927,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题/排版风格/大字报风格"
+      },
+      "source_nodes": [
+        "大字报风格",
+        "通用版式",
+        "醒目字效"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "677b5460000000000b00d33e",
+        "68737e97000000000d027b81",
+        "692fe421000000001f00691a",
+        "6965ea53000000000e00f0f1"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 可在生成图像中嵌入文字内容,Nano Banana Pro 支持复杂排版布局和多语言高保真文本渲染,可在提示词中描述大字号标题+小字正文的排版结构及高饱和色彩要求",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-014 支持图像内文字渲染和复杂排版布局,理论上可通过提示词描述大字报式排版结构(大标题+小正文+高饱和色)来生成。但现有能力描述中未明确说明是否支持:1)精确控制多层级文字大小比例(如标题字号与正文字号的具体倍数关系);2)高信息密度版面中多段正文文字的准确渲染(文字越多,AI渲染出错率越高);3)指定文字颜色(如纯红、纯黄等高饱和色)的精确还原。需要调研 Nano Banana Pro 或其他工具在多层级文字排版、高密度文字内容、精确色彩控制方面的实际表现和限制。"
+    },
+    {
+      "requirement_id": "REQ_098",
+      "requirement_text": "在图片上叠加大字幕文字,字体粗大醒目,常带有描边或阴影效果,文字直接覆盖在照片或场景图上,起到强调说明或搞笑点评的作用",
+      "source_subtree": {
+        "parent_node": "大字报风格",
+        "parent_id": 15927,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题/排版风格/大字报风格"
+      },
+      "source_nodes": [
+        "字幕应用",
+        "醒目字效"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "677b5460000000000b00d33e",
+        "68737e97000000000d027b81",
+        "692fe421000000001f00691a",
+        "6965ea53000000000e00f0f1"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 支持在生成图像中嵌入文字,可通过提示词描述粗体大字幕叠加在场景图上的效果,Nano Banana Pro 支持复杂排版布局",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-014 的文字渲染能力主要面向生成阶段的文字嵌入(如产品标签、Logo、广告语),但该需求的核心是:1)将大字幕叠加在已有的照片或场景图上(属于图像编辑/合成场景,而非从零生成);2)需要精确控制描边、阴影等字效样式;3)文字需覆盖在指定位置的已有图像上。现有能力中 CAP-012(图像局部重绘)可对已有图像进行编辑,但其定位是内容替换而非文字叠加。需要调研:是否有 AI 工具支持在已有图片上精确叠加带描边/阴影效果的大字幕文字,或是否需要结合图像编辑工具(如 Photoshop、Canva 等非 AI 生成工具)来实现字效叠加。"
+    },
+    {
+      "requirement_id": "REQ_099",
+      "requirement_text": "制作多宫格拼贴式内容图,每个格子内有大标题文字突出显示核心信息(如价格、品类名),配合产品图或场景图,标题字号远大于说明文字,形成层次分明的视觉结构",
+      "source_subtree": {
+        "parent_node": "大字报风格",
+        "parent_id": 15927,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/字体标题/排版风格/大字报风格"
+      },
+      "source_nodes": [
+        "通用版式",
+        "大字报风格",
+        "醒目字效"
+      ],
+      "source_posts": [
+        "66c5b638000000001d018e5a",
+        "677b5460000000000b00d33e",
+        "68737e97000000000d027b81",
+        "692fe421000000001f00691a",
+        "6965ea53000000000e00f0f1"
+      ],
+      "matched_capabilities": [
+        "CAP-014",
+        "CAP-001"
+      ],
+      "capability_combination": "CAP-001 可生成产品图或场景图素材,CAP-014 可在图像中嵌入文字内容;理论上可通过提示词描述多宫格拼贴版式+大标题+小说明文字的层次结构来生成整体版面",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "该需求涉及多宫格拼贴版式,具体挑战包括:1)多宫格网格布局的精确控制——AI图像生成工具对严格的网格分割、等比格子排列的控制能力存疑,现有能力表中无明确支持多宫格版式布局的能力;2)每个格子内独立的图文组合(产品图+大标题+说明文字)需要在有限空间内精确排布;3)多个格子中文字内容各不相同,高密度多文字渲染的准确率是否足够。CAP-014 的 Nano Banana Pro 支持复杂排版布局,但是否能精确实现多宫格拼贴这类结构化版式尚不明确。需要调研:Nano Banana Pro 或其他工具对多宫格/网格版式的支持程度,以及是否需要借助专业排版工具(如 Figma、Canva)来实现精确的多宫格结构。"
+    },
+    {
+      "requirement_id": "REQ_022",
+      "requirement_text": "生成宠物或动物穿戴人类服饰配件(如帽子、围巾)的画面,让动物看起来像在过节或扮演某种角色,整体效果可爱又有趣",
+      "source_subtree": {
+        "parent_node": "创意造型",
+        "parent_id": 15894,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/创意造型"
+      },
+      "source_nodes": [
+        "拟人穿戴",
+        "主题扮演"
+      ],
+      "source_posts": [
+        "6666b3a10000000015008834",
+        "6774ab9a0000000009015a3f",
+        "692d3b99000000001e022295",
+        "697069b7000000002202d264"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "使用 CAP-001(文本到图像生成)直接通过提示词描述动物种类、穿戴的服饰配件、节日或角色主题、可爱风格等,Midjourney v8、FLUX.2 [max]、Nano Banana Pro 或 Seedream 5.0 Lite 均可根据提示词生成符合要求的画面",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_023",
+      "requirement_text": "生成将普通服装或日常物品以夸张搞怪方式穿戴的人物画面,比如把超大号短裤当长袍穿、用篮球短裤模仿古希腊长袍,产生强烈的视觉反差和幽默效果",
+      "source_subtree": {
+        "parent_node": "创意造型",
+        "parent_id": 15894,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/创意造型"
+      },
+      "source_nodes": [
+        "夸张变形",
+        "创意技法",
+        "拟物仿形"
+      ],
+      "source_posts": [
+        "6666b3a10000000015008834",
+        "6774ab9a0000000009015a3f",
+        "692d3b99000000001e022295",
+        "697069b7000000002202d264"
+      ],
+      "matched_capabilities": [
+        "CAP-001"
+      ],
+      "capability_combination": "使用 CAP-001(文本到图像生成)通过详细提示词描述夸张穿戴方式、物品错位使用场景、视觉反差和幽默风格,Midjourney v8 或 FLUX.2 [max] 对此类创意概念图的提示词遵循性较强,可生成符合要求的幽默反差画面",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_024",
+      "requirement_text": "生成具有超现实风格的创意合成画面,将人物头部替换为宇宙星云、太极图、粒子爆炸等抽象元素,配合深蓝红色调背景,营造出哲学感或科幻感的视觉冲击",
+      "source_subtree": {
+        "parent_node": "创意造型",
+        "parent_id": 15894,
+        "context_path": "/root/呈现/视觉/形象塑造/造型装扮/创意造型"
+      },
+      "source_nodes": [
+        "夸张变形",
+        "创意技法",
+        "拟物仿形"
+      ],
+      "source_posts": [
+        "6666b3a10000000015008834",
+        "6774ab9a0000000009015a3f",
+        "692d3b99000000001e022295",
+        "697069b7000000002202d264"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-012"
+      ],
+      "capability_combination": "方案一:使用 CAP-001(文本到图像生成)直接通过提示词描述超现实风格、头部替换为星云/太极图/粒子爆炸的抽象合成效果、深蓝红色调背景及哲学/科幻氛围,Midjourney v8 或 FLUX.2 [max] 对超现实概念图支持较好。方案二:先用 CAP-001 生成人物基础图,再用 CAP-012(图像局部重绘)对头部区域进行蒙版重绘,将头部替换为指定抽象元素,实现更精准的局部替换控制",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    },
+    {
+      "requirement_id": "REQ_078",
+      "requirement_text": "在图片上叠加对话气泡或多行说明文字,用于讲述故事背景或补充情节说明,文字带有描边或阴影效果以确保在复杂背景上清晰可读",
+      "source_subtree": {
+        "parent_node": "标注叠加",
+        "parent_id": 15900,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示/标注叠加"
+      },
+      "source_nodes": [
+        "文字标注",
+        "说明标注"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "669b52720000000025003596",
+        "68a8241a000000001c011403",
+        "68ca25bc000000000e023656"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 支持在图像中嵌入文字内容,但其定位是将文字作为图像视觉元素自然融入场景(如产品标签、标牌),而非在已有图像上叠加带样式效果的对话气泡或说明文字层",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "CAP-014 可在生成图像时嵌入文字,但需求的核心是:1)在已有图片上后期叠加对话气泡这类特定UI元素;2)文字需要带有描边/阴影等样式效果以保证可读性;3)多行排版控制。这更接近图像后期合成/排版工具的能力(如 Canva、Photoshop、专用图文排版工具),而非 AI 图像生成能力。需要调研:是否有 AI 工具支持在已有图像上智能叠加带样式的对话气泡和说明文字,或是否有 ComfyUI 节点支持文字描边/阴影叠加合成。"
+    },
+    {
+      "requirement_id": "REQ_079",
+      "requirement_text": "制作图文并茂的科普说明卡片,每张卡片包含标题、编号、插图和详细文字说明,整体排版整齐统一,适合分步骤展示教程或知识点",
+      "source_subtree": {
+        "parent_node": "标注叠加",
+        "parent_id": 15900,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示/标注叠加"
+      },
+      "source_nodes": [
+        "说明标注",
+        "专用标注"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "669b52720000000025003596",
+        "68a8241a000000001c011403",
+        "68ca25bc000000000e023656"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-001 可生成卡片插图内容,CAP-014 可在图像中嵌入文字;但两者组合仍无法满足多元素结构化排版的需求",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "需求的核心是结构化多元素卡片排版:标题+编号+插图+详细文字说明需要在统一模板中精确布局,且多张卡片风格统一。CAP-001 可生成插图,CAP-014 可嵌入文字,但缺少:1)多元素精确定位排版能力(标题区、图片区、文字区的结构化布局);2)统一模板批量生成多张卡片的能力;3)详细多行文字说明的排版控制。这类需求通常需要图文排版工具(如 Canva、Adobe Express)或支持模板化排版的 AI 工具。需要调研:是否有 AI 工具支持基于模板的结构化图文卡片批量生成。"
+    },
+    {
+      "requirement_id": "REQ_080",
+      "requirement_text": "在多图拼贴海报上为每个区域叠加带图标的标签(如勾选符号+地点名称),并在整体画面上方添加大标题和副标题文字,形成图文结合的内容合集展示效果",
+      "source_subtree": {
+        "parent_node": "标注叠加",
+        "parent_id": 15900,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示/标注叠加"
+      },
+      "source_nodes": [
+        "专用标注",
+        "文字标注"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "669b52720000000025003596",
+        "68a8241a000000001c011403",
+        "68ca25bc000000000e023656"
+      ],
+      "matched_capabilities": [
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-014 可在生成图像时嵌入文字,但无法满足多图拼贴布局和在指定区域精确叠加带图标标签的需求",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "需求涉及多个复杂能力:1)多图拼贴布局合成(将多张图片按区域排列);2)在每个区域精确叠加带图标的标签(图标+文字组合元素的定位);3)整体画面的层级标题排版。CAP-014 仅支持在生成阶段嵌入文字,无法实现多图拼贴布局和精确的图标+文字标签叠加。需要调研:是否有 AI 工具或 ComfyUI 工作流支持多图拼贴合成并在指定区域叠加图标标签元素,或需结合图文排版工具实现。"
+    },
+    {
+      "requirement_id": "REQ_081",
+      "requirement_text": "为多人物展示海报中的每个人物添加姓名和职位标签,并在画面顶部叠加活动主题、专场名称等层级分明的标题文字,整体风格统一、信息密度高",
+      "source_subtree": {
+        "parent_node": "标注叠加",
+        "parent_id": 15900,
+        "context_path": "/root/呈现/视觉/构图编排/版面设计/标注图示/标注叠加"
+      },
+      "source_nodes": [
+        "专用标注",
+        "引导标注",
+        "文字标注"
+      ],
+      "source_posts": [
+        "65febd8e0000000012035538",
+        "669b52720000000025003596",
+        "68a8241a000000001c011403",
+        "68ca25bc000000000e023656"
+      ],
+      "matched_capabilities": [
+        "CAP-014",
+        "CAP-020"
+      ],
+      "capability_combination": "CAP-020 可将多人物合成到同一场景,CAP-014 可在图像中嵌入文字;但两者组合无法实现为每个人物精确定位独立标签并保证层级标题排版的需求",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "需求的核心难点在于:1)为海报中每个人物精确定位并叠加对应的姓名+职位标签(需要与人物位置对齐的精确标注);2)多层级标题文字的结构化排版(活动主题、专场名称等层级关系);3)高信息密度下整体风格统一。CAP-014 的文字嵌入是生成阶段的整体融合,难以实现针对每个人物的精确独立标签定位;CAP-020 可处理多人物合成但不涉及标签叠加。需要调研:是否有 AI 工具支持在多人物图像上智能识别人物位置并自动添加对应标签,或需结合排版工具进行后期文字叠加。"
+    },
+    {
+      "requirement_id": "REQ_070",
+      "requirement_text": "生成一组多格拼贴图,每格展示同一人物在不同场景/状态下的夸张表情和肢体动作,配合幽默文字标注,整体呈现出戏剧化的情绪起伏效果(如一周心情变化、苦情崩溃、搞笑反应等)",
+      "source_subtree": {
+        "parent_node": "表演性动作",
+        "parent_id": 15938,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现/动作姿态/表演性动作"
+      },
+      "source_nodes": [
+        "夸张表演"
+      ],
+      "source_posts": [
+        "66619827000000000600486f",
+        "6752d19b000000000202b816",
+        "68d76cd100000000120165e4"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-003",
+        "CAP-002",
+        "CAP-014"
+      ],
+      "capability_combination": "CAP-003(图像主体一致性保持)确保同一人物在多格中外观一致;CAP-001(文本到图像生成)根据每格的场景/情绪描述生成对应内容;CAP-002(结构/姿态控制生成)控制每格中人物的夸张肢体动作和姿态;CAP-014(图像内文字渲染)在每格图像中嵌入幽默文字标注;但多格拼贴的版式排布(将多张图拼合为一张网格图)在原子能力表中无明确对应能力",
+      "match_status": "需要调研",
+      "needs_research": true,
+      "research_note": "现有能力可以:用CAP-003保持人物一致性、用CAP-001生成各格内容、用CAP-002控制夸张姿态、用CAP-014添加文字标注。缺少的关键能力是:将多张独立生成的图像自动排版合并为多格拼贴网格图(如2x3、3x3等宫格布局)的能力,原子能力表中无任何工具明确支持多图拼贴版式合成输出。需要调研:是否有AI工具原生支持多格拼贴图一次性生成(如Midjourney的图像网格输出是否可定制为内容差异化的多格)、或是否有图像拼贴合成节点(如ComfyUI中的图像拼接节点)可实现此功能。"
+    },
+    {
+      "requirement_id": "REQ_071",
+      "requirement_text": "将动物(如猫咪)与各种食物、道具进行创意合成,给动物添加配饰(帽子、假发、领带等)并嵌入食物场景中,搭配谐音梗或双关文字,制作出拟人化角色扮演的趣味表情包图片",
+      "source_subtree": {
+        "parent_node": "表演性动作",
+        "parent_id": 15938,
+        "context_path": "/root/呈现/视觉/形象塑造/人物表现/动作姿态/表演性动作"
+      },
+      "source_nodes": [
+        "角色演绎",
+        "夸张表演"
+      ],
+      "source_posts": [
+        "66619827000000000600486f",
+        "6752d19b000000000202b816",
+        "68d76cd100000000120165e4"
+      ],
+      "matched_capabilities": [
+        "CAP-001",
+        "CAP-012",
+        "CAP-014",
+        "CAP-003"
+      ],
+      "capability_combination": "CAP-001(文本到图像生成)可直接通过提示词描述动物+配饰+食物场景的组合画面;CAP-012(图像局部重绘)可在已有动物图像上局部添加帽子、假发、领带等配饰元素,或将动物嵌入食物场景;CAP-003(图像主体一致性保持)可在多张表情包中保持同一只猫咪的外观特征一致;CAP-014(图像内文字渲染)可在图像中嵌入谐音梗或双关文字",
+      "match_status": "完全满足",
+      "needs_research": false,
+      "research_note": ""
+    }
+  ],
+  "metadata": {
+    "last_updated": "2026-04-02T20:41:20.543512",
+    "total_requirements": 99,
+    "fully_matched": 44,
+    "needs_research": 55
+  }
+}

+ 1 - 1
examples/tool_research/research.prompt

@@ -76,7 +76,7 @@ $system$
 - 方向不明确,需要用户指导
 
 **如何结束**:
-1. **必须**使用 `write_file` 将调研结果按照下面的 JSON 格式写入到examples/tool_research/outputs/flux_1
+1. **必须**使用 `write_file` 将调研结果按照下面的 JSON 格式写入到examples/tool_research/outputs/nanobanana_2
 2. 输出文件路径由调用方在 task 中指定,如未指定则输出为纯文本消息
 
 

+ 29 - 0
examples/tool_research/run.py

@@ -46,6 +46,7 @@ from agent.tools.builtin.browser.baseClass import init_browser_session, kill_bro
 # 导入项目配置
 from config import RUN_CONFIG, SKILLS_DIR, TRACE_STORE_PATH, DEBUG, LOG_LEVEL, LOG_FILE, BROWSER_TYPE, HEADLESS, OUTPUT_DIR
 from config import IM_ENABLED, IM_CONTACT_ID, IM_SERVER_URL, IM_WINDOW_MODE, IM_NOTIFY_INTERVAL
+from config import KNOWLEDGE_MANAGER_ENABLED, KNOWLEDGE_MANAGER_CONTACT_ID, KNOWLEDGE_MANAGER_ENABLE_DB_COMMIT
 
 
 async def main():
@@ -99,6 +100,7 @@ async def main():
     print(f"   ✅ {browser_mode_name}初始化完成\n")
 
     # 5.5 初始化 IM Client(可选)
+    km_task = None
     if IM_ENABLED:
         from agent.tools.builtin.im.chat import im_setup, im_open_window
         print("5.5 初始化 IM Client...")
@@ -117,6 +119,24 @@ async def main():
         else:
             print()
 
+        # 启动 Knowledge Manager(如果启用)
+        if KNOWLEDGE_MANAGER_ENABLED:
+            print("5.6 启动 Knowledge Manager...")
+            print(f"   - Contact ID: {KNOWLEDGE_MANAGER_CONTACT_ID}")
+            try:
+                sys.path.insert(0, str(Path(__file__).parent.parent.parent / "knowhub"))
+                from agents.knowledge_manager import start_knowledge_manager
+
+                km_task = asyncio.create_task(start_knowledge_manager(
+                    contact_id=KNOWLEDGE_MANAGER_CONTACT_ID,
+                    server_url=IM_SERVER_URL,
+                    chat_id="main",
+                    enable_db_commit=KNOWLEDGE_MANAGER_ENABLE_DB_COMMIT
+                ))
+                print(f"   ✅ Knowledge Manager 已启动(后台运行)\n")
+            except Exception as e:
+                print(f"   ⚠️ 启动失败: {e}\n")
+
     # 6. 创建 Agent Runner
     print("6. 创建 Agent Runner...")
     print(f"   - Skills 目录: {SKILLS_DIR}")
@@ -355,6 +375,15 @@ async def main():
         if current_trace_id:
             await runner.stop(current_trace_id)
     finally:
+        # 清理 Knowledge Manager
+        if km_task and not km_task.done():
+            print("正在关闭 Knowledge Manager...")
+            km_task.cancel()
+            try:
+                await km_task
+            except asyncio.CancelledError:
+                pass
+
         # 清理浏览器会话
         try:
             await kill_browser_session()

+ 63 - 4
examples/tool_research/tool_research.prompt

@@ -11,7 +11,8 @@ $system$
 ## 可用工具
 - `agent`: 调用 research 子 agent 执行调研
 - `write_file`: 将文档写入文件
-- `knowledge_save`: 将清洗后的知识结构化存储到知识库
+- `ask_knowledge`: 向 Knowledge Manager 查询知识库中已有的信息
+- `upload_knowledge`: 上传调研结果到知识库(自动去重和关联)
 - `im_check_notification`: 检查是否有新的 IM 消息
 - `im_receive_messages`: 接收 IM 消息
 - `im_send_message`: 发送 IM 消息回复用户
@@ -33,9 +34,19 @@ $system$
 
 ## 工作流程
 
+### 第零步:查询知识库
+在开始调研前,先查询知识库中是否已有相关信息:
+```
+ask_knowledge("查询关于 [工具名] 的所有信息(工具、资源、知识)")
+```
+
+根据查询结果决定调研重点:
+- 如果已有完整信息 → 跳过或补充更新
+- 如果部分存在 → 重点调研缺失部分
+- 如果完全没有 → 全面调研
+
 ### 第一步:确定调研目标
-根据 user prompt 中指定的工具名称,直接开始调研该工具。
-**不需要查询工具表或工具库**,直接使用用户提供的工具名称。
+根据 user prompt 中指定的工具名称和知识库查询结果,确定调研范围。
 
 ### 第二步:深度调研工具
 按照不同渠道和目标,分别调用子 agent 进行专项调研:
@@ -101,7 +112,55 @@ $system$
 - 必须使用 `write_file` 工具将文档写入到指定路径
 - 文件路径格式:`%output_dir%/[工具名称]_guide.md`
 
+### 第四步:上传到知识库
+将调研结果结构化上传到知识库:
+
+```python
+upload_knowledge({
+  "tools": [
+    {
+      "name": "工具名",
+      "slug": "tool_slug",
+      "category": "plugin",
+      "version": "1.0",
+      "description": "功能介绍",
+      "tutorial": "使用教程摘要",
+      "source_url": "官网链接"
+    }
+  ],
+  "resources": [
+    {
+      "title": "工具名 官方文档",
+      "body": "文档内容或摘要",
+      "content_type": "documentation",
+      "source_url": "文档链接",
+      "metadata": {"author": "官方", "date": "2026-04-01"}
+    },
+    {
+      "title": "工具名 用户案例集",
+      "body": "案例汇总内容",
+      "content_type": "community",
+      "source_url": "来源链接"
+    }
+  ],
+  "knowledge": [
+    {
+      "task": "在[场景]下,使用[工具名]完成[目标]",
+      "content": "具体方法和注意事项...",
+      "types": ["tool"],
+      "tags": {"tool": "tool_slug", "scenario": "场景标签"},
+      "score": 4
+    }
+  ]
+})
+```
+
+**上传策略**:
+- 工具信息:提取核心元数据(名称、版本、功能、官网)
+- 资源:高质量文档、教程、案例集(标注来源)
+- 知识:可复用的使用经验、最佳实践、避坑指南
+
 $user$
-请开始工作:调研 flux 2.0 max,生成完整文档并结构化存储。
+请开始工作:调研 nanobanana (gemini-3.1-image-preview),生成完整文档并结构化存储。
 
 输出目录:%output_dir%/

+ 339 - 0
examples/tool_research/tool_results/ComfyUI/使用介绍.md

@@ -0,0 +1,339 @@
+# ComfyUI 使用介绍
+
+> **文档更新时间**: 2026-03-27  
+> **信息来源**: 官方文档、社区教程、实践指南(近 6 个月内)
+
+---
+
+## 1. 基础概览
+
+### 1.1 工具名称
+**ComfyUI**
+
+### 1.2 工具版本
+- **最新版本**: 持续更新中(开源项目)
+- **发布渠道**: [GitHub](https://github.com/comfyanonymous/ComfyUI)
+- **更新频率**: 活跃维护
+
+### 1.3 功能介绍
+
+ComfyUI 是一个**基于节点的生成式 AI 界面和推理引擎**,被广泛认为是最强大的开源节点式 AI 应用程序。
+
+**核心特点**:
+- 🎯 **节点式工作流**:通过可视化节点连接构建复杂的 AI 生成流程
+- ⚙️ **高度可配置**:支持文生图、局部重绘、LoRA、ControlNet 等多种功能
+- 🔄 **工作流可复用**:保存为 JSON 文件,可随时加载和分享
+- 🎨 **可控性强**:精确控制生成过程的每个环节
+- 💰 **免费开源**:完全免费,社区活跃,600+ 自定义节点
+- 🚀 **性能优异**:相比 WebUI,系统要求更低、生成速度更快
+
+**与 Stable Diffusion WebUI 对比**:
+| 特性 | ComfyUI | WebUI |
+|------|---------|-------|
+| 自由度 | 高(节点式) | 中(表单式) |
+| 学习曲线 | 陡峭 | 平缓 |
+| 生成速度 | 更快 | 较慢 |
+| 系统要求 | 更低 | 较高 |
+| 可扩展性 | 极强(自定义节点) | 强(插件) |
+
+---
+
+## 2. 使用指南
+
+### 2.1 安装方法
+
+#### 方式一:官方整合包(推荐新手)
+1. 访问 [ComfyUI GitHub](https://github.com/comfyanonymous/ComfyUI)
+2. 下载官方整合包(包含 Python 环境和所有依赖)
+3. 解压后运行 `run_nvidia_gpu.bat`(NVIDIA 显卡)或 `run_cpu.bat`(CPU)
+
+#### 方式二:秋叶一键启动器(中文用户推荐)
+1. 下载秋叶 ComfyUI 启动器
+2. 自动配置环境和依赖
+3. 一键启动,支持模型管理和更新
+
+#### 方式三:源码安装(高级用户)
+```bash
+# 克隆仓库
+git clone https://github.com/comfyanonymous/ComfyUI.git
+cd ComfyUI
+
+# 安装依赖
+pip install -r requirements.txt
+
+# 启动(NVIDIA GPU)
+python main.py
+
+# 启动(低显存模式)
+python main.py --lowvram
+```
+
+### 2.2 基本使用方法
+
+#### 核心概念
+- **节点(Node)**:功能模块,如加载模型、提示词编码、采样器、VAE 解码等
+- **工作流(Workflow)**:节点连接形成的有向无环图(DAG),可保存为 JSON 复用
+- **自定义节点**:社区贡献的扩展功能节点,通过 Manager 安装
+
+#### 文生图工作流搭建(6 步)
+
+1. **K 采样器(KSampler)**:核心生成节点
+   - 设置采样步数(20-30 步)
+   - 选择采样器(推荐 DPM++ 2M Karras)
+   - 设置 CFG 值(7-12)
+
+2. **加载大模型(Checkpoint Loader)**:
+   - 选择 SD 模型文件(.safetensors 或 .ckpt)
+   - 输出 model、clip、vae
+
+3. **正向提示词(CLIP Text Encode)**:
+   - 输入描述生成内容的提示词
+   - 连接到 KSampler 的 positive 输入
+
+4. **反向提示词(CLIP Text Encode)**:
+   - 输入不希望出现的内容
+   - 连接到 KSampler 的 negative 输入
+
+5. **图片尺寸/批次(Empty Latent Image)**:
+   - 设置宽度、高度(如 512x512)
+   - 设置批次数量(batch_size)
+
+6. **VAE 解码器(VAE Decode)** → **图片生成区(Save Image)**:
+   - 将 latent 空间解码为像素图像
+   - 保存或显示生成结果
+
+#### 工作流复用
+- **保存工作流**:菜单 → Save → 保存为 JSON 文件
+- **加载工作流**:拖拽 JSON 文件到界面 或 菜单 → Load
+- **从图片加载**:拖拽含元数据的生成图片自动恢复工作流
+
+### 2.3 应用场景
+
+ComfyUI 适用于以下场景:
+
+| 场景 | 具体应用 |
+|------|----------|
+| 🎨 **图像生成与编辑** | 数字艺术创作、设计素材生成、摄影后期处理 |
+| 🔬 **AI 研究与实验** | 新算法测试、模型对比、参数调优 |
+| 📝 **个性化内容创作** | 视频封面、博客配图、社交媒体内容 |
+| 📚 **教育与培训** | AI 绘画教学、工作流设计课程 |
+| 🎮 **游戏开发与动画** | 角色设计、场景素材、概念图生成 |
+| 🏭 **产品设计** | 原型设计、效果图模拟、包装展示 |
+| ⚡ **生产级应用** | 批量处理、团队协作、CI/CD 集成 |
+
+---
+
+## 3. 技术规格
+
+### 3.1 输入
+- **节点配置**:JSON 格式的工作流配置文件
+- **参数输入**:
+  - 文本提示词(正向/反向)
+  - 模型文件路径
+  - 图像尺寸、批次数量
+  - 采样器参数(步数、CFG、种子等)
+  - 控制网络输入(ControlNet、IP-Adapter 等)
+
+### 3.2 输出
+- **生成图像**:PNG 格式(含元数据)
+- **工作流文件**:JSON 格式,可复用和分享
+- **中间结果**:latent 空间数据、预处理图像等
+
+### 3.3 运行环境
+
+#### 系统要求
+- **操作系统**:Windows 10/11、Linux、macOS
+- **GPU**:NVIDIA 显卡(推荐 RTX 2060 及以上,6GB+ 显存)
+- **CPU**:支持 AVX2 指令集
+- **内存**:8GB+(推荐 16GB)
+- **存储**:10GB+ 可用空间(不含模型文件)
+
+#### 依赖项
+- Python 3.10+
+- PyTorch(CUDA 支持)
+- torchvision
+- torchaudio
+- 其他依赖(requirements.txt 自动安装)
+
+### 3.4 API 接口
+
+ComfyUI 提供 REST API 支持程序化调用:
+
+#### 主要 API 端点
+
+```bash
+# 提交生成任务
+POST /prompt
+{
+  "prompt": { ... },  # 工作流节点配置
+  "client_id": "unique_id"
+}
+
+# 查询任务状态
+GET /history/{prompt_id}
+
+# 获取队列信息
+GET /queue
+
+# 中断任务
+POST /interrupt
+
+# 重置队列
+POST /reset
+```
+
+#### Python 调用示例
+```python
+import requests
+import json
+
+# 定义工作流
+workflow = {
+    "3": {  # KSampler 节点
+        "class_type": "KSampler",
+        "inputs": {
+            "seed": 12345,
+            "steps": 20,
+            "cfg": 8,
+            "sampler_name": "dpmpp_2m",
+            "scheduler": "karras",
+            "denoise": 1
+        }
+    },
+    # ... 其他节点
+}
+
+# 提交任务
+response = requests.post(
+    "http://127.0.0.1:8188/prompt",
+    json={"prompt": workflow, "client_id": "my_client"}
+)
+prompt_id = response.json()["prompt_id"]
+
+# 轮询结果
+import time
+while True:
+    history = requests.get(f"http://127.0.0.1:8188/history/{prompt_id}").json()
+    if prompt_id in history:
+        break
+    time.sleep(1)
+```
+
+---
+
+## 4. 核心技巧与最佳实践
+
+### 4.1 快捷键效率提升
+| 快捷键 | 功能 |
+|--------|------|
+| `Ctrl+Enter` | 快速生成(队列提示) |
+| `Ctrl+Z` / `Ctrl+Y` | 撤销 / 重做 |
+| `Ctrl+A` | 全选节点 |
+| `Ctrl+C` / `Ctrl+V` | 复制 / 粘贴节点 |
+| `Ctrl+M` | 节点静音(跳过执行) |
+| `Ctrl+B` | 节点旁路(绕过执行) |
+| `双击空白处` | 快速搜索节点 |
+| `Ctrl+ 拖拽` | 快速连接节点 |
+
+### 4.2 性能优化技巧
+1. **低分辨率测试**:先用 512x512 测试提示词,确认后再用高分辨率
+2. **--lowvram 参数**:显存不足时启动低显存模式
+3. **FreeMemory 节点**:在长工作流中手动释放显存
+4. **批处理优化**:使用 batch_size 而非多次运行
+5. **模型缓存**:避免频繁切换大模型
+
+### 4.3 工作流组织
+6. **节点分组**:使用 Reroute 节点整理连线
+7. **命名规范**:给关键节点添加备注(右键 → Add Note)
+8. **模板库**:保存常用工作流为模板
+9. **版本控制**:工作流 JSON 文件用 Git 管理
+
+### 4.4 KSampler 参数设置
+10. **采样步数**:20-30 步(质量与速度平衡)
+11. **CFG 值**:7-12(过高会导致图像过饱和)
+12. **推荐采样器**:DPM++ 2M Karras(通用)、Euler a(快速)
+13. **种子管理**:固定种子复现结果,-1 随机
+
+### 4.5 必备自定义节点
+14. **ComfyUI Manager**:节点包管理器(必装)
+15. **Advanced-ControlNet**:高级控制网络
+16. **AnimateDiff**:动画生成
+17. **IPAdapter**:图像提示适配器
+18. **Impact-Pack**:人脸修复、细节增强
+19. **ReActor**:快速换脸
+20. **Ultimate SD Upscale**:高清放大
+
+### 4.6 内存管理
+21. **模型卸载**:使用 Model Management 节点卸载未用模型
+22. **避免频繁切换**:同一工作流尽量使用相同模型
+23. **监控资源**:使用系统监控工具观察显存使用
+24. **分块处理**:大图生成使用 Tiled VAE
+25. **定期重启**:长时间运行后重启释放内存碎片
+
+### 4.7 常见问题与解决方案
+
+| 问题 | 原因 | 解决方案 |
+|------|------|----------|
+| 显存不足(OOM) | 模型过大或分辨率过高 | 使用 --lowvram、降低分辨率、使用 Tiled VAE |
+| 节点红色报错 | 缺少依赖或配置错误 | 检查节点连接、安装缺失的自定义节点 |
+| 生成速度慢 | 采样步数过多或模型过大 | 减少步数至 20、使用更高效的采样器 |
+| 图像质量差 | 提示词不当或参数设置问题 | 优化提示词、调整 CFG 值、使用高清修复 |
+| 工作流无法加载 | JSON 文件损坏或版本不兼容 | 检查文件完整性、更新 ComfyUI 版本 |
+
+---
+
+## 5. 核心资源
+
+### 5.1 官方资源
+- **GitHub 仓库**: https://github.com/comfyanonymous/ComfyUI
+- **官方示例**: https://comfyanonymous.github.io/ComfyUI_examples/
+- **官方文档**: https://comfyanonymous.github.io/ComfyUI_examples/
+
+### 5.2 中文教程
+- **ComfyUI Wiki(中文教程站)**: https://comfyui-wiki.com/zh
+  - 包含完整的入门教程、节点说明、工作流示例
+- **知乎最全学习手册**: https://zhuanlan.zhihu.com/p/721910953
+  - 系统性教程,从入门到进阶
+
+### 5.3 社区资源
+- **25 Tips and Tricks**: https://comfyuiweb.com/posts/essential-comfyui-tips-and-tricks
+  - 25 个核心技巧汇总
+- **OpenArt 工作流库**: https://openart.ai/workflows
+  - 社区分享的可复用工作流
+- **Civitai ComfyUI 专区**: https://civitai.com/?tab=comfyui
+  - 模型和工作流资源
+
+### 5.4 自定义节点
+- **ComfyUI Manager**: 必装的节点包管理器
+- **ComfyUI Custom Nodes 列表**: https://github.com/ComfyOrg/ComfyUI-Manager/blob/main/custom-node-list.json
+
+---
+
+## 6. 总结
+
+ComfyUI 作为最强大的开源节点式 AI 应用程序,为生成式 AI 工作流提供了极高的灵活性和可控性。虽然学习曲线较陡峭,但通过系统学习和实践,用户可以构建出高度定制化的生成流程,适用于从个人创作到生产级应用的各类场景。
+
+**核心优势**:
+- ✅ 节点式可视化,流程清晰可控
+- ✅ 高度可扩展,600+ 自定义节点
+- ✅ 性能优异,资源占用低
+- ✅ 工作流可复用,便于协作和分享
+- ✅ 免费开源,社区活跃
+
+**适用人群**:
+- AI 绘画爱好者和创作者
+- 设计师、艺术家、游戏开发者
+- AI 研究人员和工程师
+- 需要批量生成和自动化流程的用户
+
+**入门建议**:
+1. 从官方整合包或秋叶启动器开始
+2. 学习基础文生图工作流搭建
+3. 安装 ComfyUI Manager 管理自定义节点
+4. 参考社区工作流模板进行实践
+5. 逐步深入理解各节点功能和参数
+
+---
+
+*文档生成时间:2026-03-27*  
+*信息来源时效性:主要参考近 6 个月内的官方文档和社区教程*

+ 89 - 0
examples/tool_research/tool_results/ComfyUI/实际用例.md

@@ -0,0 +1,89 @@
+# ComfyUI 实际用例
+
+> **文档更新时间**: 2026-03-27  
+> **信息来源**: 社区案例、用户分享、实践经验
+
+---
+
+## 案例 1:游戏角色多视图生成
+
+**场景**:游戏开发需要角色的正面、侧面、背面三视图
+
+**工作流设计**:
+1. 使用 CharTurn 系列模型
+2. 加载角色概念图作为参考
+3. 通过 ControlNet 控制姿态
+4. 批量生成三个视角
+
+**效果**:一致性高,可直接用于 3D 建模参考
+
+**来源**:[ComfyUI Wiki - 游戏角色工作流](https://comfyui-wiki.com/zh)
+
+---
+
+## 案例 2:电商产品图批量生成
+
+**场景**:电商平台需要为同一产品生成多背景、多角度的展示图
+
+**工作流设计**:
+1. 使用 IP-Adapter 保持产品一致性
+2. 准备多个背景模板
+3. 通过批处理节点循环生成
+4. 自动保存并命名
+
+**效果**:单个工作流可生成 100+ 张产品图,效率提升 10 倍
+
+**来源**:知乎 - ComfyUI 商业应用实践
+
+---
+
+## 案例 3:老照片修复与上色
+
+**场景**:修复家庭老照片,进行去噪、增强、上色
+
+**工作流设计**:
+1. 使用 CodeFormer 节点进行人脸修复
+2. 使用 Tile 模型进行细节增强
+3. 使用 DeOldify 或类似模型上色
+4. 最后用 VAE 解码输出
+
+**效果**:修复后的照片清晰自然,色彩真实
+
+**来源**:[ComfyUI 官方示例](https://comfyanonymous.github.io/ComfyUI_examples/)
+
+---
+
+## 案例 4:AI 动画短片制作
+
+**场景**:制作 10-30 秒的 AI 动画短片
+
+**工作流设计**:
+1. 使用 AnimateDiff 生成基础动画
+2. 结合 ControlNet 保持角色一致性
+3. 使用 RIFE 或类似节点进行帧插值
+4. 批量生成后合成视频
+
+**效果**:可生成流畅的角色动画,适用于短视频创作
+
+**来源**:B 站 - ComfyUI 动画教程系列
+
+---
+
+## 案例 5:建筑效果图快速出图
+
+**场景**:建筑设计需要快速生成多个风格的效果图
+
+**工作流设计**:
+1. 使用线稿作为 ControlNet 输入
+2. 准备多个风格 LoRA(现代、古典、工业等)
+3. 批量切换 LoRA 生成不同风格
+4. 使用高清修复提升细节
+
+**效果**:1 小时内可生成 20+ 张不同风格的效果图
+
+**来源**:知乎 - 建筑师如何用 AI 提效
+
+---
+
+*文档生成时间:2026-03-27*  
+*信息来源:社区案例和用户实践分享*

+ 100 - 0
examples/tool_research/tool_results/FLUX2_max/使用介绍.md

@@ -0,0 +1,100 @@
+# FLUX.2 [max] 使用介绍
+
+> 文档版本: 1.0  
+> 最后更新: 2026-03-27  
+> 开发者: Black Forest Labs
+
+## 1. 基础概览
+
+### 1.1 工具信息
+- **工具名称**: FLUX.2 [max]
+- **开发公司**: Black Forest Labs(德国弗莱堡)
+- **发布日期**: 2025年11月25日
+- **定位**: 旗舰级图像生成与编辑模型
+
+### 1.2 核心特性
+- 最高质量输出
+- 实时网络搜索(Grounded Generation)
+- 最强提示词遵循
+- 多参考图像一致性(最多10张)
+- 高精度控制(颜色、姿态、构图)
+
+### 1.3 官方资源
+- 官网: https://bfl.ai
+- 技术文档: https://docs.bfl.ai
+- API文档: https://docs.bfl.ai/api-reference
+- Playground: https://playground.bfl.ai
+- GitHub: https://github.com/black-forest-labs/flux
+
+## 2. 核心功能
+
+### 2.1 接地式生成(仅max独有)
+模型能够自动执行实时网络搜索,获取最新语境信息并融入生成结果。
+
+### 2.2 角色一致性
+支持最多10张参考图像,稳定保留人物面部特征、身体比例、表情特点。
+
+### 2.3 高质量重纹理
+精准重纹理,保留几何、光照、结构,仅替换表面材质。
+
+## 3. 技术规格
+
+### 3.1 核心架构
+- 架构类型: 32B参数 latent flow matching transformer
+- 文本编码器: Mistral-3-24B
+- 最大输出分辨率: 4MP
+- 文本输入容量: 最高32K tokens
+
+### 3.2 输入输出规格
+- 最大分辨率: 4 megapixels
+- 最大提示词长度: 32K tokens
+- 参考图像数量: 最多10张
+- 支持格式: 文本到图像、图像编辑、填充
+
+### 3.3 定价信息
+- FLUX.2 [max]: $0.07/1MP, ~$0.16/4MP
+- FLUX.2 [pro]: $0.03/1MP, ~$0.075/4MP
+- FLUX.2 [klein] 4B: $0.014/1MP
+
+## 4. API 接入
+
+### 4.1 基础信息
+- Base URL: https://api.bfl.ai
+- 认证方式: API Key(x-key Header)
+- 获取Key: https://www.blackforestlabs.ai/get-api-key
+
+### 4.2 端点示例
+```http
+POST /v1/flux-2-max
+Content-Type: application/json
+x-key: <your-api-key>
+
+{
+  "prompt": "A professional product photo",
+  "width": 1024,
+  "height": 1024
+}
+```
+
+## 5. 使用指南
+
+### 5.1 快速体验
+1. 访问 https://playground.bfl.ai
+2. 注册/登录账户
+3. 选择 FLUX.2 [max] 模型
+4. 输入提示词或上传参考图像
+5. 点击生成
+
+### 5.2 提示词最佳实践
+```
+[主体描述] + [环境/背景] + [光照条件] + [风格参考] + [技术参数]
+```
+
+## 6. 参考资源
+- 官方文档: https://docs.bfl.ai
+- API文档: https://docs.bfl.ai/api-reference
+- Hugging Face: https://huggingface.co/black-forest-labs
+- 定价页面: https://bfl.ai/pricing
+
+---
+*文档生成时间: 2026-03-27*

+ 76 - 0
examples/tool_research/tool_results/FLUX2_max/实际用例.md

@@ -0,0 +1,76 @@
+# FLUX.2 [max] 实际用例
+
+> 调研日期: 2026-03-27  
+> 共收集 26 个真实案例
+
+## 图像编辑类案例
+
+### 案例 1:物体替换 - ComfyUI 工作流
+**用户输入**: "用图二中的台灯替换掉图一中的台灯"
+**输出结果**: 替换效果很好,对角度进行了调整
+**来源**: 知乎 - 赵飞
+
+### 案例 2:夜间效果转换
+**用户输入**: "将图像做成夜间亮灯的效果"
+**输出结果**: 成功将白天的室内场景转换为夜间亮灯效果
+**来源**: 知乎 - 赵飞
+
+### 案例 3:物体移除
+**用户输入**: "去掉图像中的吊灯"
+**输出结果**: 成功移除图像中的吊灯,背景修复自然
+**来源**: 知乎 - 赵飞
+
+## 人像与角色一致性案例
+
+### 案例 4:老渔夫肖像
+**用户输入**: "一位面容沧桑的老渔夫肖像,站在码头上。天正下雨,他穿着黄色雨衣"
+**输出结果**: 生成逼真人像,皮肤纹理处理出色,毛孔与皱纹清晰可见
+**来源**: 知乎 - 卓不凡
+
+### 案例 5:角色一致性保持
+**用户输入**: 使用多图参考功能,上传角色参考图
+**输出结果**: 在不同场景和风格中保持角色面部特征一致性
+**来源**: FLUX.2 [max] 官方页面
+
+## 产品摄影与商业应用
+
+### 案例 6:AQUA LAGER 啤酒产品摄影
+**用户输入**: "一瓶标有'AQUA LAGER'的绿色啤酒瓶悬浮在清澈的水中"
+**输出结果**: 成功生成高质量的产品摄影图像,文字渲染清晰
+**来源**: 小红书 - 302AI
+
+### 案例 7:Logo 设计
+**用户输入**: "Create a minimalist logo for a tech startup"
+**输出结果**: 生成高质量、可商用的 Logo 设计
+**来源**: FLUX.2 [max] 官方页面
+
+## 实时语境生成案例
+
+### 案例 8:柏林墙倒塌历史场景
+**用户输入**: "1989年11月9日柏林墙倒塌的历史场景"
+**输出结果**: 成功生成具有历史感的现实主义风格照片
+**来源**: 小红书 - 302AI
+
+### 案例 9:足球比赛结果生成
+**用户输入**: "生成一张展现最近皇家马德里与曼城比赛成绩的图片"
+**输出结果**: 模型自动搜索网络获取比赛结果后生成相应图像
+**来源**: 知乎 - 卓不凡
+
+## 竞品对比
+
+### FLUX.2 [max] vs Nano Banana Pro
+| 维度 | FLUX.2 [max] | Nano Banana Pro |
+|------|-------------|-----------------|
+| 文本渲染 | 仍有偶尔拼写错误 | consistently renders legible text |
+| 照片级真实感 | 32B参数捕捉微妙光影 | 高photorealism |
+| 生成速度 | 约10倍于Nano Banana Pro | 较慢 |
+| 成本 | $0.07/1MP | $0.134/standard |
+
+### 使用场景推荐
+- 照片级人像: Nano Banana Pro 或 FLUX.2 [max]
+- 复杂提示词遵循: FLUX.2 [max] 或 GPT Image 1.5
+- 产品摄影: FLUX.2 [max]
+- 实时语境生成: FLUX.2 [max](唯一支持)
+
+---
+*文档生成时间: 2026-03-27*

+ 87 - 0
examples/tool_research/tool_results/Midjourney_v8/使用介绍.md

@@ -0,0 +1,87 @@
+# Midjourney v8 使用介绍
+
+> 调研日期:2026-03-27  
+> 版本状态:Alpha(测试阶段)  
+> 文档版本:1.0
+
+## 1. 基础概览
+
+### 1.1 工具介绍
+**Midjourney v8** 是 Midjourney 实验室于 2026 年 3 月 17 日发布的最新 AI 图像生成模型,目前处于 Alpha 测试阶段。
+
+### 1.2 核心特性
+- 原生 2K 分辨率(--hd 参数)
+- 5 倍速度提升
+- 改进的文本渲染
+- 更好的提示词遵循
+- 多种风格控制
+
+### 1.3 访问方式
+- 官网:https://www.midjourney.com
+- Alpha 平台:https://alpha.midjourney.com
+- Discord:https://discord.gg/midjourney
+- 文档:https://docs.midjourney.com/hc/en-us
+
+## 2. 使用指南
+
+### 2.1 基础命令
+```
+/imagine prompt: 你的提示词 --v 8
+```
+
+### 2.2 基础参数
+| 参数 | 说明 | 示例 |
+|------|------|------|
+| --v 8 | 使用 V8 模型 | --v 8 |
+| --ar | 宽高比 | --ar 16:9 |
+| --hd | 高清模式(2048px) | --hd |
+| --q 4 | 高质量模式 | --q 4 |
+| --stylize | 风格化程度 | --stylize 250 |
+| --raw | 原始风格 | --raw |
+
+### 2.3 高级功能
+- 风格参考(--sref)
+- 角色参考(--cref)
+- 全向参考(Omni Reference)
+- 高清模式(--hd)
+- 个人化系统(--p)
+
+## 3. 技术规格
+
+### 3.1 输入规格
+- 提示词长度:最多 4000 字符
+- 支持语言:多语言(英文效果最佳)
+- 图片输入:支持图片提示、风格参考、角色参考
+
+### 3.2 输出规格
+- 默认分辨率:1024x1024
+- HD 模式分辨率:2048px
+- 最大宽高比:14:1(标准)/ 4:1(HD)
+- 输出格式:PNG
+- 生成数量:默认 4 张网格
+
+### 3.3 环境要求
+- 平台:Discord 或 Web(alpha.midjourney.com)
+- 账户:需要 Midjourney 订阅账户
+- 网络:稳定的互联网连接
+
+### 3.4 定价说明
+- 标准生成:1x GPU 成本
+- HD 模式:4x GPU 成本
+- Q4 模式:4x GPU 成本
+- HD + Q4:16x GPU 成本
+
+## 4. 参考资源
+
+### 4.1 官方资源
+- 官网:https://www.midjourney.com
+- 帮助中心:https://docs.midjourney.com/hc/en-us
+- Discord:https://discord.gg/midjourney
+
+### 4.2 教程视频
+1. Everything YOU Need to Know about Midjourney V8 (8:13)
+2. FINALLY - Midjourney V8 is HERE! (15:31)
+3. THE BEST TIP for Generating Text in Midjourney V8 (10:46)
+
+---
+*文档生成时间:2026-03-27*

+ 63 - 0
examples/tool_research/tool_results/Midjourney_v8/实际用例.md

@@ -0,0 +1,63 @@
+# Midjourney v8 实际用例
+
+> 调研日期:2026-03-27  
+> 共收集 20 个真实案例
+
+## 案例 1:荒漠游侠角色设计
+**用户输入**:
+```
+Desert ranger, wearing a sandy cloak, with a background of endless dunes and a twilight sky --v 6.1
+```
+**效果**:生成荒漠游侠角色形象,沙色斗篷在黄昏天空下呈现温暖色调
+**来源**:https://mj.bandeyu.com/doc/1/
+
+## 案例 2:水下城市居民
+**用户输入**:
+```
+Resident of an underwater city, with translucent fins and glowing eyes, surrounded by colorful coral reefs --v 6.1
+```
+**效果**:生成水下城市居民形象,透明鱼鳍和发光眼睛细节清晰
+**来源**:https://mj.bandeyu.com/doc/1/
+
+## 案例 3:现代简约客厅
+**用户输入**:
+```
+Modern living room, minimalist furniture, large windows providing ample natural light --v 6.1 --ar 16:9
+```
+**效果**:生成现代简约风格客厅,大窗户引入充足自然光
+**来源**:https://mj.bandeyu.com/doc/2/
+
+## 案例 4:男士西装设计
+**用户输入**:
+```
+Modern urban men's suit, clean lines, made of dark grey wool fabric --v 6.1 --stylize 250
+```
+**效果**:生成现代都市风格男士西装,深灰色羊毛面料质感清晰
+**来源**:https://mj.bandeyu.com/doc/3/
+
+## 案例 5:童话城堡插画
+**用户输入**:
+```
+Fairy tale castle on a hill, watercolor style, soft pastel colors, storybook illustration --v 6.1
+```
+**效果**:生成童话风格城堡插画,水彩画风,柔和的粉彩色调
+**来源**:https://mj.bandeyu.com/doc/4/
+
+## 评测与对比
+
+### V8 vs V7 对比
+- V8 在解剖结构(手部)和角色一致性方面有明显优势
+- V8 默认偏向摄影写实风格
+- V7 默认偏向艺术化、温暖、绘画感风格
+- 生成速度:V8 提升约 5 倍
+
+### 竞品对比
+| 特性 | Midjourney v8 | DALL-E 3 | Stable Diffusion 3 |
+|------|--------------|----------|-------------------|
+| 图像质量 | 9.5/10 | 8.5/10 | 8.0/10 |
+| 生成速度 | 10-15秒 | 15-30秒 | 取决于硬件 |
+| 分辨率 | 原生2K | 1792px | 取决于配置 |
+| 价格 | $10-120/月 | $20/月 | 免费(本地) |
+
+---
+*文档生成时间:2026-03-27*

+ 139 - 0
examples/tool_research/tool_results/Nano_Banana_Pro/使用介绍.md

@@ -0,0 +1,139 @@
+# Nano Banana Pro 使用介绍
+
+> 最后更新: 2026-03-27  
+> 模型版本: gemini-3-pro-image-preview  
+> 发布机构: Google DeepMind
+
+## 1. 基础概览
+
+### 1.1 工具简介
+Nano Banana Pro(官方名称:Gemini 3 Pro Image)是 Google DeepMind 推出的专业级 AI 图像生成与编辑模型。
+
+### 1.2 核心特性
+- 支持 4K 超高分辨率
+- 多语言高保真文本渲染
+- 最多 14 张参考图输入
+- 基于 Gemini 3 Pro 的高级推理
+- 集成 Google Search 实时知识检索
+- 专业级参数控制
+
+### 1.3 访问渠道
+- Gemini App: 免费额度 + 付费
+- Google AI Studio: $0.134-0.24/张
+- API: gemini-3-pro-image-preview
+
+## 2. 技术规格
+
+### 2.1 模型信息
+- 官方名称: Gemini 3 Pro Image
+- 模型 ID: gemini-3-pro-image-preview
+- SDK 最低版本: Python 1.52.0, JS/TS 1.30
+- Python 版本要求: 3.11+
+
+### 2.2 分辨率选项
+- 1K: 标准分辨率 ($0.134)
+- 2K: 高分辨率 ($0.134)
+- 4K: 超高分辨率 ($0.24)
+
+### 2.3 支持的宽高比
+1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9
+
+### 2.4 输入限制
+- 最大图片输入: 14张
+- 支持格式: PNG, JPEG, WebP
+
+## 3. 核心功能
+
+### 3.1 完美文本渲染
+支持多语言文本(英文、中文、日文、西班牙文等),可生成复杂的排版布局。
+
+### 3.2 思考过程(Thinking Process)
+模型在生成前会进行推理,用户可查看其思考过程。
+
+### 3.3 搜索接地(Search Grounding)
+连接 Google 搜索获取实时数据,生成准确的信息图表。
+
+### 3.4 4K 超高分辨率
+支持多种分辨率选项,满足专业印刷和展示需求。
+
+### 3.5 多图像混合
+最多支持 14 张图片输入,保持角色和品牌一致性。
+
+## 4. 安装与配置
+
+### 4.1 获取 API 密钥
+1. 访问 https://aistudio.google.com
+2. 使用 Google 账号登录
+3. 进入 API Key 管理页面
+4. 复制 API 密钥
+
+### 4.2 启用计费
+Nano Banana Pro 没有免费层级,必须启用计费。
+
+### 4.3 安装 SDK
+```bash
+# Python
+pip install -U "google-genai>=1.52.0"
+pip install Pillow
+
+# JavaScript/TypeScript
+npm install @google/genai
+```
+
+### 4.4 初始化客户端
+```python
+from google import genai
+from google.genai import types
+
+client = genai.Client(api_key="YOUR_API_KEY")
+PRO_MODEL_ID = "gemini-3-pro-image-preview"
+```
+
+## 5. 使用指南
+
+### 5.1 基础图像生成
+```python
+response = client.models.generate_content(
+    model=PRO_MODEL_ID,
+    contents="Create a photorealistic image of a siamese cat",
+    config=types.GenerateContentConfig(
+        response_modalities=['Text', 'Image'],
+        image_config=types.ImageConfig(aspect_ratio="16:9")
+    )
+)
+```
+
+### 5.2 启用思考模式
+```python
+config=types.GenerateContentConfig(
+    thinking_config=types.ThinkingConfig(include_thoughts=True)
+)
+```
+
+### 5.3 使用搜索接地
+```python
+config=types.GenerateContentConfig(
+    tools=[{"google_search": {}}]
+)
+```
+
+### 5.4 4K 图像生成
+```python
+image_config=types.ImageConfig(
+    aspect_ratio="1:1",
+    image_size="4K"
+)
+```
+
+## 6. 定价信息
+- 1K/2K 图像: $0.134/张
+- 4K 图像: $0.24/张
+- Batch API: 50% 折扣
+
+## 7. 参考资源
+- Google AI Studio: https://aistudio.google.com
+- 官方文档: https://ai.google.dev
+- API 文档: https://ai.google.dev/gemini-api/docs/image-generation
+
+---
+*文档生成时间: 2026-03-27*

+ 124 - 0
examples/tool_research/tool_results/Nano_Banana_Pro/实际用例.md

@@ -0,0 +1,124 @@
+# Nano Banana Pro 实际用例
+
+> 最后更新: 2026-03-27
+
+## 应用场景
+
+### 1. AI Influencer 创作
+**场景**: 创建一致的虚拟网红形象
+**提示词示例**:
+```
+Create a full-body photo of a 25-year-old female fashion influencer 
+standing in a modern urban street, photorealistic, golden hour lighting.
+```
+**来源**: YouTube - AI Master 频道
+
+### 2. 专业头像生成
+**场景**: 为 LinkedIn 等职业平台生成专业头像
+**提示词示例**:
+```
+Professional headshot for LinkedIn, business casual attire, 
+neutral background, soft studio lighting, confident expression.
+```
+**来源**: YouTube - AI Master 频道
+
+### 3. 电商产品 Mockup
+**场景**: 生成产品展示图,无需实际拍摄
+**提示词示例**:
+```
+A photo of a premium skincare product bottle on a marble surface, 
+soft natural lighting, minimalist aesthetic, 4K resolution.
+```
+**来源**: 小红书用户案例
+
+### 4. 社交媒体广告
+**场景**: 快速生成广告素材
+**提示词示例**:
+```
+Create a vibrant social media ad for a summer sale, 
+bold typography saying "50% OFF", Instagram story format 9:16.
+```
+**来源**: YouTube - Dan Kieft 频道
+
+### 5. 书籍封面设计
+**场景**: 自助出版书籍的封面设计
+**提示词示例**:
+```
+Book cover design for a mystery novel, dark atmospheric scene 
+with a foggy London street, title text "The Shadow Case".
+```
+**来源**: YouTube - AI Master 频道
+
+### 6. 信息图与教育内容
+**场景**: 创建教学材料、知识图表
+**提示词示例**:
+```
+Create an educational infographic explaining the water cycle 
+for middle school students, colorful and engaging style.
+```
+**来源**: 官方文档示例
+
+### 7. 建筑与室内设计可视化
+**场景**: 快速生成设计方案效果图
+**提示词示例**:
+```
+Modern minimalist living room interior, floor-to-ceiling windows 
+with city view, natural daylight, architectural visualization style.
+```
+**来源**: YouTube - Thomas Creates 频道
+
+### 8. 漫画与插画翻译
+**场景**: 将漫画中的文字翻译成其他语言
+**提示词示例**:
+```
+Translate all text in this comic panel from English to Japanese, 
+maintain the original font style and layout.
+```
+**来源**: 知乎变现案例
+
+### 9. 手绘转效果图
+**场景**: 将草图转换为精美效果图
+**提示词示例**:
+```
+Convert this hand-drawn sketch into a photorealistic architectural 
+rendering, maintain the original composition.
+```
+**来源**: 小红书用户案例
+
+### 10. 多角色场景合成
+**场景**: 将多个人物合成到一个场景中
+**提示词示例**:
+```
+Create a team photo of these 8 people in a modern office setting, 
+professional business attire.
+```
+**来源**: 官方文档示例
+
+## 竞品对比
+
+### 综合对比表
+| 指标 | Nano Banana Pro | Midjourney V7 | DALL-E 3 |
+|------|-----------------|---------------|----------|
+| 文本准确率 | 94-96% ⭐⭐⭐⭐⭐ | 71% ⭐⭐ | 76-78% ⭐⭐⭐ |
+| 最大分辨率 | 4K 原生 | 1024×1024 | 1792px |
+| 生成速度 | 8-12秒 | 20-30秒 | 15-25秒 |
+| API 成本 | $0.134-0.24/张 | $0.30-0.60/张 | $0.016/张 |
+| 多图像输入 | 14张 | 有限支持 | 不支持 |
+| 实时搜索 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 |
+
+### 选择建议
+- 专业商业项目: Nano Banana Pro
+- 艺术创作: Midjourney V7
+- 低成本/易用性: DALL-E 3
+- 信息图表: Nano Banana Pro
+
+## 视频教程资源
+
+### 精选 YouTube 教程
+1. Nano Banana AI Tutorial (Kevin Stratvert) - 377K 观看
+2. ULTIMATE NANO BANANA TUTORIAL (AI Master) - 251K 观看
+3. Create Cinematic AI Ads (Dan Kieft) - 201K 观看
+4. Master Google Nano Banana Pro (Rourke Heath) - 109K 观看
+
+---
+*文档生成时间: 2026-03-27*

+ 93 - 0
examples/tool_research/tool_results/Seedream_5.0_Lite/使用介绍.md

@@ -0,0 +1,93 @@
+# Seedream 5.0 Lite 使用介绍
+
+> 调研日期: 2026-03-27  
+> 开发者: ByteDance (字节跳动) Seed 团队  
+> 模型类型: 多模态图像生成模型
+
+## 1. 基础概览
+
+### 1.1 工具介绍
+Seedream 5.0 Lite 是字节跳动 Seed 团队于 2026 年 2 月发布的新一代智能图像创作模型。
+
+### 1.2 核心特性
+- 精准指令理解与图像重建
+- 图像一致性增强(支持最多 14 张参考图)
+- 美学质量优化
+- 深度推理与意图理解
+- 实时联网检索(业界首创)
+- 高级编辑能力
+
+### 1.3 官方链接
+- 产品主页: https://www.byteplus.com/en/product/Seedream
+- 免费试用: https://console.byteplus.com/ark
+- API Explorer: https://api.byteplus.com/api-explorer
+- Discord: https://discord.gg/VBzeWBKctr
+
+### 1.4 平台支持
+- BytePlus (官方): $0.035/张
+- Replicate: $0.035/张
+- Together AI: $0.04/张
+- MindStudio: 免费
+- Dreamina: 免费试用
+- fal.ai: 按量计费
+
+## 2. 技术规格
+
+### 2.1 核心架构
+- Chain-of-Thought 推理先行架构
+- 实时网络搜索能力
+- 多图参考一致性(最多 10 张)
+- 轻量化部署(延迟 <1.2s)
+
+### 2.2 输入输出
+- 支持文本到图像
+- 支持图像到图像
+- 支持多图参考(最多 14 张)
+- 输出格式: PNG, JPEG
+
+### 2.3 定价
+- BytePlus: $0.035/张
+- Replicate: $0.035/张
+- Together AI: $0.04/张
+
+## 3. 核心功能
+
+### 3.1 实时联网检索
+首次引入联网能力,可获取天气、金价、票房等实时信息。
+
+### 3.2 多步视觉推理
+基于增强的全球知识库,提供准确解释与教育级洞察。
+
+### 3.3 图像一致性
+人脸特征、色调、风格等高度稳定,支持最多 14 张参考图。
+
+### 3.4 高级编辑
+支持单图编辑、元素删除/修改、背景随机替换。
+
+## 4. 使用指南
+
+### 4.1 快速开始
+1. 访问 BytePlus 控制台
+2. 注册/登录账户
+3. 选择 Seedream 5.0 Lite 模型
+4. 输入提示词或上传参考图
+5. 点击生成
+
+### 4.2 API 调用
+```python
+import requests
+
+response = requests.post(
+    "https://api.byteplus.com/v1/seedream",
+    headers={"Authorization": "Bearer YOUR_API_KEY"},
+    json={"prompt": "your prompt here"}
+)
+```
+
+## 5. 参考资源
+- 产品主页: https://www.byteplus.com/en/product/Seedream
+- API 文档: https://docs.byteplus.com/en/docs
+- 免费试用: https://console.byteplus.com/ark
+
+---
+*文档生成时间: 2026-03-27*

+ 87 - 0
examples/tool_research/tool_results/Seedream_5.0_Lite/实际用例.md

@@ -0,0 +1,87 @@
+# Seedream 5.0 Lite 实际用例
+
+> 调研日期: 2026-03-27
+
+## 核心应用场景
+
+### 1. 实时信息可视化
+**场景**: 生成包含实时数据的图像
+**示例**: 
+- 当前天气预报可视化
+- 实时金价走势图
+- 最新票房数据图表
+**优势**: 业界首创的联网检索能力
+
+### 2. 教育内容创作
+**场景**: 创建教学材料和知识图表
+**示例**:
+- 科学原理解释图
+- 历史事件时间线
+- 数学公式可视化
+**优势**: 深度推理与意图理解
+
+### 3. 多图一致性创作
+**场景**: 保持角色/品牌一致性的系列创作
+**示例**:
+- 游戏角色多场景展示
+- 品牌营销素材系列
+- 故事板连续画面
+**优势**: 支持最多 14 张参考图,一致性达 92%
+
+### 4. 高级图像编辑
+**场景**: 精细化图像修改
+**示例**:
+- 局部元素删除/修改
+- 背景随机替换
+- 风格迁移
+**优势**: 单图编辑能力强
+
+### 5. 专业场景生成
+**场景**: 特定行业的专业内容
+**示例**:
+- 建筑效果图
+- 产品设计图
+- 室内设计方案
+**优势**: 美学质量优化,电影级纹理
+
+## 竞品对比
+
+### Seedream 5.0 Lite vs 竞品
+| 特性 | Seedream 5.0 Lite | FLUX.2 | Midjourney |
+|------|------------------|--------|------------|
+| 实时联网 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
+| 多图参考 | 14张 | 10张 | 有限 |
+| 推理能力 | Chain-of-Thought | 强 | 中等 |
+| 价格 | $0.035/张 | $0.07/张 | 订阅制 |
+| 生成速度 | <1.2s | 8-15s | 10-15s |
+
+### 适用场景推荐
+- 需要实时信息: Seedream 5.0 Lite
+- 需要最高质量: FLUX.2 [max]
+- 需要艺术风格: Midjourney
+- 需要快速生成: Seedream 5.0 Lite
+
+## 技术亮点
+
+### 1. Chain-of-Thought 推理
+先进行逻辑解析,再进行像素生成,提升准确性。
+
+### 2. 实时网络搜索
+可生成包含实时天气、新闻、金价等动态信息的图像。
+
+### 3. 强提示词遵循
+在 MagicBench 基准测试中,Prompt Following 维度有显著提升。
+
+### 4. 轻量化部署
+延迟 <1.2s(1080p),适合集成到设计工具链。
+
+## 首发平台
+- Dreamina(CapCut)- 主力支持平台
+- Higgsfield
+- Flux AI
+- fal.ai
+- Freepik
+- Zawa.ai
+
+---
+*文档生成时间: 2026-03-27*

+ 27 - 0
im-client/client.py

@@ -120,6 +120,9 @@ class IMClient:
         self.log = logging.getLogger(contact_id)
         self._send_queue = asyncio.Queue()
 
+        # 消息回调:{chat_id: callback} 或 {"*": callback} 匹配所有窗口
+        self._on_message_callbacks: dict[str, callable] = {}
+
     def open_window(self, chat_id: str | None = None, notifier: AgentNotifier | None = None) -> str:
         """打开一个新窗口。
 
@@ -147,6 +150,15 @@ class IMClient:
         """关闭一个窗口。"""
         self._windows.pop(chat_id, None)
         self._notifiers.pop(chat_id, None)
+
+    def on_message(self, callback: callable, chat_id: str = "*"):
+        """注册消息回调。收到消息时立即触发,无需轮询。
+
+        Args:
+            callback: 回调函数,签名 async def callback(msg: dict)
+            chat_id: 监听的窗口 ID,"*" 表示所有窗口
+        """
+        self._on_message_callbacks[chat_id] = callback
         self.log.info(f"关闭窗口: {chat_id}")
 
     def list_windows(self) -> list[str]:
@@ -195,11 +207,13 @@ class IMClient:
                     window.append_to_in_pending(data)
                     window.append_to_chatbox(data)
                     self.log.info(f"收到消息 -> 窗口 {receiver_chat_id}: {data['sender']}")
+                    await self._fire_on_message(receiver_chat_id, data)
                 elif not receiver_chat_id:
                     # 广播到所有窗口
                     for chat_id, window in self._windows.items():
                         window.append_to_in_pending(data)
                         window.append_to_chatbox(data)
+                        await self._fire_on_message(chat_id, data)
                     self.log.info(f"收到消息 -> 广播到 {len(self._windows)} 个窗口: {data['sender']}")
                 else:
                     self.log.warning(f"收到消息但窗口 {receiver_chat_id} 不存在")
@@ -212,6 +226,19 @@ class IMClient:
                 else:
                     self.log.warning(f"消息 {resp.msg_id} 发送失败: {resp.error}")
 
+    async def _fire_on_message(self, chat_id: str, data: dict):
+        """触发消息回调。"""
+        # 精确匹配
+        cb = self._on_message_callbacks.get(chat_id)
+        if cb is None:
+            # 通配符匹配
+            cb = self._on_message_callbacks.get("*")
+        if cb:
+            try:
+                await cb(data)
+            except Exception as e:
+                self.log.error(f"on_message 回调异常: {e}")
+
     async def _send_worker(self):
         """从队列取消息并发送。"""
         while True:

+ 75 - 0
im-server/contact_store.py

@@ -0,0 +1,75 @@
+import json
+from pathlib import Path
+from filelock import FileLock
+
+
+class ContactStore:
+    """联系人关系和 chat_id 存储。
+
+    存储格式 (data/contacts.json):
+    {
+        "alice": {
+            "contacts": ["bob", "charlie"],
+            "chats": {
+                "bob": ["chat_001", "chat_002"],
+                "charlie": ["chat_003"]
+            }
+        }
+    }
+    """
+
+    def __init__(self, data_dir: str = "data"):
+        self.data_dir = Path(data_dir)
+        self.data_dir.mkdir(parents=True, exist_ok=True)
+        self.contacts_path = self.data_dir / "contacts.json"
+        self._lock = FileLock(str(self.data_dir / ".contacts.lock"))
+
+        if not self.contacts_path.exists():
+            self.contacts_path.write_text("{}")
+
+    def add_contact(self, user_id: str, contact_id: str):
+        """添加联系人(单向)。"""
+        with self._lock:
+            data = self._load()
+            if user_id not in data:
+                data[user_id] = {"contacts": [], "chats": {}}
+            if contact_id not in data[user_id]["contacts"]:
+                data[user_id]["contacts"].append(contact_id)
+            if contact_id not in data[user_id]["chats"]:
+                data[user_id]["chats"][contact_id] = []
+            self._save(data)
+
+    def add_chat(self, user_id: str, contact_id: str, chat_id: str):
+        """为某个联系人添加新的 chat_id。"""
+        with self._lock:
+            data = self._load()
+            if user_id not in data:
+                data[user_id] = {"contacts": [], "chats": {}}
+            if contact_id not in data[user_id]["chats"]:
+                data[user_id]["chats"][contact_id] = []
+            if chat_id not in data[user_id]["chats"][contact_id]:
+                data[user_id]["chats"][contact_id].append(chat_id)
+            self._save(data)
+
+    def get_contacts(self, user_id: str) -> list[str]:
+        """获取某用户的联系人列表。"""
+        with self._lock:
+            data = self._load()
+            if user_id not in data:
+                return []
+            return data[user_id].get("contacts", [])
+
+    def get_chats(self, user_id: str, contact_id: str) -> list[str]:
+        """获取与某联系人的所有 chat_id。"""
+        with self._lock:
+            data = self._load()
+            if user_id not in data:
+                return []
+            return data[user_id].get("chats", {}).get(contact_id, [])
+
+    def _load(self) -> dict:
+        text = self.contacts_path.read_text(encoding="utf-8")
+        return json.loads(text) if text.strip() else {}
+
+    def _save(self, data: dict):
+        self.contacts_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")

+ 128 - 0
im-server/main.py

@@ -0,0 +1,128 @@
+import logging
+from fastapi import FastAPI, WebSocket, WebSocketDisconnect
+from protocol import IMMessage, IMResponse
+from contact_store import ContactStore
+
+logging.basicConfig(level=logging.INFO, format="%(asctime)s [SERVER] %(message)s")
+log = logging.getLogger(__name__)
+
+app = FastAPI()
+
+# 在线路由表: (contact_id, chat_id) -> WebSocket
+registry: dict[tuple[str, str], WebSocket] = {}
+
+# 联系人存储
+contact_store = ContactStore()
+
+
+@app.get("/health")
+async def health():
+    # 返回格式: {contact_id: [chat_id1, chat_id2, ...]}
+    online_map = {}
+    for (contact_id, chat_id) in registry.keys():
+        online_map.setdefault(contact_id, []).append(chat_id)
+    return {"status": "ok", "online": online_map}
+
+
+@app.post("/contacts/{user_id}/add")
+async def add_contact(user_id: str, contact_id: str):
+    """添加联系人。"""
+    contact_store.add_contact(user_id, contact_id)
+    return {"status": "ok"}
+
+
+@app.get("/contacts/{user_id}")
+async def get_contacts(user_id: str):
+    """获取联系人列表。"""
+    contacts = contact_store.get_contacts(user_id)
+    return {"contacts": contacts}
+
+
+@app.post("/chats/{user_id}/add")
+async def add_chat(user_id: str, contact_id: str, chat_id: str):
+    """为某联系人添加新的 chat_id。"""
+    contact_store.add_chat(user_id, contact_id, chat_id)
+    return {"status": "ok"}
+
+
+@app.get("/chats/{user_id}/{contact_id}")
+async def get_chats(user_id: str, contact_id: str):
+    """获取与某联系人的所有 chat_id。"""
+    chats = contact_store.get_chats(user_id, contact_id)
+    return {"chats": chats}
+
+
+@app.websocket("/ws")
+async def ws_endpoint(ws: WebSocket, contact_id: str, chat_id: str):
+    await ws.accept()
+
+    key = (contact_id, chat_id)
+
+    # 如果同 (contact_id, chat_id) 已连接,踢掉旧连接
+    old = registry.pop(key, None)
+    if old:
+        try:
+            await old.close(code=4001, reason="replaced")
+        except Exception:
+            pass
+
+    registry[key] = ws
+    log.info(f"{contact_id}:{chat_id} 上线 (当前在线: {len(registry)} 个窗口)")
+
+    try:
+        while True:
+            data = await ws.receive_json()
+            msg = IMMessage(**data)
+
+            # 截取消息内容用于日志(避免日志过长)
+            content_preview = (msg.content or "")[:150]
+            if len(msg.content or "") > 150:
+                content_preview += "..."
+
+            log.info(f"收到消息: {msg.sender}:{msg.sender_chat_id} -> {msg.receiver}:{msg.receiver_chat_id or '*'}")
+            log.info(f"  内容: {content_preview}")
+
+            # 查找目标连接
+            if msg.receiver_chat_id:
+                # 定向发送到指定窗口
+                target_key = (msg.receiver, msg.receiver_chat_id)
+                target_ws = registry.get(target_key)
+                targets = [(target_key, target_ws)] if target_ws else []
+            else:
+                # 广播给该 contact_id 的所有窗口
+                targets = [(k, v) for k, v in registry.items() if k[0] == msg.receiver]
+
+            if not targets:
+                await ws.send_json(
+                    IMResponse(status="failed", msg_id=msg.msg_id, error="target_offline").model_dump()
+                )
+                log.info(f"转发失败: {msg.receiver} 不在线")
+                continue
+
+            # 尝试发送到所有目标
+            success_count = 0
+            for target_key, target_ws in targets:
+                try:
+                    await target_ws.send_json(msg.model_dump())
+                    success_count += 1
+                except Exception:
+                    # 目标连接已死但未清理
+                    registry.pop(target_key, None)
+                    log.warning(f"转发失败: {target_key} 连接已断, 已清理")
+
+            if success_count > 0:
+                await ws.send_json(
+                    IMResponse(status="success", msg_id=msg.msg_id).model_dump()
+                )
+                log.info(f"转发成功: {msg.sender} -> {msg.receiver} ({success_count} 个窗口)")
+            else:
+                await ws.send_json(
+                    IMResponse(status="failed", msg_id=msg.msg_id, error="send_error").model_dump()
+                )
+
+    except WebSocketDisconnect:
+        registry.pop(key, None)
+        log.info(f"{contact_id}:{chat_id} 下线 (当前在线: {len(registry)} 个窗口)")
+    except Exception as e:
+        registry.pop(key, None)
+        log.error(f"{contact_id}:{chat_id} 异常断开: {e}")

+ 23 - 0
im-server/protocol.py

@@ -0,0 +1,23 @@
+from pydantic import BaseModel
+from typing import Optional
+import uuid
+
+
+class IMMessage(BaseModel):
+    msg_id: str = ""
+    sender: str
+    receiver: str
+    content: str
+    msg_type: str = "chat"  # chat | image | video | system
+    sender_chat_id: Optional[str] = None  # 发送方窗口 ID
+    receiver_chat_id: Optional[str] = None  # 接收方窗口 ID(指定则定向,否则广播)
+
+    def model_post_init(self, __context):
+        if not self.msg_id:
+            self.msg_id = uuid.uuid4().hex[:12]
+
+
+class IMResponse(BaseModel):
+    status: str  # "success" | "failed"
+    msg_id: str
+    error: Optional[str] = None

+ 100 - 0
knowhub/agents/README.md

@@ -0,0 +1,100 @@
+# Knowledge Manager Agent
+
+知识库管理 Agent,负责知识库的查询、维护和更新。
+
+## 功能
+
+### 1. 查询模式(Librarian)
+帮助其他 Agent 了解知识库中已有的内容。
+
+**使用示例**:
+```python
+# 在调研前查询已有信息
+result = agent("knowledge_manager", 
+    "查询知识库中关于 ControlNet 的所有信息")
+```
+
+**输出**:
+- 已有工具列表
+- 已有资源列表
+- 已有知识列表
+- 补充建议
+
+### 2. 保存模式(Saver)
+接收调研结果,智能去重并保存到知识库。
+
+**使用示例**:
+```python
+# 调研完成后保存结果
+data = {
+    "tools": [...],
+    "resources": [...],
+    "knowledge": [...]
+}
+result = agent("knowledge_manager", 
+    f"保存以下调研结果:{json.dumps(data, ensure_ascii=False)}")
+```
+
+## 数据格式
+
+### 工具(tools)
+```json
+{
+  "name": "ControlNet",
+  "slug": "controlnet",
+  "category": "plugin",
+  "version": "1.1",
+  "description": "SD 姿态控制插件",
+  "tutorial": "使用方法...",
+  "source_url": "https://..."
+}
+```
+
+### 资源(resources)
+```json
+{
+  "title": "ControlNet 官方文档",
+  "body": "文档内容...",
+  "content_type": "documentation",
+  "source_url": "https://...",
+  "metadata": {}
+}
+```
+
+### 知识(knowledge)
+```json
+{
+  "task": "在生成人物图像时,需要控制姿态",
+  "content": "使用 ControlNet 插件...",
+  "types": ["tool"],
+  "tags": {"tool": "controlnet"},
+  "score": 4
+}
+```
+
+## 环境配置
+
+需要设置以下环境变量:
+```bash
+export DATABASE_URL="postgresql://user:pass@localhost:5432/knowhub"
+export KNOWHUB_API="http://localhost:8000"
+```
+
+## 作为子 Agent 使用
+
+在 presets.json 中注册:
+```json
+{
+  "knowledge_manager": {
+    "prompt_file": "knowhub/agents/knowledge_manager.prompt",
+    "model": "qwen3.5-plus",
+    "temperature": 0.2
+  }
+}
+```
+
+在主 Agent 中调用:
+```python
+# 通过 agent 工具调用
+result = agent("knowledge_manager", "查询 ControlNet")
+```

+ 282 - 0
knowhub/agents/knowledge_manager.prompt

@@ -0,0 +1,282 @@
+---
+model: qwen3.5-plus
+temperature: 0.2
+---
+
+$system$
+
+## 角色
+你是知识库管理专家(Knowledge Manager),作为后台服务运行,通过 IM 消息与调研 Agent 协作。
+
+## 运行模式
+你是事件驱动的:
+- 收到消息时立即处理并回复
+- 处理完毕后自动休眠,等待下一条消息
+- 每条消息都是独立的请求,快速响应是第一优先级
+
+## 数据库表结构
+
+### 1. knowledge 表(核心知识条目)
+存储所有类型的知识,通过 `types` 字段区分:
+
+**知识分类(types 字段)**:
+- `["tool"]`:工具知识 - 单个工具的功能、用法、限制
+- `["strategy"]`:工序知识 - 工作流、方案、多步骤流程
+- `["case"]`:用例知识 - 真实案例、应用场景
+- `["experience"]`:执行经验 - Agent 执行任务时的反思和总结
+
+**关键字段**:
+- `task`: 产生该知识时的任务描述
+- `content`: 知识正文
+- `types`: 知识类型(上述分类)
+- `tags`: 标签键值对(如 `{"intent": "调研", "domain": "image_gen"}`)
+- `resource_ids`: 关联的 resource ID 列表
+- `eval.score`: 评分 1-5
+
+### 2. tool_table 表(工具表)
+存储工具的元信息和关联知识:
+
+**关键字段**:
+- `id`: 工具 ID,格式 `tools/{category}/{name}`
+- `name`: 工具名称
+- `introduction`: 简介
+- `tutorial`: 使用教程
+- `input`/`output`: 输入输出规格
+- `knowledge`: 关联的通用知识 ID 列表(工具知识)
+- `case_knowledge`: 关联的用例知识 ID 列表
+- `process_knowledge`: 关联的工序知识 ID 列表
+
+### 3. resources 表(资源表)
+存储文档、代码、凭证等资源:
+
+**关键字段**:
+- `id`: 资源 ID,路径格式(如 `code/crawler/baidu`)
+- `title`: 标题
+- `body`: 公开内容
+- `secure_body`: 私有/加密内容
+- `content_type`: 类型(text/code/credential/cookie)
+- `metadata.knowledge_ids`: 关联的知识 ID 列表
+
+## 可用工具
+
+### 知识库工具
+- `knowledge_search(query, top_k, types, owner)`: 搜索知识库
+- `knowledge_save(task, content, types, tags, resource_ids, score)`: 保存知识
+- `knowledge_list(limit, types, scopes)`: 列出知识
+- `knowledge_update(knowledge_id, ...)`: 更新知识
+- `resource_save(resource_id, title, body, content_type, metadata)`: 保存资源
+- `resource_get(resource_id)`: 获取资源详情
+
+### 本地缓存工具(优先使用)
+- `cache_research_data(data, source)`: 缓存调研数据到本地(不入库)
+  - **参数 data**: JSON 字符串或字典,包含 tools/resources/knowledge
+  - **自动解析**: 支持传 JSON 字符串,工具会自动解析
+  - **示例**: `cache_research_data(data='{"knowledge": [...]}', source="agent_research")`
+- `organize_cached_data(merge)`: 整理缓存数据(去重、合并)
+- `commit_to_database(organized_file)`: 将整理后的数据提交到数据库(**可能不可用**)
+- `list_cache_status()`: 查看缓存状态(显示文件列表和统计)
+
+**重要**:
+- 收到 upload 消息时,**必须使用 `cache_research_data`**,不要用 `write_file`
+- `cache_research_data` 会自动生成文件名、统计数据、处理 JSON 格式
+- 使用 `list_cache_status` 查看当前缓存状态,了解有多少数据待处理
+- **入库功能**:如果 `commit_to_database` 工具不可用,说明当前配置为"仅缓存模式",不支持入库
+
+### 文件工具(仅用于特殊场景)
+- `read_file(file_path)`: 读取文件
+- `write_file(file_path, content)`: 写入文件(不要用于缓存知识!)
+
+## 消息处理
+
+### 1. 查询请求
+调研 Agent 想了解知识库中已有什么信息。
+
+**处理方式**:
+1. 调用 `knowledge_search` 搜索相关知识,按 types 分类查询:
+   - `types=["tool"]` 查工具知识
+   - `types=["strategy"]` 查工序知识
+   - `types=["case"]` 查用例知识
+   - `types=["experience"]` 查执行经验
+2. 分析搜索结果,整理出:
+   - 已有信息:知识库中已覆盖的方面(按类型分组)
+   - 缺失信息:知识库中缺少的关键信息
+   - 调研建议:明确的调研方向和优先级
+3. 回复结构化报告
+
+**回复格式**:
+```
+## 已有信息
+
+**工具知识**(X 条):
+- 工具1:核心能力
+- 工具2:核心能力
+
+**工序知识**(Y 条):
+- 工序1:关键步骤
+- 工序2:关键步骤
+
+**用例知识**(Z 条):
+- 用例1:应用场景
+- 用例2:应用场景
+
+## 缺失信息
+- 缺少xxx
+- 缺少yyy
+
+## 调研建议
+1. 优先调研:xxx
+2. 补充:yyy
+```
+
+**要求**:简洁直接,每个要点 1-2 句话。重点突出缺失部分。
+
+### 2. 上传请求
+调研 Agent 发送调研结果,格式为 JSON,包含 tools/resources/knowledge。
+
+**消息类型**:
+- `[UPLOAD] {...}`: 增量上传,缓存到本地
+- `[UPLOAD:BATCH] ...`: 批量上传(多条合并),一起处理
+
+**处理方式(默认只缓存,不入库)**:
+1. 解析消息内容(批量消息需要解析多个 JSON)
+2. **调用 `cache_research_data(data, source)`** 缓存到本地
+   - 直接传 JSON 字符串或字典,工具会自动处理
+   - 不要用 `write_file`,缓存工具会自动生成文件名和统计
+3. 如果是 BATCH,调用 `organize_cached_data(merge=True)` 整理
+4. 回复缓存确认 + 统计
+5. **不要自动入库**:只缓存和整理,等待用户明确要求"提交到数据库"
+
+**批量处理优势**:
+- 多条 upload 消息会自动合并成一条 BATCH 消息
+- 可以一次性去重、整理所有数据,全局观更好
+- 如果正在处理时收到新消息,通过 `get_current_context` 看到队列状态,可以快速结束当前轮次
+
+**回复格式**:
+```
+✅ 已缓存到本地(批量处理 N 条)
+
+**数据统计**:
+- 工具: X 个
+- 资源: Y 个
+- 知识: Z 个(工具知识 A / 工序知识 B / 用例知识 C / 执行经验 D)
+
+**去重检查**:
+- 新增: N 条
+- 重复跳过: M 条
+
+**缓存位置**:`.cache/.knowledge/buffer/{source}-{timestamp}.json`
+
+💡 数据已保存到本地缓存,尚未入库。如需提交到数据库,请回复"提交到数据库"或"入库"。
+```
+
+### 3. 整理请求
+用户或调研 Agent 要求整理缓存数据。
+
+**处理方式**:
+1. 调用 `organize_cached_data(merge=True)` 整理所有缓存文件
+2. 去重、合并,保存到 `.cache/.knowledge/organized/`
+3. **按知识类型分组统计**
+4. 回复整理统计
+
+**回复格式**:
+```
+已整理完成
+
+**按类型统计**:
+- 工具知识: X → Y (去重 Z)
+- 工序知识: A → B (去重 C)
+- 用例知识: D → E (去重 F)
+- 执行经验: G → H (去重 I)
+
+**资源统计**:
+- 资源: J → K (去重 L)
+
+**工具统计**:
+- 工具: M → N (去重 O)
+```
+
+### 4. 提交到数据库请求
+用户明确要求将缓存数据提交到数据库(关键词:"提交到数据库"、"入库"、"保存到数据库")。
+
+**前置检查**:
+- 如果 `commit_to_database` 工具不可用,回复:"当前配置为仅缓存模式,不支持入库。如需入库,请联系管理员修改配置。"
+
+**处理方式**:
+1. 如果 buffer 中还有未整理的数据,先调用 `organize_cached_data()`
+2. 调用 `commit_to_database()` 将整理后的数据提交到数据库
+3. **建立关联关系**:
+   - 工具 → tool_table,关联 knowledge/case_knowledge/process_knowledge
+   - 资源 → resources,在 metadata.knowledge_ids 中关联知识
+   - 知识 → knowledge,在 resource_ids 中关联资源
+4. 回复提交统计
+
+**回复格式**:
+```
+✅ 已提交到数据库
+
+**knowledge 表**:
+- 工具知识: X 条
+- 工序知识: Y 条
+- 用例知识: Z 条
+- 执行经验: W 条
+
+**tool_table 表**:
+- 工具: A 个(已关联知识)
+
+**resources 表**:
+- 资源: B 个(已关联知识)
+
+**关联关系**:
+- 工具 ↔ 知识: C 条
+- 资源 ↔ 知识: D 条
+```
+
+**重要**:
+- 只有在用户明确要求"提交到数据库"、"入库"、"保存到数据库"时才尝试入库
+- 如果 `commit_to_database` 工具不可用,说明当前为"仅缓存模式",告知用户无法入库
+- 默认情况下,只缓存和整理,不入库
+- 如果用户没有明确要求入库,不要主动提示或询问是否入库
+
+## 响应原则
+
+1. **快速响应**:尽快回复,不要做不必要的操作
+2. **简洁回复**:回复内容精炼,不要冗长
+3. **默认缓存**:收到上传请求时,默认使用 `cache_research_data` 缓存到本地,不直接入库
+4. **按需入库**:只有在明确要求"提交到数据库"、"入库"时才调用 `commit_to_database`
+5. **不主动提示入库**:缓存完成后,不要主动询问或提示用户是否入库,只在回复末尾简单说明"如需入库,请回复..."
+6. **去重优先**:整理时必须去重,避免重复知识
+7. **知识分类**:严格区分工具知识、工序知识、用例知识、执行经验
+8. **关联完整**:
+   - knowledge 要关联相关的 resource_ids
+   - tool_table 要关联 knowledge/case_knowledge/process_knowledge
+   - resources 要在 metadata.knowledge_ids 中关联知识
+9. **ID 规范**:
+   - 工具:`tools/{category}/{slug}`
+   - 资源:`code/{category}/{name}` 或 `references/{topic}` 或 `credentials/{website}`
+   - 知识:自动生成 `knowledge-{date}-{time}-{hash}`
+
+## 知识分类指南
+
+**如何判断知识类型**:
+
+1. **工具知识(tool)**:
+   - 描述单个工具的功能、用法、限制
+   - 关键词:工具名称、API、参数、输入输出
+   - 示例:"Midjourney 支持 --ar 参数控制宽高比"
+
+2. **工序知识(strategy)**:
+   - 描述多步骤的工作流、方案、流程
+   - 关键词:步骤、流程、方案、工序
+   - 示例:"角色一致性生成的三步流程:1. 生成基础形象 2. 提取特征 3. 应用到新场景"
+
+3. **用例知识(case)**:
+   - 描述真实的应用案例、场景
+   - 关键词:案例、场景、实例、应用
+   - 示例:"某公司使用 ComfyUI 批量生成产品图,提升效率 10 倍"
+
+4. **执行经验(experience)**:
+   - Agent 执行任务时的反思、总结、教训
+   - 关键词:应该、避免、经验、教训
+   - 示例:"当调研 AI 工具时,应该优先访问官方文档而非第三方博客"
+
+$user$

+ 371 - 0
knowhub/agents/knowledge_manager.py

@@ -0,0 +1,371 @@
+"""
+Knowledge Manager Agent
+
+基于 AgentRunner 驱动,有完整的 trace 记录。
+通过 IM Client 事件驱动,收到消息时传给 AgentRunner 处理。
+
+架构:
+  IM 消息 → user message → AgentRunner → LLM 自主决策 → 工具调用 → trace 记录
+"""
+
+import asyncio
+import json
+import logging
+import sys
+from pathlib import Path
+from typing import Optional
+
+# 确保项目路径可用
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from agent.core.runner import AgentRunner, RunConfig
+from agent.trace import FileSystemTraceStore, Trace, Message
+from agent.llm import create_qwen_llm_call
+from agent.llm.prompts import SimplePrompt
+from agent.tools.builtin.knowledge import KnowledgeConfig
+
+logger = logging.getLogger("agents.knowledge_manager")
+
+# ===== 配置项 =====
+ENABLE_DATABASE_COMMIT = False  # 是否允许入库(False=只缓存,True=可入库)
+
+# Knowledge Manager Agent 配置
+def get_knowledge_manager_config(enable_db_commit: bool = ENABLE_DATABASE_COMMIT) -> RunConfig:
+    """获取 Knowledge Manager 配置(根据是否允许入库动态调整工具列表)"""
+    tools = [
+        "knowledge_search",
+        "knowledge_save",
+        "knowledge_list",
+        "knowledge_update",
+        "resource_save",
+        "resource_get",
+        "read_file",
+        "write_file",
+        # 本地缓存工具
+        "cache_research_data",
+        "organize_cached_data",
+        "list_cache_status",
+    ]
+
+    # 只有启用入库时才开放 commit_to_database 工具
+    if enable_db_commit:
+        tools.append("commit_to_database")
+
+    return RunConfig(
+        model="qwen3.5-plus",
+        temperature=0.2,
+        max_iterations=50,
+        agent_type="knowledge_manager",
+        name="知识库管理",
+        goal_compression="none",
+        # 禁用所有知识提取和反思
+        knowledge=KnowledgeConfig(
+            enable_extraction=False,
+            enable_completion_extraction=False,
+            enable_injection=False,
+        ),
+        tools=tools,
+    )
+
+
+async def start_knowledge_manager(
+    contact_id: str = "knowledge_manager",
+    server_url: str = "ws://localhost:58005",
+    chat_id: str = "main",
+    enable_db_commit: bool = ENABLE_DATABASE_COMMIT
+):
+    """
+    启动 Knowledge Manager(AgentRunner 驱动 + 事件驱动)
+
+    收到 IM 消息时,将消息作为 user message 传给 AgentRunner。
+    LLM 自主决策调用什么工具,所有操作记录到 trace。
+
+    Args:
+        contact_id: IM 身份 ID
+        server_url: IM Server 地址
+        chat_id: 聊天窗口 ID
+        enable_db_commit: 是否允许入库(False=只缓存,True=可入库)
+    """
+    logger.info(f"正在启动 Knowledge Manager...")
+    logger.info(f"  - Contact ID: {contact_id}")
+    logger.info(f"  - Server: {server_url}")
+    logger.info(f"  - 入库功能: {'启用' if enable_db_commit else '禁用(仅缓存)'}")
+
+    # 注册内部工具(缓存管理)
+    try:
+        sys.path.insert(0, str(Path(__file__).parent.parent))
+        from internal_tools.cache_manager import (
+            cache_research_data,
+            organize_cached_data,
+            commit_to_database,
+            list_cache_status,
+        )
+        from agent.tools import get_tool_registry
+        registry = get_tool_registry()
+        registry.register(cache_research_data)
+        registry.register(organize_cached_data)
+        registry.register(commit_to_database)
+        registry.register(list_cache_status)
+        logger.info("  ✓ 已注册缓存管理工具")
+    except Exception as e:
+        logger.error(f"  ✗ 注册缓存工具失败: {e}")
+
+    # 导入 IM Client
+    try:
+        sys.path.insert(0, str(Path(__file__).parent.parent.parent / "im-client"))
+        from client import IMClient
+    except ImportError as e:
+        logger.error(f"无法导入 IM Client: {e}")
+        return
+
+    # --- 初始化 AgentRunner ---
+    store = FileSystemTraceStore(base_path=".trace")
+    llm_call = create_qwen_llm_call(model=KNOWLEDGE_MANAGER_CONFIG.model)
+
+    runner = AgentRunner(
+        trace_store=store,
+        llm_call=llm_call,
+        debug=True,
+        logger_name="agents.knowledge_manager"
+    )
+
+    # 加载 system prompt
+    prompt_path = Path(__file__).parent / "knowledge_manager.prompt"
+    prompt = SimplePrompt(prompt_path)
+    system_messages = prompt.build_messages()
+
+    # Trace 状态(同一个 trace 持续追加,保持上下文)
+    current_trace_id = None
+    message_queue = asyncio.Queue()  # 消息队列,防止丢消息
+    upload_buffer = []  # upload 消息缓冲区(用于批处理)
+    upload_timer = None  # 延迟处理定时器
+
+    # --- 初始化 IM Client ---
+    client = IMClient(
+        contact_id=contact_id,
+        server_url=server_url,
+        data_dir=str(Path.home() / ".knowhub" / "im_data")
+    )
+    # 静默 notifier(消息由 on_message 回调处理,不需要 notifier 通知)
+    class _SilentNotifier:
+        async def notify(self, count, from_contacts):
+            pass
+
+    # 打开窗口
+    client.open_window(chat_id=chat_id, notifier=_SilentNotifier())
+
+    # --- 消息处理器(从队列中取消息,逐条处理)---
+    async def message_processor():
+        nonlocal current_trace_id
+
+        while True:
+            msg = await message_queue.get()  # 阻塞等待,零消耗
+
+            sender = msg.get("sender")
+            content = msg.get("content", "")
+
+            # 处理完消息后清空 pending,防止 notifier 反复通知
+            client.read_pending(chat_id)
+
+            logger.info(f"[KM] <- 处理消息: {sender}")
+            logger.info(f"[KM]    内容: {content[:120]}{'...' if len(content) > 120 else ''}")
+
+            try:
+                # 续跑同一个 trace(首次为 None 会新建,后续复用)
+                if current_trace_id is None:
+                    messages = system_messages + [{"role": "user", "content": content}]
+                else:
+                    messages = [{"role": "user", "content": content}]
+
+                # 获取配置
+                km_config = get_knowledge_manager_config(enable_db_commit)
+
+                config = RunConfig(
+                    model=km_config.model,
+                    temperature=km_config.temperature,
+                    max_iterations=km_config.max_iterations,
+                    agent_type=km_config.agent_type,
+                    name=km_config.name,
+                    goal_compression=km_config.goal_compression,
+                    tools=km_config.tools,
+                    knowledge=km_config.knowledge,
+                    trace_id=current_trace_id,  # 复用 trace,保持完整生命周期
+                    context={
+                        "km_queue_size": message_queue.qsize(),  # 当前队列中待处理消息数
+                        "current_sender": sender,  # 当前处理的消息来源
+                    }
+                )
+
+                # 执行 AgentRunner
+                response_text = ""
+                async for item in runner.run(messages=messages, config=config):
+                    if isinstance(item, Trace):
+                        current_trace_id = item.trace_id
+                        if item.status == "running":
+                            logger.info(f"[KM] Trace: {item.trace_id[:8]}...")
+                        elif item.status == "completed":
+                            logger.info(f"[KM] Trace 完成 (消息: {item.total_messages})")
+                        elif item.status == "failed":
+                            logger.error(f"[KM] Trace 失败: {item.error_message}")
+
+                    elif isinstance(item, Message):
+                        if item.role == "assistant":
+                            msg_content = item.content
+                            if isinstance(msg_content, dict):
+                                text = msg_content.get("text", "")
+                                tool_calls = msg_content.get("tool_calls")
+                                if text:
+                                    # 始终记录最新的文本(最后一条就是最终回复)
+                                    response_text = text
+                                    if tool_calls:
+                                        logger.info(f"[KM] 思考: {text[:80]}...")
+                            elif isinstance(msg_content, str) and msg_content:
+                                response_text = msg_content
+
+                        elif item.role == "tool":
+                            tool_content = item.content
+                            tool_name = tool_content.get("tool_name", "?") if isinstance(tool_content, dict) else "?"
+                            logger.info(f"[KM] 工具: {tool_name}")
+
+                # 回复
+                if response_text:
+                    client.send_message(
+                        chat_id=chat_id,
+                        receiver=sender,
+                        content=response_text
+                    )
+                    logger.info(f"[KM] -> 已回复: {sender} ({len(response_text)} 字符)")
+                else:
+                    logger.warning(f"[KM] AgentRunner 没有生成回复")
+
+            except Exception as e:
+                logger.error(f"[KM] 处理失败: {e}", exc_info=True)
+
+            message_queue.task_done()
+
+    # --- 批处理 upload 消息 ---
+    async def process_upload_batch():
+        """批处理 upload 消息(延迟 5 秒,合并多个 upload)"""
+        nonlocal upload_buffer, upload_timer
+
+        await asyncio.sleep(5)  # 等待 5 秒,收集更多 upload
+
+        if not upload_buffer:
+            upload_timer = None
+            return
+
+        # 合并所有 upload 消息
+        batch = upload_buffer.copy()
+        upload_buffer.clear()
+        upload_timer = None
+
+        logger.info(f"[KM] 批处理 {len(batch)} 条 upload 消息")
+
+        # 合并成一条消息入队
+        merged_content = f"[UPLOAD:BATCH] 收到 {len(batch)} 条上传请求,请合并处理:\n"
+        for i, msg in enumerate(batch, 1):
+            merged_content += f"\n--- 第 {i} 条 ---\n{msg['content']}\n"
+
+        await message_queue.put({
+            "sender": batch[0]["sender"],
+            "content": merged_content
+        })
+
+    # --- 快速处理 ask 查询(不经过队列)---
+    async def handle_ask_query(sender: str, content: str):
+        """快速响应 ask 查询,不阻塞 upload 处理"""
+        try:
+            logger.info(f"[KM] <- 快速查询: {sender}")
+
+            # 1. 查询数据库
+            from agent.tools.builtin.knowledge import knowledge_search
+            db_result = await knowledge_search(query=content, top_k=5, min_score=3)
+
+            # 2. 读取缓存(正在处理的 upload 数据)
+            from knowhub.internal_tools.cache_manager import list_cache_status
+            cache_status = await list_cache_status()
+
+            # 3. 组合回复
+            response_parts = []
+
+            if db_result.output and db_result.output != "未找到相关知识":
+                response_parts.append("## 数据库中的知识\n\n" + db_result.output)
+
+            if cache_status.metadata and cache_status.metadata.get("files"):
+                cache_files = cache_status.metadata["files"]
+                if cache_files:
+                    response_parts.append(
+                        f"## 缓存中的数据\n\n"
+                        f"正在处理 {len(cache_files)} 个缓存文件,包含最新调研数据(尚未入库)。\n"
+                        f"如需查看详情,请稍后再次查询。"
+                    )
+
+            if not response_parts:
+                response_parts.append("暂无相关知识,数据库和缓存均为空。")
+
+            response_text = "\n\n".join(response_parts)
+
+            # 4. 立即回复
+            client.send_message(
+                chat_id=chat_id,
+                receiver=sender,
+                content=response_text
+            )
+            logger.info(f"[KM] -> 快速回复: {sender} ({len(response_text)} 字符)")
+
+        except Exception as e:
+            logger.error(f"[KM] 快速查询失败: {e}", exc_info=True)
+            client.send_message(
+                chat_id=chat_id,
+                receiver=sender,
+                content=f"查询失败: {e}"
+            )
+
+    # --- 消息回调(ask 快速通道,upload 批处理)---
+    async def on_message(msg: dict):
+        nonlocal upload_timer
+
+        sender = msg.get("sender")
+        content = msg.get("content", "")
+
+        # 忽略自己发的消息
+        if sender == contact_id:
+            return
+
+        # 判断消息类型(根据前缀标记)
+        if content.startswith("[ASK]"):
+            # 快速通道:立即处理,不入队
+            query = content[5:].strip()  # 去掉 [ASK] 前缀
+            logger.info(f"[KM] <- 收到查询消息: {sender} (快速通道)")
+            asyncio.create_task(handle_ask_query(sender, query))
+
+        elif content.startswith("[UPLOAD"):
+            # 批处理:加入缓冲区,延迟处理
+            logger.info(f"[KM] <- 收到上传消息: {sender} (加入批处理缓冲区)")
+            upload_buffer.append(msg)
+
+            # 启动或重置定时器
+            if upload_timer:
+                upload_timer.cancel()
+            upload_timer = asyncio.create_task(process_upload_batch())
+
+        else:
+            # 其他消息:正常入队
+            logger.info(f"[KM] <- 收到消息: {sender} (入队,队列长度: {message_queue.qsize() + 1})")
+            await message_queue.put(msg)
+
+    client.on_message(on_message, chat_id="*")
+
+    # 启动消息处理器(后台任务)
+    processor_task = asyncio.create_task(message_processor())
+
+    # 启动 IM Client(事件驱动)
+    logger.info("✅ Knowledge Manager 已启动(AgentRunner 驱动)")
+    try:
+        await client.run()
+    finally:
+        processor_task.cancel()
+        try:
+            await processor_task
+        except asyncio.CancelledError:
+            pass

+ 279 - 0
knowhub/agents/knowledge_manager_v2.py

@@ -0,0 +1,279 @@
+"""
+Knowledge Manager V2 - 轻量级缓存 + 按需 Agent
+
+架构:
+1. 收到 upload 消息 → 直接调用 cache_research_data,不启动 Agent
+2. 收到 query/organize 消息 → 启动 Agent 处理
+3. 消息队列保证不丢消息
+
+优势:
+- upload 操作轻量快速
+- 只在需要时才启动 Agent(查询、整理、入库)
+"""
+
+import asyncio
+import json
+import logging
+import sys
+from pathlib import Path
+from typing import Optional
+
+# 确保项目路径可用
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from agent.core.runner import AgentRunner, RunConfig
+from agent.trace import FileSystemTraceStore, Trace, Message
+from agent.llm import create_qwen_llm_call
+from agent.llm.prompts import SimplePrompt
+from agent.tools.builtin.knowledge import KnowledgeConfig
+
+# 导入缓存工具
+from knowhub.internal_tools.cache_manager import cache_research_data
+
+logger = logging.getLogger("agents.knowledge_manager_v2")
+
+# Knowledge Manager Agent 配置
+KNOWLEDGE_MANAGER_CONFIG = RunConfig(
+    model="qwen3.5-plus",
+    temperature=0.2,
+    max_iterations=50,
+    agent_type="knowledge_manager",
+    name="知识库管理",
+    goal_compression="none",
+    knowledge=KnowledgeConfig(
+        enable_extraction=False,
+        enable_completion_extraction=False,
+        enable_injection=False,
+    ),
+    tools=[
+        "knowledge_search",
+        "knowledge_save",
+        "knowledge_list",
+        "knowledge_update",
+        "resource_save",
+        "resource_get",
+        "read_file",
+        "write_file",
+        "cache_research_data",
+        "organize_cached_data",
+        "commit_to_database",
+        "list_cache_status",
+    ],
+)
+
+
+async def start_knowledge_manager_v2(
+    contact_id: str = "knowledge_manager",
+    server_url: str = "ws://localhost:58005",
+    chat_id: str = "main"
+):
+    """
+    启动 Knowledge Manager V2(轻量级缓存 + 按需 Agent)
+
+    消息类型:
+    1. upload 消息 → 直接缓存,不启动 Agent
+    2. query/organize/commit 消息 → 启动 Agent 处理
+    """
+    logger.info(f"正在启动 Knowledge Manager V2...")
+    logger.info(f"  - Contact ID: {contact_id}")
+    logger.info(f"  - Server: {server_url}")
+
+    # 导入 IM Client
+    try:
+        sys.path.insert(0, str(Path(__file__).parent.parent.parent / "im-client"))
+        from client import IMClient
+    except ImportError as e:
+        logger.error(f"无法导入 IM Client: {e}")
+        return
+
+    # --- 初始化 AgentRunner ---
+    store = FileSystemTraceStore(base_path=".trace")
+    llm_call = create_qwen_llm_call(model=KNOWLEDGE_MANAGER_CONFIG.model)
+
+    runner = AgentRunner(
+        trace_store=store,
+        llm_call=llm_call,
+        debug=True
+    )
+
+    # 加载 system prompt
+    prompt_path = Path(__file__).parent / "knowledge_manager.prompt"
+    prompt = SimplePrompt(prompt_path)
+    system_messages = prompt.build_messages()
+
+    # Trace 状态
+    current_trace_id = None
+    message_queue = asyncio.Queue()
+
+    # --- 初始化 IM Client ---
+    client = IMClient(
+        contact_id=contact_id,
+        server_url=server_url,
+        data_dir=str(Path.home() / ".knowhub" / "im_data")
+    )
+    client.open_window(chat_id=chat_id)
+
+    # --- 消息分类处理器 ---
+    async def message_processor():
+        nonlocal current_trace_id
+
+        while True:
+            msg = await message_queue.get()
+
+            sender = msg.get("sender")
+            content = msg.get("content", "")
+
+            logger.info(f"[KM] <- 收到消息: {sender}")
+            logger.info(f"[KM]    内容: {content[:120]}{'...' if len(content) > 120 else ''}")
+
+            try:
+                # 判断消息类型
+                is_upload = "增量上传" in content or "最终提交" in content
+
+                if is_upload:
+                    # 直接缓存,不启动 Agent
+                    await handle_upload_message(content, sender, client, chat_id)
+                else:
+                    # 启动 Agent 处理
+                    await handle_agent_message(
+                        content, sender, client, chat_id,
+                        runner, system_messages, current_trace_id
+                    )
+
+            except Exception as e:
+                logger.error(f"[KM] 处理失败: {e}", exc_info=True)
+                # 回复错误
+                client.send_message(
+                    chat_id=chat_id,
+                    receiver=sender,
+                    content=f"处理失败: {str(e)}"
+                )
+
+            message_queue.task_done()
+
+    async def handle_upload_message(content: str, sender: str, client, chat_id: str):
+        """处理 upload 消息:直接缓存,不启动 Agent"""
+        logger.info(f"[KM] 处理 upload 消息(轻量级)")
+
+        # 提取 JSON 数据
+        try:
+            # 找到 JSON 部分
+            json_start = content.find("{")
+            if json_start == -1:
+                raise ValueError("消息中没有 JSON 数据")
+
+            json_str = content[json_start:]
+            data = json.loads(json_str)
+
+            # 直接调用缓存工具
+            result = await cache_research_data(data=data, source=sender)
+
+            # 回复
+            response = result.output if hasattr(result, 'output') else str(result)
+            client.send_message(
+                chat_id=chat_id,
+                receiver=sender,
+                content=response
+            )
+            logger.info(f"[KM] -> 已缓存并回复: {sender}")
+
+        except Exception as e:
+            logger.error(f"[KM] 缓存失败: {e}")
+            client.send_message(
+                chat_id=chat_id,
+                receiver=sender,
+                content=f"缓存失败: {str(e)}"
+            )
+
+    async def handle_agent_message(
+        content: str, sender: str, client, chat_id: str,
+        runner, system_messages, trace_id
+    ):
+        """处理需要 Agent 的消息:查询、整理、入库"""
+        logger.info(f"[KM] 处理 Agent 消息(重量级)")
+
+        nonlocal current_trace_id
+
+        # 构造 user message
+        if current_trace_id is None:
+            messages = system_messages + [{"role": "user", "content": content}]
+        else:
+            messages = [{"role": "user", "content": content}]
+
+        config = RunConfig(
+            model=KNOWLEDGE_MANAGER_CONFIG.model,
+            temperature=KNOWLEDGE_MANAGER_CONFIG.temperature,
+            max_iterations=KNOWLEDGE_MANAGER_CONFIG.max_iterations,
+            agent_type=KNOWLEDGE_MANAGER_CONFIG.agent_type,
+            name=KNOWLEDGE_MANAGER_CONFIG.name,
+            goal_compression=KNOWLEDGE_MANAGER_CONFIG.goal_compression,
+            tools=KNOWLEDGE_MANAGER_CONFIG.tools,
+            knowledge=KNOWLEDGE_MANAGER_CONFIG.knowledge,
+            trace_id=current_trace_id,
+        )
+
+        # 执行 AgentRunner
+        response_text = ""
+        async for item in runner.run(messages=messages, config=config):
+            if isinstance(item, Trace):
+                current_trace_id = item.trace_id
+                if item.status == "running":
+                    logger.info(f"[KM] Trace: {item.trace_id[:8]}...")
+                elif item.status == "completed":
+                    logger.info(f"[KM] Trace 完成")
+                elif item.status == "failed":
+                    logger.error(f"[KM] Trace 失败: {item.error_message}")
+
+            elif isinstance(item, Message):
+                if item.role == "assistant":
+                    msg_content = item.content
+                    if isinstance(msg_content, dict):
+                        text = msg_content.get("text", "")
+                        tool_calls = msg_content.get("tool_calls")
+                        if text and not tool_calls:
+                            response_text = text
+
+                elif item.role == "tool":
+                    tool_content = item.content
+                    tool_name = tool_content.get("tool_name", "?") if isinstance(tool_content, dict) else "?"
+                    logger.info(f"[KM] 工具: {tool_name}")
+
+        # 回复
+        if response_text:
+            client.send_message(
+                chat_id=chat_id,
+                receiver=sender,
+                content=response_text
+            )
+            logger.info(f"[KM] -> 已回复: {sender}")
+        else:
+            logger.warning(f"[KM] AgentRunner 没有生成回复")
+
+    # --- 消息回调(只入队)---
+    async def on_message(msg: dict):
+        sender = msg.get("sender")
+        if sender == contact_id:
+            return
+
+        logger.info(f"[KM] <- 入队: {sender} (队列长度: {message_queue.qsize() + 1})")
+        await message_queue.put(msg)
+
+    client.on_message(on_message, chat_id="*")
+
+    # 启动消息处理器
+    processor_task = asyncio.create_task(message_processor())
+
+    # 启动 IM Client
+    logger.info("✅ Knowledge Manager V2 已启动(轻量级缓存 + 按需 Agent)")
+    try:
+        await client.run()
+    finally:
+        processor_task.cancel()
+        try:
+            await processor_task
+        except asyncio.CancelledError:
+            pass
+
+
+if __name__ == "__main__":
+    asyncio.run(start_knowledge_manager_v2())

+ 64 - 0
knowhub/agents/librarian.prompt

@@ -0,0 +1,64 @@
+---
+model: qwen3.5-plus
+temperature: 0.1
+---
+
+$system$
+
+## 角色
+你是知识库的图书管理员(Librarian),负责帮助其他 Agent 查询和了解知识库中已有的内容。
+
+## 职责
+1. **搜索已有工具**:根据工具名称/类别搜索 tool_table
+2. **搜索已有资源**:根据关键词搜索 resources(文档、手册、代码等)
+3. **搜索已有知识**:根据任务场景搜索 knowledge
+4. **提供建议**:告知调研者哪些内容已存在,哪些需要补充
+
+## 可用工具
+- `knowledge_search`: 搜索知识库中的知识
+- `resource_get`: 获取资源详情
+- `bash`: 执行数据库查询(通过 psql 或 HTTP API)
+
+## 工作流程
+
+### 当收到查询请求时:
+1. **理解需求**:明确调研者想了解什么(工具/资源/知识)
+2. **多维度搜索**:
+   - 搜索 tool_table:是否已有该工具
+   - 搜索 resources:是否已有相关文档
+   - 搜索 knowledge:是否已有相关经验
+3. **整理结果**:
+   - 已有内容:列出 ID、标题、简介
+   - 缺失内容:明确指出哪些方面需要补充
+4. **提供建议**:
+   - 如果已有完整信息 → 建议复用
+   - 如果部分存在 → 建议补充更新
+   - 如果完全没有 → 建议全新调研
+
+## 输出格式
+
+```markdown
+# 查询结果:[工具名/主题]
+
+## 已有工具
+- [tool_id] 工具名 v版本 - 简介
+  - 关联知识:X 条
+  - 状态:已接入/未接入
+
+## 已有资源
+- [resource_id] 资源标题 (类型)
+  - 来源:URL
+  - 内容摘要:...
+
+## 已有知识
+- [knowledge_id] 任务场景 (⭐评分)
+  - 内容摘要:...
+
+## 建议
+- ✅ 已有完整的工具信息,建议复用
+- ⚠️ 缺少用户案例,建议补充
+- ❌ 完全没有相关内容,建议全新调研
+```
+
+$user$
+{query}

+ 61 - 0
knowhub/agents/run_knowledge_manager.py

@@ -0,0 +1,61 @@
+"""
+Knowledge Manager Agent - 启动脚本
+
+作为独立的 IM Client 运行,监听并处理知识库查询和保存请求。
+"""
+
+import asyncio
+import json
+import logging
+import os
+import sys
+from pathlib import Path
+
+# 添加项目根目录到 Python 路径
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from dotenv import load_dotenv
+load_dotenv()
+
+logging.basicConfig(
+    level=logging.INFO,
+    format="%(asctime)s [KM] %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+async def main():
+    """启动 Knowledge Manager Agent"""
+
+    # 配置
+    contact_id = os.getenv("KNOWLEDGE_MANAGER_CONTACT_ID", "knowledge_manager")
+    server_url = os.getenv("IM_SERVER_URL", "ws://localhost:8005")
+    chat_id = "main"
+
+    logger.info(f"正在启动 Knowledge Manager...")
+    logger.info(f"  - Contact ID: {contact_id}")
+    logger.info(f"  - Server: {server_url}")
+    logger.info(f"  - Chat ID: {chat_id}")
+
+    # 导入 IM Client
+    try:
+        sys.path.insert(0, str(Path(__file__).parent.parent.parent / "im-client"))
+        from client import IMClient
+    except ImportError as e:
+        logger.error(f"无法导入 IM Client: {e}")
+        return
+
+    # 创建 IM Client
+    client = IMClient(
+        contact_id=contact_id,
+        server_url=server_url,
+        data_dir=Path.home() / ".knowhub" / "im_data"
+    )
+
+    # 连接到服务器
+    try:
+        await client.connect(chat_id=chat_id)
+        logger.info("✅ 已连接到 IM Server")
+    except Exception as e:
+        logger.error(f"连接失败: {e}")
+        return

+ 89 - 0
knowhub/docs/migration-guide.md

@@ -0,0 +1,89 @@
+# 知识管理工具迁移指南
+
+## 架构变更
+
+### 旧架构(Deprecated)
+```
+Agent → knowledge_search/knowledge_save → HTTP API → KnowHub Server → Database
+```
+
+### 新架构(推荐)
+```
+Agent → ask_knowledge/upload_knowledge → IM → Knowledge Manager → Internal Tools → Database
+```
+
+## 工具对照表
+
+| 旧工具 | 新工具 | 说明 |
+|--------|--------|------|
+| `knowledge_search` | `ask_knowledge` | 查询知识库(同步等待回复) |
+| `knowledge_save` | `upload_knowledge` | 上传知识(异步,支持增量+批量) |
+| `resource_save` | `upload_knowledge` | 统一通过 upload_knowledge 上传 |
+
+## 迁移步骤
+
+### 1. 启用 IM 和 Knowledge Manager
+
+```python
+# config.py
+IM_ENABLED = True
+KNOWLEDGE_MANAGER_ENABLED = True
+```
+
+### 2. 更新 Prompt
+
+**旧写法**:
+```
+knowledge_search("查询 ControlNet")
+knowledge_save(task="...", content="...", types=["tool"])
+```
+
+**新写法**:
+```
+ask_knowledge("查询 ControlNet 相关信息")
+upload_knowledge({
+  "tools": [...],
+  "resources": [...],
+  "knowledge": [...]
+})
+```
+
+### 3. 增量上传模式
+
+新架构支持边搜边传:
+```python
+# 第一批数据
+upload_knowledge({"tools": [...]})
+
+# 第二批数据
+upload_knowledge({"resources": [...]})
+
+# 最终提交(触发入库)
+upload_knowledge({"knowledge": [...]}, finalize=True)
+```
+
+## 新架构优势
+
+1. **自动去重**:Knowledge Manager 在缓冲区中自动去重
+2. **批量提交**:减少数据库操作,提高性能
+3. **并发控制**:通过 IM 消息队列避免并发冲突
+4. **状态管理**:Knowledge Manager 维护 IDLE/WORKING 状态
+5. **后台处理**:异步深度去重和整合
+
+## 向后兼容
+
+旧工具仍然可用,但会输出 deprecation 警告:
+```
+WARNING: knowledge_search is deprecated. Use ask_knowledge instead.
+```
+
+建议尽快迁移到新架构。
+
+## 内部工具封装
+
+直接数据库访问的工具已移至 `knowhub/internal_tools/`,仅 Knowledge Manager 内部使用:
+- `knowledge_search` (内部)
+- `knowledge_save` (内部)
+- `resource_save` (内部)
+
+普通 Agent 不应直接调用这些工具。

+ 105 - 0
knowhub/internal_tools/README.md

@@ -0,0 +1,105 @@
+# KnowHub Internal Tools
+
+这些工具只能在 Knowledge Manager 内部使用,不暴露给普通 Agent。
+
+## 缓存管理工具
+
+Knowledge Manager 使用本地缓存机制来处理调研数据,流程如下:
+
+### 1. 接收数据 → 缓存到本地
+
+调研 Agent 通过 IM 发送调研结果时,KM 使用 `cache_research_data` 缓存到本地:
+
+```python
+await cache_research_data(
+    data={
+        "tools": [...],
+        "resources": [...],
+        "knowledge": [...]
+    },
+    source="agent_research"
+)
+```
+
+**存储位置**:`.cache/.knowledge/buffer/{source}_{timestamp}.json`
+
+### 2. 整理数据 → 去重合并
+
+当需要整理缓存数据时,使用 `organize_cached_data`:
+
+```python
+await organize_cached_data(merge=True)
+```
+
+**功能**:
+- 读取所有 buffer 中的文件
+- 按名称/标题去重
+- 合并到一个文件
+- 保存到 `.cache/.knowledge/organized/organized_{timestamp}.json`
+- 清空 buffer(如果 `merge=True`)
+
+### 3. 提交到数据库(可选)
+
+只有在明确要求时,才使用 `commit_to_database` 提交到数据库:
+
+```python
+await commit_to_database()
+```
+
+**功能**:
+- 读取最新的 organized 文件
+- 调用 `resource_save` 和 `knowledge_save` 入库
+- 返回提交统计
+
+### 4. 查看缓存状态
+
+使用 `list_cache_status` 查看当前缓存情况:
+
+```python
+await list_cache_status()
+```
+
+## 工作流示例
+
+### 场景 1:只缓存和整理,不入库
+
+```
+调研 Agent: 发送调研结果
+KM: cache_research_data() → 缓存到 buffer
+KM: 回复"已缓存"
+
+用户: 整理一下
+KM: organize_cached_data() → 整理到 organized
+KM: 回复"已整理,去重 X 个"
+```
+
+### 场景 2:缓存、整理、入库
+
+```
+调研 Agent: 发送调研结果
+KM: cache_research_data() → 缓存到 buffer
+KM: 回复"已缓存"
+
+用户: 提交到数据库
+KM: organize_cached_data() → 整理
+KM: commit_to_database() → 入库
+KM: 回复"已提交到数据库"
+```
+
+## 目录结构
+
+```
+.cache/.knowledge/
+├── buffer/              # 原始缓存(调研 Agent 提交的数据)
+│   ├── agent_research_20260402_120000.json
+│   └── agent_research_20260402_130000.json
+└── organized/           # 整理后的数据(去重合并)
+    └── organized_20260402_140000.json
+```
+
+## 设计原则
+
+1. **默认缓存**:收到数据时先缓存,不直接入库
+2. **按需入库**:只有明确要求时才提交到数据库
+3. **去重优先**:整理时必须去重
+4. **快速响应**:缓存操作很快,不阻塞调研 Agent

+ 48 - 0
knowhub/internal_tools/__init__.py

@@ -0,0 +1,48 @@
+"""
+KnowHub 内部工具
+
+这些工具只能在 Knowledge Manager 内部使用,不暴露给普通 Agent。
+直接操作数据库,用于实现 Knowledge Manager 的核心功能。
+
+从 agent/tools/builtin/knowledge.py 迁移而来。
+"""
+
+# 重新导出原有工具供 Knowledge Manager 使用
+from agent.tools.builtin.knowledge import (
+    knowledge_search,
+    knowledge_save,
+    knowledge_list,
+    knowledge_update,
+    knowledge_batch_update,
+    knowledge_slim,
+)
+
+from agent.tools.builtin.knowledge import (
+    resource_save,
+    resource_get,
+)
+
+from knowhub.internal_tools.cache_manager import (
+    cache_research_data,
+    organize_cached_data,
+    commit_to_database,
+    list_cache_status,
+)
+
+__all__ = [
+    # 知识操作
+    "knowledge_search",
+    "knowledge_save",
+    "knowledge_list",
+    "knowledge_update",
+    "knowledge_batch_update",
+    "knowledge_slim",
+    # 资源操作
+    "resource_save",
+    "resource_get",
+    # 缓存操作
+    "cache_research_data",
+    "organize_cached_data",
+    "commit_to_database",
+    "list_cache_status",
+]

+ 418 - 0
knowhub/internal_tools/cache_manager.py

@@ -0,0 +1,418 @@
+"""
+Knowledge Manager 本地缓存工具
+
+负责:
+1. 接收调研数据,存入本地缓存
+2. 整理缓存数据(去重、合并、关联)
+3. 可选提交到数据库或仅保存本地
+"""
+
+import json
+import logging
+from pathlib import Path
+from typing import Dict, Any, List, Optional
+from datetime import datetime
+from agent.tools import tool, ToolResult
+
+logger = logging.getLogger(__name__)
+
+# 缓存目录
+CACHE_DIR = Path(".cache/.knowledge")
+BUFFER_DIR = CACHE_DIR / "buffer"
+ORGANIZED_DIR = CACHE_DIR / "organized"
+
+
+def _ensure_dirs():
+    """确保缓存目录存在"""
+    BUFFER_DIR.mkdir(parents=True, exist_ok=True)
+    ORGANIZED_DIR.mkdir(parents=True, exist_ok=True)
+
+
+@tool(
+    description=(
+        "缓存调研数据到本地(不入库)。"
+        "接受 JSON 字符串或字典,自动解析。"
+        "适用于增量上传场景,先缓存后整理。"
+    )
+)
+async def cache_research_data(
+    data: str | Dict[str, Any],
+    source: str = "unknown",
+) -> ToolResult:
+    """
+    缓存调研数据到本地(不入库)
+
+    Args:
+        data: 调研结果(JSON 字符串或字典),包含 tools/resources/knowledge
+        source: 数据来源标识(如 agent_id)
+
+    Returns:
+        缓存确认和统计
+
+    Examples:
+        # 方式 1:直接传字典
+        cache_research_data(data={"knowledge": [...]}, source="agent_research")
+
+        # 方式 2:传 JSON 字符串
+        cache_research_data(data='{"knowledge": [...]}', source="agent_research")
+    """
+    try:
+        _ensure_dirs()
+
+        # 自动解析 JSON 字符串
+        if isinstance(data, str):
+            try:
+                data = json.loads(data)
+            except json.JSONDecodeError as e:
+                return ToolResult(
+                    title="❌ JSON 解析失败",
+                    output=f"无法解析 JSON 字符串: {e}",
+                    error=str(e)
+                )
+
+        # 生成文件名
+        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+        filename = f"{source}_{timestamp}.json"
+        filepath = BUFFER_DIR / filename
+
+        # 写入缓存
+        with open(filepath, "w", encoding="utf-8") as f:
+            json.dump(data, f, ensure_ascii=False, indent=2)
+
+        # 统计
+        stats = []
+        if data.get("tools"):
+            stats.append(f"工具: {len(data['tools'])} 个")
+        if data.get("resources"):
+            stats.append(f"资源: {len(data['resources'])} 个")
+        if data.get("knowledge"):
+            stats.append(f"知识: {len(data['knowledge'])} 个")
+
+        return ToolResult(
+            title="✅ 已缓存到本地",
+            output=f"文件: {filename}\n\n" + "\n".join(f"- {s}" for s in stats),
+            metadata={"filepath": str(filepath), "stats": stats}
+        )
+
+    except Exception as e:
+        logger.error(f"缓存失败: {e}")
+        return ToolResult(
+            title="❌ 缓存失败",
+            output=f"错误: {str(e)}",
+            error=str(e)
+        )
+
+
+@tool()
+async def organize_cached_data(
+    merge: bool = True,
+) -> ToolResult:
+    """
+    整理缓存数据(去重、合并)
+
+    Args:
+        merge: 是否合并所有缓存文件
+
+    Returns:
+        整理后的数据统计
+    """
+    try:
+        _ensure_dirs()
+
+        # 读取所有缓存文件
+        buffer_files = list(BUFFER_DIR.glob("*.json"))
+        if not buffer_files:
+            return ToolResult(
+                title="ℹ️ 无缓存数据",
+                output="buffer 目录为空"
+            )
+
+        all_tools = []
+        all_resources = []
+        all_knowledge = []
+
+        for filepath in buffer_files:
+            with open(filepath, "r", encoding="utf-8") as f:
+                data = json.load(f)
+                all_tools.extend(data.get("tools", []))
+                all_resources.extend(data.get("resources", []))
+                all_knowledge.extend(data.get("knowledge", []))
+
+        # 去重(基于名称/标题)
+        def dedupe_by_key(items: List[Dict], key: str) -> List[Dict]:
+            seen = set()
+            result = []
+            for item in items:
+                identifier = item.get(key)
+                if identifier and identifier not in seen:
+                    seen.add(identifier)
+                    result.append(item)
+            return result
+
+        tools_deduped = dedupe_by_key(all_tools, "名称")
+        resources_deduped = dedupe_by_key(all_resources, "标题")
+        knowledge_deduped = dedupe_by_key(all_knowledge, "内容")
+
+        # 保存整理后的数据
+        organized_data = {
+            "tools": tools_deduped,
+            "resources": resources_deduped,
+            "knowledge": knowledge_deduped,
+            "organized_at": datetime.now().isoformat(),
+            "source_files": [f.name for f in buffer_files]
+        }
+
+        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+        organized_file = ORGANIZED_DIR / f"organized_{timestamp}.json"
+
+        with open(organized_file, "w", encoding="utf-8") as f:
+            json.dump(organized_data, f, ensure_ascii=False, indent=2)
+
+        # 清空 buffer(可选)
+        if merge:
+            for filepath in buffer_files:
+                filepath.unlink()
+
+        stats = [
+            f"工具: {len(all_tools)} → {len(tools_deduped)} (去重 {len(all_tools) - len(tools_deduped)})",
+            f"资源: {len(all_resources)} → {len(resources_deduped)} (去重 {len(all_resources) - len(resources_deduped)})",
+            f"知识: {len(all_knowledge)} → {len(knowledge_deduped)} (去重 {len(all_knowledge) - len(knowledge_deduped)})",
+        ]
+
+        return ToolResult(
+            title="✅ 整理完成",
+            output=f"文件: {organized_file.name}\n\n" + "\n".join(f"- {s}" for s in stats),
+            metadata={"filepath": str(organized_file), "stats": organized_data}
+        )
+
+    except Exception as e:
+        logger.error(f"整理失败: {e}")
+        return ToolResult(
+            title="❌ 整理失败",
+            output=f"错误: {str(e)}",
+            error=str(e)
+        )
+
+
+@tool()
+async def commit_to_database(
+    organized_file: Optional[str] = None,
+) -> ToolResult:
+    """
+    将整理后的数据提交到数据库,建立完整的关联关系
+
+    Args:
+        organized_file: 指定要提交的文件(不指定则提交最新的)
+
+    Returns:
+        提交结果统计(按知识类型分组)
+    """
+    try:
+        _ensure_dirs()
+
+        # 找到要提交的文件
+        if organized_file:
+            filepath = ORGANIZED_DIR / organized_file
+        else:
+            organized_files = sorted(ORGANIZED_DIR.glob("organized_*.json"))
+            if not organized_files:
+                return ToolResult(
+                    title="ℹ️ 无整理数据",
+                    output="organized 目录为空,请先调用 organize_cached_data"
+                )
+            filepath = organized_files[-1]
+
+        if not filepath.exists():
+            return ToolResult(
+                title="❌ 文件不存在",
+                output=f"文件: {filepath.name}"
+            )
+
+        # 读取数据
+        with open(filepath, "r", encoding="utf-8") as f:
+            data = json.load(f)
+
+        # 导入数据库工具
+        from agent.tools.builtin.knowledge import resource_save, knowledge_save
+
+        # 统计变量
+        saved_tools = 0
+        saved_resources = 0
+        knowledge_by_type = {"tool": 0, "strategy": 0, "case": 0, "experience": 0}
+        errors = []
+
+        # 映射:resource_id -> knowledge_ids(用于反向关联)
+        resource_to_knowledge = {}
+
+        # 第一步:保存资源
+        for resource in data.get("resources", []):
+            try:
+                resource_id = resource.get("id", f"resource_{saved_resources}")
+                await resource_save(
+                    resource_id=resource_id,
+                    title=resource.get("标题", ""),
+                    body=resource.get("内容", ""),
+                    content_type=resource.get("类型", "text"),
+                    metadata=resource.get("元数据", {})
+                )
+                saved_resources += 1
+                resource_to_knowledge[resource_id] = []
+            except Exception as e:
+                errors.append(f"资源 {resource.get('标题')}: {e}")
+
+        # 第二步:保存知识,建立 resource_ids 关联
+        for knowledge in data.get("knowledge", []):
+            try:
+                # 提取关联的 resource_ids
+                resource_ids = knowledge.get("resource_ids", [])
+
+                # 保存知识
+                result = await knowledge_save(
+                    task=knowledge.get("主题", ""),
+                    content=knowledge.get("内容", ""),
+                    types=knowledge.get("类型", []),
+                    tags=knowledge.get("标签", {}),
+                    resource_ids=resource_ids,
+                )
+
+                # 统计知识类型
+                types = knowledge.get("类型", [])
+                for t in types:
+                    if t in knowledge_by_type:
+                        knowledge_by_type[t] += 1
+
+                # 提取 knowledge_id,用于反向关联
+                knowledge_id = result.metadata.get("knowledge_id")
+                if knowledge_id:
+                    for rid in resource_ids:
+                        if rid in resource_to_knowledge:
+                            resource_to_knowledge[rid].append(knowledge_id)
+
+            except Exception as e:
+                errors.append(f"知识 {knowledge.get('主题')}: {e}")
+
+        # 第三步:保存工具(作为 resource,类型为 tool)
+        for tool in data.get("tools", []):
+            try:
+                tool_id = f"tools/{tool.get('分类', 'misc')}/{tool.get('名称', 'unknown')}"
+                await resource_save(
+                    resource_id=tool_id,
+                    title=tool.get("名称", ""),
+                    body=json.dumps(tool, ensure_ascii=False, indent=2),
+                    content_type="tool",
+                    metadata={
+                        "category": tool.get("分类", ""),
+                        "introduction": tool.get("简介", ""),
+                        **tool.get("工具信息", {})
+                    }
+                )
+                saved_tools += 1
+            except Exception as e:
+                errors.append(f"工具 {tool.get('名称')}: {e}")
+
+        # 构建统计输出
+        stats_lines = [
+            "**knowledge 表**:",
+            f"- 工具知识: {knowledge_by_type['tool']} 条",
+            f"- 工序知识: {knowledge_by_type['strategy']} 条",
+            f"- 用例知识: {knowledge_by_type['case']} 条",
+            f"- 执行经验: {knowledge_by_type['experience']} 条",
+            "",
+            "**resources 表**:",
+            f"- 资源: {saved_resources} 个",
+            f"- 工具: {saved_tools} 个",
+        ]
+
+        if errors:
+            stats_lines.append(f"\n**错误**: {len(errors)} 个")
+
+        output = "已提交到数据库\n\n" + "\n".join(stats_lines)
+        if errors:
+            output += "\n\n错误详情:\n" + "\n".join(f"- {e}" for e in errors[:5])
+
+        return ToolResult(
+            title="✅ 提交到数据库完成",
+            output=output,
+            metadata={
+                "saved": {
+                    "tools": saved_tools,
+                    "resources": saved_resources,
+                    "knowledge": knowledge_by_type
+                }
+            }
+        )
+
+    except Exception as e:
+        logger.error(f"提交到数据库失败: {e}")
+        return ToolResult(
+            title="❌ 提交失败",
+            output=f"错误: {str(e)}",
+            error=str(e)
+        )
+
+
+@tool(
+    description=(
+        "查看缓存状态,包括 buffer 和 organized 中的文件列表、统计信息。"
+        "用于了解当前有多少数据在缓存中等待处理。"
+    )
+)
+async def list_cache_status() -> ToolResult:
+    """
+    查看缓存状态(buffer 和 organized 中的文件)
+
+    Returns:
+        缓存目录中的文件列表和统计
+    """
+    _ensure_dirs()
+
+    buffer_files = sorted(BUFFER_DIR.glob("*.json"))
+    organized_files = sorted(ORGANIZED_DIR.glob("*.json"))
+
+    lines = [f"**Buffer** ({len(buffer_files)} 个文件):"]
+
+    total_tools = 0
+    total_resources = 0
+    total_knowledge = 0
+
+    for f in buffer_files:
+        size = f.stat().st_size
+        # 读取文件统计
+        try:
+            with open(f, "r", encoding="utf-8") as file:
+                data = json.load(file)
+                tools = len(data.get("tools", []))
+                resources = len(data.get("resources", []))
+                knowledge = len(data.get("knowledge", []))
+                total_tools += tools
+                total_resources += resources
+                total_knowledge += knowledge
+                lines.append(
+                    f"  - {f.name} ({size // 1024}KB): "
+                    f"工具 {tools}, 资源 {resources}, 知识 {knowledge}"
+                )
+        except Exception:
+            lines.append(f"  - {f.name} ({size // 1024}KB)")
+
+    if buffer_files:
+        lines.append(
+            f"\n  **合计**: 工具 {total_tools}, 资源 {total_resources}, 知识 {total_knowledge}"
+        )
+
+    lines.append(f"\n**Organized** ({len(organized_files)} 个文件):")
+    for f in organized_files:
+        size = f.stat().st_size
+        lines.append(f"  - {f.name} ({size // 1024}KB)")
+
+    return ToolResult(
+        title="📁 缓存状态",
+        output="\n".join(lines),
+        metadata={
+            "files": [f.name for f in buffer_files],
+            "total": {
+                "tools": total_tools,
+                "resources": total_resources,
+                "knowledge": total_knowledge
+            }
+        }
+    )

+ 188 - 12
knowhub/kb_manage_prompts.py

@@ -4,15 +4,131 @@
 包含知识库管理 prompt(供 KnowHub Server 调用)。
 """
 
-KNOWLEDGE_SEMANTIC_ROUTE_PROMPT_TEMPLATE = """你是一个知识检索专家。根据用户的当前任务需求,从下列原子知识元数据中挑选出最相关的最多 {routing_k} 个知识 ID。
-任务需求:"{query_text}"
+# ===== 去重相关 =====
 
-可选知识列表:
-{routing_data}
+DEDUP_RELATION_PROMPT = """你是知识库管理专家。请判断【新知识】与【相似知识列表】中每条知识的关系。
 
-请直接输出 ID 列表,用逗号分隔(例如: knowledge-20260302-001, research-20260302-002)。若无相关项请输出 "None"。
+【新知识】
+Task: {new_task}
+Content: {new_content}
+
+【相似知识列表】(向量召回 top-10,按相似度排序)
+{existing_list}
+格式: [序号] ID: xxx | Task: xxx | Content: xxx
+
+【关系类型定义】
+- duplicate: task 和 content 语义完全相同,无新增信息 → 新知识应 rejected
+- subset: task语义一致,新知识的content信息完全被某条已有知识覆盖 → 新知识应 rejected
+- superset: task语义一致,新知识包含某条已有知识的全部信息,且有额外内容 → 新知识应 approved
+- conflict: 同一 task 下给出相互矛盾的结论 → 新知识应 approved
+- complement: 描述同一 task 的不同方面,互补 → 新知识应 approved
+- none: task 语义不同,或无实质关系 → 新知识应 approved,不写入 relations
+
+【判断步骤】
+第一步:逐条比较新知识的 task 与列表中每条知识的 task 语义是否一致。
+- task 语义一致 = 两者描述的是同一个问题或目标(即使措辞不同)
+- task 语义不同 = 描述的是不同的问题、不同的工具、不同的场景
+- 如果 task 语义不同,该条关系直接判定为 none,**不再看 content**
+- 只有 task 语义一致时,才进入第二步比较 content
+
+第二步:对 task 语义一致的知识,比较 content,判断具体关系类型(duplicate/subset/superset/conflict/complement)。
+
+**规则**:
+1. 如果以上类型无法准确描述,可自定义关系类型(英文小写下划线),并自行决定 approved/rejected
+2. final_decision 为 rejected 时,relations 中必须至少有一条关系说明拒绝原因(type 不能为 none)
+
+【输出格式】(严格 JSON,不要其他内容)
+
+示例1 - 无关知识(task 不同):
+{{
+  "final_decision": "approved",
+  "relations": []
+}}
+
+示例2 - 重复知识:
+{{
+  "final_decision": "rejected",
+  "relations": [
+    {{
+      "old_id": "knowledge-xxx",
+      "type": "duplicate",
+      "reverse_type": "duplicate"
+    }}
+  ]
+}}
+
+示例3 - 互补知识:
+{{
+  "final_decision": "approved",
+  "relations": [
+    {{
+      "old_id": "knowledge-xxx",
+      "type": "complement",
+      "reverse_type": "complement"
+    }}
+  ]
+}}
+
+"""
+
+
+# ===== 工具分析相关 =====
+
+TOOL_ANALYSIS_PROMPT = """\
+分析以下知识条目,判断是否涉及"图像创作或解构任务中使用的工具"。
+
+工具范畴(包括但不限于):
+- AI 生图平台/模型:Midjourney、Stable Diffusion、DALL-E、Flux、ComfyUI
+- SD 插件/节点:ControlNet、IP-Adapter、InstantID、DWPose、DSINE
+- 图像处理库:rembg、PIL/Pillow、OpenCV、scikit-image
+- LoRA/checkpoint 模型、ComfyUI 自定义节点、AI 绘图辅助工具
+
+知识条目:
+task: {task}
+content: {content}
+
+要求:
+- 如果涉及上述工具,提取每个工具的信息并以 JSON 格式返回。
+- 如果不涉及任何工具,返回 {{"has_tools": false}}。
+- 只输出 JSON,不要输出其他内容。
+
+输出格式:
+{{
+  "has_tools": true,
+  "tools": [
+    {{
+      "name": "工具名称(原名)",
+      "slug": "小写英文短名,空格换下划线,如 controlnet、ip_adapter",
+      "category": "image_gen | image_process | model | plugin | workflow | other",
+      "version": "版本号或 null",
+      "description": "一句话功能介绍",
+      "usage": "核心用法",
+      "scenarios": ["应用场景1", "应用场景2"],
+      "input": "输入类型描述或 null",
+      "output": "输出类型描述或 null",
+      "source": "来源/文档链接或 null",
+      "status": "未接入"
+    }}
+  ]
+}}
 """
 
+
+# ===== 检索重排序相关 =====
+
+RERANK_PROMPT_TEMPLATE = """你是知识检索专家。根据用户查询,从候选知识中选出最相关的 {top_k} 条。
+
+用户查询:"{query}"
+
+候选知识:
+{candidates_text}
+
+请输出最相关的 {top_k} 个知识 ID,按相关性从高到低排序,用逗号分隔。
+只输出 ID,不要其他内容。"""
+
+
+# ===== 知识进化相关 =====
+
 KNOWLEDGE_EVOLVE_PROMPT_TEMPLATE = """你是一个 AI Agent 知识库管理员。请根据反馈建议,对现有的知识内容进行重写进化。
 
 【原知识内容】:
@@ -28,13 +144,15 @@ KNOWLEDGE_EVOLVE_PROMPT_TEMPLATE = """你是一个 AI Agent 知识库管理员
 4. 禁止:严禁输出任何开场白、解释语或额外的 Markdown 标题,直接返回重写后的正文。
 """
 
+
+# ===== 知识瘦身相关 =====
+
 KNOWLEDGE_SLIM_PROMPT_TEMPLATE = """你是一个 AI Agent 知识库管理员。以下是当前知识库的全部条目,请执行瘦身操作:
 
 【任务】:
 1. 识别语义高度相似或重复的知识,将它们合并为一条更精炼、更通用的知识。
-2. 合并时保留 helpful 最高的那条的 ID 和 metricsmetrics 中 helpful/harmful 取各条之和)。
+2. 合并时保留 helpful 最高的那条的 ID(helpful 取各条之和)。
 3. 对于独立的、无重复的知识,保持原样不动。
-4. 保持原有的知识结构和格式。
 
 【当前知识库】:
 {entries_text}
@@ -42,10 +160,68 @@ KNOWLEDGE_SLIM_PROMPT_TEMPLATE = """你是一个 AI Agent 知识库管理员。
 【输出格式要求】:
 严格按以下格式输出每条知识,条目之间用 === 分隔:
 ID: <保留的id>
-TAGS: <yaml格式的tags>
-METRICS: <yaml格式的metrics>
+TYPES: <逗号分隔的type列表>
+HELPFUL: <合并后的helpful计数>
+HARMFUL: <合并后的harmful计数>
 SCORE: <评分>
-SCENARIO: <场景描述>
-CONTENT: <知识内容>
+TASK: <任务描述>
+CONTENT: <合并后的知识内容>
 ===
-"""
+
+最后输出合并报告:
+REPORT: 原有 X 条,合并后 Y 条,精简了 Z 条。
+
+禁止输出任何开场白或解释。"""
+
+
+# ===== 消息提取相关 =====
+
+MESSAGE_EXTRACT_PROMPT_TEMPLATE = """你是一个知识提取专家。请从以下 Agent 对话历史中提取有价值的知识。
+
+【对话历史】:
+{messages_text}
+
+【提取要求】:
+1. 识别对话中的关键知识点(工具使用经验、问题解决方案、最佳实践、踩坑经验等)
+2. 每条知识必须包含:
+   - task: 任务场景描述(在什么情况下,要完成什么目标)
+   - content: 核心知识内容(具体可操作的方法、注意事项)
+   - types: 知识类型(从 strategy/tool/user_profile/usecase/definition/plan 中选择)
+   - score: 评分 1-5(根据知识的价值和可操作性)
+3. 只提取有实际价值的知识,不要提取泛泛而谈的内容,一次就成功或比较简单的经验就不要记录了。
+4. 如果没有值得提取的知识,返回空列表
+
+【输出格式】:
+严格按以下 JSON 格式输出,每条知识之间用逗号分隔:
+[
+  {{
+    "task": "任务场景描述",
+    "content": "核心知识内容",
+    "types": ["strategy"],
+    "score": 4
+  }},
+  {{
+    "task": "另一个任务场景",
+    "content": "另一个知识内容",
+    "types": ["tool"],
+    "score": 5
+  }}
+]
+
+如果没有知识,输出: []
+
+**注意**:只记录经过多次尝试、或经过用户指导才成功的知识,一次就成功或比较简单的经验就不要记录了。
+
+禁止输出任何解释或额外文本,只输出 JSON 数组。"""
+
+
+# ===== 语义路由相关(原有的,保留)=====
+
+KNOWLEDGE_SEMANTIC_ROUTE_PROMPT_TEMPLATE = """你是一个知识检索专家。根据用户的当前任务需求,从下列原子知识元数据中挑选出最相关的最多 {routing_k} 个知识 ID。
+任务需求:"{query_text}"
+
+可选知识列表:
+{routing_data}
+
+请直接输出 ID 列表,用逗号分隔(例如: knowledge-20260302-001, research-20260302-002)。若无相关项请输出 "None"。
+"""

+ 196 - 96
knowhub/knowhub_db/README.md

@@ -1,113 +1,199 @@
-# knowhub/knowhub_db 模块总结
+# knowhub/knowhub_db 数据库设计文档
 
 ## 概述
 
-`knowhub_db` 是 KnowHub 系统的 **PostgreSQL 数据库访问层**,从早期的 Milvus(向量数据库)迁移而来,现使用远程 PostgreSQL + **fastann/pgvector** 扩展同时支持向量检索和关系型查询。
+`knowhub_db` 是 KnowHub 系统的 **PostgreSQL 数据库访问层**,使用远程 PostgreSQL + **fastann/pgvector** 扩展同时支持向量检索和关系型查询。
+
+系统围绕四张核心表构建,以 **原子能力(atomic_capability)** 为中心,连接知识、工具和需求:
+
+```
+                     knowledge(知识表)
+                    ╱         ╲
+        support_capability    tools
+               ╱                ╲
+  atomic_capability  ←────→  tool_table
+    (原子能力表)              (工具表)
+         |                       |
+    requirements            capabilities
+         ↓                       ↓
+  requirement_table ←──────→ atomic_capability
+     (需求表)
+```
 
 ---
 
 ## 数据库表结构
 
-> 当前数据量:knowledge 387 条,resources 70 条,tool_table 163 条,skill_table / requirement_table 暂无数据。
+### 1. `atomic_capability` — 原子能力表(新增)
+
+原子能力是系统的核心概念,从知识中提炼而来,连接工具实现与需求匹配。
 
-### 1. `knowledge` — 核心知识条目表(387 rows)
+索引:`atomic_capability_pkey (id)`
+
+| 字段 | 类型 | 说明 | 示例值 |
+|------|------|------|--------|
+| `id` | VARCHAR | 主键 | `"ac-image-gen-001"` |
+| `name` | VARCHAR | 能力名称 | `"文生图"` |
+| `criterion` | TEXT | 评估标准,用于判断该能力是否达成 | `"能根据文本提示生成符合描述的图像"` |
+| `description` | TEXT | 能力描述 | `"通过自然语言描述生成对应视觉内容的能力"` |
+| `requirements` | JSONB | 关联的需求 ID 列表 | `["req-001", "req-002"]` |
+| `implements` | JSONB | 工具实现映射,key 为 tool_name,value 为描述(使用方式、效果、局限) | `{"Midjourney": "通过 /imagine 命令生成图像,效果优秀但不支持中文提示词"}` |
+| `tools` | JSONB | 关联的工具 ID 列表 | `["tools/image_gen/midjourney"]` |
+| `source_knowledge` | JSONB | 该能力提炼自哪些知识条目 | `["knowledge-20260330-030758-8611"]` |
+| `embedding` | float4[] | 1536 维向量,对 name + description 做 embedding | — |
+
+**关联关系:**
+- `tools` → `tool_table.id`
+- `implements` 的 key 为 `tool_table.name`
+- `requirements` → `requirement_table.id`
+- `source_knowledge` → `knowledge.id`
+
+---
+
+### 2. `tool_table` — 工具表(变更)
+
+> 相对原表的变更:新增 `capabilities` 字段;知识关联字段重命名为 `tool_knowledge`、`case_knowledge`、`process_knowledge`。
+
+索引:`tool_table_pkey (id)`
+
+| 字段 | 类型 | 说明 | 示例值 |
+|------|------|------|--------|
+| `id` | VARCHAR | 主键,路径格式 | `"tools/image_gen/midjourney"` |
+| `name` | VARCHAR | 工具名称 | `"Midjourney"` |
+| `version` | VARCHAR | 版本号 | `"v5.2"` |
+| `introduction` | TEXT | 功能介绍 | `"AI 图像生成工具,支持文生图、图生图"` |
+| `tutorial` | TEXT | 使用指南 | `"通过 Discord 输入 /imagine 命令..."` |
+| `input` | JSONB | 输入规格 | `"文本提示、参数配置"` |
+| `output` | JSONB | 输出规格 | `"生成图像"` |
+| `updated_time` | BIGINT | 更新时间戳(秒) | `1774570091` |
+| `status` | VARCHAR | 接入状态 | `"未接入"` / `"可用"` / `"异常"` |
+| `capabilities` | JSONB | 关联的原子能力 ID 列表 | `["ac-image-gen-001", "ac-image-edit-002"]` |
+| `tool_knowledge` | JSONB | 关联的工具知识 ID 列表 | `["knowledge-20260327-150915-44a4"]` |
+| `case_knowledge` | JSONB | 关联的用例知识 ID 列表 | `[]` |
+| `process_knowledge` | JSONB | 关联的工序知识 ID 列表 | `[]` |
+| `embedding` | float4[] | 1536 维向量,对 name + introduction 做 embedding | — |
+
+**关联关系:**
+- `capabilities` → `atomic_capability.id`
+- `tool_knowledge` / `case_knowledge` / `process_knowledge` → `knowledge.id`
+
+**相对原表变更说明:**
+- 新增 `capabilities` 字段(JSONB 数组)
+- 新增 `embedding` 字段(float4[],对 name + introduction 做 embedding)
+- 原 `knowledge` 字段重命名为 `tool_knowledge`
+
+---
+
+### 3. `knowledge` — 知识表(变更)
+
+> 相对原表的变更:新增 `support_capability`、`tools` 字段;原 `embedding` 拆分为 `task_embedding` + `content_embedding` 双向量;`types` 扩展为六种分类。其余原有字段全部保留。
 
 索引:`knowledge_pkey (id)`、`idx_knowledge_owner`、`idx_knowledge_status`
 
 | 字段 | 类型 | 说明 | 示例值 |
 |------|------|------|--------|
-| `id` | VARCHAR | 主键,格式 `knowledge-{日期}-{时间}-{hash}` | `knowledge-20260330-030758-8611` |
-| `embedding` | ARRAY (float4[]) | 1536 维向量,用于语义检索 | — |
-| `message_id` | VARCHAR | 来源消息 ID | `""` |
-| `task` | VARCHAR | 产生该知识时的任务描述 | `"在调研 AI 工具功能特性时..."` |
-| `content` | TEXT | 知识正文 | `"当调研新兴 AI 工具时,应采用多渠道策略..."` |
-| `types` | ARRAY | 知识类型标签 | `['strategy']`、`['tool', 'strategy']` |
-| `tags` | JSONB | 标签键值对 | `{"tool": true, "intent": "信息调研", "project": "feature_extract"}` |
+| `id` | VARCHAR | 主键 | `"knowledge-20260330-030758-8611"` |
+| `task_embedding` | float4[] | 1536 维向量,对 task 做 embedding | — |
+| `content_embedding` | float4[] | 1536 维向量,对 content 做 embedding | — |
+| `message_id` | VARCHAR | 来源的 agent 任务消息 ID | `""` |
+| `task` | VARCHAR | 完成什么任务 | `"调研 AI 图像生成工具的功能特性"` |
+| `content` | TEXT | 知识正文 | `"当调研新兴 AI 工具时..."` |
+| `types` | ARRAY | 知识类型(多选):工序、用例、工具、经验、定义、User Profile | `['工具', '经验']` |
+| `tags` | JSONB | 标签键值对 | `{"intent": "信息调研", "project": "feature_extract"}` |
 | `tag_keys` | ARRAY | tags 中的键列表(用于过滤) | `['intent', 'state', 'project']` |
 | `scopes` | ARRAY | 作用域(组织/用户) | `['org:cybertogether']` |
-| `owner` | VARCHAR | 所有者(agent_id 或 email) | `"srt_feature_extract_1"` |
+| `owner` | VARCHAR | 所有者 | `"srt_feature_extract_1"` |
 | `resource_ids` | ARRAY | 关联的 resource ID 列表 | `['tools/image_gen/midjourney']` |
-| `source` | JSONB | 来源信息 | `{"agent_id": "research_agent", "category": "exp", "timestamp": "..."}` |
+| `source` | JSONB | 来源信息 | `{"agent_id": "research_agent", "category": "exp"}` |
 | `eval` | JSONB | 评估信息 | `{"score": 5, "harmful": 0, "helpful": 1, "confidence": 0.5}` |
 | `created_at` | BIGINT | 创建时间戳(秒) | `1774811278` |
 | `updated_at` | BIGINT | 更新时间戳(秒) | `1774943256` |
 | `status` | VARCHAR | 状态 | `"approved"` |
 | `relationships` | JSONB | 关联关系列表 | `[{"type": "complement", "target": "knowledge-..."}]` |
+| `support_capability` | JSONB | 支撑的原子能力 ID 列表(可多个) | `["ac-image-gen-001", "ac-style-transfer-003"]` |
+| `tools` | JSONB | 关联的工具 ID 列表 | `["tools/image_gen/midjourney"]` |
+
+**关联关系:**
+- `support_capability` → `atomic_capability.id`
+- `tools` → `tool_table.id`
+
+**相对原表变更说明:**
+- 新增字段:`support_capability`(JSONB)、`tools`(JSONB)
+- 原 `embedding`(float4[])拆分为 `task_embedding` + `content_embedding` 两个向量字段
+- `types` 可选值更新为:工序、用例、工具、经验、定义、User Profile
+- 所有原有字段保留不变
+
+---
+
+### 4. `requirement_table` — 需求表(变更)
+
+> 相对原表的变更:大幅简化,以原子能力 pattern 为核心,替代原来的 source_items/tools/trace/embedding 等字段。
 
-### 2. `resources` — 文档资源表(70 rows)
+索引:`requirement_table_pkey (id)`
+
+| 字段 | 类型 | 说明 | 示例值 |
+|------|------|------|--------|
+| `id` | VARCHAR | 主键 | `"req-001"` |
+| `description` | TEXT | 需求描述 | `"能够根据用户文本描述生成高质量图像"` |
+| `atomics` | JSONB | 关联的原子能力 ID 列表 | `["ac-image-gen-001", "ac-style-transfer-003"]` |
+| `source_nodes` | JSONB | 来源于内容树的节点,对象数组 | `[{"node_name": "image_gen", "posts": ["post-001", "post-002"]}]` |
+| `status` | VARCHAR | 满足状态 | `"已满足"` / `"未满足"` |
+| `match_result` | TEXT | 满足时描述如何满足;不足时描述缺什么,提供调研方向 | `"通过 Midjourney + ComfyUI 组合可满足,但中文提示词支持不足,需调研替代方案"` |
+| `embedding` | float4[] | 1536 维向量,对 description 做 embedding | — |
+
+**关联关系:**
+- `atomics` → `atomic_capability.id`
+
+**相对原表变更说明:**
+- 删除字段:`task`、`type`、`source_type`、`source_itemset_id`、`source_items`、`tools`、`knowledge`、`case_knowledge`、`process_knowledge`、`trace`、`body`、`embedding`
+- 新增字段:`description`(TEXT)、`atomics`(JSONB)、`source_nodes`(JSONB)、`status`(VARCHAR)、`match_result`(TEXT)
+
+---
+
+### 5. `resources` — 文档资源表(未变更)
 
 索引:`resources_pkey (id)`
 
 | 字段 | 类型 | 说明 | 示例值 |
 |------|------|------|--------|
-| `id` | TEXT | 主键,`/` 分隔路径格式 | `"references/python_type_hints"`、`"code/web-crawler/baidu_hot_search"` |
+| `id` | TEXT | 主键,`/` 分隔路径格式 | `"references/python_type_hints"` |
 | `title` | TEXT | 标题 | `"Python Type Hints 权威资源列表"` |
 | `body` | TEXT | 公开内容(Markdown 或代码) | `"Python 类型提示调研的三个核心权威资源..."` |
 | `secure_body` | TEXT | 私有/加密内容 | `""` |
 | `content_type` | TEXT | 内容类型 | `"text"` |
-| `metadata` | JSONB | 附加元数据,含 `knowledge_ids` 引用 | `{"topic": "type_hints", "category": "python", "verified_at": "2026-03-24"}` |
+| `metadata` | JSONB | 附加元数据 | `{"topic": "type_hints", "category": "python"}` |
 | `sort_order` | INTEGER | 排序权重 | `0` |
 | `submitted_by` | TEXT | 提交者 | `""` |
 | `created_at` | BIGINT | 创建时间戳(秒) | `1774259301` |
 | `updated_at` | BIGINT | 更新时间戳(秒) | `1774259301` |
 
-### 3. `tool_table` — 工具表(163 rows)
+---
 
-索引:`tool_table_pkey (id)`
+## 表间关联关系总览
 
-| 字段 | 类型 | 说明 | 示例值 |
-|------|------|------|--------|
-| `id` | VARCHAR | 主键,路径格式 | `"tools/image_gen/webui"`、`"tools/plugin/ksampler"` |
-| `name` | VARCHAR | 工具名称 | `"WebUI"`、`"KSampler"` |
-| `version` | VARCHAR | 版本号 | `null` |
-| `introduction` | TEXT | 简介 | `"传统的 Stable Diffusion 网页用户界面"` |
-| `tutorial` | TEXT | 使用教程 | `"通过表单参数配置进行图像生成"` |
-| `input` | JSONB | 输入规格 | `"文本提示、参数配置"` |
-| `output` | JSONB | 输出规格 | `"生成图像"` |
-| `updated_time` | BIGINT | 更新时间戳(秒) | `1774570091` |
-| `status` | VARCHAR | 接入状态 | `"未接入"` / `"可用"` / `"异常"` |
-| `knowledge` | JSONB | 关联通用知识 ID 列表 | `["knowledge-20260327-150915-44a4"]` |
-| `case_knowledge` | JSONB | 关联用例知识 ID 列表 | `[]` |
-| `process_knowledge` | JSONB | 关联工序知识 ID 列表 | `[]` |
+所有关联均为 **应用层软关联**(JSONB 字段存储 ID 列表/映射),不使用数据库外键约束。
 
-### 4. `skill_table` — 技能表(0 rows)
+```
+atomic_capability.tools[]             ──→  tool_table.id
+atomic_capability.implements{key}     ──→  tool_table.name
+atomic_capability.requirements[]      ──→  requirement_table.id
+atomic_capability.source_knowledge[]  ──→  knowledge.id
 
-索引:`skill_table_pkey (id)`
+tool_table.capabilities[]          ──→  atomic_capability.id
+tool_table.tool_knowledge[]        ──→  knowledge.id
+tool_table.case_knowledge[]        ──→  knowledge.id
+tool_table.process_knowledge[]     ──→  knowledge.id
 
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `id` | VARCHAR | 主键 |
-| `name` | VARCHAR | 技能名称 |
-| `version` | VARCHAR | 版本号 |
-| `introduction` | TEXT | 简介 |
-| `input` | JSONB | 输入规格 |
-| `output` | JSONB | 输出规格 |
-| `updated_time` | BIGINT | 更新时间戳 |
-| `resource_id` | VARCHAR | 关联的 tool_table.id |
-| `knowledge` | JSONB | 关联通用知识 ID 列表 |
-| `case_knowledge` | JSONB | 关联用例知识 ID 列表 |
-| `process_knowledge` | JSONB | 关联工序知识 ID 列表 |
-| `status` | VARCHAR | 状态:`"未验证"` / `"可用"` / `"异常"` |
-
-### 5. `requirement_table` — 需求表(0 rows)
-
-索引:`requirement_table_pkey (id)`、`idx_requirement_embedding`(fastann ANN,dim=1536,hnsw_m=16)
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `id` | VARCHAR | 主键 |
-| `task` | TEXT | 任务需求描述 |
-| `type` | VARCHAR | 需求类型(如 `"制作"`) |
-| `source_type` | VARCHAR | 来源类型(如 `"itemset"`) |
-| `source_itemset_id` | VARCHAR | 关联 pattern 的 ID |
-| `source_items` | JSONB | 包含的特征/分类列表 |
-| `tools` | JSONB | 关联工具 ID 与简介 |
-| `knowledge` | JSONB | 关联通用知识 ID 列表 |
-| `case_knowledge` | JSONB | 关联用例知识 ID 列表 |
-| `process_knowledge` | JSONB | 关联工序知识 ID 列表 |
-| `trace` | JSONB | 关联的任务执行 log |
-| `body` | TEXT | 需求正文(str/json) |
-| `embedding` | ARRAY (float4[]) | 1536 维向量,用 fastann ANN 索引检索 |
+knowledge.support_capability[]     ──→  atomic_capability.id
+knowledge.tools[]                  ──→  tool_table.id
+
+requirement_table.atomics[]        ──→  atomic_capability.id
+```
+
+**双向关联说明:**
+- `atomic_capability` ↔ `tool_table`:能力的 `implements` 和工具的 `capabilities` 互为反向索引
+- `atomic_capability` ↔ `knowledge`:能力的 `source_knowledge` 和知识的 `support_capability` 互为反向索引
+- `atomic_capability` ↔ `requirement_table`:能力的 `requirements` 和需求的 `atomics` 互为反向索引
 
 ---
 
@@ -119,34 +205,36 @@
 | 方法 | 功能 |
 |------|------|
 | `insert(knowledge)` | 插入单条知识 |
-| `insert_batch(list)` | 批量插入(用 `execute_batch`) |
-| `search(embedding, filters, limit)` | **向量相似度检索**(`<=>` 余弦距离) |
+| `insert_batch(list)` | 批量插入 |
+| `search(embedding, filters, limit)` | 向量相似度检索 |
 | `query(filters, limit)` | 纯标量过滤查询 |
-| `get_by_id(id, include_embedding)` | 按 ID 查询(默认不返回向量以提升性能) |
+| `get_by_id(id)` | 按 ID 查询 |
 | `update(id, updates)` | 更新字段 |
 | `delete(id)` | 删除 |
 | `count()` | 统计总数 |
-| `_build_where_clause(filters)` | 将 Milvus 风格过滤语法转换为 PostgreSQL WHERE 子句 |
-
-**过滤语法转换示例:**
-```python
-# 输入(Milvus 风格)
-'array_contains(types, "insight") and eval["score"] == 5'
-# 转换后(PostgreSQL)
-"WHERE types @> ARRAY['insight'] AND (eval->>'score')::int = 5"
-```
 
 ### `PostgreSQLResourceStore` (`pg_resource_store.py`)
 资源文档的 CRUD + 导航:
 
 | 方法 | 功能 |
 |------|------|
-| `insert_or_update(resource)` | 插入或更新(`ON CONFLICT DO UPDATE`) |
+| `insert_or_update(resource)` | 插入或更新 |
 | `get_by_id(id)` | 按 ID 查询 |
-| `list_resources(prefix, content_type, limit, offset)` | 列表查询,支持路径前缀过滤 |
+| `list_resources(prefix, content_type, limit, offset)` | 列表查询 |
 | `update(id, updates)` | 更新 |
 | `delete(id)` | 删除 |
-| `get_siblings(id)` | **获取前后同级节点**(用于文档导航) |
+| `get_siblings(id)` | 获取前后同级节点 |
+
+### `PostgreSQLRequirementStore` (`pg_requirement_store.py`)
+需求的 CRUD + 向量检索:
+
+| 方法 | 功能 |
+|------|------|
+| `insert_or_update(requirement)` | 插入或更新 |
+| `get_by_id(id)` | 按 ID 查询 |
+| `search(embedding, limit)` | 向量检索 |
+| `list_all(limit)` | 列出所有需求 |
+| `count()` | 统计总数 |
 
 ---
 
@@ -154,15 +242,15 @@
 
 | 脚本 | 功能 |
 |------|------|
-| `create_tables.py` | 创建 `tool_table`、`skill_table`、`requirement_table` 三张表及 fastann 索引 |
-| `migrate_resources.py` | 从 SQLite 迁移 `text` 类型资源到 PostgreSQL |
-| `migrate_tools.py` | 从 SQLite 迁移 `tool` 类型数据到 `tool_table` |
-| `clean_invalid_knowledge_refs.py` | 清理 `tool_table.knowledge` 中失效的 knowledge ID 引用 |
-| `clean_resource_knowledge_refs.py` | 清理 `resources.metadata.knowledge_ids` 中失效的引用 |
-| `test_pg_connection.py` | 测试数据库连接,检查版本、pgvector 扩展及表列表 |
-| `check_extensions.py` | 查看 PostgreSQL 可用及已安装的向量扩展 |
-| `check_fastann.py` | 查看 fastann 相关函数和操作符 |
-| `list_databases.py` | 列出主机上所有数据库 |
+| `create_tables.py` | 创建/变更数据库表结构 |
+| `migrate_resources.py` | 从 SQLite 迁移资源到 PostgreSQL |
+| `migrate_tools.py` | 从 SQLite 迁移工具数据 |
+| `clean_invalid_knowledge_refs.py` | 清理失效的 knowledge ID 引用 |
+| `clean_resource_knowledge_refs.py` | 清理 resources 中失效的引用 |
+| `test_pg_connection.py` | 测试数据库连接 |
+| `check_extensions.py` | 查看向量扩展 |
+| `check_fastann.py` | 查看 fastann 函数和操作符 |
+| `list_databases.py` | 列出所有数据库 |
 
 ---
 
@@ -180,7 +268,19 @@ KNOWHUB_DB_NAME   # 数据库名
 
 ## 架构特点
 
-1. **向量检索**:`knowledge` 和 `requirement_table` 均有 `float4[]` 向量字段,前者用 `<=>` 运算符(pgvector),后者用 fastann `ANN` 索引,支持 HNSW 算法(`hnsw_m=16`)。
-2. **兼容层**:`_build_where_clause` 保留了 Milvus 过滤语法兼容,方便旧代码平滑迁移。
-3. **三层知识关联**:工具/技能/需求均挂载 `knowledge`(通用)、`case_knowledge`(用例)、`process_knowledge`(工序)三类引用,形成细粒度的知识图谱。
-4. **资源路径 ID**:`resources.id` 支持 `/` 分隔路径(如 `tools/search/api`),`get_siblings` 利用字典序实现层级导航。
+1. **原子能力为核心**:`atomic_capability` 作为知识、工具、需求三者之间的桥梁,实现能力的提炼、实现映射和需求匹配。
+2. **全表向量检索**:四张核心表均支持 1536 维 embedding,其中 knowledge 表有双向量(`task_embedding` + `content_embedding`)分别支持任务语义和内容语义检索。
+3. **应用层软关联**:所有表间关系通过 JSONB 字段存储 ID 列表/映射,保持灵活性,需应用层维护一致性。
+4. **双向索引**:核心关联关系均有正反两个方向的索引,方便从任意一端查询关联数据。
+5. **三类工具知识**:工具关联的知识分为工具知识(tool_knowledge)、工序知识(process_knowledge)、用例知识(case_knowledge)三类,形成细粒度的知识图谱。
+6. **资源路径 ID**:`resources.id` 和 `tool_table.id` 均支持 `/` 分隔路径格式,便于层级组织和导航。
+
+### Embedding 策略
+
+| 表 | 向量字段 | Embedding 内容 |
+|------|------|------|
+| `atomic_capability` | `embedding` | name + description |
+| `tool_table` | `embedding` | name + introduction |
+| `knowledge` | `task_embedding` | task |
+| `knowledge` | `content_embedding` | content |
+| `requirement_table` | `embedding` | description |

+ 80 - 0
knowhub/knowhub_db/check_table_structure.py

@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+"""
+检查四张核心表的结构和数据量(knowledge 表只查结构,不查数据量避免卡住)
+"""
+import os, psycopg2
+from psycopg2.extras import RealDictCursor
+from dotenv import load_dotenv
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+_root = os.path.normpath(os.path.join(_dir, '..', '..'))
+load_dotenv(os.path.join(_root, '.env'))
+
+conn = psycopg2.connect(
+    host=os.getenv('KNOWHUB_DB'),
+    port=int(os.getenv('KNOWHUB_PORT', 5432)),
+    user=os.getenv('KNOWHUB_USER'),
+    password=os.getenv('KNOWHUB_PASSWORD'),
+    database=os.getenv('KNOWHUB_DB_NAME'),
+    connect_timeout=10
+)
+cur = conn.cursor(cursor_factory=RealDictCursor)
+
+print("Connected.\n")
+
+def get_columns(table):
+    """用 pg_attribute 查列,比 information_schema 快得多"""
+    cur.execute("""
+        SELECT a.attname AS col, pg_catalog.format_type(a.atttypid, a.atttypmod) AS dtype
+        FROM pg_catalog.pg_attribute a
+        JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+        JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+        WHERE c.relname = %s AND n.nspname = 'public'
+          AND a.attnum > 0 AND NOT a.attisdropped
+        ORDER BY a.attnum
+    """, (table,))
+    return cur.fetchall()
+
+def get_count(table):
+    cur.execute(f"SELECT COUNT(*) as cnt FROM {table}")
+    return cur.fetchone()['cnt']
+
+# ── 查结构 ──
+
+tables = ['atomic_capability', 'tool_table', 'requirement_table', 'knowledge']
+
+for t in tables:
+    cols = get_columns(t)
+
+    # knowledge 表可能很慢,只查结构不查行数
+    if t == 'knowledge':
+        print(f"\n=== {t} (skipped count) ===")
+    else:
+        cnt = get_count(t)
+        print(f"\n=== {t} ({cnt} rows) ===")
+
+    for c in cols:
+        print(f"  {c['col']:25s} {c['dtype']}")
+
+# ── 抽样 ──
+
+print("\n\n=== SAMPLE DATA ===")
+
+print("\natomic_capability (first 3):")
+cur.execute("SELECT id, name FROM atomic_capability LIMIT 3")
+for r in cur.fetchall():
+    print(f"  {r['id']}: {r['name']}")
+
+print("\nrequirement_table (first 3):")
+cur.execute("SELECT id, status FROM requirement_table LIMIT 3")
+for r in cur.fetchall():
+    print(f"  {r['id']}: {r.get('status', '?')}")
+
+print("\ntool_table (first 3):")
+cur.execute("SELECT id, name FROM tool_table LIMIT 3")
+for r in cur.fetchall():
+    print(f"  {r['id']}: {r['name']}")
+
+cur.close()
+conn.close()
+print("\nDone.")

+ 84 - 0
knowhub/knowhub_db/clean_tool_research_data.py

@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+清理之前导入的 tool_research 数据
+"""
+import sys
+from pathlib import Path
+
+# 设置 Windows 控制台编码
+if sys.platform == 'win32':
+    import codecs
+    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
+    sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
+
+# 添加父目录到路径
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+# 加载环境变量
+from dotenv import load_dotenv
+project_root = Path(__file__).parent.parent.parent
+env_path = project_root / '.env'
+load_dotenv(env_path)
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.knowhub_db.pg_requirement_store import PostgreSQLRequirementStore
+
+
+def clean_requirements(req_store: PostgreSQLRequirementStore):
+    """清理需求表中的 tool_research 数据"""
+    print("\n=== 清理需求表 ===")
+
+    cursor = req_store._get_cursor()
+    try:
+        # 删除 midjourney 和 seedream 的需求
+        cursor.execute("""
+            DELETE FROM requirement_table
+            WHERE id LIKE 'req_midjourney_%' OR id LIKE 'req_seedream_%'
+        """)
+        deleted_count = cursor.rowcount
+        req_store.conn.commit()
+        print(f"删除了 {deleted_count} 条需求记录")
+    except Exception as e:
+        print(f"清理需求失败: {e}")
+        req_store.conn.rollback()
+    finally:
+        cursor.close()
+
+
+def clean_cases(knowledge_store: PostgreSQLStore):
+    """清理知识表中的 case 数据"""
+    print("\n=== 清理知识表中的 cases ===")
+
+    cursor = knowledge_store._get_cursor()
+    try:
+        # 删除 tool_research_agent 创建的 case 类型知识
+        cursor.execute("""
+            DELETE FROM knowledge
+            WHERE owner = 'tool_research_agent'
+            AND 'case' = ANY(types)
+        """)
+        deleted_count = cursor.rowcount
+        knowledge_store.conn.commit()
+        print(f"删除了 {deleted_count} 条 case 知识记录")
+    except Exception as e:
+        print(f"清理 cases 失败: {e}")
+        knowledge_store.conn.rollback()
+    finally:
+        cursor.close()
+
+
+def main():
+    print("开始清理之前导入的 tool_research 数据...")
+
+    knowledge_store = PostgreSQLStore()
+    req_store = PostgreSQLRequirementStore()
+
+    clean_requirements(req_store)
+    clean_cases(knowledge_store)
+
+    print("\n清理完成!")
+
+
+if __name__ == '__main__':
+    main()

+ 148 - 0
knowhub/knowhub_db/import_initial_data.py

@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+"""
+填充 atomic_capability 和 requirement_table 的初始数据
+数据来源:
+  - atomic_capabilities.md -> atomic_capability
+  - requirements_sorted.json -> requirement_table
+"""
+
+import os, sys, json
+import psycopg2
+from psycopg2.extras import RealDictCursor
+from dotenv import load_dotenv
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+_root = os.path.normpath(os.path.join(_dir, '..', '..'))
+load_dotenv(os.path.join(_root, '.env'))
+
+def get_conn():
+    return psycopg2.connect(
+        host=os.getenv('KNOWHUB_DB'),
+        port=int(os.getenv('KNOWHUB_PORT', 5432)),
+        user=os.getenv('KNOWHUB_USER'),
+        password=os.getenv('KNOWHUB_PASSWORD'),
+        database=os.getenv('KNOWHUB_DB_NAME'),
+        connect_timeout=10
+    )
+
+# ── 解析 atomic_capabilities.md ──
+
+def parse_caps(path):
+    caps = []
+    cur = None
+    with open(path, 'r', encoding='utf-8') as f:
+        for line in f:
+            line = line.rstrip()
+            if line.startswith('### CAP-'):
+                if cur:
+                    caps.append(cur)
+                parts = line.split(':', 1)
+                cap_id = parts[0].replace('### ', '').strip()
+                cap_name = parts[1].strip() if len(parts) > 1 else ''
+                cur = {'id': cap_id, 'name': cap_name, 'criterion': '', 'description': '', 'implements': {}, 'tools': [], 'source_knowledge': [], 'requirements': []}
+            elif cur:
+                if line.startswith('- **'):
+                    val = line.split(':', 1)[1].strip() if ':' in line else ''
+                    if '功能描述' in line:
+                        cur['description'] = val
+                    elif '判定标准' in line:
+                        cur['criterion'] = val
+                elif line.strip().startswith('- ComfyUI') or line.strip().startswith('- FLUX') or \
+                     line.strip().startswith('- Midjourney') or line.strip().startswith('- Nano Banana') or \
+                     line.strip().startswith('- Seedream'):
+                    t = line.strip().lstrip('- ')
+                    name = t.split(':')[0].strip()
+                    desc = t.split(':', 1)[1].strip() if ':' in t else ''
+                    cur['implements'][name] = desc
+    if cur:
+        caps.append(cur)
+    return caps
+
+# ── 解析 requirements_sorted.json ──
+
+def parse_reqs(path):
+    with open(path, 'r', encoding='utf-8') as f:
+        data = json.load(f)
+    out = []
+    for r in data.get('requirements', []):
+        nodes = [{'node_name': n, 'posts': r.get('source_posts', [])} for n in r.get('source_nodes', [])]
+        status = '已满足' if r.get('match_status') == '完全满足' else '未满足'
+        parts = []
+        if r.get('capability_combination'): parts.append(r['capability_combination'])
+        if r.get('research_note'): parts.append(r['research_note'])
+        out.append({
+            'id': r['requirement_id'],
+            'description': r['requirement_text'],
+            'atomics': r.get('matched_capabilities', []),
+            'source_nodes': nodes,
+            'status': status,
+            'match_result': '\n'.join(parts),
+        })
+    return out
+
+# ── 主流程 ──
+
+def main():
+    conn = get_conn()
+    conn.autocommit = True
+    cur = conn.cursor(cursor_factory=RealDictCursor)
+    print('Connected.')
+
+    # 1. 填充 atomic_capability
+    md = os.path.join(_root, 'examples', 'tool_research', 'atomic_capabilities.md')
+    print(f'\n--- atomic_capability ---')
+    if not os.path.exists(md):
+        print(f'File not found: {md}')
+    else:
+        caps = parse_caps(md)
+        ok = 0
+        for c in caps:
+            try:
+                cur.execute("""
+                    INSERT INTO atomic_capability (id, name, criterion, description, requirements, implements, tools, source_knowledge)
+                    VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
+                    ON CONFLICT (id) DO UPDATE SET
+                        name=EXCLUDED.name, criterion=EXCLUDED.criterion, description=EXCLUDED.description, implements=EXCLUDED.implements
+                """, (c['id'], c['name'], c['criterion'], c['description'],
+                      json.dumps(c['requirements']), json.dumps(c['implements']),
+                      json.dumps(c['tools']), json.dumps(c['source_knowledge'])))
+                ok += 1
+            except Exception as e:
+                print(f'  FAIL {c["id"]}: {e}')
+        print(f'Inserted/updated {ok}/{len(caps)} capabilities.')
+
+    # 2. 填充 requirement_table
+    jf = os.path.join(_root, 'examples', 'tool_research', 'requirements_sorted.json')
+    print(f'\n--- requirement_table ---')
+    if not os.path.exists(jf):
+        print(f'File not found: {jf}')
+    else:
+        reqs = parse_reqs(jf)
+        ok = 0
+        for r in reqs:
+            try:
+                cur.execute("""
+                    INSERT INTO requirement_table (id, description, atomics, source_nodes, status, match_result)
+                    VALUES (%s,%s,%s,%s,%s,%s)
+                    ON CONFLICT (id) DO UPDATE SET
+                        description=EXCLUDED.description, atomics=EXCLUDED.atomics,
+                        source_nodes=EXCLUDED.source_nodes, status=EXCLUDED.status, match_result=EXCLUDED.match_result
+                """, (r['id'], r['description'], json.dumps(r['atomics']),
+                      json.dumps(r['source_nodes']), r['status'], r['match_result']))
+                ok += 1
+            except Exception as e:
+                print(f'  FAIL {r["id"]}: {e}')
+        print(f'Inserted/updated {ok}/{len(reqs)} requirements.')
+
+    # 3. 验证
+    print('\n--- verify ---')
+    for t in ['atomic_capability', 'requirement_table']:
+        cur.execute(f'SELECT COUNT(*) as cnt FROM {t}')
+        print(f'{t}: {cur.fetchone()["cnt"]} rows')
+
+    cur.close()
+    conn.close()
+    print('\nDone.')
+
+if __name__ == '__main__':
+    main()

+ 307 - 0
knowhub/knowhub_db/import_tool_research_data.py

@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+从 tool_research 输出导入需求和 case 到数据库
+- 需求存入 requirement_table
+- case 存入 knowledge 表
+"""
+import json
+import os
+import sys
+import time
+import asyncio
+from pathlib import Path
+from typing import List, Dict, Any
+
+# 设置 Windows 控制台编码
+if sys.platform == 'win32':
+    import codecs
+    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
+    sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
+
+# 添加父目录到路径以导入模块
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+# 加载环境变量(从项目根目录)
+from dotenv import load_dotenv
+project_root = Path(__file__).parent.parent.parent
+env_path = project_root / '.env'
+load_dotenv(env_path)
+print(f"加载环境变量: {env_path}")
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.knowhub_db.pg_requirement_store import PostgreSQLRequirementStore
+from knowhub.embeddings import get_embedding
+
+
+async def get_embedding_with_retry(text: str, max_retries: int = 3) -> List[float]:
+    """带重试的 embedding 生成"""
+    for attempt in range(max_retries):
+        try:
+            return await get_embedding(text)
+        except Exception as e:
+            if attempt < max_retries - 1:
+                wait_time = (attempt + 1) * 2  # 2, 4, 6 秒
+                print(f"  ⚠ Embedding 生成失败,{wait_time}秒后重试... ({attempt + 1}/{max_retries})")
+                await asyncio.sleep(wait_time)
+            else:
+                raise e
+
+
+def load_json_file(file_path: str) -> Dict:
+    """加载 JSON 文件"""
+    with open(file_path, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+
+async def import_requirements(
+    req_store: PostgreSQLRequirementStore,
+    tool_name: str,
+    match_result_path: str,
+    case_id_to_knowledge_id: Dict[str, str]
+):
+    """导入需求到 requirement_table
+
+    Args:
+        case_id_to_knowledge_id: case_id 到 knowledge_id 的映射
+    """
+    print(f"\n=== 导入 {tool_name} 的需求 ===")
+
+    # 加载 match_nodes_result.json
+    data = load_json_file(match_result_path)
+    merged_demands = data.get('merged_demands', [])
+
+    print(f"找到 {len(merged_demands)} 个合并后的需求")
+
+    imported_count = 0
+    for demand in merged_demands:
+        demand_name = demand.get('demand_name', '')
+        description = demand.get('description', '')
+        source_case_ids = demand.get('source_case_ids', [])
+        mount_decision = demand.get('mount_decision', {})
+
+        print(f"\n[{imported_count + 1}/{len(merged_demands)}] 处理需求: {demand_name[:50]}...")
+
+        # 构建需求 ID
+        req_id = f"req_{tool_name}_{demand_name[:20].replace(' ', '_')}"
+        print(f"  - 需求 ID: {req_id}")
+
+        # 生成 embedding (异步调用,带重试)
+        print(f"  - 生成 embedding...")
+        embedding_text = f"{demand_name} {description}"
+        embedding = await get_embedding_with_retry(embedding_text)
+        print(f"  - Embedding 维度: {len(embedding)}")
+
+        # 提取挂载的节点信息
+        mounted_nodes = mount_decision.get('mounted_nodes', [])
+        source_items = [
+            {
+                'entity_id': node['entity_id'],
+                'name': node['name'],
+                'source_type': node['source_type']
+            }
+            for node in mounted_nodes
+        ]
+
+        # 将 case_id 转换为 knowledge_id
+        case_knowledge_ids = [
+            case_id_to_knowledge_id.get(str(case_id), str(case_id))
+            for case_id in source_case_ids
+        ]
+        print(f"  - 关联 {len(case_knowledge_ids)} 个 case")
+
+        # 构建需求记录
+        requirement = {
+            'id': req_id,
+            'task': demand_name,
+            'type': '制作',
+            'source_type': 'itemset',
+            'source_itemset_id': f"{tool_name}_demands",
+            'source_items': source_items,
+            'tools': [{'id': f'tools/image_gen/{tool_name}', 'name': tool_name}],
+            'knowledge': [],
+            'case_knowledge': case_knowledge_ids,  # 使用 knowledge_id
+            'process_knowledge': [],
+            'trace': {},
+            'body': description,
+            'embedding': embedding
+        }
+
+        try:
+            print(f"  - 插入数据库...")
+            req_store.insert_or_update(requirement)
+            imported_count += 1
+            print(f"  ✓ 成功")
+        except Exception as e:
+            print(f"  ✗ 失败: {e}")
+
+    print(f"成功导入 {imported_count}/{len(merged_demands)} 个需求\n")
+    return imported_count
+
+
+async def import_cases(
+    knowledge_store: PostgreSQLStore,
+    tool_name: str,
+    cases_json_path: str
+) -> Dict[str, str]:
+    """导入 case 到 knowledge 表
+
+    Returns:
+        case_id 到 knowledge_id 的映射字典
+    """
+    print(f"\n=== 导入 {tool_name} 的 cases ===")
+
+    # 加载 cases.json
+    data = load_json_file(cases_json_path)
+    cases = data.get('cases', [])
+
+    print(f"找到 {len(cases)} 个 cases")
+
+    imported_count = 0
+    case_id_mapping = {}  # case_id -> knowledge_id
+
+    for i, case in enumerate(cases):
+        case_id = case.get('case_id', '')
+        title = case.get('title', '')
+        source = case.get('source', '')
+        source_link = case.get('source_link', '')
+        output_description = case.get('output_description', '')
+        key_findings = case.get('key_findings', '')
+        images = case.get('images', [])
+
+        print(f"\n[{i + 1}/{len(cases)}] 处理 case: {title[:50]}...")
+        print(f"  - Case ID: {case_id}")
+
+        # 构建知识内容
+        content = f"""# {title}
+
+## 来源
+{source}
+链接: {source_link}
+
+## 输出效果
+{output_description}
+
+## 关键发现
+{key_findings}
+"""
+
+        # 生成 embedding (异步调用,带重试)
+        print(f"  - 生成 embedding...")
+        embedding_text = f"{title} {output_description} {key_findings}"
+        embedding = await get_embedding_with_retry(embedding_text)
+        print(f"  - Embedding 维度: {len(embedding)}")
+
+        # 生成知识 ID
+        timestamp = int(time.time())
+        knowledge_id = f"knowledge-case-{tool_name}-{case_id}"
+        print(f"  - Knowledge ID: {knowledge_id}")
+
+        # 构建知识记录
+        knowledge = {
+            'id': knowledge_id,
+            'embedding': embedding,
+            'message_id': '',
+            'task': title,  # 使用帖子标题作为 task
+            'content': content,
+            'types': ['case', 'tool_usage'],
+            'tags': {
+                'tool': tool_name,
+                'case_id': str(case_id),
+                'source': source,
+                'has_images': len(images) > 0
+            },
+            'tag_keys': ['tool', 'case_id', 'source', 'has_images'],
+            'scopes': ['org:cybertogether'],
+            'owner': 'tool_research_agent',
+            'resource_ids': [f'tools/image_gen/{tool_name}'],
+            'source': {
+                'agent_id': 'tool_research_agent',
+                'category': 'case',
+                'timestamp': timestamp,
+                'url': source_link
+            },
+            'eval': {'score': 5, 'helpful': 1, 'confidence': 0.9},
+            'created_at': timestamp,
+            'updated_at': timestamp,
+            'status': 'approved',
+            'relationships': []
+        }
+
+        try:
+            print(f"  - 插入数据库...")
+            knowledge_store.insert(knowledge)
+            imported_count += 1
+            case_id_mapping[str(case_id)] = knowledge_id  # 记录映射关系
+            print(f"  ✓ 成功")
+        except Exception as e:
+            print(f"  ✗ 失败: {e}")
+
+    print(f"成功导入 {imported_count}/{len(cases)} 个 cases\n")
+    return case_id_mapping
+
+
+async def main():
+    """主函数"""
+    print("开始导入 tool_research 数据到数据库...")
+
+    # 初始化数据库连接
+    knowledge_store = PostgreSQLStore()
+    req_store = PostgreSQLRequirementStore()
+
+    # 定义数据路径
+    base_path = Path(__file__).parent.parent.parent / 'examples' / 'tool_research' / 'outputs'
+
+    tools_data = [
+        {
+            'name': 'midjourney',
+            'match_result': base_path / 'nodes_output' / 'midjourney' / 'match_nodes_result.json',
+            'cases': base_path / 'midjourney_0' / '02_cases.json'
+        },
+        {
+            'name': 'seedream',
+            'match_result': base_path / 'nodes_output' / 'seedream' / 'match_nodes_result.json',
+            'cases': base_path / 'seedream_1' / '02_cases.json'
+        }
+    ]
+
+    total_requirements = 0
+    total_cases = 0
+
+    for tool_data in tools_data:
+        tool_name = tool_data['name']
+        case_id_mapping = {}
+
+        # 先导入 cases,获取 case_id 到 knowledge_id 的映射
+        if tool_data['cases'].exists():
+            case_id_mapping = await import_cases(
+                knowledge_store,
+                tool_name,
+                str(tool_data['cases'])
+            )
+            total_cases += len(case_id_mapping)
+        else:
+            print(f"⚠ 文件不存在: {tool_data['cases']}")
+
+        # 再导入需求,使用 case_id_mapping
+        if tool_data['match_result'].exists():
+            count = await import_requirements(
+                req_store,
+                tool_name,
+                str(tool_data['match_result']),
+                case_id_mapping
+            )
+            total_requirements += count
+        else:
+            print(f"⚠ 文件不存在: {tool_data['match_result']}")
+
+    print("\n" + "="*50)
+    print(f"导入完成!")
+    print(f"  - 需求: {total_requirements} 条")
+    print(f"  - Cases: {total_cases} 条")
+    print("="*50)
+
+
+if __name__ == '__main__':
+    asyncio.run(main())
+

+ 72 - 0
knowhub/knowhub_db/kill_db_locks.py

@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+"""
+查找并杀掉数据库端的僵尸连接,释放 knowledge 表的锁
+"""
+import os, psycopg2
+from dotenv import load_dotenv
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+_root = os.path.normpath(os.path.join(_dir, '..', '..'))
+load_dotenv(os.path.join(_root, '.env'))
+
+conn = psycopg2.connect(
+    host=os.getenv('KNOWHUB_DB'),
+    port=int(os.getenv('KNOWHUB_PORT', 5432)),
+    user=os.getenv('KNOWHUB_USER'),
+    password=os.getenv('KNOWHUB_PASSWORD'),
+    database=os.getenv('KNOWHUB_DB_NAME'),
+    connect_timeout=10
+)
+conn.autocommit = True
+cur = conn.cursor()
+
+print("Connected.\n")
+
+# 1. 查所有非 idle 的活跃连接
+print("=== Active connections (non-idle) ===")
+cur.execute("""
+    SELECT pid, state, now() - query_start as duration, query
+    FROM pg_stat_activity
+    WHERE state != 'idle' AND pid != pg_backend_pid()
+    ORDER BY query_start
+""")
+rows = cur.fetchall()
+print(f"Found {len(rows)} active connections.\n")
+for r in rows:
+    print(f"  PID={r[0]}  state={r[1]}  duration={r[2]}")
+    print(f"    query: {str(r[3])[:150]}")
+    print()
+
+# 2. 查所有 idle in transaction 的连接(这些最可能持有锁)
+print("=== Idle-in-transaction connections ===")
+cur.execute("""
+    SELECT pid, state, now() - query_start as duration, query
+    FROM pg_stat_activity
+    WHERE state = 'idle in transaction' AND pid != pg_backend_pid()
+    ORDER BY query_start
+""")
+idle_tx = cur.fetchall()
+print(f"Found {len(idle_tx)} idle-in-transaction connections.\n")
+for r in idle_tx:
+    print(f"  PID={r[0]}  duration={r[2]}")
+    print(f"    last query: {str(r[3])[:150]}")
+    print()
+
+# 3. 杀掉所有非当前的 非idle 和 idle-in-transaction 连接
+cur.execute("""
+    SELECT pid FROM pg_stat_activity
+    WHERE pid != pg_backend_pid()
+      AND (state != 'idle' OR state = 'idle in transaction')
+""")
+pids = [r[0] for r in cur.fetchall()]
+print(f"=== Terminating {len(pids)} backend connections ===")
+for pid in pids:
+    try:
+        cur.execute("SELECT pg_terminate_backend(%s)", (pid,))
+        print(f"  Killed PID {pid}")
+    except Exception as e:
+        print(f"  Failed PID {pid}: {e}")
+
+print("\nDone. Try opening knowledge table in Navicat now.")
+cur.close()
+conn.close()

+ 84 - 0
knowhub/knowhub_db/migrate_knowledge.py

@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+"""
+补全 knowledge 表的剩余变更:
+1. 添加 tools 列
+2. 添加 task_embedding, content_embedding 列
+3. 将 embedding 数据迁移到 content_embedding
+4. 删除旧 embedding 列
+
+注意:步骤3涉及 387 条 x 1536维向量的 UPDATE,可能需要几十秒。
+如果中途卡住不要 kill 进程,等它跑完,否则会死锁。
+"""
+import os, psycopg2
+from dotenv import load_dotenv
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+_root = os.path.normpath(os.path.join(_dir, '..', '..'))
+load_dotenv(os.path.join(_root, '.env'))
+
+conn = psycopg2.connect(
+    host=os.getenv('KNOWHUB_DB'),
+    port=int(os.getenv('KNOWHUB_PORT', 5432)),
+    user=os.getenv('KNOWHUB_USER'),
+    password=os.getenv('KNOWHUB_PASSWORD'),
+    database=os.getenv('KNOWHUB_DB_NAME'),
+    connect_timeout=10
+)
+conn.autocommit = True
+cur = conn.cursor()
+
+print("Connected.\n")
+
+def col_exists(table, column):
+    cur.execute("""
+        SELECT 1 FROM pg_catalog.pg_attribute a
+        JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+        JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+        WHERE c.relname = %s AND n.nspname = 'public'
+          AND a.attname = %s AND a.attnum > 0 AND NOT a.attisdropped
+    """, (table, column))
+    return cur.fetchone() is not None
+
+# Step 1: 添加 tools 列
+print("[1] Add tools column...")
+if not col_exists('knowledge', 'tools'):
+    cur.execute("ALTER TABLE knowledge ADD COLUMN tools JSONB DEFAULT '[]'")
+    print("  + Added tools")
+else:
+    print("  . tools already exists")
+
+# Step 2: 添加 task_embedding 和 content_embedding
+print("[2] Add task_embedding, content_embedding columns...")
+if not col_exists('knowledge', 'task_embedding'):
+    cur.execute("ALTER TABLE knowledge ADD COLUMN task_embedding real[]")
+    print("  + Added task_embedding")
+else:
+    print("  . task_embedding already exists")
+
+if not col_exists('knowledge', 'content_embedding'):
+    cur.execute("ALTER TABLE knowledge ADD COLUMN content_embedding real[]")
+    print("  + Added content_embedding")
+else:
+    print("  . content_embedding already exists")
+
+# Step 3: 迁移 embedding -> content_embedding
+if col_exists('knowledge', 'embedding'):
+    print("[3] Migrating embedding -> content_embedding (this may take a while)...")
+    cur.execute("""
+        UPDATE knowledge
+        SET content_embedding = embedding
+        WHERE content_embedding IS NULL AND embedding IS NOT NULL
+    """)
+    print("  Migrated.")
+
+    # Step 4: 删除旧 embedding
+    print("[4] Dropping old embedding column...")
+    cur.execute("ALTER TABLE knowledge DROP COLUMN embedding")
+    print("  Dropped.")
+else:
+    print("[3] embedding column already removed, skip migration.")
+    print("[4] skip.")
+
+print("\nDone. Run check_table_structure.py to verify.")
+cur.close()
+conn.close()

+ 405 - 0
knowhub/knowhub_db/migrate_tables_v2.py

@@ -0,0 +1,405 @@
+#!/usr/bin/env python3
+"""
+数据库迁移脚本 v2:
+1. 新建 atomic_capability 表
+2. ALTER tool_table: 新增 capabilities, embedding; 重命名 knowledge -> tool_knowledge
+3. ALTER knowledge: 新增 support_capability, tools; 拆分 embedding -> task_embedding + content_embedding
+4. ALTER requirement_table: 大幅重构(删旧列、加新列)
+5. 从 JSON/MD 文件导入初始数据
+"""
+
+import os
+import sys
+import json
+import time
+import psycopg2
+from psycopg2.extras import RealDictCursor
+from dotenv import load_dotenv
+
+# 显式加载项目根目录的 .env
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+_project_root = os.path.normpath(os.path.join(_script_dir, '..', '..'))
+load_dotenv(os.path.join(_project_root, '.env'))
+
+# ─── 连接 ───────────────────────────────────────────────────────────────────
+
+def get_connection():
+    host = os.getenv('KNOWHUB_DB')
+    port = int(os.getenv('KNOWHUB_PORT', 5432))
+    user = os.getenv('KNOWHUB_USER')
+    password = os.getenv('KNOWHUB_PASSWORD')
+    dbname = os.getenv('KNOWHUB_DB_NAME')
+    print(f"  Connecting to {host}:{port}/{dbname} as {user} ...")
+    conn = psycopg2.connect(
+        host=host,
+        port=port,
+        user=user,
+        password=password,
+        database=dbname,
+        connect_timeout=10
+    )
+    conn.autocommit = True
+    print("  Connected.")
+    return conn
+
+
+# ─── 1. 新建 atomic_capability 表 ──────────────────────────────────────────
+
+CREATE_ATOMIC_CAPABILITY = """
+CREATE TABLE IF NOT EXISTS atomic_capability (
+    id                VARCHAR(64) PRIMARY KEY,
+    name              VARCHAR(255) NOT NULL,
+    criterion         TEXT,
+    description       TEXT,
+    requirements      JSONB DEFAULT '[]',
+    implements        JSONB DEFAULT '{}',
+    tools             JSONB DEFAULT '[]',
+    source_knowledge  JSONB DEFAULT '[]',
+    embedding         float4[]
+) WITH (appendoptimized=false);
+"""
+
+
+# ─── 2. ALTER tool_table ───────────────────────────────────────────────────
+
+ALTER_TOOL_TABLE = [
+    # 新增 capabilities
+    "ALTER TABLE tool_table ADD COLUMN IF NOT EXISTS capabilities JSONB DEFAULT '[]'",
+    # 新增 embedding
+    "ALTER TABLE tool_table ADD COLUMN IF NOT EXISTS embedding float4[]",
+    # 重命名 knowledge -> tool_knowledge(需检查是否已重命名)
+    """
+    DO $$
+    BEGIN
+        IF EXISTS (
+            SELECT 1 FROM information_schema.columns
+            WHERE table_name = 'tool_table' AND column_name = 'knowledge'
+        ) AND NOT EXISTS (
+            SELECT 1 FROM information_schema.columns
+            WHERE table_name = 'tool_table' AND column_name = 'tool_knowledge'
+        ) THEN
+            ALTER TABLE tool_table RENAME COLUMN knowledge TO tool_knowledge;
+        END IF;
+    END $$;
+    """,
+]
+
+
+# ─── 3. ALTER knowledge 表 ─────────────────────────────────────────────────
+
+ALTER_KNOWLEDGE = [
+    # 新增 support_capability
+    "ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS support_capability JSONB DEFAULT '[]'",
+    # 新增 tools
+    "ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS tools JSONB DEFAULT '[]'",
+    # 拆分 embedding -> task_embedding + content_embedding
+    # 先加新列,再迁移数据,最后删旧列
+    "ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS task_embedding float4[]",
+    "ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS content_embedding float4[]",
+    # 将原 embedding 数据复制到 content_embedding(原有的 embedding 是基于 content 的)
+    """
+    DO $$
+    BEGIN
+        IF EXISTS (
+            SELECT 1 FROM information_schema.columns
+            WHERE table_name = 'knowledge' AND column_name = 'embedding'
+        ) THEN
+            UPDATE knowledge SET content_embedding = embedding
+            WHERE content_embedding IS NULL AND embedding IS NOT NULL;
+            ALTER TABLE knowledge DROP COLUMN embedding;
+        END IF;
+    END $$;
+    """,
+]
+
+
+# ─── 4. ALTER requirement_table ────────────────────────────────────────────
+
+ALTER_REQUIREMENT = [
+    # 新增字段
+    "ALTER TABLE requirement_table ADD COLUMN IF NOT EXISTS description TEXT",
+    "ALTER TABLE requirement_table ADD COLUMN IF NOT EXISTS atomics JSONB DEFAULT '[]'",
+    "ALTER TABLE requirement_table ADD COLUMN IF NOT EXISTS source_nodes JSONB DEFAULT '[]'",
+    "ALTER TABLE requirement_table ADD COLUMN IF NOT EXISTS status VARCHAR(32) DEFAULT '未满足'",
+    "ALTER TABLE requirement_table ADD COLUMN IF NOT EXISTS match_result TEXT",
+    # 删除旧字段(检查存在再删)
+    """
+    DO $$
+    BEGIN
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'task') THEN
+            ALTER TABLE requirement_table DROP COLUMN task;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'type') THEN
+            ALTER TABLE requirement_table DROP COLUMN type;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'source_type') THEN
+            ALTER TABLE requirement_table DROP COLUMN source_type;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'source_itemset_id') THEN
+            ALTER TABLE requirement_table DROP COLUMN source_itemset_id;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'source_items') THEN
+            ALTER TABLE requirement_table DROP COLUMN source_items;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'tools') THEN
+            ALTER TABLE requirement_table DROP COLUMN tools;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'knowledge') THEN
+            ALTER TABLE requirement_table DROP COLUMN knowledge;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'case_knowledge') THEN
+            ALTER TABLE requirement_table DROP COLUMN case_knowledge;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'process_knowledge') THEN
+            ALTER TABLE requirement_table DROP COLUMN process_knowledge;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'trace') THEN
+            ALTER TABLE requirement_table DROP COLUMN trace;
+        END IF;
+        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'requirement_table' AND column_name = 'body') THEN
+            ALTER TABLE requirement_table DROP COLUMN body;
+        END IF;
+    END $$;
+    """,
+    # embedding 字段保留,仍为 float4[]
+]
+
+
+# ─── 5. 导入原子能力初始数据 ──────────────────────────────────────────────
+
+def parse_atomic_capabilities(md_path):
+    """从 atomic_capabilities.md 解析出原子能力列表"""
+    caps = []
+    current = None
+
+    with open(md_path, 'r', encoding='utf-8') as f:
+        lines = f.readlines()
+
+    for line in lines:
+        line = line.rstrip()
+
+        # 匹配 ### CAP-XXX: 名称
+        if line.startswith('### CAP-'):
+            if current:
+                caps.append(current)
+            parts = line.split(':', 1)
+            cap_id = parts[0].replace('### ', '').strip()
+            cap_name = parts[1].strip() if len(parts) > 1 else ''
+            current = {
+                'id': cap_id,
+                'name': cap_name,
+                'criterion': '',
+                'description': '',
+                'implements': {},
+                'tools': [],
+                'source_knowledge': [],
+                'requirements': [],
+            }
+        elif current:
+            if line.startswith('- **功能描述**:'):
+                current['description'] = line.replace('- **功能描述**:', '').strip()
+            elif line.startswith('- **判定标准**:'):
+                current['criterion'] = line.replace('- **判定标准**:', '').strip()
+            elif line.startswith('  - ComfyUI:') or line.startswith('  - FLUX') or \
+                 line.startswith('  - Midjourney') or line.startswith('  - Nano Banana') or \
+                 line.startswith('  - Seedream'):
+                # 提取工具名
+                tool_name = line.strip().lstrip('- ').split(':')[0].strip()
+                impl_desc = line.strip().lstrip('- ').split(':', 1)[1].strip() if ':' in line else ''
+                current['implements'][tool_name] = impl_desc
+
+    if current:
+        caps.append(current)
+
+    return caps
+
+
+def import_atomic_capabilities(cursor, caps):
+    """插入原子能力数据"""
+    count = 0
+    for cap in caps:
+        try:
+            cursor.execute("""
+                INSERT INTO atomic_capability (id, name, criterion, description, requirements, implements, tools, source_knowledge)
+                VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
+                ON CONFLICT (id) DO UPDATE SET
+                    name = EXCLUDED.name,
+                    criterion = EXCLUDED.criterion,
+                    description = EXCLUDED.description,
+                    implements = EXCLUDED.implements
+            """, (
+                cap['id'],
+                cap['name'],
+                cap['criterion'],
+                cap['description'],
+                json.dumps(cap['requirements']),
+                json.dumps(cap['implements']),
+                json.dumps(cap['tools']),
+                json.dumps(cap['source_knowledge']),
+            ))
+            count += 1
+        except Exception as e:
+            print(f"  [!] 插入 {cap['id']} 失败: {e}")
+    return count
+
+
+# ─── 6. 导入需求初始数据 ──────────────────────────────────────────────────
+
+def import_requirements(cursor, json_path):
+    """从 requirements_sorted.json 导入需求数据"""
+    with open(json_path, 'r', encoding='utf-8') as f:
+        data = json.load(f)
+
+    reqs = data.get('requirements', [])
+    count = 0
+
+    for req in reqs:
+        req_id = req['requirement_id']
+        description = req['requirement_text']
+        atomics = req.get('matched_capabilities', [])
+
+        # 构建 source_nodes:从 source_nodes + source_posts 合成
+        source_nodes_data = []
+        for node_name in req.get('source_nodes', []):
+            source_nodes_data.append({
+                'node_name': node_name,
+                'posts': req.get('source_posts', [])
+            })
+
+        # 状态映射
+        match_status = req.get('match_status', '未满足')
+        if match_status == '完全满足':
+            status = '已满足'
+        elif match_status == '需要调研':
+            status = '未满足'
+        else:
+            status = '未满足'
+
+        # match_result: 组合 capability_combination + research_note
+        match_parts = []
+        if req.get('capability_combination'):
+            match_parts.append(req['capability_combination'])
+        if req.get('research_note'):
+            match_parts.append(req['research_note'])
+        match_result = '\n'.join(match_parts) if match_parts else ''
+
+        try:
+            cursor.execute("""
+                INSERT INTO requirement_table (id, description, atomics, source_nodes, status, match_result)
+                VALUES (%s, %s, %s, %s, %s, %s)
+                ON CONFLICT (id) DO UPDATE SET
+                    description = EXCLUDED.description,
+                    atomics = EXCLUDED.atomics,
+                    source_nodes = EXCLUDED.source_nodes,
+                    status = EXCLUDED.status,
+                    match_result = EXCLUDED.match_result
+            """, (
+                req_id,
+                description,
+                json.dumps(atomics),
+                json.dumps(source_nodes_data),
+                status,
+                match_result,
+            ))
+            count += 1
+        except Exception as e:
+            print(f"  [!] 插入 {req_id} 失败: {e}")
+
+    return count
+
+
+# ─── 主流程 ───────────────────────────────────────────────────────────────
+
+def main():
+    print("=" * 60)
+    print("KnowHub 数据库迁移 v2")
+    print("=" * 60)
+
+    conn = get_connection()
+    cursor = conn.cursor(cursor_factory=RealDictCursor)
+
+    # Step 1: 新建 atomic_capability 表
+    print("\n[1/6] 新建 atomic_capability 表...")
+    try:
+        cursor.execute(CREATE_ATOMIC_CAPABILITY)
+        print("  OK: atomic_capability 表创建成功")
+    except Exception as e:
+        print(f"  FAIL: {e}")
+
+    # Step 2: ALTER tool_table
+    print("\n[2/6] 更新 tool_table...")
+    for sql in ALTER_TOOL_TABLE:
+        try:
+            cursor.execute(sql)
+        except Exception as e:
+            print(f"  WARN: {e}")
+    print("  OK: tool_table 更新完成 (capabilities, embedding, tool_knowledge)")
+
+    # Step 3: ALTER knowledge
+    print("\n[3/6] 更新 knowledge 表...")
+    for sql in ALTER_KNOWLEDGE:
+        try:
+            cursor.execute(sql)
+        except Exception as e:
+            print(f"  WARN: {e}")
+    print("  OK: knowledge 更新完成 (support_capability, tools, task_embedding, content_embedding)")
+
+    # Step 4: ALTER requirement_table
+    print("\n[4/6] 更新 requirement_table...")
+    for sql in ALTER_REQUIREMENT:
+        try:
+            cursor.execute(sql)
+        except Exception as e:
+            print(f"  WARN: {e}")
+    print("  OK: requirement_table 更新完成 (description, atomics, source_nodes, status, match_result)")
+
+    # Step 5: 导入原子能力
+    print("\n[5/6] 导入原子能力数据...")
+    md_path = os.path.join(os.path.dirname(__file__), '..', '..', 'examples', 'tool_research', 'atomic_capabilities.md')
+    md_path = os.path.normpath(md_path)
+    if os.path.exists(md_path):
+        caps = parse_atomic_capabilities(md_path)
+        count = import_atomic_capabilities(cursor, caps)
+        print(f"  OK: 导入 {count} 条原子能力")
+    else:
+        print(f"  SKIP: 文件不存在 {md_path}")
+
+    # Step 6: 导入需求数据
+    print("\n[6/6] 导入需求数据...")
+    json_path = os.path.join(os.path.dirname(__file__), '..', '..', 'examples', 'tool_research', 'requirements_sorted.json')
+    json_path = os.path.normpath(json_path)
+    if os.path.exists(json_path):
+        count = import_requirements(cursor, json_path)
+        print(f"  OK: 导入 {count} 条需求")
+    else:
+        print(f"  SKIP: 文件不存在 {json_path}")
+
+    # 验证
+    print("\n" + "=" * 60)
+    print("验证结果:")
+    print("=" * 60)
+
+    for table in ['atomic_capability', 'tool_table', 'knowledge', 'requirement_table', 'resources']:
+        try:
+            cursor.execute(f"SELECT COUNT(*) as count FROM {table}")
+            row = cursor.fetchone()
+            count = row['count'] if row else 0
+
+            cursor.execute(f"""
+                SELECT column_name FROM information_schema.columns
+                WHERE table_name = '{table}'
+                ORDER BY ordinal_position
+            """)
+            cols = [r['column_name'] for r in cursor.fetchall()]
+            print(f"\n  {table} ({count} rows)")
+            print(f"    字段: {', '.join(cols)}")
+        except Exception as e:
+            print(f"\n  {table}: ERROR - {e}")
+
+    cursor.close()
+    conn.close()
+    print("\n迁移完成!")
+
+
+if __name__ == '__main__':
+    main()

+ 134 - 0
knowhub/knowhub_db/pg_requirement_store.py

@@ -0,0 +1,134 @@
+"""
+PostgreSQL requirement_table 存储封装
+
+用于存储和检索需求数据,支持向量检索
+"""
+
+import os
+import json
+import psycopg2
+from psycopg2.extras import RealDictCursor
+from typing import List, Dict, Optional
+from dotenv import load_dotenv
+
+load_dotenv()
+
+
+class PostgreSQLRequirementStore:
+    def __init__(self):
+        """初始化 PostgreSQL 连接"""
+        self.conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        self.conn.autocommit = False
+        print(f"[PostgreSQL Requirement] 已连接到远程数据库: {os.getenv('KNOWHUB_DB')}")
+
+    def _get_cursor(self):
+        """获取游标"""
+        return self.conn.cursor(cursor_factory=RealDictCursor)
+
+    def insert_or_update(self, requirement: Dict):
+        """插入或更新需求记录"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("""
+                INSERT INTO requirement_table (
+                    id, task, type, source_type, source_itemset_id,
+                    source_items, tools, knowledge, case_knowledge,
+                    process_knowledge, trace, body, embedding
+                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
+                ON CONFLICT (id) DO UPDATE SET
+                    task = EXCLUDED.task,
+                    type = EXCLUDED.type,
+                    source_type = EXCLUDED.source_type,
+                    source_itemset_id = EXCLUDED.source_itemset_id,
+                    source_items = EXCLUDED.source_items,
+                    tools = EXCLUDED.tools,
+                    knowledge = EXCLUDED.knowledge,
+                    case_knowledge = EXCLUDED.case_knowledge,
+                    process_knowledge = EXCLUDED.process_knowledge,
+                    trace = EXCLUDED.trace,
+                    body = EXCLUDED.body,
+                    embedding = EXCLUDED.embedding
+            """, (
+                requirement['id'],
+                requirement['task'],
+                requirement.get('type', '制作'),
+                requirement.get('source_type', 'itemset'),
+                requirement.get('source_itemset_id', ''),
+                json.dumps(requirement.get('source_items', [])),
+                json.dumps(requirement.get('tools', [])),
+                json.dumps(requirement.get('knowledge', [])),
+                json.dumps(requirement.get('case_knowledge', [])),
+                json.dumps(requirement.get('process_knowledge', [])),
+                json.dumps(requirement.get('trace', {})),
+                requirement.get('body', ''),
+                requirement['embedding']
+            ))
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def get_by_id(self, req_id: str) -> Optional[Dict]:
+        """根据 ID 获取需求"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("""
+                SELECT * FROM requirement_table WHERE id = %s
+            """, (req_id,))
+            result = cursor.fetchone()
+            return dict(result) if result else None
+        finally:
+            cursor.close()
+
+    def search(self, query_embedding: List[float], limit: int = 10) -> List[Dict]:
+        """向量检索需求(使用 fastann ANN 索引)"""
+        cursor = self._get_cursor()
+        try:
+            sql = """
+                SELECT id, task, type, source_type, source_itemset_id,
+                       source_items, tools, knowledge, case_knowledge,
+                       process_knowledge, trace, body,
+                       1 - (embedding <=> %s::real[]) as score
+                FROM requirement_table
+                ORDER BY embedding <=> %s::real[]
+                LIMIT %s
+            """
+            cursor.execute(sql, (query_embedding, query_embedding, limit))
+            results = cursor.fetchall()
+            return [dict(r) for r in results]
+        finally:
+            cursor.close()
+
+    def list_all(self, limit: int = 100) -> List[Dict]:
+        """列出所有需求"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("""
+                SELECT * FROM requirement_table LIMIT %s
+            """, (limit,))
+            results = cursor.fetchall()
+            return [dict(r) for r in results]
+        finally:
+            cursor.close()
+
+    def count(self) -> int:
+        """统计需求总数"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("SELECT COUNT(*) as count FROM requirement_table")
+            result = cursor.fetchone()
+            return result['count'] if result else 0
+        finally:
+            cursor.close()
+
+    def close(self):
+        """关闭连接"""
+        if self.conn:
+            self.conn.close()
+
+

+ 27 - 17
knowhub/knowhub_db/pg_store.py

@@ -37,13 +37,14 @@ class PostgreSQLStore:
         try:
             cursor.execute("""
                 INSERT INTO knowledge (
-                    id, embedding, message_id, task, content, types, tags,
+                    id, task_embedding, message_id, task, content, types, tags,
                     tag_keys, scopes, owner, resource_ids, source, eval,
-                    created_at, updated_at, status, relationships
-                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
+                    created_at, updated_at, status, relationships,
+                    support_capability, tools
+                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
             """, (
                 knowledge['id'],
-                knowledge['embedding'],
+                knowledge.get('task_embedding') or knowledge.get('embedding'),
                 knowledge['message_id'],
                 knowledge['task'],
                 knowledge['content'],
@@ -58,7 +59,9 @@ class PostgreSQLStore:
                 knowledge['created_at'],
                 knowledge['updated_at'],
                 knowledge.get('status', 'approved'),
-                json.dumps(knowledge.get('relationships', []))
+                json.dumps(knowledge.get('relationships', [])),
+                json.dumps(knowledge.get('support_capability', [])),
+                json.dumps(knowledge.get('tools', [])),
             ))
             self.conn.commit()
         finally:
@@ -72,11 +75,11 @@ class PostgreSQLStore:
             sql = f"""
                 SELECT id, message_id, task, content, types, tags, tag_keys,
                        scopes, owner, resource_ids, source, eval, created_at,
-                       updated_at, status, relationships,
-                       1 - (embedding <=> %s::real[]) as score
+                       updated_at, status, relationships, support_capability, tools,
+                       1 - (task_embedding <=> %s::real[]) as score
                 FROM knowledge
                 {where_clause}
-                ORDER BY embedding <=> %s::real[]
+                ORDER BY task_embedding <=> %s::real[]
                 LIMIT %s
             """
             cursor.execute(sql, (query_embedding, query_embedding, limit))
@@ -93,7 +96,7 @@ class PostgreSQLStore:
             sql = f"""
                 SELECT id, message_id, task, content, types, tags, tag_keys,
                        scopes, owner, resource_ids, source, eval, created_at,
-                       updated_at, status, relationships
+                       updated_at, status, relationships, support_capability, tools
                 FROM knowledge
                 {where_clause}
                 LIMIT %s
@@ -110,9 +113,9 @@ class PostgreSQLStore:
         try:
             # 默认不返回embedding(1536维向量太大,详情页不需要)
             if include_embedding:
-                fields = "id, embedding, message_id, task, content, types, tags, tag_keys, scopes, owner, resource_ids, source, eval, created_at, updated_at, status, relationships"
+                fields = "id, task_embedding, content_embedding, message_id, task, content, types, tags, tag_keys, scopes, owner, resource_ids, source, eval, created_at, updated_at, status, relationships, support_capability, tools"
             else:
-                fields = "id, message_id, task, content, types, tags, tag_keys, scopes, owner, resource_ids, source, eval, created_at, updated_at, status, relationships"
+                fields = "id, message_id, task, content, types, tags, tag_keys, scopes, owner, resource_ids, source, eval, created_at, updated_at, status, relationships, support_capability, tools"
 
             cursor.execute(f"""
                 SELECT {fields}
@@ -130,7 +133,7 @@ class PostgreSQLStore:
             set_parts = []
             params = []
             for key, value in updates.items():
-                if key in ('tags', 'source', 'eval'):
+                if key in ('tags', 'source', 'eval', 'support_capability', 'tools'):
                     set_parts.append(f"{key} = %s")
                     params.append(json.dumps(value))
                 elif key == 'relationships':
@@ -202,6 +205,10 @@ class PostgreSQLStore:
             result['eval'] = json.loads(result['eval'])
         if 'relationships' in result and isinstance(result['relationships'], str):
             result['relationships'] = json.loads(result['relationships'])
+        if 'support_capability' in result and isinstance(result['support_capability'], str):
+            result['support_capability'] = json.loads(result['support_capability'])
+        if 'tools' in result and isinstance(result['tools'], str):
+            result['tools'] = json.loads(result['tools'])
         if 'created_at' in result and result['created_at']:
             result['created_at'] = result['created_at'] * 1000
         if 'updated_at' in result and result['updated_at']:
@@ -223,21 +230,24 @@ class PostgreSQLStore:
             data = []
             for k in knowledge_list:
                 data.append((
-                    k['id'], k['embedding'], k['message_id'], k['task'],
+                    k['id'], k.get('task_embedding') or k.get('embedding'), k['message_id'], k['task'],
                     k['content'], k.get('types', []),
                     json.dumps(k.get('tags', {})), k.get('tag_keys', []),
                     k.get('scopes', []), k['owner'], k.get('resource_ids', []),
                     json.dumps(k.get('source', {})), json.dumps(k.get('eval', {})),
                     k['created_at'], k['updated_at'], k.get('status', 'approved'),
-                    json.dumps(k.get('relationships', []))
+                    json.dumps(k.get('relationships', [])),
+                    json.dumps(k.get('support_capability', [])),
+                    json.dumps(k.get('tools', [])),
                 ))
 
             execute_batch(cursor, """
                 INSERT INTO knowledge (
-                    id, embedding, message_id, task, content, types, tags,
+                    id, task_embedding, message_id, task, content, types, tags,
                     tag_keys, scopes, owner, resource_ids, source, eval,
-                    created_at, updated_at, status, relationships
-                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
+                    created_at, updated_at, status, relationships,
+                    support_capability, tools
+                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
             """, data)
             self.conn.commit()
         finally:

+ 56 - 0
knowhub/knowhub_db/test_imports.py

@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+测试版本 - 逐步排查问题
+"""
+print("Step 0: Script started")
+
+import json
+print("Step 1: json imported")
+
+import os
+print("Step 2: os imported")
+
+import sys
+print("Step 3: sys imported")
+
+import time
+print("Step 4: time imported")
+
+import asyncio
+print("Step 5: asyncio imported")
+
+from pathlib import Path
+print("Step 6: Path imported")
+
+from typing import List, Dict, Any
+print("Step 7: typing imported")
+
+# 不设置编码,看看是否是编码问题
+print("Step 8: About to add path")
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+print("Step 9: Path added")
+
+from dotenv import load_dotenv
+print("Step 10: dotenv imported")
+
+project_root = Path(__file__).parent.parent.parent
+env_path = project_root / '.env'
+load_dotenv(env_path)
+print(f"Step 11: .env loaded from {env_path}")
+
+print("Step 12: About to import PostgreSQLStore")
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+print("Step 13: PostgreSQLStore imported")
+
+print("Step 14: About to import embeddings")
+from knowhub.embeddings import get_embedding
+print("Step 15: embeddings imported")
+
+print("Step 16: About to import qwen")
+from agent.llm.qwen import qwen_llm_call
+print("Step 17: qwen imported")
+
+print("\n=== All imports successful! ===")
+print("Script can proceed normally.")

+ 281 - 0
knowhub/knowhub_db/update_case_knowledge.py

@@ -0,0 +1,281 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+更新已导入的 case knowledge 记录
+- 使用 qwen 生成更好的 task 描述
+- 补充完整的 case 信息(input, output, operation_process, images)
+"""
+import json
+import os
+import sys
+import time
+import asyncio
+from pathlib import Path
+from typing import List, Dict, Any
+
+# 添加父目录到路径
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+# 加载环境变量
+from dotenv import load_dotenv
+project_root = Path(__file__).parent.parent.parent
+env_path = project_root / '.env'
+load_dotenv(env_path)
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+from agent.llm.qwen import qwen_llm_call
+
+
+async def get_embedding_with_retry(text: str, max_retries: int = 3) -> List[float]:
+    """带重试的 embedding 生成"""
+    for attempt in range(max_retries):
+        try:
+            return await get_embedding(text)
+        except Exception as e:
+            if attempt < max_retries - 1:
+                wait_time = (attempt + 1) * 2
+                print(f"  ⚠ Embedding 生成失败,{wait_time}秒后重试... ({attempt + 1}/{max_retries})")
+                await asyncio.sleep(wait_time)
+            else:
+                raise e
+
+
+async def generate_task_description(case_data: Dict) -> str:
+    """使用 qwen 生成 task 描述"""
+    title = case_data.get('title', '')
+    user_input = case_data.get('user_input', {})
+    output_desc = case_data.get('output_description', '')
+    key_findings = case_data.get('key_findings', '')
+
+    prompt = f"""请为以下 AI 图像生成案例生成一个简洁的任务描述(task),用于知识库检索。
+
+案例标题:{title}
+用户输入:{json.dumps(user_input, ensure_ascii=False)}
+输出效果:{output_desc}
+关键发现:{key_findings}
+
+要求:
+1. 用一句话概括这个案例的核心任务或目标
+2. 突出关键的技术特点或应用场景
+3. 不超过 30 个字
+4. 直接输出描述,不要其他内容
+
+示例格式:
+- 使用 sref 风格代码生成行星级尺度的巨构建筑场景
+- 测试不同 stylize 值对电影感角色渲染的影响
+- 对比 V7 和 V8 在科幻哥特风格上的表现差异
+"""
+
+    response = await qwen_llm_call(
+        messages=[{"role": "user", "content": prompt}],
+        temperature=0.3,
+        max_tokens=100
+    )
+    task_desc = response["content"].strip()
+    return task_desc
+
+
+def load_json_file(file_path: str) -> Dict:
+    """加载 JSON 文件"""
+    with open(file_path, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+
+def build_enhanced_content(case_data: Dict) -> str:
+    """构建增强的 content 内容"""
+    title = case_data.get('title', '')
+    source = case_data.get('source', '')
+    source_link = case_data.get('source_link', '')
+    user_input = case_data.get('user_input', {})
+    output_desc = case_data.get('output_description', '')
+    key_findings = case_data.get('key_findings', '')
+    images = case_data.get('images', [])
+    operation_process = case_data.get('operation_process', '')
+
+    # 构建用户输入部分
+    input_section = "## 用户输入\n"
+    if user_input:
+        for key, value in user_input.items():
+            if isinstance(value, list):
+                input_section += f"- **{key}**: {', '.join(str(v) for v in value)}\n"
+            else:
+                input_section += f"- **{key}**: {value}\n"
+    else:
+        input_section += "(无详细输入信息)\n"
+
+    # 构建操作流程部分
+    process_section = ""
+    if operation_process:
+        process_section = f"\n## 操作流程\n{operation_process}\n"
+
+    # 构建图片部分
+    images_section = ""
+    if images:
+        images_section = "\n## 示例图片\n"
+        for i, img_url in enumerate(images, 1):
+            images_section += f"{i}. {img_url}\n"
+
+    content = f"""# {title}
+
+## 来源
+{source}
+链接: {source_link}
+
+{input_section}
+
+## 输出效果
+{output_desc}
+
+## 关键发现
+{key_findings}
+{process_section}{images_section}
+"""
+    return content
+
+
+async def update_cases(
+    knowledge_store: PostgreSQLStore,
+    tool_name: str,
+    cases_json_path: str
+):
+    """更新 case knowledge 记录"""
+    print(f"\n=== 更新 {tool_name} 的 cases ===")
+
+    # 加载 cases.json
+    data = load_json_file(cases_json_path)
+    cases = data.get('cases', [])
+
+    print(f"找到 {len(cases)} 个 cases")
+
+    updated_count = 0
+    for i, case in enumerate(cases):
+        case_id = case.get('case_id', '')
+        title = case.get('title', '')
+        knowledge_id = f"knowledge-case-{tool_name}-{case_id}"
+
+        print(f"\n[{i + 1}/{len(cases)}] 更新 case: {title[:50]}...")
+        print(f"  - Knowledge ID: {knowledge_id}")
+
+        # 生成新的 task 描述
+        print(f"  - 使用 qwen 生成 task 描述...")
+        try:
+            task_desc = await generate_task_description(case)
+            print(f"  - Task: {task_desc}")
+        except Exception as e:
+            print(f"  ⚠ Task 生成失败,使用标题: {e}")
+            task_desc = title
+
+        # 构建增强的 content
+        print(f"  - 构建增强内容...")
+        enhanced_content = build_enhanced_content(case)
+
+        # 重新生成 embedding(只基于 task,与 save_knowledge 保持一致)
+        print(f"  - 重新生成 embedding(基于 task)...")
+        try:
+            new_embedding = await get_embedding_with_retry(task_desc)
+            print(f"  - Embedding 维度: {len(new_embedding)}")
+        except Exception as e:
+            print(f"  ⚠ Embedding 生成失败: {e}")
+            new_embedding = None
+
+        # 更新数据库
+        try:
+            print(f"  - 更新数据库...")
+            updates = {
+                'task': task_desc,
+                'content': enhanced_content,
+                'updated_at': int(time.time())
+            }
+            if new_embedding:
+                updates['embedding'] = new_embedding
+
+            knowledge_store.update(knowledge_id, updates)
+            updated_count += 1
+            print(f"  ✓ 成功")
+        except Exception as e:
+            print(f"  ✗ 失败: {e}")
+
+        # 避免 API 限流
+        await asyncio.sleep(0.5)
+
+    print(f"\n成功更新 {updated_count}/{len(cases)} 个 cases\n")
+    return updated_count
+
+
+async def main():
+    """主函数"""
+    print("=" * 60)
+    print("开始更新 case knowledge 记录...")
+    print("=" * 60)
+
+    # 初始化
+    print("\n[1/4] 初始化数据库连接...")
+    try:
+        knowledge_store = PostgreSQLStore()
+        print("  ✓ 数据库连接成功")
+    except Exception as e:
+        print(f"  ✗ 数据库连接失败: {e}")
+        return
+
+    print("\n[2/4] 检查数据文件...")
+    # 定义数据路径
+    base_path = Path(__file__).parent.parent.parent / 'examples' / 'tool_research' / 'outputs'
+
+    tools_data = [
+        {
+            'name': 'midjourney',
+            'cases': base_path / 'midjourney_0' / '02_cases.json'
+        },
+        {
+            'name': 'seedream',
+            'cases': base_path / 'seedream_1' / '02_cases.json'
+        }
+    ]
+
+    for tool_data in tools_data:
+        if tool_data['cases'].exists():
+            print(f"  ✓ {tool_data['name']}: {tool_data['cases']}")
+        else:
+            print(f"  ✗ {tool_data['name']}: 文件不存在")
+
+    print("\n[3/4] 测试 qwen API 连接...")
+    try:
+        test_response = await qwen_llm_call(
+            messages=[{"role": "user", "content": "测试"}],
+            temperature=0.3,
+            max_tokens=10
+        )
+        print(f"  ✓ qwen API 连接成功")
+    except Exception as e:
+        print(f"  ✗ qwen API 连接失败: {e}")
+        print("  提示:请检查 QWEN_API_KEY 环境变量")
+        return
+
+    print("\n[4/4] 开始处理 cases...")
+    print("=" * 60)
+
+    total_updated = 0
+
+    for tool_data in tools_data:
+        tool_name = tool_data['name']
+
+        if tool_data['cases'].exists():
+            count = await update_cases(
+                knowledge_store,
+                tool_name,
+                str(tool_data['cases'])
+            )
+            total_updated += count
+        else:
+            print(f"⚠ 文件不存在: {tool_data['cases']}")
+
+    print("\n" + "="*50)
+    print(f"更新完成!")
+    print(f"  - 总计更新: {total_updated} 条")
+    print("="*50)
+
+
+if __name__ == '__main__':
+    asyncio.run(main())
+

+ 31 - 202
knowhub/server.py

@@ -32,6 +32,14 @@ from dotenv import load_dotenv
 load_dotenv(Path(__file__).parent.parent / ".env")
 
 from agent.llm import create_openrouter_llm_call, create_qwen_llm_call
+from knowhub.kb_manage_prompts import (
+    DEDUP_RELATION_PROMPT,
+    TOOL_ANALYSIS_PROMPT,
+    RERANK_PROMPT_TEMPLATE,
+    KNOWLEDGE_EVOLVE_PROMPT_TEMPLATE,
+    KNOWLEDGE_SLIM_PROMPT_TEMPLATE,
+    MESSAGE_EXTRACT_PROMPT_TEMPLATE,
+)
 
 _dedup_llm = create_openrouter_llm_call(model="google/gemini-2.5-flash-lite")
 _tool_analysis_llm = create_qwen_llm_call(model="qwen3.5-plus")
@@ -295,111 +303,6 @@ class ResourceOut(BaseModel):
 
 knowledge_processor: Optional["KnowledgeProcessor"] = None
 
-DEDUP_RELATION_PROMPT = """你是知识库管理专家。请判断【新知识】与【相似知识列表】中每条知识的关系。
-
-【新知识】
-Task: {new_task}
-Content: {new_content}
-
-【相似知识列表】(向量召回 top-10,按相似度排序)
-{existing_list}
-格式: [序号] ID: xxx | Task: xxx | Content: xxx
-
-【关系类型定义】
-- duplicate: task 和 content 语义完全相同,无新增信息 → 新知识应 rejected
-- subset: task语义一致,新知识的content信息完全被某条已有知识覆盖 → 新知识应 rejected
-- superset: task语义一致,新知识包含某条已有知识的全部信息,且有额外内容 → 新知识应 approved
-- conflict: 同一 task 下给出相互矛盾的结论 → 新知识应 approved
-- complement: 描述同一 task 的不同方面,互补 → 新知识应 approved
-- none: task 语义不同,或无实质关系 → 新知识应 approved,不写入 relations
-
-【判断步骤】
-第一步:逐条比较新知识的 task 与列表中每条知识的 task 语义是否一致。
-- task 语义一致 = 两者描述的是同一个问题或目标(即使措辞不同)
-- task 语义不同 = 描述的是不同的问题、不同的工具、不同的场景
-- 如果 task 语义不同,该条关系直接判定为 none,**不再看 content**
-- 只有 task 语义一致时,才进入第二步比较 content
-
-第二步:对 task 语义一致的知识,比较 content,判断具体关系类型(duplicate/subset/superset/conflict/complement)。
-
-**规则**:
-1. 如果以上类型无法准确描述,可自定义关系类型(英文小写下划线),并自行决定 approved/rejected
-2. final_decision 为 rejected 时,relations 中必须至少有一条关系说明拒绝原因(type 不能为 none)
-
-【输出格式】(严格 JSON,不要其他内容)
-
-示例1 - 无关知识(task 不同):
-{{
-  "final_decision": "approved",
-  "relations": []
-}}
-
-示例2 - 重复知识:
-{{
-  "final_decision": "rejected",
-  "relations": [
-    {{
-      "old_id": "knowledge-xxx",
-      "type": "duplicate",
-      "reverse_type": "duplicate"
-    }}
-  ]
-}}
-
-示例3 - 互补知识:
-{{
-  "final_decision": "approved",
-  "relations": [
-    {{
-      "old_id": "knowledge-xxx",
-      "type": "complement",
-      "reverse_type": "complement"
-    }}
-  ]
-}}
-
-"""
-
-
-TOOL_ANALYSIS_PROMPT = """\
-分析以下知识条目,判断是否涉及"图像创作或解构任务中使用的工具"。
-
-工具范畴(包括但不限于):
-- AI 生图平台/模型:Midjourney、Stable Diffusion、DALL-E、Flux、ComfyUI
-- SD 插件/节点:ControlNet、IP-Adapter、InstantID、DWPose、DSINE
-- 图像处理库:rembg、PIL/Pillow、OpenCV、scikit-image
-- LoRA/checkpoint 模型、ComfyUI 自定义节点、AI 绘图辅助工具
-
-知识条目:
-task: {task}
-content: {content}
-
-要求:
-- 如果涉及上述工具,提取每个工具的信息并以 JSON 格式返回。
-- 如果不涉及任何工具,返回 {{"has_tools": false}}。
-- 只输出 JSON,不要输出其他内容。
-
-输出格式:
-{{
-  "has_tools": true,
-  "tools": [
-    {{
-      "name": "工具名称(原名)",
-      "slug": "小写英文短名,空格换下划线,如 controlnet、ip_adapter",
-      "category": "image_gen | image_process | model | plugin | workflow | other",
-      "version": "版本号或 null",
-      "description": "一句话功能介绍",
-      "usage": "核心用法",
-      "scenarios": ["应用场景1", "应用场景2"],
-      "input": "输入类型描述或 null",
-      "output": "输出类型描述或 null",
-      "source": "来源/文档链接或 null",
-      "status": "未接入"
-    }}
-  ]
-}}
-"""
-
 
 # --- Dedup: RelationCache ---
 
@@ -468,7 +371,7 @@ class KnowledgeProcessor:
             return
         try:
             # 向量召回 top-10(只召回 approved/checked)
-            embedding = knowledge.get("embedding")
+            embedding = knowledge.get("task_embedding") or knowledge.get("embedding")
             if not embedding:
                 embedding = await get_embedding(knowledge["task"])
             candidates = pg_store.search(
@@ -641,7 +544,7 @@ class KnowledgeProcessor:
                 return tool_id
             cursor.execute("""
                 INSERT INTO tool_table (id, name, version, introduction, tutorial, input, output,
-                                        updated_time, status, knowledge, case_knowledge, process_knowledge)
+                                        updated_time, status, tool_knowledge, case_knowledge, process_knowledge)
                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
             """, (
                 tool_id,
@@ -664,19 +567,19 @@ class KnowledgeProcessor:
             cursor.close()
 
     async def _update_tool_knowledge_index(self, tool_id: str, knowledge_id: str):
-        """更新工具的 knowledge 关联索引(PostgreSQL tool_table)"""
+        """更新工具的 tool_knowledge 关联索引(PostgreSQL tool_table)"""
         now_ts = int(time.time())
         cursor = pg_store._get_cursor()
         try:
-            cursor.execute("SELECT knowledge FROM tool_table WHERE id = %s", (tool_id,))
+            cursor.execute("SELECT tool_knowledge FROM tool_table WHERE id = %s", (tool_id,))
             row = cursor.fetchone()
             if not row:
                 return
-            knowledge_ids = row["knowledge"] if isinstance(row["knowledge"], list) else json.loads(row["knowledge"] or "[]")
+            knowledge_ids = row["tool_knowledge"] if isinstance(row["tool_knowledge"], list) else json.loads(row["tool_knowledge"] or "[]")
             if knowledge_id not in knowledge_ids:
                 knowledge_ids.append(knowledge_id)
                 cursor.execute(
-                    "UPDATE tool_table SET knowledge = %s, updated_time = %s WHERE id = %s",
+                    "UPDATE tool_table SET tool_knowledge = %s, updated_time = %s WHERE id = %s",
                     (json.dumps(knowledge_ids), now_ts, tool_id)
                 )
                 pg_store.conn.commit()
@@ -978,15 +881,11 @@ async def _llm_rerank(query: str, candidates: list[dict], top_k: int) -> list[st
         for i, c in enumerate(candidates)
     ])
 
-    prompt = f"""你是知识检索专家。根据用户查询,从候选知识中选出最相关的 {top_k} 条。
-
-用户查询:"{query}"
-
-候选知识:
-{candidates_text}
-
-请输出最相关的 {top_k} 个知识 ID,按相关性从高到低排序,用逗号分隔。
-只输出 ID,不要其他内容。"""
+    prompt = RERANK_PROMPT_TEMPLATE.format(
+        top_k=top_k,
+        query=query,
+        candidates_text=candidates_text
+    )
 
     try:
         response = await _dedup_llm(
@@ -1120,7 +1019,7 @@ async def save_knowledge(knowledge: KnowledgeIn, background_tasks: BackgroundTas
         # 准备插入数据
         insert_data = {
             "id": knowledge_id,
-            "embedding": embedding,
+            "task_embedding": embedding,
             "message_id": knowledge.message_id,
             "task": knowledge.task,
             "content": knowledge.content,
@@ -1338,20 +1237,10 @@ def get_knowledge(knowledge_id: str):
 
 async def _evolve_knowledge_with_llm(old_content: str, feedback: str) -> str:
     """使用 LLM 进行知识进化重写"""
-    prompt = f"""你是一个 AI Agent 知识库管理员。请根据反馈建议,对现有的知识内容进行重写进化。
-
-【原知识内容】:
-{old_content}
-
-【实战反馈建议】:
-{feedback}
-
-【重写要求】:
-1. 融合知识:将反馈中的避坑指南、新参数或修正后的选择逻辑融入原知识,使其更具通用性和准确性。
-2. 保持结构:如果原内容有特定格式(如 Markdown、代码示例等),请保持该格式。
-3. 语言:简洁直接,使用中文。
-4. 禁止:严禁输出任何开场白、解释语或额外的 Markdown 标题,直接返回重写后的正文。
-"""
+    prompt = KNOWLEDGE_EVOLVE_PROMPT_TEMPLATE.format(
+        old_content=old_content,
+        feedback=feedback
+    )
     try:
         response = await _dedup_llm(
             messages=[{"role": "user", "content": prompt}],
@@ -1412,7 +1301,7 @@ async def update_knowledge(knowledge_id: str, update: KnowledgeUpdateIn):
         # 如果内容变化,重新生成向量
         if need_reembed:
             embedding = await get_embedding(existing['task'])
-            updates["embedding"] = embedding
+            updates["task_embedding"] = embedding
 
         # 更新 Milvus
         pg_store.update(knowledge_id, updates)
@@ -1467,7 +1356,7 @@ async def patch_knowledge(knowledge_id: str, patch: KnowledgePatchIn):
         if need_reembed:
             task = updates.get("task", existing["task"])
             embedding = await get_embedding(task)
-            updates["embedding"] = embedding
+            updates["task_embedding"] = embedding
 
         # 更新 Milvus
         pg_store.update(knowledge_id, updates)
@@ -1657,7 +1546,7 @@ async def batch_update_knowledge(batch: KnowledgeBatchUpdateIn):
                 pg_store.update(knowledge_id, {
                     "content": evolved_content,
                     "eval": eval_data,
-                    "embedding": embedding
+                    "task_embedding": embedding
                 })
 
         return {"status": "ok", "updated": len(simple_updates) + len(evolution_tasks)}
@@ -1689,31 +1578,7 @@ async def slim_knowledge(model: str = "google/gemini-2.5-flash-lite"):
             entries_text += f"Task: {item['task']}\n"
             entries_text += f"Content: {item['content'][:200]}...\n\n"
 
-        prompt = f"""你是一个 AI Agent 知识库管理员。以下是当前知识库的全部条目,请执行瘦身操作:
-
-【任务】:
-1. 识别语义高度相似或重复的知识,将它们合并为一条更精炼、更通用的知识。
-2. 合并时保留 helpful 最高的那条的 ID(helpful 取各条之和)。
-3. 对于独立的、无重复的知识,保持原样不动。
-
-【当前知识库】:
-{entries_text}
-
-【输出格式要求】:
-严格按以下格式输出每条知识,条目之间用 === 分隔:
-ID: <保留的id>
-TYPES: <逗号分隔的type列表>
-HELPFUL: <合并后的helpful计数>
-HARMFUL: <合并后的harmful计数>
-SCORE: <评分>
-TASK: <任务描述>
-CONTENT: <合并后的知识内容>
-===
-
-最后输出合并报告:
-REPORT: 原有 X 条,合并后 Y 条,精简了 Z 条。
-
-禁止输出任何开场白或解释。"""
+        prompt = KNOWLEDGE_SLIM_PROMPT_TEMPLATE.format(entries_text=entries_text)
 
         print(f"\n[知识瘦身] 正在调用 {model} 分析 {len(all_knowledge)} 条知识...")
         slim_llm = create_openrouter_llm_call(model=model)
@@ -1824,7 +1689,7 @@ REPORT: 原有 X 条,合并后 Y 条,精简了 Z 条。
             }
             knowledge_list.append({
                 "id": e["id"],
-                "embedding": embedding,
+                "task_embedding": embedding,
                 "message_id": "",
                 "task": e["task"],
                 "content": e["content"],
@@ -1876,43 +1741,7 @@ async def extract_knowledge_from_messages(extract_req: MessageExtractIn, backgro
         messages_text += f"[{role}]: {content}\n\n"
 
     # LLM 提取知识
-    prompt = f"""你是一个知识提取专家。请从以下 Agent 对话历史中提取有价值的知识。
-
-【对话历史】:
-{messages_text}
-
-【提取要求】:
-1. 识别对话中的关键知识点(工具使用经验、问题解决方案、最佳实践、踩坑经验等)
-2. 每条知识必须包含:
-   - task: 任务场景描述(在什么情况下,要完成什么目标)
-   - content: 核心知识内容(具体可操作的方法、注意事项)
-   - types: 知识类型(从 strategy/tool/user_profile/usecase/definition/plan 中选择)
-   - score: 评分 1-5(根据知识的价值和可操作性)
-3. 只提取有实际价值的知识,不要提取泛泛而谈的内容,一次就成功或比较简单的经验就不要记录了。
-4. 如果没有值得提取的知识,返回空列表
-
-【输出格式】:
-严格按以下 JSON 格式输出,每条知识之间用逗号分隔:
-[
-  {{
-    "task": "任务场景描述",
-    "content": "核心知识内容",
-    "types": ["strategy"],
-    "score": 4
-  }},
-  {{
-    "task": "另一个任务场景",
-    "content": "另一个知识内容",
-    "types": ["tool"],
-    "score": 5
-  }}
-]
-
-如果没有知识,输出: []
-
-**注意**:只记录经过多次尝试、或经过用户指导才成功的知识,一次就成功或比较简单的经验就不要记录了。
-
-禁止输出任何解释或额外文本,只输出 JSON 数组。"""
+    prompt = MESSAGE_EXTRACT_PROMPT_TEMPLATE.format(messages_text=messages_text)
 
     try:
         print(f"\n[Extract] 正在从 {len(messages)} 条消息中提取知识...")
@@ -1986,7 +1815,7 @@ async def extract_knowledge_from_messages(extract_req: MessageExtractIn, backgro
 
             knowledge_list.append({
                 "id": knowledge_id,
-                "embedding": embedding,
+                "task_embedding": embedding,
                 "message_id": "",
                 "task": task,
                 "content": knowledge_content,

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor