ai-chatbot/docs/智能分析链路重构方案-2026-03-06.md
Your Name dc6162c93d
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: apply latest canvas editor and chat updates
2026-03-06 16:24:23 +08:00

22 KiB
Raw Blame History

智能分析链路重构方案(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. 目标架构

建议重构为四层:

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/默认桶可访问性修正

标准输出建议:

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:

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)

作用:

  • 保存最近若干轮对话
  • 保存当前任务目标
  • 保存最近一次用户选中的图片集合
  • 保存当前“焦点图片”

特点:

  • 只服务当前会话
  • 生命周期短
  • 响应最快

建议内容:

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 类体验真正需要补足的一层。

作用:

  • 记录“哪张图是原图”
  • 哪张图是由哪张图编辑出来的
  • 哪张图当前位于画布哪个节点
  • 哪张图在这轮被用户选中了
  • 哪张图是上一轮最新结果
  • 哪张图和哪条消息绑定

建议抽象:

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[];
};

还需要关系表:

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 资产级摘要比原图更重要

为了让连续对话精准命中图片,建议每张图生成后都自动保存一份轻量摘要:

  • 主体
  • 场景
  • 风格
  • 颜色
  • 位置
  • 来源
  • 是否为最新结果

例如:

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、实时画布等复杂能力的扩展空间。