# Dev Changelog — Full 完整的开发改动记录,按时间倒序排列。作为主动 RAG 的数据源,用户手动唤醒时读取。 ## 记录 ### [CL-20260413-1130] 2026-04-13 11:30 — 修复 SDXL 模型 404 错误:补全 Replicate 版本 hash - **tags**: bug修复, Replicate, SDXL, 模型配置, 404 - **affected_files**: - art-agent/backend/app/config.py - **what**: 修复 Stable Diffusion XL 模型调用 Replicate API 返回 404 的问题 - **why**: 与之前 Kolors IP-Adapter(CL-20260413-0440)完全相同的根因——Replicate 对非 Black Forest Labs 官方模型需要 `owner/model:version_hash` 完整格式,短格式 `stability-ai/sdxl` 无法定位到具体版本 - **decisions**: 使用 Replicate 官方文档中的最新推荐版本 hash `39ed52f2...e08b` - **notes**: Flux 系列(black-forest-labs/flux-schnell、flux-dev)不受此问题影响,Replicate 对其支持短格式。后续新增模型时应默认使用完整的 `owner/model:version_hash` 格式 ### [CL-20260413-1100] 2026-04-13 11:00 — 修复切换模型后 LLM 仍声称使用旧模型名 - **tags**: Agent Loop, system prompt, 模型选择, LLM 幻觉, 模型名 - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: System Prompt 中动态注入当前生图模型名称,防止 LLM 从对话历史幻觉旧模型名 - **why**: 用户在同一会话内切换模型(如从 InstantStyle 切到 Kolors),LLM 在回复中仍声称使用旧模型。根因是 System Prompt 不包含模型信息,LLM 从对话历史中的工具返回结果/文字记录推断模型名,导致幻觉 - **decisions**: 在 system prompt 末尾动态拼接当前模型名 + 强调忽略旧记录。同时在假生成重试的纠正消息中也附带模型名 - **notes**: 通过 debug 日志验证:前端→后端→工具执行的模型传递链路完全正确(kolors-ipadapter 一路贯穿),问题仅在 LLM 文字输出层面。修复后验证通过 ### [CL-20260413-1030] 2026-04-13 10:30 — Agent Loop 假生成检测 + 自动重试机制 - **tags**: Agent Loop, DeepSeek, function calling, 防幻觉, 自动修复 - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: 当 LLM 返回纯文字(无 tool_calls)但内容包含"已生成"等模拟生图关键词时,自动注入纠正消息强制 LLM 重新调用工具 - **why**: DeepSeek 即使在 System Prompt 中明确禁止模拟工具调用(CL-0930),仍会在复杂多步任务中用文字"扮演"生图过程,导致前端收不到 image_result 事件。Prompt 约束对 DeepSeek 无效,必须在代码层面硬性拦截 - **decisions**: 用正则匹配"已生成"、"生成完成"、"图片已"等关键词,命中 2 次以上且文本 >= 50 字时判定为假生成。检测到后将 LLM 的回复保留在历史中,追加一条纠正消息"你没有调用工具,请立即调用",然后 continue 回到循环让 LLM 重试。重试消耗一次迭代配额,不影响正常对话 - **notes**: 此机制主要针对 DeepSeek 的 function calling 纪律问题。GPT-4o 等模型通常不会触发。如果 DeepSeek 重试后仍然不调用工具,会在 max_iterations 用尽后正常结束 ### [CL-20260413-1000] 2026-04-13 10:00 — 助手消息气泡添加复制文本按钮 - **tags**: 前端, UX, chat-messages, 复制 - **affected_files**: - art-agent/frontend/src/components/chat/chat-messages.tsx - **what**: 助手回复气泡下方新增复制文本按钮,hover 时显示,点击复制文本内容并显示对勾反馈 - **why**: LLM 生成的 prompt 思路、风格描述等文字内容用户经常需要复制,之前只能手动选中 - **decisions**: 按钮放在气泡下方左侧而非内部,避免干扰阅读。只对 assistant 消息显示(用户自己的消息不需要复制)。使用 navigator.clipboard API,hover 显示 + 1.5s 对勾反馈 - **notes**: 仅复制纯文本内容,不含图片信息 ### [CL-20260413-0930] 2026-04-13 09:30 — System Prompt 加入"禁止模拟工具调用"约束 - **tags**: prompt 工程, system prompt, DeepSeek, function calling, 防幻觉 - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: System Prompt 注意事项中新增"禁止模拟工具调用"硬性约束 - **why**: DeepSeek 在复杂多步任务(如一次生成 3 种不同建筑)中,有时不通过 function calling 调用 generate_image 工具,而是用文字"模拟"生成过程(如"已生成"),导致 Agent Loop 第一轮就因 tool_calls_data 为空而 yield done,前端收不到 image_result 事件,用户看不到图片 - **decisions**: 在 System Prompt 中加一条行为红线,告诉 LLM 必须实际调用工具。轻量级防御(检测文字中包含"已生成"但无工具调用时发警告)暂不实现,先观察 prompt 约束的效果 - **notes**: 这是 DeepSeek function calling 纪律问题,GPT-4o 等模型通常不会出现。如果 prompt 约束不够,后续可加检测机制 ### [CL-20260413-0900] 2026-04-13 09:00 — 非 vision + 参考图场景:尊重用户主动指定的风格意图 - **tags**: prompt 工程, IP-Adapter, 风格, system prompt, UX - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: 将"绝对禁止风格关键词"改为"不自行猜测,但保留用户明确指定的风格" - **why**: 之前的 system prompt 和非 vision hint 中对风格关键词的禁止是绝对性的("绝对不要"、"不要添加任何"),导致 LLM 在用户主动输入了风格描述(如"赛博朋克风"、"水彩风")时也会丢弃用户意图。系统不应替用户做决定 - **decisions**: 措辞从"绝对禁止"改为"不自行猜测,但用户明确指定则保留"。两处同步修改:SYSTEM_PROMPT 第 47 行 + 非 vision 模型的 hint 文本。IP-Adapter 风格迁移与用户文字风格描述可叠加不冲突 - **notes**: 这是对 CL-20260413-0540 策略的修正——当时为了防止 DeepSeek 脑补画风而加了绝对禁止,现在细化为区分 LLM 猜测 vs 用户意图 ### [CL-20260413-0800] 2026-04-13 08:00 — 修复 InstantStyle ReadTimeout + 多次生成丢失参考图 - **tags**: bug修复, Replicate, InstantStyle, timeout, 参考图, Agent Loop, wait, SDK - **affected_files**: - art-agent/backend/app/services/image_gen.py - art-agent/backend/app/agent/loop.py - **what**: 修复 InstantStyle 首次调用 ReadTimeout (61.4s) + 后续调用丢失参考图返回 "No input, Save money" - **why**: 两个运行时日志证实的独立问题:(1) `wait=60` 时 SDK `_create_prediction_timeout` 生成 `httpx.Timeout(5.0, read=60.5)` 作为 per-request timeout 传给 `httpx.AsyncClient.request()`,优先级高于客户端级 300s timeout;(2) 成功生成后执行 `ref_image_url = None`,但 LLM 可能在后续轮次继续调用工具,此时 InstantStyle 因没有 `style_image` 返回 "No input, Save money" - **decisions**: (1) `wait=60` → `wait=False`:不发 `Prefer: wait` header,create 请求立即返回 prediction ID,SDK 自动进入 `prediction.async_wait()` 轮询路径。轮询请求不带 per-request timeout,使用客户端级 `read=300s`。代价:比 `Prefer: wait` 模式多几秒轮询延迟,但绝不会 timeout。(2) 完全移除 `ref_image_url = None`:消息构建(附加参考图到 LLM 消息)在外层循环前一次性完成,不受影响。ref_image_url 只在 `execute_tool()` 中使用,整个对话期间都应该传递 - **notes**: 迭代了 CL-0700 和 CL-0730 的方案。wait 参数完整语义:`True` = `Prefer: wait` + read=60.5s;`int(n)` = `Prefer: wait={n}` + read={n+0.5}s(n 必须 1-60);`False` = 不等待、纯轮询。对慢模型(InstantStyle ~128s)配合大 payload(1.6MB base64),`wait=False` 是唯一可靠选项 ### [CL-20260413-0730] 2026-04-13 07:30 — 修复生图失败后 LLM 重试丢失参考图 - **tags**: bug修复, Agent Loop, 参考图, InstantStyle, ref_image_url - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: 修复 LLM 第一次生图工具调用失败后重试时参考图丢失,InstantStyle 返回 "No input, Save money" - **why**: `loop.py` 在每轮工具调用循环结束后无条件执行 `ref_image_url = None`。当第一次调用超时失败后,LLM 决定重试,但此时 ref_image_url 已被清空,第二次工具调用没有参考图。InstantStyle 模型必须有 `style_image` 才能工作 - **decisions**: 改为条件性清除:在工具调用循环中标记 `tc_data["_had_images"] = True`,循环结束后检查是否有任何成功产出,只有 `any_success` 时才清除 ref_image_url。失败时保留参考图供后续重试 - **notes**: 这个改动对所有参考图模型生效(Kolors、InstantStyle 等)。只要本轮所有工具调用都失败了,ref_image_url 会一直保留直到某次成功为止 ### [CL-20260413-0700] 2026-04-13 07:00 — 修复 InstantStyle 生图 422 + ReadTimeout:wait 参数超限与 SDK 内部 timeout 覆盖 - **tags**: bug修复, Replicate, InstantStyle, API参数, async_run, Prefer header, timeout - **affected_files**: - art-agent/backend/app/services/image_gen.py - **what**: 修复 InstantStyle 生图 422(`Prefer: wait=x must be 1-60`)和后续 ReadTimeout(61.4s 超时) - **why**: 上次修超时时将 `async_run(wait=300)` 设为 300 秒。Replicate SDK 将 int 类型的 wait 值设为 `Prefer: wait=300` header → API 返回 422。改为 `wait=True` 后 SDK 生成内部 `httpx.Timeout(read=60.5)` 作为请求级 timeout 覆盖了客户端级 300s timeout → 上传 1.6MB base64 style_image + 等待响应超过 60s → ReadTimeout - **decisions**: 最终改为 `wait=60`(API 允许的最大 int 值)。SDK 行为:(1) `Prefer: wait=60` header(合法);(2) 内部 timeout `read=60.5s` 用于初始 create prediction 请求;(3) 60s 内未完成自动 fallback 到 `prediction.async_wait()` 轮询;(4) 轮询请求用客户端级 `httpx.Timeout(read=300s)` - **notes**: Replicate SDK wait 参数语义:`True` = `Prefer: wait` + read=60.5s;`int(n)` = `Prefer: wait={n}` + read={n+0.5}s(n 必须 1-60);`False` = 不等待。对慢模型用 `wait=60` + 长客户端 timeout 是最优组合 ### [CL-20260413-0630] 2026-04-13 06:30 — 修复 Replicate SDK 不走代理 + 超时:自定义 Client 注入代理和长超时 - **tags**: bug修复, Replicate, InstantStyle, 代理, httpx, 超时, 图像生成 - **affected_files**: - art-agent/backend/app/services/image_gen.py - **what**: 修复 InstantStyle 调用时 ConnectTimeout / ReadTimeout + "URL missing protocol" 连环错误 - **why**: Replicate Python SDK 的 `_build_httpx_client` 显式传入 `transport=AsyncHTTPTransport()`,导致 httpx 跳过环境变量中的 `HTTPS_PROXY` 代理配置直连 Replicate API。网络不稳定时直连超时,SDK 内部状态异常导致后续请求报 "URL missing protocol"。同时默认 read timeout 30s 不够 InstantStyle 的 ~128s 生成时间 - **decisions**: 创建 `_make_replicate_client()` 工厂函数,从环境变量读取代理并注入到 `AsyncHTTPTransport(proxy=...)` 中传给 `ReplicateClient`。read timeout 300s、connect timeout 30s。`_download_image` 也加了代理和 60s 超时 - **notes**: Replicate SDK 的代理绕过是个已知设计缺陷(显式 transport 覆盖了 httpx 的代理自动检测)。此修复对所有通过 `_replicate_client` 的 Replicate API 调用生效。无代理环境下也兼容(`proxy=None` 时 httpx 不走代理) ### [CL-20260413-0600] 2026-04-13 06:00 — 新增 InstantStyle 模型:强风格迁移选项 - **tags**: 模型注册, InstantStyle, 风格迁移, IP-Adapter, 模型配置 - **affected_files**: - art-agent/backend/app/config.py - **what**: 在模型注册表中新增 InstantStyle(`jyoung105/instant-style`),专门做风格分离迁移,与 Kolors IP-Adapter 并列为两个参考图模型选项 - **why**: Kolors IP-Adapter 的风格迁移弱(内容+风格+构图混合提取),经调研 InstantStyle 是 Replicate 上风格迁移能力最强的可用模型,能从参考图中分离出纯画风(线条、着色、色调) - **decisions**: 利用现有 `supports_ref_image` + `ref_image_param` 抽象机制,InstantStyle 的 `style_image` 参数名通过配置映射,无需修改 `_build_input` 逻辑。默认 `block_mode: "style-only"`(只迁移风格不迁移构图),`style_strength: 1.0` - **notes**: InstantStyle 较贵(~$0.12/次)且慢(~128s),适合风格定调阶段使用;Kolors 保留为低成本快速测试选项。社区模型需版本 hash ### [CL-20260413-0540] 2026-04-13 05:40 — 非 vision LLM + 参考图时禁止猜测风格,避免 prompt 与参考图风格冲突 - **tags**: prompt 工程, IP-Adapter, 风格一致性, system prompt, 非vision模型 - **affected_files**: - art-agent/backend/app/agent/loop.py - **what**: 修改 system prompt 和非 vision 模型的参考图文字提示,明确要求 LLM 不要猜测/指定画风关键词,风格完全交给 IP-Adapter 从参考图提取 - **why**: DeepSeek 等非 vision LLM 看不到参考图内容,会根据"2D 游戏"等关键词脑补风格(如 pixel art),导致 prompt 中的风格描述与参考图实际风格严重冲突。IP-Adapter 优先服从 prompt 的显式风格指令,参考图的风格影响被稀释 - **decisions**: 双重约束——system prompt 中加入"看不到参考图时不猜风格"的通用指引 + 注入的文字提示中用编号列表明确三条规则。保留了 vision 模型路径不变(vision 模型能看图,可以写风格词) - **notes**: 这是过渡方案,等切换到 vision LLM(如 GPT-4o)后,LLM 可直接看图描述风格,此约束自动失效(走 vision_capable 分支) ### [CL-20260413-0520] 2026-04-13 05:20 — 多图生成改为单次 API 调用 + 修复空错误信息 - **tags**: 性能优化, Replicate, 速率限制, 错误处理, 模型配置 - **affected_files**: - art-agent/backend/app/services/image_gen.py - art-agent/backend/app/config.py - **what**: 多图生成从 for 循环多次 API 调用改为单次调用(利用模型原生批量参数),同时修复异常 str() 为空时的错误信息丢失 - **why**: LLM 传 num_images=3 时会发 3 个独立 API 请求,在低余额账户的严格速率限制(burst=1)下后续请求会被 429 拦截。改为单次调用后只消耗 1 次请求配额。空错误信息(`[生成失败: ]`)导致用户无法诊断失败原因 - **decisions**: 新增 `num_images_param` 配置字段(Flux 用 `num_outputs`,Kolors 用 `number_of_images`),由 `_build_input` 统一注入。从 kolors 的 default_params 中移除了硬编码的 `number_of_images: 1`,改为动态设置 - **notes**: 空异常现在显示为 `{TypeName}: {repr(e)}`,确保始终有诊断信息 ### [CL-20260413-0500] 2026-04-13 05:00 — 生成图片后显示使用的模型名称 - **tags**: 模型溯源, SSE, 图片生成, 前端展示 - **affected_files**: - art-agent/backend/app/services/image_gen.py - art-agent/backend/app/agent/tools.py - art-agent/backend/app/agent/loop.py - art-agent/frontend/src/lib/types.ts - art-agent/frontend/src/app/page.tsx - art-agent/frontend/src/components/chat/chat-messages.tsx - **what**: 图片生成后在对话中显示实际使用的模型名称(如"由 Kolors IP-Adapter 生成"),失败时也附带模型名称 - **why**: 用户无法确认前端选择的模型是否真的被用于生图,特别是在多模型切换场景下需要明确溯源 - **decisions**: `generate_images` 返回值从 `list[str]` 改为 `GenerateResult` 数据类(包含 urls + model_name + model_id),模型名称通过 SSE `image_result` 和 `tool_error` 事件透传到前端。ChatMessage 类型新增 `modelName` 可选字段,消息归档后仍可查看模型来源 - **notes**: 模型名称来自 config.py 注册表中的 `name` 字段,所以显示的是友好名称而非 Replicate 的 model_id ### [CL-20260413-0440] 2026-04-13 04:40 — 修复 Kolors IP-Adapter 模型 404 错误:补全 Replicate 版本 hash - **tags**: bug修复, Replicate, Kolors, IP-Adapter, 模型配置 - **affected_files**: - art-agent/backend/app/config.py - **what**: 在 kolors-ipadapter 的 model_id 中补全版本 hash,从 `fofr/kolors-with-ipadapter` 改为 `fofr/kolors-with-ipadapter:5a1a92b2...` - **why**: Replicate SDK `async_run` 对非 official 模型(社区模型)只用 `owner/model` 格式调用时,会尝试 official model predictions 端点,该端点返回 404。必须使用 `owner/model:version_hash` 格式才能正确创建 prediction - **decisions**: 只修复 kolors 模型(唯一报错的),其他三个模型(flux-schnell、flux-dev、sdxl)均为 official/热门模型,`owner/model` 格式能正常工作 - **notes**: 未来新增社区模型时,需确保 model_id 包含版本 hash。经实测加版本 hash 后生图成功 ### [CL-20260413-0430] 2026-04-13 04:30 — 图片生成失败时将具体错误信息透传到前端 - **tags**: 错误处理, Agent Loop, SSE, 调试体验 - **affected_files**: - art-agent/backend/app/agent/loop.py - art-agent/frontend/src/app/page.tsx - art-agent/frontend/src/lib/api.ts - **what**: 新增 `tool_error` SSE 事件类型,将图片生成工具的具体错误信息(如 Replicate API 报错详情)直接透传到前端展示 - **why**: 之前工具调用失败时,错误信息只传回给 LLM,LLM 会自行"翻译"错误(如说成"速率限制"),用户无法看到真实报错,排查困难。典型场景:使用 Kolors IP-Adapter 生图失败,前端只看到 LLM 的模糊描述,无法定位是参数错误、超时还是 API 限制 - **decisions**: 错误信息同时发给前端展示和 LLM 继续对话,不互斥。前端用 ⚠️ 前缀醒目显示错误详情 - **notes**: 此改动不影响 LLM 的重试机制(工具结果仍然回传给 LLM),只是额外增加了一条面向用户的 SSE 事件 ### [CL-20260413-0330] 2026-04-13 03:30 — README 补充访问地址汇总 + 穿透管理文档 - **tags**: 文档, README, 穿透, Cloudflare, cloudflared - **affected_files**: - art-agent/README.md (新增"访问地址"章节 + 重写"远程访问"章节) - **what**: README 新增三种访问场景(本机/局域网/外网)的地址汇总表,重写远程访问章节补充 cloudflared 安装指引、穿透管理操作表、代理配置排错 - **why**: 原 README 远程访问章节过于简略,缺少 cloudflared 安装方式、穿透的启动/停止/排错说明,用户实际操作时需要这些信息 - **decisions**: - 访问地址章节放在"使用"之后、"配置说明"之前,让用户启动后立即看到访问方式 - cloudflared 安装提供 winget 和手动下载两种方式(winget 安装后可能不在 PATH 中) - 穿透管理用表格呈现(启动/停止/查看状态/重启),简洁直观 - 代理排错放在注意事项中,因为国内环境常需代理才能连上 Cloudflare - **notes**: 无 ### [CL-20260412-2345] 2026-04-12 23:45 — 参考图上传独立化:进度条 + 状态反馈 + 失败重试 - **tags**: 前端, 后端, 参考图, 上传, UX, 进度反馈 - **affected_files**: - art-agent/backend/app/api/chat.py (新增 POST /api/upload-ref-image 独立上传端点,chat 端点新增 ref_image_url 字段) - art-agent/frontend/src/lib/api.ts (新增 uploadRefImage 函数,XMLHttpRequest 实现进度回调;sendChat 参数从 File 改为服务端路径字符串) - art-agent/frontend/src/components/chat/chat-input.tsx (重写上传流程:选图即上传、环形进度条、状态角标、失败重试、input value 重置) - art-agent/frontend/src/app/page.tsx (handleSend 适配新签名:接收服务端路径而非 File;标注截图也走独立上传) - **what**: 将参考图从"随消息一起提交"改为"选中后立即独立上传",全程有进度条、状态提示、失败重试入口 - **why**: 原方案图片上传绑定在发送消息时才执行,无任何进度反馈。大图或走 Cloudflare Tunnel 时用户完全无法判断上传是否开始、是否成功、是否卡住。多次上传参考图后 file input 不重置也导致交互异常 - **decisions**: 使用 XMLHttpRequest 而非 fetch(fetch 不支持 upload progress 事件);后端同时保留 ref_image 文件上传和 ref_image_url 路径两种方式,向后兼容 - **notes**: 上传超时设为 120 秒;支持 AbortController 取消;file input 每次选择后立即重置 value,确保同一文件可重复选择 ### [CL-20260412-2315] 2026-04-12 23:15 — 修复 Cloudflare Tunnel 跨域开发警告 - **tags**: 前端, 配置, Next.js, Cloudflare, 跨域 - **affected_files**: - art-agent/frontend/next.config.ts (新增 allowedDevOrigins) - **what**: 在 next.config.ts 中添加 `allowedDevOrigins: ["*.trycloudflare.com"]`,消除 Cloudflare Tunnel 穿透时的跨域警告 - **why**: Next.js 检测到来自 trycloudflare.com 的跨域请求,发出警告提示未来版本将强制要求配置 - **decisions**: 使用通配符 `*.trycloudflare.com` 覆盖所有 Quick Tunnel 随机域名 - **notes**: 仅影响开发模式,生产构建不受影响 ### [CL-20260412-2300] 2026-04-12 23:00 — 集成 Kolors IP-Adapter 模型,参考图可直接用于风格生成 - **tags**: 后端, 前端, IP-Adapter, 风格迁移, 参考图, 模型注册, Replicate, 架构 - **affected_files**: - art-agent/backend/app/config.py (新增 kolors-ipadapter 模型注册 + supports_ref_image / ref_image_param 通用字段) - art-agent/backend/app/services/image_gen.py (ReplicateProvider._build_input 支持参考图注入 + to_data_uri 公共工具函数) - art-agent/backend/app/agent/loop.py (复用 to_data_uri 替代原 _to_vision_url,移除冗余 import) - art-agent/frontend/src/lib/types.ts (ImageModelInfo 新增 supports_ref_image 字段) - art-agent/frontend/src/components/chat/model-selector.tsx (模型列表中显示"参考图"标记) - **what**: 集成 Kolors IP-Adapter 模型(fofr/kolors-with-ipadapter),参考图可真正参与图像生成的风格控制;架构上为后续切换其他 IP-Adapter 模型预留了通用扩展机制 - **why**: 之前参考图仅由 LLM 侧理解(且 DeepSeek 看不到图),生图工具侧完全忽略了参考图。用户希望参考图能真正影响生成结果的风格 - **decisions**: - 模型注册表新增两个通用字段:supports_ref_image(布尔标记)和 ref_image_param(参考图参数名,不同模型可能不同) - _build_input 根据 supports_ref_image 自动走不同构建逻辑,新增 IP-Adapter 模型只需加注册表配置 - 参考图通过 base64 data URI 传递给 Replicate(避免本地 URL 不可达的问题) - to_data_uri 抽为 image_gen.py 中的公共函数,loop.py 复用(替代原 _to_vision_url) - Kolors IP-Adapter 默认 ip_adapter_weight=0.8(非 1.0),给 prompt 文字留一些影响空间 - 前端模型列表中为支持参考图的模型显示绿色"参考图"徽章 - get_image_models_list 返回 supports_ref_image 字段,前端据此渲染标记 - **notes**: - Kolors IP-Adapter 成本极低(~$0.004/次),速度快(~4s) - 后续要换其他 IP-Adapter 模型(如 Flux IP-Adapter v2),只需在 IMAGE_MODELS 中新增配置 - 分层组合产线方案(IP-Adapter → LoRA → ControlNet)已记录为延期方案 [style-pipeline-layered] ### [CL-20260412-2230] 2026-04-12 22:30 — 用户消息气泡中显示参考图缩略图 - **tags**: 前端, 参考图, UX, chat-messages - **affected_files**: - art-agent/frontend/src/lib/types.ts (ChatMessage 新增 refImageUrl 字段) - art-agent/frontend/src/app/page.tsx (构造 userMessage 时保存参考图 Blob URL) - art-agent/frontend/src/components/chat/chat-messages.tsx (用户消息气泡中渲染参考图缩略图) - **what**: 用户上传参考图发送后,在聊天气泡中显示参考图缩略图,让用户确认图片确实被附带发送了 - **why**: 之前用户上传参考图后,发送消息后气泡中看不到参考图,无法确认是否上传成功,体验不好 - **decisions**: - 使用 URL.createObjectURL 创建 Blob URL 用于当前会话内显示(轻量、即时) - Blob URL 不持久化到 localStorage(刷新后失效),避免存储 base64 data URI 的空间开销 - 参考图缩略图显示在消息文本上方,最大尺寸 160×120px - **notes**: - Blob URL 仅在当前标签页有效,刷新页面后旧消息中的参考图缩略图不可见(MVP 可接受) - 如需持久化,后续可改为存储后端上传路径(需要等后端返回 upload URL 后再构造 userMessage) ### [CL-20260412-2200] 2026-04-12 22:00 — 修复 DeepSeek 不支持 image_url 导致参考图上传报错 - **tags**: bug修复, 后端, Agent Loop, vision, DeepSeek, 多模态 - **affected_files**: - art-agent/backend/app/agent/loop.py (新增 vision 能力检测,非 vision 模型走文字提示) - **what**: 修复使用 DeepSeek 作为 LLM 时,上传参考图后报 400 错误 `unknown variant 'image_url', expected 'text'` - **why**: 代码原本假设所有 LLM 都支持 OpenAI Vision API 的 `image_url` content type,但 DeepSeek 的 Chat API 只支持 `text` 类型 - **decisions**: - 通过模型名称关键词检测 vision 能力(gpt-4o / gpt-4-vision / claude 视为支持) - 不支持 vision 的模型:用文字提示告知 LLM 有参考图存在,参考图仍由图像生成工具侧处理 - 支持 vision 的模型:保持原有 image_url 格式不变 - **notes**: - 此方案确保参考图功能在任何 LLM 下都不会报错 - 非 vision 模型无法"看到"参考图,但生图工具仍会收到参考图用于 img2img 场景 - 如需更精确的 vision 能力检测,可后续改为在 config.py 中按模型注册 vision 标志 ### [CL-20260412-2130] 2026-04-12 21:30 — 修复图片生成失败时前端显示破碎图标 - **tags**: bug修复, 前端, 后端, 图像生成, 错误处理 - **affected_files**: - art-agent/backend/app/agent/tools.py (分离有效 URL 和错误信息) - art-agent/frontend/src/app/page.tsx (过滤无效 URL,空列表时不渲染图片网格) - **what**: 修复 Replicate API 失败时前端出现 4 个破碎图标框的问题 - **why**: 用户在 Replicate 余额不足(402)时生图,错误字符串被当作图片 URL 传给 `` 标签 - **decisions**: - 后端 tools.py:将 generate_images 返回值分为 images(有效 URL)和 errors(错误信息)两个字段 - 前端 page.tsx:过滤以 `[` 开头的无效 URL,无有效图片时直接 break 不创建 ImageAsset - **notes**: - 根因是 Replicate 账户余额不足,需用户到 https://replicate.com/account/billing#billing 充值 - 此修复确保任何 API 错误场景下前端都不会显示破碎图标 ### [CL-20260412-2100] 2026-04-12 21:00 — 生图模型动态切换:Provider 抽象 + 前端模型选择器 - **tags**: 后端, 前端, 图像生成, 模型切换, Provider, 重构, Replicate - **affected_files**: - art-agent/backend/app/config.py (新增 IMAGE_MODELS 注册表 + get_image_model_config / get_image_models_list / get_default_image_model_id) - art-agent/backend/app/services/image_gen.py (重构:ImageProvider 抽象基类 + ReplicateProvider 实现 + generate_images 接受 model_id) - art-agent/backend/app/api/chat.py (新增 GET /api/models 端点 + POST /api/chat 增加 image_model 参数) - art-agent/backend/app/agent/loop.py (run_agent_loop 增加 image_model 参数透传) - art-agent/backend/app/agent/tools.py (execute_tool 增加 image_model 参数透传) - art-agent/backend/.env (IMAGE_MODEL 改为短 ID 格式) - art-agent/backend/.env.example (同步更新) - art-agent/frontend/src/lib/types.ts (新增 ImageModelInfo 类型) - art-agent/frontend/src/lib/api.ts (新增 fetchModels() + sendChat 增加 imageModel 参数) - art-agent/frontend/src/components/chat/model-selector.tsx (新建:模型选择下拉组件) - art-agent/frontend/src/components/chat/chat-input.tsx (集成 ModelSelector + onSend 签名扩展) - art-agent/frontend/src/app/page.tsx (handleSend 透传 imageModel 到 sendChat) - **what**: 将图像生成服务从 Replicate 硬绑定重构为 Provider 抽象架构,支持 Replicate 上多模型(flux-schnell / flux-dev / SDXL)按消息粒度切换 - **why**: 用户希望能在对话中随时切换生图模型来对比不同模型的效果 - **decisions**: - Provider 模式:抽象基类 ImageProvider + 具体实现(当前只有 ReplicateProvider),预留 DalleProvider/ComfyUIProvider 等扩展点 - 模型注册表放在 config.py 中(Python dict),新增模型只需加一条配置 - 前端模型列表由后端 API 驱动(GET /api/models),不在前端硬编码 - 切换粒度为每条消息级(最灵活),通过 FormData 传递 image_model 参数 - 模型选择持久化到 localStorage,作为后续消息的默认值 - Flux 系列使用 aspect_ratio 参数,SDXL 使用 width/height 参数,由 ReplicateProvider._build_input 内部分流处理 - **notes**: - 向后兼容:image_model 参数可选,不传时使用 .env 中的默认模型 - .env 中 IMAGE_MODEL 支持短 ID(flux-schnell)和完整 Replicate ID(black-forest-labs/flux-schnell),自动转换 ### [CL-20260412-1800] 2026-04-12 18:00 — 移动端适配 + Cloudflare Tunnel 内网穿透 - **tags**: 前端, 响应式, 移动端, 部署, Cloudflare, 穿透, 配置 - **affected_files**: - art-agent/frontend/src/app/layout.tsx (viewport meta 配置) - art-agent/frontend/src/app/globals.css (iOS bounce 防护、tap highlight、dvh 支持、遮罩动画) - art-agent/frontend/src/app/page.tsx (桌面端展开按钮加 hidden md:flex) - art-agent/frontend/src/app/gallery/page.tsx (网格 gap/列数适配、hover 操作移动端始终可见、选择框可见) - art-agent/frontend/src/components/sidebar/sidebar.tsx (移动端固定定位抽屉 + 遮罩) - art-agent/frontend/src/components/layout/top-nav.tsx (新增汉堡菜单按钮、间距微调) - art-agent/frontend/src/components/detail/image-detail-panel.tsx (移动端全屏 overlay) - art-agent/frontend/src/components/chat/chat-messages.tsx (气泡宽度 90%、间距收紧) - art-agent/frontend/src/components/chat/chat-input.tsx (padding 收紧) - art-agent/frontend/src/components/chat/image-grid.tsx (max-width 适配、操作栏移动端可见) - art-agent/frontend/src/lib/app-context.tsx (小屏默认折叠侧边栏、切换会话自动收起) - art-agent/frontend/next.config.ts (trycloudflare.com + replicate.delivery 白名单) - art-agent/start-tunnel.ps1 (新建:一键穿透脚本) - art-agent/README.md (新增远程访问章节、uvicorn 加 --host 0.0.0.0) - **what**: 为实现手机浏览器远程访问,完成移动端响应式适配(9 个组件)和 Cloudflare Quick Tunnel 穿透方案 - **why**: 用户希望把项目发给自己和朋友在手机上通过浏览器使用 - **decisions**: - 移动端断点统一用 `md:` (768px),与 Tailwind 默认保持一致 - 侧边栏移动端用 fixed + overlay 抽屉模式,桌面端保持原有 w-0/w-[280px] 切换 - 图片详情面板移动端全屏(fixed inset-0),不做半屏,因为手机屏幕空间有限 - 穿透方案选 Cloudflare Quick Tunnel(免费、免注册),不选 ngrok(有连接限制) - 穿透脚本自动管理 .env.local 的更新和恢复,减少手动操作 - hover 操作在移动端始终显示(无 hover 事件),避免操作不可达 - 路线 B(云服务器正式部署)记录为延期方案,待后续实施 - **notes**: - 构建验证通过,无 lint 错误 - cloudflared 通过 winget 安装,版本 2025.8.1 - Quick Tunnel 每次启动分配随机域名(xxx.trycloudflare.com),非固定 - 穿透启动后需重启前端才能读取新的 NEXT_PUBLIC_API_URL - project-launcher Skill 中的 uvicorn 命令也同步加了 --host 0.0.0.0 ### [CL-20260412-1530] 2026-04-12 15:30 — 修复 .env 配置加载时序问题 - **tags**: 后端, bug修复, 配置, dotenv, Python - **affected_files**: - art-agent/backend/app/main.py (load_dotenv 前置 + override=True) - art-agent/backend/app/config.py (模块级常量 → 函数式懒读取) - art-agent/backend/app/agent/loop.py (引用改为函数调用) - art-agent/backend/app/services/image_gen.py (引用改为函数调用) - art-agent/backend/.env (OPENAI_BASE_URL 去掉 /v1) - **what**: 修复切换 DeepSeek API 后报 "Model Not Exist" 400 错误 - **why**: 用户修改 .env 切换到 DeepSeek,但实际发出的请求仍使用默认模型名 gpt-4o-mini - **decisions**: - load_dotenv(override=True) 放在 main.py 所有业务 import 之前,确保环境变量在任何模块读取前就位 - config.py 从模块级常量改为函数(get_llm_model() 等),避免 import 时求值被固化 - 不使用 lru_cache(避免首次调用在 load_dotenv 前就缓存了空值) - **notes**: - 根因有两层:import 时序(config.py 在 load_dotenv 前被 import)+ override 默认行为(不覆盖已有环境变量) - 通过在 uvicorn 同环境中运行 Python 脚本成功复现了 API 调通,证明问题在进程内加载顺序而非 API 本身 - DeepSeek base_url 使用 https://api.deepseek.com(不带 /v1),与官方文档一致 ### [CL-20260412-1500] 2026-04-12 15:00 — 品牌重命名 EPEEKit + API 配置集中化 - **tags**: EPEEKit, 品牌, 配置, 后端, 前端, 重构 - **affected_files**: - art-agent/frontend/src/app/layout.tsx (title/description) - art-agent/frontend/src/components/layout/top-nav.tsx (Logo SVG + 品牌名) - art-agent/frontend/src/app/page.tsx (欢迎文案) - art-agent/frontend/src/lib/store.ts (localStorage 键名) - art-agent/frontend/src/app/gallery/page.tsx (下载前缀) - art-agent/frontend/src/components/detail/image-detail-panel.tsx (下载前缀) - art-agent/frontend/src/components/chat/image-grid.tsx (下载前缀) - art-agent/frontend/package.json (项目名) - art-agent/frontend/package-lock.json (项目名) - art-agent/backend/app/main.py (FastAPI title) - art-agent/backend/app/config.py (新建:集中配置) - art-agent/backend/app/agent/loop.py (引用集中配置) - art-agent/backend/app/services/image_gen.py (引用集中配置 + 简化重复分支) - art-agent/backend/.env (重组为分组结构 + 新增 LLM_MODEL/IMAGE_MODEL 等) - art-agent/backend/.env.example (新建) - art-agent/README.md (全文更新) - **what**: 将应用从 "Art Agent" 正式命名为 "EPEEKit",同时将后端所有硬编码的 API/模型配置抽取到 .env 文件,通过 config.py 集中管理 - **why**: 用户要求正式命名应用并希望能方便地切换 LLM 和图像生成模型进行测试 - **decisions**: - 品牌名全部统一为 "EPEEKit",包括页面标题、Logo、下载文件名、localStorage 键名 - Logo 采用内联 SVG 设计(主题色圆角方块 + A/i 组合),不引入外部图片资源 - API 配置走 .env + config.py 方案(非 settings.py 类模式),保持极简 - 切换 LLM 只需改 .env 中 3 个值:OPENAI_BASE_URL + OPENAI_API_KEY + LLM_MODEL - image_gen.py 中去除了参考图分支的重复代码(两个分支逻辑完全相同) - **notes**: - localStorage 键名变更意味着旧数据不会自动迁移(MVP 阶段可接受) - .env 文件中含真实密钥,需确认 .gitignore 覆盖 ### [CL-20260412-0300] 2026-04-12 03:00 — 完成交互原型全面重构 - **tags**: art-agent, 前端, 交互设计, 三栏布局, 标签系统, Canvas标注, 资源库, 后端 - **affected_files**: - art-agent/frontend/src/app/page.tsx (重写) - art-agent/frontend/src/app/layout.tsx (添加 AppProvider) - art-agent/frontend/src/app/globals.css (增加样式) - art-agent/frontend/src/app/gallery/page.tsx (新建) - art-agent/frontend/src/lib/types.ts (新建:全局类型定义) - art-agent/frontend/src/lib/store.ts (新建:localStorage 持久化存储) - art-agent/frontend/src/lib/app-context.tsx (新建:全局状态 Context) - art-agent/frontend/src/lib/api.ts (更新类型兼容) - art-agent/frontend/src/components/layout/top-nav.tsx (新建) - art-agent/frontend/src/components/sidebar/sidebar.tsx (新建) - art-agent/frontend/src/components/sidebar/session-list.tsx (新建) - art-agent/frontend/src/components/sidebar/tag-filter.tsx (新建) - art-agent/frontend/src/components/chat/chat-messages.tsx (适配新类型) - art-agent/frontend/src/components/chat/chat-input.tsx (增加拖拽上传) - art-agent/frontend/src/components/chat/image-grid.tsx (增加操作栏) - art-agent/frontend/src/components/detail/image-detail-panel.tsx (新建) - art-agent/frontend/src/components/detail/annotation-canvas.tsx (新建) - art-agent/backend/app/agent/loop.py (System Prompt + base64 转换) - **what**: 从单页对话 MVP 全面重构为完整交互原型,三个 Phase 一次性完成 - **why**: 用户要求完整设计交互原型,通过规划讨论确定了三栏布局 + 标签系统 + 标注 + 资源库的完整方案 - **decisions**: - 数据持久化使用 localStorage(MVP 阶段),后续可迁移到后端数据库 - 全局状态管理使用 React Context + useReducer 模式,不引入第三方状态库 - 标注功能使用原生 Canvas API,不引入第三方画板库(减少依赖) - 标注坐标使用归一化比例(0-1),与图片实际分辨率解耦 - 资源库使用 Modal overlay 展示详情,而非独立路由页面 - 标注截图通过 base64 data URI 直接传给 OpenAI Vision,后端自动处理本地路径转换 - **notes**: - 新增 12 个源文件,修改 6 个现有文件,无新依赖引入 - 内置标签 6 个:UI、Icon、原画、风格探索、立绘、概念图 - 标注工具 4 种:矩形框选、箭头、自由画笔、文字,含撤销/清除 - 构建验证通过,无 linter 错误 - 未来可优化:标签自动推荐逻辑(当前仅预留接口)、后端会话持久化、图片搜索索引 ### [CL-20260411-1500] 2026-04-11 15:00 — 完成美术 Agent 产品决策和技术选型 - **tags**: art-agent, 产品决策, 技术选型, 规划 - **affected_files**: - docs/art-agent/DECISIONS.md - docs/art-agent/TECH-STACK.md - **what**: 通过 5 轮结构化讨论确定了美术 Agent 工具的产品决策(产品形态、交互模型、AI 架构、风格管理、资源 Pipeline),并完成全部 8 个维度的技术选型 - **why**: 项目从零开始,需要先把产品方向和技术基准定下来再动手写代码 - **decisions**: - 产品形态选 Chat-first Web App(排除纯 Bot 和桌面应用) - 前端 Next.js / 后端 Python FastAPI / 自建 Agent Loop(不用 LangChain) - 数据库 PostgreSQL / 图像生成 Replicate / 部署 Vercel + Railway - 对象存储 MVP 先用本地文件系统 - **notes**: 技术选型中图像生成 API、部署方案、异步任务三项用户未明确选定,采用了推荐方案 ### [CL-20260411-1600] 2026-04-11 16:00 — 搭建美术 Agent MVP 全部前后端代码 - **tags**: art-agent, MVP, 前端, 后端, FastAPI, Next.js, Agent Loop - **affected_files**: - art-agent/README.md - art-agent/backend/requirements.txt - art-agent/backend/.env.example - art-agent/backend/app/main.py - art-agent/backend/app/api/chat.py - art-agent/backend/app/agent/loop.py - art-agent/backend/app/agent/tools.py - art-agent/backend/app/services/image_gen.py - art-agent/frontend/package.json - art-agent/frontend/src/app/page.tsx - art-agent/frontend/src/app/layout.tsx - art-agent/frontend/src/app/globals.css - art-agent/frontend/src/components/chat/chat-messages.tsx - art-agent/frontend/src/components/chat/chat-input.tsx - art-agent/frontend/src/components/chat/image-grid.tsx - art-agent/frontend/src/lib/api.ts - docs/art-agent/MVP-PLAN.md - **what**: 从零创建了完整的 MVP 前后端代码,包括 FastAPI 后端(Agent Loop + Replicate 图像生成 + SSE 流式推送)和 Next.js 前端(Chat UI + 参考图上传 + 图片下载),后端已验证可正常启动 - **why**: 以最小流程跑通端到端闭环(对话 → 生图 → 迭代 → 保存),快速暴露集成问题 - **decisions**: - MVP 范围刻意砍掉:数据库/持久化、Skill/Rules 机制、风格库、用户认证、云端部署 - Agent Loop 硬编码 system prompt,不走 Skill 扩展(最简化) - 图像模型选 flux-schnell(快速版),优先验证流程通畅 - OpenAI 客户端延迟初始化,避免无 Key 时模块加载失败 - **notes**: - 前端使用 Tailwind CSS v4 + PostCSS 配置方式(非 tailwind.config.ts) - 参考图在 MVP 阶段仅通过 GPT vision 理解风格后融入 prompt,未直接传给图像模型做 img2img ### [CL-20260411-1630] 2026-04-11 16:30 — 完成开发环境搭建和依赖安装 - **tags**: art-agent, 环境搭建, Node.js, Python - **affected_files**: - art-agent/backend/venv/ - art-agent/frontend/node_modules/ - **what**: 通过 winget 安装 Node.js v24.14.1,创建 Python 虚拟环境并安装后端依赖(9 个包),安装前端 npm 依赖(46 个包),后端启动验证通过 - **why**: 代码写好后需要实际运行环境来验证 - **decisions**: - Node.js 用 winget 安装 LTS 版本 - Python 虚拟环境放在 backend/venv/ 下 - **notes**: - 遇到 3 个环境问题已解决:OpenAI 延迟初始化、PowerShell 不支持 &&、脚本执行策略限制 - 端到端完整测试待用户配置 API Key 后进行