GPU_GUARD_MONOREPO/docs/superpowers/specs/2026-04-26-multimodal-tool-design.md
2026-05-20 21:39:12 +08:00

13 KiB
Raw Permalink Blame History

多模态图片识别工具 & 工具模型分类 & 对话附件上传 设计

日期2026-04-26 状态:待实施

概述

在工具管理系统中引入三个能力:

  1. 工具模型分类:区分"需要大模型配置"和"不需要大模型配置"的工具,支持工具级别的模型渠道绑定
  2. 图片识别工具:新增 image_recognize 多模态工具,支持 URL 和 base64 两种图片输入,通过配置的多模态模型进行图片分析
  3. 对话附件上传Agent 对话页面支持上传图片/视频/PDF 等附件,支持多文件、首尾帧,为后续文生图/图生图/视频生成预留扩展

一、数据层变更

netaclaw_tool 表新增字段

字段 类型 默认值 说明
requiresModel int 0 是否需要大模型配置0否 1是
modelChannelId int null 关联模型渠道ID指向 netaclaw_model_channel.id
modelId varchar(100) null 关联模型ID渠道内具体模型名

NetaClawToolEntity 中新增对应字段。

Catalog Schema 扩展

registerSchema() 的 schema 对象新增 requiresModel?: boolean 字段,工具注册时声明是否需要模型。 syncCatalogToDb() 同步时将此字段写入 DB。

二、图片识别工具实现

文件位置

packages/backend/src/modules/netaclaw/tools/builtin/image_recognize.ts

工具元信息

  • name: image_recognize
  • label: 图片识别
  • toolset: vision
  • capability: multimodal
  • requiresModel: true
  • visibility: tool
  • isCore: false
  • canDisable: true

依赖注入模式

遵循项目现有的工厂函数 + 闭包模式。现有工厂函数接收的是运行时数据provider 实例、凭证),不是 service 对象。image_recognize 也应如此:

// image_recognize.ts — 工厂函数接收已解析的运行时凭证
export function createImageRecognizeTool(credentials: {
  baseUrl: string; apiKey: string; supplier: string;
  modelId: string; promptHint: string | null;
}): AnyAgentTool {
  return {
    name: 'image_recognize',
    async execute(id, params) {
      // 通过闭包直接访问已解析的凭证,无需再查 DB
      const provider = getProvider(supplierToProvider[credentials.supplier]);
      // ... 调用 provider.chat()
    },
  };
}

tool_resolver.tsresolve() 方法中,先查询 DB 获取凭证,再传给工厂函数:

// tool_resolver.ts resolve() 方法中
if (filteredNames.includes('image_recognize')) {
  const toolConfig = await this.toolRegistry.getToolModelConfig('image_recognize');
  if (toolConfig) {
    const channel = await this.modelChannelService.resolveForAgent(toolConfig.modelChannelId);
    if (channel) {
      runtimeTools.push(createImageRecognizeTool({
        ...channel, modelId: toolConfig.modelId, promptHint: toolConfig.promptHint,
      }));
    }
  }
  // 若未配置或渠道不可用,工具不进入 runtimeToolsLLM 不会看到该工具)
}

关键决策:模型未配置时在 resolve 阶段排除工具,避免 LLM 尝试调用必然失败的工具。

参数 Schema (TypeBox)

{
  image: Type.String({ description: '图片URL或base64编码字符串' }),
  prompt: Type.Optional(Type.String({ description: '分析提示词,不传则使用工具默认提示词' }))
}

默认系统提示词

工具内置一个高质量默认提示词,存储在 promptHint 字段中,前端可编辑覆写:

你是一个专业的图像分析助手。请按以下步骤分析图片:

1. **图像分类**:首先识别图片类型(如:身份证、驾驶证、行驶证、营业执照、发票、商品图片、截图、照片、表格、图表、手写文字、印刷文字等)。

2. **结构化提取**:根据图片类型,提取关键信息:
   - 证件类:提取所有字段(姓名、证件号、有效期、地址等)
   - 票据类:提取金额、日期、项目明细等
   - 商品类:提取品名、规格、价格、品牌等
   - 表格/图表类:提取数据结构和关键数值
   - 其他类:详细描述画面内容

3. **详细描述**:对图片内容进行全面、详细的文字描述,不遗漏任何可见信息。包括文字内容、图形元素、颜色、布局等。

4. **质量评估**:简要说明图片清晰度、是否有遮挡或模糊区域。

请以结构化格式输出分析结果。

执行流程

  1. 工厂函数通过闭包直接访问已解析的凭证baseUrl、apiKey、supplier、modelId、promptHint
  2. 判断 image 参数URLhttp/https 开头)或 base64
  3. 读取闭包中的 promptHint 作为系统提示词,若用户传了 prompt 参数则追加
  4. 通过项目现有的 LLM provider 层调用模型(getProvider(supplierToProvider[supplier])provider.chat()),不直接用 fetch
  5. 构造 OpenAI 兼容的 multimodal messages火山引擎 volcengine supplier 映射到 openai provider
    {
      "model": "doubao-seed-2-0-pro-260215",
      "messages": [{
        "role": "user",
        "content": [
          { "type": "text", "text": "<promptHint + prompt>" },
          { "type": "image_url", "image_url": { "url": "<url或data:image/...;base64,...>" } }
        ]
      }]
    }
    
  6. 返回 textResult(response.choices[0].message.content)

三、后端接口变更

tool controller 变更

  • page 接口 pageQueryOp 新增 requiresModelfieldEq
  • update 接口自动支持CoolController 的 update 会更新所有传入字段)

tool_registry service 变更

  • syncCatalogToDb() 同步时处理 requiresModel 字段
  • page() 方法支持 requiresModel 过滤
  • 新增 getToolModelConfig(toolName) 方法:
    async getToolModelConfig(toolName: string): Promise<{ modelChannelId: number; modelId: string } | null> {
      const tool = await this.toolEntity.findOneBy({ name: toolName });
      if (!tool?.modelChannelId || !tool?.modelId) return null;
      return { modelChannelId: tool.modelChannelId, modelId: tool.modelId };
    }
    

tool_resolver.ts 变更

resolve() 方法中,为 image_recognize 工具创建实例时注入依赖(与 memory/delegate 工具同模式)。

四、前端工具管理页改造

筛选栏

新增"模型依赖"下拉筛选:全部 / 需要模型 / 不需要模型

表格列

新增"模型配置"列:

  • requiresModel === 0:显示 -
  • requiresModel === 1 且已配置:显示模型名称(如 doubao-seed-2-0-pro-260215
  • requiresModel === 1 且未配置:显示橙色 el-tag type="warning" "未配置"

编辑抽屉

requiresModel === 1 时,在编辑抽屉中显示"模型配置"区域(el-divider 分隔):

  • 模型渠道下拉(调用 model_channelallModels 接口,筛选含 multimodal 能力模型的渠道)
  • 模型选择下拉(联动,显示所选渠道下 capability 为 multimodal 的模型)
  • 默认提示词文本框(el-input type="textarea" :rows="8",编辑 promptHint 字段)

五、Agent 对话附件上传

架构决策:主 Agent LLM 如何感知附件

主 Agent LLM 可能是纯文本模型(如 DeepSeek无法直接"看到"图片。采用 metadata + prompt_builder 注入方案

  1. 用户消息 content 保持纯净,不污染原始文本
  2. 附件信息存储在 message 的 metadata.attachments
  3. prompt_builder 层构造 LLM messages 时,检查 metadata.attachments将附件信息作为独立的 system/user message 注入到 messages 数组中:
[系统注入的附件提示]
用户上传了以下附件:
- 图片: photo.jpg (URL: https://xxx/photo.jpg)
- PDF: report.pdf (URL: https://xxx/report.pdf)
如需分析图片内容,请使用 image_recognize 工具传入图片URL。

这样避免了"后端注入 + 前端过滤"的脆弱双向耦合content 字段始终是用户原始输入。

协议层扩展

gateway/protocol.ts 中 ClientChatMessage 新增 attachments 字段:

interface ChatAttachment {
  id: string;
  type: 'image' | 'video' | 'pdf' | 'document' | 'other';
  url: string;
  name: string;
  size: number;
  mimeType: string;
  role?: 'start_frame' | 'end_frame';
}

消息存储

netaclaw_message.metadata JSON 中存储:

{ "attachments": [...] }

前端组件化设计

将附件功能拆分为独立组件,不膨胀现有 ChatComposer

components/chat/
├── ChatComposer.vue          (现有slim 协调器,新增 attachments prop 传递)
├── ChatAttachmentButton.vue   (新增,附件按钮 + 文件选择器)
├── ChatAttachmentPreview.vue  (新增,附件预览区,缩略图/删除/首尾帧标记)
├── MessageAttachments.vue     (新增,消息气泡中的附件展示)
└── ...

ChatAttachmentButton.vue

  • 回形针图标按钮,点击打开文件选择器
  • 接受类型:图片(jpg/png/gif/webp/bmp)、视频(mp4/mov/avi)、PDF、文档(doc/docx/xls/xlsx)
  • 支持多选
  • emit: @select(files: File[])

ChatAttachmentPreview.vue

  • 横向滚动的附件预览条,位于输入框上方
  • 每个附件显示:缩略图(图片)/ 文件图标(其他)+ 文件名 + 大小 + 删除按钮
  • 图片:显示缩略图(前端 URL.createObjectURL 或上传后的 URL
  • 视频:显示视频图标 + 文件名
  • 右键菜单:标记为"首帧" / "尾帧"(设置 role 字段)
  • 上传进度条(每个文件独立进度)
  • props: attachments: ChatAttachment[]
  • emit: @remove(id), @update-role(id, role)

ChatComposer.vue 改造

  • 在 textarea 上方条件渲染 ChatAttachmentPreview
  • 在 textarea 左侧添加 ChatAttachmentButton
  • 支持拖拽上传(@dragover + @drop 事件)
  • 支持粘贴上传(@paste 事件,检测 clipboardData.files
  • 新增 attachments ref发送时随消息一起 emit

MessageAttachments.vue

  • message-item.vue 中,当 metadata.attachments 存在时渲染
  • 图片:网格布局缩略图,点击 el-image-viewer 放大
  • 视频:视频缩略图 + 播放图标
  • PDF/文档:文件图标 + 文件名 + 大小,点击新窗口打开

上传流程

  1. 用户选择/拖拽/粘贴文件
  2. 前端调用 /admin/base/comm/upload 上传到 Space复用现有上传基础设施
  3. 上传成功后获得文件 URL构造 ChatAttachment 对象
  4. 添加到 attachments 数组,显示预览
  5. 用户点击发送 → attachments 随消息通过 WebSocket 发送(只携带 URL不携带 base64 数据)
  6. 后端将附件信息存入 message metadata

ConversationList 消息展示

message-item.vue 的用户消息气泡中:

  • 检查 metadata.attachments,若存在则渲染 MessageAttachments 组件
  • content 字段始终是用户原始输入,无需过滤

六、影响范围

新增文件

  • packages/backend/src/modules/netaclaw/tools/builtin/image_recognize.ts — 图片识别工具
  • packages/frontend/src/modules/agent/components/chat/ChatAttachmentButton.vue — 附件按钮
  • packages/frontend/src/modules/agent/components/chat/ChatAttachmentPreview.vue — 附件预览
  • packages/frontend/src/modules/agent/components/chat/MessageAttachments.vue — 消息附件展示

修改文件

  • packages/backend/src/modules/netaclaw/entity/tool.ts — 新增 3 个字段
  • packages/backend/src/modules/netaclaw/tools/catalog.ts — schema 类型扩展 + import 新工具
  • packages/backend/src/modules/netaclaw/service/tool_registry.ts — 同步、查询、模型配置方法
  • packages/backend/src/modules/netaclaw/service/tool_resolver.ts — 注入 image_recognize 工具依赖
  • packages/backend/src/modules/netaclaw/controller/admin/tool.ts — 筛选参数
  • packages/frontend/src/modules/agent/views/tools.vue — 筛选、表格列、编辑抽屉
  • packages/backend/src/modules/netaclaw/gateway/protocol.ts — ChatAttachment 类型 + ClientChatMessage 扩展
  • packages/backend/src/modules/netaclaw/gateway/server.ts — 附件消息处理(存 metadata不改 content
  • packages/backend/src/modules/netaclaw/service/prompt_builder.ts — 附件信息注入到 LLM messages
  • packages/frontend/src/modules/agent/components/chat/ChatComposer.vue — 集成附件按钮/预览/拖拽/粘贴
  • packages/frontend/src/modules/agent/components/message-item.vue — 渲染 MessageAttachments
  • packages/frontend/src/modules/agent/types/index.d.ts — ChatAttachment 类型 + WSClientMessage 扩展
  • packages/frontend/src/modules/agent/hooks/websocket.ts — 消息发送携带 attachments

不需要修改

  • model_channel 相关代码(复用现有 resolveForAgent + allModels 接口)
  • tool_resolver.ts 的 capability 匹配逻辑(已存在,无需改动)
  • Agent entity工具模型配置在工具级别

安全注意事项

  • 后端文件上传接口需验证 MIME 白名单和文件大小限制
  • API Key 不得出现在工具执行日志或前端返回的 tool_result 事件中
  • 附件 URL 应有访问控制(签名 URL 或 token 验证)