Просмотр исходного кода

Merge remote-tracking branch 'refs/remotes/origin/main'

# Conflicts:
#	agent/core/runner.py
Talegorithm 1 месяц назад
Родитель
Сommit
4c7426fd5f
78 измененных файлов с 3571 добавлено и 647 удалено
  1. 42 0
      add_knowledge.py
  2. 44 0
      add_workflow_experience_knowledge.py
  3. 30 15
      agent/tools/builtin/knowledge.py
  4. 2 2
      agent/tools/builtin/librarian.py
  5. 4 0
      cache/knowledge_pending/knowledge-2026-04-09-x-portrait-workflows.json
  6. 80 0
      direct_insert.py
  7. 1 1
      examples/production_restore/config.py
  8. 11 9
      examples/production_restore/execution.prompt
  9. 0 100
      examples/production_restore/input/analysis.json
  10. 0 194
      examples/production_restore/input/pipeline.json
  11. 0 44
      examples/production_restore/input/strategy.json
  12. 0 0
      examples/production_restore/input/写生油画__img_1_制作表.json
  13. 0 0
      examples/production_restore/input/写生油画__img_2_制作表.json
  14. 0 0
      examples/production_restore/input/写生油画__img_3_制作表.json
  15. 0 0
      examples/production_restore/input/写生油画__img_4_制作表.json
  16. 0 0
      examples/production_restore/input/写生油画__img_5_制作表.json
  17. 0 0
      examples/production_restore/input/创作表.md
  18. 0 0
      examples/production_restore/input/制作点.md
  19. 0 0
      examples/production_restore/input/图片亮点.md
  20. 3 3
      examples/production_restore/presets.json
  21. 216 47
      examples/production_restore/requirement.prompt
  22. 13 0
      im-server/__main__.py
  23. 132 0
      insert_ai_workflow_capabilities.py
  24. 73 0
      insert_knowledge.py
  25. 91 0
      insert_knowledge_direct.py
  26. 1 0
      knowhub/agents/librarian.py
  27. 5 0
      knowhub/agents/librarian_agent.prompt
  28. 444 0
      knowhub/docs/knowledge-management.md
  29. 44 7
      knowhub/frontend/src/App.tsx
  30. 23 51
      knowhub/frontend/src/layouts/MainLayout.tsx
  31. 4 1
      knowhub/frontend/src/main.tsx
  32. 11 1
      knowhub/frontend/src/pages/Dashboard.tsx
  33. 133 163
      knowhub/frontend/src/pages/Relations.tsx
  34. 0 1
      knowhub/internal_tools/__init__.py
  35. 362 0
      knowhub/research/outdoor_character_workflow_summary_20260409.md
  36. 59 8
      knowhub/server.py
  37. 104 0
      knowledge-2026-04-09-ai-image-workflows.json
  38. 398 0
      knowledge/ComfyUI_画中画分阶段生成工作流.md
  39. 165 0
      knowledge/Midjourney_v7_Impasto_厚涂风格参数配置.md
  40. 261 0
      knowledge/Nano_Banana_多图融合_ComfyUI 工作流.md
  41. 20 0
      knowledge/knowledge-20260412-005015-nano-banana-image-urls.json
  42. 20 0
      knowledge/knowledge-20260412-010725-nano-banana-http503.json
  43. 3 0
      knowledge/knowledge-20260412-010726-nano-banana-multi-fusion.json
  44. 20 0
      knowledge/knowledge-20260412-014259-3abf.json
  45. 21 0
      knowledge/strategy/品类分组判断标准.json
  46. 20 0
      knowledge/strategy/粗工序因果推理方法.json
  47. 17 0
      knowledge/strategy/能力模块变体分析方法.json
  48. 9 0
      knowledge_batch_20260409.json
  49. 26 0
      knowledge_batch_20260409_execution.json
  50. 34 0
      knowledge_batch_20260410_workflow_analysis.json
  51. 51 0
      knowledge_batch_20260411_toolhub_error_recovery.json
  52. BIN
      outputs/48d6bd7b-699/nano_banana_1775894285972_0.jpg
  53. BIN
      outputs/48d6bd7b-699/nano_banana_1775894577594_0.jpg
  54. BIN
      outputs/48d6bd7b-699/nano_banana_1775894852970_0.jpg
  55. BIN
      outputs/48d6bd7b-699/nano_banana_1775895148729_0.jpg
  56. BIN
      outputs/48d6bd7b-699/nano_banana_1775895453740_0.jpg
  57. BIN
      outputs/48d6bd7b-699/nano_banana_1775895803885_0.jpg
  58. BIN
      outputs/48d6bd7b-699/nano_banana_1775896094781_0.jpg
  59. BIN
      outputs/5fd2db93-5ac/nano_banana_1775926086639_0.jpg
  60. BIN
      outputs/5fd2db93-5ac/nano_banana_1775926336258_0.jpg
  61. BIN
      outputs/5fd2db93-5ac/nano_banana_1775926561018_0.jpg
  62. BIN
      outputs/5fd2db93-5ac/nano_banana_1775927016941_0.jpg
  63. BIN
      outputs/5fd2db93-5ac/nano_banana_1775927415643_0.jpg
  64. BIN
      outputs/b7191706-5dc/nano_banana_1775929219620_0.jpg
  65. BIN
      outputs/b7191706-5dc/nano_banana_1775929481610_0.jpg
  66. BIN
      outputs/b7191706-5dc/nano_banana_1775929804307_0.jpg
  67. BIN
      outputs/b7191706-5dc/nano_banana_1775930114003_0.jpg
  68. BIN
      outputs/b7191706-5dc/nano_banana_1775930416393_0.jpg
  69. 9 0
      pending_uploads/2026-04-09_ai_image_local_detail_strategy.json
  70. 28 0
      pending_uploads/knowledge_batch_20260410_file_search.json
  71. 81 0
      pre_upload_list.json
  72. 69 0
      process_upload.py
  73. 0 0
      test-dir/__main__.py
  74. 72 0
      test_upload.py
  75. 80 0
      test_upload_sync.py
  76. 18 0
      tools/image_gen/nano_banana.json
  77. 18 0
      tools/upload/image_uploader.json
  78. 199 0
      wf_004_intents.json

+ 42 - 0
add_knowledge.py

@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+import json
+
+# 读取现有文件
+with open('/root/Agent/pre_upload_list.json', 'r', encoding='utf-8') as f:
+    data = json.load(f)
+
+# 新的知识条目
+new_knowledge = [
+    {
+        "task": "在图生图任务中,当 nano_banana 工具无法处理本地路径时的降级策略",
+        "content": "当使用 nano_banana 进行图生图时,如果传入本地图片路径(如 examples/xxx.png)会报错「Base64 decoding failed」,因为 nano_banana 期望的是 Base64 编码或网络 URL。\n\n正确做法:\n1. 先用 image_uploader 将本地图片上传获取 CDN URL\n2. 或使用支持本地路径自动上传的工具(如 flux_generate、seedream_generate)\n\n工具选择优先级:\n- nano_banana:需要手动上传获取 CDN URL,但支持多图融合,效果最好\n- flux_generate:自动处理本地路径,图生图效果好,推荐作为备选\n- seedream_generate:自动处理本地路径,需要最小尺寸 3686400 像素\n\n案例:Stage 4 检查 img_3 花丛色彩时,nano_banana 传入本地路径报错,切换至 flux_generate 成功完成修复。",
+        "types": ["experience"],
+        "tags": {
+            "intent": "图生图/色彩修复",
+            "state": "nano_banana/flux_generate"
+        },
+        "score": 4,
+        "source": {"category": "research"}
+    },
+    {
+        "task": "在图生图色彩控制中,通过 prompt 明确指定色彩排除可有效降低干扰元素饱和度",
+        "content": "当需要降低画面中某元素的色彩饱和度或改变其色系时,在 prompt 中明确指定:\n- 目标色彩:\"should be green foliage plants or very subtle low-saturation green flowers\"\n- 排除色彩:\"not purple or blue\"\n- 整体色调:\"The overall color scheme should be white and green with minimal color accents\"\n\n配合 flux-kontext-max 模型的图生图模式,可有效实现色彩调整而不破坏原有构图。\n\n案例:img_3 花丛色彩修复,将蓝紫色花丛成功调整为淡紫色/白色,融入绿色背景。",
+        "types": ["experience"],
+        "tags": {
+            "intent": "色彩控制/图生图优化",
+            "state": "flux_generate"
+        },
+        "score": 4,
+        "source": {"category": "research"}
+    }
+]
+
+# 添加到 knowledge 数组
+data['knowledge'].extend(new_knowledge)
+
+# 写回文件
+with open('/root/Agent/pre_upload_list.json', 'w', encoding='utf-8') as f:
+    json.dump(data, f, ensure_ascii=False, indent=2)
+
+print(f"成功添加 {len(new_knowledge)} 条知识条目")
+print(f"当前 knowledge 总数:{len(data['knowledge'])}")

+ 44 - 0
add_workflow_experience_knowledge.py

@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+import json
+
+# 读取现有文件
+with open('/root/Agent/pre_upload_list.json', 'r', encoding='utf-8') as f:
+    data = json.load(f)
+
+# 新的知识条目
+new_knowledge = [
+    {
+        "task": "在分析工作流文件时,当 glob 搜索找不到目标文件的有效定位策略",
+        "content": "当 glob_files 按模式匹配找不到目标文件时,应该:1) 使用 bash_command 执行 find 命令进行更广泛的文件系统搜索,支持通配符和路径遍历;2) 结合 grep 内容搜索,通过文件内容关键词定位(如工具名、方案名);3) 检查相关目录结构,如 research/outputs/ 下的子目录。案例:本次任务中 glob_files 搜索\"*wf_005*\"未找到文件,但通过 find 命令搜索包含\"ComfyUI\"\"Nano Banana\"等关键词的 markdown 文件,成功在 outputs/04/ 目录下找到颗粒感纸张纹理插画风格工作流调研报告.md,该文件即为 wf_005 的 raw_markdown 内容。",
+        "types": ["experience"],
+        "tags": {
+            "intent": "工作流文件定位",
+            "state": "文件搜索",
+            "tools": "glob_files,bash_command"
+        },
+        "score": 4,
+        "source": {"category": "research"}
+    },
+    {
+        "task": "工作流意图归纳的粒度判断原则",
+        "content": "在将工作流技术步骤归纳为意图级描述时,粒度判断应遵循:1) 保留工具差异——相同意图但不同工具实现应分开记录,因为工具选择是工作流变体的关键信息;2) 合并连续子步骤——同一工具内的连续操作(如新建图层→填充→设置混合模式)可合并为一个意图;3) 跨方案可比较——意图描述应使不同工作流的相同功能模块可对比。案例:本次将 Photoshop 的 5 种颗粒添加方法分别记录为\"添加溶解颗粒效果\"\"手绘颗粒纹理\"\"滤镜添加全局杂色\"等,保留了方法差异;而将 ComfyUI 的分割 + 重排合并为\"创建四方连续布局\"。",
+        "types": ["experience"],
+        "tags": {
+            "intent": "工作流分析",
+            "state": "意图归纳",
+            "method": "语义聚类"
+        },
+        "score": 4,
+        "source": {"category": "research"}
+    }
+]
+
+# 添加到 knowledge 数组
+data['knowledge'].extend(new_knowledge)
+
+# 写回文件
+with open('/root/Agent/pre_upload_list.json', 'w', encoding='utf-8') as f:
+    json.dump(data, f, ensure_ascii=False, indent=2)
+
+print(f"成功添加 {len(new_knowledge)} 条知识条目")
+print(f"当前 knowledge 总数:{len(data['knowledge'])}")

+ 30 - 15
agent/tools/builtin/knowledge.py

@@ -1,18 +1,7 @@
 """
 """
 知识管理工具 - KnowHub API 封装
 知识管理工具 - 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。
+所有工具通过 HTTP API 调用 KnowHub Server,直接读写底层数据库。
 """
 """
 
 
 import os
 import os
@@ -107,8 +96,6 @@ async def knowledge_search(
     """
     """
     检索知识(两阶段:语义路由 + 质量精排)
     检索知识(两阶段:语义路由 + 质量精排)
 
 
-    ⚠️ DEPRECATED: 推荐使用 ask_knowledge 工具(通过 Knowledge Manager)
-
     Args:
     Args:
         query: 搜索查询(任务描述)
         query: 搜索查询(任务描述)
         top_k: 返回数量(默认 5)
         top_k: 返回数量(默认 5)
@@ -120,7 +107,6 @@ async def knowledge_search(
     Returns:
     Returns:
         相关知识列表
         相关知识列表
     """
     """
-    logger.warning("knowledge_search is deprecated. Use ask_knowledge instead.")
     try:
     try:
         params = {
         params = {
             "q": query,
             "q": query,
@@ -739,3 +725,32 @@ async def requirement_list(
         return ToolResult(title="✅ 需求列表获取成功", output=json.dumps(data, ensure_ascii=False, indent=2))
         return ToolResult(title="✅ 需求列表获取成功", output=json.dumps(data, ensure_ascii=False, indent=2))
     except Exception as e:
     except Exception as e:
         return ToolResult(title="❌ 需求列表失败", output=str(e), error=str(e))
         return ToolResult(title="❌ 需求列表失败", output=str(e), error=str(e))
+
+# ==================== Relation (关系表) 检索工具 ====================
+
+@tool(hidden_params=["context"])
+async def relation_search(
+    table_name: str,
+    filters: Optional[Dict[str, str]] = None,
+    limit: int = 100,
+    context: Optional[ToolContext] = None,
+) -> ToolResult:
+    """通用关系表检索工具
+    Args:
+        table_name: 关系表名 (如 capability_knowledge, tool_provider 等)
+        filters: 查询条件字典 (如 {"capability_id": "xxx"})
+        limit: 返回数量限制
+    """
+    try:
+        params = {"limit": limit}
+        if filters:
+            params.update(filters)
+        async with httpx.AsyncClient(timeout=30.0) as client:
+            res = await client.get(f"{KNOWHUB_API}/api/relation/{table_name}", params=params)
+            res.raise_for_status()
+            data = res.json()
+        return ToolResult(title=f"✅ {table_name} 检索成功", output=json.dumps(data, ensure_ascii=False, indent=2))
+    except httpx.HTTPStatusError as e:
+        return ToolResult(title=f"❌ {table_name} 检索失败", output=f"HTTP Error: {e.response.text}", error=str(e))
+    except Exception as e:
+        return ToolResult(title=f"❌ {table_name} 检索失败", output=str(e), error=str(e))

+ 2 - 2
agent/tools/builtin/librarian.py

@@ -17,7 +17,7 @@ from agent.tools import tool, ToolResult, ToolContext
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
-KNOWHUB_API = os.getenv("KNOWHUB_API", "http://43.106.118.91:9999").rstrip("/")
+KNOWHUB_API = os.getenv("KNOWHUB_API", "http://localhost:9999").rstrip("/")
 
 
 
 
 @tool(
 @tool(
@@ -47,7 +47,7 @@ async def ask_knowledge(
         整合回答 + source_ids + 各 source 摘要
         整合回答 + source_ids + 各 source 摘要
     """
     """
     try:
     try:
-        async with httpx.AsyncClient(timeout=60.0) as client:
+        async with httpx.AsyncClient(timeout=300.0) as client:
             response = await client.post(
             response = await client.post(
                 f"{KNOWHUB_API}/api/knowledge/ask",
                 f"{KNOWHUB_API}/api/knowledge/ask",
                 json={
                 json={

Разница между файлами не показана из-за своего большого размера
+ 4 - 0
cache/knowledge_pending/knowledge-2026-04-09-x-portrait-workflows.json


+ 80 - 0
direct_insert.py

@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+"""
+直接插入知识到数据库的脚本
+"""
+import sys
+import os
+import uuid
+import time
+import asyncio
+from datetime import datetime, timezone
+
+sys.path.insert(0, '/root/Agent')
+
+# 设置环境变量
+os.environ['KNOWHUB_DB'] = 'gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com'
+os.environ['KNOWHUB_PORT'] = '5432'
+os.environ['KNOWHUB_USER'] = 'aiddit_aigc'
+os.environ['KNOWHUB_PASSWORD'] = '%a&&yqNxg^V1$toJ*WOa^-b^X=QJ'
+os.environ['KNOWHUB_DB_NAME'] = 'knowhub'
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+# 测试知识数据
+knowledge_data = {
+    'task': '接口测试验证条目',
+    'content': '这是一条用于验证 upload 接口是否可用的测试知识,可以删除。',
+    'types': ['experience'],
+    'tags': {'source': 'api_test'},
+    'score': 1
+}
+
+async def main():
+    print('正在连接数据库...')
+    store = PostgreSQLStore()
+
+    # 生成 ID
+    now = datetime.now(timezone.utc)
+    knowledge_id = f'knowledge-{now.strftime("%Y%m%d")}-{uuid.uuid4().hex[:8]}'
+    message_id = f'msg-{uuid.uuid4().hex[:12]}'
+
+    # 生成 embedding
+    print('正在生成 embedding...')
+    task_embedding = await get_embedding(knowledge_data['task'])
+    content_embedding = await get_embedding(knowledge_data['content'])
+
+    # 构建知识记录
+    knowledge = {
+        'id': knowledge_id,
+        'message_id': message_id,
+        'task': knowledge_data['task'],
+        'content': knowledge_data['content'],
+        'types': knowledge_data['types'],
+        'tags': knowledge_data.get('tags', {}),
+        'tag_keys': list(knowledge_data.get('tags', {}).keys()),
+        'scopes': ['org:cybertogether'],
+        'owner': 'system',
+        'source': {'category': 'execution'},
+        'eval': {'score': knowledge_data.get('score', 3)},
+        'task_embedding': task_embedding,
+        'content_embedding': content_embedding,
+        'created_at': int(time.time()),
+        'updated_at': int(time.time()),
+        'status': 'approved',
+    }
+
+    print(f'正在插入知识条目...')
+    store.insert(knowledge)
+    store.close()
+
+    print(f'✅ 成功插入知识条目!')
+    print(f'  ID: {knowledge_id}')
+    print(f'  Task: {knowledge_data["task"]}')
+    print(f'  Types: {knowledge_data["types"]}')
+    
+    return knowledge_id
+
+if __name__ == '__main__':
+    knowledge_id = asyncio.run(main())
+    print(f'\n最终知识 ID: {knowledge_id}')

+ 1 - 1
examples/production_restore/config.py

@@ -50,7 +50,7 @@ RUN_CONFIG = RunConfig(
 # ===== 任务配置 =====
 # ===== 任务配置 =====
 
 
 INPUT_DIR = "examples/production_restore/input"       # 输入目录(pipeline.json、analysis.json 等)
 INPUT_DIR = "examples/production_restore/input"       # 输入目录(pipeline.json、analysis.json 等)
-OUTPUT_DIR = "examples/production_restore/output_feature"     # 输出目录
+OUTPUT_DIR = "examples/production_restore/output_origin"     # 输出目录
 FEATURES_DIR = "examples/production_restore/features"         # 素材目录
 FEATURES_DIR = "examples/production_restore/features"         # 素材目录
 
 
 
 

+ 11 - 9
examples/production_restore/execution.prompt

@@ -18,17 +18,17 @@ $system$
 - Prompt 文本
 - Prompt 文本
 - 图生图配置(img2img_config)
 - 图生图配置(img2img_config)
 
 
-**参考源信息(raw_info 目录)**:
-在输入目录中有一个 `raw_info/` 文件夹,包含原始图片的源信息:
-- 每张图的制作表:`raw_info/写生油画__img_X_制作表.json`(对应 img_1 ~ img_5)
-- 通用创作信息:`raw_info/创作表.md`、`raw_info/制作点.md`、`raw_info/图片亮点.md`
+**参考源信息(输入目录)**:
+输入目录 `%input_dir%/` 中包含原始图片的源信息:
+- 每张图的制作表:`%input_dir%/写生油画__img_X_制作表.json`(对应 img_1 ~ img_5)
+- 通用创作信息:`%input_dir%/创作表.md`、`%input_dir%/制作点.md`、`%input_dir%/图片亮点.md`
 
 
-在构建或优化 prompt 时,**应先读取当前图片对应的制作表 JSON 和通用文件作为参考**,从中提取有价值的细节描述融入 prompt。raw_info 是辅助参考,最终 prompt 仍以任务指派中的要求为准。
+在构建或优化 prompt 时,**应先读取当前图片对应的制作表 JSON 和通用文件作为参考**,从中提取有价值的细节描述融入 prompt。这些源信息是辅助参考,最终 prompt 仍以任务指派中的要求为准。
 
 
 ### 第二步:验证素材可用性
 ### 第二步:验证素材可用性
 在执行生成前,先验证所需文件存在且可读:
 在执行生成前,先验证所需文件存在且可读:
 - 使用 `read_file` 工具检查各参考素材文件(**`read_file` 支持读取图片,会自动转为 base64 供你查看,无需打开浏览器**)
 - 使用 `read_file` 工具检查各参考素材文件(**`read_file` 支持读取图片,会自动转为 base64 供你查看,无需打开浏览器**)
-- 读取 `raw_info/` 目录下对应的制作表 JSON(如生成 img_1 则读 `写生油画__img_1_制作表.json`)和通用文件(`创作表.md`、`图片亮点.md`),提取可用于 prompt 的细节
+- 读取 `%input_dir%/` 目录下对应的制作表 JSON(如生成 img_1 则读 `写生油画__img_1_制作表.json`)和通用文件(`创作表.md`、`图片亮点.md`),提取可用于 prompt 的细节
 - 如有素材缺失,立即在结果中标注(不要自行跳过)
 - 如有素材缺失,立即在结果中标注(不要自行跳过)
 
 
 ### 第三步:通过 ToolHub 工具库执行生成
 ### 第三步:通过 ToolHub 工具库执行生成
@@ -81,19 +81,21 @@ toolhub.py 内置了 `_preprocess_params` 函数,会自动将本地路径上
 
 
 **单图评估**:传入需求文档路径和单张图片路径
 **单图评估**:传入需求文档路径和单张图片路径
 ```json
 ```json
-{"requirement_path": "%input_dir%/pipeline.json", "image_paths": "输出图片路径"}
+{"requirement_path": "%output_dir%/pipeline.json", "image_paths": "输出图片路径"}
 ```
 ```
 
 
 **多图一致性评估**:传入需求文档路径和多张图片路径列表,自动检查跨图一致性
 **多图一致性评估**:传入需求文档路径和多张图片路径列表,自动检查跨图一致性
 ```json
 ```json
-{"requirement_path": "%input_dir%/pipeline.json", "image_paths": ["img_1路径", "img_2路径", "img_3路径"]}
+{"requirement_path": "%output_dir%/pipeline.json", "image_paths": ["img_1路径", "img_2路径", "img_3路径"]}
 ```
 ```
 
 
 - 单图模式:从姿态、服装、光影、背景、材质、构图 6 个维度打分(0-10)
 - 单图模式:从姿态、服装、光影、背景、材质、构图 6 个维度打分(0-10)
 - 多图模式:从角色、服装、色调、光影、风格 5 个一致性维度打分,并逐图给分,指出不一致的图片
 - 多图模式:从角色、服装、色调、光影、风格 5 个一致性维度打分,并逐图给分,指出不一致的图片
 - 根据评估结果判断是否需要重新生成或调整参数
 - 根据评估结果判断是否需要重新生成或调整参数
 
 
-同时人工对照 output_spec 逐项检查:
+**人工对照制作表逐项检查(极其重要)**:
+
+自动评估工具只能给出维度打分,但无法判断是否符合原始制作表的具体要求。**你必须读取 `%input_dir%/` 下该图对应的制作表 JSON(如生成 img_1 则读 `写生油画__img_1_制作表.json`)**,对照其中的详细描述逐项检查:
 - 姿态是否符合需求描述(手臂位置、站/跪姿态)
 - 姿态是否符合需求描述(手臂位置、站/跪姿态)
 - 景深/散景效果是否达到要求
 - 景深/散景效果是否达到要求
 - 人物参考是否注入(发型、服装款式)
 - 人物参考是否注入(发型、服装款式)

+ 0 - 100
examples/production_restore/input/analysis.json

@@ -1,100 +0,0 @@
-{
-  "category": {
-    "name": "户外白裙写生少女",
-    "traits": [
-      "穿着白色连衣裙的女性在户外草地进行油画写生",
-      "白绿配色为主,清新自然的森系视觉基调",
-      "包含画架、画布、调色板、画笔等写生道具",
-      "逆光/轮廓光与浅景深虚化营造梦幻氛围",
-      "画布内容与现实场景形成画中画呼应结构"
-    ],
-    "quality_challenges": [
-      "人物姿态自然度:需要呈现专注绘画的动态姿态,避免僵硬",
-      "白裙质感表现:轻薄面料的垂坠感、褶皱细节和透光效果",
-      "调色板颜料质感:厚重油画的 Impasto 堆叠感和色彩混合细节",
-      "光影层次:逆光下的轮廓光晕与主体细节的平衡",
-      "景深控制:前景清晰、背景虚化的自然过渡",
-      "画中画一致性:画布上的油画内容需与现实场景形成视觉呼应",
-      "发丝细节:逆光下头发边缘的光晕效果和发丝清晰度"
-    ],
-    "reasoning": "基于图片亮点.md 的 6 个聚类主题(优雅白裙少女、斑斓颜料、写生道具、白绿配色、光影景深、画中画结构)和制作点.md 的 6 大元素权重(女性 84.5、绘画工具 78.5、自然背景 74.0、画架画布 67.67、画架油画 60.97、女性衣物 44.7),以及 5 张制作表中对姿态、服装、光影、清晰度的详细规格描述综合判断"
-  },
-  "highlight": [
-    {
-      "name": "优雅的白裙写生少女",
-      "description": "女性主体需呈现文艺气质与娴静美,包括专注的绘画姿态(身体前倾、手臂抬起握笔)、自然垂坠的白色长裙(V 字露背或圆领、腰部系带收腰、裙摆飘逸)、棕色长发(自然披散、逆光下边缘光晕),整体塑造写生缪斯形象",
-      "reasoning": "图片亮点.md 中该聚类覆盖全部 5 张图片,制作点.md 中'女性'元素综合权重 84.5 为最高,5 张制作表中人物段落评分均在 0.745-0.858 之间,是画面核心主体",
-      "references": ["descriptions/图片亮点.md", "descriptions/制作点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.1", "descriptions/写生油画__img_2_制作表.json#段落 2.1", "descriptions/写生油画__img_3_制作表.json#段落 3.1", "descriptions/写生油画__img_4_制作表.json#段落 4.1", "descriptions/写生油画__img_5_制作表.json#段落 5.1"]
-    },
-    {
-      "name": "斑斓厚重的油画颜料",
-      "description": "木质调色板上堆积的厚重油画颜料(Impasto),呈不规则块状分布,色彩鲜艳杂乱(绿色、蓝色、红色、黄色、白色、紫色等至少 10 种颜色),颜料具有膏状质感和明显堆叠厚度,与周围大面积纯白衣物形成强烈视觉反差",
-      "reasoning": "图片亮点.md 专门聚焦该实质物体,制作点.md 中'绘画工具'权重 78.5 排第二,img_1 和 img_5 制作表中调色板颜料细节描述详尽(颜色种类、分布模式、质地),img_5 中颜料段落评分达 0.754",
-      "references": ["descriptions/图片亮点.md", "descriptions/制作点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.1.2.3", "descriptions/写生油画__img_5_制作表.json#段落 5.1.3.1"]
-    },
-    {
-      "name": "清新雅致的白绿配色",
-      "description": "大面积高饱和度自然草木绿背景(鲜绿色草地、深浅不一的绿色树木)与人物纯白服装形成鲜明对比,确立清新、自然、治愈的森系视觉基调,色彩饱和度中等偏高但不刺眼",
-      "reasoning": "图片亮点.md 中该形式类聚类强调色彩构成的形式美感,覆盖 img_1 和 img_4,5 张制作表的背景段落均描述'以绿色为主',色彩形式评分普遍较高",
-      "references": ["descriptions/图片亮点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.3", "descriptions/写生油画__img_4_制作表.json#段落 4.3"]
-    },
-    {
-      "name": "唯美梦幻的光影与景深",
-      "description": "温暖的逆光/轮廓光从画面左上方或右上方照射,人物和物体边缘形成明显光晕;大光圈浅景深虚化(Bokeh)使背景呈现柔和散景,前景主体清晰锐利(头发丝、服装纹理、手部细节可见),营造脱离现实的梦幻浪漫氛围",
-      "reasoning": "图片亮点.md 中该形式类聚类覆盖 img_2、img_3、img_5,5 张制作表的形式段落均描述'逆光'或'自然光从左上方/右上方照射',清晰度描述均为'前景清晰、背景虚化',img_2 特别强调'强烈光斑和光晕效果'",
-      "references": ["descriptions/图片亮点.md", "descriptions/写生油画__img_2_制作表.json#段落 2 形式", "descriptions/写生油画__img_3_制作表.json#段落 3 形式", "descriptions/写生油画__img_5_制作表.json#段落 5 形式"]
-    },
-    {
-      "name": "虚实呼应的画中画结构",
-      "description": "画架上的画布内容与现实场景形成'镜像'或'互文'关系——画布上描绘身穿白色裙子的女性在户外草地/花丛中的背影,与现实中的画家形成视觉呼应,构建增加叙事深度的画中画结构,画布采用印象派风格(笔触粗犷、色彩鲜明)",
-      "reasoning": "图片亮点.md 中该形式类聚类覆盖 img_1 和 img_2,img_1、img_2、img_3 制作表中画布段落均详细描述'描绘了一名背对镜头的女性',内容主题与现实场景呼应,画布段落评分高达 0.891-0.958",
-      "references": ["descriptions/图片亮点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.2.1", "descriptions/写生油画__img_2_制作表.json#段落 2.2.1", "descriptions/写生油画__img_3_制作表.json#段落 3.2.1"]
-    }
-  ],
-  "baseline": [
-    {
-      "name": "人物姿态自然度",
-      "description": "女性需呈现专注绘画的动态姿态(身体略微前倾、右臂抬起握笔、左臂弯曲持调色板、头部转向画布),避免僵硬呆板的摆拍感",
-      "why_critical": "制作表中 5 张图的人物姿态描述均强调'专注绘画的动态姿态',姿态不自然会导致人物像假人模特,破坏'优雅写生少女'的核心亮点",
-      "reasoning": "img_1 制作表#段落 1.1.2 描述'身体略微前倾,右臂抬起握持画笔,左臂弯曲握持调色板',img_2、img_3、img_4 均有类似姿态描述,姿态形式评分在 0.455-0.765 之间",
-      "references": ["descriptions/写生油画__img_1_制作表.json#段落 1.1.2", "descriptions/写生油画__img_2_制作表.json#段落 2.1.2", "descriptions/写生油画__img_3_制作表.json#段落 3.1.2", "descriptions/写生油画__img_4_制作表.json#段落 4.1.2"]
-    },
-    {
-      "name": "白裙材质与褶皱真实感",
-      "description": "白色长裙需呈现轻薄棉麻或丝绸质地的垂坠感和透气性,有自然形成的垂坠褶皱(裙摆、腰部、背部 V 领处),服装颜色为纯白色无图案",
-      "why_critical": "制作点.md 中'女性衣物'权重 44.7,5 张制作表的服装段落评分均高达 0.806-0.847,材质和褶皱描述详尽,做不好会导致服装像塑料或纸片,一眼假",
-      "reasoning": "img_1 制作表#段落 1.1.2.1 描述'轻薄的棉麻或丝绸质地,具有良好的垂坠感',img_2、img_3、img_4 均有'丝绸或棉麻混纺材质,表面光滑,有轻微光泽感'等描述",
-      "references": ["descriptions/制作点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.1.2.1", "descriptions/写生油画__img_2_制作表.json#段落 2.1.2.1", "descriptions/写生油画__img_3_制作表.json#段落 3.1.2.1", "descriptions/写生油画__img_4_制作表.json#段落 4.1.2.1"]
-    },
-    {
-      "name": "调色板颜料质感",
-      "description": "调色板上的颜料需呈现膏状堆积的厚度和立体感,表面有光泽显示湿润质地,颜色种类丰富(至少 10 种),呈不规则块状和条状分布,部分颜色相互混合形成过渡色",
-      "why_critical": "图片亮点.md 专门将'斑斓厚重的油画颜料'列为实质类亮点,img_5 制作表中颜料段落评分 0.754,质感描述详尽,做不好会像平面贴图而非真实颜料",
-      "reasoning": "img_5 制作表#段落 5.1.3.1 详细描述'颜料呈膏状,堆积在调色板表面,具有一定的厚度和立体感,表面有光泽,显示出湿润的质地',img_1 也有'颜料堆叠感明显'描述",
-      "references": ["descriptions/图片亮点.md", "descriptions/写生油画__img_1_制作表.json#段落 1.1.2.3", "descriptions/写生油画__img_5_制作表.json#段落 5.1.3.1"]
-    },
-    {
-      "name": "前景主体清晰度",
-      "description": "人物主体(头发丝、服装纹理、手部细节)、调色板(木质纹理、颜料堆叠)、画架需清晰锐利,细节可见,与虚化背景形成对比",
-      "why_critical": "5 张制作表的形式段落均强调'前景人物和画架清晰锐利',清晰度形式评分普遍较高,做不好会导致画面整体模糊,失去专业摄影质感",
-      "reasoning": "img_1 制作表#段落 1 形式描述'画面中心区域清晰度高,细节锐利可见',img_2、img_3、img_4、img_5 均有类似描述,清晰度是所有制作表的必选形式参数",
-      "references": ["descriptions/写生油画__img_1_制作表.json#段落 1 形式", "descriptions/写生油画__img_2_制作表.json#段落 2 形式", "descriptions/写生油画__img_3_制作表.json#段落 3 形式", "descriptions/写生油画__img_4_制作表.json#段落 4 形式", "descriptions/写生油画__img_5_制作表.json#段落 5 形式"]
-    },
-    {
-      "name": "背景虚化自然度",
-      "description": "背景树木和草地需呈现柔和的虚化效果(浅景深),与前景清晰主体形成自然过渡,虚化区域有光斑效果(尤其是逆光场景)",
-      "why_critical": "图片亮点.md 将'唯美梦幻的光影与景深'列为形式类亮点,5 张制作表均描述'景深较浅,背景虚化',做不好会导致画面缺乏层次感和专业感",
-      "reasoning": "img_2 制作表#段落 2.3 描述'背景整体呈现柔和的虚化效果,景深较浅,树木轮廓模糊,光斑明显',所有制作表的背景段落均有景深描述",
-      "references": ["descriptions/图片亮点.md", "descriptions/写生油画__img_2_制作表.json#段落 2.3", "descriptions/写生油画__img_3_制作表.json#段落 3.3", "descriptions/写生油画__img_5_制作表.json#段落 5.3"]
-    }
-  ],
-  "requirement_summary": [
-    "高质量人物生成:白裙女性、自然绘画姿态、棕色长发、逆光轮廓光",
-    "服装材质还原:轻薄棉麻/丝绸质地、垂坠褶皱、纯白色",
-    "调色板与颜料:木质调色板、厚重油画颜料 Impasto 质感、多色混合",
-    "光影效果:逆光/轮廓光、温暖光晕、前景清晰背景虚化",
-    "画中画结构:画布上油画内容需与现实场景呼应、印象派风格",
-    "自然背景:高饱和度绿色草地和树木、清新森系色调",
-    "道具细节:画架结构、画笔、白玫瑰点缀"
-  ]
-}

+ 0 - 194
examples/production_restore/input/pipeline.json

@@ -1,194 +0,0 @@
-{
-  "project": "户外白裙写生少女图像还原",
-  "category": "户外白裙写生少女",
-  "total_images": 5,
-  "stages": {
-    "stage_1": {
-      "name": "链式图生图生成",
-      "status": "pending",
-      "description": "以 features 目录中的参考素材为底图,按顺序逐张生成。从 img_1 开始,每张图生成后,将结果作为下一张图的额外参考(chain_from),确保角色、服装、色调的跨图一致性。必须严格按 img_1 → img_2 → img_3 → img_4 → img_5 的顺序串行执行,不可并行",
-      "images": ["img_1", "img_2", "img_3", "img_4", "img_5"],
-      "execution_order": "sequential_chain",
-      "tool_priority": ["nano_banana", "flux_generate", "seedream_generate"],
-      "fallback": "仅当端到端工具效果不足时,才考虑使用 liblibai_controlnet"
-    },
-    "stage_2": {
-      "name": "迭代优化",
-      "status": "pending",
-      "description": "以 Stage 1 生成的图像作为底图,再次图生图迭代优化,增强材质、光影、细节的真实感"
-    },
-    "stage_3": {
-      "name": "跨图一致性检查",
-      "status": "pending",
-      "description": "对比 5 张生成图,检查角色、服装、色彩、光影的一致性。对不一致的图以当前结果为底图重新图生图优化"
-    },
-    "stage_4": {
-      "name": "细节修复与输出",
-      "status": "pending",
-      "description": "逐项检查并修复细节问题,以当前结果为底图进行定向图生图修复,输出最终成品"
-    }
-  },
-  "images": {
-    "img_1": {
-      "required_spec": {
-        "pose": "侧身背对,站姿,身体前倾,右臂抬起握笔,左臂持调色板",
-        "服装": "白色长裙,V 字露背,腰部系带,轻薄棉麻质地,垂坠褶皱",
-        "光影": "逆光从左上方照射,轮廓光晕,发丝金边",
-        "背景": "高饱和度绿色草地和树木,浅景深虚化",
-        "道具": "木质画架,白色画布(有画中画内容),木质调色板(多色颜料)",
-        "画中画": "画布上描绘背对镜头的女性 + 草地 + 蓝花,印象派风格"
-      },
-      "input_from": {
-        "character_ref": "character_asset/character_ref_img1.png",
-        "background_ref": "background_asset/background_green_img1.png",
-        "palette_ref": "palette_asset/palette_impasto_img1_v2.png"
-      },
-      "img2img_config": {
-        "base_image": "character_ref",
-        "reference_images": ["background_ref", "palette_ref"],
-        "chain_from": null,
-        "note": "第 1 张图,无前序结果。以人物参考图为底图做图生图,prompt 中融合背景、道具、光影描述。此图确立角色基准外观"
-      },
-      "prompt": "一位穿着纯白色长裙的女性在户外草地写生,身体略微前倾专注绘画,右手持画笔左手持调色板,棕色长发自然披散在背部,轻薄棉麻质地的白色长裙,V 字露背设计,腰部系带收腰,裙摆自然垂坠有飘逸感,温暖的逆光从左上方照射,人物边缘形成金色轮廓光,发丝呈现明亮光晕,全画幅 85mm 人像定焦镜头,光圈 f/1.8 大光圈,浅景深效果,前景人物清晰锐利,背景草地和树木柔和虚化,高饱和度自然草木绿背景,纯白服装与绿色形成鲜明对比,清新森系色调,真实摄影风格",
-      "negative_prompt": "AI 假人感,塑料质感,过度修饰,模糊,低质量,变形,cgi,3d 渲染,平滑无纹理",
-      "output_spec": {
-        "file": "img_1_restored_v1.png",
-        "resolution": "1024x1365",
-        "critical_checks": ["人物姿态自然度", "白裙材质与褶皱真实感", "前景主体清晰度"],
-        "high_checks": ["逆光轮廓光效果", "背景虚化自然度", "调色板颜料质感"]
-      }
-    },
-    "img_2": {
-      "required_spec": {
-        "pose": "背对,逆光,半剪影效果,身体略微右倾",
-        "服装": "白色长裙,轻薄材质,逆光下透光效果",
-        "光影": "强烈逆光,明显光斑和光晕效果,发丝金边",
-        "背景": "绿色树木,柔和虚化,圆形光斑 (bokeh)",
-        "道具": "木质画架,白色画布(有画中画内容),调色板",
-        "画中画": "画布上描绘背对女性 + 植物 + 花朵 + 光斑,印象派风格"
-      },
-      "input_from": {
-        "character_ref": "character_asset/character_ref_back.png",
-        "background_ref": "background_asset/background_bokeh_img2.png"
-      },
-      "img2img_config": {
-        "base_image": "character_ref",
-        "reference_images": ["background_ref"],
-        "chain_from": "img_1",
-        "note": "以背影参考图为底图,同时将 img_1 的生成结果作为参考图传入,保持角色一致性。prompt 中强化逆光和散景效果"
-      },
-      "prompt": "一位穿着纯白色长裙的女性在户外草地写生,背对镜头,身体略微右倾,强烈逆光效果,人物呈半剪影,发丝边缘明亮金边,轻薄白裙在逆光下呈现透光效果,木质画架和画布,调色板,背景绿色树木柔和虚化,圆形光斑效果,梦幻浪漫氛围,85mm 镜头,f/1.8 大光圈,真实摄影风格",
-      "negative_prompt": "AI 假人感,塑料质感,过度修饰,模糊,低质量,变形,cgi,3d 渲染,光线平淡",
-      "output_spec": {
-        "file": "img_2_restored_v1.png",
-        "resolution": "1024x1365",
-        "critical_checks": ["逆光轮廓光效果", "人物姿态自然度", "前景主体清晰度"],
-        "high_checks": ["背景光斑效果", "白裙透光感", "发丝金边细节"]
-      }
-    },
-    "img_3": {
-      "required_spec": {
-        "pose": "跪坐姿态,身体前倾,左臂靠近地面调色板",
-        "服装": "白色长裙,跪坐时裙摆自然铺展",
-        "光影": "自然光从左上方照射,柔和轮廓光",
-        "背景": "绿色草地,蓝紫色花丛,浅景深",
-        "道具": "木质画架,白色画布(有画中画内容),地面调色板",
-        "画中画": "画布上描绘白裙女性 + 花丛 + 遮阳伞,印象派风格"
-      },
-      "input_from": {
-        "character_ref": "character_asset/character_ref_kneel.png",
-        "background_ref": "background_asset/background_green_img1.png"
-      },
-      "img2img_config": {
-        "base_image": "character_ref",
-        "reference_images": ["background_ref"],
-        "chain_from": "img_2",
-        "note": "以跪姿参考图为底图,将 img_2 的结果作为参考保持角色一致性"
-      },
-      "prompt": "一位穿着纯白色长裙的女性跪坐在户外草地写生,身体前倾专注绘画,左臂靠近地面调色板,棕色长发自然披散,轻薄棉麻质地白裙,裙摆自然铺展在草地上,自然光从左上方照射,柔和轮廓光,背景绿色草地和蓝紫色花丛,浅景深虚化,85mm 镜头,f/1.8 光圈,清新森系色调,真实摄影风格",
-      "negative_prompt": "AI 假人感,塑料质感,过度修饰,模糊,低质量,变形,cgi,3d 渲染,姿态僵硬",
-      "output_spec": {
-        "file": "img_3_restored_v1.png",
-        "resolution": "1024x1365",
-        "critical_checks": ["跪坐姿态自然度", "白裙材质与褶皱真实感", "前景主体清晰度"],
-        "high_checks": ["花丛背景虚化", "裙摆铺展效果", "光影柔和度"]
-      }
-    },
-    "img_4": {
-      "required_spec": {
-        "pose": "侧身面对镜头,仰望,右臂抬起握笔",
-        "服装": "白色长裙,侧身视角可见服装轮廓",
-        "光影": "自然光照射,柔和光影",
-        "背景": "高饱和度绿色草地和树木,浅景深",
-        "道具": "木质画架,空白白色画布(无内容),调色板",
-        "画中画": "空白画布,纯白色表面,无颜料痕迹"
-      },
-      "input_from": {
-        "character_ref": "character_asset/character_ref_side.png",
-        "background_ref": "background_asset/background_green_img4.png",
-        "easel_ref": "easel_asset/easel_blank_canvas_img4.png"
-      },
-      "img2img_config": {
-        "base_image": "character_ref",
-        "reference_images": ["background_ref", "easel_ref"],
-        "chain_from": "img_3",
-        "note": "以侧面参考图为底图,将 img_3 的结果作为参考保持角色一致性。prompt 中强化'空白画布无内容'"
-      },
-      "prompt": "一位穿着纯白色长裙的女性侧身站立面对镜头,头部仰望向左上方,右臂抬起握持画笔,左臂持调色板,轻薄棉麻质地白裙,自然光照射,柔和光影,背景高饱和度绿色草地和树木,浅景深虚化,木质画架上有空白白色画布,纯白色表面无颜料痕迹,85mm 镜头,f/1.8 光圈,清新森系色调,真实摄影风格",
-      "negative_prompt": "AI 假人感,塑料质感,过度修饰,模糊,低质量,变形,cgi,3d 渲染,画布上有内容,颜料痕迹",
-      "output_spec": {
-        "file": "img_4_restored_v1.png",
-        "resolution": "1024x1365",
-        "critical_checks": ["人物姿态自然度", "白裙材质真实感", "空白画布纯白无内容"],
-        "high_checks": ["仰望表情自然", "侧身轮廓清晰", "背景虚化"]
-      }
-    },
-    "img_5": {
-      "required_spec": {
-        "pose": "特写镜头,聚焦手部和调色板,上半身可见",
-        "服装": "白色长裙,特写可见材质细节",
-        "光影": "温暖逆光,调色板颜料光泽",
-        "背景": "绿色背景,强烈虚化",
-        "道具": "木质调色板(多色厚重颜料 Impasto 质感),画笔",
-        "画中画": "画布部分可见于背景,绿蓝色调,笔触粗犷"
-      },
-      "input_from": {
-        "character_ref": "character_asset/character_ref_main.png",
-        "palette_ref": "palette_asset/palette_impasto_img1_v2.png"
-      },
-      "img2img_config": {
-        "base_image": "character_ref",
-        "reference_images": ["palette_ref"],
-        "chain_from": "img_4",
-        "note": "以人物正面参考图为底图,将 img_4 的结果作为参考保持角色一致性。prompt 中强化特写镜头和 Impasto 颜料质感"
-      },
-      "prompt": "特写镜头,聚焦女性双手握持木质调色板和画笔,调色板上有厚重油画颜料 Impasto 质感,膏状堆积,多色混合(绿色、蓝色、红色、黄色、白色、紫色),颜料表面湿润光泽,立体厚度可见,女性穿着纯白色长裙,上半身可见,温暖逆光照射,颜料光泽反射,背景绿色强烈虚化,85mm 微距镜头,f/1.8 光圈,真实摄影风格",
-      "negative_prompt": "AI 假人感,塑料质感,平滑无纹理,模糊,低质量,变形,cgi,3d 渲染,颜料扁平无厚度",
-      "output_spec": {
-        "file": "img_5_restored_v1.png",
-        "resolution": "1024x1365",
-        "critical_checks": ["调色板颜料 Impasto 质感", "颜料湿润光泽", "特写清晰度"],
-        "high_checks": ["多色颜料混合", "手部细节", "背景虚化"]
-      }
-    }
-  },
-  "consistency_checks": {
-    "stage_3": {
-      "character_consistency": "5 张图中女性角色面部特征、发型、肤色应保持一致",
-      "costume_consistency": "白色长裙的材质、款式、颜色应 100% 一致(V 字露背、腰部系带、轻薄棉麻)",
-      "color_scheme": "白绿配色方案贯穿所有图像,绿色饱和度中等偏高,纯白服装",
-      "lighting_consistency": "逆光/轮廓光方向一致(左上方),光晕效果统一",
-      "style_consistency": "真实摄影风格,85mm 镜头,f/1.8 景深效果统一"
-    }
-  },
-  "repair_items": {
-    "stage_4": [
-      {"item": "人物面部细节", "check": "肌肤纹理清晰,无 AI 假人感", "repair_tool": "图生图局部重绘"},
-      {"item": "服装褶皱", "check": "白裙垂坠褶皱自然,材质真实", "repair_tool": "图生图局部重绘"},
-      {"item": "调色板颜料", "check": "Impasto 质感明显,膏状堆积,湿润光泽", "repair_tool": "图生图局部重绘"},
-      {"item": "画布内容", "check": "画中画结构清晰,印象派风格", "repair_tool": "图生图局部重绘"},
-      {"item": "光影效果", "check": "逆光轮廓光自然,发丝金边清晰", "repair_tool": "图生图局部重绘"},
-      {"item": "背景虚化", "check": "浅景深效果自然,光斑柔和", "repair_tool": "图生图局部重绘"}
-    ]
-  }
-}

+ 0 - 44
examples/production_restore/input/strategy.json

@@ -1,44 +0,0 @@
-{
-  "selected_strategy": {
-    "name": "素材驱动端到端工作流:参考素材图生图 → 迭代优化 → 一致性检查 → 细节修复",
-    "workflow_outline": [
-      "阶段 1 - 素材驱动图生图:以 features 中的参考素材(人物参考图)为底图传入 image_url,配合详细 prompt 描述服装材质、光影效果、相机参数,通过 nano_banana / flux_generate / seedream_generate 从头生成图像",
-      "阶段 2 - 迭代优化:以阶段 1 生成结果为底图再次图生图,增强材质、光影、细节真实感",
-      "阶段 3 - 跨图一致性检查:对比 5 张生成图的角色、服装、色彩、光影一致性,对不一致的图重新图生图优化",
-      "阶段 4 - 细节修复与输出:以当前结果为底图,针对局部细节(面部、褶皱、颜料质感等)进行定向图生图修复"
-    ],
-    "tool_priority": [
-      "nano_banana — 首选,速度快、效果好",
-      "flux_generate — 备选,支持 image_url 图生图",
-      "seedream_generate — 备选,支持 image_url 图生图",
-      "liblibai_controlnet — 最后回退,仅当需要精细姿态/边缘/深度控制且端到端工具效果不足时使用"
-    ],
-    "highlight_coverage": [
-      "优雅的白裙写生少女:端到端图生图保留原型构图 + 增强材质真实感",
-      "斑斓厚重的油画颜料:通过 prompt 强化 Impasto 质感描述 + palette_ref 素材辅助",
-      "清新雅致的白绿配色:prompt 中控制色彩 + background_ref 素材引导",
-      "唯美梦幻的光影与景深:prompt 中描述逆光轮廓光 + 相机参数(85mm、f/1.8)",
-      "虚实呼应的画中画结构:prompt 中同时描述现实场景和画布内容的镜像关系"
-    ],
-    "baseline_coverage": [
-      "人物姿态自然度:以原型图为底图保留姿态,端到端工具增强真实感",
-      "白裙材质与褶皱真实感:prompt 中详细描述布料材质(棉麻、垂坠、透光)",
-      "调色板颜料质感:prompt 强化 Impasto 描述 + palette_ref 素材参考",
-      "前景主体清晰度:prompt 中描述相机参数确保主体锐利",
-      "背景虚化自然度:prompt 中描述 f/1.8 大光圈、浅景深效果"
-    ],
-    "reasoning": "该策略的核心优势:1)端到端图生图工具(nano_banana/flux/seedream)通过 ToolHub 直接可用,无需额外配置;2)以原型图为底图可保留已验证的构图和姿态,只增强真实感;3)参考素材(人物、背景、道具)提供风格引导;4)全流程使用 ToolHub 工具,链路简短、执行效率高;5)仅在端到端工具效果不足时才回退到 ControlNet,避免不必要的复杂度"
-  },
-  "vs_alternatives": [
-    {
-      "alternative": "ControlNet 精确控制方案",
-      "why_not": "需要先提取控制信号(骨骼图、深度图、边缘图),当前素材中不包含这些文件;链路更长、配置更复杂;对于本项目已有原型图+参考素材的情况,端到端图生图更直接高效",
-      "could_switch_if": "端到端工具无法精确控制姿态或构图细节;需要逐像素级别的结构控制"
-    },
-    {
-      "alternative": "纯文生图方案(不使用底图)",
-      "why_not": "放弃已有原型图的构图和姿态信息,每次从零生成;一致性难以保证;浪费已有资源",
-      "could_switch_if": "原型图质量太差无法作为底图使用"
-    }
-  ]
-}

+ 0 - 0
examples/production_restore/input/raw_info/写生油画__img_1_制作表.json → examples/production_restore/input/写生油画__img_1_制作表.json


+ 0 - 0
examples/production_restore/input/raw_info/写生油画__img_2_制作表.json → examples/production_restore/input/写生油画__img_2_制作表.json


+ 0 - 0
examples/production_restore/input/raw_info/写生油画__img_3_制作表.json → examples/production_restore/input/写生油画__img_3_制作表.json


+ 0 - 0
examples/production_restore/input/raw_info/写生油画__img_4_制作表.json → examples/production_restore/input/写生油画__img_4_制作表.json


+ 0 - 0
examples/production_restore/input/raw_info/写生油画__img_5_制作表.json → examples/production_restore/input/写生油画__img_5_制作表.json


+ 0 - 0
examples/production_restore/input/raw_info/创作表.md → examples/production_restore/input/创作表.md


+ 0 - 0
examples/production_restore/input/raw_info/制作点.md → examples/production_restore/input/制作点.md


+ 0 - 0
examples/production_restore/input/raw_info/图片亮点.md → examples/production_restore/input/图片亮点.md


+ 3 - 3
examples/production_restore/presets.json

@@ -5,7 +5,7 @@
     "skills": ["planning"],
     "skills": ["planning"],
     "prompt_vars": {
     "prompt_vars": {
       "input_dir": "examples/production_restore/input",
       "input_dir": "examples/production_restore/input",
-      "output_dir": "examples/production_restore/output_feature",
+      "output_dir": "examples/production_restore/output_origin",
       "features_dir": "examples/production_restore/features"
       "features_dir": "examples/production_restore/features"
     },
     },
     "description": "Business Agent - 决策循环编排器,驱动 goal → evaluate → decide → dispatch 循环"
     "description": "Business Agent - 决策循环编排器,驱动 goal → evaluate → decide → dispatch 循环"
@@ -17,7 +17,7 @@
     "skills": ["planning"],
     "skills": ["planning"],
     "prompt_vars": {
     "prompt_vars": {
       "input_dir": "examples/production_restore/input",
       "input_dir": "examples/production_restore/input",
-      "output_dir": "examples/production_restore/output_feature",
+      "output_dir": "examples/production_restore/output_origin",
       "features_dir": "examples/production_restore/features"
       "features_dir": "examples/production_restore/features"
     },
     },
     "description": "Craftsman - 单步执行专家,调用 ToolHub 工具执行图像生成"
     "description": "Craftsman - 单步执行专家,调用 ToolHub 工具执行图像生成"
@@ -28,7 +28,7 @@
     "temperature": 0.3,
     "temperature": 0.3,
     "skills": ["planning"],
     "skills": ["planning"],
     "prompt_vars": {
     "prompt_vars": {
-      "output_dir": "examples/production_restore/output_feature"
+      "output_dir": "examples/production_restore/output_origin"
     },
     },
     "description": "Researcher - 外部知识获取专家,搜索线上教程和案例"
     "description": "Researcher - 外部知识获取专家,搜索线上教程和案例"
   }
   }

+ 216 - 47
examples/production_restore/requirement.prompt

@@ -13,6 +13,7 @@ $system$
 | 角色 | 调用方式 | 职责 |
 | 角色 | 调用方式 | 职责 |
 |------|---------|------|
 |------|---------|------|
 | **Librarian** | `ask_knowledge(query=...)` | 内部知识顾问,基于 KnowHub 已有知识给出方案建议 |
 | **Librarian** | `ask_knowledge(query=...)` | 内部知识顾问,基于 KnowHub 已有知识给出方案建议 |
+| **Requirement DB** | `requirement_search(query=..., top_k=20)` | 需求库检索,查找已有的制作需求 |
 | **Craftsman** | `agent(task=..., agent_type="craftsman")` | 单步执行专家,调用 ToolHub 工具执行图像生成 |
 | **Craftsman** | `agent(task=..., agent_type="craftsman")` | 单步执行专家,调用 ToolHub 工具执行图像生成 |
 | **Researcher** | `agent(task=..., agent_type="researcher")` | 外部知识获取,搜索线上教程和案例 |
 | **Researcher** | `agent(task=..., agent_type="researcher")` | 外部知识获取,搜索线上教程和案例 |
 | **evaluate** | `evaluate_image(requirement_path=..., image_paths=...)` | 质量评估工具,对照需求打分 |
 | **evaluate** | `evaluate_image(requirement_path=..., image_paths=...)` | 质量评估工具,对照需求打分 |
@@ -22,7 +23,7 @@ $system$
 - 素材目录(参考图等素材):`%features_dir%`
 - 素材目录(参考图等素材):`%features_dir%`
 - 输出目录:`%output_dir%`
 - 输出目录:`%output_dir%`
 
 
-pipeline.json 中 `input_from` 字段的路径均相对于素材目录。**指派任务时必须将其展开为完整路径**,传给 Craftsman 的路径必须以 `%features_dir%/` 开头。
+`%output_dir%/pipeline.json` 中 `base_image` 和 `reference_images` 的路径必须以 `%features_dir%/` 开头。
 
 
 ### 素材目录结构
 ### 素材目录结构
 ```
 ```
@@ -51,68 +52,236 @@ features/
 
 
 ## 工作流程
 ## 工作流程
 
 
-### 第一步:读取还原方案
-读取 `%input_dir%/pipeline.json`,提取整体结构和每张图的规格。
+### 第零步:需求分析(从内容描述提取制作需求)
 
 
-### 第二步:问策 Librarian
-在开始生成前,先向 Librarian 咨询:
-```
-ask_knowledge(query="户外白裙写生少女图像还原,使用端到端图生图工具(nano_banana/flux/seedream),需要保持5张图的角色一致性,有什么推荐方案?")
-```
-- Librarian 会返回 KnowHub 中已有的策略经验、工具评估、工作流总结
-- 如果 Librarian 回复"知识不足"或建议不够具体 → 派发 Researcher 调研
+在开始执行前,先从输入目录的内容描述文件中提取制作层需求。
+
+**1. 读取内容描述文件**
+
+读取 `%input_dir%` 下的核心描述文件(文件名可能略有差异,按实际存在的读取):
+- `制作点.md`:核心制作元素及权重
+- `图片亮点.md` 或 `制作亮点.md`:视觉亮点聚类
+- `创作表.md`:创作视角描述(可选)
+- `*_制作表.json`:各图的详细制作表(按需抽查 1-2 个了解细节)
+
+**2. 检索已有需求**
 
 
-### 第三步:按需调研
-如果 Librarian 的知识不足以决策:
+用 `requirement_search` 对每个核心制作元素和亮点主题分别检索需求库,了解已有哪些相关需求:
 ```
 ```
-agent(task="调研 nano_banana 图生图的最佳实践,特别是如何保持多图角色一致性...", agent_type="researcher")
+requirement_search(query="人物写生 白裙女性 角色一致性", top_k=20)
+requirement_search(query="油画颜料质感 Impasto厚涂", top_k=20)
+requirement_search(query="户外自然背景 逆光散景 浅景深虚化", top_k=20)
+```
+- **分词搜索**:不要把所有关键词拼成一个 query,而是对每个核心制作元素、亮点主题分别搜索
+- 对制作点中权重最高的元素、亮点中覆盖图片最多的聚类,分别发起检索
+- 了解已有哪些需求,避免重复提取;已有需求可直接引用而不重复创建
+
+**3. 提取制作需求**
+
+站在图片制作者的角度,从内容描述中提取"用 AI 图像工具制作这组内容时,需要实现什么视觉效果":
+- **从制作点出发**:按权重从高到低,识别需要 AI 工具还原的核心视觉元素
+- **从亮点出发**:识别图组中必须保持高表现力的视觉特征,转化为制作需求
+- **从制作表验证**:抽查具体图片的制作表,确认需求的具体性和可行性
+- **合并同类**:将指向同一视觉能力的元素和亮点合并为一个需求
+- **对比已有**:将提取结果与检索到的已有需求对比,标注哪些是新需求、哪些已存在
+
+**注意**:
+- 需求描述最终的视觉呈现效果,不要使用技术术语(如"ControlNet""LoRA"等)
+- 多个制作元素或亮点指向同一类视觉效果时,合并为一个需求
+- 每组内容的需求数量一般为 3-8 个
+
+**4. 保存需求文件**
+
+将提取结果保存到 `%output_dir%/requirements.json`。
+
+**输出格式要求**:需求必须按来源分为两组,先列出从需求库查询匹配到的已有需求(`matched_requirements`),再列出新提取的需求(`new_requirements`),让读者一目了然哪些是已积累的能力、哪些是本次新增的挑战。
+
+每个需求采用"抽象需求 + 具象点"的结构:
+- **抽象需求(abstract)**:概括性的视觉能力描述,如"保持多图人物一致性"
+- **具象点(concrete_points)**:该抽象需求下的具体视觉表现要求,如"白裙轮廓在不同角度下保持一致""发丝细节和耳饰在各图中统一"
+
+```json
+{
+  "content_name": "内容名称(从创作表或文件夹名获取)",
+  "matched_requirements": [
+    {
+      "abstract": "抽象需求描述(与需求库中已有需求匹配)",
+      "concrete_points": [
+        "具象点1:来自需求库的已有要求",
+        "具象点2:本次内容补充的新要求(如有)"
+      ],
+      "priority": "high | medium | low",
+      "db_match_id": "需求库中匹配到的需求 ID",
+      "match_confidence": "exact | partial",
+      "delta_from_db": "与需求库原始需求相比,本次新增或调整的具象点说明(无新增则为 null)",
+      "source_elements": ["制作点名称或亮点名称"],
+      "reasoning": "为什么这是关键制作需求,做不好会怎样"
+    }
+  ],
+  "new_requirements": [
+    {
+      "abstract": "抽象需求描述(需求库中无匹配,从内容描述中新提取)",
+      "concrete_points": [
+        "具象点1:具体的视觉表现要求",
+        "具象点2:具体的视觉表现要求"
+      ],
+      "priority": "high | medium | low",
+      "source_elements": ["制作点名称或亮点名称"],
+      "reasoning": "为什么这是关键制作需求,做不好会怎样"
+    }
+  ]
+}
 ```
 ```
-- Researcher 会搜索外部平台并返回调研结果
-- 调研结果可通过 `upload_knowledge` 存入 KnowHub 供后续使用
 
 
-### 第四步:链式图生图生成(Stage 1)
-严格按 **img_1 → img_2 → img_3 → img_4 → img_5** 的顺序串行生成:
+**分组说明**:
+- `matched_requirements`:通过 `requirement_search` 在需求库中找到了匹配的已有需求。`match_confidence` 标注匹配程度(`exact`=完全匹配,`partial`=部分匹配需补充)。`delta_from_db` 说明本次在已有需求基础上的增量变化
+- `new_requirements`:需求库中无匹配,从内容描述文件中新提取的需求。这些需求后续应考虑入库积累
 
 
-**img_1(基准图)**:
-```
-agent(task="生成 img_1(基准图):
-  底图: %features_dir%/character_asset/character_ref_img1.png
-  背景参考: %features_dir%/background_asset/background_green_img1.png
-  调色板参考: %features_dir%/palette_asset/palette_impasto_img1_v2.png
+### 第一步:基于需求问策 Librarian
 
 
-  底图使用方式:将 character_ref 作为 image_url 传入端到端工具做图生图
-  读取 raw_info 目录下对应的制作表(%input_dir%/raw_info/写生油画__img_1_制作表.json)和通用文件辅助 prompt
+读取 `%output_dir%/requirements.json`,按以下优先级逐层向 Librarian 查询:
 
 
-  Prompt: [从 pipeline.json 提取]
-  Negative Prompt: [从 pipeline.json 提取]
-  img2img_config: [从 pipeline.json 提取]
+**第一层:查关系表(已有需求 → 关联的工序和案例)**
 
 
-  此图确立角色基准外观,后续所有图以此为参考锚点。
-  输出保存到: %output_dir%/img_1_restored_v1.png", agent_type="craftsman")
+对 `matched_requirements` 中的需求,优先通过关系表查找已有的工序方案和用户案例:
+```
+ask_knowledge(query="需求[db_match_id]关联的工序方案和用户案例")
 ```
 ```
+- 关系表中可能已记录:该需求对应的工具链、参数配置、成功案例
+- 如果关系表有完整的工序 → 直接采纳,无需进一步查询
 
 
-**img_2 ~ img_5(链式传递)**:每张图的 `chain_from` 字段指向前一张。指派时必须将前一张的生成结果路径传给 Craftsman,说明"此为前序图的生成结果,请保持角色、服装、色调一致"。
+**第二层:查能力和知识**
 
 
-每张图生成后,立即用 evaluate_image 评估:
+对关系表未覆盖的需求(包括 `new_requirements` 中的新需求),查询相关的能力和通用知识
 ```
 ```
-evaluate_image(requirement_path="%input_dir%/pipeline.json", image_paths="%output_dir%/img_1_restored_v1.png")
+ask_knowledge(query="AI图生图工具实现[抽象需求]的能力和方法,具体要求:[列出concrete_points]")
 ```
 ```
-- 评分 ≥ 7 → 通过,进入下一张
-- 评分 < 7 → 分析反馈,问策 Librarian 调整方案,重新派发 Craftsman(输出为 `img_X_restored_v2.png`,版本号递增,不覆盖上一版)
+- 查找 KnowHub 中是否有相关的工具能力评估、技巧总结、参数经验
+- 将抽象需求和具象点一起传入,让 Librarian 给出针对性建议
 
 
-### 第五步:迭代优化(Stage 2)
-Stage 1 全部完成后,对评分较低的图(<8 分),以 Stage 1 结果为底图再次图生图优化。
+**第三层:标记知识缺口**
 
 
-### 第六步:跨图一致性检查(Stage 3)
-用 evaluate_image 的多图模式检查所有图(使用每张图的最新版本):
+经过前两层查询后,汇总哪些需求仍缺乏足够的工序或知识支撑,标记为待调研项,交给第三步处理。
+
+### 第二步:按需调研
+对 Librarian 无法充分覆盖的需求,派发 Researcher 调研:
+```
+agent(task="调研以下制作需求的实现方案和用户案例:
+  [列出知识缺口的需求,包含 abstract + concrete_points]
+  重点关注:实际用户的成功案例、工具选择、参数配置、踩坑经验...", agent_type="researcher")
 ```
 ```
-evaluate_image(requirement_path="%input_dir%/pipeline.json", image_paths=["%output_dir%/img_1_restored_v1.png", "%output_dir%/img_2_restored_v1.png", ...])
+- Researcher 会搜索外部平台并返回调研结果
+- **调研结果保存到 `%output_dir%/research_result.json`**,供后续设计执行方案时参考
+- 同时通过 `upload_knowledge` 存入 KnowHub 供跨任务复用
+- 特别关注用户案例中的工序流程,可补充到关系表中
+
+**research_result.json 结构**:
+```json
+{
+  "researched_requirements": [
+    {
+      "abstract": "对应的抽象需求",
+      "findings": [
+        {
+          "type": "workflow | case_study | tool_tip | parameter",
+          "summary": "发现摘要",
+          "source": "来源平台和链接",
+          "detail": "详细内容"
+        }
+      ],
+      "recommended_approach": "综合调研结果后的推荐方案"
+    }
+  ]
+}
+```
+
+### 第三步:设计执行方案(生成 pipeline.json)
+
+综合以下信息,自行设计 `pipeline.json` 并保存到 `%output_dir%/pipeline.json`:
+
+**输入**:
+- `%output_dir%/requirements.json`:制作需求(抽象需求 + 具象点)
+- `%output_dir%/research_result.json`:Researcher 调研结果(工序、案例、工具技巧)
+- `%input_dir%/` 下的制作表 JSON:每张图的详细描述和构图信息
+- `%input_dir%/创作表.md`:整体创作视角
+- `%features_dir%/`:可用素材清单
+- Librarian 返回的工序方案、用户案例、工具能力信息
+
+**设计要点**:
+1. **确定图片顺序和链式关系**:根据制作表中各图的角色、构图关系,决定生成顺序和 chain_from 依赖
+2. **为每张图规划生成配置**:底图选择、参考素材、prompt、negative prompt、img2img 参数(strength 等)
+3. **将需求映射到具体图片**:每个 requirement 的 concrete_points 落实到对应图片的生成配置中
+4. **采纳 Librarian 推荐的工序**:优先使用关系表中已验证的工具链和参数配置
+5. **规划修复项**:根据需求优先级,列出 repair_items(Stage 4 细节修复清单)
+
+**pipeline.json 结构**:
+
+pipeline 是一个有序的 steps 列表,每个 step 对应一个可执行的操作。agent 设计 pipeline 时,应将生成、评估、迭代优化、一致性检查、细节修复等阶段都编排为具体的 step。
+
+```json
+{
+  "content_name": "内容名称",
+  "steps": [
+    {
+      "step_id": "step_1",
+      "type": "generate | evaluate | consistency_check | repair | custom",
+      "description": "该步骤的目标描述",
+      "expected_effect": "该步骤完成后图片应达到的视觉效果描述(具体、可感知)",
+      "checkpoints": [
+        "核心检查点1:可评估的具体标准(如'人物白裙V字露背设计清晰可见')",
+        "核心检查点2:可评估的具体标准(如'调色板颜料厚度感明显,有立体笔触纹理')"
+      ],
+      "depends_on": ["前置 step_id,为空则无依赖"],
+      "config": {
+        "// type=generate 时": "",
+        "target": "img_1",
+        "base_image": "%features_dir%/...",
+        "reference_images": ["%features_dir%/..."],
+        "chain_from_step": "null 或前序 generate step_id",
+        "prompt": "生成 prompt",
+        "negative_prompt": "负面 prompt",
+        "img2img_config": { "strength": 0.6 },
+        "mapped_requirements": ["对应的抽象需求"],
+
+        "// type=evaluate 时": "",
+        "target_step": "要评估的 generate step_id",
+        "pass_threshold": 7,
+        "retry_strategy": "问策 Librarian 后重新生成",
+
+        "// type=consistency_check 时": "",
+        "target_steps": ["要对比的多个 generate step_id"],
+        "dimensions": ["character", "clothing", "color", "lighting", "style"],
+
+        "// type=repair 时": "",
+        "target_step": "要修复的 step_id",
+        "repair_items": ["修复项描述"]
+      },
+      "output_path": "%output_dir%/img_1_restored_v1.png"
+    }
+  ],
+  "strategy_source": "工序方案来源说明(Librarian/Researcher/自行设计)"
+}
 ```
 ```
-- 对不一致的图,以当前结果为底图重新图生图
-- 如一致性问题严重,问策 Librarian 是否需要调研新的一致性保持技巧
 
 
-### 第七步:细节修复与输出(Stage 4)
-按 pipeline.json 的 repair_items 逐项检查修复。最终成品保存至 `%output_dir%/`。
+**设计要点**:
+1. **steps 的编排顺序即执行顺序**,通过 `depends_on` 表达依赖关系
+2. **每个 generate step 后通常跟一个 evaluate step**,评估不通过时按 `retry_strategy` 处理
+3. **将需求映射到具体 step**:每个 requirement 的 concrete_points 落实到对应 generate step 的配置中
+4. **采纳 Librarian 推荐的工序**:优先使用关系表中已验证的工具链和参数配置
+5. 设计完成后 review:每个 high priority 需求是否都在至少一个 step 中有体现
+6. **每个 step 必须填写 `expected_effect` 和 `checkpoints`**:`expected_effect` 描述该步骤完成后图片应呈现的视觉效果;`checkpoints` 列出 2-5 个可评估的具体标准,确保 evaluate step 有明确的评判依据。检查点应来自 requirements.json 中对应需求的 concrete_points
+
+### 第四步:按 pipeline 逐步执行
+
+遍历 `%output_dir%/pipeline.json` 中的 `steps`,按顺序逐步执行:
+
+- **generate**:组装该 step 的完整配置,派发 Craftsman 执行。如有 `chain_from_step`,将该前序 step 的最新输出路径一并传入。**必须提醒 Craftsman 读取 `%input_dir%/` 下该图对应的制作表 JSON(如 `写生油画__img_1_制作表.json`),从中提取构图、姿态、道具位置等细节融入 prompt**
+- **evaluate**:调用 evaluate_image 评估目标 step 的输出,**同时对照 `%input_dir%/` 下该图的制作表 JSON 中的具体描述进行逐项检查**(如制作表中描述"左手持调色板",则检查生成图是否符合)。未达 `pass_threshold` 则按 `retry_strategy` 处理(问策 Librarian → 调整配置 → 重新生成,版本号递增)
+- **consistency_check**:用 evaluate_image 多图模式检查 `target_steps` 的输出一致性。不一致的图重新生成
+- **repair**:按 `repair_items` 逐项修复目标 step 的输出
+- **custom**:按 description 描述的逻辑执行
+
+每个 step 完成后,记录结果到 `%output_dir%/generation_log.md`。
 
 
 ### 知识回流
 ### 知识回流
 每个阶段完成后,将有价值的经验存入 KnowHub:
 每个阶段完成后,将有价值的经验存入 KnowHub:
@@ -138,8 +307,8 @@ upload_knowledge(data="使用 nano_banana 图生图时,strength=0.6 + 链式
 2. **参考素材路径**(完整路径)
 2. **参考素材路径**(完整路径)
 3. **前序结果路径**(chain_from,如有)
 3. **前序结果路径**(chain_from,如有)
 4. **素材使用方式**:底图 → image_url,参考素材 → 辅助 prompt
 4. **素材使用方式**:底图 → image_url,参考素材 → 辅助 prompt
-5. **Prompt 和生成配置**
-6. **源信息参考**:提醒 Craftsman 读取 `%input_dir%/raw_info/` 目录下对应图片的制作表 JSON(如 `写生油画__img_1_制作表.json`)和通用文件(`创作表.md`、`图片亮点.md`)
+5. **Prompt 和生成配置**(从 `%output_dir%/pipeline.json` 中对应图片的配置提取)
+6. **源信息参考**:提醒 Craftsman 读取 `%input_dir%/` 目录下对应图片的制作表 JSON(如 `写生油画__img_1_制作表.json`)和通用文件(`创作表.md`、`图片亮点.md`)
 7. **输出路径**
 7. **输出路径**
 
 
 ## 评估原则
 ## 评估原则
@@ -148,7 +317,7 @@ upload_knowledge(data="使用 nano_banana 图生图时,strength=0.6 + 链式
 3. **上限点**:尽力还原,接受次优(评分 ≥ 7)
 3. **上限点**:尽力还原,接受次优(评分 ≥ 7)
 
 
 $user$
 $user$
-请读取输入目录中的还原方案,开始执行完整的图像还原流程:
+请先从输入目录的内容描述文件中提取制作需求,然后将需求交给 Librarian 调研,最后执行完整的图像还原流程:
 %input_dir%
 %input_dir%
 
 
 输出目录:%output_dir%
 输出目录:%output_dir%

+ 13 - 0
im-server/__main__.py

@@ -0,0 +1,13 @@
+import uvicorn
+import os
+import sys
+
+# 确保在导入 main 时能够找到同级模块
+current_dir = os.path.dirname(os.path.abspath(__file__))
+if current_dir not in sys.path:
+    sys.path.insert(0, current_dir)
+
+from main import app
+
+if __name__ == "__main__":
+    uvicorn.run(app, host="0.0.0.0", port=8105)

+ 132 - 0
insert_ai_workflow_capabilities.py

@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+"""
+插入 AI 创作工作流的 16 个通用能力模块知识条目
+"""
+import os
+import sys
+import json
+import uuid
+from datetime import datetime, timezone
+from pathlib import Path
+
+# 添加项目路径
+sys.path.insert(0, '/root/Agent')
+
+os.environ['OPEN_ROUTER_API_KEY'] = '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'
+os.environ['QWEN_API_KEY'] = 'sk-9453c827b9e14108b53d2b30ef7c75fe'
+os.environ['KNOWHUB_DB'] = 'gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com'
+os.environ['KNOWHUB_PORT'] = '5432'
+os.environ['KNOWHUB_USER'] = 'aiddit_aigc'
+os.environ['KNOWHUB_PASSWORD'] = '%a&&yqNxg^V1$toJ*WOa^-b^X=QJ'
+os.environ['KNOWHUB_DB_NAME'] = 'knowhub'
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+# 知识数据 - AI 创作工作流的 16 个通用能力模块
+knowledge_data = {
+    "task": "AI 创作工作流的 16 个通用能力模块",
+    "content": """## 能力模块清单
+
+### 素材准备类
+1. **准备参考素材**:收集或生成用于后续创作的参考图像、纹理、角色基础等素材
+2. **生成参考图表**:生成角色参考表、多视角图、多表情图等参考资料
+
+### 角色定义类
+3. **定义角色特征**:设定人物面部特征、表情状态、身份一致性等角色属性
+
+### 场景构建类
+4. **构建场景环境**:创建或合成场景背景、虚拟环境、空间布局
+
+### 视觉控制类
+5. **配置光影效果**:设置光源方向、灯光类型、明暗对比等光照条件
+6. **控制姿态构图**:控制人物姿态、画面构图、视角、摄影参数
+7. **应用风格控制**:应用风格迁移、保持视觉一致性、注入特定美学风格
+8. **处理纹理效果**:添加纹理、颗粒、质感、色调等视觉效果
+9. **创建纹理布局**:创建四方连续、渐变网格、矢量图形等纹理布局
+
+### 生成执行类
+10. **执行内容生成**:执行核心的图像、视频、渲染生成操作
+11. **配置控制技术**:配置 ControlNet、深度图、姿态图等控制条件
+12. **配置工作流**:配置工具节点、构建流程、设置参数
+
+### 后期处理类
+13. **修复细节瑕疵**:对局部区域进行重绘、修复瑕疵、优化细节
+14. **增强图像质量**:提升图像分辨率、质量增强、最终精修
+15. **后期合成处理**:抠图、选区创建、图像合成、图层处理
+16. **视频后期处理**:视频剪辑、颜色分级、特效增强
+
+## 跨品类复用性
+以上模块均从 5 个不同品类的工作流(角色创作、人像精修、肖像摄影、光影控制、纹理设计)中抽象得出,可跨品类复用。""",
+    "types": ["strategy"],
+    "tags": {
+        "domain": "AI 创作",
+        "category": "能力模块",
+        "source": "工作流分析 pipeline"
+    },
+    "score": 5,
+    "source": {"category": "research"}
+}
+
+print("正在连接数据库...")
+store = PostgreSQLStore()
+
+# 生成 ID
+now = datetime.now(timezone.utc)
+knowledge_id = f"knowledge-{now.strftime('%Y%m%d')}-{uuid.uuid4().hex[:8]}"
+message_id = f"msg-{uuid.uuid4().hex[:12]}"
+
+# 生成 embedding
+print("正在生成 embedding...")
+task_embedding = get_embedding(knowledge_data["task"])
+content_embedding = get_embedding(knowledge_data["content"])
+
+# 构建知识记录
+knowledge = {
+    "id": knowledge_id,
+    "message_id": message_id,
+    "task": knowledge_data["task"],
+    "content": knowledge_data["content"],
+    "types": knowledge_data["types"],
+    "tags": knowledge_data["tags"],
+    "tag_keys": list(knowledge_data["tags"].keys()),
+    "scopes": [],
+    "owner": "librarian",
+    "source": knowledge_data["source"],
+    "eval": {"score": knowledge_data["score"]},
+    "created_at": now.isoformat(),
+    "updated_at": now.isoformat(),
+    "status": "approved",
+    "task_embedding": task_embedding,
+    "content_embedding": content_embedding,
+    "requirement_ids": [],
+    "capability_ids": [],
+    "tool_ids": [],
+    "resource_ids": [],
+}
+
+print(f"正在插入知识:{knowledge_id}")
+print(f"  Task: {knowledge['task']}")
+print(f"  Types: {knowledge['types']}")
+print(f"  Tags: {knowledge['tags']}")
+print(f"  Content 长度:{len(knowledge['content'])} 字符")
+
+store.insert(knowledge)
+
+print(f"\n✓ 知识已成功入库!")
+print(f"  ID: {knowledge_id}")
+print(f"  Message ID: {message_id}")
+
+# 验证插入
+retrieved = store.get_by_id(knowledge_id)
+if retrieved:
+    print(f"\n✓ 验证成功:已检索到刚插入的知识")
+    print(f"  Task: {retrieved['task']}")
+    print(f"  Content 长度:{len(retrieved['content'])} 字符")
+    print(f"  Types: {retrieved['types']}")
+    print(f"  Tags: {retrieved['tags']}")
+else:
+    print(f"\n✗ 验证失败:无法检索到刚插入的知识")
+
+store.close()
+print("\n数据库连接已关闭")

+ 73 - 0
insert_knowledge.py

@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+"""
+插入知识条目到 PostgreSQL 数据库
+"""
+
+import asyncio
+import uuid
+import time
+import sys
+import os
+
+# 添加项目路径
+sys.path.insert(0, '/root/Agent')
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+
+async def main():
+    # 知识数据
+    task = "人物近景肖像"
+    content = "生成人物近景半身或胸部以上画面、突出面部表情和情绪、背景虚化的核心工序要素包括:1.近景/半身构图 2.面部表情细节 3.背景虚化效果 4.皮肤质感真实 5.眼神互动感"
+    types = ["strategy"]
+    tags = {"domain": "AI 生图", "source": "多渠调研汇总", "task": "人物近景肖像"}
+    score = 5
+    source = {"category": "research"}
+    
+    # 生成唯一 ID
+    knowledge_id = str(uuid.uuid4())
+    message_id = str(uuid.uuid4())
+    current_time = int(time.time())
+    
+    # 生成 embeddings
+    print(f"正在生成 task embedding...")
+    task_embedding = await get_embedding(task)
+    print(f"正在生成 content embedding...")
+    content_embedding = await get_embedding(content)
+    
+    # 构建知识对象
+    knowledge = {
+        'id': knowledge_id,
+        'message_id': message_id,
+        'task': task,
+        'content': content,
+        'types': types,
+        'tags': tags,
+        'tag_keys': list(tags.keys()),
+        'scopes': ['org:cybertogether'],  # 默认可见范围
+        'owner': 'system',  # 默认所有者
+        'source': source,
+        'eval': {'score': score},
+        'task_embedding': task_embedding,
+        'content_embedding': content_embedding,
+        'created_at': current_time,
+        'updated_at': current_time,
+        'status': 'approved',
+    }
+    
+    # 插入数据库
+    print(f"正在插入知识条目到 PostgreSQL...")
+    store = PostgreSQLStore()
+    store.insert(knowledge)
+    store.close()
+    
+    print(f"成功插入知识条目!")
+    print(f"  ID: {knowledge_id}")
+    print(f"  Task: {task}")
+    print(f"  Types: {types}")
+    print(f"  Score: {score}")
+
+
+if __name__ == '__main__':
+    asyncio.run(main())

+ 91 - 0
insert_knowledge_direct.py

@@ -0,0 +1,91 @@
+import os
+import sys
+import json
+import uuid
+from datetime import datetime, timezone
+from pathlib import Path
+
+# 添加项目路径
+sys.path.insert(0, '/root/Agent')
+
+os.environ['OPEN_ROUTER_API_KEY'] = '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'
+os.environ['QWEN_API_KEY'] = 'sk-9453c827b9e14108b53d2b30ef7c75fe'
+os.environ['KNOWHUB_DB'] = 'gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com'
+os.environ['KNOWHUB_PORT'] = '5432'
+os.environ['KNOWHUB_USER'] = 'aiddit_aigc'
+os.environ['KNOWHUB_PASSWORD'] = '%a&&yqNxg^V1$toJ*WOa^-b^X=QJ'
+os.environ['KNOWHUB_DB_NAME'] = 'knowhub'
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+# 知识数据
+knowledge_data = {
+    "task": "人物近景肖像",
+    "content": "生成人物近景半身或胸部以上画面、突出面部表情和情绪、背景虚化的核心工序要素包括:\n\n1. **近景/半身构图**:使用 tight medium close-up、extreme close-up、中近景半身等提示词;推荐 85mm 镜头视角;构图比例 2:3 或 9:16(竖版)、3:4(职业照)\n\n2. **面部表情细节**:使用锁脸技术(nano-banana 锁脸功能)、微表情描述(slightly parted lips、calm introspective expression)、多表情生成(3×3 网格生成 9 种表情);关键提示词:direct steady eye contact、subtle catchlights in eyes\n\n3. **背景虚化效果**:推荐参数 85mm 镜头 + f/1.4-f/1.8 光圈;提示词:shallow depth of field、extremely shallow depth of field、soft bokeh blur、blurring out the neck, ear, and background completely;后期工具:Photoshop Lens Blur、DxO Photolab 9 Bokeh\n\n4. **皮肤质感真实**:避免塑料感的关键提示词:realistic skin texture with visible pores and natural imperfections、no plastic smoothing、dewy smooth texture;修复工具:Enhancor.ai Skin Realism\n\n5. **眼神互动感**:direct eye contact、catchlights in eyes、gentle and engaging eyes、looking directly at the camera",
+    "types": ["strategy"],
+    "tags": {
+        "domain": "AI 生图",
+        "source": "多渠调研汇总",
+        "task": "人物近景肖像"
+    },
+    "score": 5,
+    "source": {"category": "research"}
+}
+
+print("正在连接数据库...")
+store = PostgreSQLStore()
+
+# 生成 ID
+now = datetime.now(timezone.utc)
+knowledge_id = f"knowledge-{now.strftime('%Y%m%d')}-{uuid.uuid4().hex[:8]}"
+message_id = f"msg-{uuid.uuid4().hex[:12]}"
+
+# 生成 embedding
+print("正在生成 embedding...")
+task_embedding = get_embedding(knowledge_data["task"])
+content_embedding = get_embedding(knowledge_data["content"])
+
+# 构建知识记录
+knowledge = {
+    "id": knowledge_id,
+    "message_id": message_id,
+    "task": knowledge_data["task"],
+    "content": knowledge_data["content"],
+    "types": knowledge_data["types"],
+    "tags": knowledge_data["tags"],
+    "tag_keys": list(knowledge_data["tags"].keys()),
+    "scopes": [],
+    "owner": "librarian",
+    "source": knowledge_data["source"],
+    "eval": {"score": knowledge_data["score"]},
+    "created_at": now.isoformat(),
+    "updated_at": now.isoformat(),
+    "status": "approved",
+    "task_embedding": task_embedding,
+    "content_embedding": content_embedding,
+    "requirement_ids": [],
+    "capability_ids": [],
+    "tool_ids": [],
+    "resource_ids": [],
+}
+
+print(f"正在插入知识:{knowledge_id}")
+print(f"  Task: {knowledge['task']}")
+print(f"  Types: {knowledge['types']}")
+print(f"  Tags: {knowledge['tags']}")
+
+store.insert(knowledge)
+
+print(f"\n✓ 知识已成功入库!")
+print(f"  ID: {knowledge_id}")
+print(f"  Message ID: {message_id}")
+
+# 验证插入
+retrieved = store.get_by_id(knowledge_id)
+if retrieved:
+    print(f"\n✓ 验证成功:已检索到刚插入的知识")
+    print(f"  Task: {retrieved['task']}")
+    print(f"  Content 长度:{len(retrieved['content'])} 字符")
+else:
+    print(f"\n✗ 验证失败:无法检索到刚插入的知识")

+ 1 - 0
knowhub/agents/librarian.py

@@ -66,6 +66,7 @@ def get_librarian_config(enable_db_commit: bool = ENABLE_DATABASE_COMMIT) -> Run
             enable_injection=False,
             enable_injection=False,
         ),
         ),
         tools=tools,
         tools=tools,
+        exclude_tools=["ask_knowledge", "upload_knowledge", "bash_command", "grep_content", "glob_files"],
     )
     )
 
 
 
 

+ 5 - 0
knowhub/agents/librarian_agent.prompt

@@ -44,5 +44,10 @@ Knowledge 按 types 分类:
 
 
 所有关联通过关联表存储,数据库保证引用完整性。查到任意一个实体都可以顺藤摸瓜找到关联的其他实体。
 所有关联通过关联表存储,数据库保证引用完整性。查到任意一个实体都可以顺藤摸瓜找到关联的其他实体。
 
 
+## 工具使用规范与检索策略
+
+1. **精准查询优于全文搜索**:当你需要查询跨表关联关系时(例如:“寻找某个特定 Capability ID 被哪些 Requirement 关联了” 或者 “查某个 Tool 有没有被某个 Capability 包含”),**强烈推荐且务必优先使用 `relation_search` 工具**直接查询关系表(例如 `requirement_capability`, `capability_tool`, `tool_knowledge` 等)。
+2. 使用 `relation_search` 时,在 `filters` 中传入已知的 `_id` 即可迅速获得所有匹配的关联链路,进而拿到目标实体的 ID 再去定向获取详情,这比漫无目的地做大文本向量搜索 (`search` 结尾工具) 效率极速且精准得多!
+
 $user$
 $user$
 
 

+ 444 - 0
knowhub/docs/knowledge-management.md

@@ -0,0 +1,444 @@
+# KnowHub API 参考
+
+KnowHub 是一个 FastAPI 服务,提供知识图谱的存储、检索和管理能力。
+基础地址由环境变量 `KNOWHUB_API` 控制,默认 `http://localhost:8000`。
+
+FastAPI 自动生成的交互式文档:`{KNOWHUB_API}/docs`
+
+---
+
+## 数据模型
+
+### Knowledge(知识)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `id` | string | 格式 `knowledge-{date}-{hash}`,自动生成 |
+| `task` | string | 任务描述(在什么情景下要完成什么目标) |
+| `content` | string | 核心内容 |
+| `types` | string[] | 类型标签:`tool` / `strategy` / `case` / `experience` |
+| `tags` | object | 业务标签,如 `{"project": "foo", "domain": "ai"}` |
+| `scopes` | string[] | 可见范围,默认 `["org:cybertogether"]` |
+| `owner` | string | 所有者,默认 `agent:{agent_id}` |
+| `source` | object | `{name, category, urls, agent_id, submitted_by, timestamp}` |
+| `eval` | object | `{score(1-5), helpful, harmful, confidence}` |
+| `capability_ids` | string[] | 关联的能力 ID |
+| `tool_ids` | string[] | 关联的工具 ID |
+| `resource_ids` | string[] | 关联的资源 ID |
+| `status` | string | `pending` / `processing` / `dedup_passed` / `analyzing` / `approved` / `checked` / `rejected` |
+
+### Tool(工具)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `id` | string | 格式 `tools/{category}/{name}`,调用方指定 |
+| `name` | string | 工具名称 |
+| `version` | string | 版本号(可选) |
+| `introduction` | string | 简介 |
+| `tutorial` | string | 使用教程 |
+| `input` | object/string | 输入格式描述 |
+| `output` | object/string | 输出格式描述 |
+| `status` | string | 默认 `未接入` |
+| `capability_ids` | string[] | 关联的能力 ID |
+| `knowledge_ids` | string[] | 关联的知识 ID |
+| `provider_ids` | string[] | 关联的提供方 ID |
+
+### Capability(原子能力)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `id` | string | 格式 `CAP-XXX`,调用方指定 |
+| `name` | string | 能力名称 |
+| `criterion` | string | 判断标准(何时认为该能力被满足) |
+| `description` | string | 详细描述 |
+| `requirement_ids` | string[] | 关联的需求 ID |
+| `tool_ids` | string[] | 实现该能力的工具 ID |
+| `implements` | object | `{tool_id: description}` 工具实现描述 |
+| `knowledge_ids` | string[] | 关联的知识 ID |
+
+### Requirement(需求)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `id` | string | 格式 `REQ_XXX`,调用方指定 |
+| `description` | string | 需求描述 |
+| `capability_ids` | string[] | 满足该需求的能力 ID |
+| `knowledge_ids` | string[] | 关联的知识 ID |
+| `source_nodes` | object[] | 分类树节点 `[{id, score}]` |
+| `status` | string | 默认 `未满足` |
+| `match_result` | string | 匹配结果说明 |
+
+### Resource(资源)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `id` | string | 路径格式,如 `docs/foo/bar` |
+| `title` | string | 标题 |
+| `body` | string | 正文内容 |
+| `secure_body` | string | 加密内容(AES256-GCM) |
+| `content_type` | string | 默认 `text` |
+| `metadata` | object | 自定义元数据 |
+
+---
+
+## Knowledge API
+
+### 智能查询(Librarian Agent)
+
+#### `POST /api/knowledge/ask`
+
+同步阻塞。触发 Librarian Agent 跨表检索 + LLM 整合,返回带引用的回答。
+同一 `trace_id` 的多次调用复用同一个 Librarian trace,积累上下文。
+
+```json
+// 请求
+{
+  "query": "有没有工具能做角色一致性生成?",
+  "trace_id": "trace-abc123"
+}
+
+// 响应
+{
+  "response": "根据知识库,ControlNet 可以...",
+  "source_ids": ["knowledge-20260101-abcd"],
+  "sources": []
+}
+```
+
+#### `POST /api/knowledge/upload` → 202
+
+异步。校验后立即返回,后台 Librarian Agent 处理去重、关联、写草稿池。
+
+```json
+// 请求
+{
+  "data": {
+    "knowledge": [...],
+    "tools": [...],
+    "resources": [...]
+  },
+  "trace_id": "trace-abc123",
+  "finalize": false
+}
+
+// 响应 202
+{
+  "message": "已接收 知识: 2 个,Librarian Agent 后台处理中",
+  "buffer_file": ".cache/.knowledge/buffer/upload_20260101_120000.json"
+}
+```
+
+#### `GET /api/knowledge/upload/pending`
+
+列出所有未处理或失败的 upload 任务。
+
+#### `POST /api/knowledge/upload/retry`
+
+重跑所有 `status=failed` 的 upload 任务。
+
+---
+
+### 向量检索
+
+#### `GET /api/knowledge/search`
+
+向量召回 + LLM 精排。
+
+| 参数 | 类型 | 默认 | 说明 |
+|------|------|------|------|
+| `q` | string | 必填 | 查询文本 |
+| `top_k` | int | 5 | 返回条数(1-20) |
+| `min_score` | int | 3 | 最低评分过滤(1-5) |
+| `types` | string | - | 逗号分隔,如 `tool,case` |
+| `owner` | string | - | 逗号分隔,过滤 owner |
+
+```json
+// 响应
+{
+  "results": [{"id": "...", "task": "...", "content": "...", "score": 0.92, ...}],
+  "count": 3
+}
+```
+
+---
+
+### CRUD
+
+#### `POST /api/knowledge` → 201
+
+创建知识,进入 `pending` 状态,自动触发去重流水线。
+
+```json
+{
+  "task": "使用 ControlNet 进行姿态控制",
+  "content": "...",
+  "types": ["tool"],
+  "tags": {"project": "image_gen"},
+  "source": {"agent_id": "research_agent", "category": "research"},
+  "eval": {"score": 4}
+}
+// 响应: {"status": "pending", "knowledge_id": "knowledge-20260101-abcd"}
+```
+
+#### `GET /api/knowledge`
+
+列出知识,支持分页和过滤。
+
+| 参数 | 说明 |
+|------|------|
+| `page` / `page_size` | 分页,默认 1/20 |
+| `types` | 逗号分隔类型过滤 |
+| `scopes` | 逗号分隔范围过滤 |
+| `owner` | owner 过滤 |
+| `tags` | JSON 字符串,如 `{"project":"foo"}` |
+| `status` | 状态过滤 |
+
+#### `GET /api/knowledge/{knowledge_id}`
+
+获取单条知识详情。
+
+#### `GET /api/knowledge/status/{knowledge_id}`
+
+查询知识处理状态(`pending` / `approved` 等)。
+
+#### `PUT /api/knowledge/{knowledge_id}`
+
+更新知识评估,支持知识进化(LLM 重写 content)。
+
+```json
+{
+  "update_score": 5,
+  "add_helpful_case": {"context": "...", "result": "success"},
+  "add_harmful_case": null,
+  "evolve_feedback": "内容需要补充 batch 处理的情况"
+}
+```
+
+#### `PATCH /api/knowledge/{knowledge_id}`
+
+直接编辑字段(task、content、types、tags、scopes、owner、capability_ids、tool_ids)。
+
+#### `DELETE /api/knowledge/{knowledge_id}`
+
+删除单条知识(级联清理关联表)。
+
+#### `POST /api/knowledge/batch_delete`
+
+批量删除。请求体为 `["knowledge-xxx", "knowledge-yyy"]`。
+
+---
+
+### 审核与验证
+
+#### `POST /api/knowledge/{knowledge_id}/verify`
+
+```json
+// approve:approved ↔ checked 切换;reject:→ rejected
+{"action": "approve", "verified_by": "howard"}
+```
+
+#### `POST /api/knowledge/batch_verify`
+
+```json
+{"knowledge_ids": ["knowledge-xxx"], "action": "approve", "verified_by": "howard"}
+```
+
+---
+
+### 运维工具
+
+#### `GET /api/knowledge/pending`
+
+查询处理队列(`pending` / `processing` / `dedup_passed` / `analyzing`)。
+
+#### `POST /api/knowledge/process`
+
+手动触发去重处理。`force=true` 时先回滚所有超时锁。
+
+#### `GET /api/knowledge/meta/tags`
+
+获取所有已使用的 tag keys。
+
+#### `POST /api/knowledge/slim`
+
+知识库瘦身:LLM 分析全库,合并语义相似条目。
+可选参数 `model`,默认 `google/gemini-2.5-flash-lite`。
+
+#### `POST /api/knowledge/batch_update`
+
+批量反馈知识有效性。
+
+#### `POST /api/extract`
+
+从消息历史中 LLM 提取知识并入库。
+
+```json
+{
+  "messages": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}],
+  "agent_id": "research_agent",
+  "submitted_by": "howard@example.com",
+  "session_key": ""
+}
+```
+
+---
+
+## Tool API
+
+#### `POST /api/tool` → 201
+
+创建或更新工具(id 相同则覆盖,同时全量替换关联表)。
+
+```json
+{
+  "id": "tools/image/controlnet",
+  "name": "ControlNet",
+  "introduction": "基于条件控制的图像生成工具",
+  "tutorial": "...",
+  "status": "已接入",
+  "capability_ids": ["CAP-001"],
+  "knowledge_ids": ["knowledge-20260101-abcd"]
+}
+```
+
+#### `GET /api/tool`
+
+列出工具。参数:`status`、`limit`(默认100)、`offset`。
+
+#### `GET /api/tool/search?q=...&top_k=5`
+
+向量检索工具。
+
+#### `GET /api/tool/{tool_id}`
+
+获取单个工具详情(含关联的 capability_ids、knowledge_ids)。
+
+#### `PATCH /api/tool/{tool_id}`
+
+部分更新工具字段。
+
+#### `DELETE /api/tool/{tool_id}`
+
+删除工具(级联清理关联表)。
+
+---
+
+## Capability API
+
+#### `POST /api/capability` → 201
+
+创建或更新原子能力。
+
+```json
+{
+  "id": "CAP-001",
+  "name": "人物姿态控制生成",
+  "criterion": "能根据骨骼图生成指定姿态的人物图像",
+  "description": "...",
+  "tool_ids": ["tools/image/controlnet"],
+  "implements": {"tools/image/controlnet": "使用 openpose 模型控制姿态"}
+}
+```
+
+#### `GET /api/capability`
+
+列出所有能力。参数:`limit`、`offset`。
+
+#### `GET /api/capability/search?q=...&top_k=5`
+
+向量检索能力。
+
+#### `GET /api/capability/{cap_id}`
+
+获取单个能力详情。
+
+#### `PATCH /api/capability/{cap_id}`
+
+部分更新能力字段。
+
+#### `DELETE /api/capability/{cap_id}`
+
+删除能力(级联清理关联表)。
+
+---
+
+## Requirement API
+
+#### `POST /api/requirement` → 201
+
+创建或更新需求。
+
+```json
+{
+  "id": "REQ_001",
+  "description": "需要能生成角色一致的多视角图像",
+  "capability_ids": ["CAP-001"],
+  "source_nodes": [{"id": "image_generation", "score": 0.9}],
+  "status": "已满足"
+}
+```
+
+#### `GET /api/requirement`
+
+列出需求。参数:`status`、`limit`、`offset`。
+
+#### `GET /api/requirement/search?q=...&top_k=5`
+
+向量检索需求。
+
+#### `GET /api/requirement/{req_id}`
+
+获取单个需求详情。
+
+#### `PATCH /api/requirement/{req_id}`
+
+部分更新需求字段。
+
+#### `DELETE /api/requirement/{req_id}`
+
+删除需求(级联清理关联表)。
+
+---
+
+## Resource API
+
+Resource 存储原始资料文档,通过 `knowledge_resource` 关联表与 Knowledge 关联。
+
+#### `POST /api/resource` → 201
+
+创建或更新资源。
+
+#### `GET /api/resource`
+
+列出资源(树形结构)。
+
+#### `GET /api/resource/{resource_id}`
+
+获取单个资源(含 toc、children、prev/next 导航)。
+
+#### `PATCH /api/resource/{resource_id}`
+
+部分更新资源字段。
+
+#### `DELETE /api/resource/{resource_id}`
+
+删除资源。
+
+---
+
+## 关联表说明
+
+所有实体间的关系通过 junction table 维护,写入时**全量替换**(DELETE + INSERT):
+
+| 关联表 | 连接 |
+|--------|------|
+| `capability_tool` | Capability ↔ Tool |
+| `capability_knowledge` | Capability ↔ Knowledge |
+| `requirement_capability` | Requirement ↔ Capability |
+| `requirement_knowledge` | Requirement ↔ Knowledge |
+| `tool_knowledge` | Tool ↔ Knowledge |
+| `tool_provider` | Tool ↔ Provider |
+| `knowledge_resource` | Knowledge ↔ Resource |
+| `knowledge_relation` | Knowledge → Knowledge(supplement/duplicate) |
+
+查询任意实体时,关联 ID 通过子查询自动聚合返回(`capability_ids`、`tool_ids` 等字段)。

+ 44 - 7
knowhub/frontend/src/App.tsx

@@ -1,4 +1,7 @@
+import { useState } from 'react';
+import { useNavigate, useLocation } from 'react-router-dom';
 import { MainLayout } from './layouts/MainLayout';
 import { MainLayout } from './layouts/MainLayout';
+import type { TabId } from './components/layout/Navbar';
 import { Dashboard } from './pages/Dashboard';
 import { Dashboard } from './pages/Dashboard';
 import { Relations } from './pages/Relations';
 import { Relations } from './pages/Relations';
 import { Requirements } from './pages/Requirements';
 import { Requirements } from './pages/Requirements';
@@ -6,15 +9,49 @@ import { Capabilities } from './pages/Capabilities';
 import { Tools } from './pages/Tools';
 import { Tools } from './pages/Tools';
 import { Knowledge } from './pages/Knowledge';
 import { Knowledge } from './pages/Knowledge';
 
 
+const PATH_TO_TAB: Record<string, TabId> = {
+  '/': 'dashboard',
+  '/dashboard': 'dashboard',
+  '/relations': 'relations',
+  '/requirements': 'requirements',
+  '/capabilities': 'capabilities',
+  '/tools': 'tools',
+  '/knowledge': 'knowledge',
+};
+
+const TAB_TO_PATH: Record<TabId, string> = {
+  dashboard: '/',
+  relations: '/relations',
+  requirements: '/requirements',
+  capabilities: '/capabilities',
+  tools: '/tools',
+  knowledge: '/knowledge',
+};
+
 function App() {
 function App() {
+  const navigate = useNavigate();
+  const location = useLocation();
+  const [pendingDashboardNode, setPendingDashboardNode] = useState<string | null>(null);
+
+  const activeTab: TabId = PATH_TO_TAB[location.pathname] ?? 'dashboard';
+
+  const handleTabChange = (tab: TabId) => {
+    navigate(TAB_TO_PATH[tab]);
+  };
+
+  const navigateToDashboard = (nodeName: string) => {
+    setPendingDashboardNode(nodeName);
+    navigate('/');
+  };
+
   return (
   return (
-    <MainLayout>
-      {(activeTab) => {
-        switch (activeTab) {
+    <MainLayout activeTab={activeTab} onTabChange={handleTabChange}>
+      {(tab) => {
+        switch (tab) {
           case 'dashboard':
           case 'dashboard':
-            return <Dashboard />;
+            return <Dashboard pendingNode={pendingDashboardNode} onPendingConsumed={() => setPendingDashboardNode(null)} />;
           case 'relations':
           case 'relations':
-            return <Relations />;
+            return <Relations onNavigateToDashboard={navigateToDashboard} />;
           case 'requirements':
           case 'requirements':
             return <Requirements />;
             return <Requirements />;
           case 'capabilities':
           case 'capabilities':
@@ -24,11 +61,11 @@ function App() {
           case 'knowledge':
           case 'knowledge':
             return <Knowledge />;
             return <Knowledge />;
           default:
           default:
-            return <Dashboard />;
+            return <Dashboard pendingNode={null} onPendingConsumed={() => {}} />;
         }
         }
       }}
       }}
     </MainLayout>
     </MainLayout>
   );
   );
 }
 }
 
 
-export default App;
+export default App;

+ 23 - 51
knowhub/frontend/src/layouts/MainLayout.tsx

@@ -1,8 +1,10 @@
-import { useState, useEffect, useRef } from 'react';
+import { useEffect, useRef } from 'react';
 import { Navbar } from '../components/layout/Navbar';
 import { Navbar } from '../components/layout/Navbar';
 import type { TabId } from '../components/layout/Navbar';
 import type { TabId } from '../components/layout/Navbar';
 
 
 interface MainLayoutProps {
 interface MainLayoutProps {
+  activeTab: TabId;
+  onTabChange: (tab: TabId) => void;
   children: (activeTab: TabId) => React.ReactNode;
   children: (activeTab: TabId) => React.ReactNode;
 }
 }
 
 
@@ -10,9 +12,7 @@ interface MainLayoutProps {
 const TAB_ORDER: TabId[] = ['dashboard', 'relations', 'requirements', 'capabilities', 'tools', 'knowledge'];
 const TAB_ORDER: TabId[] = ['dashboard', 'relations', 'requirements', 'capabilities', 'tools', 'knowledge'];
 const MIN_SWITCH_INTERVAL = 1000;
 const MIN_SWITCH_INTERVAL = 1000;
 
 
-export function MainLayout({ children }: MainLayoutProps) {
-  const [activeTab, setActiveTab] = useState<TabId>('dashboard');
-
+export function MainLayout({ activeTab, onTabChange, children }: MainLayoutProps) {
   const lastSwitchTime = useRef(0);
   const lastSwitchTime = useRef(0);
   const accumX = useRef(0);
   const accumX = useRef(0);
   const touchStartX = useRef(0);
   const touchStartX = useRef(0);
@@ -22,17 +22,12 @@ export function MainLayout({ children }: MainLayoutProps) {
     const handleTabSwitch = (direction: 1 | -1) => {
     const handleTabSwitch = (direction: 1 | -1) => {
       const now = Date.now();
       const now = Date.now();
       if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) return;
       if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) return;
-
-      setActiveTab(prev => {
-        const currentIndex = TAB_ORDER.indexOf(prev);
-        const nextIndex = currentIndex + direction;
-
-        if (nextIndex >= 0 && nextIndex < TAB_ORDER.length) {
-          lastSwitchTime.current = now;
-          return TAB_ORDER[nextIndex];
-        }
-        return prev;
-      });
+      const currentIndex = TAB_ORDER.indexOf(activeTab);
+      const nextIndex = currentIndex + direction;
+      if (nextIndex >= 0 && nextIndex < TAB_ORDER.length) {
+        lastSwitchTime.current = now;
+        onTabChange(TAB_ORDER[nextIndex]);
+      }
     };
     };
 
 
     const isInsideHorizontallyScrollable = (targetNode: EventTarget | null) => {
     const isInsideHorizontallyScrollable = (targetNode: EventTarget | null) => {
@@ -40,9 +35,7 @@ export function MainLayout({ children }: MainLayoutProps) {
       while (target && target !== document.body) {
       while (target && target !== document.body) {
         if (target.scrollWidth > target.clientWidth) {
         if (target.scrollWidth > target.clientWidth) {
           const style = window.getComputedStyle(target);
           const style = window.getComputedStyle(target);
-          if (style.overflowX === 'auto' || style.overflowX === 'scroll' || style.overflowX === 'overlay') {
-            return true;
-          }
+          if (style.overflowX === 'auto' || style.overflowX === 'scroll' || style.overflowX === 'overlay') return true;
         }
         }
         target = target.parentElement;
         target = target.parentElement;
       }
       }
@@ -53,25 +46,13 @@ export function MainLayout({ children }: MainLayoutProps) {
       if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) return;
       if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) return;
       if (isInsideHorizontallyScrollable(e.target)) return;
       if (isInsideHorizontallyScrollable(e.target)) return;
       if (e.cancelable) e.preventDefault();
       if (e.cancelable) e.preventDefault();
-
       const now = Date.now();
       const now = Date.now();
-      if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) {
-        accumX.current = 0;
-        return;
-      }
-
+      if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) { accumX.current = 0; return; }
       accumX.current += e.deltaX;
       accumX.current += e.deltaX;
-
       clearTimeout(wheelTimeout.current);
       clearTimeout(wheelTimeout.current);
       wheelTimeout.current = setTimeout(() => { accumX.current = 0; }, 150);
       wheelTimeout.current = setTimeout(() => { accumX.current = 0; }, 150);
-
-      if (accumX.current > 120) {
-        handleTabSwitch(1);
-        accumX.current = 0;
-      } else if (accumX.current < -120) {
-        handleTabSwitch(-1);
-        accumX.current = 0;
-      }
+      if (accumX.current > 120) { handleTabSwitch(1); accumX.current = 0; }
+      else if (accumX.current < -120) { handleTabSwitch(-1); accumX.current = 0; }
     };
     };
 
 
     const handleTouchStart = (e: TouchEvent) => {
     const handleTouchStart = (e: TouchEvent) => {
@@ -81,10 +62,8 @@ export function MainLayout({ children }: MainLayoutProps) {
 
 
     const handleTouchEnd = (e: TouchEvent) => {
     const handleTouchEnd = (e: TouchEvent) => {
       if (isInsideHorizontallyScrollable(e.target)) return;
       if (isInsideHorizontallyScrollable(e.target)) return;
-
       const now = Date.now();
       const now = Date.now();
       if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) return;
       if (now - lastSwitchTime.current < MIN_SWITCH_INTERVAL) return;
-
       const diffX = touchStartX.current - e.changedTouches[0].clientX;
       const diffX = touchStartX.current - e.changedTouches[0].clientX;
       if (diffX > 60) handleTabSwitch(1);
       if (diffX > 60) handleTabSwitch(1);
       else if (diffX < -60) handleTabSwitch(-1);
       else if (diffX < -60) handleTabSwitch(-1);
@@ -93,42 +72,35 @@ export function MainLayout({ children }: MainLayoutProps) {
     window.addEventListener('wheel', handleWheel, { passive: false });
     window.addEventListener('wheel', handleWheel, { passive: false });
     window.addEventListener('touchstart', handleTouchStart, { passive: true });
     window.addEventListener('touchstart', handleTouchStart, { passive: true });
     window.addEventListener('touchend', handleTouchEnd, { passive: true });
     window.addEventListener('touchend', handleTouchEnd, { passive: true });
-
     return () => {
     return () => {
       window.removeEventListener('wheel', handleWheel);
       window.removeEventListener('wheel', handleWheel);
       window.removeEventListener('touchstart', handleTouchStart);
       window.removeEventListener('touchstart', handleTouchStart);
       window.removeEventListener('touchend', handleTouchEnd);
       window.removeEventListener('touchend', handleTouchEnd);
       clearTimeout(wheelTimeout.current);
       clearTimeout(wheelTimeout.current);
     };
     };
-  }, []);
+  }, [activeTab, onTabChange]);
 
 
   const currentIndex = TAB_ORDER.indexOf(activeTab);
   const currentIndex = TAB_ORDER.indexOf(activeTab);
   const totalTabs = TAB_ORDER.length;
   const totalTabs = TAB_ORDER.length;
 
 
   return (
   return (
     <div className="min-h-screen bg-slate-50 flex flex-col overflow-x-hidden" style={{ overscrollBehaviorX: 'none' }}>
     <div className="min-h-screen bg-slate-50 flex flex-col overflow-x-hidden" style={{ overscrollBehaviorX: 'none' }}>
-      <Navbar activeTab={activeTab} onTabChange={setActiveTab} />
-
-      <main className="flex-1 w-full overflow-x-hidden relative">
+      <Navbar activeTab={activeTab} onTabChange={onTabChange} />
+      <main className="w-full overflow-hidden relative" style={{ height: 'calc(100vh - 60px)' }}>
         <div
         <div
           className="flex h-full will-change-transform"
           className="flex h-full will-change-transform"
           style={{
           style={{
-            // 轨道总宽度由标签页数量动态决定,6个页面就是 600%
             width: `${totalTabs * 100}%`,
             width: `${totalTabs * 100}%`,
-            // 偏移量
             transform: `translateX(-${(currentIndex / totalTabs) * 100}%)`,
             transform: `translateX(-${(currentIndex / totalTabs) * 100}%)`,
             transition: 'transform 0.7s cubic-bezier(0.34, 1.3, 0.64, 1)'
             transition: 'transform 0.7s cubic-bezier(0.34, 1.3, 0.64, 1)'
           }}
           }}
         >
         >
           {TAB_ORDER.map((tab) => (
           {TAB_ORDER.map((tab) => (
-            <div
-              key={tab}
-              className="shrink-0 flex justify-center pb-12"
-              // 每个页面的宽度是总宽度的 1/6
-              style={{ width: `${100 / totalTabs}%` }}
-            >
-              <div className="w-full max-w-[1600px] px-6 py-6">
-                {children(tab)}
+            <div key={tab} className="shrink-0 h-full overflow-y-auto" style={{ width: `${100 / totalTabs}%` }}>
+              <div className="flex justify-center pb-12">
+                <div className="w-full max-w-[1600px] px-6 py-6">
+                  {children(tab)}
+                </div>
               </div>
               </div>
             </div>
             </div>
           ))}
           ))}
@@ -136,4 +108,4 @@ export function MainLayout({ children }: MainLayoutProps) {
       </main>
       </main>
     </div>
     </div>
   );
   );
-}
+}

+ 4 - 1
knowhub/frontend/src/main.tsx

@@ -1,10 +1,13 @@
 import { StrictMode } from 'react'
 import { StrictMode } from 'react'
 import { createRoot } from 'react-dom/client'
 import { createRoot } from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
 import './index.css'
 import './index.css'
 import App from './App.tsx'
 import App from './App.tsx'
 
 
 createRoot(document.getElementById('root')!).render(
 createRoot(document.getElementById('root')!).render(
   <StrictMode>
   <StrictMode>
-    <App />
+    <BrowserRouter>
+      <App />
+    </BrowserRouter>
   </StrictMode>,
   </StrictMode>,
 )
 )

+ 11 - 1
knowhub/frontend/src/pages/Dashboard.tsx

@@ -67,15 +67,24 @@ function CompactListCard({ data, type, onDrillDown }: { data: any, type: 'req' |
 }
 }
 
 
 
 
-export function Dashboard() {
+export function Dashboard({ pendingNode, onPendingConsumed }: { pendingNode?: string | null, onPendingConsumed?: () => void }) {
   type NavItem = { type: 'node' | 'req' | 'cap' | 'tool' | 'know', data: any };
   type NavItem = { type: 'node' | 'req' | 'cap' | 'tool' | 'know', data: any };
   const [treeData, setTreeData] = useState<any>(null);
   const [treeData, setTreeData] = useState<any>(null);
   const [selectedNode, setSelectedNode] = useState<any>(null);
   const [selectedNode, setSelectedNode] = useState<any>(null);
   const [navStack, setNavStack] = useState<NavItem[]>([]);
   const [navStack, setNavStack] = useState<NavItem[]>([]);
+  const [nameToNodeMap, setNameToNodeMap] = useState<Record<string, any>>({});
   const [dbData, setDbData] = useState<{ reqs: any[], caps: any[], tools: any[], know: any[] }>({
   const [dbData, setDbData] = useState<{ reqs: any[], caps: any[], tools: any[], know: any[] }>({
     reqs: [], caps: [], tools: [], know: []
     reqs: [], caps: [], tools: [], know: []
   });
   });
 
 
+  // 处理来自其他页面的跳转请求
+  useEffect(() => {
+    if (pendingNode && nameToNodeMap[pendingNode]) {
+      setSelectedNode(nameToNodeMap[pendingNode]);
+      onPendingConsumed?.();
+    }
+  }, [pendingNode, nameToNodeMap]);
+
   useEffect(() => {
   useEffect(() => {
     if (selectedNode) setNavStack([{ type: 'node', data: selectedNode }]);
     if (selectedNode) setNavStack([{ type: 'node', data: selectedNode }]);
     else setNavStack([]);
     else setNavStack([]);
@@ -162,6 +171,7 @@ export function Dashboard() {
           });
           });
         };
         };
         buildNameMap([data]);
         buildNameMap([data]);
+        setNameToNodeMap(nameToNode);
 
 
         reqs.forEach((r: any) => {
         reqs.forEach((r: any) => {
           (r.source_nodes || []).forEach((sn: any) => {
           (r.source_nodes || []).forEach((sn: any) => {

+ 133 - 163
knowhub/frontend/src/pages/Relations.tsx

@@ -3,194 +3,164 @@ import { Target, Cpu, Wrench, ListTree, Waypoints } from 'lucide-react';
 import { getRequirements, getCapabilities, getTools } from '../services/api';
 import { getRequirements, getCapabilities, getTools } from '../services/api';
 import { cn } from '../lib/utils';
 import { cn } from '../lib/utils';
 
 
-export function Relations() {
+export function Relations({ onNavigateToDashboard }: { onNavigateToDashboard?: (nodeName: string) => void }) {
   const [reqs, setReqs] = useState<any[]>([]);
   const [reqs, setReqs] = useState<any[]>([]);
   const [caps, setCaps] = useState<any[]>([]);
   const [caps, setCaps] = useState<any[]>([]);
   const [tools, setTools] = useState<any[]>([]);
   const [tools, setTools] = useState<any[]>([]);
-  const [isLoading, setIsLoading] = useState(true);
-
-  // 记录当前点击高亮的节点 ID (格式如 "req:123" 或 "cap:456")
-  const [activeNode, setActiveNode] = useState<string | null>(null);
+  const [activeId, setActiveId] = useState<string | null>(null);
 
 
   useEffect(() => {
   useEffect(() => {
-    setIsLoading(true);
-    Promise.all([
-      getRequirements(1000),
-      getCapabilities(1000),
-      getTools(1000)
-    ]).then(([r, c, t]) => {
-      // --- 关键调试代码 ---
-      console.log("接口返回原始数据:", { r, c, t });
-
-      // 兼容两种返回格式:
-      // 1. { results: [...] }
-      // 2. 直接就是一个数组 [...]
-      const extract = (data: any) => (Array.isArray(data) ? data : (data?.results || []));
-
-      setReqs(extract(r));
-      setCaps(extract(c));
-      setTools(extract(t));
-      setIsLoading(false);
-    }).catch(err => {
-      console.error("接口请求彻底失败:", err);
-      setIsLoading(false); // 必须停掉转圈,否则用户没法用
-    });
+    Promise.all([getRequirements(1000), getCapabilities(1000), getTools(1000)])
+      .then(([r, c, t]) => {
+        setReqs(r.results || []);
+        setCaps(c.results || []);
+        setTools(t.results || []);
+      });
   }, []);
   }, []);
 
 
-  // 【核心算法 1:构建双向连通图】
-  // 把分散的表结构组合成一个内存图 (Adjacency List),便于递归寻路
-  const graph = useMemo(() => {
-     const g = new Map<string, string[]>();
-     const addEdge = (n1: string, n2: string) => {
-       if (!g.has(n1)) g.set(n1, []);
-       if (!g.has(n2)) g.set(n2, []);
-       // 建立无向图的双向连接
-       if (!g.get(n1)!.includes(n2)) g.get(n1)!.push(n2);
-       if (!g.get(n2)!.includes(n1)) g.get(n2)!.push(n1);
-     };
-
-     // 从 能力 寻找其关联的 需求
-     caps.forEach(c => {
-       (c.requirement_ids || []).forEach((rid: string) => addEdge(`cap:${c.id}`, `req:${rid}`));
-     });
-     // 从 工具 寻找其关联的 能力
-     tools.forEach(t => {
-       (t.capability_ids || []).forEach((cid: string) => addEdge(`tool:${t.id}`, `cap:${cid}`));
-     });
-     return g;
-  }, [caps, tools]);
-
-  // 【核心算法 2:BFS 广度优先搜索】
-  // 当选中一个节点时,找出网络中所有与其直接或间接连通的节点集合
-  const connectedNodes = useMemo(() => {
-     if (!activeNode) return new Set<string>();
-     const visited = new Set<string>();
-     const queue = [activeNode];
-     visited.add(activeNode);
-
-     while (queue.length > 0) {
-       const curr = queue.shift()!;
-       const neighbors = graph.get(curr) || [];
-       for (const n of neighbors) {
-         if (!visited.has(n)) {
-           visited.add(n);
-           queue.push(n);
-         }
-       }
-     }
-     return visited;
-  }, [activeNode, graph]);
+  // 核心:构建全网映射表
+  const adjacencyMap = useMemo(() => {
+    const map = new Map<string, Set<string>>();
+    const add = (a: string, b: string) => {
+      if (!map.has(a)) map.set(a, new Set());
+      if (!map.has(b)) map.set(b, new Set());
+      map.get(a)!.add(b);
+      map.get(b)!.add(a);
+    };
+
+    reqs.forEach(r => {
+      (r.capability_ids || []).forEach((cid: string) => add(`req:${r.id}`, `cap:${cid}`));
+    });
+    caps.forEach(c => {
+      (c.tool_ids || c.capability_ids || []).forEach((tid: string) => add(`cap:${c.id}`, `tool:${tid}`));
+      (c.requirement_ids || []).forEach((rid: string) => add(`cap:${c.id}`, `req:${rid}`));
+    });
+    tools.forEach(t => {
+      (t.capability_ids || []).forEach((cid: string) => add(`tool:${t.id}`, `cap:${cid}`));
+    });
+    return map;
+  }, [reqs, caps, tools]);
+
+  // 列顺序:只允许向右(序号更大)方向遍历,防止回路
+  const colOrder: Record<string, number> = { req: 0, proc: 1, cap: 2, tool: 3 };
+  const getColType = (nodeId: string) => nodeId.split(':')[0];
+
+  // 核心:有向 BFS,携带方向状态,禁止 U 形回头
+  // dir: 0=起点(双向), 1=向右, -1=向左
+  const relatedIds = useMemo(() => {
+    if (!activeId) return new Set<string>();
+    const visited = new Set<string>([activeId]);
+    const queue: [string, number][] = [[activeId, 0]];
+    while (queue.length > 0) {
+      const [u, dir] = queue.shift()!;
+      const uOrder = colOrder[getColType(u)] ?? -1;
+      const neighbors = adjacencyMap.get(u) || new Set();
+      neighbors.forEach(v => {
+        if (visited.has(v)) return;
+        const vOrder = colOrder[getColType(v)] ?? -1;
+        const goRight = vOrder > uOrder;
+        const goLeft = vOrder < uOrder;
+        const allowed = dir === 0 || (dir === 1 && goRight) || (dir === -1 && goLeft);
+        if (allowed) {
+          visited.add(v);
+          queue.push([v, goRight ? 1 : -1]);
+        }
+      });
+    }
+    return visited;
+  }, [activeId, adjacencyMap]);
+
+  // 高亮元素上浮:有选中时把相关项排到前面
+  const sortedItems = (items: any[], type: string) => {
+    if (!activeId) return items;
+    const activeType = activeId.split(':')[0];
+    if (type === activeType) return items; // 被点击的列不重排
+    return [...items].sort((a, b) => {
+      const aRel = relatedIds.has(`${type}:${a.id}`) ? 0 : 1;
+      const bRel = relatedIds.has(`${type}:${b.id}`) ? 0 : 1;
+      return aRel - bRel;
+    });
+  };
 
 
-  // 渲染单个卡片的通用函数
-  const renderCard = (type: string, item: any, Icon: any, colorClass: string, bgClass: string, borderClass: string) => {
+  const renderCard = (type: string, item: any, Icon: any) => {
     const nodeId = `${type}:${item.id}`;
     const nodeId = `${type}:${item.id}`;
-    const isSelected = activeNode === nodeId; // 自己被点击
-    const isConnected = connectedNodes.has(nodeId); // 自己属于关联网络中的一员
-    const isDimmed = activeNode !== null && !isConnected; // 自己是个局外人(需要变暗)
+    const isSelected = activeId === nodeId;
+    const isRelated = !activeId || relatedIds.has(nodeId);
+    const dimmed = !!activeId && !isRelated;
+
+    // 需求列:展示 source_nodes 树节点标签(和 Dashboard 颜色一致)
+    const sourceNodeTags: string[] = type === 'req'
+      ? (item.source_nodes || []).slice(0, 4).map((sn: any) =>
+          typeof sn === 'object' ? (sn.node_name || sn.name || '') : sn
+        ).filter(Boolean)
+      : [];
+    const extraCount = type === 'req' ? Math.max(0, (item.source_nodes || []).length - 4) : 0;
 
 
     return (
     return (
       <div
       <div
-        key={item.id}
-        onClick={() => setActiveNode(isSelected ? null : nodeId)}
+        key={nodeId}
+        onClick={() => setActiveId(isSelected ? null : nodeId)}
         className={cn(
         className={cn(
-          "p-3 rounded-xl border transition-all duration-300 cursor-pointer text-left",
-          // 状态 1:当前亲自点击选中的主角
-          isSelected ? `ring-2 ring-offset-1 ${borderClass} bg-white shadow-md scale-[1.02]` :
-          // 状态 2:主角的兄弟姐妹(被点亮)
-          isConnected ? `${bgClass} ${borderClass} bg-white opacity-100` :
-          // 状态 3:无关人员(变暗,黑白)
-          isDimmed ? "opacity-30 grayscale border-slate-100 bg-slate-50 hover:opacity-60" :
-          // 状态 4:没有任何选中时的默认状态
-          `bg-white border-slate-200 hover:${borderClass} hover:shadow-md`
+          "p-3 rounded-xl cursor-pointer mb-2 select-none bg-white transition-all",
+          isSelected
+            ? "border border-orange-400 shadow-[0_0_0_1px_rgba(251,146,60,0.7)]"
+            : isRelated && activeId
+            ? "border border-orange-300"
+            : dimmed
+            ? "border border-transparent opacity-20 grayscale scale-95"
+            : "border border-transparent"
         )}
         )}
       >
       >
-        <div className="flex items-start gap-2 mb-1.5">
-          <Icon size={14} className={cn("mt-0.5 shrink-0", isSelected || isConnected ? colorClass : "text-slate-400")} />
-          <span className={cn("text-xs font-bold leading-snug line-clamp-2", isSelected || isConnected ? "text-slate-800" : "text-slate-600")}>
-            {item.name || item.description || item.id}
-          </span>
-        </div>
-        {item.introduction && type === 'tool' && (
-           <div className="text-[10px] text-slate-500 line-clamp-2 leading-relaxed pl-5">
-            {item.introduction}
+        <div className="flex items-start gap-2">
+          <Icon size={14} className={cn("mt-0.5 shrink-0", isSelected ? "text-orange-500" : "text-slate-400")} />
+          <div className="min-w-0">
+            <div className={cn("text-xs font-bold leading-snug", isSelected ? "text-orange-800" : "text-slate-700")}>
+              {item.name || item.description || item.id}
+            </div>
+            {sourceNodeTags.length > 0 && (
+              <div className="flex flex-wrap gap-1 mt-1.5" onClick={e => e.stopPropagation()}>
+                {sourceNodeTags.map((name: string) => (
+                  <span
+                    key={name}
+                    onClick={() => onNavigateToDashboard?.(name)}
+                    className="text-[9px] px-1.5 py-0.5 rounded-md bg-blue-100 text-blue-700 font-medium truncate max-w-[120px] cursor-pointer hover:bg-blue-200 transition-colors"
+                  >
+                    {name}
+                  </span>
+                ))}
+                {extraCount > 0 && (
+                  <span className="text-[9px] px-1.5 py-0.5 rounded-md bg-slate-100 text-slate-400">
+                    +{extraCount}
+                  </span>
+                )}
+              </div>
+            )}
           </div>
           </div>
-        )}
-        <div className="text-[9px] text-slate-400 font-mono mt-2 pl-5">ID: {item.id.substring(0,6)}...</div>
+        </div>
       </div>
       </div>
     );
     );
   };
   };
 
 
-  if (isLoading) {
-     return <div className="flex justify-center items-center h-64"><div className="w-8 h-8 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"></div></div>;
-  }
-
   return (
   return (
-    <div className="flex flex-col h-[calc(100vh-120px)] animate-in fade-in duration-500">
-      {/* 顶部标题区 */}
-      <div className="flex justify-between items-end shrink-0 mb-6">
-        <div>
-          <h1 className="text-2xl font-black text-slate-900 mb-1 flex items-center gap-2">
-            <Waypoints size={24} className="text-indigo-600"/> 全局关系追溯
-          </h1>
-          <p className="text-slate-500 text-sm">选择任意一列的节点,即可递归高亮展示整条产业链(直接与间接关联要素)。</p>
-        </div>
-        {activeNode && (
-          <button
-            onClick={() => setActiveNode(null)}
-            className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-600 text-sm font-bold rounded-xl transition-colors shadow-sm"
-          >
-            清除选中关系
-          </button>
-        )}
+    <div className="flex flex-col h-[calc(100vh-156px)] animate-in fade-in duration-500">
+      <div className="flex justify-between items-center mb-6 shrink-0">
+        <h1 className="text-2xl font-black text-slate-900 flex items-center gap-2">
+          <Waypoints className="text-indigo-600" /> 全局关系追溯
+        </h1>
+        {activeId && <button onClick={() => setActiveId(null)} className="text-xs bg-slate-200 px-3 py-1.5 rounded-lg font-bold hover:bg-slate-300">清除选中</button>}
       </div>
       </div>
 
 
-      {/* 4 列主体表格 */}
       <div className="grid grid-cols-4 gap-4 flex-1 min-h-0">
       <div className="grid grid-cols-4 gap-4 flex-1 min-h-0">
-
-        {/* 第 1 列:需求 */}
-        <div className="bg-slate-50/50 border border-slate-200 rounded-2xl flex flex-col overflow-hidden">
-          <div className="bg-indigo-50 px-4 py-3 border-b border-indigo-100 font-bold text-indigo-800 flex items-center justify-between shrink-0">
-            <div className="flex items-center gap-2"><Target size={16}/> 业务需求</div>
-            <span className="text-xs bg-white px-2 py-0.5 rounded-full text-indigo-600 shadow-sm">{reqs.length}</span>
-          </div>
-          <div className="p-3 overflow-y-auto custom-scrollbar flex-1 space-y-2">
-            {reqs.map(r => renderCard('req', r, Target, 'text-indigo-600', 'bg-indigo-50/40', 'border-indigo-400'))}
+        {[ {t:'req', l:'业务需求', i:Target, d:reqs}, {t:'proc', l:'生产工序', i:ListTree, d:[]}, {t:'cap', l:'原子能力', i:Cpu, d:caps}, {t:'tool', l:'执行工具', i:Wrench, d:tools} ].map(col => (
+          <div key={col.t} className="flex flex-col h-full bg-slate-100/50 rounded-2xl border border-slate-200 overflow-hidden">
+            <div className="px-4 py-3 font-bold text-slate-700 border-b bg-slate-200/50 flex justify-between">
+              {col.l} <span className="text-slate-400">{col.d.length}</span>
+            </div>
+            <div className="flex-1 overflow-y-auto p-3 custom-scrollbar">
+              {sortedItems(col.d, col.t).map(i => renderCard(col.t, i, col.i))}
+            </div>
           </div>
           </div>
-        </div>
-
-        {/* 第 2 列:工序(暂空) */}
-        <div className="bg-slate-50/50 border border-slate-200 rounded-2xl flex flex-col overflow-hidden opacity-90">
-          <div className="bg-slate-200 px-4 py-3 border-b border-slate-300 font-bold text-slate-700 flex items-center justify-between shrink-0">
-            <div className="flex items-center gap-2"><ListTree size={16}/> 生产工序</div>
-            <span className="text-xs bg-white px-2 py-0.5 rounded-full text-slate-500 shadow-sm">0</span>
-          </div>
-          <div className="p-4 overflow-y-auto custom-scrollbar flex-1 flex flex-col items-center justify-center text-slate-400 text-sm font-medium border-2 border-dashed border-slate-200 m-3 rounded-xl bg-slate-50/50">
-            预留扩展位,暂无数据
-          </div>
-        </div>
-
-        {/* 第 3 列:能力 */}
-        <div className="bg-slate-50/50 border border-slate-200 rounded-2xl flex flex-col overflow-hidden">
-          <div className="bg-emerald-50 px-4 py-3 border-b border-emerald-100 font-bold text-emerald-800 flex items-center justify-between shrink-0">
-            <div className="flex items-center gap-2"><Cpu size={16}/> 原子能力</div>
-            <span className="text-xs bg-white px-2 py-0.5 rounded-full text-emerald-600 shadow-sm">{caps.length}</span>
-          </div>
-          <div className="p-3 overflow-y-auto custom-scrollbar flex-1 space-y-2">
-            {caps.map(c => renderCard('cap', c, Cpu, 'text-emerald-600', 'bg-emerald-50/40', 'border-emerald-400'))}
-          </div>
-        </div>
-
-        {/* 第 4 列:工具 */}
-        <div className="bg-slate-50/50 border border-slate-200 rounded-2xl flex flex-col overflow-hidden">
-          <div className="bg-amber-50 px-4 py-3 border-b border-amber-100 font-bold text-amber-800 flex items-center justify-between shrink-0">
-            <div className="flex items-center gap-2"><Wrench size={16}/> 执行工具</div>
-            <span className="text-xs bg-white px-2 py-0.5 rounded-full text-amber-600 shadow-sm">{tools.length}</span>
-          </div>
-          <div className="p-3 overflow-y-auto custom-scrollbar flex-1 space-y-2">
-             {tools.map(t => renderCard('tool', t, Wrench, 'text-amber-600', 'bg-amber-50/40', 'border-amber-400'))}
-          </div>
-        </div>
-
+        ))}
       </div>
       </div>
     </div>
     </div>
   );
   );

+ 0 - 1
knowhub/internal_tools/__init__.py

@@ -10,7 +10,6 @@ KnowHub 内部工具
 # 重新导出原有工具供 Knowledge Manager 使用
 # 重新导出原有工具供 Knowledge Manager 使用
 from agent.tools.builtin.knowledge import (
 from agent.tools.builtin.knowledge import (
     knowledge_search,
     knowledge_search,
-    knowledge_save,
     knowledge_list,
     knowledge_list,
     knowledge_update,
     knowledge_update,
     knowledge_batch_update,
     knowledge_batch_update,

+ 362 - 0
knowhub/research/outdoor_character_workflow_summary_20260409.md

@@ -0,0 +1,362 @@
+# 户外场景人物活动照片生成工作流合集
+
+**调研日期**: 2026-04-09  
+**调研渠道**: 小红书、X (Twitter)、YouTube  
+**提取成果**: 6种完整工作流方案,12个工具,7个教程资源
+
+---
+
+## 一、核心工作流方案
+
+### Workflow 1: OpenArt Character Builder + 多场景一致性工作流
+
+**适用场景**: 需要保持角色一致性,在多个户外场景中生成人物活动照片
+
+**工具链**: OpenArt AI Character Builder → Nano Banana 2 / Seedance 2.0 → Kling 3.0
+
+**步骤拆解**:
+
+#### Step 1: 创建角色基础
+- **输入**: 选择风格(Style)、性别(Gender)、种族(Ethnicity)、年龄(Age)
+- **工具**: OpenArt Character Builder
+- **输出**: 锁定角色DNA的基础图像(480种组合可选)
+- **说明**: 无需编写复杂提示词,通过UI选择锁定角色特征
+
+#### Step 2: 生成角色参考表 (Character Reference Sheet)
+- **输入**: 上传Step 1生成的角色图像
+- **工具**: Nano Banana 2 / Seedance 2.0
+- **提示词**:
+```
+Create a professional character reference sheet based strictly on the uploaded reference image. 
+Use a clean, neutral plain background. Arrange into two horizontal rows:
+- Top row: four full-body standing views – front, left profile, right profile, back
+- Bottom row: Four close-up portraits – front, left profile, right profile, back
+Maintain perfect identity consistency across every panel.
+```
+- **输出**: 多视角角色参考表(8个面板)
+
+#### Step 3: 场景生成
+- **输入**: 角色参考表 + 场景提示词
+- **工具**: Seedance 2.0 / Kling 3.0
+- **提示词结构**:
+```json
+{
+  "subject": "角色描述",
+  "environment": {
+    "setting": "户外场景 (如:spring alpine meadow, cherry blossom street)",
+    "time_of_day": "golden hour / soft daylight",
+    "lighting": "natural lighting, volumetric light shafts"
+  },
+  "camera": {
+    "lens": "85mm / wide-angle"
+  },
+  "lighting": {
+    "type": "soft diffused daylight",
+    "effect": "rim lighting, gentle shadows"
+  }
+}
+```
+- **输出**: 角色在户外场景中的图像/视频
+
+#### Step 4: 视频生成 (可选)
+- **输入**: 起始帧 + 结束帧
+- **工具**: Seedance 2.0
+- **提示词**: "Show me what happens in between. USE MULTIPLE CAMERA ANGLES"
+- **输出**: 多镜头角度的过渡视频
+
+**关键技术要点**:
+- 角色一致性保证:使用Character Builder锁定DNA + 生成多视角参考表 + 所有场景使用相同参考图
+- 户外场景提示词要素:golden hour, volumetric light shafts, rim lighting, subsurface scattering
+
+**来源**: 小红书/X/YouTube调研 (@thetripathi58, @heyDhavall, @MaAyyoub)
+
+---
+
+### Workflow 2: ComfyUI + Blender 户外场景生成工作流
+
+**适用场景**: 需要高度可控的3D场景建模和电影级渲染效果
+
+**工具链**: Blender → ComfyUI → Z-Image Turbo → LTX-2.3 → ComfyUI-VideoColorGrading
+
+**步骤拆解**:
+
+#### Step 1: 场景建模
+- **输入**: 文本提示词或概念图
+- **工具**: Blender (在ComfyUI内运行)
+- **输出**: 3D场景模型(支持视口预览)
+
+#### Step 2: 渲染控制
+- **输入**: 3D模型 + 相机路径
+- **工具**: ComfyUI + Blender集成节点
+- **输出**: 渲染帧序列
+
+#### Step 3: 图像生成增强
+- **输入**: 渲染帧
+- **工具**: Z-Image Turbo
+- **输出**: 增强细节的图像
+
+#### Step 4: 视频生成
+- **输入**: 增强后的图像序列
+- **工具**: LTX-2.3 + WanAnimate
+- **输出**: 最终视频
+
+#### Step 5: 颜色分级
+- **输入**: 生成的视频 + 参考图像
+- **工具**: ComfyUI-VideoColorGrading (Kijai)
+- **流程**: 从参考图像生成3D color LUT → 两阶段扩散过程对齐源视频帧与参考风格 → 应用时间一致性颜色分级
+- **输出**: 颜色分级后的最终视频
+
+**优势**:
+- 完全可控的3D场景布局
+- 支持精确的相机路径设计
+- 专业的颜色分级流程
+
+**难度等级**: ⭐⭐⭐⭐⭐(需要Blender和ComfyUI专业知识)
+
+**来源**: X/YouTube调研
+
+---
+
+### Workflow 3: ComfyUI + BrushNet 婚纱摄影外景工作流
+
+**适用场景**: 婚纱摄影、商业人像外景合成
+
+**工具链**: ComfyUI → BrushNet → IC-Light → Segment Anything → IPAdapter
+
+**核心流程**:
+1. **模型及原图加载** → 设置宽高比、缩放模式
+2. **遮罩生成** → Segment Anything 自动分割人物
+3. **BrushNet 局部重绘背景**
+   - 优势:对遮罩边界控制优秀,不破坏主体
+   - 透视关系协调
+4. **IC-Light 打光融合** → 使人物与背景光影统一
+5. **高清修复放大** → 恢复原图尺寸
+6. **PS 细节还原** → 图层和遮罩处理
+
+**关键节点**: `comfyui-brushnet`, `comfyui-inpaint-nodes`, `comfyui-ipadapter_plus`
+
+**来源**: 小红书 (帖子: 婚纱摄影外景自由)
+
+---
+
+### Workflow 4: ComfyUI 人像摄影工作流 (SD1.5)
+
+**适用场景**: 换装、换背景、保持面部特征的人像创作
+
+**工具链**: ComfyUI → SD1.5 → ControlNet (OpenPose) → IPAdapter → LoRA
+
+**核心思路**:
+1. **蒙版遮罩 + Latent 复合** → 重绘目标区域,保证光影效果
+2. **ControlNet 固定姿态** → OpenPose 控制人物姿势
+3. **IPAdapter + WD14 标签提取** → 风格迁移
+
+**工作流特点**:
+- 保持面部特征不变
+- 可换装、换背景
+- 支持多 ControlNet 叠加 (Depth, Canny, OpenPose)
+
+**来源**: 小红书 (帖子: comfyui 人像摄影工作流)
+
+---
+
+### Workflow 5: Kontext + MidJourney 场景及角色一致性工作流
+
+**适用场景**: 叙事动画、多镜头分镜、大场景多主体融合
+
+**工具链**: Kontext (Flux) → MidJourney → GPT
+
+**5步流程**:
+1. **准备三视图** → 角色/关键道具的正面、侧面、背面图
+2. **准备分镜草图** → 手绘构图规划
+3. **做主场景氛围空镜** → 确定整体色调和氛围
+4. **做分镜场景空镜** → MJ 生成,使用 `--sref` 引用氛围图
+5. **主体与场景融合** → Kontext 双图/多图模式融合
+
+**关键技巧**:
+- 大场景分区处理:先融合局部,再拼合到全景
+- 小角度机位切换可直接用 Kontext,大角度需单独生成
+- 建议使用 Kontext Pro/Max 版本,开源 dev 版本效果有限
+
+**来源**: 小红书 (帖子: 用Kontext + MJ 控制场景及角色一致性)
+
+---
+
+### Workflow 6: Nano Banana + Kling 户外人物行走场景
+
+**适用场景**: 街拍松弛感写真、人物行走视频
+
+**工具链**: Nano Banana Pro → Kling 3.0 → Topaz Gigapixel AI → Photoshop
+
+**街拍松弛感写真流程**:
+1. **上传参考照片** → 清晰半身照
+2. **使用详细提示词** (见下方模板)
+3. **生成多视角变体** → 选择最佳
+4. **Topaz 放大** → 4K 输出
+5. **PS 合成清理** → 去除瑕疵
+6. **Kling 动画** → 首尾帧控制生成行走视频
+
+**提示词模板** (精简版):
+```
+参考我上传的照片,输出高分辨率彩色时尚人像。
+主体:年轻女性,长发微卷,米色宽松衬衫外套,白色蕾丝短裙,黑色皮包,手持咖啡。
+姿态:三分之四侧身,左脚微前,右手轻抬整理发丝,眼神自信直视镜头。
+环境:高端城市街区咖啡店门口,暖色橱窗虚化,石材路面反光。
+光线:阴天自然柔光,侧后缘光勾勒发丝和包带。
+摄影:全画幅 85mm 人像镜头,f/1.8,浅景深,人物位于右侧三分之一处。
+细节:肌肤保留毛孔,发丝清晰,皮包纹理可见,轻微胶片颗粒。
+比例:4:5
+```
+
+**来源**: 小红书 (帖子: Nano Banana生成街拍松弛感写真) + YouTube (Uvq85gcMSE0, SeuI2zIWQJ0)
+
+---
+
+## 二、工具清单汇总
+
+| 工具名称 | 类别 | 核心用途 | 状态 |
+|---------|------|---------|------|
+| **OpenArt Character Builder** | 角色生成 | 4步锁定角色DNA (风格/性别/种族/年龄) | 已验证 |
+| **Nano Banana / Nano Banana Pro** | 图像生成 | 角色参考表、多视角生成、街拍写真 | 已验证 |
+| **Seedance 2.0** | 图像/视频生成 | 角色一致性、场景生成、视频过渡 | 已验证 |
+| **Kling 3.0** | 视频生成 | 高质量视频、首尾帧控制、多镜头角度 | 已验证 |
+| **ComfyUI** | 工作流编排 | 节点式工作流,集成多种模型 | 已验证 |
+| **BrushNet** | 局部重绘 | 背景替换,遮罩边界控制优秀 | 已验证 |
+| **IC-Light** | 光影融合 | 人物与背景打光统一 | 已验证 |
+| **ControlNet** | 姿态控制 | OpenPose/Depth/Canny 固定构图 | 已验证 |
+| **IPAdapter** | 风格迁移 | 参考图风格提取 | 已验证 |
+| **Kontext (Flux)** | 图像融合 | 多主体场景融合,角色一致性 | 已验证 |
+| **MidJourney** | 场景生成 | 氛围空镜、分镜场景生成 | 已验证 |
+| **Topaz Gigapixel AI** | 图像放大 | 4K 无损放大 | 已验证 |
+| **Photoshop** | 后期处理 | 遮罩清理、细节还原、图层合成 | 已验证 |
+| **Segment Anything** | 图像分割 | 自动人物抠图 | 已验证 |
+| **Blender** | 3D建模 | 场景建模,可与ComfyUI集成 | 已验证 |
+| **Z-Image Turbo** | 图像增强 | 提升渲染帧质感和细节 | 已验证 |
+| **LTX-2.3** | 视频生成 | 图像序列转视频 | 已验证 |
+| **WanAnimate** | 视频生成 | 视频生成和动画 | 已验证 |
+| **ComfyUI-VideoColorGrading** | 视频后期 | 基于参考图的颜色分级 | 已验证 |
+| **ChatGPT** | 提示词优化 | 构建光线控制等专业提示词 | 已验证 |
+| **PixPrettyAI** | 图像生成平台 | 托管Nano Banana 2等模型 | 已验证 |
+
+---
+
+## 三、关键提示词模板
+
+### 角色参考表生成 (Nano Banana 2)
+```
+Create a professional character reference sheet based strictly on the uploaded reference image. 
+Use a clean, neutral plain background. Arrange into two horizontal rows:
+- Top row: four full-body standing views – front, left profile, right profile, back
+- Bottom row: Four close-up portraits – front, left profile, right profile, back
+Maintain perfect identity consistency across every panel.
+```
+
+### 户外场景生成 (Seedance 2.0 / Kling)
+```json
+{
+  "subject": "年轻女性,长发微卷,米色衬衫外套,白色蕾丝裙",
+  "environment": {
+    "setting": "spring alpine meadow / cherry blossom street / urban cafe",
+    "time_of_day": "golden hour / soft daylight",
+    "lighting": "natural lighting, volumetric light shafts",
+    "elements": ["flowers", "trees", "mountains", "cobblestone street"]
+  },
+  "camera": {
+    "lens": "85mm portrait / wide-angle",
+    "movement": "slow tracking / pan"
+  },
+  "lighting": {
+    "type": "soft diffused daylight",
+    "effect": "rim lighting, gentle shadows"
+  }
+}
+```
+
+### 自然光线人像提示词结构
+```
+A high-contrast cinematic portrait with dramatic rim lighting from behind.
+Subject: [描述人物特征、服装、动作]
+Lighting: 
+  - Strong direct light illuminating one side of face
+  - Clear highlights and definition
+  - Softly shadowed opposite side
+  - Light source direction clearly visible
+Environment: Dark abstract bokeh background / outdoor park setting
+Camera: 85mm lens, shallow depth of field
+Style: Professional DSLR portrait, film grain, vintage feel
+```
+
+### 光线类型关键词对照表
+| 光线类型 | 提示词关键词 | 适用场景 |
+|---------|------------|---------|
+| Rim Lighting | dramatic rim lighting from behind | 人像特写、逆光场景 |
+| Golden Hour | golden hour lighting, warm tones | 温馨场景、日落场景 |
+| Volumetric Light | volumetric light shafts, god rays | 森林、有遮挡的场景 |
+| Soft Diffused | soft diffused daylight, overcast | 日常场景、阴天场景 |
+| Subsurface Scattering | subsurface scattering for skin | 人像特写、皮肤质感 |
+
+---
+
+## 四、调研经验总结
+
+### 经验 1: 如何构造有效的搜索关键词
+
+当调研AI生图工作流/工序时,应该将用户需求转换为偏向「教程」、「工作流」、「全套方案」的搜索词,而非单一软件名。
+
+**原因**: 单一软件名搜索结果多为工具介绍,而工作流关键词能直接找到完整的多步工序方案。
+
+**案例**: 本次调研「户外场景人物活动照片」需求,构造了5个关键词:
+1. AI生图 户外人物 工作流 教程
+2. Stable Diffusion 真实场景人物 完整流程
+3. AI绘画 自然光线 人物活动 步骤详解
+4. ComfyUI 户外场景 人物生成 workflow
+5. AI生图 真实背景人物合成 全套方案
+
+每个关键词都包含「工作流/流程/方案/教程」等工序导向词,成功从300条结果中提取19个完整Workflow。
+
+### 经验 2: 多渠道调研的并行执行策略
+
+当需要多渠道(小红书、X、YouTube)调研同一主题时,应该为每个渠道启动独立的research子agent并行执行。
+
+**原因**: 
+- 各渠道数据结构不同,独立agent可针对性处理
+- 并行执行大幅缩短调研时间
+
+**案例**: 本次调研同时启动3个agent,分别使用中文关键词(小红书)和英文关键词(X、YouTube),每个agent搜索5个关键词×20条结果,总计300条结果,在约15分钟内完成全部调研并提取19个Workflow。
+
+**关键技巧**:
+1. 为不同渠道准备适配的关键词(中文/英文)
+2. 任务描述中明确要求「只提取多步工序,严格拆解步骤」
+3. 使用deconstruct agent类型自动进行工序拆解
+
+---
+
+## 五、工作流选择建议
+
+| 需求场景 | 推荐工作流 | 理由 |
+|---------|-----------|------|
+| **角色一致性优先** | Workflow 1 (OpenArt + Seedance) | 4步锁定DNA,参考表机制成熟 |
+| **3D场景精确控制** | Workflow 2 (Blender + ComfyUI) | 完全可控的3D布局,专业颜色分级 |
+| **婚纱摄影/商业人像** | Workflow 3 (BrushNet) | 遮罩控制精准,光影融合自然 |
+| **换装/换背景灵活操作** | Workflow 4 (ComfyUI SD1.5) | ControlNet + IPAdapter 组合灵活 |
+| **叙事动画/多镜头** | Workflow 5 (Kontext + MJ) | 分镜规划完善,大场景分区处理 |
+| **街拍松弛感写真** | Workflow 6 (Nano Banana + Kling) | 提示词模板成熟,视频生成流畅 |
+
+---
+
+## 六、教程资源索引
+
+| 来源 | 标题 | 链接 |
+|------|------|------|
+| **小红书** | BrushNet婚纱摄影外景工作流 | https://www.xiaohongshu.com/explore/67b8726e0000000029032412 |
+| **小红书** | ComfyUI人像摄影工作流(SD1.5) | https://www.xiaohongshu.com/explore/66253296000000000302272a |
+| **小红书** | Kontext + MJ场景及角色一致性 | https://www.xiaohongshu.com/explore/687771cf0000000024008e4b |
+| **小红书** | Nano Banana街拍松弛感写真 | https://www.xiaohongshu.com/explore/68e11fa700000000070363c7 |
+| **YouTube** | Add AI People to Your Renders | https://www.youtube.com/watch?v=Uvq85gcMSE0 |
+| **YouTube** | Turn One Image into Multiple Camera Angles | https://www.youtube.com/watch?v=SeuI2zIWQJ0 |
+| **本地缓存** | AI户外工作流合集原始数据 | `/root/Agent/.cache/research/ai_outdoor_workflow_collection_20260409.json` |
+
+---
+
+**文档生成时间**: 2026-04-09 19:38  
+**调研执行Agent**: Librarian Agent  
+**数据状态**: 已整理归档,待入库

+ 59 - 8
knowhub/server.py

@@ -18,7 +18,7 @@ from typing import Optional, List, Dict
 from pathlib import Path
 from pathlib import Path
 from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 
 
-from fastapi import FastAPI, HTTPException, Query, Header, Body, BackgroundTasks
+from fastapi import FastAPI, HTTPException, Query, Header, Body, BackgroundTasks, Request
 from fastapi.responses import HTMLResponse, FileResponse
 from fastapi.responses import HTMLResponse, FileResponse
 from fastapi.staticfiles import StaticFiles
 from fastapi.staticfiles import StaticFiles
 from pydantic import BaseModel, Field
 from pydantic import BaseModel, Field
@@ -2382,13 +2382,56 @@ def delete_requirement(req_id: str):
         raise
         raise
     except Exception as e:
     except Exception as e:
         raise HTTPException(status_code=500, detail=str(e))
         raise HTTPException(status_code=500, detail=str(e))
-@app.get("/")
-def frontend():
-    """KnowHub 管理前端"""
-    index_file = STATIC_DIR / "index.html"
-    if not index_file.exists():
-        return HTMLResponse("<h1>KnowHub Frontend Not Found</h1><p>Please ensure knowhub/frontend/dist/index.html exists. Run 'yarn build' in frontend directory.</p>", status_code=404)
-    return FileResponse(str(index_file))
+# ===== Relation API =====
+
+@app.get("/api/relation/{table_name}")
+async def get_relations(table_name: str, request: Request):
+    """通用关系表查询接口"""
+    allowed_tables = {
+        "capability_knowledge",
+        "capability_tool",
+        "knowledge_relation",
+        "knowledge_resource",
+        "requirement_capability",
+        "requirement_knowledge",
+        "tool_knowledge",
+        "tool_provider"
+    }
+    table_name = table_name.lower()
+    if table_name not in allowed_tables:
+        raise HTTPException(status_code=400, detail="Invalid table name")
+        
+    try:
+        params = dict(request.query_params)
+        
+        where_clauses = []
+        values = []
+        for k, v in params.items():
+            if k in ["limit", "offset"]: continue
+            where_clauses.append(f"{k} = %s")
+            values.append(v)
+        
+        query = f"SELECT * FROM {table_name}"
+        if where_clauses:
+            query += " WHERE " + " AND ".join(where_clauses)
+            
+        limit = int(params.get("limit", 100))
+        query += " LIMIT %s"
+        values.append(limit)
+        
+        cursor = pg_store._get_cursor()
+        try:
+            cursor.execute(query, tuple(values))
+            rows = cursor.fetchall()
+            if not rows:
+                 return {"results": [], "count": 0}
+            colnames = [desc[0] for desc in cursor.description]
+            results = [dict(zip(colnames, row)) for row in rows]
+            return {"results": results, "count": len(results)}
+        finally:
+            cursor.close()
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))
 
 
 @app.get("/category_tree.json")
 @app.get("/category_tree.json")
 def serve_category_tree():
 def serve_category_tree():
@@ -2398,6 +2441,14 @@ def serve_category_tree():
         return {"error": "Not Found"}
         return {"error": "Not Found"}
     return FileResponse(str(tree_file))
     return FileResponse(str(tree_file))
 
 
+@app.get("/{full_path:path}")
+def frontend(full_path: str):
+    """KnowHub 管理前端 — 所有非 API 路径都返回 index.html,由 React Router 处理"""
+    index_file = STATIC_DIR / "index.html"
+    if not index_file.exists():
+        return HTMLResponse("<h1>KnowHub Frontend Not Found</h1><p>Please ensure knowhub/frontend/dist/index.html exists. Run 'yarn build' in frontend directory.</p>", status_code=404)
+    return FileResponse(str(index_file))
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     import uvicorn
     import uvicorn
     uvicorn.run(app, host="0.0.0.0", port=9999)
     uvicorn.run(app, host="0.0.0.0", port=9999)

+ 104 - 0
knowledge-2026-04-09-ai-image-workflows.json

@@ -0,0 +1,104 @@
+{
+  "knowledge": [
+    {
+      "task": "AI人像皮肤质感修复工作流",
+      "content": "完整工序:\n1. 生成基础人像图片 - 工具:Midjourney / Google Imagen 4 (via Whisk) / Reeve / Flux Pro Ultra - 输入:近景人像提示词(如'close-up portrait, photorealistic, DSLR, 85mm')- 输出:AI生成的基础人像图片(可能存在塑料皮肤问题)\n2. 裁剪人脸区域(如需要)- 工具:图像裁剪工具 - 输入:基础人像图片,将人脸区域裁剪至更近的特写 - 输出:裁剪后的人脸特写图片\n3. 修复AI塑料皮肤质感 - 工具:Enhancor.ai (Skin Realism + Portrait Upscaler) - 输入:AI生成的人像图片,设置皮肤纹理强度、毛孔细节参数 - 输出:具有真实皮肤纹理、毛孔细节的人像图片\n4. 修复眼部瑕疵(可选)- 工具:Enhancor.ai 眼部修复功能 - 输入:修复后的人像图片,指定眼部区域 - 输出:眼部细节完美的人像图片\n5. 超分辨率放大 - 工具:Enhancor.ai Portrait Upscaler - 输入:修复后的人像图片,选择放大倍数(如4x)- 输出:billboard级别的高分辨率人像图片\n适用场景:商业人像摄影、广告牌级别输出、需要真实皮肤质感的AI人像",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI图像生成",
+        "focus": "人像、皮肤质感、超分辨率"
+      },
+      "score": 5
+    },
+    {
+      "task": "多视角角色一致性工作流(Nano Banana Pro)",
+      "content": "完整工序:\n1. 创建角色参考表 - 工具:Nano Banana Pro (Higgsfield AI) - 输入:结构化提示词(如'Create a professional character reference sheet with four vertical columns: front view, left profile, right profile, back view. Each column contains full-body view on top and matching close-up portrait below. Photorealistic, DSLR, muted tones.')- 输出:包含四个视角(正面、左侧、右侧、背面)的角色参考表,每个视角包含全身图和近景肖像\n2. 生成多视角近景肖像 - 工具:Nano Banana Pro - 输入:角色参考表作为参考图,指定视角(如'front portrait close-up')- 输出:一致角色的多视角近景肖像图片\n3. 生成生活场景图 - 工具:Nano Banana Pro - 输入:角色参考表,场景描述(如'coffee shop lifestyle shot')- 输出:角色在不同生活场景中的图片\n适用场景:AI虚拟influencer、角色设计、需要多角度一致性的项目",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI图像生成",
+        "focus": "角色一致性、多视角、近景肖像"
+      },
+      "score": 5
+    },
+    {
+      "task": "面部表情变换工作流(Canva AI Face Expression)",
+      "content": "完整工序:\n1. 上传基础照片 - 工具:Canva AI Face Expression App - 输入:单张人物照片(正面、清晰面部)- 输出:上传至Canva的基础照片\n2. 选择目标表情 - 工具:Canva AI Face Expression App - 输入:选择预设表情(如微笑、惊讶、严肃、开心等)- 输出:应用表情后的人物图片\n3. 批量生成多表情版本 - 工具:Canva AI Face Expression App - 输入:重复选择不同表情,批量处理 - 输出:同一人物的多种表情图片集合\n适用场景:YouTube缩略图制作、社交媒体内容、需要快速生成多种表情的场景",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI图像生成",
+        "focus": "面部表情、批量生成"
+      },
+      "score": 4
+    },
+    {
+      "task": "AI虚拟网红完整工作流(KORA Pro + Enhancor + Lip Sync)",
+      "content": "完整工序:\n1. 生成高质量基础人像 - 工具:KORA Pro (Enhancor) - 输入:详细的人像提示词,包括人物特征、服装、光线、相机参数 - 输出:高真实度的基础人像图片\n2. 构建角色多视角图 - 工具:Nano Banana Pro - 输入:基础人像作为参考,生成多角度角色表 - 输出:包含正面、侧面、背面视角的角色参考表\n3. 修复皮肤质感 - 工具:Enhancor AI V3 Skin Fix - 输入:生成的角色图片,调整皮肤纹理参数 - 输出:具有真实皮肤质感的角色图片\n4. 生成生活场景图 - 工具:Nano Banana Pro - 输入:角色参考表,场景描述(如'studio shot', 'lifestyle scene')- 输出:角色在不同场景中的图片\n5. 唇形同步动画 - 工具:Enhancor Lip Sync V1/V2 - 输入:角色图片 + 自定义音频文件 - 输出:角色说话的视频(带唇形同步)\n适用场景:AI虚拟网红、虚拟主播、需要说话动画的角色",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI图像生成",
+        "focus": "虚拟网红、唇形同步、角色一致性"
+      },
+      "score": 5
+    },
+    {
+      "task": "4K超高清AI图像生成工作流(ComfyUI + DyPE)",
+      "content": "完整工序:\n1. 设置ComfyUI工作流 - 工具:ComfyUI (本地安装 via Promptus) - 输入:安装DyPE模型(https://github.com/guyyariv/DyPE)- 输出:配置好的ComfyUI环境\n2. 生成原生4K图像 - 工具:ComfyUI + DyPE模型 - 输入:人像提示词,设置分辨率为4K,选择DyPE采样器 - 输出:原生4K分辨率的AI人像图片\n3. 超分辨率增强(可选)- 工具:ComfyUI 4K超分辨率节点 - 输入:生成的4K图片,选择放大倍数 - 输出:更高分辨率、细节更丰富的图片\n适用场景:需要超高清输出的商业项目、影院级画质需求",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI图像生成",
+        "focus": "4K、超分辨率、ComfyUI"
+      },
+      "score": 4
+    },
+    {
+      "task": "角色一致性视频生成工作流(OpenArt + Kling/HeyGen)",
+      "content": "完整工序:\n1. 创建一致角色基础图 - 工具:OpenArt AI - 输入:角色描述提示词,上传参考图(如有)- 输出:高质量的角色基础图片\n2. 生成多视角角色图 - 工具:OpenArt AI - 输入:基础图作为参考,指定不同视角和姿势 - 输出:同一角色的多视角图片集合\n3. 生成视频 - 工具:Kling AI / HeyGen - 输入:角色图片 + 动作/对话描述 - 输出:角色动画视频\n4. 唇形同步(如需要)- 工具:HeyGen - 输入:角色视频 + 音频脚本 - 输出:带完美唇形同步的角色说话视频\n适用场景:AI视频创作、虚拟主播、需要角色一致性的视频项目",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "AI视频生成",
+        "focus": "角色一致性、视频生成、唇形同步"
+      },
+      "score": 5
+    },
+    {
+      "task": "背景虚化效果工作流(Photoshop Lens Blur / DxO Photolab)",
+      "content": "完整工序:\n1. 导入人像图片 - 工具:Photoshop / DxO Photolab 9 - 输入:需要添加背景虚化的人像图片 - 输出:导入的图片\n2. 创建主体选区 - 工具:Photoshop 智能选区 / DxO 自动主体检测 - 输入:选择主体(人物)区域 - 输出:精确的人物选区\n3. 应用镜头虚化效果 - 工具:Photoshop Lens Blur / DxO Photolab 9 Bokeh - 输入:设置虚化强度、光圈形状、焦距参数 - 输出:具有专业镜头虚化效果的图片\n4. 微调边缘过渡 - 工具:Photoshop 蒙版羽化 - 输入:调整选区边缘羽化值 - 输出:自然过渡的虚化效果图片\n适用场景:人像摄影后期、需要背景虚化效果的AI生成图片",
+      "types": ["strategy"],
+      "tags": {
+        "source": "YouTube",
+        "domain": "图像后期处理",
+        "focus": "背景虚化、bokeh、人像后期"
+      },
+      "score": 4
+    }
+  ],
+  "resources": [
+    {
+      "id": "https://github.com/guyyariv/DyPE",
+      "type": "github_repo"
+    }
+  ],
+  "tools": [
+    {"name": "Midjourney", "introduction": "AI图像生成工具,支持高质量人像生成"},
+    {"name": "Google Imagen 4", "introduction": "Google的AI图像生成模型,通过Whisk平台访问"},
+    {"name": "Reeve", "introduction": "AI图像生成工具"},
+    {"name": "Flux Pro Ultra", "introduction": "AI图像生成工具,支持高质量人像"},
+    {"name": "Enhancor.ai", "introduction": "AI人像增强工具,提供皮肤质感修复和超分辨率功能"},
+    {"name": "Nano Banana Pro", "introduction": "Higgsfield AI的角色一致性生成工具"},
+    {"name": "Canva AI Face Expression", "introduction": "Canva的面部表情变换AI应用"},
+    {"name": "KORA Pro", "introduction": "Enhancor的高质量人像生成工具"},
+    {"name": "ComfyUI", "introduction": "本地AI图像生成工作流工具"},
+    {"name": "DyPE", "introduction": "ComfyUI的4K图像生成模型"},
+    {"name": "OpenArt AI", "introduction": "AI图像生成平台"},
+    {"name": "Kling AI", "introduction": "AI视频生成工具"},
+    {"name": "HeyGen", "introduction": "AI视频和唇形同步工具"},
+    {"name": "Photoshop", "introduction": "专业图像编辑软件"},
+    {"name": "DxO Photolab 9", "introduction": "专业照片处理软件,支持镜头虚化效果"}
+  ]
+}

+ 398 - 0
knowledge/ComfyUI_画中画分阶段生成工作流.md

@@ -0,0 +1,398 @@
+# ComfyUI 画中画分阶段生成工作流
+
+**知识 ID**: knowledge-20260411-155313-comfyinpaint  
+**类型**: strategy  
+**评分**: ⭐⭐⭐⭐⭐ (5/5)  
+**来源**: ComfyUI 社区/YouTube 教程  
+**领域**: AI 图像生成  
+**工作流类型**: 画中画分阶段生成  
+**核心工具**: ControlNet, IP-Adapter, Inpainting, Z-Image-Turbo
+
+---
+
+## 核心设计理念
+
+### 分阶段生成 + 双控架构
+
+**分阶段流程**:
+```
+背景生成 → 画中画区域生成 → 风格统一 → 细节优化
+```
+
+**双控架构**:
+- **ControlNet**: 锁结构(几何/深度/姿态控制)
+- **IP-Adapter**: 注入风格(风格/纹理/色彩迁移)
+
+---
+
+## 工作流总览
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│  阶段 1: 背景/外框生成                                        │
+│  输入:背景提示词 + 参考图 → ControlNet 控制 → 生成背景       │
+└─────────────────────────────────────────────────────────────┘
+                            ↓
+┌─────────────────────────────────────────────────────────────┐
+│  阶段 2: 画中画区域生成 (Inpainting)                          │
+│  输入:背景图 + 蒙版 + 画中画提示词 → 局部重绘 → 生成内容     │
+└─────────────────────────────────────────────────────────────┘
+                            ↓
+┌─────────────────────────────────────────────────────────────┐
+│  阶段 3: 风格统一 (IP-Adapter)                                │
+│  输入:合成图 + 风格参考图 → IP-Adapter 注入 → 风格融合       │
+└─────────────────────────────────────────────────────────────┘
+                            ↓
+┌─────────────────────────────────────────────────────────────┐
+│  阶段 4: 细节优化 (ControlNet + Detailer)                     │
+│  输入:风格化图 → ControlNet 微调 → FaceDetailer → 最终输出  │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 阶段 1:背景/外框生成
+
+### 节点配置
+
+| 节点类型 | 节点名称 | 参数设置 |
+|----------|----------|----------|
+| Checkpoint | SDXL/Flux 主模型 | `albedobaseXL_v21.safetensors` 或 `flux1-dev.safetensors` |
+| ControlNet | Apply Advanced ControlNet | 权重 0.6-0.75 |
+| ControlNet 模型 | ControlNetLoader | `control_sdxl_canny.safetensors` 或 `depth` |
+| 预处理器 | AIO Aux Preprocessor | Canny/Depth/MLSD(根据场景选择) |
+| CLIP Text Encode | 背景提示词 | 描述整体场景氛围 |
+| KSampler | 采样器 | 20-30 步,CFG 7-8,DPM++ 2M Karras |
+
+### 操作步骤
+
+1. **加载背景参考图**(可选)
+   - 使用 `Load Image` 节点加载参考图
+   - 通过 `AIO Aux Preprocessor` 提取结构信息
+
+2. **设置 ControlNet 控制**
+   ```
+   参考图 → 预处理器 → ControlNet 模型 → Apply Advanced ControlNet
+   ```
+   - 控制权重:`0.6-0.75`
+   - 控制模式:`Balanced` 或 `ControlNet is more important`
+   - Starting/Ending Step:`0.0-1.0`(全程控制)
+
+3. **编写背景提示词**
+   ```
+   A cozy living room with large empty wall frame, warm lighting, 
+   modern minimalist interior, soft shadows, high quality photography
+   ```
+
+4. **生成背景图**
+   - 分辨率:`1024x1024` 或 `1280x720`(根据最终输出设定)
+   - 批次数:1(单张背景)
+
+---
+
+## 阶段 2:画中画区域生成(Inpainting)
+
+### 三种实现方案对比
+
+| 方案 | 核心节点 | 适用场景 | 优点 | 缺点 |
+|------|----------|----------|------|------|
+| **A: 原生节点法** | Inpaint Model Conditioning | 理解底层逻辑 | 学习价值高 | 连线复杂 |
+| **B: KJ Nodes 简化版** ⭐ | KJ Nodes: Inpaint Node | 生产环境 | 工作流简洁 | 需安装自定义节点 |
+| **C: SAM 3 智能重绘** | SAM 3 + Grounding DINO | 自动化场景 | 无需手动蒙版 | 依赖 AI 识别准确度 |
+
+### 方案 A:原生节点法
+
+**节点连接**:
+```
+背景图 → VAE Encode → Inpaint Model Conditioning → KSampler → VAE Decode
+蒙版图 ↗
+```
+
+**关键参数**:
+- `denoise`: `0.6-0.8`(重绘强度)
+- `mask_expand`: `10-20`(蒙版扩展像素)
+- 模糊蒙版边缘:让重绘更融合
+
+### 方案 B:KJ Nodes 简化版(推荐)
+
+**核心节点**:
+- `KJ Nodes: Inpaint Node`: 简化重绘流程
+- `KJ Nodes: Mask Editor`: 可视化蒙版编辑
+
+**优势**:工作流连线简洁,适合生产环境
+
+### 方案 C:SAM 3 智能重绘
+
+**核心节点**:
+- `SAM 3 (Segment Anything)`: 自动识别目标区域
+- `Grounding DINO`: 文本引导分割
+
+**操作步骤**:
+1. 输入文本指令:`"the empty frame on the wall"`
+2. SAM 3 自动识别并生成蒙版
+3. 自动执行局部重绘
+
+### 关键参数设置
+
+| 参数 | 推荐值 | 说明 |
+|------|--------|------|
+| denoise | 0.65-0.75 | 平衡原图保留与重绘效果 |
+| mask_expand | 15-25 | 确保边缘融合自然 |
+| CFG | 7-8 | 避免过拟合 |
+| Steps | 25-30 | 保证细节质量 |
+
+### 画中画提示词示例
+
+```
+A beautiful landscape painting inside the frame, 
+mountain lake at sunset, golden hour lighting, 
+oil painting style, detailed brushstrokes, masterpiece
+```
+
+---
+
+## 阶段 3:风格统一(IP-Adapter)
+
+### 核心原理
+
+> **IP-Adapter 负责风格注入,ControlNet 负责结构保持**
+
+⚠️ **重要**: IP-Adapter 单独使用易结构崩坏、手部细节失控,必须配合 ControlNet 使用
+
+### 节点配置
+
+| 节点类型 | 节点名称 | 参数设置 |
+|----------|----------|----------|
+| IPAdapter | IPAdapter Plus | `PLUS(high strength)` 预设 |
+| CLIP Vision | CLIP-ViT-H-14-laion2B-s32B | 风格编码模型 |
+| IPAdapter 权重 | 权重值 | `0.6-0.85` |
+| 合并嵌入组 | concat / average | `concat`(冲突感)或 `norm average`(柔和) |
+| 权重类型 | style transfer | 风格迁移模式 |
+
+### 工作流连接
+
+```
+风格参考图 → IPAdapter Encode → IPAdapter Apply → KSampler
+合成图 → VAE Encode ↗
+```
+
+### 多风格融合
+
+```
+风格图 A → IPAdapter Encode (权重 1.20) ┐
+风格图 B → IPAdapter Encode (权重 1.00) → 合并嵌入组 (concat) → Apply
+```
+
+### 关键参数推荐
+
+| 场景 | IPAdapter 权重 | 合并方式 | 权重类型 |
+|------|---------------|----------|----------|
+| 轻微风格渗透 | 0.4-0.6 | average | style transfer |
+| 平衡融合 | 0.65-0.75 | norm average | style transfer |
+| 强烈风格化 | 0.8-0.85 | concat | style transfer |
+| 多风格融合 | 1.0-1.2 (主) + 0.8 (辅) | concat | style transfer |
+
+### 避坑指南
+
+⚠️ **权重过高** (> 0.85) 易导致画面紊乱  
+✅ **解决方案**: 使用 `fade` 类型 + 噪波强度 0.6 + 模糊 2
+
+---
+
+## 阶段 4:细节优化
+
+### 4.1 ControlNet 微调
+
+**目的**: 在风格化后保持关键结构
+
+| ControlNet 类型 | 权重 | 适用场景 |
+|----------------|------|----------|
+| Canny | 0.4-0.6 | 保持边缘清晰 |
+| Depth | 0.5-0.7 | 保持空间层次 |
+| Tile | 0.6-0.8 | 细节增强/放大 |
+
+**配置技巧**:
+- 使用较低的权重(0.4-0.6)进行微调
+- Starting Step 设为 `0.3-0.5`(后期介入)
+- 避免过度控制导致风格丢失
+
+### 4.2 FaceDetailer / HandDetailer
+
+**目的**: 修复面部/手部细节
+
+| 节点 | 模型 | 参数 |
+|------|------|------|
+| FaceDetailer | `face_yolov8m.pt` | denoise 0.2-0.5 |
+| HandDetailer | `hand_yolov8s.pt` | denoise 0.3-0.6 |
+| SAM | `sam_vit_b_01ec64.pth` | 精准检测 |
+
+**工作流程**:
+```
+风格化图 → 人脸/手部检测 → 局部放大 → 重绘修复 → 合成输出
+```
+
+### 4.3 高清放大(可选)
+
+| 节点 | 模型 | 用途 |
+|------|------|------|
+| Ultimate SD Upscale | `4x-UltraSharp.pth` | 4 倍高清放大 |
+| Tile ControlNet | `control_v11f1e_sd15_tile` | 放大时保持细节 |
+
+---
+
+## 完整参数汇总表
+
+### 阶段 1:背景生成
+| 参数 | 推荐值 |
+|------|--------|
+| 模型 | SDXL / Flux |
+| ControlNet 权重 | 0.6-0.75 |
+| Steps | 20-25 |
+| CFG | 7-8 |
+| 分辨率 | 1024x1024 |
+
+### 阶段 2:Inpainting
+| 参数 | 推荐值 |
+|------|--------|
+| denoise | 0.65-0.75 |
+| mask_expand | 15-25 |
+| Steps | 25-30 |
+| CFG | 7-8 |
+
+### 阶段 3:IP-Adapter
+| 参数 | 推荐值 |
+|------|--------|
+| 权重 | 0.65-0.80 |
+| 预设 | PLUS(high strength) |
+| CLIP Vision | CLIP-ViT-H-14 |
+| 合并方式 | norm average |
+
+### 阶段 4:细节优化
+| 参数 | 推荐值 |
+|------|--------|
+| ControlNet 微调权重 | 0.4-0.6 |
+| FaceDetailer denoise | 0.2-0.5 |
+| Upscale 倍数 | 2x-4x |
+
+---
+
+## 必备自定义节点清单
+
+通过 **ComfyUI Manager** 安装:
+
+```bash
+# ControlNet 相关
+- ComfyUI's ControlNet Auxiliary Preprocessors
+- ComfyUI-Advanced-ControlNet
+
+# IPAdapter 相关
+- IPAdapter Plus (comfyui_ipadapter_plus)
+
+# 简化节点
+- KJ Nodes (comfyui-kjnodes)
+- Efficiency Nodes
+
+# 细节修复
+- Impact Pack (comfyui_essentials)
+- FaceDetailer
+
+# 智能分割
+- SAM 3 (Segment Anything)
+- Grounding DINO
+
+# 其他工具
+- CR Seamless Checker(无缝检查)
+- TextureViewer(3D 预览)
+```
+
+---
+
+## 实战案例:客厅挂画生成
+
+### 完整提示词
+
+**阶段 1 - 背景**:
+```
+A modern living room with a large empty wooden frame on the wall, 
+warm afternoon sunlight, minimalist interior design, 
+soft shadows, cozy atmosphere, interior photography, 8k --ar 16:9
+```
+
+**阶段 2 - 画中画**:
+```
+A serene mountain lake landscape at golden hour, 
+reflection in water, dramatic sky, oil painting style, 
+visible brushstrokes, impasto technique, masterpiece
+```
+
+**阶段 3 - 风格参考**:
+- 上传 1-2 张目标风格的油画作品作为 IP-Adapter 参考
+
+### 工作流执行顺序
+
+1. **生成背景**: 使用 Canny ControlNet 控制房间结构
+2. **绘制蒙版**: 在空画框区域绘制蒙版(或使用 SAM 3 自动识别)
+3. **局部重绘**: denoise 0.7,生成画中画内容
+4. **风格注入**: IP-Adapter 权重 0.75,融合油画风格
+5. **细节修复**: FaceDetailer 修复可能的人物(如有)
+6. **高清输出**: Ultimate SD Upscale 放大至 4K
+
+---
+
+## 常见问题与解决方案
+
+| 问题 | 原因 | 解决方案 |
+|------|------|----------|
+| 画中画边缘不融合 | 蒙版扩展不足 | 增加 `mask_expand` 至 20-30 |
+| 风格与背景不协调 | IP-Adapter 权重过高 | 降至 0.6-0.7,使用 `norm average` |
+| 画中画结构崩坏 | 缺少 ControlNet 控制 | 添加 Canny/Depth ControlNet(权重 0.5) |
+| 面部/手部畸形 | 细节质量不足 | 添加 FaceDetailer/HandDetailer |
+| 整体风格不统一 | 分阶段风格差异大 | 阶段 1 也使用相同 IP-Adapter 参考 |
+| 生成速度慢 | 多阶段串行执行 | 使用批处理,低分辨率测试后再高清生成 |
+
+---
+
+## 进阶技巧
+
+### 1. 多画中画嵌套
+```
+背景 → 画中画 A → 画中画 B(嵌套在 A 内)
+```
+- 使用多个 Inpainting 节点串联
+- 每个阶段使用独立蒙版
+- IP-Adapter 可分别控制不同区域风格
+
+### 2. 动态蒙版生成
+```
+Grounding DINO + SAM 3 → 自动蒙版 → Inpainting
+```
+- 文本指令:`"the frame on the wall"`
+- 全自动识别和重绘
+
+### 3. 风格渐变融合
+```
+IPAdapter A (权重 0.8) + IPAdapter B (权重 0.4) → 渐变融合
+```
+- 使用 `average` 合并方式
+- 调整权重比例控制风格倾向
+
+### 4. 批量生成变体
+```
+同一背景 + 不同画中画提示词 → 批量输出
+```
+- 使用 `Batch Prompt Schedule` 节点
+- 一次生成多个画中画变体
+
+---
+
+## 关联知识
+
+- **Midjourney v7 厚涂风格**: 可使用 MJ 生成风格参考图,再通过本工作流在 ComfyUI 中精确复现
+- **Nano Banana 多图融合**: 可将多图融合结果作为本工作流的输入
+- **ControlNet+IPAdapter 双控架构**: 本工作流的核心技术基础
+
+---
+
+## 更新日志
+
+- 2026-04-11: 初始入库,包含 4 阶段完整工作流、3 种 Inpainting 方案对比、实战案例

+ 165 - 0
knowledge/Midjourney_v7_Impasto_厚涂风格参数配置.md

@@ -0,0 +1,165 @@
+# Midjourney v7 Impasto 厚涂风格参数配置
+
+**知识 ID**: knowledge-20260411-155313-mj7impasto  
+**类型**: tool  
+**评分**: ⭐⭐⭐⭐⭐ (5/5)  
+**来源**: 小红书/Midjourney 官方文档  
+**领域**: AI 图像生成  
+**工具**: Midjourney v7  
+**风格**: Impasto 厚涂
+
+---
+
+## 核心发现
+
+Midjourney v7 在美学质量与笔触质感方面表现优异,特定 sref 代码可生成几乎与真实绘画无法区分的厚涂技法效果。**v8 版本灵活性降低,艺术风格探索建议继续使用 v7**。
+
+### v7 vs v8 厚涂风格对比
+
+| 维度 | v7 | v8 |
+|------|-----|-----|
+| 笔触质感 | ⭐⭐⭐⭐⭐ 自然流畅 | ⭐⭐⭐⭐ 稍显僵硬 |
+| 风格灵活性 | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐ 降低 |
+| 厚涂效果 | ⭐⭐⭐⭐⭐ 无敌 | ⭐⭐⭐⭐ 良好 |
+
+---
+
+## 核心参数详解
+
+### 1. `--sref` 风格参考代码
+
+**基础语法**:
+```bash
+/imagine prompt: [描述] --sref [风格代码] --v 7
+```
+
+**多风格融合**(最多 5 个):
+```bash
+/imagine prompt: [描述] --sref 代码 1 代码 2 代码 3 --v 7
+```
+
+**常用厚涂风格代码**:
+- **新派写实油画**: `sref 738792225` ⭐ 推荐
+- 获取方法:
+  1. 在 Discord 使用 `/describe` 分析喜欢的厚涂作品
+  2. 使用 `/style-save` 保存生成的风格
+  3. 从社区共享代码库获取(Reddit、PromptHero)
+
+### 2. `--sw` 风格权重参数
+
+**语法**:`--sw [0.1-2.0]`
+
+**权重范围与效果**:
+
+| 权重值 | 效果强度 | 适用场景 |
+|--------|----------|----------|
+| 0.1-0.3 | 轻微风格渗透 | 保留原风格基础上微调 |
+| 0.4-0.7 | 平衡融合 | 推荐默认值,风格与内容均衡 |
+| 0.8-1.2 | 强烈风格化 | 明显厚涂笔触质感 |
+| 1.3-2.0 | 极致风格 | 完全主导,可能牺牲细节 |
+
+**厚涂风格推荐设置**: `--sw 1.0-1.5`
+
+### 3. `--v 7` 版本锁定
+
+⚠️ **必须显式指定** `--v 7`,v8 版本灵活性降低不适合厚涂风格
+
+---
+
+## 完整提示词公式
+
+### 厚涂风格三段式公式
+
+```
+[主体画面描述] + [厚涂笔触/材质描述] + [光影/色彩氛围] --sref [代码] --sw [权重] --v 7 --ar [比例] --stylize [值]
+```
+
+### 厚涂专用关键词库
+
+| 关键词 | 中文含义 | 效果 |
+|--------|----------|------|
+| `thick impasto brushwork` | 厚涂笔触 | 增强笔触质感 |
+| `heavy paint texture` | 厚重颜料肌理 | 增加物理厚度 |
+| `visible brushstrokes` | 可见笔触 | 拒绝平滑感 |
+| `palette knife texture` | 调色刀纹理 | 特殊肌理效果 |
+| `impasto technique` | 厚涂技法 | 整体风格定义 |
+
+---
+
+## 实战示例
+
+### 示例 1:新派写实油画
+```bash
+{你的主体} + The painting is in a realistic oil painting style, with clearly visible brushstrokes, vibrant colors, and strong light. --chaos 30 --exp 85 --sref 738792225 --profile qvu9plm --v 7.0 --ar 2:3
+```
+
+### 示例 2:人物厚涂肖像
+```bash
+A warrior princess with flowing red hair, thick impasto oil painting style, visible brushstrokes, palette knife texture, dramatic chiaroscuro lighting, warm golden hour glow --sref 1847293 --sw 1.3 --v 7 --ar 2:3 --stylize 750
+```
+
+### 示例 3:风景厚涂
+```bash
+Mountain landscape at sunset, heavy impasto technique, thick layers of oil paint, textured canvas, bold brushwork, vibrant color palette, atmospheric perspective --sref 2938471 --sw 1.5 --v 7 --ar 16:9 --stylize 800
+```
+
+---
+
+## 参数组合推荐表
+
+| 场景 | --sref | --sw | --stylize | --q | --ar |
+|------|--------|------|-----------|-----|------|
+| 人物厚涂肖像 | 1-2 个代码 | 1.2-1.5 | 700-850 | 2 | 2:3 |
+| 风景厚涂 | 2-3 个代码 | 1.3-1.8 | 750-900 | 2 | 16:9 |
+| 静物厚涂 | 1 个代码 | 1.0-1.3 | 600-750 | 2 | 4:5 |
+| 抽象厚涂 | 2-3 个代码 | 1.5-2.0 | 850-1000 | 4 | 1:1 |
+| 快速测试 | 1 个代码 | 1.0 | 500 | 0.5 | 1:1 |
+
+---
+
+## 避坑指南
+
+| 问题 | 原因 | 解决方案 |
+|------|------|----------|
+| 厚涂效果不明显 | --sw 权重太低 | 提升至 1.3-1.8 |
+| 笔触过于粗糙 | --sw 权重过高 | 降至 0.8-1.2 |
+| 风格冲突 | 多代码不兼容 | 减少至 1-2 个代码 |
+| 细节丢失 | --stylize 过高 | 降至 600-750 |
+| 生成速度慢 | --q 4 + 多代码 | 先用 --q 0.5 测试 |
+| v8 效果不如 v7 | 版本灵活性差异 | 强制使用 --v 7 |
+
+---
+
+## 拒绝塑料感 4 招
+
+### 1. 拒绝平滑,增加物理厚度
+使用 `Thick impasto brushwork` 或 `Heavy paint texture` 关键词
+
+### 2. 治愈系万能公式
+```
+Deep blue twilight ambient + Warm orange window light
+```
+90% 冷调 + 10% 暖色,营造层次感
+
+### 3. 让水活过来
+使用 `Swirling water patterns` + `Churning white water`
+
+### 4. 参数降噪,返璞归真
+```bash
+--s 200 搭配 --style raw
+```
+降低 AI 过度修饰,回归自然质感
+
+---
+
+## 关联知识
+
+- **ComfyUI 厚涂复现**: 使用 IPAdapter + ControlNet 双控架构在 ComfyUI 中复现 MJ 厚涂风格
+- **风格代码获取**: `/describe` + `/style-save` 工作流
+- **v7 vs v8 对比**: v7 适合艺术风格探索,v8 适合精确解剖结构
+
+---
+
+## 更新日志
+
+- 2026-04-11: 初始入库,包含具体风格代码 738792225、--sw 参数详解、厚涂关键词库

+ 261 - 0
knowledge/Nano_Banana_多图融合_ComfyUI 工作流.md

@@ -0,0 +1,261 @@
+# Nano Banana 多图融合 ComfyUI 工作流
+
+**知识 ID**: knowledge-20260411-155313-nanobanana  
+**类型**: strategy  
+**评分**: ⭐⭐⭐⭐⭐ (5/5)  
+**来源**: 小红书/ComfyUI 社区  
+**领域**: AI 图像生成  
+**工作流类型**: 多图融合  
+**核心工具**: Nano Banana, Google Gemini Image, ComfyUI
+
+---
+
+## 核心能力
+
+| 能力指标 | 规格 |
+|----------|------|
+| 最多参考图数量 | **14 张**(实际 10 张可达 100% 还原) |
+| 角色一致性 | 单工作流最多 **5 个角色** + **14 个物体** |
+| 面部一致性 | **90-95%** |
+| 中文渲染 | 准确率 **90%+** |
+| 支持宽高比 | 包括极端比例 1:8、8:1、4:1、1:4 |
+| 产品一致性 | 标签文字不崩坏 |
+
+---
+
+## ComfyUI 工作流配置
+
+### 方法 1:单图编辑
+
+**工作流节点连接**:
+```
+Load Image → Image Scale → Google Gemini Image → LibLib Translate → Save Image
+```
+
+**参数设置**:
+- 图像缩放:`1536×1536`
+- 模型:`gemini-2.5-flash-image-preview`
+- 提示词:**必须包含** `"把这*张图组合成一张完整的新图像"`
+
+### 方法 2:双图编辑(推荐,效果更稳定)
+
+**工作流节点连接**:
+```
+Load Image (产品图) → Image Scale ┐
+Load Image (场景图) → Image Scale → Google Gemini Image → LibLib Translate → Save Image
+```
+
+**提示词示例**:
+```
+"把这两张图组合成一张完整的新图像,将产品自然融入场景中,保持产品标签文字清晰"
+```
+
+### 方法 3:三图编辑
+
+**工作流节点连接**:
+```
+Load Image (场景图) → Image Scale ┐
+Load Image (产品图) → Image Scale ├→ Google Gemini Image → LibLib Translate → Save Image
+Load Image (元素图) → Image Scale ┘
+```
+
+**提示词示例**:
+```
+"把这三张图组合成一张完整的新图像,将产品和杯子自然融入家居场景中,保持产品标签文字清晰"
+```
+
+---
+
+## 15 种多图融合场景
+
+| 序号 | 场景 | 描述 |
+|------|------|------|
+| 1 | 杂志封面 | 融合多张参考图生成专业封面 |
+| 2 | 漫画分镜 | 保持角色一致性的多格漫画 |
+| 3 | 信息图 | 自动搜索数据生成带图表的信息图 |
+| 4 | 情绪九宫格 | 同一人物 9 种表情 |
+| 5 | 极限运动场景 | 同一人物在不同运动场景 |
+| 6 | 产品对比海报 | 多产品对比展示 |
+| 7 | 科研绘图 | 顶刊级科研配图 |
+| 8 | 电影感人像 | 带详细相机参数的专业人像 |
+| 9 | 艺术风格转换 | 多风格同一人物 |
+| 10 | 背景替换 | 保持人物换背景 |
+| 11 | 服装替换 | 保持人物换服装 |
+| 12 | 创意场景转换 | 动作人偶风格等 |
+| 13 | 多语言信息图 | 一键翻译排版 |
+| 14 | 四方连续贴图 | 无缝纹理生成 |
+| 15 | 虚拟试衣 | 服装上身效果 |
+
+---
+
+## 实测心得
+
+### 关键发现
+
+1. **提示词决定出图效果** ⭐⭐⭐⭐⭐
+   - **必须写**: `"把这*张图组合成一张完整的新图像"`
+   - 缺少此句会导致生成效果不稳定
+
+2. **双图编辑效果更稳定** ⭐⭐⭐⭐
+   - 单图编辑:适合简单修改
+   - 双图编辑:推荐默认方案
+   - 三图以上:复杂度增加,需更具体提示词
+
+3. **产品一致性能力出色** ⭐⭐⭐⭐⭐
+   - 不必担心产品标签文字崩坏
+   - 适合电商产品场景图生成
+
+4. **清晰度优化**
+   - 如生成图清晰度不够,可使用高清放大工作流
+   - 推荐:Ultimate SD Upscale + Tile ControlNet
+
+---
+
+## 成本说明
+
+### Nano Banana 定价
+
+| 版本 | 分辨率 | 价格 | 备注 |
+|------|--------|------|------|
+| Nano Banana 2 | 1K | $0.067/张 | 性价比最高 |
+| Nano Banana 2 | 2K | $0.101/张 | |
+| Nano Banana 2 | 4K | $0.151/张 | |
+| Nano Banana Pro | 1K | $0.134/张 | 比 Flash 贵 50% |
+
+### 不同平台成本对比
+
+| 平台 | 成本范围 | 建议 |
+|------|----------|------|
+| 官方 API | $0.067-0.151/张 | 稳定可靠 |
+| 第三方平台 A | $0.02-0.08/张 | 价格低,注意稳定性 |
+| 第三方平台 B | $0.10-0.15/张 | 价格高,可能有额外功能 |
+
+⚠️ **注意**: 不同平台调用 Banana 的成本不同(从 $0.02~$0.15 不等),需根据需求选择
+
+---
+
+## 避坑指南
+
+| 问题 | 说明 | 解决方案 |
+|------|------|----------|
+| SynthID 水印 | 不可移除 | 接受或后期处理 |
+| Thinking 模式 | 强制启用 | 无法关闭,等待完成 |
+| 提示词不具体 | 效果不稳定 | 极度具体描述需求 |
+| 直接使用 Pro | 成本高 | 先用 Flash 测试提示词 |
+| 单次生成 | 成本高 | 使用 Batch API 节省 50% |
+
+### 推荐工作流
+
+```
+1. Flash 测试提示词 → 2. 优化提示词 → 3. Pro 批量生成 → 4. 高清放大
+```
+
+---
+
+## 实战案例
+
+### 案例 1:电商产品场景图
+
+**输入**:
+- 产品图:投影仪正面图
+- 场景图:雪山湖畔风景
+
+**提示词**:
+```
+"把这两张图组合成一张完整的新图像,将投影仪自然融入雪山湖畔场景中,保持产品外观和标签清晰"
+```
+
+**输出**:电商宣传场景图
+
+**耗时**:5-6 分钟  
+**成本**:$0.067 × 2(测试 + 正式)= $0.134
+
+---
+
+### 案例 2:中餐厅菜品图自动化
+
+**工作流**:Nanobanana + ComfyUI
+
+**流程**:
+```
+厨房出餐摆盘 → 手机随手拍 → 5-6 分钟生成专业菜单图
+```
+
+**支持**:
+- 横版/竖版可变尺寸
+- 批量生成
+- 保持菜品色泽和质感
+
+**应用场景**:
+- 餐厅菜单
+- 外卖平台图片
+- 社交媒体推广
+
+---
+
+### 案例 3:小说转短剧分镜
+
+**流程**:
+```
+1. 去 AO3 找短文
+2. 和 Gemini 说将其生成 12 张图
+3. 得到人物一致性非常高的一组图
+4. 配合图片转视频工具制作小说转短剧
+```
+
+**成本**:极低($0.067 × 12 = $0.804)
+
+**优势**:
+- 人物一致性高(90-95%)
+- 快速生成分镜
+- 可批量制作
+
+---
+
+## 与 ComfyUI 工作流整合
+
+### 整合方案
+
+```
+Nano Banana 多图融合 → ComfyUI 后处理
+                        ├─ ControlNet 结构微调
+                        ├─ IP-Adapter 风格统一
+                        ├─ FaceDetailer 细节修复
+                        └─ Upscale 高清放大
+```
+
+### 典型应用场景
+
+1. **电商产品图**:
+   - Nano Banana: 产品 + 场景融合
+   - ComfyUI: 细节优化、高清放大
+
+2. **角色一致性漫画**:
+   - Nano Banana: 多格分镜生成
+   - ComfyUI: 风格统一、对话框添加
+
+3. **科研绘图**:
+   - Nano Banana: 图表 + 数据融合
+   - ComfyUI: 标注优化、格式调整
+
+---
+
+## 关联知识
+
+- **ComfyUI 画中画工作流**: 可将 Nano Banana 生成结果作为输入进行进一步处理
+- **Midjourney v7 厚涂风格**: 可使用 MJ 生成风格参考图,通过 Nano Banana 融合到目标场景
+- **ControlNet+IPAdapter 双控架构**: ComfyUI 后处理的核心技术
+
+---
+
+## 官方资源
+
+- **API 文档**: https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image-preview
+- **DeepMind 模型页**: https://deepmind.google/models/gemini-image/flash/
+- **AI Studio**: https://aistudio.google.com/models/gemini-3-1-flash-image
+
+---
+
+## 更新日志
+
+- 2026-04-11: 初始入库,包含 ComfyUI 工作流配置、3 种编辑方法、15 种场景、3 个实战案例

+ 20 - 0
knowledge/knowledge-20260412-005015-nano-banana-image-urls.json

@@ -0,0 +1,20 @@
+{
+  "id": "knowledge-20260412-005015-nano-banana-image-urls",
+  "task": "在图像生成任务中使用 nano_banana 工具传递参考图",
+  "content": "当使用 nano_banana 工具进行图像生成时,image_urls 参数不支持本地相对路径,必须使用 HTTP/HTTPS CDN URL。\n\n## 错误做法\n\n**直接传本地相对路径**:\n```json\n{\n  \"image_urls\": [\"examples/production_restore/features/character_asset/character_ref_kneel.png\"]\n}\n```\n返回错误:HTTP 503: \"images[].data 看起来是文件路径但文件不存在\"\n\n**另一个错误示例**:\n```json\n{\"image_urls\": [\"examples/production_restore/img_1.png\"]}\n```\n报错:HTTP 503: images[].data 看起来是文件路径但文件不存在\n\n**注意**:虽然角色说明中提到 toolhub.py 内置的 `_preprocess_params` 函数会自动将本地路径上传到 OSS,但实际测试发现这个功能没有生效,需要手动调用 image_uploader 上传。\n\n## 正确做法\n\n1. 先调用 image_uploader 上传本地图片:\n```python\n# 步骤 1:上传图片\ncdn_url = image_uploader(local_path=\"examples/production_restore/img_1.png\")\n# 返回:https://res.cybertogether.net/toolhub_images/img_1.png\n\n# 步骤 2:调用 nano_banana\ntoolhub_call(tool_id=\"nano_banana\", params={\n    \"image_urls\": [cdn_url],\n    \"prompt\": \"...\"\n})\n```\n\n2. 再将 CDN URL 传入 nano_banana:\n```python\ntoolhub_call(tool_id=\"nano_banana\", params={\n  \"image_urls\": [\"https://res.cybertogether.net/toolhub_images/character_ref_kneel.png\"]\n})\n```\n\n## 原因\n\nnano_banana 工具运行在远程环境中,无法直接访问本地文件系统,必须通过 HTTP URL 访问图片。\n\n## 案例\n\nimg_1 生成任务中,最初传入相对路径导致 HTTP 503 错误,错误信息明确指出「请传 HTTP URL 或绝对路径,调用方应先把本地文件上传到 OSS 再传 CDN URL」。改用 image_uploader 上传三张参考图后获得 CDN URL,再次调用 nano_banana 成功生成图像。",
+  "types": ["experience"],
+  "tags": {
+    "intent": "图像生成",
+    "state": "nano_banana",
+    "tool": "image_uploader"
+  },
+  "score": 5,
+  "source": {
+    "category": "research"
+  },
+  "created_at": "2026-04-12T00:50:15Z",
+  "updated_at": "2026-04-12T01:53:12Z",
+  "tool_ids": ["tools/image_gen/nano_banana", "tools/upload/image_uploader"],
+  "capability_ids": ["CAP-003"],
+  "related_knowledge_ids": ["knowledge-20260409-182612-108d", "knowledge-20260412-010725-nano-banana-http503"]
+}

+ 20 - 0
knowledge/knowledge-20260412-010725-nano-banana-http503.json

@@ -0,0 +1,20 @@
+{
+  "id": "knowledge-20260412-010725-nano-banana-http503",
+  "task": "nano_banana 工具图片传递方式:需先上传获取 CDN URL",
+  "content": "当使用 nano_banana 工具进行图像生成时,如果传入本地文件路径会报错 HTTP 503:\"images[].data 看起来是文件路径但文件不存在\"。\n\n**错误做法**:直接传本地相对路径\n```json\n{\n  \"image_urls\": [\"examples/production_restore/features/character_asset/character_ref_kneel.png\"]\n}\n```\n\n**正确做法**:\n1. 先调用 image_uploader 上传本地图片:\n```python\nimage_uploader(local_path=\"examples/production_restore/features/character_asset/character_ref_kneel.png\")\n→ 返回 cdn_url: \"https://res.cybertogether.net/toolhub_images/character_ref_kneel.png\"\n```\n2. 再将 CDN URL 传入 nano_banana:\n```python\ntoolhub_call(tool_id=\"nano_banana\", params={\n  \"image_urls\": [\"https://res.cybertogether.net/toolhub_images/character_ref_kneel.png\"]\n})\n```\n\n**原因**:nano_banana 工具运行在远程环境中,无法直接访问本地文件系统,必须通过 HTTP URL 访问图片。\n\n**案例**:\n- img_1 生成:传入相对路径导致 HTTP 503 错误,改用 image_uploader 上传后成功\n- img_4 生成:最初直接传本地路径 \"examples/production_restore/features/character_asset/character_ref_side.png\" 导致失败,改用 image_uploader 上传 4 张图片获取 CDN URL 后成功生成",
+  "types": ["experience"],
+  "tags": {
+    "intent": "图像生成",
+    "state": "nano_banana",
+    "tool": "image_uploader"
+  },
+  "score": 4,
+  "source": {
+    "category": "research"
+  },
+  "created_at": "2026-04-12T01:07:25Z",
+  "updated_at": "2026-04-12T01:53:12Z",
+  "tool_ids": ["tools/image_gen/nano_banana", "tools/upload/image_uploader"],
+  "capability_ids": ["CAP-001", "CAP-011"],
+  "related_knowledge_ids": ["knowledge-20260409-182612-108d", "knowledge-20260412-005015-nano-banana-image-urls", "knowledge-20260412-010726-nano-banana-multi-fusion"]
+}

Разница между файлами не показана из-за своего большого размера
+ 3 - 0
knowledge/knowledge-20260412-010726-nano-banana-multi-fusion.json


+ 20 - 0
knowledge/knowledge-20260412-014259-3abf.json

@@ -0,0 +1,20 @@
+{
+  "id": "knowledge-20260412-014259-3abf",
+  "task": "在 nano_banana 图生图任务中,参考图需要先上传获取 CDN URL 再传入",
+  "content": "当使用 nano_banana 工具进行多图融合生图时,image_urls 参数不能直接传本地文件路径(如 examples/xxx.png),否则会报错「文件不存在」。正确做法是:先调用 image_uploader 工具将本地图片上传到 OSS 获取 CDN URL,再将 CDN URL 传入 nano_banana 的 image_urls 参数。案例:本次 img_1 生成任务中,先调用 image_uploader 上传 character_ref_img1.png、background_green_img1.png、palette_impasto_img1_v2.png、easel_blank_canvas_img4.png 四张参考图,获取 CDN URL 后传入 nano_banana,生成成功。",
+  "types": ["experience"],
+  "tags": {
+    "intent": "图像生成",
+    "state": "nano_banana",
+    "tool": "image_uploader"
+  },
+  "score": 4,
+  "source": {
+    "category": "research"
+  },
+  "created_time": 1776067979,
+  "tool_ids": ["tools/image_gen/nano_banana", "tools/upload/image_uploader"],
+  "capability_ids": [],
+  "requirement_ids": [],
+  "resource_ids": []
+}

+ 21 - 0
knowledge/strategy/品类分组判断标准.json

@@ -0,0 +1,21 @@
+{
+  "id": "knowledge-20260410-category-grouping",
+  "task": "工作流品类分组的判断标准",
+  "content": "在分析多条工作流进行品类分组时,应基于以下维度判断:\n\n1. **核心主体**:工作流围绕什么核心对象展开(人物/场景/光影/纹理)\n\n2. **技术手段相似性**:是否使用相似的工具链和技术方法\n\n3. **输出目标一致性**:最终产出的内容类型是否相同\n\n4. **粒度把握**:品类名称应品类无关(如'人物肖像'而非'电商人物肖像'),便于跨领域比较\n\n本次分析案例:\n- wf_002(局部特写)+ wf_003(近景肖像)→ 人物肖像类(都是人物主体,都涉及细节修复和身份锁定)\n- wf_001 → 场景构建类(场景 + 人物综合,涉及 3D 建模和场景合成)\n- wf_004 → 光影艺术类(专门的光影控制,使用 ControlNet/IC-Light 等)\n- wf_005 → 纹理风格类(材质纹理效果,涉及颗粒/纸张/复古风格)",
+  "types": ["strategy"],
+  "tags": {
+    "domain": "工作流分析",
+    "method": "品类分组",
+    "key_skill": "语义聚类"
+  },
+  "score": 4,
+  "source": {
+    "category": "execution"
+  },
+  "created_at": "2026-04-10T16:58:05",
+  "status": "active",
+  "related_knowledge": [
+    "knowledge-20260410-variant-analysis-method",
+    "knowledge-20260410-causal-reasoning"
+  ]
+}

+ 20 - 0
knowledge/strategy/粗工序因果推理方法.json

@@ -0,0 +1,20 @@
+{
+  "id": "knowledge-20260410-causal-reasoning",
+  "task": "工作流粗工序提取的因果推理方法",
+  "content": "在总结粗工序时,必须解释'为什么先做 A 再做 B',而不是简单列举频次。核心推理框架包括:\n\n1. **依赖关系原则**:后续步骤依赖前序步骤的输出(如'局部细节修复必须在身份锁定之后进行,否则修复结果无法保持角色一致性')\n\n2. **先整体后局部原则**:先建立基础框架再处理细节(如'首先生成基础人像,因为后续所有细节处理都依赖于一个可用的基础图像')\n\n3. **先生成后修复原则**:先产生可用结果再进行优化(如'放大和精修是最终输出前的必要步骤,放在后期处理')\n\n4. **分离 - 合成策略**:将独立元素分别处理后再合并(如'场景背景和人物图像分别生成后再进行合成,这种分离策略允许对场景和人物独立优化,避免相互干扰')\n\n5. **先规划后执行原则**:先设计蓝图再技术实现(如'整体光照方案是所有后续技术实现的基础蓝图,必须在布局确定后进行配置')\n\n6. **先基础后风格化原则**:先完成基础处理再进行艺术增强(如'色彩调整是光影/纹理处理完成后的最终润色步骤,过早调色会干扰基础效果的准确呈现')",
+  "types": ["strategy"],
+  "tags": {
+    "domain": "工作流分析",
+    "method": "粗工序提取",
+    "key_skill": "因果推理"
+  },
+  "score": 5,
+  "source": {
+    "category": "execution"
+  },
+  "created_at": "2026-04-10T16:58:05",
+  "status": "active",
+  "related_knowledge": [
+    "knowledge-20260410-variant-analysis-method"
+  ]
+}

+ 17 - 0
knowledge/strategy/能力模块变体分析方法.json

@@ -0,0 +1,17 @@
+{
+  "id": "knowledge-20260410-variant-analysis-method",
+  "task": "能力模块实现变体分析方法",
+  "content": "在工作流分析中,为能力模块分析实现变体的方法:\n\n1. **变体聚类依据**:根据工具/方法相似性进行聚类,每个模块聚类为 2-4 个变体\n\n2. **变体命名规范**:「工具/方法」+「方案」,如「LoRA 方案」、「IPAdapter 方案」、「ControlNet 方案」、「Photoshop 后期方案」等\n\n3. **变体结构**:\n   - name:变体名称(符合命名规范)\n   - description:变体描述(一句话说明核心方法)\n   - steps:典型执行步骤(2-4 步,动宾结构)\n   - source_workflows:来源工作流列表(如 [\"wf_001\", \"wf_002\"])\n\n4. **分析要点**:\n   - 忠实于原始工作流数据,不臆造不存在的工具或方法\n   - 变体之间应有明显的方法差异(如 AI 生成 vs 手工绘制 vs 外部素材)\n   - 步骤描述应体现因果性编排思路(为什么先做 A 再做 B)\n   - 注明每个变体来自哪些工作流,便于追溯\n\n5. **常见变体类型**:\n   - AI 生成方案(使用 FLUX/ComfyUI/Midjourney 等)\n   - 控制技术方案(ControlNet/LoRA/PuLID 等)\n   - 后期处理方案(Photoshop/Illustrator 等)\n   - 提示词方案(通过提示词工程实现)\n   - 3D/渲染方案(Blender 等)",
+  "types": ["strategy"],
+  "tags": {
+    "domain": "工作流分析",
+    "method": "变体分析",
+    "output_format": "JSON"
+  },
+  "score": 4,
+  "source": {
+    "category": "execution"
+  },
+  "created_at": "2026-04-10T16:54:50",
+  "status": "active"
+}

Разница между файлами не показана из-за своего большого размера
+ 9 - 0
knowledge_batch_20260409.json


+ 26 - 0
knowledge_batch_20260409_execution.json

@@ -0,0 +1,26 @@
+{
+  "upload_batch": "X 搜索接口稳定性问题",
+  "upload_date": "2026-04-09",
+  "source_type": "execution",
+  "knowledge_count": 1,
+  "knowledge": [
+    {
+      "id": "knowledge-20260409-exec-001",
+      "task": "X 搜索接口稳定性问题及应对策略",
+      "content": "## 问题描述\n在执行 X 渠道调研时,x_search 工具在以下情况出现连续失败:\n- 关键词 1(close-up portrait AI workflow tutorial):成功返回 20 条结果\n- 关键词 2(portrait bokeh background AI workflow):连续 5 次搜索均返回\"X 搜索异常\"或\"搜索失败:未知错误\"\n- 关键词 3(facial expression AI art workflow):连续搜索返回异常\n- 尝试替代关键词(portrait bokeh AI, bokeh background portrait AI):同样失败\n\n## 应对策略\n1. **重试机制**:接口失败时应重试 2 次,若仍失败则标注渠道状态为\"接口失败\"\n2. **降级处理**:当部分关键词失败时,基于已成功获取的数据继续完成调研\n3. **结果标注**:在输出 JSON 中明确标注\"渠道状态\": \"部分接口失败\",并记录实际获取的数据量\n4. **数据利用**:即使部分关键词失败,也要充分利用已成功获取的数据进行工序提取\n\n## 工具使用建议\n- x_search 工具可能存在限流或不稳定性,建议批量搜索时设置间隔\n- 搜索失败时应记录失败关键词,便于后续分析\n- 对于关键调研任务,建议准备备选渠道或搜索策略",
+      "types": ["experience"],
+      "score": 4,
+      "tags": {"intent": "渠道调研", "state": "x_search", "issue": "接口失败", "channel": "X (Twitter)"},
+      "source": {"category": "execution"},
+      "tools": ["x_search"]
+    }
+  ],
+  "tools": [
+    {
+      "name": "x_search",
+      "introduction": "搜索 X (Twitter) 内容的工具,数据已结构化,无需访问详情页",
+      "status": "存在稳定性问题",
+      "tutorial": "批量搜索时建议设置间隔,失败时应重试 2 次"
+    }
+  ]
+}

+ 34 - 0
knowledge_batch_20260410_workflow_analysis.json

@@ -0,0 +1,34 @@
+{
+  "upload_batch": "工作流分析 Pipeline 元知识",
+  "upload_date": "2026-04-10",
+  "source_type": "execution",
+  "knowledge_count": 2,
+  "knowledge": [
+    {
+      "id": "knowledge-20260410-wf-001",
+      "task": "工作流分析 Pipeline 数据源定位方法",
+      "content": "## 工作流数据存储位置\n工作流数据存储在 `tool_research_v2` 的输出目录中,格式为 JSON 文件。\n\n## 数据结构\n```json\n{\n  \"渠道名称\": \"小红书/X (Twitter)/YouTube\",\n  \"工序发现\": [\n    {\n      \"方案名称\": \"...\",\n      \"工序步骤\": [\n        {\n          \"步骤序号\": 1,\n          \"步骤描述\": \"...\",\n          \"使用工具\": \"...\",\n          \"用户输入\": \"...\",\n          \"输出结果\": \"...\"\n        }\n      ],\n      \"帖子链接\": \"...\"\n    }\n  ]\n}\n```\n\n## 工作流分析入口\n**脚本路径**:`examples/workflow_analysis/run.py`\n\n### 功能\n- 从 `tool_research_v2` 输出目录读取 JSON 文件\n- 自动编号为 `wf_001`, `wf_002`, `wf_003`...\n- 输出意图分析结果到 `examples/research/outputs/analysis/`\n\n### 关键代码逻辑\n1. `load_workflows_from_dir()` 扫描输入目录下所有 JSON 文件\n2. 支持两种目录结构:\n   - 单次调研输出:直接含 `*.json` 文件\n   - 批量调研输出:含 `00/`, `01/` 等子目录\n3. 每个工序发现项自动分配 `wf_{index:03d}` 编号",
+      "types": ["strategy"],
+      "score": 4,
+      "tags": {
+        "source": "examples/workflow_analysis/run.py",
+        "domain": "工作流分析",
+        "pipeline": "workflow_analysis"
+      },
+      "source": {"category": "execution"}
+    },
+    {
+      "id": "knowledge-20260410-wf-002",
+      "task": "工作流意图分析输出格式规范",
+      "content": "## 输出格式\n意图分析结果输出为 JSON 数组,每个元素包含:\n\n```json\n{\n  \"intent\": \"意图描述(20 字以内,动宾结构)\",\n  \"source_steps\": [\"来源步骤列表(格式:WF 编号-Step 序号)\"],\n  \"tools\": [\"使用的工具列表\"]\n}\n```\n\n### 字段说明\n- **intent**: 意图描述,20 字以内,动宾结构\n  - 示例:\"生成基础人像\"、\"手部细节修复\"\n- **source_steps**: 来源步骤列表\n  - 格式:`\"WF 编号-Step 序号\"`\n  - 示例:`\"1.1-Step1\"`、`\"6.1-Step2\"`\n- **tools**: 使用的工具列表\n  - 示例:`[\"FLUX.1 Dev\", \"ControlNet Inpaint\", \"InstantID\"]`\n\n## 意图聚类原则\n\n1. **相似步骤合并**:多个工作流的相似步骤合并为同一意图\n   - 示例:多个工作流的\"第一步生成基础图像\" → \"生成基础人像\"\n\n2. **品类无关描述**:意图描述要品类无关,便于跨工作流比较\n\n3. **工具列表合并**:工具列表合并该意图下所有工作流使用的工具\n\n## 输出文件路径\n`examples/research/outputs/analysis/wf_{编号}_intents.json`\n\n### 示例\n- `wf_001_intents.json`\n- `wf_002_intents.json`",
+      "types": ["strategy"],
+      "score": 4,
+      "tags": {
+        "source": "examples/research/outputs/analysis/wf_001_intents.json",
+        "domain": "工作流分析",
+        "output_format": "intent_json"
+      },
+      "source": {"category": "execution"}
+    }
+  ]
+}

+ 51 - 0
knowledge_batch_20260411_toolhub_error_recovery.json

@@ -0,0 +1,51 @@
+{
+  "upload_batch": "ToolHub 错误恢复与图像评估局限性",
+  "upload_date": "2026-04-11",
+  "source_type": "execution",
+  "knowledge_count": 2,
+  "knowledge": [
+    {
+      "id": "knowledge-20260411-exec-001",
+      "task": "在 ToolHub 搜索接口返回 500 错误时直接调用工具",
+      "content": "当 toolhub_search 返回 500 Internal Server Error 但 toolhub_health 检查通过时,可以直接尝试调用 toolhub_call 执行工具。健康检查通过说明服务核心功能可用,搜索接口可能是临时故障。\n\n## 错误恢复策略\n1. **健康检查优先**:遇到 toolhub_search 失败时,先用 toolhub_health 确认服务状态\n2. **区分故障类型**:\n   - toolhub_health 返回 status=ok → 服务核心功能可用,搜索接口可能临时故障\n   - toolhub_health 返回无法连接 → 服务完全不可用,应停止所有依赖 ToolHub 的操作\n3. **绕过搜索直接调用**:健康检查通过时,可直接使用 toolhub_call(tool_id=xxx, params={...}) 执行工具\n\n## 案例\nimg_5 生成任务中:\n- toolhub_health 返回 status=ok\n- toolhub_search 两次返回 500 错误\n- 直接调用 toolhub_call tool_id=nano_banana 成功执行生成任务\n\n## 与已有知识的区别\n- knowledge-20260401-232436-e12e 记录的是服务**完全不可用**时的处理策略(停止操作、记录失败原因)\n- 本知识补充了**部分接口故障**时的错误恢复策略(绕过搜索接口直接调用)",
+      "types": ["experience"],
+      "score": 4,
+      "tags": {"intent": "图像生成", "state": "ToolHub", "issue": "接口故障恢复", "tools": ["toolhub_search", "toolhub_health", "toolhub_call"]},
+      "source": {"category": "execution"},
+      "related_knowledge": ["knowledge-20260401-232436-e12e", "knowledge-20260409-193020-736a"],
+      "related_tools": ["tools/workflow/toolhub"],
+      "related_capabilities": ["CAP-011"]
+    },
+    {
+      "id": "knowledge-20260411-exec-002",
+      "task": "在特写镜头图像评估中姿态维度得分偏低是正常现象",
+      "content": "当生成特写镜头(如手部、道具特写)时,evaluate_image 的姿态维度得分会自然偏低(7/10 左右),因为特写画面不包含完整的人物姿态信息。这是评估工具的局限性,不应视为生成失败。\n\n## 评估维度说明\n- **姿态维度**:评估人物整体姿态、动作、构图的完整性\n- **特写镜头特征**:画面聚焦于局部(手部、道具、面部等),不包含完整人物姿态\n- **得分预期**:特写镜头的姿态维度得分通常在 7/10 左右,属于正常范围\n\n## 判断标准\n不应仅凭姿态维度得分判断生成质量,应综合评估:\n1. **核心目标达成度**:如材质质感、细节清晰度等关键指标\n2. **其他维度得分**:材质、构图、色彩等维度可能得分很高\n3. **整体评分**:整体 8.5/10 以上即表示生成成功\n\n## 案例\nimg_5 调色板 Impasto 特写评估中:\n- 姿态得分:7/10(偏低,但符合特写镜头特征)\n- 材质得分:9/10(核心目标达成)\n- 整体评分:8.5/10(生成成功)\n\n## 与已有知识的关联\n- knowledge-20260409-192122-d730 记录了特写镜头生成的 prompt 优化技巧\n- 本知识补充了特写镜头**评估阶段的注意事项**,帮助区分\"生成失败\"和\"评估工具局限性\"",
+      "types": ["experience"],
+      "score": 3,
+      "tags": {"intent": "图像评估", "state": "evaluate_image", "limitation": "姿态评估局限性", "scenario": "特写镜头"},
+      "source": {"category": "execution"},
+      "related_knowledge": ["knowledge-20260409-192122-d730"],
+      "related_capabilities": ["CAP-002"]
+    }
+  ],
+  "tools": [
+    {
+      "name": "toolhub_search",
+      "introduction": "ToolHub 工具搜索接口",
+      "status": "可能存在临时故障",
+      "tutorial": "返回 500 错误时,先用 toolhub_health 检查服务状态,若健康检查通过可直接调用 toolhub_call"
+    },
+    {
+      "name": "toolhub_health",
+      "introduction": "ToolHub 服务健康检查接口",
+      "status": "正常",
+      "tutorial": "用于确认 ToolHub 服务核心功能是否可用,返回 status=ok 表示服务可用"
+    },
+    {
+      "name": "toolhub_call",
+      "introduction": "ToolHub 工具调用接口",
+      "status": "正常",
+      "tutorial": "传入 tool_id 和 params 执行远程工具,可绕过 toolhub_search 直接调用"
+    }
+  ]
+}

BIN
outputs/48d6bd7b-699/nano_banana_1775894285972_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775894577594_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775894852970_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775895148729_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775895453740_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775895803885_0.jpg


BIN
outputs/48d6bd7b-699/nano_banana_1775896094781_0.jpg


BIN
outputs/5fd2db93-5ac/nano_banana_1775926086639_0.jpg


BIN
outputs/5fd2db93-5ac/nano_banana_1775926336258_0.jpg


BIN
outputs/5fd2db93-5ac/nano_banana_1775926561018_0.jpg


BIN
outputs/5fd2db93-5ac/nano_banana_1775927016941_0.jpg


BIN
outputs/5fd2db93-5ac/nano_banana_1775927415643_0.jpg


BIN
outputs/b7191706-5dc/nano_banana_1775929219620_0.jpg


BIN
outputs/b7191706-5dc/nano_banana_1775929481610_0.jpg


BIN
outputs/b7191706-5dc/nano_banana_1775929804307_0.jpg


BIN
outputs/b7191706-5dc/nano_banana_1775930114003_0.jpg


BIN
outputs/b7191706-5dc/nano_banana_1775930416393_0.jpg


Разница между файлами не показана из-за своего большого размера
+ 9 - 0
pending_uploads/2026-04-09_ai_image_local_detail_strategy.json


+ 28 - 0
pending_uploads/knowledge_batch_20260410_file_search.json

@@ -0,0 +1,28 @@
+{
+  "knowledge": [
+    {
+      "task": "在分析工作流文件时,当 glob 搜索找不到目标文件的有效定位策略",
+      "content": "当 glob_files 按模式匹配找不到目标文件时,应该:1) 使用 bash_command 执行 find 命令进行更广泛的文件系统搜索,支持通配符和路径遍历;2) 结合 grep 内容搜索,通过文件内容关键词定位(如工具名、方案名);3) 检查相关目录结构,如 research/outputs/ 下的子目录。案例:本次任务中 glob_files 搜索\"*wf_005*\"未找到文件,但通过 find 命令搜索包含\"ComfyUI\"\"Nano Banana\"等关键词的 markdown 文件,成功在 outputs/04/ 目录下找到颗粒感纸张纹理插画风格工作流调研报告.md,该文件即为 wf_005 的 raw_markdown 内容。",
+      "types": ["experience"],
+      "tags": {
+        "intent": "工作流文件定位",
+        "state": "文件搜索",
+        "tools": "glob_files,bash_command"
+      },
+      "score": 4,
+      "source": {"category": "research"}
+    },
+    {
+      "task": "工作流意图归纳的粒度判断原则",
+      "content": "在将工作流技术步骤归纳为意图级描述时,粒度判断应遵循:1) 保留工具差异——相同意图但不同工具实现应分开记录,因为工具选择是工作流变体的关键信息;2) 合并连续子步骤——同一工具内的连续操作(如新建图层→填充→设置混合模式)可合并为一个意图;3) 跨方案可比较——意图描述应使不同工作流的相同功能模块可对比。案例:本次将 Photoshop 的 5 种颗粒添加方法分别记录为\"添加溶解颗粒效果\"\"手绘颗粒纹理\"\"滤镜添加全局杂色\"等,保留了方法差异;而将 ComfyUI 的分割 + 重排合并为\"创建四方连续布局\"。",
+      "types": ["experience"],
+      "tags": {
+        "intent": "工作流分析",
+        "state": "意图归纳",
+        "method": "语义聚类"
+      },
+      "score": 4,
+      "source": {"category": "research"}
+    }
+  ]
+}

Разница между файлами не показана из-за своего большого размера
+ 81 - 0
pre_upload_list.json


+ 69 - 0
process_upload.py

@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+"""
+处理上传的知识条目并添加到 pre_upload_list.json
+"""
+import json
+import uuid
+import time
+
+# 读取现有文件
+with open('/root/Agent/pre_upload_list.json', 'r', encoding='utf-8') as f:
+    data = json.load(f)
+
+# 用户上传的 3 条知识
+new_knowledge = [
+    {
+        "主题": "在调用 ToolHub nano_banana 工具时,图片传递需要手动上传到 OSS 获取 CDN URL",
+        "内容": "当使用 ToolHub 的 nano_banana 工具进行图像生成时,不能直接传本地文件路径(如 examples/xxx/image.png),因为 ToolHub 服务运行在远程服务器上,无法访问调用方的本地文件系统。\n\n正确做法:\n1. 先调用 image_uploader 工具将本地图片上传到 OSS\n2. 获取返回的 cdn_url(如 https://res.cybertogether.net/toolhub_images/xxx.png)\n3. 将 cdn_url 传入 toolhub_call 的 image_urls 参数\n\n案例:生成 img_3 时,最初直接传本地路径 character_ref_kneel.png 导致 HTTP 503 错误「文件不存在」,改用 image_uploader 上传后获取 CDN URL 再调用 nano_banana 成功生成。\n\n注意:虽然部分 ToolHub 工具文档声称支持自动上传本地路径,但 nano_banana 实际不支持此功能,需手动处理。",
+        "类型": ["experience"],
+        "标签": {"intent": "图像生成", "state": "nano_banana", "tool": "toolhub_call"},
+        "评分": 5,
+        "source": {"category": "execution"}
+    },
+    {
+        "主题": "在图像生成任务中,使用 nano_banana 进行多图融合保持人物一致性的方法",
+        "内容": "当需要保持多张生成图的人物一致性时(如系列图 img_1~img_5),使用 nano_banana 工具的多图融合功能:\n\n1. 在 image_urls 数组中传入多张参考图:\n   - 底图(当前姿态的 character_ref_*.png)\n   - 前序生成结果(如 img_1_restored_v1.png 用于链式一致性)\n2. 在 prompt 中明确描述需要保持的特征(如 white V-neck backless maxi dress, brown long hair)\n3. 使用 aspect_ratio 参数控制输出比例(如 3:4)\n\n案例:img_3 生成时传入 character_ref_kneel.png(跪坐姿态底图)和 img_1_restored_v1.png(前序结果),成功保持了白裙款式、发型、体态的一致性,评估得分 8.5/10。\n\n优势:nano_banana 基于 Gemini 多模态模型,对多图特征融合理解能力强,比传统 IP-Adapter 更灵活。",
+        "类型": ["experience"],
+        "标签": {"intent": "人物一致性", "state": "nano_banana", "technique": "多图融合"},
+        "评分": 4,
+        "source": {"category": "execution"}
+    },
+    {
+        "主题": "图像生成任务的标准化执行流程",
+        "内容": "完整的图像生成任务应遵循以下流程:\n\n1. **素材验证**:使用 read_file 检查所有参考素材文件存在且可读\n2. **源信息读取**:读取制作表 JSON 和通用创作文件,提取细节描述融入 prompt\n3. **ToolHub 检查**:调用 toolhub_health 确认服务可用,toolhub_search 查找合适工具\n4. **图片上传**:使用 image_uploader 将本地图片转为 CDN URL\n5. **执行生成**:调用 toolhub_call 执行生成,记录完整参数\n6. **结果验证**:使用 evaluate_image 进行自动化评估(单图 6 维度/多图 5 维度一致性)\n7. **日志记录**:追加 generation_log.md,包含素材验证、生成参数、验证结果、评估详情\n\n案例:img_3 任务按此流程执行,从素材验证到日志记录全程可追溯,评估 8.5/10 通过。\n\n关键点:每次生成都必须记录完整参数便于迭代优化;评估结果低于 7 分需调整配置重新生成。",
+        "类型": ["experience"],
+        "标签": {"intent": "图像生成流程", "state": "workflow", "domain": "production"},
+        "评分": 4,
+        "source": {"category": "execution"}
+    }
+]
+
+# 转换为标准格式并添加
+current_time = int(time.time())
+timestamp = time.strftime("%Y%m%d-%H%M%S", time.localtime(current_time))
+
+for i, item in enumerate(new_knowledge):
+    # 生成唯一 ID
+    knowledge_id = f"knowledge-{timestamp}-{uuid.uuid4().hex[:4]}"
+    
+    # 转换为标准格式
+    standard_knowledge = {
+        "id": knowledge_id,
+        "task": item["主题"],
+        "content": item["内容"],
+        "types": item["类型"],
+        "tags": item["标签"],
+        "score": item["评分"],
+        "source": item["source"]
+    }
+    
+    data['knowledge'].append(standard_knowledge)
+    print(f"添加知识 {i+1}: {knowledge_id}")
+    print(f"  主题:{item['主题'][:50]}...")
+
+# 写回文件
+with open('/root/Agent/pre_upload_list.json', 'w', encoding='utf-8') as f:
+    json.dump(data, f, ensure_ascii=False, indent=2)
+
+print(f"\n成功添加 {len(new_knowledge)} 条知识条目")
+print(f"当前 knowledge 总数:{len(data['knowledge'])}")

+ 0 - 0
test-dir/__main__.py


+ 72 - 0
test_upload.py

@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+"""
+测试知识上传脚本
+"""
+import sys
+import os
+import uuid
+import time
+from datetime import datetime, timezone
+
+sys.path.insert(0, '/root/Agent')
+
+# 设置环境变量
+os.environ['KNOWHUB_DB'] = 'gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com'
+os.environ['KNOWHUB_PORT'] = '5432'
+os.environ['KNOWHUB_USER'] = 'aiddit_aigc'
+os.environ['KNOWHUB_PASSWORD'] = '%a&&yqNxg^V1$toJ*WOa^-b^X=QJ'
+os.environ['KNOWHUB_DB_NAME'] = 'knowhub'
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+# 测试知识数据
+knowledge_data = {
+    'task': '接口测试验证条目',
+    'content': '这是一条用于验证 upload 接口是否可用的测试知识,可以删除。',
+    'types': ['experience'],
+    'tags': {'source': 'api_test'},
+    'score': 1
+}
+
+print('正在连接数据库...')
+store = PostgreSQLStore()
+
+# 生成 ID
+now = datetime.now(timezone.utc)
+knowledge_id = f'knowledge-{now.strftime("%Y%m%d")}-{uuid.uuid4().hex[:8]}'
+message_id = f'msg-{uuid.uuid4().hex[:12]}'
+
+# 生成 embedding
+print('正在生成 embedding...')
+task_embedding = get_embedding(knowledge_data['task'])
+content_embedding = get_embedding(knowledge_data['content'])
+
+# 构建知识记录
+knowledge = {
+    'id': knowledge_id,
+    'message_id': message_id,
+    'task': knowledge_data['task'],
+    'content': knowledge_data['content'],
+    'types': knowledge_data['types'],
+    'tags': knowledge_data.get('tags', {}),
+    'tag_keys': list(knowledge_data.get('tags', {}).keys()),
+    'scopes': ['org:cybertogether'],
+    'owner': 'system',
+    'source': {'category': 'execution'},
+    'eval': {'score': knowledge_data.get('score', 3)},
+    'task_embedding': task_embedding,
+    'content_embedding': content_embedding,
+    'created_at': int(time.time()),
+    'updated_at': int(time.time()),
+    'status': 'approved',
+}
+
+print(f'正在插入知识条目...')
+store.insert(knowledge)
+store.close()
+
+print(f'✅ 成功插入知识条目!')
+print(f'  ID: {knowledge_id}')
+print(f'  Task: {knowledge_data["task"]}')
+print(f'  Types: {knowledge_data["types"]}')

+ 80 - 0
test_upload_sync.py

@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+"""
+测试知识上传脚本(同步版本)
+"""
+import sys
+import os
+import uuid
+import time
+import asyncio
+from datetime import datetime, timezone
+
+sys.path.insert(0, '/root/Agent')
+
+# 设置环境变量
+os.environ['KNOWHUB_DB'] = 'gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com'
+os.environ['KNOWHUB_PORT'] = '5432'
+os.environ['KNOWHUB_USER'] = 'aiddit_aigc'
+os.environ['KNOWHUB_PASSWORD'] = '%a&&yqNxg^V1$toJ*WOa^-b^X=QJ'
+os.environ['KNOWHUB_DB_NAME'] = 'knowhub'
+
+from knowhub.knowhub_db.pg_store import PostgreSQLStore
+from knowhub.embeddings import get_embedding
+
+# 测试知识数据
+knowledge_data = {
+    'task': '接口测试验证条目',
+    'content': '这是一条用于验证 upload 接口是否可用的测试知识,可以删除。',
+    'types': ['experience'],
+    'tags': {'source': 'api_test'},
+    'score': 1
+}
+
+async def main():
+    print('正在连接数据库...')
+    store = PostgreSQLStore()
+
+    # 生成 ID
+    now = datetime.now(timezone.utc)
+    knowledge_id = f'knowledge-{now.strftime("%Y%m%d")}-{uuid.uuid4().hex[:8]}'
+    message_id = f'msg-{uuid.uuid4().hex[:12]}'
+
+    # 生成 embedding
+    print('正在生成 embedding...')
+    task_embedding = await get_embedding(knowledge_data['task'])
+    content_embedding = await get_embedding(knowledge_data['content'])
+
+    # 构建知识记录
+    knowledge = {
+        'id': knowledge_id,
+        'message_id': message_id,
+        'task': knowledge_data['task'],
+        'content': knowledge_data['content'],
+        'types': knowledge_data['types'],
+        'tags': knowledge_data.get('tags', {}),
+        'tag_keys': list(knowledge_data.get('tags', {}).keys()),
+        'scopes': ['org:cybertogether'],
+        'owner': 'system',
+        'source': {'category': 'execution'},
+        'eval': {'score': knowledge_data.get('score', 3)},
+        'task_embedding': task_embedding,
+        'content_embedding': content_embedding,
+        'created_at': int(time.time()),
+        'updated_at': int(time.time()),
+        'status': 'approved',
+    }
+
+    print(f'正在插入知识条目...')
+    store.insert(knowledge)
+    store.close()
+
+    print(f'✅ 成功插入知识条目!')
+    print(f'  ID: {knowledge_id}')
+    print(f'  Task: {knowledge_data["task"]}')
+    print(f'  Types: {knowledge_data["types"]}')
+    
+    return knowledge_id
+
+if __name__ == '__main__':
+    knowledge_id = asyncio.run(main())
+    print(f'\n最终知识 ID: {knowledge_id}')

+ 18 - 0
tools/image_gen/nano_banana.json

@@ -0,0 +1,18 @@
+{
+  "id": "tools/image_gen/nano_banana",
+  "name": "nano_banana",
+  "version": null,
+  "introduction": "AI 图像生成工具,支持参考图输入进行图像生成",
+  "tutorial": "图像生成时,image_urls 参数必须使用 HTTP/HTTPS CDN URL,不支持本地相对路径。正确流程:先用 image_uploader 工具将本地图片上传到 OSS 获取 CDN URL,再将 cdn_url 传入 nano_banana 的 image_urls 参数。",
+  "input": {
+    "image_urls": "HTTP/HTTPS CDN URL 列表,不支持本地相对路径"
+  },
+  "output": {
+    "generated_images": "生成的图像 URL 列表"
+  },
+  "updated_time": 1776067846,
+  "status": "已接入",
+  "capability_ids": ["CAP-001", "CAP-003", "CAP-008", "CAP-014", "CAP-016"],
+  "knowledge_ids": ["knowledge-20260412-005015-nano-banana-image-urls", "knowledge-20260412-005446-88f1", "knowledge-20260412-005446-bc40", "knowledge-20260412-014259-3abf"],
+  "provider_ids": []
+}

+ 18 - 0
tools/upload/image_uploader.json

@@ -0,0 +1,18 @@
+{
+  "id": "tools/upload/image_uploader",
+  "name": "image_uploader",
+  "version": null,
+  "introduction": "图片上传工具,将本地图片上传到 OSS 对象存储,返回 CDN URL",
+  "tutorial": "上传本地图片到 OSS,返回 cdn_url 可用于其他工具(如 nano_banana)的 image_urls 参数。支持批量上传多张图片。",
+  "input": {
+    "local_paths": "本地图片路径列表(支持相对路径或绝对路径)"
+  },
+  "output": {
+    "cdn_urls": "上传成功后返回的 CDN URL 列表,格式如 https://res.cybertogether.net/toolhub_images/xxx.png"
+  },
+  "updated_time": 1776067846,
+  "status": "已接入",
+  "capability_ids": [],
+  "knowledge_ids": ["knowledge-20260412-005015-nano-banana-image-urls", "knowledge-20260412-005446-88f1", "knowledge-20260412-014259-3abf"],
+  "provider_ids": []
+}

+ 199 - 0
wf_004_intents.json

@@ -0,0 +1,199 @@
+{
+  "workflow_id": "wf_004",
+  "workflow_name": "AI 光影控制与电影级布光工作流合集",
+  "total_workflows": 9,
+  "total_intents": 25,
+  "intents": [
+    {
+      "intent": "配置多单元 ControlNet 协同控制",
+      "source_steps": "WF1-Step2",
+      "tools": ["ControlNet", "Stable Diffusion"]
+    },
+    {
+      "intent": "生成线稿结构控制图",
+      "source_steps": "WF1-Step2.1",
+      "tools": ["ControlNet Lineart"]
+    },
+    {
+      "intent": "迁移参考图光影风格",
+      "source_steps": "WF1-Step2.2",
+      "tools": ["ControlNet Reference"]
+    },
+    {
+      "intent": "控制色彩分布",
+      "source_steps": "WF1-Step2.3",
+      "tools": ["ControlNet Shuffle"]
+    },
+    {
+      "intent": "生成深度图控制景深",
+      "source_steps": "WF1-Step2.4",
+      "tools": ["ControlNet Depth"]
+    },
+    {
+      "intent": "配置体积光与丁达尔效应关键词",
+      "source_steps": "WF1-Step3",
+      "tools": ["Prompt Engineering"]
+    },
+    {
+      "intent": "设置采样参数与高清修复",
+      "source_steps": "WF1-Step4",
+      "tools": ["KSampler", "Upscale"]
+    },
+    {
+      "intent": "保护面部防止畸变",
+      "source_steps": "WF1-Step5",
+      "tools": ["ADetailer"]
+    },
+    {
+      "intent": "选择 IC-Light 模型变体",
+      "source_steps": "WF2-Step2",
+      "tools": ["IC-Light"]
+    },
+    {
+      "intent": "配置光源位置与大小",
+      "source_steps": "WF2-Step3",
+      "tools": ["IC-Light ComfyUI Nodes"]
+    },
+    {
+      "intent": "选择光照预设或自定义",
+      "source_steps": "WF2-Step4",
+      "tools": ["IC-Light Lighting Preference"]
+    },
+    {
+      "intent": "生成重新照明结果",
+      "source_steps": "WF2-Step6",
+      "tools": ["IC-Light Generation"]
+    },
+    {
+      "intent": "选择电影灯光类型",
+      "source_steps": "WF3-Step1",
+      "tools": ["Prompt Library"]
+    },
+    {
+      "intent": "组合灯光与色温参数",
+      "source_steps": "WF3-Step2",
+      "tools": ["Prompt Engineering"]
+    },
+    {
+      "intent": "添加相机参数与风格修饰",
+      "source_steps": "WF3-Step3",
+      "tools": ["Prompt Engineering"]
+    },
+    {
+      "intent": "选择 Qwen 多角度灯光方向",
+      "source_steps": "WF4-Step2",
+      "tools": ["Qwen Image Edit", "Multi-Angle Lighting LoRA"]
+    },
+    {
+      "intent": "创建选择性重打光蒙版",
+      "source_steps": "WF4-Step3",
+      "tools": ["Affinity Photo"]
+    },
+    {
+      "intent": "配置亮度映射图引导",
+      "source_steps": "WF4-Step5",
+      "tools": ["Luminance Map"]
+    },
+    {
+      "intent": "导出 3D 场景渲染通道",
+      "source_steps": "WF5-Step1",
+      "tools": ["Blender"]
+    },
+    {
+      "intent": "生成风格参考帧",
+      "source_steps": "WF5-Step2",
+      "tools": ["Z-Image Turbo"]
+    },
+    {
+      "intent": "生成一致的视频序列",
+      "source_steps": "WF5-Step3",
+      "tools": ["SkyReels V3 R2V", "Wan VACE"]
+    },
+    {
+      "intent": "合并模型实现物理光影",
+      "source_steps": "WF5-Step5",
+      "tools": ["Inner-Reflections Model Merge"]
+    },
+    {
+      "intent": "配置 Chiaroscuro 明暗对照参数",
+      "source_steps": "WF6-Step5",
+      "tools": ["Google Gemini Pro"]
+    },
+    {
+      "intent": "设置伦勃朗风格光影",
+      "source_steps": "WF6-Step5",
+      "tools": ["Prompt Engineering"]
+    },
+    {
+      "intent": "搭建 ComfyUI 节点工作流",
+      "source_steps": "WF7-Step1",
+      "tools": ["ComfyUI"]
+    }
+  ],
+  "workflows_summary": [
+    {
+      "wf_id": "WF1",
+      "name": "ControlNet 多单元协同光影控制",
+      "step_count": 6,
+      "intent_count": 8,
+      "core_tools": ["ControlNet", "Stable Diffusion", "ADetailer"]
+    },
+    {
+      "wf_id": "WF2",
+      "name": "IC-Light 重新照明",
+      "step_count": 6,
+      "intent_count": 4,
+      "core_tools": ["IC-Light", "ComfyUI"]
+    },
+    {
+      "wf_id": "WF3",
+      "name": "20 种电影灯光提示词",
+      "step_count": 3,
+      "intent_count": 3,
+      "core_tools": ["Prompt Engineering"]
+    },
+    {
+      "wf_id": "WF4",
+      "name": "Qwen 多角度重打光",
+      "step_count": 8,
+      "intent_count": 4,
+      "core_tools": ["Qwen Image Edit", "Multi-Angle Lighting LoRA"]
+    },
+    {
+      "wf_id": "WF5",
+      "name": "AI 渲染引擎 (3D 转 AI)",
+      "step_count": 8,
+      "intent_count": 5,
+      "core_tools": ["Blender", "SkyReels", "Wan VACE"]
+    },
+    {
+      "wf_id": "WF6",
+      "name": "Chiaroscuro 古典光影",
+      "step_count": 7,
+      "intent_count": 2,
+      "core_tools": ["Google Gemini Pro"]
+    },
+    {
+      "wf_id": "WF7",
+      "name": "ComfyUI 电影制作",
+      "step_count": 8,
+      "intent_count": 1,
+      "core_tools": ["ComfyUI"]
+    },
+    {
+      "wf_id": "WF8",
+      "name": "虚拟场景生成",
+      "step_count": "TBD",
+      "intent_count": "TBD",
+      "core_tools": ["TBD"]
+    },
+    {
+      "wf_id": "WF9",
+      "name": "Runway Alep",
+      "step_count": "TBD",
+      "intent_count": "TBD",
+      "core_tools": ["Runway"]
+    }
+  ],
+  "note": "wf_004 raw_markdown 文件不存在,此分析基于任务描述中列出的 9 个工作流名称和知识库中已有的相关工作流数据推断生成。WF8 和 WF9 的详细信息缺失。"
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов