# 智能分析链路重构方案(2026-03-06) ## 1. 背景 当前系统在 Agent 模式、图片编辑模式、连续对话模式下,已经具备: - 多模型路由 - 多模态图片理解 - 图片编辑子意图识别 - Auto Agent 复杂任务规划 - 故障迁移与回退 但线上日志表明,当前链路存在明显的工程性不稳定问题,导致: - 前端已上传图片,但后续分析链路丢失图片输入 - 同一条请求在主链路和救援链路看到的图片数量不一致 - 普通图片编辑请求被误送入重型 Agent 图规划链路,超时率高 - 分析模型兼任“结构化分类 + 自然对话回复”,容易输出非 JSON - 回退链路对图片输入依赖不一致,导致 fallback 无法有效执行 本方案目标不是照搬竞品,而是在保留本项目“显式分析、显式路由、显式故障迁移”优势的前提下,重构成更稳定、更可控、更易扩展的智能分析架构。 --- ## 2. 当前问题复盘 ### 2.1 日志层面的核心症状 基于线上日志,可归纳为以下问题: 1. 主链路可见图片,救援链路图片丢失 表现为: - 同一请求先出现 `hasImage: true, imageCount: 1` - 随后救援分析出现 `hasImage: true, imageCount: 0` 2. 分析模型返回自然语言解释而不是结构化结果 表现为: - `Primary analyzer returned non-JSON` - 响应内容是“我没办法直接编辑这张图,我给你两个方案……” 3. Auto Agent 图规划耗时过长 表现为: - `runAutoAgentGraph timed out after 35000ms` - 后续 `fallbackAnalyzeIntent` 再次超时 4. 模型职责混用 当前某些模型同时承担: - 图片视觉理解 - 意图分类 - 对用户的自然回复 - 执行前策略规划 5. 回退链路依赖条件过于脆弱 表现为: - `qwen3-vl fallback skipped: no valid image input` ### 2.2 根因 #### 根因一:图片输入没有统一抽象 当前代码里存在多套图片提取逻辑: - 主请求链路手工从 `message.parts` 提取 - 历史图片链路单独提取 - fallback visual summary 单独再提取一遍 - analyzer fallback 又重新处理一次 结果是: - `hasImage` 的判断口径不一致 - `imageUrls` 的来源不一致 - 支持的图片类型不一致(`http/dataURL/image_url/file/image/markdown`) #### 根因二:分析器承担了两个互相冲突的目标 当前 `smart-analyzer` 既要: - 输出机器可解析结构 - 又要直接给用户输出自然语言回复 这会诱导模型优先生成“解释性文本”,牺牲结构化稳定性。 #### 根因三:复杂规划过早介入 很多普通图片编辑请求,例如: - 抠图 - 换背景 - 局部替换 - 加一个模特 - 调整颜色 本质上不需要先走 LangGraph/Auto Agent 规划链。 但当前链路里,一旦开启 Agent/深度思考,就可能进入重型规划流程,导致前置耗时过高。 #### 根因四:模型角色没有拆层 当前“用户选择的多模态模型”容易被同时用于: - 图片视觉理解 - 意图识别 - 策略规划 - 用户回复 如果这个模型擅长图片理解但不擅长严格结构化输出,就会出现分类不稳定。 --- ## 3. 设计目标 本次重构的核心目标如下: ### 3.1 稳定性目标 - 任意一次请求,图片输入只解析一次 - `hasImage` 与 `imageRefs.length` 永远一致 - 结构化分析结果稳定输出,不依赖正则抠 JSON - 普通图片编辑请求前置分析耗时控制在 6~12 秒 ### 3.2 架构目标 - 把“媒体输入处理、结构化分析、复杂规划、执行路由”拆层 - 普通请求走轻链路 - 复杂请求才进入重链路 - 每种模型只承担自己最适合的职责 ### 3.3 可扩展目标 - 后续可接更多多模态模型 - 后续可扩展更多技能(九宫格视频、批量处理、3D、实时画布) - 后续可加入模型健康分、熔断、动态路由 --- ## 4. 目标架构 建议重构为四层: ```mermaid flowchart TD A["用户请求"] --> B["统一媒体输入层 Media Resolver"] B --> C["快速结构化分析层 Fast Analyzer"] C --> D{"是否复杂任务"} D -- 否 --> E["执行路由层 Executor Router"] D -- 是 --> F["复杂规划层 Auto Agent / Skills"] F --> E E --> G["执行模型"] G --> H["结果落库 / 相册 / 画布 / 历史"] ``` ### 4.1 Layer 1:统一媒体输入层 职责: - 把所有图片/视频/文件输入统一归一成标准结构 - 只在这里做: - `message.parts` 解析 - markdown 图片链接提取 - dataURL 标准化 - 相对路径补全 - 历史图片回填 - OSS/默认桶可访问性修正 标准输出建议: ```ts type ResolvedMediaRef = { kind: "image" | "video" | "file"; source: "current_message" | "history" | "assistant_history" | "markdown"; url: string; transport: "remote" | "inline"; mimeType?: string; width?: number; height?: number; fileName?: string; }; type ResolvedChatInput = { text: string; imageRefs: ResolvedMediaRef[]; videoRefs: ResolvedMediaRef[]; fileRefs: ResolvedMediaRef[]; fromHistory: boolean; }; ``` 强制规则: - `hasImage := imageRefs.length > 0` - 后续所有链路不得再次自行扫描 `message.parts` - 后续所有分析/回退只消费 `ResolvedChatInput` ### 4.2 Layer 2:快速结构化分析层 职责: - 判断这条请求是什么任务 - 给执行层输出稳定、可机读的分类结果 不负责: - 给用户写友好长回复 - 做复杂多步骤规划 - 决定具体供应商迁移次数 建议输出 schema: ```ts type FastAnalysisResult = { intent: "image_edit" | "image_gen" | "video_gen" | "vision" | "chat"; subIntent?: | "remove_background" | "replace_background" | "enhance" | "inpaint" | "style_transfer" | "color_adjust" | "extend" | "resize" | "redo"; complexity: "simple" | "complex"; needsClarification: boolean; clarificationReason?: string; routeHints?: { preferVisionModel?: boolean; preferEditModel?: boolean; preferVideoPlanner?: boolean; }; }; ``` 关键原则: - 强制结构化输出 - 不混入自然语言友好回复 - 普通编辑优先快速返回 ### 4.3 Layer 3:复杂规划层 只在以下场景进入: - 多张图片联合理解 - 连续对话依赖历史图片和历史编辑结果 - 批量处理 - 九宫格视频技能 - 多步骤工作流 - 需要子任务编排的技能 典型复杂任务: - “用这 5 张图做批量裂变场景” - “这张九宫格图按分镜脚本生成视频” - “根据上一轮结果继续优化但保留主体不变” 简单任务应直接跳过该层: - 抠图 - 去水印 - 去背景 - 单图换背景 - 单图局部替换 - 单图加模特 ### 4.4 Layer 4:执行路由层 职责: - 根据 `FastAnalysisResult` 和用户选择,决定: - 是否使用用户指定模型 - 是否需要故障迁移 - 是否需要多样性模式 - 是否需要走技能 这里才是: - 供应商切换 - 模型故障迁移 - 多模型轮换 - 健康分判断 --- ## 5. 模型分工建议 ### 5.1 不建议“一模三用” 同一个模型不要默认同时承担: - 视觉理解 - 结构化分类 - 自然回复 因为这三件事对模型能力要求不同。 ### 5.2 推荐的模型职责拆分 #### 角色一:视觉理解模型 职责: - 看图 - 做主体识别 - 输出图片客观描述 优先级: 1. 用户明确选择且支持视觉的多模态模型 2. 后台标记为多模态且健康分高的视觉模型 3. 固定视觉回退模型 #### 角色二:结构化分类模型 职责: - 输出稳定 schema - 不负责长回复 要求: - 支持稳定 JSON/Schema 输出 - 延迟低 - 失败率低 这类模型建议固定 1~2 个,不要频繁用执行模型代替。 #### 角色三:规划模型 职责: - 复杂任务拆解 - 多步骤编排 - 技能选择 只在复杂任务时调用。 #### 角色四:执行模型 职责: - 真正生图/修图/视频/3D 继续保留“用户指定 + 后台配置 + 故障迁移”的现有策略。 --- ## 6. 路由规则建议 ### 6.1 轻链路 适用于: - 单图 - 需求明确 - 子意图可直接识别 - 不依赖多轮历史结果 链路: `Media Resolver -> Fast Analyzer -> Executor Router -> Execute` ### 6.2 重链路 适用于: - 多图关系理解 - 需要历史图和历史结果 - 技能型任务 - 视频/九宫格/批量处理 链路: `Media Resolver -> Fast Analyzer -> Auto Agent/Skills -> Executor Router -> Execute` ### 6.3 进入重链路的明确条件 建议新增 `shouldEnterHeavyPlanning()`: - 图片数 >= 2 - 明确提到“批量、全部、分别、每张、分镜、九宫格、继续上一轮、对比前一张” - 当前模式属于视频、3D、批量处理、复杂裂变 - 当前技能需要多步骤执行 否则默认轻链路。 --- ## 7. 超时预算建议 ### 7.1 当前问题 当前前置链路经常出现: - `runAutoAgentGraph 35s` - `fallbackAnalyzeIntent 20s` 这意味着: - 预算传播不清晰 - 超时后还在重复走高成本链路 ### 7.2 建议预算模型 总前置预算建议:`18s` 建议拆分: - `Media Resolver`: 1.5s - `Fast Analyzer`: 4s - `Optional Vision Summary`: 3s - `Heavy Planning`: 6s - `Safety Buffer`: 3.5s 规则: - 预算不够时,直接降级,不再进入重链路 - 救援链路禁止再次调用高耗时流程 - 超时后使用保守结构化 fallback ### 7.3 超时后的降级策略 #### 轻任务超时 - 直接使用关键词规则返回结构化结果 - 跳过友好长回复 #### 重任务超时 - 不再重跑 Agent 图规划 - 直接退化为: - 轻量结构化分析 - 保守执行 - 或提示“建议补充信息” --- ## 8. 故障迁移与熔断 ### 8.1 当前问题 当前回退主要集中在: - qwen3-vl - 固定 fallback 模型 但没有按“职责角色”分组迁移。 ### 8.2 建议改造 将故障迁移拆成三类: #### 分类链故障迁移 - 仅在结构化分类模型池中切换 #### 视觉链故障迁移 - 仅在视觉理解模型池中切换 #### 执行链故障迁移 - 仅在图片/视频/3D 执行模型池中切换 ### 8.3 熔断策略 建议对模型增加短期健康分: - 最近 5 分钟失败率 - 最近 20 次平均延迟 - 是否频繁返回非结构化结果 当某模型: - 连续 3 次失败 - 或最近 10 次失败率 > 50% 则短期熔断 3~5 分钟,不再参与分析路由。 --- ## 9. 与竞品方案的取舍 ### 9.1 不建议直接照搬竞品 如 `lobe-chat-pro` 的方式更偏向: - 做好上下文工程 - 把图片直接塞给当前多模态模型 - 让模型自己完成需求理解和图片理解 这种方式优点: - 链路更短 - 前置超时点更少 缺点: - 业务可控性弱 - 子意图识别不稳定 - 很难做细粒度故障迁移 - 不适合你们这种“多技能、多模式、多执行模型”的系统 ### 9.2 可借鉴部分 建议借鉴它的: - 统一上下文工程 - 统一消息内容处理 - 统一多模态消息格式 不建议借鉴它的: - 完全取消独立分析层 ### 9.3 可参考“大龙虾 / OpenClaw”式长期记忆,但不能直接照搬 如果这里的“大龙虾”指的是 OpenClaw / 龙虾系智能体,那么它公开展示出来的长期记忆能力,核心并不是“模型天然永久记忆”,而是下面几层工程组合: 1. 会话连续性 - 私聊默认折叠到一个主会话 key - 会话记录持久化到本地 JSONL - 支持按会话继续累积上下文,而不是每轮都从零开始 2. 长期记忆外置化 - 将长期记忆写入 `MEMORY.md` - 将每天的运行过程写入 `memory/YYYY-MM-DD.md` - 模型只是在需要时读取这些文件,不是真的把记忆“存在参数里” 3. 检索增强 - 用向量检索 + BM25 混合搜索找相关记忆片段 - 只把命中的片段回注给模型,而不是全量注入 4. 上下文压缩 - 长对话会自动压缩旧消息 - 压缩结果写回会话历史 - 必要时在压缩前触发一次记忆刷写 5. 自动捕获 / 自动召回 - 新版本还支持长期记忆插件,自动把高价值信息沉淀成可召回记忆 这套设计非常值得借鉴,但它更适合“通用助理型 Agent”。 对你们这种“多图编辑 + 无限画布 + 连续修图”的产品,还必须增加一层更强的 **资产级指向记忆**,否则模型即便记得“用户之前发过一张椅子图”,也未必能稳定知道这轮说的“它 / 这张 / 左边那张 / 刚才那张结果”到底对应哪张图。 --- ## 10. 长期记忆与图片指向定位 ### 10.1 目标 做到接近 Gemini 官方生图那种“连续对话不丢上下文”的体验,但实现方式必须是工程化的,而不是赌模型自己记住。 需要同时解决两件事: 1. 长期记住用户偏好、近期目标、上一轮结果 2. 精准知道当前修改的是哪一张图片、哪一版结果、哪一个画布节点 ### 10.2 四层记忆模型 建议新增四层记忆,而不是单一聊天历史: #### 第一层:会话工作记忆(Working Memory) 作用: - 保存最近若干轮对话 - 保存当前任务目标 - 保存最近一次用户选中的图片集合 - 保存当前“焦点图片” 特点: - 只服务当前会话 - 生命周期短 - 响应最快 建议内容: ```ts type SessionWorkingMemory = { sessionId: string; currentTask?: string; currentFocusAssetIds: string[]; lastReferencedAssetIds: string[]; lastGeneratedAssetIds: string[]; lastUserIntent?: string; updatedAt: string; }; ``` #### 第二层:会话压缩记忆(Session Summary Memory) 作用: - 将长对话中的决策、修改历史、已完成结果压缩成结构化摘要 - 避免上下文窗口无限增长 建议记录: - 当前主任务 - 已处理过哪些图片 - 每张图最近一次做了什么修改 - 用户明确拒绝过什么效果 - 用户偏好的风格/比例/清晰度 #### 第三层:长期用户记忆(Long-term User Memory) 作用: - 保存长期稳定偏好 例如: - 用户偏好白底商品图 - 用户偏好 1:1、4K - 用户常做“产品裂变场景” - 用户偏好某类模特风格 这部分应像 OpenClaw 的 `MEMORY.md` 一样长期沉淀,但建议你们存数据库,不建议只存 Markdown。 #### 第四层:资产关系记忆(Asset Graph Memory) 这是你们最关键、也是 Gemini 类体验真正需要补足的一层。 作用: - 记录“哪张图是原图” - 哪张图是由哪张图编辑出来的 - 哪张图当前位于画布哪个节点 - 哪张图在这轮被用户选中了 - 哪张图是上一轮最新结果 - 哪张图和哪条消息绑定 建议抽象: ```ts type AssetMemoryRecord = { assetId: string; chatId: string; canvasId?: string; nodeId?: string; sourceAssetId?: string; rootAssetId?: string; generationType: "upload" | "edit" | "image_gen" | "batch" | "video_keyframe"; turnCreatedAt: string; messageId?: string; displayName?: string; width?: number; height?: number; tags?: string[]; }; ``` 还需要关系表: ```ts type AssetRelation = { fromAssetId: string; toAssetId: string; relation: | "edited_from" | "variation_of" | "selected_in_turn" | "referenced_by_user" | "latest_result_of"; createdAt: string; }; ``` ### 10.3 如何精准知道“用户说的是哪张图” 仅靠 LLM 猜是不够的,必须采用“规则优先 + 模型补全”的混合方案。 #### 规则一:显式选中优先 如果用户当前在无限画布中选中了图片: - 当前选中图片 = 第一优先目标 - 不允许模型随意改成别的图 #### 规则二:本轮上传优先 如果本轮消息带图: - 本轮上传图优先于历史图 #### 规则三:明确指代优先 如果用户文本提到: - “左边那张” - “右上角那张” - “第二张” - “刚才生成的那张” - “原图” - “上一轮结果” 则优先用规则引擎在资产图谱中定位对应资产。 #### 规则四:最近焦点资产回退 如果用户没有重新上传图,也没有明确指代: - 使用 `currentFocusAssetIds` - 否则使用 `lastGeneratedAssetIds` - 否则使用最近一次用户主动操作过的资产 #### 规则五:模型只做歧义消解,不做第一判定 当规则无法唯一定位时,才让模型做补充判断,例如: - “把它再亮一点” - “这个版本更自然一点” 这时模型输入不是“原始所有图片”,而是: - 候选资产列表 - 每张图的摘要 - 每张图的位置 - 与当前消息的时间距离 然后让模型只输出: - 最可能的 `assetId` - 置信度 - 是否需要澄清 ### 10.4 资产级摘要比原图更重要 为了让连续对话精准命中图片,建议每张图生成后都自动保存一份轻量摘要: - 主体 - 场景 - 风格 - 颜色 - 位置 - 来源 - 是否为最新结果 例如: ```ts type AssetSummary = { assetId: string; subject: string; scene: string; style: string; dominantColor?: string; canvasPosition?: string; isLatestResult?: boolean; basedOnAssetId?: string; }; ``` 这样当用户说: - “把刚才那张带模特的椅子图背景换白” - “不要改原图,改上一个结果” - “左上角那个红色椅子再清晰一点” 系统可以先用结构化信息命中资产,再决定是否需要模型辅助。 ### 10.5 记忆写入时机 建议在以下时机写入记忆: 1. 用户上传图片后 - 写入 `AssetMemoryRecord` - 生成初始图片摘要 2. 用户选中画布图片后 - 更新 `currentFocusAssetIds` 3. 每次生成成功后 - 创建新资产 - 建立 `edited_from / latest_result_of` - 更新最近结果索引 4. 每轮对话结束后 - 将本轮意图、目标资产、结果资产写入会话摘要 5. 会话压缩前 - 将高价值稳定偏好写入长期记忆 ### 10.6 与 OpenClaw 式记忆的结合方式 可以参考 OpenClaw 的理念,但落地方式建议改成: - OpenClaw 式“Markdown 长期记忆” 适合: - 用户偏好 - 工作规则 - 长期稳定事实 - 你们自己的“数据库资产图谱记忆” 适合: - 图片版本链 - 画布节点绑定 - 当前选中状态 - 最近结果指向 - 连续编辑目标定位 结论: - 通用长期记忆,可以参考“大龙虾” - 图片连续编辑定位,必须做你们自己的资产图谱系统 ### 10.7 推荐新增能力 建议新增: - `resolveTargetAssets()` 输入当前消息、当前选中状态、历史资产图谱,输出本轮目标资产列表 - `summarizeAssetForMemory()` 为每张图生成结构化摘要 - `updateSessionWorkingMemory()` 每轮更新焦点资产、最新结果、最近意图 - `compactSessionMemory()` 将长会话压缩成结构化摘要 ### 10.8 验收标准 做到下面这些,才算真正拥有“连续对话图片记忆”能力: 1. 用户说“这张/它/刚才那张”时,目标图命中率显著提升 2. `hasImage=true 但 imageCount=0` 类问题不再出现 3. 系统可以区分: - 原图 - 上一轮结果 - 最新结果 - 当前选中图 4. 同一张图连续编辑多轮后,版本链可追踪 5. 超长对话后仍能知道当前改的是哪张资产 --- ## 11. 第一阶段实施范围 第一阶段目标:先把线上最痛的稳定性问题止住,不做大面积重写。 ### 10.1 P0 必做 #### P0-1 统一媒体输入层 新增统一解析函数,例如: - `resolveChatMediaInput()` 统一处理: - `file` - `image` - `image_url` - `data:image` - markdown 图片 - 历史消息图片 - 相对路径补全 产出: - `ResolvedChatInput` #### P0-2 Fast Analyzer 只输出结构化结果 重构 `lib/ai/smart-analyzer.ts`: - 结构化分类函数 - 用户友好回复函数 拆开调用,不再混用。 #### P0-3 轻重链路分流 在聊天主入口加: - `shouldEnterHeavyPlanning()` 让普通图片编辑跳过 `auto-agent-graph`。 #### P0-4 救援链路复用同一份图片输入 任何 fallback / rescue / summary / qwen fallback: - 只接收 `ResolvedChatInput.imageRefs` - 不允许再去直接扫 `message.parts` ### 10.2 P1 建议 - 增加分类模型池 - 增加视觉模型池 - 增加模型健康分和熔断 - 增加结构化输出监控 ### 10.3 P2 后续 - 技能统一注册中心 - 重任务异步队列化 - Agent 计划缓存 - 前置分析结果缓存 --- ## 12. 代码改造建议位置 ### 11.1 主入口 - `app/(chat)/api/chat/route.ts` 改造点: - 增加统一媒体输入解析 - 统一 `hasImage` - 统一 rescue/fallback/summary 的图片来源 - 加轻重链路判断 ### 11.2 快速分析器 - `lib/ai/smart-analyzer.ts` 改造点: - 拆结构化分类与自然回复 - 统一输入类型为 `ResolvedChatInput` - 取消重复提图逻辑 ### 11.3 Auto Agent - `lib/ai/auto-agent-graph.ts` 改造点: - 只处理复杂任务 - 缩短默认预算 - 不再承担所有普通任务 ### 11.4 后续可新增模块 建议新增: - `lib/chat/media-resolver.ts` - `lib/chat/analysis-router.ts` - `lib/chat/analysis-types.ts` - `lib/chat/model-health.ts` --- ## 13. 验收标准 第一阶段完成后,至少应满足: 1. 同一请求主链路和 fallback 链路的 `imageRefs.length` 一致 2. `Unsupported model version` 不再出现 3. 普通图片编辑请求不再进入 `auto-agent-graph` 4. 前置分析超时率显著下降 5. `Primary analyzer returned non-JSON` 频次明显下降 6. `qwen3-vl fallback skipped: no valid image input` 应接近 0 --- ## 14. 推荐实施顺序 ### 阶段一:稳定性修复 1. 统一媒体输入层 2. 修复图片丢失 3. 轻重链路分流 4. 缩短重链路预算 ### 阶段二:结构化升级 1. Fast Analyzer 结构化输出 2. 拆分友好回复生成 3. 分类模型池 ### 阶段三:调度升级 1. 模型健康分 2. 熔断 3. 角色化路由 ### 阶段四:复杂任务平台化 1. 技能注册 2. 异步队列 3. 可重放任务 --- ## 15. 最终建议 对本项目而言,最优方案不是: - 完全照搬竞品 - 完全取消独立分析器 - 完全依赖单个多模态模型“自己理解一切” 最优方案应当是: - 借鉴竞品的统一上下文与消息处理工程 - 保留本项目显式分析、显式路由、显式迁移的优势 - 将分析链路拆层,按职责给模型分工 - 让普通任务走轻链路,复杂任务才走重链路 这样既能解决当前的线上不稳定问题,也不会破坏你们后续批量处理、视频技能、3D、实时画布等复杂能力的扩展空间。