guantao 1 день назад
Родитель
Сommit
294ac0476e

+ 8 - 1
.env

@@ -17,4 +17,11 @@ API_TOKEN=fc0f2bdc-6ecf-4abe-8902-9be475894283
 
 
 APIYI_BASE_URL=https://api.apiyi.com/v1
-APIYI_KEY=sk-6igJkYBdNGZln2wxB8525235C1F4412dB4449eB7B8F17a2d
+APIYI_KEY=sk-6igJkYBdNGZln2wxB8525235C1F4412dB4449eB7B8F17a2d
+
+# databae configuration
+KNOWHUB_DB=gp-t4n72471pkmt4b9q7o-master.gpdbmaster.singapore.rds.aliyuncs.com
+KNOWHUB_PORT=5432
+KNOWHUB_USER=aiddit_aigc
+KNOWHUB_PASSWORD=%a&&yqNxg^V1$toJ*WOa^-b^X=QJ
+KNOWHUB_DB_NAME=knowhub

+ 4 - 3
chat_with_router.py

@@ -28,7 +28,8 @@ async def chat_loop():
     print("  - 输入 'help' 查看可用命令")
     print("-" * 60)
 
-    session_id = None
+    import uuid
+    chat_id = uuid.uuid4().hex
 
     async with httpx.AsyncClient(timeout=120.0) as client:
         # 检查服务是否可用
@@ -73,13 +74,13 @@ async def chat_loop():
                     f"{BASE_URL}/chat",
                     json={
                         "message": user_input,
-                        "session_id": session_id
+                        "chat_id": chat_id
                     }
                 )
 
                 if response.status_code == 200:
                     result = response.json()
-                    session_id = result.get("session_id")
+                    chat_id = result.get("chat_id", chat_id)
                     print(f"\n[Router Agent]\n{result['response']}")
                 else:
                     print(f"\n错误: {response.status_code} - {response.text}")

+ 36 - 0
dump_tools_for_merge.py

@@ -0,0 +1,36 @@
+import json
+import logging
+from tool_agent.tool.tool_store import PostgreSQLToolStore
+
+logging.basicConfig(level=logging.INFO)
+
+def dump_tools():
+    store = PostgreSQLToolStore()
+    tools = store.list_all(limit=1000)
+    
+    connected = []
+    unconnected = []
+    
+    for t in tools:
+        if t.get("status") == "已接入":
+            connected.append({
+                "id": t.get("id"),
+                "name": t.get("name"),
+                "introduction": t.get("introduction"),
+                "capability_ids": t.get("capability_ids", [])
+            })
+        else:
+            unconnected.append({
+                "id": t.get("id"),
+                "name": t.get("name"),
+                "introduction": t.get("introduction"),
+                "capability_ids": t.get("capability_ids", [])
+            })
+            
+    with open("tool_dump.json", "w", encoding="utf-8") as f:
+        json.dump({"connected": connected, "unconnected": unconnected}, f, ensure_ascii=False, indent=2)
+        
+    print(f"Dumped {len(connected)} connected tools and {len(unconnected)} unconnected tools.")
+
+if __name__ == "__main__":
+    dump_tools()

+ 22 - 0
manual_sync.py

@@ -0,0 +1,22 @@
+import asyncio
+import logging
+
+# 配置日志以便看到同步过程的详情
+logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
+
+from tool_agent.router.agent import Router
+
+async def main():
+    print("="*50)
+    print(" 开始全量同步 Registry 本地工具 -> PostgreSQL ...")
+    print("="*50)
+    
+    agent = Router()
+    await agent._sync_knowhub_after_register()
+    
+    print("="*50)
+    print(" 同步执行完毕!赶快去 Dashboard 看看效果吧!")
+    print("="*50)
+
+if __name__ == '__main__':
+    asyncio.run(main())

+ 472 - 0
matches.json

@@ -0,0 +1,472 @@
+[
+  {
+    "unconnected_id": "tools/plugin/ipadapter_plus",
+    "connected_id": "liblibai_controlnet",
+    "reason": "IP-Adapter 是图像提示融合控制节点,与 LibLib AI 综合生成工具支持的多路图像引导控制能力本质等价"
+  },
+  {
+    "unconnected_id": "tools/plugin/fococus_inpaint_node",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Fooocus 局部重绘节点本质是 inpaint 功能,LibLib AI 综合生成工具已原生支持 inpaint 模块"
+  },
+  {
+    "unconnected_id": "tools/image_process/insightface",
+    "connected_id": "liblibai_controlnet",
+    "reason": "InsightFace 是人脸识别/换脸核心库,LibLib AI 综合生成工具已内置 instantid(换脸)模块,本质功能等价"
+  },
+  {
+    "unconnected_id": "tools/plugin/inpaint_anything",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Inpaint Anything 提供基于 SAM 的局部重绘,LibLib AI 综合生成工具已支持 inpaint(蒙版重绘)模块"
+  },
+  {
+    "unconnected_id": "tools/plugin/adetailer",
+    "connected_id": "liblibai_controlnet",
+    "reason": "ADetailer 自动检测区域并局部重绘,LibLib AI 综合生成工具的 inpaint 模块覆盖同等能力"
+  },
+  {
+    "unconnected_id": "tools/model/face_detailer",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Face Detailer 为人脸修复 LoRA,LibLib AI 综合生成工具已支持 instantid 换脸与 inpaint 人脸重绘"
+  },
+  {
+    "unconnected_id": "tools/model/flux_1_d",
+    "connected_id": "flux_generate",
+    "reason": "Flux.1 D 是 Flux 系列基础检查点模型,flux_generate 即为调用 Flux 模型的官方生图 API,本质等价"
+  },
+  {
+    "unconnected_id": "tools/model/hand_detail_flux_xl",
+    "connected_id": "flux_generate",
+    "reason": "Hand Detail LoRA 专为 Flux/XL 增强手部细节,其能力通过 flux_generate 调用 Flux 模型时集成使用"
+  },
+  {
+    "unconnected_id": "tools/model/flux_kontext_max",
+    "connected_id": "flux_generate",
+    "reason": "flux-kontext-max 是 flux_generate 工具描述中明确推荐用于高级图像编辑的模型变体,属于同一工具的模型参数"
+  },
+  {
+    "unconnected_id": "tools/image_process/inpaint",
+    "connected_id": "liblibai_controlnet",
+    "reason": "liblibai_controlnet 明确列出支持 inpaint(蒙版重绘)模块,功能直接对应"
+  },
+  {
+    "unconnected_id": "tools/model/custom_360_scene_layout_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LibLib AI 是支持自定义 LoRA 加载的生成平台,融合描述中亦提及支持 3D 空间感知,可承载 360 场景布局 LoRA"
+  },
+  {
+    "unconnected_id": "tools/model/next_scene_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LibLib AI 综合生成引擎支持 LoRA 模型运行,可承载场景转换或分镜控制类 LoRA 的执行"
+  },
+  {
+    "unconnected_id": "tools/model/openpose",
+    "connected_id": "liblibai_controlnet",
+    "reason": "OpenPose 是 ControlNet 的姿态控制变体,依据领域等价规则3,LiblibAI 等同于任何 ControlNet 变体支持,且 liblibai_controlnet 明确列出 openpose(姿态) 为支持模块。"
+  },
+  {
+    "unconnected_id": "tools/plugin/controlnet",
+    "connected_id": "liblibai_controlnet",
+    "reason": "依据领域等价规则3,LiblibAI/哩布哩布 等同于任何 ControlNet 支持,liblibai_controlnet 即为系统内 ControlNet 能力的正式载体。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/stable_diffusion_webui",
+    "connected_id": "liblibai_controlnet",
+    "reason": "SD WebUI 本质是带 ControlNet 插件的 Stable Diffusion 生图平台,liblibai_controlnet 作为云端 SD 万能生成引擎覆盖其核心生图+ControlNet 功能。"
+  },
+  {
+    "unconnected_id": "tools/model/dwpose",
+    "connected_id": "liblibai_controlnet",
+    "reason": "DWPose 专用于生成 OpenPose 骨骼控制图,属于 ControlNet OpenPose 变体,LibLib AI 综合生成工具原生支持 openpose(姿态) 控制模式,依据领域等价字典 LiblibAI 覆盖所有 ControlNet 变体。"
+  },
+  {
+    "unconnected_id": "tools/plugin/instantid",
+    "connected_id": "liblibai_controlnet",
+    "reason": "liblibai_controlnet 明确列出支持 instantid(换脸) 模块,功能完全覆盖。"
+  },
+  {
+    "unconnected_id": "tools/model/sdxl",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LibLib AI 综合生成引擎以 SDXL 为底模运行,是系统内唯一承载 SDXL 高质量生图能力的入口。"
+  },
+  {
+    "unconnected_id": "tools/model/z_image_controlnet",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Z-Image ControlNet 是 ControlNet 变体,按领域等价规则 LiblibAI 等同于任何 ControlNet 相关支持。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/scribble_diffusion",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Scribble Diffusion 基于草图控图生图,scribble 本质是 ControlNet 的一种控制模式,对应 LiblibAI 的 ControlNet 综合生成能力。"
+  },
+  {
+    "unconnected_id": "tools/plugin/ipadapter_advanced",
+    "connected_id": "liblibai_controlnet",
+    "reason": "IPAdapter Advanced 是风格+构图的图像控制节点,属于 ControlNet 生态插件,按等价规则归入 LiblibAI ControlNet 综合工具。"
+  },
+  {
+    "unconnected_id": "tools/model/stable_diffusion",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Stable Diffusion 是 LiblibAI ControlNet 生成引擎的底层基础模型生态,LiblibAI 平台的所有 SD 系工作流均以此为核心。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/stable_diffusion",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LibLib AI 平台基于 Stable Diffusion 架构运行,提供等价的 SD 模型文生图与 ControlNet 多路控制能力。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/runway_gen_2",
+    "connected_id": "ji_meng_add_task",
+    "reason": "两者本质均为 AI 视频/动态内容生成工具,即梦以视频生成能力著称且同属字节跳动生态。"
+  },
+  {
+    "unconnected_id": "tools/plugin/inpaint_model_conditioning",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LibLib AI 原生支持 inpaint(蒙版重绘)控制模块,与该 ComfyUI 局部重绘条件节点的核心功能等价。"
+  },
+  {
+    "unconnected_id": "tools/workflow/gemini",
+    "connected_id": "nano_banana",
+    "reason": "Gemini 即 Nano Banana 的别名等价,均指 Google Gemini 多模态生图模型。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/ai_image_generation",
+    "connected_id": "liblibai_controlnet",
+    "reason": "「接收草图+提示词生图」的核心能力与 LibLib ControlNet 的 lineart/canny 草图控制生图本质相同。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/enhancor_ai",
+    "connected_id": "flux_generate",
+    "reason": "「图像生成与增强」与 Flux 支持的文生图+图生图编辑增强能力高度重合。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/nb",
+    "connected_id": "nano_banana",
+    "reason": "\"NB\" 是 \"Nano Banana\" 的缩写别名,依据领域等价字典直接对应 Google Gemini 生图能力,且其角色一致性与多视图生成特性与 nano_banana 的多模态融合生图能力吻合。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/agent_lovart",
+    "connected_id": "nano_banana",
+    "reason": "nano_banana 的融合描述中明确标注「配合 Lovart 使用的模型或工作流」,Agent Lovart 作为 AI 绘图 Agent 与 nano_banana(Gemini 多模态生图)存在直接平台绑定关系。"
+  },
+  {
+    "unconnected_id": "tools/model/lao_wang_1_5_v0_5",
+    "connected_id": "liblibai_controlnet",
+    "reason": "人物风格 LoRA 模型属于 LibLib AI 生成引擎所支持的自定义模型范畴,调用能力已被 liblibai_controlnet 涵盖。"
+  },
+  {
+    "unconnected_id": "tools/model/xiang_cun_jian_zhu_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "建筑场景 LoRA 模型属于 LibLib AI 生成引擎支持的风格模型,功能已由 liblibai_controlnet 覆盖。"
+  },
+  {
+    "unconnected_id": "tools/model/qwen_image_2509_multiple_angles_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "多角度 LoRA 模型可通过 LibLib AI 综合生成工具挂载调用,本质能力已被 liblibai_controlnet 涵盖。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/nami_ai",
+    "connected_id": "nano_banana",
+    "reason": "「纳米」即「nano」的中文直译,与别名等价字典中 Nano Banana = Google Gemini 生图完全吻合。"
+  },
+  {
+    "unconnected_id": "tools/plugin/t2i_adapter",
+    "connected_id": "liblibai_controlnet",
+    "reason": "T2I Adapter 是 ControlNet 的变体预处理器,按等价字典 LiblibAI 覆盖所有 ControlNet 变体。"
+  },
+  {
+    "unconnected_id": "tools/plugin/depth_anything_v3",
+    "connected_id": "liblibai_controlnet",
+    "reason": "深度图预处理节点属于 ControlNet depth 模块,LiblibAI 已原生支持 depth 控制条件。"
+  },
+  {
+    "unconnected_id": "tools/model/depth_anything_v3",
+    "connected_id": "liblibai_controlnet",
+    "reason": "深度估计模型用于 3D 空间感知,对应 LiblibAI ControlNet 的 depth 控制通道。"
+  },
+  {
+    "unconnected_id": "tools/plugin/comfyui_advanced_controlnet",
+    "connected_id": "liblibai_controlnet",
+    "reason": "ComfyUI ControlNet 核心节点与 LiblibAI 的多路 ControlNet 能力本质等价。"
+  },
+  {
+    "unconnected_id": "tools/plugin/comfyui_controlnet_auxiliary_preprocessors",
+    "connected_id": "liblibai_controlnet",
+    "reason": "ControlNet 辅助预处理节点包(OpenPose/Canny/Depth 等)均已被 LiblibAI 综合生成工具内置覆盖。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/midjourney_style_tuner",
+    "connected_id": "midjourney_submit_job",
+    "reason": "Style Tuner 生成的风格码通过 --sref/--sw 参数在提交任务时使用,midjourney_submit_job 已支持该风格参考系统。"
+  },
+  {
+    "unconnected_id": "tools/image_process/nano_banana_pro",
+    "connected_id": "nano_banana",
+    "reason": "Nano Banana Pro 是 Nano Banana 的进阶变体,依据领域等价字典,Nano Banana 系列等同于 Google Gemini 生图(Imagen),已接入的 nano_banana 即为其对应实现。"
+  },
+  {
+    "unconnected_id": "tools/model/seedream_4",
+    "connected_id": "seedream_generate",
+    "reason": "Seedream 4 与已接入的 SeeDream-4.0 图片生成工具为同一模型系列。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/gemini_image_preview",
+    "connected_id": "nano_banana",
+    "reason": "gemini-image-preview 是 Google Gemini 图像生成模型,按领域等价字典与 Nano Banana(Gemini 生图)完全对应。"
+  },
+  {
+    "unconnected_id": "tools/other/nano_banana_pro",
+    "connected_id": "nano_banana",
+    "reason": "Nano Banana Pro 是 Nano Banana 的增强变体,品牌与核心能力(Gemini 多模态生图)一致,归属同一已接入工具。"
+  },
+  {
+    "unconnected_id": "tools/plugin/openpose",
+    "connected_id": "liblibai_controlnet",
+    "reason": "OpenPose 是 ControlNet 的姿态预处理器变体,按领域等价字典 LiblibAI 等同于所有 ControlNet 变体支持(含 openpose)。"
+  },
+  {
+    "unconnected_id": "tools/model/flux_1_kontext",
+    "connected_id": "flux_generate",
+    "reason": "Flux.1 Kontext 是 BFL(Black Forest Labs)官方模型,flux_generate 已明确支持 flux-kontext-max 并标注 BFL 来源。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/nano_banana_2",
+    "connected_id": "nano_banana",
+    "reason": "Nano Banana 系列按领域等价字典等同于 Google Gemini 生图,nano_banana 即为该能力的已接入实现。"
+  },
+  {
+    "unconnected_id": "tools/other/nanobanana",
+    "connected_id": "nano_banana",
+    "reason": "nanobanana 按领域等价字典直接等同于 Google Gemini 生图,与已接入的 nano_banana 本质相同。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/ji_meng",
+    "connected_id": "ji_meng_add_task",
+    "reason": "即梦生成任务按领域等价字典对应 ji_meng 系列,ji_meng_add_task 是提交生成任务的核心入口。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/jimeng_5.0",
+    "connected_id": "ji_meng_add_task",
+    "reason": "即梦 5.0 属于即梦系列模型,按领域等价字典直接对应已接入的即梦任务创建工具。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/nanobanana",
+    "connected_id": "nano_banana",
+    "reason": "nanobanana 按领域等价字典等同于 Google Gemini 生图(Nano Banana),与已接入的 nano_banana 工具完全一致。"
+  },
+  {
+    "unconnected_id": "tools/plugin/ipadapter_faceid",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LiblibAI 综合生成工具原生支持 instantid(换脸/人脸身份保持),与 IPAdapter FaceID 的人脸 ID 保持功能本质等价。"
+  },
+  {
+    "unconnected_id": "tools/plugin/pulid",
+    "connected_id": "liblibai_controlnet",
+    "reason": "PuLID 同为人脸 ID 保持工具,与 liblibai_controlnet 中的 instantid 模块功能一致,属于同类换脸/身份锁定能力。"
+  },
+  {
+    "unconnected_id": "tools/model/redux",
+    "connected_id": "flux_generate",
+    "reason": "Flux Redux 是 BFL(Black Forest Labs)官方参考图融合模块,依据等价字典 BFL 对应 Flux 官方生图 API,即 flux_generate。"
+  },
+  {
+    "unconnected_id": "tools/other/gemini_3_pro",
+    "connected_id": "nano_banana",
+    "reason": "nano_banana 明确标注已支持 Gemini 3 Pro,且依据等价字典 Nano Banana 等同于 Google Gemini 系列模型能力。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/jimeng_4_5",
+    "connected_id": "ji_meng_add_task",
+    "reason": "「及梦 4.5」与「即梦」为同音异字品牌别名,核心功能均为提交 AI 图像生成任务,直接对应即梦-创建任务接口。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/google_fashion_model",
+    "connected_id": "nano_banana",
+    "reason": "Google Fashion Model 为 Google 旗下图像生成模型产品,按领域等价字典 Nano Banana 即等同于 Google Gemini/Imagen 系列生图能力,归并至 nano_banana。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/ke_ling",
+    "connected_id": "ji_meng_add_task",
+    "reason": "可灵与即梦同为国内主流 AI 视频/图像生成平台,核心生成能力高度重叠,可由即梦任务提交接口承接"
+  },
+  {
+    "unconnected_id": "tools/model/realisticvision_v6.0",
+    "connected_id": "liblibai_controlnet",
+    "reason": "RealisticVision V6.0 是 LibLib AI 平台上可调用的 SD 写实底模,其生图能力由 LibLib AI 综合生成工具承载。"
+  },
+  {
+    "unconnected_id": "tools/model/fashion_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Fashion LoRA 属于可挂载到 LibLib AI 生成流程的 LoRA 权重,功能由 LibLib AI 综合生成工具覆盖。"
+  },
+  {
+    "unconnected_id": "tools/model/tryon_lora",
+    "connected_id": "liblibai_controlnet",
+    "reason": "TryOn LoRA 同为可在 LibLib AI 平台加载使用的 LoRA 模型,生成能力由 LibLib AI 综合生成工具承载。"
+  },
+  {
+    "unconnected_id": "tools/model/juggernaut_xl",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Juggernaut XL 是 LibLib AI 平台上可调用的 SDXL 底模,其生图能力由 LibLib AI 综合生成工具覆盖。"
+  },
+  {
+    "unconnected_id": "tools/model/kontext_lora",
+    "connected_id": "flux_generate",
+    "reason": "Kontext LoRA 专为 Flux Kontext 架构设计,flux_generate 已原生支持 flux-kontext-max 模型,功能本质相同。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/seedream_5_0_lite",
+    "connected_id": "seedream_generate",
+    "reason": "Seedream 5.0 Lite 是字节跳动 SeeDream 模型系列的新版本,与已接入的 SeeDream-4.0 图片生成属于同一产品线。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/seedream",
+    "connected_id": "seedream_generate",
+    "reason": "Seedream 与已接入的 SeeDream-4.0 为同一模型品牌,功能本质均为 SeeDream 图像生成。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/dreamina",
+    "connected_id": "ji_meng_add_task",
+    "reason": "Dreamina 是字节跳动「即梦」的国际品牌名,与已接入的即梦-创建任务属于同一产品。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/midjourney_v7",
+    "connected_id": "midjourney_submit_job",
+    "reason": "Midjourney V7 是 Midjourney 的版本迭代,提交生图任务应对应已接入的 Midjourney-提交生图任务工具。"
+  },
+  {
+    "unconnected_id": "tools/model/seedream_5_0_lite",
+    "connected_id": "seedream_generate",
+    "reason": "Seedream 5.0 Lite 与已接入的 SeeDream-4.0 同属 SeeDream 模型系列,功能本质相同。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/banana_pro",
+    "connected_id": "nano_banana",
+    "reason": "banana Pro 命名含 banana,依据领域等价字典 nanobanana 对应 Gemini 生图体系,归入同一工具。"
+  },
+  {
+    "unconnected_id": "tools/plugin/redux",
+    "connected_id": "flux_generate",
+    "reason": "Redux 是 Flux 系列的一致性参考控制插件,已接入的 flux_generate 融合描述中明确包含该工作流组件。"
+  },
+  {
+    "unconnected_id": "tools/plugin/comfyui_kjnodes",
+    "connected_id": "image_stitcher",
+    "reason": "comfyui-kjnodes 核心功能之一为图像拼接,与已接入的图片拼接工具功能本质一致。"
+  },
+  {
+    "unconnected_id": "tools/model/controlnet",
+    "connected_id": "liblibai_controlnet",
+    "reason": "依据领域等价字典,LiblibAI 等同于任何 ControlNet 支持,直接对应。"
+  },
+  {
+    "unconnected_id": "tools/plugin/comfyui_controlnet_aux",
+    "connected_id": "liblibai_controlnet",
+    "reason": "该插件为 ComfyUI 提供多种 ControlNet 预处理器,功能本质与 LiblibAI 的多路 ControlNet 支持等价。"
+  },
+  {
+    "unconnected_id": "tools/model/pulid",
+    "connected_id": "liblibai_controlnet",
+    "reason": "PuLID 为身份保持/换脸模型,与 LiblibAI 已支持的 instantid(换脸)模块功能本质相同。"
+  },
+  {
+    "unconnected_id": "tools/model/flux_redux",
+    "connected_id": "flux_generate",
+    "reason": "Redux 是专为 Flux 设计的图像参考适配器,属于 Flux 生态组件,对应已接入的 Flux 多模态生图工具。"
+  },
+  {
+    "unconnected_id": "tools/model/flux_1_redux_kontext",
+    "connected_id": "flux_generate",
+    "reason": "FLUX.1 Kontext 是 Flux 官方换装编辑模型,flux_generate 已明确支持 flux-kontext-max 图像编辑能力。"
+  },
+  {
+    "unconnected_id": "tools/plugin/ip_adapter_faceid",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LiblibAI 综合生成工具支持 instantid(换脸/面部特征固化),与 IP-Adapter-FaceID 的免训练角色固化功能本质等价。"
+  },
+  {
+    "unconnected_id": "tools/plugin/controlnet_openpose",
+    "connected_id": "liblibai_controlnet",
+    "reason": "按领域等价字典,LiblibAI 等同于 ControlNet 全系支持,且工具描述明确列出 openpose(姿态)模块。"
+  },
+  {
+    "unconnected_id": "tools/plugin/controlnet_depth_normal_map",
+    "connected_id": "liblibai_controlnet",
+    "reason": "按领域等价字典,LiblibAI 等同于 ControlNet 全系支持,且工具描述明确列出 depth(深度图)模块,覆盖 Depth/Normal Map 控制需求。"
+  },
+  {
+    "unconnected_id": "tools/plugin/ip_adapter_composition",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LiblibAI 综合生成工具支持多路并发 ControlNet(含 canny/lineart/softedge),可实现构图与服装展示控制,与 IP-Adapter Composition 的构图控制功能本质等价。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/midjourney_v8",
+    "connected_id": "midjourney_submit_job",
+    "reason": "Midjourney v8 属于 Midjourney 平台的新版本,提交生图任务直接对应 midjourney_submit_job。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/stable_diffusion_3",
+    "connected_id": "liblibai_controlnet",
+    "reason": "LiblibAI 是托管并运行 Stable Diffusion 系列模型(含 SD3)的在线平台,liblibai_controlnet 即为其 SD 生成引擎入口。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/flux_2_pro",
+    "connected_id": "flux_generate",
+    "reason": "flux_generate 已支持 FLUX.2 及 flux-kontext-max 等多个 Flux 变体,FLUX.2 [pro] 属于其覆盖的模型版本之一。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/flux_2",
+    "connected_id": "flux_generate",
+    "reason": "flux_generate 明确支持 FLUX.2 模型,功能完全覆盖该未接入工具的文生图与图生图需求。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/flux_2_0",
+    "connected_id": "flux_generate",
+    "reason": "Flux 2.0 属于 Flux 模型家族,与已接入的 Flux 多模态生图编辑 API 本质功能完全一致。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/flux_ai",
+    "connected_id": "flux_generate",
+    "reason": "Flux AI 即 Flux 模型平台,与已接入的 Flux 多模态生图 API 属于同一能力。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/gemini_api",
+    "connected_id": "nano_banana",
+    "reason": "按领域等价字典,Google Gemini 图像生成 API 等同于已接入的 Nano Banana(Gemini 多模态生图)。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/nanobanana_org",
+    "connected_id": "nano_banana",
+    "reason": "nanobanana.org 是 Nano Banana Pro 的可视化前端,底层能力与已接入的 Nano Banana 工具完全一致。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/gemini_3_pro_image_preview",
+    "connected_id": "nano_banana",
+    "reason": "Gemini 3 Pro Image Preview 是 Gemini 图像生成模型,按等价字典对应已接入的 Nano Banana。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/jimeng_4_0",
+    "connected_id": "ji_meng_add_task",
+    "reason": "即梦 4.0 属于即梦系列,按领域等价字典直接对应已接入的即梦创建任务工具。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/google_gemini_image_generation_api",
+    "connected_id": "nano_banana",
+    "reason": "描述中明确标注品牌名为 Nano Banana,与已接入的 Nano Banana 工具完全等价。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/google_ai_studio",
+    "connected_id": "nano_banana",
+    "reason": "Google AI Studio 作为 Nano Banana Pro 模型的官方访问渠道,本质能力归属于已接入的 Nano Banana 生图工具。"
+  },
+  {
+    "unconnected_id": "tools/plugin/advanced_controlnet",
+    "connected_id": "liblibai_controlnet",
+    "reason": "Advanced-ControlNet 是 ControlNet 功能插件,按领域等价字典 LiblibAI 等同于任何 ControlNet 支持,liblibai_controlnet 正是系统内唯一覆盖多路 ControlNet 的综合生成工具。"
+  },
+  {
+    "unconnected_id": "tools/image_gen/jimeng_ai_image",
+    "connected_id": "ji_meng_add_task",
+    "reason": "即梦 AI 图片 4.0 属于即梦系列图像生成任务,按领域等价字典直接对应 ji_meng 系列,提交生图任务应映射至 ji_meng_add_task。"
+  }
+]

+ 35 - 0
migrate_postgres.py

@@ -0,0 +1,35 @@
+from tool_agent.registry.registry import ToolRegistry
+from tool_agent.tool.tool_store import PostgreSQLToolStore
+from tool_agent.models import ToolStatus
+
+def migrate():
+    registry = ToolRegistry()
+    store = PostgreSQLToolStore()
+    
+    local_tools = registry.list_all()
+    remote_tools = {kt["id"]: kt for kt in store.list_all(limit=1000)}
+    
+    print(f"Loaded {len(local_tools)} local tools, {len(remote_tools)} remote tools")
+    
+    for tool in local_tools:
+        tid = tool.tool_id
+        
+        # Merge logic: favor remote's name/intro if it exists
+        remote_t = remote_tools.get(tid, {})
+        
+        tool_dict = {
+            "id": tid,
+            "name": remote_t.get("name") or tool.name,
+            "version": remote_t.get("version") or tool.version,
+            "introduction": remote_t.get("introduction") or tool.description,
+            "input": tool.input_schema,  # overwrite with local JSON schema
+            "output": tool.output_schema,
+            "status": "已接入" if tool.status == ToolStatus.ACTIVE else "未接入"
+        }
+        store.insert_or_update(tool_dict)
+        print(f"Migrated / Synced tool: {tid}")
+        
+    print("Migration complete!")
+
+if __name__ == '__main__':
+    migrate()

+ 97 - 0
migrate_to_postgres.py

@@ -0,0 +1,97 @@
+import json
+import logging
+import time
+from pathlib import Path
+from tool_agent.models import ToolMeta, ToolStatus
+from tool_agent.tool.tool_store import PostgreSQLToolStore
+
+logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+def migrate():
+    print("=" * 50)
+    print(" 开始迁移数据:registry.json -> PostgreSQL Tool Store")
+    print("=" * 50)
+    
+    try:
+        store = PostgreSQLToolStore()
+    except Exception as e:
+        print(f"Failed to connect to database: {e}")
+        return
+
+    # Check for legacy groups
+    groups_path = Path("data/groups.json")
+    group_map = {}
+    if groups_path.exists():
+        try:
+            gdata = json.loads(groups_path.read_text(encoding="utf-8"))
+            for g in gdata.get("groups", []):
+                # Clean up group names if they represent providers
+                # e.g. runcomfy_lifecycle -> runcomfy
+                g_id = g.get("group_id", "")
+                provider_name = g_id.replace("_lifecycle", "").replace("_task", "")
+                group_map[g_id] = provider_name
+                
+                # Create tools mapping based on old groupings
+                for t_id in g.get("tool_ids", []):
+                    # We'll use this if registry doesn't have it directly populated
+                    pass
+        except Exception:
+            pass
+
+    registry_path = Path("data/registry.json")
+    if not registry_path.exists():
+        print("找不到 data/registry.json 文件,无法迁移。")
+        return
+
+    data = json.loads(registry_path.read_text(encoding="utf-8"))
+    
+    tools_migrated = 0
+    for item in data.get("tools", []):
+        try:
+            tool = ToolMeta(**item)
+            
+            # Map ToolStatus to Postgres string
+            status_str = "已接入" if tool.status == ToolStatus.ACTIVE else "未接入"
+            
+            # Legacy groups serve as providers now
+            provider_ids = []
+            for gid in tool.group_ids:
+                mapped_pid = group_map.get(gid, gid)
+                if mapped_pid not in provider_ids:
+                    provider_ids.append(mapped_pid)
+                    
+            # 兼容性:自动推断一些已知的裸工具 Provider
+            if not provider_ids:
+                if "liblib" in tool.tool_id.lower():
+                    provider_ids.append("liblib")
+                elif "flux" in tool.tool_id.lower():
+                    provider_ids.append("flux")
+            
+            tool_dict = {
+                "id": tool.tool_id,
+                "name": tool.name,
+                "version": "1.0.0",
+                "introduction": tool.description,
+                "tutorial": "",
+                "input": tool.input_schema,
+                "output": tool.output_schema,
+                "updated_time": int(time.time()),
+                "status": status_str,
+                "provider_ids": provider_ids,
+            }
+            
+            store.insert_or_update(tool_dict)
+            print(f"[SUCCESS] 迁移工具: {tool.tool_id} (Provider: {provider_ids})")
+            tools_migrated += 1
+            
+        except Exception as e:
+            print(f"[ERROR] 工具 {item.get('tool_id')} 迁移失败: {e}")
+            
+    store.close()
+    print("=" * 50)
+    print(f" 迁移完成!共成功处理 {tools_migrated} 个工具。")
+    print(" 现在的 tool_provider 关系表已经建立,可直接从数据库加载!")
+    print("=" * 50)
+
+if __name__ == "__main__":
+    migrate()

+ 232 - 0
scripts/merge_unconnected_tools.py

@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+import asyncio
+import json
+import logging
+import argparse
+from psycopg2.extras import RealDictCursor
+
+# 导入应用内的模块
+from tool_agent.tool.tool_store import PostgreSQLToolStore
+from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, AssistantMessage, TextBlock
+
+logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
+logger = logging.getLogger(__name__)
+
+SYSTEM_PROMPT = """你是一个专业的 API 工具库去重专家。
+你的任务是将一份【未接入工具列表】与一份核心的【已接入工具列表】进行比对归并。
+如果某一个【未接入工具】(哪怕换了名字叫别的)在【已接入工具】中已经有同样的本质功能,请找出这种映射关系。
+
+例如:
+未接入:"通过 Midjourney 生成图片" 或 "Midjourney 查进度"
+已接入:由于我们有 `midjourney_submit_job` 和 `midjourney_query_job_status`,请寻找最合理的对应。
+
+【重要领域等价字典】请你在判定时,严格遵从以下我们架构中的内置别名等价逻辑:
+1. "Nano Banana" 或者 "nanobanana" 等同于 "Google Gemini 生图" 或 "Imagen 3"
+2. "BFL" 等同于 "Flux 官方生图 API"
+3. "LiblibAI" 或 "哩布哩布" 等同于任何有关 Controlnet (不论是 OpenPose/Canny 等变体) 的支持
+4. "RunComfy" 等同于 "ComfyUI 远程实例或工作流执行"
+5. "即梦" 或 "ji_meng" 同等对应其系列任务
+
+如果未能找到合理的已接入工具应对,则不输出映射。
+
+请输出一个合法的 JSON 数组,必须是一个 list of objects:
+[
+  {
+    "unconnected_id": "<未接入的工具id>",
+    "connected_id": "<对应最合理的已接入的工具id>",
+    "reason": "<非常简短的一句话匹配理由>"
+  }
+]
+
+请绝对不要包含任何 Markdown 语法、```json 标签,直接输出原生 JSON 字符串。
+"""
+
+async def match_tools_with_claude(connected_tools, unconnected_batch):
+    conn_str = json.dumps([{"id": t["id"], "name": t["name"], "desc": t["introduction"]} for t in connected_tools], ensure_ascii=False)
+    unconn_str = json.dumps([{"id": t["id"], "name": t["name"], "desc": t["introduction"]} for t in unconnected_batch], ensure_ascii=False)
+    
+    prompt = f"【已接入工具库(作为对标参考)】:\n{conn_str}\n\n【本次需判定匹配的未接入工具】:\n{unconn_str}\n\n请输出JSON结果:"
+    
+    options = ClaudeAgentOptions(model="claude-sonnet-4-5", system_prompt=SYSTEM_PROMPT)
+    
+    # 建立客户端调用
+    result_text = ""
+    try:
+        async with ClaudeSDKClient(options=options) as client:
+            await client.query(prompt)
+            async for msg in client.receive_response():
+                if isinstance(msg, AssistantMessage):
+                    for block in msg.content:
+                        if isinstance(block, TextBlock):
+                            result_text += block.text
+    except Exception as e:
+        logger.error(f"Claude API failed: {e}")
+        return []
+
+    # 毫无保留地保存最原始的字符串落盘(防崩备用方案)
+    with open("raw_claude_responses.log", "a", encoding="utf-8") as f:
+        f.write(f"\n--- Batch Output ---\n{result_text}\n")
+
+    # 尝试解析 JSON
+    try:
+        # 去掉可能的 Markdown 包装
+        clean_json = result_text.strip()
+        if clean_json.startswith("```json"):
+            clean_json = clean_json[7:]
+        elif clean_json.startswith("```"):
+            clean_json = clean_json[3:]
+        if clean_json.endswith("```"):
+            clean_json = clean_json[:-3]
+        
+        data = json.loads(clean_json.strip())
+        if isinstance(data, dict):
+            if "unconnected_id" in data:
+                return [data]
+            return []
+        elif isinstance(data, list):
+            return [item for item in data if isinstance(item, dict)]
+        return []
+    except Exception as e:
+        logger.error(f"Failed to parse JSON response: {e}\nResponse was: {result_text[:200]}")
+        return []
+
+def apply_database_merge(store: PostgreSQLToolStore, match_plan: list):
+    """根据生成的合法匹配图,执行数据库替换"""
+    if not match_plan:
+        logger.info("没有任何匹配数据可执行落地!")
+        return
+        
+    c = store.conn.cursor(cursor_factory=RealDictCursor)
+    
+    merged_count = 0
+    for match in match_plan:
+        uid = match.get("unconnected_id")
+        cid = match.get("connected_id")
+        
+        if not uid or not cid:
+            continue
+            
+        logger.info(f"==> 执行合并映射 [未接入: {uid} -> 已接入: {cid}]")
+        
+        try:
+            # 1. 查询出原未接入工具的特征介绍准备拼接
+            c.execute("SELECT introduction, tutorial, version FROM tool WHERE id = %s", (uid,))
+            u_tool = c.fetchone()
+            
+            c.execute("SELECT introduction, tutorial, version FROM tool WHERE id = %s", (cid,))
+            c_tool = c.fetchone()
+            
+            if u_tool and c_tool:
+                u_ver = u_tool.get('version') or ''
+                c_ver = c_tool.get('version') or ''
+                # Extract best version, favor c_ver if exists and not placeholder
+                latest_ver = c_ver if c_ver and c_ver != "1.0.0" else (u_ver if u_ver else "1.0.0")
+                
+                version_prefix = f"已支持的最新版本: {latest_ver}\n\n"
+                
+                new_intro = version_prefix + (c_tool['introduction'] or '') + "\n\n[融合补充描述]:\n" + (u_tool['introduction'] or '')
+                new_tutorial = (c_tool['tutorial'] or '') + "\n\n" + (u_tool['tutorial'] or '')
+                c.execute("UPDATE tool SET introduction=%s, tutorial=%s WHERE id=%s", (new_intro[:4000], new_tutorial[:4000], cid))
+                
+            # 2. 跟原表里外键绑定的迁移过去(只搬迁关系,利用 ON CONFLICT 防止主键重复)
+            tables_to_migrate = [ 
+                ("capability_tool", "capability_id", "tool_id"), 
+                ("tool_knowledge", "knowledge_id", "tool_id"),
+                ("tool_provider", "provider_id", "tool_id")
+            ]
+            
+            for table, _, tool_col in tables_to_migrate:
+                # 找出旧工具的所有关联
+                c.execute(f"SELECT * FROM {table} WHERE {tool_col} = %s", (uid,))
+                rows = c.fetchall()
+                for row in rows:
+                    if table == "capability_tool":
+                        c.execute("INSERT INTO capability_tool (capability_id, tool_id) VALUES (%s, %s) ON CONFLICT DO NOTHING", (row['capability_id'], cid))
+                    elif table == "tool_knowledge":
+                        c.execute("INSERT INTO tool_knowledge (knowledge_id, tool_id) VALUES (%s, %s) ON CONFLICT DO NOTHING", (row['knowledge_id'], cid))
+                    elif table == "tool_provider":
+                        c.execute("INSERT INTO tool_provider (provider_id, tool_id) VALUES (%s, %s) ON CONFLICT DO NOTHING", (row['provider_id'], cid))
+                        
+            # 3. 将原未接入工具删除 (cascade delete if properly constructed by DB, else manually delete relations first)
+            # 既然我们已经读了并插入到了新的关联里,我们需要手动删除旧的外键绑定好让 CASCADE 清晰或者直接删
+            c.execute("DELETE FROM capability_tool WHERE tool_id = %s", (uid,))
+            c.execute("DELETE FROM tool_knowledge WHERE tool_id = %s", (uid,))
+            c.execute("DELETE FROM tool_provider WHERE tool_id = %s", (uid,))
+            c.execute("DELETE FROM tool WHERE id = %s", (uid,))
+            
+            merged_count += 1
+        except Exception as e:
+            logger.error(f"处理 {uid} 的融合时出错: {e}")
+            store.conn.rollback() # 出错则回滚该条
+            continue
+            
+        # 单条合并成功可提交
+        store.conn.commit()
+    
+    c.close()
+    logger.info(f"所有指定合并执行完毕,已彻底抹除融合了 {merged_count} 个废弃节点工具!")
+
+async def run_workflow(apply=False):
+    store = PostgreSQLToolStore()
+    tools = store.list_all(limit=2000)
+    
+    connected = [t for t in tools if t.get("status") == "已接入"]
+    unconnected = [t for t in tools if t.get("status") != "已接入"]
+    
+    logger.info(f"提取出 {len(connected)} 个基础已接入工具,以及 {len(unconnected)} 个未接入待判决工具。")
+    if not unconnected:
+        logger.info("没有任何未接入工具需要处理。")
+        store.close()
+        return
+
+    # 尝试从本地缓存文件读取,避免重复消耗大模型调用
+    import os
+    match_file = "matches.json"
+    
+    if os.path.exists(match_file):
+        logger.info(f"发现本地存在的缓存文件 {match_file},将直接读取现有分析结果!")
+        with open(match_file, "r", encoding="utf-8") as f:
+            all_matches = json.load(f)
+    else:
+        # 切割 batch,降低大模型的单次信息处理压力,提升专注度
+        batch_size = 10
+        all_matches = []
+        
+        total_batches = (len(unconnected) // batch_size) + (1 if len(unconnected) % batch_size != 0 else 0)
+        for i in range(0, len(unconnected), batch_size):
+            batch = unconnected[i:i + batch_size]
+            logger.info(f"正在交给 Claude 引擎评估第 {i//batch_size + 1}/{total_batches} 批 ({len(batch)} items) ...")
+            
+            matches = await match_tools_with_claude(connected, batch)
+            if matches:
+                all_matches.extend(matches)
+                
+        # 缓存映射结果
+        with open(match_file, "w", encoding="utf-8") as f:
+            json.dump(all_matches, f, ensure_ascii=False, indent=2)
+        
+    logger.info(f"\n====================== 预览报告 ======================")
+    if not all_matches:
+        logger.info("Claude 没有找到任何可以安全结合的工具!")
+    else:
+        logger.info(f"Claude 匹配上了 {len(all_matches)} 个工具,建议的更换路线如下:")
+        for m in all_matches:
+            logger.info(f" - [冗余废除] {m.get('unconnected_id')}  ==> [主工具] {m.get('connected_id')}")
+            logger.info(f"   理由: {m.get('reason','')}")
+            
+    logger.info(f"====================================================\n")
+    
+    if apply:
+        logger.info("启动真实的数据库落地置换 (Apply mode is ON)...")
+        apply_database_merge(store, all_matches)
+    else:
+        logger.info("目前为 Dry-Run 模式,如果审查匹配无误觉得干的漂亮,请携带 --apply 开关挂载实际融合执行!")
+
+    store.close()
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--apply", action="store_true", help="Apply DB migrations physically")
+    args = parser.parse_args()
+    
+    asyncio.run(run_workflow(apply=args.apply))

+ 4 - 0
src/tool_agent/__main__.py

@@ -1,6 +1,10 @@
 import asyncio
 import logging
 import sys
+import os
+from dotenv import load_dotenv
+
+load_dotenv(override=True)
 
 from tool_agent.config import settings
 from tool_agent.router.agent import Router

BIN
src/tool_agent/__pycache__/__main__.cpython-312.pyc


BIN
src/tool_agent/__pycache__/models.cpython-312.pyc


+ 3 - 3
src/tool_agent/models.py

@@ -54,9 +54,9 @@ class ToolMeta(BaseModel):
 
     # 新增字段(向后兼容)
     backend_runtime: BackendRuntime | None = None  # 后端执行环境
-    group_ids: list[str] = Field(default_factory=list)  # 所属工具组
-    tool_slug_ids: list[str] = Field(default_factory=list)  # 关联的 KnowHub 工具 tool_slug 列表
-
+    provider_ids: list[str] = Field(default_factory=list)  # 工具提供方
+    capability_ids: list[str] = Field(default_factory=list)  # 工具解决的原子能力
+    knowledge_ids: list[str] = Field(default_factory=list)  # 工具挂载的知识资产
 
 # ---- 容器信息(Docker 运行时,独立于工具元数据) ----
 

BIN
src/tool_agent/registry/__pycache__/registry.cpython-312.pyc


+ 73 - 116
src/tool_agent/registry/registry.py

@@ -1,8 +1,7 @@
-"""注册表 CRUD — 纯元数据管理,读写 registry.json"""
+"""注册表 CRUD — 纯元数据管理,读写 PostgreSQL DB"""
 
 from __future__ import annotations
 
-import asyncio
 import json
 import logging
 import shutil
@@ -12,107 +11,82 @@ from pathlib import Path
 
 from tool_agent.config import settings
 from tool_agent.models import ToolMeta, ToolStatus, BackendRuntime
+from tool_agent.tool.tool_store import PostgreSQLToolStore
 
 logger = logging.getLogger(__name__)
 
 
-class RegistryWriteQueue:
-    """Registry 写入队列,保证并发安全"""
-
-    def __init__(self):
-        self._queue: asyncio.Queue = None
-        self._worker_task = None
-        self._running = False
-
-    async def start(self):
-        """启动写入队列"""
-        if self._running:
-            return
-        self._queue = asyncio.Queue()
-        self._running = True
-        self._worker_task = asyncio.create_task(self._worker())
-        logger.info("Registry write queue started")
-
-    async def stop(self):
-        """停止写入队列"""
-        if not self._running:
-            return
-        self._running = False
-        await self._queue.put(None)  # 发送停止信号
-        if self._worker_task:
-            await self._worker_task
-        logger.info("Registry write queue stopped")
-
-    async def enqueue(self, operation: str, data: dict):
-        """将写入操作加入队列"""
-        if not self._running:
-            await self.start()
-        await self._queue.put({"operation": operation, "data": data})
-
-    async def _worker(self):
-        """后台工作线程,顺序处理写入操作"""
-        while self._running:
-            try:
-                item = await self._queue.get()
-                if item is None:  # 停止信号
-                    break
-
-                operation = item["operation"]
-                data = item["data"]
-
-                # 执行写入操作
-                if operation == "save":
-                    callback = data.get("callback")
-                    if callback:
-                        callback()
-
-                self._queue.task_done()
-            except Exception as e:
-                logger.error(f"Registry write queue error: {e}")
-
-
 class ToolRegistry:
-    """工具注册表 — 只管工具是什么,不管在哪跑"""
+    """工具注册表 — 直接对接远端 PostgreSQL"""
 
     def __init__(self) -> None:
-        self._path = settings.data_dir / "registry.json"
         self._tools: dict[str, ToolMeta] = {}
         self._lock = threading.Lock()
-        self._write_queue = RegistryWriteQueue()
-
-        # 工具组管理器
+        
+        # 为了兼容向下的局部推断
         from tool_agent.registry.groups import ToolGroupManager
         self.group_manager = ToolGroupManager()
 
         self._load()
 
     def _load(self) -> None:
+        """从云端 PostgreSQL 拉取所有工具数据"""
         with self._lock:
             self._tools = {}
-            if self._path.exists():
-                data = json.loads(self._path.read_text(encoding="utf-8"))
-                for item in data.get("tools", []):
-                    tool = ToolMeta(**item)
-                    if tool.backend_runtime is None:
-                        tool.backend_runtime = self._infer_backend_runtime(tool.tool_id)
-                    if not tool.group_ids:
-                        tool.group_ids = [g.group_id for g in self.group_manager.find_groups_for_tool(tool.tool_id)]
-                    self._tools[tool.tool_id] = tool
-
-    def _save(self) -> None:
-        """同步保存(内部使用,已加锁)"""
-        self._path.parent.mkdir(parents=True, exist_ok=True)
-        data = {"tools": [t.model_dump(mode="json") for t in self._tools.values()], "version": "2.0"}
-        self._path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
-
-    async def _save_async(self) -> None:
-        """异步保存(通过队列)"""
-        await self._write_queue.enqueue("save", {"callback": self._save_sync})
-
-    def _save_sync(self) -> None:
-        """同步保存回调"""
-        with self._lock:
-            self._save()
+            try:
+                store = PostgreSQLToolStore()
+                db_tools = store.list_all(limit=1000)
+                
+                for db_t in db_tools:
+                    status = ToolStatus.ACTIVE if db_t.get("status") == "已接入" else ToolStatus.INACTIVE
+                    
+                    t = ToolMeta(
+                        tool_id=db_t.get("id", ""),
+                        name=db_t.get("name", ""),
+                        description=db_t.get("introduction") or "",
+                        version=db_t.get("version") or "1.0.0",
+                        input_schema=db_t.get("input") or {},
+                        output_schema=db_t.get("output") or {},
+                        status=status,
+                        provider_ids=db_t.get("provider_ids", []),
+                        capability_ids=db_t.get("capability_ids", []),
+                        knowledge_ids=db_t.get("knowledge_ids", [])
+                    )
+                    
+                    if t.backend_runtime is None:
+                        t.backend_runtime = self._infer_backend_runtime(t.tool_id)
+                        
+                    self._tools[t.tool_id] = t
+                    
+                store.close()
+                logger.info(f"Loaded {len(self._tools)} tools from PostgreSQL")
+            except Exception as e:
+                logger.error(f"Failed to load registry from PostgreSQL: {e}")
+
+    def _db_save(self, tool: ToolMeta) -> None:
+        """将单个工具写入/更新到 PostgreSQL"""
+        try:
+            store = PostgreSQLToolStore()
+            status_str = "已接入" if tool.status == ToolStatus.ACTIVE else "未接入"
+            
+            tool_dict = {
+                "id": tool.tool_id,
+                "name": tool.name,
+                "version": getattr(tool, "version", "1.0.0"),
+                "introduction": tool.description,
+                "tutorial": "",
+                "input": tool.input_schema,
+                "output": tool.output_schema,
+                "updated_time": int(time.time()),
+                "status": status_str,
+                "provider_ids": tool.provider_ids,
+                "capability_ids": tool.capability_ids,
+                "knowledge_ids": tool.knowledge_ids,
+            }
+            store.insert_or_update(tool_dict)
+            store.close()
+        except Exception as e:
+            logger.error(f"DB Save error for tool {tool.tool_id}: {e}")
 
     def _infer_backend_runtime(self, tool_id: str) -> BackendRuntime:
         """根据 source 推断后端执行环境"""
@@ -143,24 +117,19 @@ class ToolRegistry:
     def register(self, tool: ToolMeta) -> None:
         with self._lock:
             self._tools[tool.tool_id] = tool
-        # 异步保存,避免阻塞
-        try:
-            loop = asyncio.get_event_loop()
-            if loop.is_running():
-                asyncio.create_task(self._save_async())
-            else:
-                self._save()
-        except RuntimeError:
-            # 没有事件循环,同步保存
-            with self._lock:
-                self._save()
-        logger.info(f"Registered tool: {tool.tool_id}")
+            self._db_save(tool)
+        logger.info(f"Registered/Updated tool in Postgres: {tool.tool_id}")
 
     def unregister(self, tool_id: str) -> bool:
         with self._lock:
             if tool_id in self._tools:
                 del self._tools[tool_id]
-                self._save()
+                try:
+                    store = PostgreSQLToolStore()
+                    store.delete(tool_id)
+                    store.close()
+                except Exception as e:
+                    logger.error(f"Failed to delete {tool_id} from Postgres: {e}")
                 logger.info(f"Unregistered tool: {tool_id}")
                 return True
         return False
@@ -168,16 +137,8 @@ class ToolRegistry:
     def update(self, tool: ToolMeta) -> None:
         with self._lock:
             self._tools[tool.tool_id] = tool
-        # 异步保存
-        try:
-            loop = asyncio.get_event_loop()
-            if loop.is_running():
-                asyncio.create_task(self._save_async())
-            else:
-                self._save()
-        except RuntimeError:
-            with self._lock:
-                self._save()
+            self._db_save(tool)
+        logger.info(f"Updated tool in Postgres: {tool.tool_id}")
 
     # ---- 查询 ----
 
@@ -196,12 +157,9 @@ class ToolRegistry:
         except ValueError:
             return []
 
-    def get_tools_in_group(self, group_id: str) -> list[ToolMeta]:
-        """获取工具组内的所有工具"""
-        group = self.group_manager.get_group(group_id)
-        if not group:
-            return []
-        return [self._tools[tid] for tid in group.tool_ids if tid in self._tools and self._tools[tid].status == ToolStatus.ACTIVE]
+    def get_tools_by_provider(self, provider_id: str) -> list[ToolMeta]:
+        """获取同属一个 provider 发行的工具群"""
+        return [t for t in self._tools.values() if provider_id in t.provider_ids and t.status == ToolStatus.ACTIVE]
 
     def search(self, keyword: str) -> list[ToolMeta]:
         kw = keyword.lower()
@@ -290,7 +248,6 @@ class ToolRegistry:
                 except Exception:
                     pass
 
-        # 清理来源存储
         try:
             from tool_agent.router.status import SourceStore
             SourceStore().remove_tool(tool_id)

BIN
src/tool_agent/router/__pycache__/agent.cpython-312.pyc


+ 0 - 107
src/tool_agent/router/agent.py

@@ -70,8 +70,6 @@ class Router:
             self.registry._load()
             self.status_manager._sync_from_registry()
 
-            # CodingAgent 完成后,同步点亮 KnowHub 工具表
-            await self._sync_knowhub_after_register()
         except Exception as e:
             logger.error(f"Create task {task_id} failed: {e}")
             self._tasks[task_id].update({"status": "failed", "error": str(e)})
@@ -115,108 +113,3 @@ class Router:
 
     def stop_all(self) -> int:
         return self.status_manager.stop_all()
-
-    async def _sync_knowhub_after_register(self) -> None:
-        """CodingAgent 注册完工具后,智能匹配 KnowHub 工具表并点亮
-
-        1. 获取所有 registry 工具和 KnowHub 工具
-        2. 根据名称、描述、分类等信息智能匹配
-        3. 更新双向索引:registry.tool_slug_ids 和 KnowHub.toolhub_items
-        4. 点亮已接入的 KnowHub 工具
-        """
-        try:
-            from tool_agent.tool_table import get_client
-            from tool_agent.registry.groups import ToolGroupManager
-
-            client = get_client()
-            group_manager = ToolGroupManager()
-            all_knowhub_tools = client.list_all_tools()
-
-            if not all_knowhub_tools:
-                logger.info("KnowHub tool table is empty or unreachable, skipping sync")
-                return
-
-            # 构建 KnowHub 工具索引
-            knowhub_index = {}  # tool_slug -> knowhub_tool
-            for kt in all_knowhub_tools:
-                slug = kt.get("metadata", {}).get("tool_slug", "")
-                if slug:
-                    knowhub_index[slug] = kt
-
-            # 遍历 registry 工具,智能匹配 KnowHub 工具
-            matched_map: dict[str, dict] = {}  # slug -> {"tools": [...], "groups": set()}
-
-            for tool in self.registry.list_all():
-                matched_slugs = self._match_knowhub_tools(tool, knowhub_index)
-
-                if matched_slugs:
-                    # 更新 registry 中的 tool_slug_ids
-                    if set(matched_slugs) != set(tool.tool_slug_ids):
-                        tool.tool_slug_ids = matched_slugs
-                        self.registry.update(tool)
-                        logger.info(f"Updated {tool.tool_id} -> {matched_slugs}")
-
-                    # 收集到 matched_map
-                    for slug in matched_slugs:
-                        if slug not in matched_map:
-                            matched_map[slug] = {"tools": [], "groups": set()}
-                        matched_map[slug]["tools"].append({tool.tool_id: tool.description})
-                        matched_map[slug]["groups"].update(tool.group_ids)
-
-            # 更新 KnowHub 工具表
-            for slug, data in matched_map.items():
-                kt = knowhub_index.get(slug)
-                if not kt:
-                    continue
-
-                # 合并工具和组
-                items = data["tools"].copy()
-                for group_id in data["groups"]:
-                    group = group_manager.get_group(group_id)
-                    if group:
-                        items.append({group_id: group.description})
-
-                try:
-                    client.update_toolhub_items(kt["id"], items)
-                    client.update_tool_status(kt["id"], "已接入")
-                    logger.info(f"Synced {slug}: {len(data['tools'])} tools, {len(data['groups'])} groups")
-                except Exception as e:
-                    logger.warning(f"Failed to sync {slug}: {e}")
-
-        except Exception as e:
-            logger.warning(f"KnowHub sync failed (non-fatal): {e}")
-
-    def _match_knowhub_tools(self, tool, knowhub_index: dict) -> list[str]:
-        """智能匹配工具能解决哪些 KnowHub 工具问题
-
-        匹配规则:
-        1. 工具名称包含 KnowHub 工具的 slug 或 title
-        2. 工具描述包含 KnowHub 工具的关键词
-        3. 分类匹配
-        """
-        matched = []
-        tool_name_lower = tool.name.lower()
-        tool_id_lower = tool.tool_id.lower()
-        tool_desc_lower = tool.description.lower()
-
-        for slug, kt in knowhub_index.items():
-            kt_slug = slug.lower()
-            kt_title = kt.get("title", "").lower()
-            kt_desc = kt.get("metadata", {}).get("description", "").lower()
-
-            # 规则1: 名称匹配
-            if kt_slug in tool_name_lower or kt_slug in tool_id_lower:
-                matched.append(slug)
-                continue
-            if kt_title and kt_title in tool_name_lower:
-                matched.append(slug)
-                continue
-
-            # 规则2: 描述关键词匹配(提取 KnowHub 工具的核心词)
-            if kt_desc:
-                keywords = [w for w in kt_desc.split() if len(w) > 3][:5]
-                if any(kw in tool_desc_lower for kw in keywords):
-                    matched.append(slug)
-                    continue
-
-        return matched

+ 27 - 12
src/tool_agent/service/agent.py

@@ -149,7 +149,8 @@ def _build_context(router: Router) -> str:
             "tool_id": t.tool_id, "name": t.name,
             "description": t.description[:80],
             "backend_runtime": t.backend_runtime.value if t.backend_runtime else "unknown",
-            "group_ids": t.group_ids,
+            "provider_ids": t.provider_ids,
+            "capability_ids": t.capability_ids,
             "state": route.state.value if route else "stopped",
         })
     groups = [{"group_id": g.group_id, "name": g.name,
@@ -195,26 +196,39 @@ SYSTEM_PROMPT = """你是 Tool Agent 系统的智能客服。
 """
 
 
-# ===========================================================================
-#  ServiceAgent 类
-# ===========================================================================
-
 class ServiceAgent:
     """只读客服 Agent,按 session_id 维护对话记忆"""
 
     def __init__(self, router: Router, model: str = "claude-sonnet-4-5"):
         self.router = router
         self.model = model
+        self._chat_history: dict[str, list[dict]] = {}
         global _router
         _router = router
 
     async def chat(self, message: str, session_id: str | None = None) -> tuple[str, str]:
         """对话。返回 (回复文本, session_id)"""
+        actual_sid = session_id or str(uuid.uuid4())
+        
         mcp_server = create_sdk_mcp_server(
             name=MCP_SERVER_NAME, version="1.0.0", tools=ALL_TOOLS)
 
         ctx = _build_context(self.router)
-        enhanced = f"[实时系统上下文]\n{ctx}\n\n用户消息:{message}"
+        
+        # 构建历史记录
+        history = self._chat_history.setdefault(actual_sid, [])
+        history_str = ""
+        if history:
+            history_str = "【过去的对话记录,供你参考联系上下文】\n"
+            # 截取最近 10 条对话,防止越积越多
+            for msg in history[-10:]:
+                history_str += f"{msg['role']}: {msg['content']}\n"
+            history_str += "\n"
+
+        enhanced = f"[实时系统上下文]\n{ctx}\n\n{history_str}当前用户的最新消息:{message}"
+
+        def _log_stderr(line: str):
+            logger.error(f"[Claude CLI] {line}")
 
         options = ClaudeAgentOptions(
             system_prompt=SYSTEM_PROMPT,
@@ -222,20 +236,21 @@ class ServiceAgent:
             model=self.model,
             setting_sources=["project"],
             allowed_tools=[f"mcp__{MCP_SERVER_NAME}__{t.name}" for t in ALL_TOOLS],
+            stderr=_log_stderr,
         )
-        if session_id:
-            options.resume = session_id
 
         result_text = ""
-        actual_sid = session_id
 
         async with ClaudeSDKClient(options=options) as client:
             await client.query(enhanced)
-            for msg in client.get_conversation():
+            async for msg in client.receive_response():
                 if isinstance(msg, AssistantMessage):
                     for block in msg.content:
                         if isinstance(block, TextBlock):
                             result_text += block.text
-            actual_sid = getattr(client, 'session_id', None) or actual_sid
 
-        return result_text, actual_sid or str(uuid.uuid4())
+        # 记录本次问答到内存,用于下一轮补充
+        history.append({"role": "User", "content": message})
+        history.append({"role": "Assistant", "content": result_text})
+
+        return result_text, actual_sid

BIN
src/tool_agent/tool/__pycache__/__init__.cpython-312.pyc


+ 3 - 2
src/tool_agent/tool/agent.py

@@ -502,8 +502,9 @@ async def register_tool_fn(args):
             stream_support=args.get("stream_support", False),
             status=ToolStatus.ACTIVE,
             backend_runtime=backend_runtime,
-            group_ids=args.get("group_ids", []),
-            tool_slug_ids=args.get("tool_slug_ids", []),
+            provider_ids=args.get("provider_ids", []),
+            capability_ids=args.get("capability_ids", []),
+            knowledge_ids=args.get("knowledge_ids", []),
         )
         _registry.register(tool_meta)
 

+ 243 - 0
src/tool_agent/tool/capability.py

@@ -0,0 +1,243 @@
+"""PostgreSQL capability 存储封装
+
+用于存储和检索原子能力数据,支持向量检索。
+表名:capability(从 atomic_capability 迁移)
+"""
+
+import os
+import json
+import psycopg2
+from psycopg2.extras import RealDictCursor
+from typing import List, Dict, Optional
+from dotenv import load_dotenv
+from knowhub.knowhub_db.cascade import cascade_delete
+
+load_dotenv()
+
+# 关联字段子查询
+_REL_SUBQUERIES = """
+    (SELECT COALESCE(json_agg(rc.requirement_id), '[]'::json)
+     FROM requirement_capability rc WHERE rc.capability_id = capability.id) AS requirement_ids,
+    (SELECT COALESCE(json_agg(ct.tool_id), '[]'::json)
+     FROM capability_tool ct WHERE ct.capability_id = capability.id) AS tool_ids,
+    (SELECT COALESCE(
+        json_object_agg(ct2.tool_id, ct2.description), '{}'::json)
+     FROM capability_tool ct2 WHERE ct2.capability_id = capability.id AND ct2.description != '') AS implements,
+    (SELECT COALESCE(json_agg(ck.knowledge_id), '[]'::json)
+     FROM capability_knowledge ck WHERE ck.capability_id = capability.id) AS knowledge_ids
+"""
+
+_BASE_FIELDS = "id, name, criterion, description"
+
+_SELECT_FIELDS = f"{_BASE_FIELDS}, {_REL_SUBQUERIES}"
+
+
+class PostgreSQLCapabilityStore:
+    def __init__(self):
+        """初始化 PostgreSQL 连接"""
+        self.conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        self.conn.autocommit = False
+        print(f"[PostgreSQL Capability] 已连接到远程数据库: {os.getenv('KNOWHUB_DB')}")
+
+    def _reconnect(self):
+        self.conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        self.conn.autocommit = False
+
+    def _ensure_connection(self):
+        if self.conn.closed != 0:
+            self._reconnect()
+        else:
+            try:
+                c = self.conn.cursor()
+                c.execute("SELECT 1")
+                c.close()
+            except (psycopg2.OperationalError, psycopg2.InterfaceError):
+                self._reconnect()
+
+    def _get_cursor(self):
+        self._ensure_connection()
+        return self.conn.cursor(cursor_factory=RealDictCursor)
+
+    def _save_relations(self, cursor, cap_id: str, data: Dict):
+        """保存 capability 的关联表数据"""
+        if 'requirement_ids' in data:
+            cursor.execute("DELETE FROM requirement_capability WHERE capability_id = %s", (cap_id,))
+            for req_id in data['requirement_ids']:
+                cursor.execute(
+                    "INSERT INTO requirement_capability (requirement_id, capability_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                    (req_id, cap_id))
+
+        # tool_ids + implements 合并写入 capability_tool
+        if 'tool_ids' in data or 'implements' in data:
+            cursor.execute("DELETE FROM capability_tool WHERE capability_id = %s", (cap_id,))
+            implements = data.get('implements', {})
+            tool_ids = set(data.get('tool_ids', []))
+            # 先写 tool_ids 列表中的(附带 implements 的 description)
+            for tool_id in tool_ids:
+                desc = implements.get(tool_id, '')
+                cursor.execute(
+                    "INSERT INTO capability_tool (capability_id, tool_id, description) VALUES (%s, %s, %s) ON CONFLICT DO NOTHING",
+                    (cap_id, tool_id, desc))
+            # 再写 implements 中有但 tool_ids 列表没有的
+            for tool_id, desc in implements.items():
+                if tool_id not in tool_ids:
+                    cursor.execute(
+                        "INSERT INTO capability_tool (capability_id, tool_id, description) VALUES (%s, %s, %s) ON CONFLICT DO NOTHING",
+                        (cap_id, tool_id, desc))
+
+        if 'knowledge_ids' in data:
+            cursor.execute("DELETE FROM capability_knowledge WHERE capability_id = %s", (cap_id,))
+            for kid in data['knowledge_ids']:
+                cursor.execute(
+                    "INSERT INTO capability_knowledge (capability_id, knowledge_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                    (cap_id, kid))
+
+    def insert_or_update(self, cap: Dict):
+        """插入或更新原子能力"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("""
+                INSERT INTO capability (
+                    id, name, criterion, description, embedding
+                ) VALUES (%s, %s, %s, %s, %s)
+                ON CONFLICT (id) DO UPDATE SET
+                    name = EXCLUDED.name,
+                    criterion = EXCLUDED.criterion,
+                    description = EXCLUDED.description,
+                    embedding = EXCLUDED.embedding
+            """, (
+                cap['id'],
+                cap.get('name', ''),
+                cap.get('criterion', ''),
+                cap.get('description', ''),
+                cap.get('embedding'),
+            ))
+            self._save_relations(cursor, cap['id'], cap)
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def get_by_id(self, cap_id: str) -> Optional[Dict]:
+        """根据 ID 获取原子能力"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute(f"""
+                SELECT {_SELECT_FIELDS}
+                FROM capability WHERE id = %s
+            """, (cap_id,))
+            result = cursor.fetchone()
+            return self._format_result(result) if result else None
+        finally:
+            cursor.close()
+
+    def search(self, query_embedding: List[float], limit: int = 10) -> List[Dict]:
+        """向量检索原子能力"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute(f"""
+                SELECT {_SELECT_FIELDS},
+                       1 - (embedding <=> %s::real[]) as score
+                FROM capability
+                WHERE embedding IS NOT NULL
+                ORDER BY embedding <=> %s::real[]
+                LIMIT %s
+            """, (query_embedding, query_embedding, limit))
+            results = cursor.fetchall()
+            return [self._format_result(r) for r in results]
+        finally:
+            cursor.close()
+
+    def list_all(self, limit: int = 100, offset: int = 0) -> List[Dict]:
+        """列出原子能力"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute(f"""
+                SELECT {_SELECT_FIELDS}
+                FROM capability
+                ORDER BY id
+                LIMIT %s OFFSET %s
+            """, (limit, offset))
+            results = cursor.fetchall()
+            return [self._format_result(r) for r in results]
+        finally:
+            cursor.close()
+
+    def update(self, cap_id: str, updates: Dict):
+        """更新原子能力字段"""
+        cursor = self._get_cursor()
+        try:
+            # 分离关联字段
+            rel_fields = {}
+            for key in ('requirement_ids', 'implements', 'tool_ids', 'knowledge_ids'):
+                if key in updates:
+                    rel_fields[key] = updates.pop(key)
+
+            if updates:
+                set_parts = []
+                params = []
+                for key, value in updates.items():
+                    set_parts.append(f"{key} = %s")
+                    params.append(value)
+                params.append(cap_id)
+                cursor.execute(
+                    f"UPDATE capability SET {', '.join(set_parts)} WHERE id = %s",
+                    params
+                )
+
+            if rel_fields:
+                self._save_relations(cursor, cap_id, rel_fields)
+
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def delete(self, cap_id: str):
+        """删除原子能力及其关联表记录"""
+        cursor = self._get_cursor()
+        try:
+            cascade_delete(cursor, 'capability', cap_id)
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def count(self) -> int:
+        """统计原子能力总数"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("SELECT COUNT(*) as count FROM capability")
+            return cursor.fetchone()['count']
+        finally:
+            cursor.close()
+
+    def _format_result(self, row: Dict) -> Dict:
+        """格式化查询结果"""
+        if not row:
+            return None
+        result = dict(row)
+        for field in ('requirement_ids', 'tool_ids', 'knowledge_ids'):
+            if field in result and isinstance(result[field], str):
+                result[field] = json.loads(result[field])
+            elif field in result and result[field] is None:
+                result[field] = []
+        if 'implements' in result:
+            if isinstance(result['implements'], str):
+                result['implements'] = json.loads(result['implements'])
+            elif result['implements'] is None:
+                result['implements'] = {}
+        return result
+
+    def close(self):
+        if self.conn:
+            self.conn.close()

+ 299 - 0
src/tool_agent/tool/tool_store.py

@@ -0,0 +1,299 @@
+"""PostgreSQL tool 存储封装
+
+用于存储和检索工具数据,支持向量检索。
+表名:tool(从 tool_table 迁移)
+"""
+
+import os
+import json
+import psycopg2
+from psycopg2.extras import RealDictCursor
+from typing import List, Dict, Optional
+from dotenv import load_dotenv
+try:
+    from knowhub.knowhub_db.cascade import cascade_delete
+except ImportError:
+    cascade_delete = None
+
+load_dotenv()
+
+# 关联字段子查询
+_REL_SUBQUERIES = """
+    (SELECT COALESCE(json_agg(ct.capability_id), '[]'::json)
+     FROM capability_tool ct WHERE ct.tool_id = tool.id) AS capability_ids,
+    (SELECT COALESCE(json_agg(tk.knowledge_id), '[]'::json)
+     FROM tool_knowledge tk WHERE tk.tool_id = tool.id) AS knowledge_ids,
+    (SELECT COALESCE(json_agg(tp.provider_id), '[]'::json)
+     FROM tool_provider tp WHERE tp.tool_id = tool.id) AS provider_ids
+"""
+
+_BASE_FIELDS = "id, name, version, introduction, tutorial, input, output, updated_time, status"
+
+_SELECT_FIELDS = f"{_BASE_FIELDS}, {_REL_SUBQUERIES}"
+
+
+class PostgreSQLToolStore:
+    def __init__(self):
+        """初始化 PostgreSQL 连接"""
+        self.conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        self.conn.autocommit = False
+        print(f"[PostgreSQL Tool] 已连接到远程数据库: {os.getenv('KNOWHUB_DB')}")
+
+    def _reconnect(self):
+        self.conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        self.conn.autocommit = False
+
+    def _ensure_connection(self):
+        if self.conn.closed != 0:
+            self._reconnect()
+        else:
+            try:
+                c = self.conn.cursor()
+                c.execute("SELECT 1")
+                c.close()
+            except (psycopg2.OperationalError, psycopg2.InterfaceError):
+                self._reconnect()
+
+    def _get_cursor(self):
+        self._ensure_connection()
+        return self.conn.cursor(cursor_factory=RealDictCursor)
+
+    def insert_or_update(self, tool: Dict):
+        """插入或更新工具"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute("""
+                INSERT INTO tool (
+                    id, name, version, introduction, tutorial, input, output,
+                    updated_time, status, embedding
+                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
+                ON CONFLICT (id) DO UPDATE SET
+                    name = EXCLUDED.name,
+                    version = EXCLUDED.version,
+                    introduction = EXCLUDED.introduction,
+                    tutorial = EXCLUDED.tutorial,
+                    input = EXCLUDED.input,
+                    output = EXCLUDED.output,
+                    updated_time = EXCLUDED.updated_time,
+                    status = EXCLUDED.status,
+                    embedding = EXCLUDED.embedding
+            """, (
+                tool['id'],
+                tool.get('name', ''),
+                tool.get('version'),
+                tool.get('introduction', ''),
+                tool.get('tutorial', ''),
+                json.dumps(tool.get('input', '')),
+                json.dumps(tool.get('output', '')),
+                tool.get('updated_time', 0),
+                tool.get('status', '未接入'),
+                tool.get('embedding'),
+            ))
+            # 写入关联表
+            tool_id = tool['id']
+            if 'capability_ids' in tool:
+                cursor.execute("DELETE FROM capability_tool WHERE tool_id = %s", (tool_id,))
+                for cap_id in tool['capability_ids']:
+                    cursor.execute(
+                        "INSERT INTO capability_tool (capability_id, tool_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (cap_id, tool_id))
+            if 'knowledge_ids' in tool:
+                cursor.execute("DELETE FROM tool_knowledge WHERE tool_id = %s", (tool_id,))
+                for kid in tool['knowledge_ids']:
+                    cursor.execute(
+                        "INSERT INTO tool_knowledge (tool_id, knowledge_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (tool_id, kid))
+            if 'provider_ids' in tool:
+                cursor.execute("DELETE FROM tool_provider WHERE tool_id = %s", (tool_id,))
+                for pid in tool['provider_ids']:
+                    cursor.execute(
+                        "INSERT INTO tool_provider (tool_id, provider_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (tool_id, pid))
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def get_by_id(self, tool_id: str) -> Optional[Dict]:
+        """根据 ID 获取工具"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute(f"""
+                SELECT {_SELECT_FIELDS}
+                FROM tool WHERE id = %s
+            """, (tool_id,))
+            result = cursor.fetchone()
+            return self._format_result(result) if result else None
+        finally:
+            cursor.close()
+
+    def search(self, query_embedding: List[float], limit: int = 10, status: Optional[str] = None) -> List[Dict]:
+        """向量检索工具"""
+        cursor = self._get_cursor()
+        try:
+            if status:
+                sql = f"""
+                    SELECT {_SELECT_FIELDS},
+                           1 - (embedding <=> %s::real[]) as score
+                    FROM tool
+                    WHERE embedding IS NOT NULL AND status = %s
+                    ORDER BY embedding <=> %s::real[]
+                    LIMIT %s
+                """
+                params = [query_embedding, status, query_embedding, limit]
+            else:
+                sql = f"""
+                    SELECT {_SELECT_FIELDS},
+                           1 - (embedding <=> %s::real[]) as score
+                    FROM tool
+                    WHERE embedding IS NOT NULL
+                    ORDER BY embedding <=> %s::real[]
+                    LIMIT %s
+                """
+                params = [query_embedding, query_embedding, limit]
+            cursor.execute(sql, params)
+            results = cursor.fetchall()
+            return [self._format_result(r) for r in results]
+        finally:
+            cursor.close()
+
+    def list_all(self, limit: int = 100, offset: int = 0, status: Optional[str] = None) -> List[Dict]:
+        """列出工具"""
+        cursor = self._get_cursor()
+        try:
+            if status:
+                cursor.execute(f"""
+                    SELECT {_SELECT_FIELDS}
+                    FROM tool
+                    WHERE status = %s
+                    ORDER BY updated_time DESC
+                    LIMIT %s OFFSET %s
+                """, (status, limit, offset))
+            else:
+                cursor.execute(f"""
+                    SELECT {_SELECT_FIELDS}
+                    FROM tool
+                    ORDER BY updated_time DESC
+                    LIMIT %s OFFSET %s
+                """, (limit, offset))
+            results = cursor.fetchall()
+            return [self._format_result(r) for r in results]
+        finally:
+            cursor.close()
+
+    def update(self, tool_id: str, updates: Dict):
+        """更新工具字段"""
+        cursor = self._get_cursor()
+        try:
+            # 分离关联字段
+            cap_ids = updates.pop('capability_ids', None)
+            knowledge_ids = updates.pop('knowledge_ids', None)
+            provider_ids = updates.pop('provider_ids', None)
+
+            if updates:
+                set_parts = []
+                params = []
+                json_fields = ('input', 'output')
+                for key, value in updates.items():
+                    set_parts.append(f"{key} = %s")
+                    if key in json_fields:
+                        params.append(json.dumps(value))
+                    else:
+                        params.append(value)
+                params.append(tool_id)
+                cursor.execute(
+                    f"UPDATE tool SET {', '.join(set_parts)} WHERE id = %s",
+                    params
+                )
+
+            # 更新关联表
+            if cap_ids is not None:
+                cursor.execute("DELETE FROM capability_tool WHERE tool_id = %s", (tool_id,))
+                for cap_id in cap_ids:
+                    cursor.execute(
+                        "INSERT INTO capability_tool (capability_id, tool_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (cap_id, tool_id))
+
+            if knowledge_ids is not None:
+                cursor.execute("DELETE FROM tool_knowledge WHERE tool_id = %s", (tool_id,))
+                for kid in knowledge_ids:
+                    cursor.execute(
+                        "INSERT INTO tool_knowledge (tool_id, knowledge_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (tool_id, kid))
+
+            if provider_ids is not None:
+                cursor.execute("DELETE FROM tool_provider WHERE tool_id = %s", (tool_id,))
+                for pid in provider_ids:
+                    cursor.execute(
+                        "INSERT INTO tool_provider (tool_id, provider_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                        (tool_id, pid))
+
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def add_knowledge(self, tool_id: str, knowledge_id: str):
+        """向工具添加一条知识关联(不删除已有关联)"""
+        cursor = self._get_cursor()
+        try:
+            cursor.execute(
+                "INSERT INTO tool_knowledge (tool_id, knowledge_id) VALUES (%s, %s) ON CONFLICT DO NOTHING",
+                (tool_id, knowledge_id))
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def delete(self, tool_id: str):
+        """删除工具及其关联表记录"""
+        cursor = self._get_cursor()
+        try:
+            cascade_delete(cursor, 'tool', tool_id)
+            self.conn.commit()
+        finally:
+            cursor.close()
+
+    def count(self, status: Optional[str] = None) -> int:
+        """统计工具总数"""
+        cursor = self._get_cursor()
+        try:
+            if status:
+                cursor.execute("SELECT COUNT(*) as count FROM tool WHERE status = %s", (status,))
+            else:
+                cursor.execute("SELECT COUNT(*) as count FROM tool")
+            return cursor.fetchone()['count']
+        finally:
+            cursor.close()
+
+    def _format_result(self, row: Dict) -> Dict:
+        """格式化查询结果,将 JSON 字符串解析为对象"""
+        if not row:
+            return None
+        result = dict(row)
+        for field in ('input', 'output'):
+            if field in result and isinstance(result[field], str):
+                try:
+                    result[field] = json.loads(result[field]) if result[field].strip() else None
+                except json.JSONDecodeError:
+                    result[field] = None
+        # 关联字段(来自 junction table 子查询)
+        for field in ('capability_ids', 'knowledge_ids', 'provider_ids'):
+            if field in result and isinstance(result[field], str):
+                result[field] = json.loads(result[field])
+            elif field in result and result[field] is None:
+                result[field] = []
+        return result
+
+    def close(self):
+        if self.conn:
+            self.conn.close()

+ 11 - 0
test_chat.py

@@ -0,0 +1,11 @@
+import asyncio
+import httpx
+import uuid
+
+async def test():
+    async with httpx.AsyncClient() as client:
+        resp = await client.post("http://127.0.0.1:8001/chat", json={"message": "列举你的tool", "chat_id": uuid.uuid4().hex})
+        print(resp.status_code)
+        print(resp.text)
+
+asyncio.run(test())

+ 40 - 0
test_tool_provider.py

@@ -0,0 +1,40 @@
+import os
+from dotenv import load_dotenv
+import psycopg2
+
+load_dotenv()
+
+def test_tool_provider():
+    print("Testing connection to tool_provider table...")
+    try:
+        conn = psycopg2.connect(
+            host=os.getenv('KNOWHUB_DB'),
+            port=int(os.getenv('KNOWHUB_PORT', 5432)),
+            user=os.getenv('KNOWHUB_USER'),
+            password=os.getenv('KNOWHUB_PASSWORD'),
+            database=os.getenv('KNOWHUB_DB_NAME')
+        )
+        c = conn.cursor()
+        
+        # Check if tool_provider table exists by selecting 0 rows
+        c.execute("SELECT * FROM tool_provider LIMIT 0")
+        print("Success! Table tool_provider exists.")
+        
+        # Ensure it has the correct columns
+        expected_cols = ['tool_id', 'provider_id']
+        col_names = [desc[0] for desc in c.description]
+        print(f"Columns in tool_provider: {col_names}")
+        
+        # Check if there's any data
+        c.execute("SELECT * FROM tool_provider LIMIT 5")
+        rows = c.fetchall()
+        print(f"Sample data from tool_provider: {rows}")
+        
+        cursor = conn.cursor()
+        c.close()
+        conn.close()
+    except Exception as e:
+        print(f"Failed to access tool_provider table: {e}")
+
+if __name__ == "__main__":
+    test_tool_provider()

+ 2186 - 0
tool_dump.json

@@ -0,0 +1,2186 @@
+{
+  "connected": [
+    {
+      "id": "liblibai_uuid_matching_rules",
+      "name": "LibLib AI UUID 匹配规则指南",
+      "introduction": "读取 Liblib AI 模型 UUID 匹配规则和工作流底模使用大全。在生图参数涉及到 templateUuid,或者在组合 checkpoint 和 ControlNet 且不确定对应的 baseType 和匹配值时,调用此工具来阅读核心指南!以防止你的生图因为参数错误而崩溃。",
+      "capability_ids": []
+    },
+    {
+      "id": "liblibai_model_detail",
+      "name": "LibLib AI 模型详情",
+      "introduction": "通过检索返回的 uuid 和 version_uuid 读取指定大模型的完整上下文。常用来了解它的详细触发词、生成特点或特定参数推荐。",
+      "capability_ids": []
+    },
+    {
+      "id": "nano_banana",
+      "name": "Nano Banana(Gemini 多模态神级图模)",
+      "introduction": "通过 Google Gemini 原生大模型生图。它是系统内唯一原生支持【跨模态/多图融合意象生图】的模型!支持在 images 数组里丢进 N 张完全不同的图片(例如:一张人像、一把宝剑、一栋大楼),并在 prompt 中基于这几张图进行超强逻辑融合。强力推荐用来做抽象图生图、无边界的多图概念重组。",
+      "capability_ids": []
+    },
+    {
+      "id": "midjourney_query_job_status",
+      "name": "Midjourney-查询任务状态",
+      "introduction": "[🔴 暂时不可用/本地重构中] 查询指定任务状态(转发 MIDJOURNEY_API_BASE/query_job_status)。",
+      "capability_ids": []
+    },
+    {
+      "id": "liblibai_model_search",
+      "name": "LibLib AI 模型搜索",
+      "introduction": "通过关键词搜索 LibLib AI 上的最新或特定风格的大模型(比如国风、二次元、写实等)。成功返回会包括一列推荐模型实体,必须从中提取关键的 versionUuid 以获取后续生图能力。",
+      "capability_ids": []
+    },
+    {
+      "id": "image_stitcher",
+      "name": "图片拼接工具",
+      "introduction": "将多张图片按指定方向(水平/垂直/网格)拼接成一张大图。支持间距设置、背景色填充和统一缩放模式。输入支持网络 URL 链接和 Base64,输出直接返回一张拼接成功的干净云端长效外链(URL)。",
+      "capability_ids": []
+    },
+    {
+      "id": "seedream_generate",
+      "name": "SeeDream-4.0图片生成",
+      "introduction": "基于 APIyi 中转站实现的 SeeDream 图片生成与编辑接口。若传入 image_url 将触发高级图生图(图像编辑)。",
+      "capability_ids": []
+    },
+    {
+      "id": "flux_generate",
+      "name": "Flux-多模态生图编辑API",
+      "introduction": "基于 APIyi 中转站实现的高级 Flux 图像生成与编辑(图生图/局部重绘)调用。如果只传 prompt 则触发纯文生图;如果传入 image_url (最好配合 flux-kontext-max 模型) 则触发高级图像编辑。一次性返回生成好的图片URL(临时URL,10分钟有效)。",
+      "capability_ids": []
+    },
+    {
+      "id": "midjourney_get_image_urls",
+      "name": "Midjourney-获取结果图链接",
+      "introduction": "[🔴 暂时不可用/本地重构中] 根据 job_id 获取 4 张图 URL(转发 MIDJOURNEY_API_BASE/get_image_urls)。",
+      "capability_ids": []
+    },
+    {
+      "id": "midjourney_submit_job",
+      "name": "Midjourney-提交生图任务",
+      "introduction": "[🔴 暂时不可用/本地重构中] 提交 Midjourney 生图任务(转发至 MIDJOURNEY_API_BASE/submit_job)。需配置 tools/local/midjourney/.env。mode 为 relaxed 或 fast。",
+      "capability_ids": []
+    },
+    {
+      "id": "ji_meng_query_task",
+      "name": "即梦-查询任务",
+      "introduction": "[🔴 暂时不可用/停机维护] 按 task_id 查询任务状态与结果(本地服务转发上游,POST /query_task)。",
+      "capability_ids": []
+    },
+    {
+      "id": "liblib_image_uploader",
+      "name": "LibLib Image Uploader",
+      "introduction": "专为将本地图片数据转为持久化云端 URL 构建的老实工具。无论你是给它纯 Base64、带有 data:image 头部的 Base64 都会原封不动传给 Liblib 对象存储,并返回生成的临时网络跨端直连图片 URL(http...),完美解决大模型生图过程中因为超大 Base64 字符撑爆思考上下文的异常限制!只接收 Base64 字符串参数,不要传图片路径。",
+      "capability_ids": []
+    },
+    {
+      "id": "liblibai_controlnet",
+      "name": "LibLib AI 综合生成工具",
+      "introduction": "基于 LibLib AI 开放 API 的万能生成引擎。支持【多路并发控制】:允许在一次请求中同时投喂多个 ControlNet(如同时传入 OpenPose + Canny + Depth),AI 会在同一张图上严格遵守所有控制条件!支持模块:canny(边缘), softedge(软边缘), lineart(线稿), openpose(姿态), depth(深度图), inpaint(蒙版重绘), instantid(换脸)。",
+      "capability_ids": []
+    },
+    {
+      "id": "ji_meng_add_task",
+      "name": "即梦-创建任务",
+      "introduction": "[🔴 暂时不可用/停机维护] 提交异步任务到上游(本地服务转发 JI_MENG_API_BASE,POST /add_task)。需配置 tools/local/ji_meng/.env。",
+      "capability_ids": []
+    }
+  ],
+  "unconnected": [
+    {
+      "id": "launch_comfy_env",
+      "name": "Launch ComfyUI Environment",
+      "introduction": "[🔴 暂时外服不可用] 启动 RunComfy 云端机器并等待就绪。返回 server_id 用于后续 ComfyUI workflow 执行。需要环境变量 RUNCOMFY_USER_ID 和 API_TOKEN。",
+      "capability_ids": []
+    },
+    {
+      "id": "runcomfy_check_status",
+      "name": "RunComfy Check Status",
+      "introduction": "[🔴 暂时外服不可用] 获取当前用户的 RunComfy 机器列表和状态。可以查看是否已有机房资源,防止重复拉起新机器。",
+      "capability_ids": []
+    },
+    {
+      "id": "runcomfy_stop_env",
+      "name": "RunComfy Stop Service",
+      "introduction": "[🔴 暂时外服不可用] Stop and delete RunComfy server instances to release resources. Works with launch_comfy_env for complete lifecycle management.",
+      "capability_ids": []
+    },
+    {
+      "id": "runcomfy_workflow_executor",
+      "name": "RunComfy Workflow Executor",
+      "introduction": "[🔴 暂时外服不可用] 在已就绪的 RunComfy 机器上提交 ComfyUI 工作流,上传输入文件,监听执行状态,下载结果图片(不启动/关闭机器)",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/efficiency_nodes",
+      "name": "Efficiency Nodes",
+      "introduction": "提供高效图像处理和覆盖功能的节点包",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/texture_viewer",
+      "name": "TextureViewer",
+      "introduction": "基于 threejs 的 3D 贴图预览节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ipadapter_plus",
+      "name": "IPAdapter_plus",
+      "introduction": "基于 IP-Adapter 的图像提示融合节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/albedobase_xl_v21",
+      "name": "albedobaseXL_v21",
+      "introduction": "基于 SDXL 架构的基础检查点模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/hyper_sdxl_8steps_lora",
+      "name": "Hyper-SDXL-8steps-lora",
+      "introduction": "加速 SDXL 生成的 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/cr_seamless_checker",
+      "name": "CR Seamless Checker",
+      "introduction": "检查纹理是否无缝的节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/fococus_inpaint_node",
+      "name": "Fococus 局部重绘节点",
+      "introduction": "基于 Fooocus 技术的局部重绘节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/face_detailer",
+      "name": "Face Detailer",
+      "introduction": "人脸修复专用 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/inpaint_anything",
+      "name": "Inpaint Anything",
+      "introduction": "基于 SAM 的局部重绘扩展",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/adetailer",
+      "name": "ADetailer",
+      "introduction": "自动检测并重绘图像细节的插件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/insightface",
+      "name": "InsightFace",
+      "introduction": "开源人脸分析库",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/realesrgan",
+      "name": "RealESRGAN",
+      "introduction": "基于 GAN 的图像超分辨率工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/lora_plugin",
+      "name": "LoRA 插件",
+      "introduction": "用于在 WebUI 中加载和管理 LoRA 模型的插件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/hand_detail_flux_xl",
+      "name": "Hand Detail FLUX & XL",
+      "introduction": "用于增强手部细节的 LoRA 微调模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_1_d",
+      "name": "Flux.1 D",
+      "introduction": "Flux 系列基础检查点模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/run_comfy",
+      "name": "RunComfy",
+      "introduction": "基于 ComfyUI 的云端图像生成运行工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/custom_360_scene_layout_lora",
+      "name": "Custom 360 Scene Layout LoRA",
+      "introduction": "用于 360 度场景布局生成的 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/next_scene_lora",
+      "name": "Next Scene LoRA",
+      "introduction": "用于场景转换或分镜控制的 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/qwen_image_edit",
+      "name": "Qwen Image Edit",
+      "introduction": "基于 Qwen 的图像编辑与特征提取工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/topaz_gigapixel",
+      "name": "Topaz Gigapixel",
+      "introduction": "图像放大与分辨率增强工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_kontext_max",
+      "name": "flux-kontext-max",
+      "introduction": "高级图像编辑模型,擅长材质与光影处理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/openart_character_builder",
+      "name": "OpenArt AI Character Builder",
+      "introduction": "通过 UI 选择锁定角色特征的基础图像生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/seedance_2_0",
+      "name": "Seedance 2.0",
+      "introduction": "用于生成角色参考表及场景图像的 AI 模型/平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_generate",
+      "name": "flux_generate",
+      "introduction": "基于 Flux 模型的图像生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/image_downloader",
+      "name": "image_downloader",
+      "introduction": "生成结果下载工具,将 CDN URL 图片保存至本地",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/seedream_generate",
+      "name": "seedream_generate",
+      "introduction": "基于 SeaDream 模型的图像生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/image_uploader",
+      "name": "image_uploader",
+      "introduction": "用于将本地图片上传到 OSS 获取 CDN URL 的辅助工具。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/inpaint",
+      "name": "Inpaint",
+      "introduction": "图像局部修复工具,用于修正生成结果中的畸形区域",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/openpose",
+      "name": "OpenPose",
+      "introduction": "姿态估计模型/工具,在小红书搜索中被识别为高价值工具关键词",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/controlnet",
+      "name": "ControlNet",
+      "introduction": "Stable Diffusion 插件,用于通过边缘、姿势等条件控制生成图像",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/opencv",
+      "name": "OpenCV",
+      "introduction": "开源计算机视觉库,提供多种图像处理算法与函数。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng",
+      "name": "即梦",
+      "introduction": "字节旗下 AI 工具,视频生成强且国内访问方便",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/keling",
+      "name": "可灵",
+      "introduction": "通用 AI 生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nano_banana",
+      "name": "Nano Banana",
+      "introduction": "Google 多模态和视频能力强的模型。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ip_adapter",
+      "name": "IP-Adapter",
+      "introduction": "专门用于图片风格迁移",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/stable_diffusion_webui",
+      "name": "Stable Diffusion WebUI",
+      "introduction": "基于 Web 的 Stable Diffusion 模型操作界面",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/run_comfy",
+      "name": "RunComfy",
+      "introduction": "云端 ComfyUI 服务,支持多 ControlNet 叠加",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_submit",
+      "name": "flux_submit",
+      "introduction": "提交 Flux 模型生成任务的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/liblib",
+      "name": "LibLib",
+      "introduction": "在线模型平台,ControlNet 功能仅支持单 Canny",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/midjourney",
+      "name": "Midjourney",
+      "introduction": "AI 图像生成工具,通过提示词控制生成图像的色调风格",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/comfyui",
+      "name": "ComfyUI",
+      "introduction": "基于节点流程的 Stable Diffusion 图形用户界面,支持自定义工作流",
+      "capability_ids": [
+        "CAP-004",
+        "CAP-005",
+        "CAP-009",
+        "CAP-003",
+        "CAP-007",
+        "CAP-010",
+        "CAP-008",
+        "CAP-011",
+        "CAP-012",
+        "CAP-006",
+        "CAP-001",
+        "CAP-002",
+        "CAP-019"
+      ]
+    },
+    {
+      "id": "tools/model/flux",
+      "name": "Flux",
+      "introduction": "高质量生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/toolhub",
+      "name": "ToolHub",
+      "introduction": "工具管理服务,用于检查工具链状态和可用性",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/comfyui",
+      "name": "ComfyUI",
+      "introduction": "开源 AI 视频工具的主要部署平台,工作流丰富且社区活跃",
+      "capability_ids": [
+        "CAP-012",
+        "CAP-007",
+        "CAP-008",
+        "CAP-011",
+        "CAP-009",
+        "CAP-005",
+        "CAP-006",
+        "CAP-003",
+        "CAP-004",
+        "CAP-001",
+        "CAP-019",
+        "CAP-010",
+        "CAP-002"
+      ]
+    },
+    {
+      "id": "tools/image_gen/flux_query",
+      "name": "flux_query",
+      "introduction": "查询 Flux 任务状态及结果的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/launch_comfy_env",
+      "name": "launch_comfy_env",
+      "introduction": "启动 ComfyUI 运行环境的辅助工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/liblibai_controlnet",
+      "name": "liblibai_controlnet",
+      "introduction": "LiblibAI 提供的 ControlNet 控制工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/runcomfy_workflow_executor",
+      "name": "runcomfy_workflow_executor",
+      "introduction": "执行 ComfyUI 工作流的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux",
+      "name": "FLUX",
+      "introduction": "开源 AI 图像生成模型,细节控制能力强。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/liblibai_controlnet",
+      "name": "liblibai_controlnet",
+      "introduction": "LibLib AI 提供的 ControlNet 工具(仅支持单 Canny)",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/gemini",
+      "name": "Gemini",
+      "introduction": "多模态大模型(此处用于图像生成任务)",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/liblib_ai",
+      "name": "LibLib AI",
+      "introduction": "在线 AI 图像生成平台,支持 ControlNet",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/lora",
+      "name": "LoRA",
+      "introduction": "低秩适应模型,用于定制化风格或角色",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/dwpose",
+      "name": "DWPose",
+      "introduction": "高精度姿态估计模型,用于生成骨骼控制图",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/photoshop",
+      "name": "PS",
+      "introduction": "图像处理软件,用于后期合成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/style_adapter",
+      "name": "Style Adapter",
+      "introduction": "用于适配和迁移图像风格的插件或模型组件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/kj_nodes",
+      "name": "KJ Nodes",
+      "introduction": "ComfyUI 自定义节点包,简化工作流连线",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/sam_3",
+      "name": "SAM 3",
+      "introduction": "用于智能选择区域的图像分割模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/wan_2_2",
+      "name": "Wan 2.2",
+      "introduction": "视频生成模型,配合 TTM 和 SAM 3 实现无训练精确动画",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/time_to_move",
+      "name": "Time-to-Move",
+      "introduction": "训练-free 精确控制技术,2026 年最新技术突破",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/nano_banana",
+      "name": "Nano Banana",
+      "introduction": "AI 图像生成模型,提供 2 和 Pro 版本,针对不同场景进行优化",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/kling",
+      "name": "可灵",
+      "introduction": "视频表情演绎工具,专注于动态表情生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/after_effects",
+      "name": "After Effects",
+      "introduction": "视觉特效和动态图形软件,用于后期处理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/seendance",
+      "name": "Seendance 2.0",
+      "introduction": "视频生成模型备选",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/xingliu_platform",
+      "name": "星流平台",
+      "introduction": "无需梯子的快速 AI 生成平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/lovart",
+      "name": "Lovart",
+      "introduction": "一句话生成,平铺图直接转模特图",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/sdxl",
+      "name": "SDXL",
+      "introduction": "Stable Diffusion XL 大模型,用于高质量图像生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/instantid",
+      "name": "InstantID",
+      "introduction": "角色一致性保持插件,常配合 ComfyUI 使用",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/wd14_tagger",
+      "name": "WD14 Tagger",
+      "introduction": "图像提示词反推工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/wd14",
+      "name": "WD14",
+      "introduction": "用于反推提示词的标签模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gemini",
+      "name": "Gemini",
+      "introduction": "Google 推出的多模态 AI 模型,支持图像生成能力",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/uso",
+      "name": "USO",
+      "introduction": "ComfyUI 生态中的开源工具链",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/z_image_turbo",
+      "name": "Z-Image Turbo",
+      "introduction": "快速推理模型,支持 8 步快速推理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_edit_2509",
+      "name": "Qwen Edit 2509",
+      "introduction": "ComfyUI 生态中的开源工具链",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/z_image_controlnet",
+      "name": "Z-Image ControlNet",
+      "introduction": "Alibaba PAI 推出的 ControlNet  variant,支持 3.1GB 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_vl",
+      "name": "QwenVL",
+      "introduction": "可结合 ControlNet 自动生成提示词",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/time_to_move",
+      "name": "Time-to-Move",
+      "introduction": "实现无训练的视频精确控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/comfyui_uso",
+      "name": "ComfyUI USO",
+      "introduction": "统一风格生成,支持单/双风格混合",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ipadapter_advanced",
+      "name": "IPAdapter Advanced",
+      "introduction": "风格 + 构图混合节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/midjourney_sref",
+      "name": "Midjourney SREF",
+      "introduction": "风格参考系统,支持--sref 风格参考和--sw 风格权重",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/wd14",
+      "name": "WD14",
+      "introduction": "用于图像反推提示词的标签模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/scribble_diffusion",
+      "name": "Scribble Diffusion",
+      "introduction": "基于快速草图和文本提示生成图像的在线工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/stable_diffusion",
+      "name": "Stable Diffusion",
+      "introduction": "AI 绘画生态基础模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/blender",
+      "name": "Blender",
+      "introduction": "开源 3D 创作套件,用于搭建场景和渲染辅助通道",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/stable_diffusion",
+      "name": "Stable Diffusion",
+      "introduction": "开源文生图模型,支持本地部署和多种插件扩展",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/adobe_firefly",
+      "name": "Adobe Firefly",
+      "introduction": "Adobe 推出的创意生成式 AI 模型系列",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nano_banana_pro",
+      "name": "Nano Banana Pro",
+      "introduction": "谷歌 Gemini 3 Pro Image 模型,用于 AI 图像生成",
+      "capability_ids": [
+        "CAP-013",
+        "CAP-008",
+        "CAP-003",
+        "CAP-014",
+        "CAP-019",
+        "CAP-001",
+        "CAP-020",
+        "CAP-011",
+        "CAP-021",
+        "CAP-016"
+      ]
+    },
+    {
+      "id": "tools/image_gen/dall_e_3",
+      "name": "DALL·E 3",
+      "introduction": "OpenAI 推出的文生图模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/doubao",
+      "name": "豆包",
+      "introduction": "字节跳动推出的多功能 AI 助手,支持图像生成与处理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/3dai_studio",
+      "name": "3DAIStudio",
+      "introduction": "AI 驱动的 2D 图像转 3D 模型工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/windowtop",
+      "name": "WindowTop",
+      "introduction": "PIP 窗口管理工具,支持点击穿透",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/runway_gen_2",
+      "name": "Runway Gen 2",
+      "introduction": "动态效果生成 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/krea_ai",
+      "name": "Krea AI",
+      "introduction": "实时 AI 变换引擎,支持屏幕捕捉实时风格化",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/inpaint_model_conditioning",
+      "name": "Inpaint Model Conditioning",
+      "introduction": "ComfyUI 原生节点,用于处理局部重绘的条件控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/gemini",
+      "name": "Gemini",
+      "introduction": "用于生成分镜提示词的 AI 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/magnific_ai",
+      "name": "Magnific AI",
+      "introduction": "AI 驱动的图像超分辨率、风格转换及细节增强平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/adobe_photoshop",
+      "name": "Adobe Photoshop",
+      "introduction": "专业图像编辑与后期处理软件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ai_image_generation",
+      "name": "AI 生图平台",
+      "introduction": "接收草图和提示词生成最终图像的 AI 模型或平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/text_ai",
+      "name": "文本 AI",
+      "introduction": "用于反推和优化 AI 生图提示词的语言模型工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gemini_3_1_image_preview",
+      "name": "gemini-3.1-image-preview",
+      "introduction": "Gemini 图像生成能力模型,品牌别名为 nanobanana",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/higgsfield_soul_2",
+      "name": "Higgsfield Soul 2",
+      "introduction": "AI 图片生成工具,专注于建筑/户外场景生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/higgsfield_ai",
+      "name": "higgsfield.ai",
+      "introduction": "AI 模型评测与生成平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ideogram",
+      "name": "Ideogram",
+      "introduction": "AI 图像生成平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/enhancor_ai",
+      "name": "Enhancor AI",
+      "introduction": "AI 图像生成与增强工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/grok_imagine",
+      "name": "Grok Imagine",
+      "introduction": "纯图片生成工具,核心能力为图片生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/imagineart",
+      "name": "ImagineArt",
+      "introduction": "提供免费开源工具使用的 AI 艺术平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/higgsfield_soul",
+      "name": "Higgsfield Soul",
+      "introduction": "同时支持图片和视频生成的 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ali_tongyi_qianwen",
+      "name": "阿里通义千问",
+      "introduction": "阿里旗下的 AI 模型,文中提及人物一致性优秀",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/photoshop_ai",
+      "name": "Photoshop AI",
+      "introduction": "后期微调",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/kling_ai",
+      "name": "可灵 AI",
+      "introduction": "支持 API 调用与批量生成的 AI 视频与图像模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/nano_banana",
+      "name": "Nano Banana",
+      "introduction": "专业级场景扩展工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/video_editing_software",
+      "name": "视频编辑软件",
+      "introduction": "用于合成视频的软件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nb",
+      "name": "NB",
+      "introduction": "角色一致性生成工具,支持三视图、线稿和色稿生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/kling",
+      "name": "Kling",
+      "introduction": "AI 视频生成模型,常用于图像转视频工作流",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/magnific",
+      "name": "Magnific",
+      "introduction": "AI 驱动的图片高清放大与细节增强工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/nano_banana",
+      "name": "Nano Banana",
+      "introduction": "工作流平台类工具(配合 ComfyUI),提供完整设计流程",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/nano_banana_pro",
+      "name": "Nano Banana Pro",
+      "introduction": "Google 新模型,人物特征保持优秀",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/agent_lovart",
+      "name": "Agent Lovart",
+      "introduction": "AI 绘图 Agent 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/nanobanana2",
+      "name": "Nanobanana2",
+      "introduction": "配合 Lovart 使用的模型或工作流",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/doubao",
+      "name": "豆包",
+      "introduction": "AI 助手,用于工作流预处理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/wenxiaobai",
+      "name": "问小白",
+      "introduction": "AI 提示词生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/qianwen_ai",
+      "name": "千问 AI",
+      "introduction": "AI 穿搭生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/tencent_yuanbao",
+      "name": "腾讯元宝",
+      "introduction": "高赞帖子中提到的 AI 工具,包含具体工具名称和核心功能。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/nanobanana2",
+      "name": "Nanobanana2",
+      "introduction": "在帖子正文和标签中都有提及的工具名称。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/quark_ai",
+      "name": "夸克 AI",
+      "introduction": "高赞帖子中提到的 AI 工具,包含具体工具名称和核心功能。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/meta_ai",
+      "name": "Meta AI",
+      "introduction": "纯图片生成工具,核心能力为图片生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/canva_magic_studio",
+      "name": "Canva Magic Studio",
+      "introduction": "零门槛设计平台,提供海量模板及 AI 辅助设计功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/qwen_ai",
+      "name": "千问 AI",
+      "introduction": "AI 生图工具,支持多种图像编辑和生成能力",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_image_2511",
+      "name": "Qwen-Image-2511",
+      "introduction": "千问 AI 旗下的图像生成模型满血版",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/ai_real_scene_synthesis_workflow",
+      "name": "AI 实景合成工作流",
+      "introduction": "用于实景与 AI 元素合成的工作流程",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/zynzo_ai",
+      "name": "Zynzo AI",
+      "introduction": "专注杂志生成的新兴 AI 排版设计工具。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/wity_ai",
+      "name": "Wity.ai",
+      "introduction": "专注杂志生成的新兴 AI 排版设计工具。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/gamma",
+      "name": "Gamma",
+      "introduction": "新兴 AI 工具,用于文档与演示文稿的生成式设计。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/esrgan",
+      "name": "ESRGAN",
+      "introduction": "超分辨率生成对抗网络,用于图像高清化处理",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/deepseek",
+      "name": "DeepSeek",
+      "introduction": "AI 模型,用于生成提示词",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng_ai",
+      "name": "即梦 AI",
+      "introduction": "字节跳动推出的 AI 创作平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/google_earth",
+      "name": "Google Earth",
+      "introduction": "地理信息工具,用于模拟真实地理场景输入",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/xiang_cun_jian_zhu_lora",
+      "name": "乡村建筑 LoRA",
+      "introduction": "建筑场景 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/lao_wang_1_5_v0_5",
+      "name": "老王 1.5 V0.5",
+      "introduction": "人物风格 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/esrgan",
+      "name": "ESRGAN",
+      "introduction": "超分辨率图像放大算法",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/kontext",
+      "name": "Kontext",
+      "introduction": "与 Midjourney 配合实现一致性控制的工作流组件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_image_2509_multiple_angles_lora",
+      "name": "Qwen-Image-2509-Multiple-Angles LORA",
+      "introduction": "支持 10+ 角度组合的 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/marble_world_model",
+      "name": "Marble 世界模型",
+      "introduction": "用于处理 3D 空间关系的世界模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nami_ai",
+      "name": "纳米 AI",
+      "introduction": "支持创建角色手办及写实风格图片的 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/t2i_adapter",
+      "name": "T2I Adapter",
+      "introduction": "ControlNet 及其预处理器之一",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/depth_anything_v3",
+      "name": "Depth Anything V3",
+      "introduction": "用于生成深度图的自定义节点模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/controlnet_union",
+      "name": "ControlNet Union",
+      "introduction": "整合多种控制条件于单一模型,支持 3D 空间感知",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/depth_anything_v3",
+      "name": "Depth Anything V3",
+      "introduction": "深度估计模型,用于 3D 空间感知",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_advanced_controlnet",
+      "name": "ComfyUI-Advanced-ControlNet",
+      "introduction": "ComfyUI 中实现 ControlNet 核心功能的自定义节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_controlnet_auxiliary_preprocessors",
+      "name": "ComfyUI's ControlNet Auxiliary Preprocessors",
+      "introduction": "为 ComfyUI ControlNet 提供图像预处理功能的节点包",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/midjourney_style_tuner",
+      "name": "Midjourney Style Tuner",
+      "introduction": "Midjourney 提供的风格调优功能,用于定制和保持图像生成风格",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/comfy_ui",
+      "name": "Comfy UI",
+      "introduction": "支持节点式工作流搭建",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/liveportrait",
+      "name": "LivePortrait",
+      "introduction": "支持通过滑块调整面部参数的表情控制模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/hailuo_ai",
+      "name": "海螺 AI",
+      "introduction": "AI 生成/控制表情的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/wan_animate",
+      "name": "Wan Animate",
+      "introduction": "开源动画模型,支持 ComfyUI 本地部署",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/wan_animate",
+      "name": "Wan Animate",
+      "introduction": "微表情控制精度最高的 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/avatar_x",
+      "name": "Avatar X",
+      "introduction": "配合 Kling AI 用于情感表达的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/heygen_avatar",
+      "name": "HeyGen Avatar 4",
+      "introduction": "商业级说话头像生成工具,口型同步业界最佳",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/heygen",
+      "name": "HeyGen",
+      "introduction": "适合企业培训的数字人视频生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/heygen",
+      "name": "HeyGen",
+      "introduction": "数字人/口型同步工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/kling_ai",
+      "name": "Kling AI",
+      "introduction": "专业 AI 视频生成模型,支持运动控制与唇形同步",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_edit",
+      "name": "Qwen-Edit",
+      "introduction": "细粒度表情编辑带连续强度控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/pixel_smile",
+      "name": "PixelSmile",
+      "introduction": "专注表情编辑研究,对比学习解耦表情语义,支持连续强度控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/wan_2_2_animate",
+      "name": "Wan 2.2 Animate",
+      "introduction": "AI 视频生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/kling_ai",
+      "name": "Kling AI",
+      "introduction": "可灵 AI 生成模型,支持文本或图像生成视频内容",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/keling_ai",
+      "name": "可灵 AI",
+      "introduction": "快手推出的生成式 AI 模型,支持视频与图像生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/nano_banana_pro",
+      "name": "Nano Banana Pro",
+      "introduction": "AI 图像处理或编辑工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_advanced_live_portrait",
+      "name": "ComfyUI-AdvancedLivePortrait",
+      "introduction": "ComfyUI 自定义节点,提供 14 个可调参数用于控制肖像表情和姿态",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/live_portrait",
+      "name": "LivePortrait",
+      "introduction": "ComfyUI 插件,面部表情编辑工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/wan_2_5",
+      "name": "Wan 2.5",
+      "introduction": "开源工具,在 ImagineArt 上免费提供",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/higgsfield",
+      "name": "Higgsfield",
+      "introduction": "使用参考图像实现跨代角色一致性",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/minimax",
+      "name": "MiniMax",
+      "introduction": "使用参考图像实现跨代角色一致性",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/seedream_4",
+      "name": "Seedream 4",
+      "introduction": "开源工具,在 ImagineArt 上免费提供",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/pixelsmile",
+      "name": "PixelSmile",
+      "introduction": "细粒度表情编辑带连续强度控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/kling_3_0",
+      "name": "Kling 3.0",
+      "introduction": "统一模型整合 text-to-video、image-to-video、motion control",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/advanced_live_portrait",
+      "name": "AdvancedLivePortrait",
+      "introduction": "ComfyUI 插件,面部表情编辑工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/advanced_live_portrait",
+      "name": "Advanced Live Portrait",
+      "introduction": "图片表情控制工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/face_expression_node",
+      "name": "Face Expression Node",
+      "introduction": "ComfyUI 用于控制图片表情的自定义节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gemini_image_preview",
+      "name": "gemini-image-preview",
+      "introduction": "Google 推出的新兴 AI 图像生成模型预览版",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_face_expression_node",
+      "name": "ComfyUI Face Expression Node",
+      "introduction": "改变图片中的面部表情",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/nano_banana_pro",
+      "name": "Nano Banana Pro",
+      "introduction": "角色一致性辅助工具,提供 JSON 提示词精确控制",
+      "capability_ids": [
+        "CAP-014",
+        "CAP-021",
+        "CAP-011",
+        "CAP-020",
+        "CAP-013",
+        "CAP-008",
+        "CAP-001",
+        "CAP-019",
+        "CAP-016",
+        "CAP-003"
+      ]
+    },
+    {
+      "id": "tools/image_gen/pixelsmile",
+      "name": "PixelSmile",
+      "introduction": "支持细粒度表情编辑的扩散框架",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/uni_1",
+      "name": "Uni-1",
+      "introduction": "多角度生成类工具,支持多角度一致性生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/openpose",
+      "name": "OpenPose",
+      "introduction": "人体姿势估计工具,常作为 ControlNet 的预处理器使用",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/modelscope",
+      "name": "ModelScope",
+      "introduction": "模型服务平台,提供多种 AI 模型的访问与部署",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/lmarena",
+      "name": "LMArena",
+      "introduction": "AI 模型基准测试与排行榜平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_1_kontext",
+      "name": "Flux.1 Kontext",
+      "introduction": "AI 图像编辑模型,在 LMArena 评测中表现优异",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nano_banana_2",
+      "name": "Nano Banana 2",
+      "introduction": "风格化、多人物场景表现好,性价比高",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/nanobanana",
+      "name": "NanoBanana",
+      "introduction": "搜索中发现的 AI 微表情控制相关工具或模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/wan_animate",
+      "name": "Wan Animate",
+      "introduction": "ComfyUI 中的高级工作流资源,包含多种视频处理功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/liveportrait",
+      "name": "LivePortrait",
+      "introduction": "肖像表情驱动与控制工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_liveportrait",
+      "name": "ComfyUI LivePortrait",
+      "introduction": "AI 生成/控制表情的工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ji_meng",
+      "name": "即梦",
+      "introduction": "AI 生成式换装代表工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/chuang_ke_tie",
+      "name": "创客贴",
+      "introduction": "在线平面设计工具,集成 AI 生成与设计功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/mei_tu_she_ji_shi",
+      "name": "美图设计室",
+      "introduction": "美图推出的在线 AI 设计工具,支持海报、logo 等设计",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng_5.0",
+      "name": "即梦 5.0 模型",
+      "introduction": "适合文化主题可视化的 AI 生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nanobanana",
+      "name": "Nanobanana",
+      "introduction": "用于制作学科知识卡片的 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/botika_ai",
+      "name": "Botika AI",
+      "introduction": "AI 生成时尚模特展示图工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/photoroom",
+      "name": "Photoroom",
+      "introduction": "AI 背景移除与图像编辑工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/sellerpic",
+      "name": "SellerPic",
+      "introduction": "AI 电商图片生成工具,用于优化产品展示图",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/wearwow",
+      "name": "WearWow",
+      "introduction": "个人日常穿搭 AI 工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/outfit_anyone",
+      "name": "Outfit Anyone",
+      "introduction": "开源虚拟试衣模型,支持多种体型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/shein_ai",
+      "name": "希音 AI",
+      "introduction": "希音推出的 AI 穿搭工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/taofenba",
+      "name": "淘粉吧",
+      "introduction": "支持详细身材参数的穿搭工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/kolors",
+      "name": "Kolors",
+      "introduction": "开源 AI 图像生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/hf_vton",
+      "name": "HF-VTON",
+      "introduction": "学术论文虚拟试衣框架",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/outfit_anyone",
+      "name": "Outfit Anyone",
+      "introduction": "阿里推出的超写实虚拟试衣模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_2_max",
+      "name": "FLUX.2 max",
+      "introduction": "Black Forest Labs 推出的 AI 图像生成模型,提供官方 API 用于生成图像。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gpt_image_1_5",
+      "name": "GPT Image 1.5",
+      "introduction": "AI 图像生成模型,排行榜第一,擅长统一生成与编辑工作流",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_2_max",
+      "name": "FLUX.2 [max]",
+      "introduction": "Black Forest Labs 开发的旗舰级图像生成与编辑模型,支持实时网络搜索与高精度提示词遵循",
+      "capability_ids": [
+        "CAP-001",
+        "CAP-014",
+        "CAP-011",
+        "CAP-012",
+        "CAP-016",
+        "CAP-020",
+        "CAP-013",
+        "CAP-003",
+        "CAP-015"
+      ]
+    },
+    {
+      "id": "tools/image_gen/gpt_image",
+      "name": "GPT Image",
+      "introduction": "支持统一生成和编辑工作流的 AI 图像工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_2",
+      "name": "FLUX.2",
+      "introduction": "提供多种变体(max/pro/flex/klein/dev)的 AI 图像生成模型家族",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/fal_ai",
+      "name": "fal.ai",
+      "introduction": "服务器less AI 模型托管平台,支持多种生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/comfyui_qwen_outfit_transfer",
+      "name": "ComfyUI Qwen Outfit Transfer",
+      "introduction": "基于 ComfyUI 的虚拟试衣方案,细节最佳免费",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/uno_comf",
+      "name": "UNO-Comf",
+      "introduction": "游戏/虚拟角色换装工作流",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/magic_clothing",
+      "name": "Magic Clothing",
+      "introduction": "虚拟试衣模型,人脸一致性好",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/qwen_outfit_transfer",
+      "name": "Qwen Outfit Transfer",
+      "introduction": "基于 Qwen 的服装迁移模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/sam",
+      "name": "SAM",
+      "introduction": "Segment Anything Model,用于图像分割的通用模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ipadapter_faceid",
+      "name": "IPAdapter FaceID",
+      "introduction": "基于 IP-Adapter 的人脸身份保持插件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/detailer",
+      "name": "Detailer",
+      "introduction": "细节增强节点,常用于面部或手部修复",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/pulid",
+      "name": "PuLID",
+      "introduction": "人脸 ID 保持工具,替代或配合 IPAdapter",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/qwen_outfit_transfer",
+      "name": "Qwen Outfit Transfer",
+      "introduction": "基于 Qwen 的服装迁移两阶段方案",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/magic_clothing",
+      "name": "Magic Clothing",
+      "introduction": "专用虚拟试穿自定义节点,优化服装穿戴效果",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/redux",
+      "name": "Redux",
+      "introduction": "参考图像特征提取与融合模块,用于保持服装一致性",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/gemini_3_pro",
+      "name": "Gemini 3 Pro",
+      "introduction": "用于生成专业提示词的大语言模型,辅助图像生成工作流",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/hunyuan_3d",
+      "name": "Hunyuan 3D",
+      "introduction": "3D 结构生成工具,支持从图片生成 3D 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_doppl",
+      "name": "Google Doppl",
+      "introduction": "360 度试穿视频,个人数字形象创建",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/weavy_ai",
+      "name": "Weavy AI",
+      "introduction": "工作流平台类工具,提供完整设计流程",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/mn_vton",
+      "name": "MN-VTON",
+      "introduction": "虚拟试穿类工具,核心功能是服装更换",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ootdiffusion",
+      "name": "OOTDiffusion",
+      "introduction": "虚拟试穿类开源工具,核心功能是服装更换,社区活跃",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_fashion_model",
+      "name": "Google Fashion Model",
+      "introduction": "大厂产品,技术实力强",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/workflow/scenario_gg",
+      "name": "Scenario.gg",
+      "introduction": "工作流平台类工具,提供完整设计流程",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/higgsfield_ai_fashion_factory",
+      "name": "Higgsfield AI Fashion Factory",
+      "introduction": "多角度生成类工具,支持 12 角度或前后侧面一致性生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_deepmind_project_genie",
+      "name": "Google DeepMind Project Genie",
+      "introduction": "大厂产品,技术实力强",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/fashion_vdm",
+      "name": "Fashion-VDM",
+      "introduction": "虚拟试穿类工具,核心功能是服装更换",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/perplexity_virtual_try_on",
+      "name": "Perplexity 虚拟试衣",
+      "introduction": "虚拟试衣工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng_4_5",
+      "name": "及梦 4.5",
+      "introduction": "一句话生成,平铺图直接转模特图",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_try_it_on",
+      "name": "谷歌 Try It On",
+      "introduction": "谷歌推出的虚拟试衣产品",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/douyin_ai_try_on",
+      "name": "抖音 AI 试衣",
+      "introduction": "虚拟试衣工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/jianying",
+      "name": "剪映",
+      "introduction": "视频编辑工具,配合 AI 生成内容",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/huiwa",
+      "name": "绘蛙",
+      "introduction": "AI 电商营销素材生成工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/fashn_vton",
+      "name": "FASHN VTON",
+      "introduction": "开源可本地部署,无需抠图直接生成试穿效果",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ali_lookie",
+      "name": "阿里 Lookie",
+      "introduction": "与淘宝商品打通,提供穿搭评分",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/ke_ling",
+      "name": "可灵",
+      "introduction": "AI 生成式换装代表工具,一键生成效果自然",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/hui_wa",
+      "name": "绘蛙",
+      "introduction": "AI 生成式换装代表工具,操作简单",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/wendy",
+      "name": "AI 智能搭配系统(Wendy)",
+      "introduction": "个人衣橱管理及智能搭配系统",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/huiwa_ai",
+      "name": "绘蛙 AI",
+      "introduction": "电商 AI 作图工具,优化商品展示",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/tiled_diffusion",
+      "name": "Tiled Diffusion",
+      "introduction": "SD 扩展,用于高清放大和显存优化",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/tryon_lora",
+      "name": "TryOn-LoRA",
+      "introduction": "阿里开源的试衣专用 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/realisticvision_v6.0",
+      "name": "RealisticVision V6.0",
+      "introduction": "基础大模型,人像细节表现强",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/juggernaut_xl",
+      "name": "Juggernaut XL",
+      "introduction": "支持 SDXL 的基础大模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/sam",
+      "name": "SAM",
+      "introduction": "Segment Anything Model,用于精准图像分割",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/gfpgan",
+      "name": "GFPGAN",
+      "introduction": "人脸修复模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/fashion_lora",
+      "name": "Fashion-LoRA",
+      "introduction": "服装专用 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/kling_ai_try_on",
+      "name": "可灵 AI 试衣",
+      "introduction": "可灵 AI 推出的虚拟试衣工具,支持将服装图合成到模特图生成静态或动态效果",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/keling_ai_try_on",
+      "name": "可灵 AI 试衣",
+      "introduction": "快手推出的一键换装工具,支持图转视频",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/kontext_lora",
+      "name": "Kontext LoRA",
+      "introduction": "配合 ComfyUI 使用的 LoRA 模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/family_pro",
+      "name": "FamilyPro",
+      "introduction": "垂直领域的 AI 穿搭工具,适合特定场景",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/yiketu",
+      "name": "易可图",
+      "introduction": "垂直领域的 AI 穿搭工具,功能更垂直",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/alibaba_lookie",
+      "name": "阿里 Lookie",
+      "introduction": "阿里推出的虚拟试衣产品,打通淘宝商品链接",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/ootdiffusion",
+      "name": "OOTDiffusion",
+      "introduction": "开源虚拟试衣模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/idm_vton",
+      "name": "IDM-VTON",
+      "introduction": "开源虚拟试衣模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/seedream_5_0_lite",
+      "name": "Seedream 5.0 Lite",
+      "introduction": "字节跳动推出的统一多模态图像生成模型,具备深度思考与在线搜索能力",
+      "capability_ids": [
+        "CAP-003",
+        "CAP-001",
+        "CAP-012",
+        "CAP-013",
+        "CAP-011"
+      ]
+    },
+    {
+      "id": "tools/image_gen/dreamina",
+      "name": "Dreamina",
+      "introduction": "第三方 AI 工具集成平台,提供多种模型访问",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/seedream",
+      "name": "Seedream",
+      "introduction": "支持视频生成,模特转身展示服装细节",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/midjourney_v7",
+      "name": "Midjourney V7",
+      "introduction": "艺术风格成熟度与社区生态领先的 AI 生图平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/hunyuan_image",
+      "name": "Hunyuan Image",
+      "introduction": "腾讯混元图像生成模型,参与对比测试",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/qwen_image",
+      "name": "Qwen-Image",
+      "introduction": "通义千问系列图像生成模型,参与对比测试",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/seedream_5_0_lite",
+      "name": "Seedream 5.0 Lite",
+      "introduction": "AI 图像生成模型,通常在多个第三方平台上线",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/fashn_vton",
+      "name": "FASHN VTON",
+      "introduction": "AI 虚拟试衣工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/advanced_refux_control",
+      "name": "AdvancedRefuxControl",
+      "introduction": "用于服装迁移参考控制的节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/florence2",
+      "name": "Florence2",
+      "introduction": "用于图像描述生成的视觉基础模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_essentials",
+      "name": "comfyui-essentials",
+      "introduction": "ComfyUI 基础功能扩展节点包",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_kjnodes",
+      "name": "comfyui-kjnodes",
+      "introduction": "ComfyUI 自定义节点包,提供图像拼接等功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/redux",
+      "name": "Redux",
+      "introduction": "控制一致性插件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/banana_pro",
+      "name": "banana Pro",
+      "introduction": "SaaS 工具,拖拽操作,所见即所得,一致性最佳",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/linkfoxai",
+      "name": "LinkFoxAI",
+      "introduction": "AI 生图工具,适合小商家快速上手,成本可控。",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/supir",
+      "name": "Supir",
+      "introduction": "高质量图像放大模型,用于高清修复",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/pulid",
+      "name": "PuLID",
+      "introduction": "身份保持与换脸模型,用于脸部修复",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/ttp",
+      "name": "TTP",
+      "introduction": "图像放大工具或模型,用于高清修复",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/controlnet",
+      "name": "ControlNet",
+      "introduction": "用于控制图像生成结构的条件预测模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_controlnet_aux",
+      "name": "comfyui_controlnet_aux",
+      "introduction": "ComfyUI 辅助节点包,提供多种预处理器和控制网支持",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/impact_pack",
+      "name": "Impact-Pack",
+      "introduction": "ComfyUI 高级影响器节点包,提供检测、细化及迭代生成功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/4x_ultrasharp",
+      "name": "4x-UltraSharp",
+      "introduction": "高性能图像放大模型,用于提升图像分辨率和细节",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_redux",
+      "name": "Redux",
+      "introduction": "配合 Flux 使用的图像参考适配器",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/model/flux_1_redux_kontext",
+      "name": "FLUX.1 Redux/Kontext",
+      "introduction": "支持换装编辑",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ip_adapter_composition",
+      "name": "IP-Adapter Composition",
+      "introduction": "实现服装展示与构图控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/controlnet_openpose",
+      "name": "ControlNet OpenPose",
+      "introduction": "精确控制人物姿态",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ip_adapter_faceid",
+      "name": "IP-Adapter-FaceID",
+      "introduction": "免训练角色固化,固定面部特征",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/controlnet_depth_normal_map",
+      "name": "ControlNet Depth/Normal Map",
+      "introduction": "实现多角度一致性生成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/midjourney_v8",
+      "name": "Midjourney v8",
+      "introduction": "艺术性和创意输出最佳的 AI 生图平台,生成速度快且细节相干性高",
+      "capability_ids": [
+        "CAP-004",
+        "CAP-016",
+        "CAP-018",
+        "CAP-001",
+        "CAP-014",
+        "CAP-003",
+        "CAP-017"
+      ]
+    },
+    {
+      "id": "tools/image_gen/adobe_firefly_3",
+      "name": "Adobe Firefly 3",
+      "introduction": "注重商业安全性的 AI 生图工具,集成于 Adobe 生态",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/stable_diffusion_3",
+      "name": "Stable Diffusion 3",
+      "introduction": "可定制性最强的开源 AI 生图模型,支持本地部署和微调",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/dall_e",
+      "name": "DALL-E",
+      "introduction": "OpenAI 开发的 AI 图像生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_2_pro",
+      "name": "FLUX.2 [pro]",
+      "introduction": "适合性价比和批量生产的 AI 图像生成模型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_2",
+      "name": "FLUX.2",
+      "introduction": "提示词准确性高、写实、支持开放权重及部分本地部署的 AI 图像生成器",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_2_0",
+      "name": "Flux 2.0",
+      "introduction": "AI 图像生成模型,擅长 photorealism 和提示词准确性",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/higgsfield",
+      "name": "Higgsfield",
+      "introduction": "AI 视频/图像生成平台,支持多种模型集成",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/flux_ai",
+      "name": "Flux AI",
+      "introduction": "支持 Flux 模型的 AI 生成平台或模型本身",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gemini_api",
+      "name": "Gemini API",
+      "introduction": "Google 官方提供的支持图像生成能力的 API 接口",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/nanobanana_org",
+      "name": "nanobanana.org",
+      "introduction": "为 Nano Banana Pro 提供可视化界面和订阅服务的第三方 Web 平台",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/gemini_3_pro_image_preview",
+      "name": "Gemini 3 Pro Image Preview",
+      "introduction": "专业级 AI 图像生成与编辑模型,基于 Gemini 3 Pro 架构,专为高保真文本渲染、复杂推理任务和专业资产生产设计",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng_4_0",
+      "name": "即梦 4.0",
+      "introduction": "涵盖海报设计、电影质感、UI 设计等场景",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/luma_uni_1",
+      "name": "Luma Uni-1",
+      "introduction": "多种风格场景下有详细对比,细节纹理丰富",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_gemini_image_generation_api",
+      "name": "Google Gemini Image Generation API",
+      "introduction": "Google Gemini 原生图像生成能力 API,品牌名为 Nano Banana",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/google_ai_studio",
+      "name": "Google AI Studio",
+      "introduction": "谷歌官方 AI 开发平台,提供 Nano Banana Pro 模型的专业开发访问渠道",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/comfyui_manager",
+      "name": "ComfyUI Manager",
+      "introduction": "用于管理自定义节点和模型的插件",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/advanced_controlnet",
+      "name": "Advanced-ControlNet",
+      "introduction": "提供高级 ControlNet 功能的自定义节点包",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/ksampler",
+      "name": "KSampler",
+      "introduction": "核心采样器节点,控制生成步数和采样方法",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/plugin/free_memory",
+      "name": "FreeMemory",
+      "introduction": "用于释放显存优化的自定义节点",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/webui",
+      "name": "WebUI",
+      "introduction": "传统的 Stable Diffusion 网页用户界面",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/davinci_resolve_studio",
+      "name": "DaVinci Resolve Studio",
+      "introduction": "专业视频后期制作软件,含 AI 驱动的专业调色模块",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/snapseed",
+      "name": "Snapseed",
+      "introduction": "Google 开发的专业手机图片编辑应用,提供多种滤镜和调整工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/adobe_lightroom",
+      "name": "Adobe Lightroom",
+      "introduction": "专业照片编辑软件,提供完整的 RAW 处理流程和精细色彩控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/luminar_neo",
+      "name": "Luminar Neo",
+      "introduction": "专业 AI 照片编辑软件,提供智能色调、光影及色彩迁移控制",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/jimeng_ai_image",
+      "name": "即梦 AI 图片 4.0",
+      "introduction": "AI 局部精准改图工具,实现局部色彩强调与自然融合",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/python_pillow",
+      "name": "Python Pillow",
+      "introduction": "Python 图像处理库,提供饱和度和对比度调整功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/supercolorpalette",
+      "name": "Supercolorpalette",
+      "introduction": "精准控色的专业配色工具",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/color_magic",
+      "name": "ColorMagic",
+      "introduction": "AI 驱动的在线配色工具,支持多模态输入生成协调配色方案",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/adobe_color",
+      "name": "Adobe Color",
+      "introduction": "专业级在线配色工具,支持多种配色规则和主题色提取",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/coolors",
+      "name": "Coolors",
+      "introduction": "超快速配色板生成器,支持图片上传取色及无障碍检查",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/haikei_app",
+      "name": "haikei.app",
+      "introduction": "多功能背景生成器,提供 15 种背景类型",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/meshgradient_in",
+      "name": "meshgradient.in",
+      "introduction": "在线渐变背景生成器,快速创建弥散渐变背景",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/fotor",
+      "name": "Fotor",
+      "introduction": "在线编辑工具,提供 AI 滤镜风格化功能,可快速调整照片色调",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/kumo_color_camera",
+      "name": "KUMO 仿色相机",
+      "introduction": "AI 仿色相机 App,支持导入参考图模仿滤镜,iOS/安卓可用",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/capture_one",
+      "name": "Capture One",
+      "introduction": "专业照片软件,提供专业色温/色调滑块、Color Grading 色轮、HSL 高级编辑等功能",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/adobe_camera_raw",
+      "name": "Adobe Camera Raw",
+      "introduction": "Photoshop 内置的 RAW 处理插件,提供完整的调色面板系统",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/colorbrew",
+      "name": "ColorBrew",
+      "introduction": "免费 AI 驱动配色板生成器,支持文本描述、图片上传生成配色方案和渐变色",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/colormind",
+      "name": "Colormind",
+      "introduction": "使用 AI 生成用户友好设计的配色方案",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/other/colormagic",
+      "name": "ColorMagic",
+      "introduction": "AI 配色生成器,支持多模态输入生成配色方案",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/topaz_photo_ai",
+      "name": "Topaz Photo AI",
+      "introduction": "AI 照片增强软件,主打自动优化与色彩平衡",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_process/layoutparser",
+      "name": "layoutparser",
+      "introduction": "开源布局分析库,支持文档区域检测、布局结构化解析、导出为 JSON/XML 格式",
+      "capability_ids": []
+    },
+    {
+      "id": "tools/image_gen/canva_ai_magic_design",
+      "name": "Canva AI (Magic Design)",
+      "introduction": "通过自然语言描述自动生成设计稿,包含自动排版、布局建议、元素 positioning",
+      "capability_ids": []
+    }
+  ]
+}

+ 4 - 0
tools/local/flux/tests/server.log

@@ -26,3 +26,7 @@ INFO:     127.0.0.1:49798 - "POST /generate HTTP/1.1" 200 OK
  - 提示词: A woman in a white dress kneeling on green grass, painting on an easel. The background features lush green meadow and trees with soft sunlight. The flowers in the background should be green foliage plants or very subtle low-saturation green flowers, not purple or blue. Maintain the white dress, green grass, and natural warm lighting. The overall color scheme should be white and green with minimal color accents. Professional photography style, soft bokeh background, natural outdoor scene.
  - 参考图: https://res.cybertogether.net/toolhub_images/img_3_restored_v1.png
 INFO:     127.0.0.1:57874 - "POST /generate HTTP/1.1" 200 OK
+INFO:     Shutting down
+INFO:     Waiting for application shutdown.
+INFO:     Application shutdown complete.
+INFO:     Finished server process [127814]

+ 3 - 82
tools/local/nano_banana/tests/server.log

@@ -1,84 +1,5 @@
-INFO:     Started server process [115371]
+INFO:     Started server process [297450]
 INFO:     Waiting for application startup.
 INFO:     Application startup complete.
-INFO:     Uvicorn running on http://0.0.0.0:46779 (Press CTRL+C to quit)
-INFO:     127.0.0.1:40858 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:47616 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:52540 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38976 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:47150 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:57230 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:56688 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38530 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:41788 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:51280 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:52510 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:59350 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:56568 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:33958 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38948 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:49772 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:35258 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:35656 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:39346 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:49104 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:42972 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:42138 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:48118 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:55048 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:57948 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:58536 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:43300 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:34922 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:47894 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:49126 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:45512 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38688 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:43316 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38774 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:58338 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:34988 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:37340 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:56030 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:37354 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:55760 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:54408 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:37504 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:51948 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38942 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:47194 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:46424 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:36120 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:60494 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:51014 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:50050 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:60818 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:53158 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:57234 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:52122 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:50546 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:58426 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:33374 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:40960 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:60080 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:56998 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:57664 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:53852 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:42622 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:55268 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:40568 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:42300 - "POST /generate HTTP/1.1" 502 Bad Gateway
-INFO:     127.0.0.1:57138 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:54384 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:54610 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:35200 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:50048 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:41996 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:54508 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:39840 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:59176 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:35004 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:38454 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:53882 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:37946 - "POST /generate HTTP/1.1" 200 OK
-INFO:     127.0.0.1:55638 - "POST /generate HTTP/1.1" 200 OK
+INFO:     Uvicorn running on http://0.0.0.0:50491 (Press CTRL+C to quit)
+INFO:     127.0.0.1:49314 - "POST /generate HTTP/1.1" 200 OK

+ 4 - 0
tools/local/seedream/tests/server.log

@@ -32,3 +32,7 @@ INFO:     127.0.0.1:49500 - "POST /generate HTTP/1.1" 500 Internal Server Error
  - Prompt: 一位穿着纯白色长裙的女性在户外草地写生,身体略微前倾专注绘画,右手持画笔左手持调色板,棕色长发自然披散在背部,轻薄棉麻质地的白色长裙,V 字露背设计,腰部系带收腰,裙摆自然垂坠有飘逸感,温暖的逆光从左上方照射,人物边缘形成金色轮廓光,发丝呈现明亮光晕,全画幅 85mm 人像定焦镜头,光圈 f/1.8 大光圈,浅景深效果,前景人物清晰锐利,背景草地和树木柔和虚化,高饱和度自然草木绿背景,纯白服装与绿色形成鲜明对比,清新森系色调,真实摄影风格,画架上的画布呈现印象派风格的画中画内容:背对镜头的女性、绿色草地、蓝色花朵,笔触粗犷色彩鲜明
 ✅ API请求成功
 INFO:     127.0.0.1:34050 - "POST /generate HTTP/1.1" 200 OK
+INFO:     Shutting down
+INFO:     Waiting for application shutdown.
+INFO:     Application shutdown complete.
+INFO:     Finished server process [128511]