22 KiB
智能分析链路重构方案(2026-03-06)
1. 背景
当前系统在 Agent 模式、图片编辑模式、连续对话模式下,已经具备:
- 多模型路由
- 多模态图片理解
- 图片编辑子意图识别
- Auto Agent 复杂任务规划
- 故障迁移与回退
但线上日志表明,当前链路存在明显的工程性不稳定问题,导致:
- 前端已上传图片,但后续分析链路丢失图片输入
- 同一条请求在主链路和救援链路看到的图片数量不一致
- 普通图片编辑请求被误送入重型 Agent 图规划链路,超时率高
- 分析模型兼任“结构化分类 + 自然对话回复”,容易输出非 JSON
- 回退链路对图片输入依赖不一致,导致 fallback 无法有效执行
本方案目标不是照搬竞品,而是在保留本项目“显式分析、显式路由、显式故障迁移”优势的前提下,重构成更稳定、更可控、更易扩展的智能分析架构。
2. 当前问题复盘
2.1 日志层面的核心症状
基于线上日志,可归纳为以下问题:
-
主链路可见图片,救援链路图片丢失
表现为:- 同一请求先出现
hasImage: true, imageCount: 1 - 随后救援分析出现
hasImage: true, imageCount: 0
- 同一请求先出现
-
分析模型返回自然语言解释而不是结构化结果
表现为:Primary analyzer returned non-JSON- 响应内容是“我没办法直接编辑这张图,我给你两个方案……”
-
Auto Agent 图规划耗时过长
表现为:runAutoAgentGraph timed out after 35000ms- 后续
fallbackAnalyzeIntent再次超时
-
模型职责混用
当前某些模型同时承担:- 图片视觉理解
- 意图分类
- 对用户的自然回复
- 执行前策略规划
-
回退链路依赖条件过于脆弱
表现为: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 推荐的模型职责拆分
角色一:视觉理解模型
职责:
- 看图
- 做主体识别
- 输出图片客观描述
优先级:
- 用户明确选择且支持视觉的多模态模型
- 后台标记为多模态且健康分高的视觉模型
- 固定视觉回退模型
角色二:结构化分类模型
职责:
- 输出稳定 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 35sfallbackAnalyzeIntent 20s
这意味着:
- 预算传播不清晰
- 超时后还在重复走高成本链路
7.2 建议预算模型
总前置预算建议:18s
建议拆分:
Media Resolver: 1.5sFast Analyzer: 4sOptional Vision Summary: 3sHeavy Planning: 6sSafety 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 / 龙虾系智能体,那么它公开展示出来的长期记忆能力,核心并不是“模型天然永久记忆”,而是下面几层工程组合:
-
会话连续性
- 私聊默认折叠到一个主会话 key
- 会话记录持久化到本地 JSONL
- 支持按会话继续累积上下文,而不是每轮都从零开始
-
长期记忆外置化
- 将长期记忆写入
MEMORY.md - 将每天的运行过程写入
memory/YYYY-MM-DD.md - 模型只是在需要时读取这些文件,不是真的把记忆“存在参数里”
- 将长期记忆写入
-
检索增强
- 用向量检索 + BM25 混合搜索找相关记忆片段
- 只把命中的片段回注给模型,而不是全量注入
-
上下文压缩
- 长对话会自动压缩旧消息
- 压缩结果写回会话历史
- 必要时在压缩前触发一次记忆刷写
-
自动捕获 / 自动召回
- 新版本还支持长期记忆插件,自动把高价值信息沉淀成可召回记忆
这套设计非常值得借鉴,但它更适合“通用助理型 Agent”。
对你们这种“多图编辑 + 无限画布 + 连续修图”的产品,还必须增加一层更强的 资产级指向记忆,否则模型即便记得“用户之前发过一张椅子图”,也未必能稳定知道这轮说的“它 / 这张 / 左边那张 / 刚才那张结果”到底对应哪张图。
10. 长期记忆与图片指向定位
10.1 目标
做到接近 Gemini 官方生图那种“连续对话不丢上下文”的体验,但实现方式必须是工程化的,而不是赌模型自己记住。
需要同时解决两件事:
- 长期记住用户偏好、近期目标、上一轮结果
- 精准知道当前修改的是哪一张图片、哪一版结果、哪一个画布节点
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 记忆写入时机
建议在以下时机写入记忆:
-
用户上传图片后
- 写入
AssetMemoryRecord - 生成初始图片摘要
- 写入
-
用户选中画布图片后
- 更新
currentFocusAssetIds
- 更新
-
每次生成成功后
- 创建新资产
- 建立
edited_from / latest_result_of - 更新最近结果索引
-
每轮对话结束后
- 将本轮意图、目标资产、结果资产写入会话摘要
-
会话压缩前
- 将高价值稳定偏好写入长期记忆
10.6 与 OpenClaw 式记忆的结合方式
可以参考 OpenClaw 的理念,但落地方式建议改成:
-
OpenClaw 式“Markdown 长期记忆”
适合:- 用户偏好
- 工作规则
- 长期稳定事实
-
你们自己的“数据库资产图谱记忆”
适合:- 图片版本链
- 画布节点绑定
- 当前选中状态
- 最近结果指向
- 连续编辑目标定位
结论:
- 通用长期记忆,可以参考“大龙虾”
- 图片连续编辑定位,必须做你们自己的资产图谱系统
10.7 推荐新增能力
建议新增:
-
resolveTargetAssets()
输入当前消息、当前选中状态、历史资产图谱,输出本轮目标资产列表 -
summarizeAssetForMemory()
为每张图生成结构化摘要 -
updateSessionWorkingMemory()
每轮更新焦点资产、最新结果、最近意图 -
compactSessionMemory()
将长会话压缩成结构化摘要
10.8 验收标准
做到下面这些,才算真正拥有“连续对话图片记忆”能力:
- 用户说“这张/它/刚才那张”时,目标图命中率显著提升
hasImage=true 但 imageCount=0类问题不再出现- 系统可以区分:
- 原图
- 上一轮结果
- 最新结果
- 当前选中图
- 同一张图连续编辑多轮后,版本链可追踪
- 超长对话后仍能知道当前改的是哪张资产
11. 第一阶段实施范围
第一阶段目标:先把线上最痛的稳定性问题止住,不做大面积重写。
10.1 P0 必做
P0-1 统一媒体输入层
新增统一解析函数,例如:
resolveChatMediaInput()
统一处理:
fileimageimage_urldata: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.tslib/chat/analysis-router.tslib/chat/analysis-types.tslib/chat/model-health.ts
13. 验收标准
第一阶段完成后,至少应满足:
- 同一请求主链路和 fallback 链路的
imageRefs.length一致 Unsupported model version不再出现- 普通图片编辑请求不再进入
auto-agent-graph - 前置分析超时率显著下降
Primary analyzer returned non-JSON频次明显下降qwen3-vl fallback skipped: no valid image input应接近 0
14. 推荐实施顺序
阶段一:稳定性修复
- 统一媒体输入层
- 修复图片丢失
- 轻重链路分流
- 缩短重链路预算
阶段二:结构化升级
- Fast Analyzer 结构化输出
- 拆分友好回复生成
- 分类模型池
阶段三:调度升级
- 模型健康分
- 熔断
- 角色化路由
阶段四:复杂任务平台化
- 技能注册
- 异步队列
- 可重放任务
15. 最终建议
对本项目而言,最优方案不是:
- 完全照搬竞品
- 完全取消独立分析器
- 完全依赖单个多模态模型“自己理解一切”
最优方案应当是:
- 借鉴竞品的统一上下文与消息处理工程
- 保留本项目显式分析、显式路由、显式迁移的优势
- 将分析链路拆层,按职责给模型分工
- 让普通任务走轻链路,复杂任务才走重链路
这样既能解决当前的线上不稳定问题,也不会破坏你们后续批量处理、视频技能、3D、实时画布等复杂能力的扩展空间。