118 KiB
118 KiB
Dev Changelog — Full
完整的开发改动记录,按时间倒序排列。作为主动 RAG 的数据源,用户手动唤醒时读取。
记录
[CL-20260420-1800] 2026-04-20 18:00 — Mesh Pipeline Phase 3 风格还原接入(IP-Adapter + ControlNet)
- tags: 视角变换, Mesh Pipeline, 风格还原, IP-Adapter, ControlNet, Replicate, style_restorer, 后端, services, config, Phase 3
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/app/services/style_restorer.py (新增)
- art-agent/backend/app/services/view_transform.py
- art-agent/backend/scripts/test_style_restore.py (新增)
- docs/art-agent/VIEW-TRANSFORM-MESH-PIPELINE-PLAN.md
- what: 按 SPEC §4 Phase 3 落地 Mesh Pipeline 的阶段 3 — 对 Stage 2 抽出的 normal 帧做 IP-Adapter + ControlNet 重绘,用用户原图做风格参考,输出保留原画风且视角正确的最终图。
- why: Phase 2 已能稳定产出 color/normal 6 视角帧,但 Trellis 的 color 帧是"半写实"纹理风,与用户上传的卡通描边原图风格差异明显。Phase 3 用
chigozienri/ip_adapter-sdxl-controlnet-depth(版本0436c870...,$0.07/张 ~72s,L40S):image字段传原图承担 IP-Adapter 风格参考,controlnet_input传 normal 帧作为结构约束的 depth 近似(按 SPEC §1.4 一期走"路 A"简化方案,效果不足再升级为depth-anything-v2真 depth)。 - decisions:
- 新模型以
internal=True注册进IMAGE_MODELS(而非VIEW_TRANSFORM_MODELS),理由:它是"普通文生/图生图"模型,只是用在内部流水线;同步改get_image_models_list()过滤internal,避免它出现在前端生图模型下拉 - 暴露
get_style_restore_model_id()helper 让 service 层不硬编码短 ID restore_batch()用asyncio.Semaphore(3)控制并发而非asyncio.gather全冲 —— SPEC §5 风险表提示过 Replicate rate limit 风险- 默认参数选
scale=0.75/controlnet_conditioning_scale=0.8(SPEC §2.3 建议范围 0.7-0.8 / 0.7-0.9 的中位偏上),最终值由 P3-4 调参确认 - 单帧失败不阻塞整体:Stage 3 任一帧失败时自动回退为 Stage 2 的 color 帧返回,前端至少能看到东西;并在该 image 上挂
restyle_error字段暴露失败原因。这一兜底 + "Stage 2 失败不影响 Stage 1 交付" 形成一致的"阶段独立可交付"设计
- 新模型以
- notes:
- 成本:一次完整 6 视角请求 ~$0.46(Trellis $0.041 + 6 × IP-Adapter $0.07 = $0.461),耗时 ~105s(30s 重建 + 并发 3 两轮 × 72s ≈ 75s 风格化)
images[i]新结构:新增color_url(保留 Stage 2 原图便于前端对比/切换)、restyled布尔、可选restyle_error;返回体_stage从stage2升级为stage3 (N/M restyled)_note字段去掉了,错误通过error字段聚合(拼接 Stage 2 + Stage 3 错误);_preserve_style_requested保留供调试- Checkpoint 3 脚本
scripts/test_style_restore.py支持--single冒烟($0.07)和全量($0.42),以及--scale/--cn-scale临时覆盖参数,便于 P3-4 调参;默认自动挑 generated/ 下最新一组view_<prefix>_az*_el0_normal.png作为结构参考 - P3-4 未完成(等用户用真实原图跑调参);P4 前端联调将在 Checkpoint 3 通过后开启
- source_chat: 继续执行 Mesh Pipeline Phase 3
[CL-20260420-1700] 2026-04-20 17:00 — Mesh Pipeline Phase 2 抽帧 + combined_video 左右切分落地
- tags: 视角变换, Mesh Pipeline, Trellis, combined_video, ffmpeg, imageio-ffmpeg, PIL, 抽帧, 后端, services, Phase 2
- affected_files:
- art-agent/backend/app/services/video_frame_extractor.py (新增)
- art-agent/backend/app/services/view_transform.py
- art-agent/backend/requirements.txt
- art-agent/backend/scripts/test_frame_extract.py (新增)
- docs/art-agent/VIEW-TRANSFORM-MESH-PIPELINE-PLAN.md
- what: 按 SPEC §4 Phase 2 清单落地。(1) 确认宿主机
ffmpeg未安装,改用imageio-ffmpeg==0.5.1自带的 Windows 静态二进制(ffmpeg-win64-v4.2.2.exe,随 wheel 一起打包 22.6MB),免系统依赖;venv 已装,requirements.txt同步写入。(2) 新增services/video_frame_extractor.py实现extract_by_azimuth(combined_video_path, azimuths, elevations=None, total_rotation_deg=360):核心策略采用"一次性把 combined_video 所有帧抽到generated/_frames_<id>/临时目录 + 按frame_idx = round(az / total_rotation_deg * total_frames) % total_frames挑帧 + PIL 按中线切左半→color、右半→normal 保存到generated/view_<batch>_az{az}_el{el}_{color|normal}.png",比逐帧用-ss抽更稳(不受 keyframe 对齐影响),5s 视频整体成本可忽略;用完即清理临时目录,只保留切好的最终帧。(3) ffmpeg + PIL 是阻塞操作,用asyncio.get_running_loop().run_in_executor(None, _do_extract, ...)丢到默认线程池跑,避免堵住 FastAPI 事件循环。(4)services/view_transform.py:_transform_view_mesh在 Stage 1 成功后串接 Stage 2——默认azimuths=[0,60,120,180,240,300](从模块级常量DEFAULT_MESH_AZIMUTHS读取),elevations缺省或长度不足补 0、超长截断,保证与 azimuths 对齐;调用video_frame_extractor.extract_by_azimuth(combined_video_path=stage1["combined_video_path"], ...)得到frames[{az, el, frame_idx, color_png, normal_png}];把images填为[{url: color_png, azimuth, elevation, normal_url: normal_png, frame_idx}];Stage 2 失败不把整体置为失败(Stage 1 的 mesh/video 仍有交付价值),单独在返回 dict 的error字段带回 Stage 2 错误信息,success同时要求 stage1 成功 + Stage 2 无 error。(5) 新增scripts/test_frame_extract.py供用户 Checkpoint 2 验收:默认取generated/下最新的trellis_combined_*.mp4做输入(不需要再花钱跑 Trellis),调用video_frame_extractor.extract_by_azimuth抽 6 视角,打印每帧的color_png/normal_pngURL + 本地文件大小,并提示用户目视检查。(6) SPEC 文档 P2-1 ~ P2-4 全部打勾、追加本次会话的进度 note(含 Agent 自测结论)。 - why: SPEC §4 "每个 Phase 完成后停下来让用户测试/审阅",Phase 2 是用户能看到"可见结果"的第一步——Phase 1 只能交付一个 .glb + 一个 mp4 视频,用户很难一眼判断是否正确;Phase 2 交付 6 张方位角明确的方形图后,用户可以直接目视环绕教堂一周。抽帧方案故意选"一次抽全 + 按索引挑"而不是"每帧一次 -ss 快进",是因为:(a) combined_video 约 5s/120 帧,总 PIL+ffmpeg 开销 ~1-2s,比 6 次 -ss (每次 ffmpeg 启动 ~300-500ms) 更快;(b)
-ss在 GOP 长的 mp4 上会对齐到最近 keyframe,取帧索引可能不精确;(c) 代码简单、debug 时看临时目录文件序号就能对应帧。Stage 2 失败不牵连 Stage 1,是考虑到"抽帧失败"通常意味着 ffmpeg 或视频本身异常,但用户可能仍想拿 .glb 做手动检视——与其整体 fail,不如降级返回部分产物 + 明确 error。测试脚本用已有 combined_video 是故意为之:Checkpoint 2 的重点是验证抽帧+切图的正确性,不是再验证一遍 Trellis,没理由让用户多花 $0.041。 - decisions: (1) ffmpeg 方案:首选
imageio-ffmpeg而不是让用户choco install ffmpeg,因为 SPEC §5 风险栏就列了 "Windows ffmpeg 未装" 是高概率风险,imageio-ffmpeg 的 pip wheel 自带 Win64 二进制是零摩擦方案;代价是 wheel 22.6MB + 每次 Python 启动查找二进制(成本可忽略)。(2) 返回 schema:给 mesh 管道的images[i]加了normal_url和frame_idx字段而不是单独的frames列表——这样前端不用改 schema 就能显示(url仍是最终要展示的彩色图),同时 Phase 3 可以直接读normal_url作为controlnet_input。(3) elevation 字段保留但忽略:combined_video 本身是水平平视一圈,elevation != 0在 Phase 2 无意义(所有帧 el 都是 0),但保留字段为 Phase 5 自由相机路径预留;当前传elevations=[30,30,...]也不会报错,只是结果图的 el 字段被原样记录、帧内容仍是水平视角(有心理预期即可,未来 renderer 路径会真正使用)。(4) 临时目录策略:抽帧到generated/_frames_<8位hex>/而不是系统临时目录,是因为后者在 Windows 跨盘符时可能慢;前者失败时也留现场方便 debug。完成后的清理用try/except OSError兜底,即使 PIL 占着句柄也不会报错退出。(5) DEFAULT_MESH_AZIMUTHS 提到模块级:下 Phase 4 前端会暴露"快速预设 6/8/12 视角"选项,这里先把默认值拎出来便于配置扩展;目前命名为 6 视角,Phase 4 再考虑增加 8/12。 - notes: (1) Stage 2 路径依赖 stage1 的
combined_video_path键名(非color_video_path)——这是 Trellis 部署版e8f6c452...的实测事实(PF-20260420-1600),不要以为 Phase 1 的字段color_video_path有值;现在该字段仍然返回但恒为None,_transform_view_mesh里优先读combined_video_path。(2) 前端目前仍不会自动展示 mesh 管道结果 ——loop.py只在result.get("images")非空时发image_result事件,Phase 2 让 images 有值了,所以前端应该能看到 6 张;但 Phase 4 才正式端到端联调,Phase 2 的建议验收入口仍是scripts/test_frame_extract.py而不是 UI。(3) normal 帧的 url 在前端目前是"隐藏字段"——images[i].normal_url前端 types.ts 里没这个字段,会被丢弃;Phase 3 接入时 view_transform 会改为把重绘后的图填到url,normal 帧继续作为中间产物留在磁盘上。(4) 预留的preserve_style参数当前不生效,返回 dict 的_preserve_style_requested字段记录了用户意图——Phase 3 会根据这个开关分流(True 走 ControlNet 重绘、False 直出 color 帧)。(5) Agent 自测结果(2026-04-20 17:05):test_frame_extract.py对trellis_combined_290da12de954.mp4运行,抽出 120 帧,az=[0,60,120,180,240,300] 分别映射到 frame_idx=[0,20,40,60,80,100](每 60° = 20 帧)完全均匀,12 个 PNG 全部落盘,单张尺寸 512×512 方形(说明原 combined_video 是 1024×512 左右并排)。自测不能替代用户目视检查 "6 张 color 图是否按 0°→300° 顺序环绕建筑一圈",Checkpoint 2 必须由用户拍板。(6) 用户验收流程:(a) 在art-agent/backend激活 venv;(b) 跑python scripts/test_frame_extract.py(默认取generated/最新 trellis_combined;也可python scripts/test_frame_extract.py generated/trellis_combined_<hash>.mp4指定);(c) 终端应打印 6 个 color_png + 6 个 normal_png 均OK,每个 size > 0;(d) 打开generated/view_*_az0_el0_color.png ~ view_*_az300_el0_color.png6 张图,验证是同一建筑从 6 个均匀角度绕一圈的视角;(e) normal 图是蓝紫调法线图,可抽 1-2 张抽查。通过后开 Phase 3。 - source_chat: 本次会话 "继续执行 Phase 2"
[CL-20260420-1600] 2026-04-20 16:00 — Mesh Pipeline Phase 1 接入 Trellis(视角变换第二条管道落地阶段 1)
- tags: 视角变换, Mesh Pipeline, Trellis, Replicate, 后端, services, config, tools, Phase 1
- affected_files:
- art-agent/backend/app/services/mesh_generator.py (新增)
- art-agent/backend/app/services/view_transform.py
- art-agent/backend/app/config.py
- art-agent/backend/app/agent/tools.py
- art-agent/backend/scripts/test_trellis.py (新增)
- docs/art-agent/VIEW-TRANSFORM-MESH-PIPELINE-PLAN.md
- what: 按 SPEC §4 Phase 1 清单落地。(1) 新增
services/mesh_generator.py,封装generate_with_trellis(image_path, params=None)和统一入口generate_mesh(image_path, model_id),调用firtoz/trellis:e8f6c452...并把model_file/color_video/normal_video/combined_video挨个 httpx 下载到GENERATED_DIR,以trellis_*_<uuid>.{glb,mp4}命名,返回/generated/...本地 URL。(2)config.py在VIEW_TRANSFORM_MODELS新增trellis(pipeline=mesh, enabled=True)和hunyuan3d(pipeline=mesh, enabled=False 占位),给原zero123plus条目补pipeline=grid;get_view_transform_models_list改为只返回 enabled 条目 + 带上 pipeline 字段。(3)services/view_transform.py改为按pipeline字段分流——grid走原 Zero123++ 逻辑(逻辑未动,仅输出字段补齐pipeline/mesh_url/color_video/normal_video),mesh走新增_transform_view_mesh():当前阶段只跑 mesh_generator 返回.glb + color_video + normal_video本地 URL,images列表留空,附_stage="stage1"和_note标记 Phase 2/3 未接入。(4)tools.py的transform_view工具定义扩三个参数model_id(enum: zero123plus/trellis, default zero123plus) /azimuths/preserve_style,执行侧把这些参数透传给transform_view()。(5) 新增backend/scripts/test_trellis.py作为 Phase 1 独立验收脚本——默认用 uploads/ 下最新 png、支持命令行指定路径、自动加载 .env、调用mesh_generator.generate_with_trellis后检查本地文件大小。(6) SPEC 文档 P1-1 ~ P1-5 打勾并追加进度 note。 - why: SPEC 明确要求"按 Phase 顺序推进,每个 Phase 完成后停下来让用户测试/审阅",Phase 1 是整条 Mesh Pipeline 的地基——验证 Replicate Trellis 调用通道是否打通、文件下载策略是否正确、与现有 grid 管道并存不互相干扰。把 mesh_generator 独立成模块是为后续 Phase 2/3/5 解耦:Phase 2 只需读 color_video_path/normal_video_path,Phase 3 只需消费 images 列表,Phase 5 切到 Hunyuan3D-2 时只需替换 generate_mesh 的分支。测试脚本独立走 asyncio 是因为 Phase 1 还没接到工具链,用户需要一个"最小能跑"的入口先确认 API 本身可用(SPEC P1-5 也是这么要求的)。
- decisions: (1)
view_transform返回结构升级为同时带grid_image/mesh_url/color_video/normal_video/pipeline字段,两种管道缺省位补None——比"两种完全不同的返回 schema"更利于前端和 loop.py 后续统一处理。(2) Phase 1 当前故意让 mesh 管道的images为空 + 带_note说明,而不是硬拼一个"只返回视频"的假结果——这样 Phase 2 接入时 frontend 的 "没有图片显示" 现象会被替换为 "6 张图片显示",语义更清晰。(3) hunyuan3d 提前注册但enabled=False,避免用户在前端误选到二期才启用的路径;get_view_transform_models_list同步过滤 enabled。(4)mesh_generator._to_url做了FileOutput → str的显式转换兜底,因为 Replicate SDK 的字典输出里每个字段可能是 FileOutput 对象而不是裸字符串 URL——这点在 image_gen.py 的ReplicateProvider._download已经吃过亏,这里主动规避。(5)_download_asset的 httpx 超时放到 300s,因为 color_video ~3-5MB 在国内网络可能慢;复用项目既有的 HTTPS_PROXY 注入模式。 - notes: 本次改动完全不触碰 Zero123++ 现有流程——grid 管道所有行为保持 byte-level 等价,只是返回字典多了几个 None 字段,
loop.py和前端已有代码没事。但还未修改 loop.py 的 SSE 事件处理:当前 loop.py 只在tool_name == "transform_view" and result.get("images")时发image_result;mesh 管道 Phase 1 的images=[]会让 loop.py 不发事件,前端看到空结果——这是预期行为,Phase 1 的测试入口是scripts/test_trellis.py而不是前端。Phase 2 接入后 images 有值,loop.py 无需改动即可工作。用户验收流程:(a) 在art-agent/backend激活 venv;(b) 跑python scripts/test_trellis.py(默认取 uploads/ 最新 png),或python scripts/test_trellis.py uploads/<hash>.png指定图;(c) 等 ~30-60s 看终端打印glb_path/color_video_path/normal_video_path均为/generated/trellis_*...+ 本地大小 > 0;(d) 可选:去backend/generated/目录用系统播放器看 color_video.mp4 是否为 360° 环绕教堂视频。验收通过后我再开 Phase 2(ffmpeg 抽帧)。 - source_chat: 本次会话 "按计划实施 Mesh Pipeline"
[CL-20260420-1530] 2026-04-20 15:30 — 写入 Mesh-Pipeline 视角变换方案 SPEC(跨会话实施计划)
- tags: 视角变换, Mesh Pipeline, Trellis, Hunyuan3D-2, ControlNet, IP-Adapter, SPEC, 方案设计, 建筑, 跨会话
- affected_files:
- docs/art-agent/VIEW-TRANSFORM-MESH-PIPELINE-PLAN.md
- what: 新增一份完整的实施计划 SPEC,规划 Zero123++ 之外的第二条视角变换路径——Mesh Pipeline。三段式流水线:(1) Trellis/Hunyuan3D-2 单图生 mesh + 360° color_video;(2) ffmpeg 从 color_video 抽指定 azimuth 的帧(一期方案,免 pyrender);(3)
chigozienri/ip_adapter-sdxl-controlnet-depth做 SDXL+ControlNet-Depth+IP-Adapter 二次重绘,用原图保持卡通描边风格。文档含:三个 Replicate 模型的精确版本 ID + OpenAPI schema、config.py 扩展设计、伪代码管道、6 个 Phase 的可独立执行任务清单(每个 Phase 有独立验收标准)、风险兜底、成本预估(~$0.46/~105s)、跨会话恢复指引。 - why: 用户反馈 Zero123++ 只能出 6 个固定视角 + 建筑大角度偏转一致性差。用户选定重流水线(P1-P3 全做),明确要求"保留 2D 卡通风格"+"自包含跨会话执行"。调研中发现:Trellis ($0.041/30s 自带 color_video) 比 Hunyuan3D-2 ($0.12/127s 仅 mesh) 快 4× 便宜 3×,一期走 Trellis 还能省掉自定义渲染模块。把 Hunyuan3D-2 路线拆进 Phase 5(二期可选),工程量从 ~9h 降到 ~5h。
- decisions: 主选 Trellis 而非 Hunyuan3D-2——以便一期完全跳过 pyrender/blender 渲染器(用 ffmpeg 抽帧替代)。阶段 3 的 ControlNet 结构约束用 normal_video 抽帧(Trellis 送的),不先走 depth-anything-v2(省一次 API 调用,不完美但足够)。Hunyuan3D-2 + pyrender 自由相机留作 Phase 5 可选。不在一期解决 elevation 仰俯视角(Trellis color_video 只给水平环绕),那是 Phase 5 的事。
- notes: 文档位置
docs/art-agent/VIEW-TRANSFORM-MESH-PIPELINE-PLAN.md。下一个会话开工时 Agent 需先读 §0-§3 建立背景,再按 §4 Phase 顺序推进,每 Phase 交付后停下来让用户验证。文档底部有"进度日志"章节,每个 Phase 完成后在 checkbox 打勾 + 追加 note。未修改任何业务代码,纯 SPEC 文档。 - source_chat: 本次会话排查 Zero123++ 切图错位 meta-bug + 后续方案调研
[CL-20260420-1501] 2026-04-20 15:01 — 后端增加 watchfiles 依赖,修复 Windows uvicorn --reload 失效
- tags: uvicorn, watchfiles, 热重载, Windows, FastAPI, requirements, 依赖管理, reload, Zero123++
- affected_files:
- art-agent/backend/requirements.txt
- art-agent/backend/requirements-lock.txt
- what: 在
requirements.txt和requirements-lock.txt中显式加入watchfiles==0.24.0,并在当前 venv 中安装。这让 uvicorn--reload使用 WatchFiles 作为 reloader 后端,而不是无依赖时的 StatReload 轮询回退。 - why: 用户反馈 Zero123++ 切图错位——但 CL-20260420-1212 里做的
cols=2/rows=3修复和_split_grid_image自动纠错逻辑在磁盘上明明是新版,独立调用也能切出正确的 320×320。进一步排查发现后端 uvicorn 进程是 11:55 启动、从未 reload,而 venv 里watchfiles/watchdog均未安装——StatReload 在 Windows + 深层子目录 + 新建文件 (view_transform.py是 untracked 新增) 的叠加场景下几乎完全失效,所以代码改了但运行时还是老版本。这一层 meta-bug 如果不治本,以后每次改后端都要手动重启。 - decisions: 选择
watchfiles(uvicorn 官方推荐、跨平台 Rust 实现、依赖最轻)而不是watchdog(历史包袱更重)。版本锁 0.24.0 与 Python 3.12 兼容良好且 uvicorn 0.44.0 默认识别。未添加--reload-dir app参数到 project-launcher Skill 的启动命令里——先靠装依赖覆盖主要场景,如果未来仍有监听遗漏再显式限定目录。 - notes: 仅装依赖不重启当前 uvicorn 进程不会生效,因为旧进程已经绑定到 StatReload。需要 kill PID 28172 并重启一次后端,之后所有代码改动才会自动 reload。启动日志里应该能看到
Started reloader process [xxx] using WatchFiles,如果仍是StatReload说明 watchfiles 没正确安装到当前 venv。踩坑已记录 PF-20260420-1501。 - source_chat: 本次会话排查 Zero123++ 切图错位 meta-bug
[CL-20260420-1212] 2026-04-20 12:12 — 修复 Zero123++ 视角切图布局反转导致的图像错位
- tags: Zero123++, 视角变换, 切图, bug修复, grid_layout, PIL, Replicate, view_transform
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/app/services/view_transform.py
- what: 修复
transform_view输出的 6 张视角图严重错位的问题。config.py里的VIEW_TRANSFORM_MODELS["zero123plus"].grid_layout由{"cols": 3, "rows": 2}改为{"cols": 2, "rows": 3},与jd7h/zero123plusplusReplicate 实际输出(高图布局)一致。同时在view_transform._split_grid_image加入"按实际图像长宽比自动纠正 cols/rows"的健壮性保护:假定每个单视角为正方形,若配置的cols/rows与图像实际w/h差异超过 20%,自动交换 cols/rows 并记录 warning。 - why: 用户反馈视角变换后的切图全部错位(窄长条、上下拼两个物体)。根因是 Zero123++ 官方 README 写 "3×2 grid" 但实际 Replicate 部署版输出的是 2 列 × 3 行(高图),config 里按"3 列 × 2 行"配置,cell_w/cell_h 算反了。加上动态纠错是为了未来换其他 view transform 模型时不再重复踩这个坑。
- decisions: 选择"修 config + 加运行时自动纠错"而非仅修 config。纯改 config 能解决眼前问题但无法防御未来模型切换;加动态纠错的代价只是一段 if 判断+warning 日志,换取对"布局文档歧义"场景的整体免疫。放弃了更激进的"用 PIL 探测内容块边界"方案,因为对有微量白边/噪声的模型不稳定。
- notes: uvicorn 以
--reload方式运行,改完自动重载,无需重启服务。用户需要在前端重新触发一次视角变换来验证切图是否正确。对应踩坑记录已写入.cursor/pitfalls/pitfalls.md(PF-20260420-1212)。 - source_chat: 本次会话启动项目并调试 Zero123++ 切图
[CL-20260417-1510] 2026-04-17 15:10 — Cloudflare Quick Tunnel 脚本修复(PS5.1 编码 + 本地 cloudflared)
- tags: 内网穿透, cloudflared, PowerShell, start-tunnel, UTF-8, gitignore
- affected_files:
- art-agent/start-tunnel.ps1
- .gitignore
- what: 修复
start-tunnel.ps1在 Windows PowerShell 5.1 下因脚本 UTF-8 无 BOM、含中文的 here-string(@")被误解析而导致的ParserError。将写入frontend/.env.local的两处 here-string 改为[System.IO.File]::WriteAllLines+ UTF-8 无 BOM(与 Next 读取.env习惯一致)。用 PowerShell 将脚本全文重写为 UTF-8 BOM,便于 PS5.1 正确分词。在脚本开头增加:若存在与脚本同目录的cloudflared.exe,则把该目录插入PATH最前,便于未全局安装 cloudflared 时使用本地下载的可执行文件。根目录.gitignore增加art-agent/cloudflared.exe,避免误提交约 65MB 二进制。 - why: 用户请求开启穿透时,脚本在 Cursor 集成终端中立即解析失败,隧道无法拉起;当前环境亦未必安装 winget/系统 PATH 中的 cloudflared,需要可放置于项目目录的 fallback。
- decisions: 不改为仅英文输出(保留原有中文提示与框线字符);
.env.local注释改为英文短句以避免无 BOM 环境下再次出现字符串边界问题。 - notes: 会话中曾从 GitHub latest release 下载
cloudflared-windows-amd64.exe至art-agent/cloudflared.exe并成功启动双隧道;该文件已被 gitignore。
[CL-20260417-1500] 2026-04-17 15:00 — 按 INFOLAYER.md 实现完整前端信息架构(Phase 1+ 占位骨架)
- tags: 前端, UI骨架, INFOLAYER, 多项目, 风格库, 角色库, 训练中心, 项目设置, TaskCard, 候选评估, 占位数据
- affected_files:
- art-agent/frontend/src/lib/types.ts
- art-agent/frontend/src/lib/placeholder-store.ts
- art-agent/frontend/src/lib/app-context.tsx
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/gallery/page.tsx
- art-agent/frontend/src/app/styles/page.tsx
- art-agent/frontend/src/app/styles/[id]/page.tsx
- art-agent/frontend/src/app/characters/page.tsx
- art-agent/frontend/src/app/characters/[id]/page.tsx
- art-agent/frontend/src/app/training/page.tsx
- art-agent/frontend/src/app/training/[id]/page.tsx
- art-agent/frontend/src/app/training/new/page.tsx
- art-agent/frontend/src/app/settings/page.tsx
- art-agent/frontend/src/components/layout/top-nav.tsx
- art-agent/frontend/src/components/layout/project-switcher.tsx
- art-agent/frontend/src/components/sidebar/sidebar.tsx
- art-agent/frontend/src/components/sidebar/project-card.tsx
- art-agent/frontend/src/components/chat/session-quick-picks.tsx
- art-agent/frontend/src/components/chat/task-card.tsx
- art-agent/frontend/src/components/workbench/advanced-controls.tsx
- art-agent/frontend/src/components/workbench/candidate-panel.tsx
- what: 按
docs/art-agent/INFOLAYER.md规范一次性铺开 Phase 1–3 产品形态的前端 UI 骨架:新增风格库/styles、角色库/characters、训练中心/training、项目设置/settings四大页面模块(含列表 + 详情 + 创建流程);工作台输入区上方加入风格/角色/资产规格快选条、高级控制抽屉、候选评估侧面板、Task Card IR 占位组件;资源库新增资产类型/风格/角色多维过滤和语义搜索切换;TopNav 扩充导航项与项目切换器,Sidebar 加入项目信息卡;新增lib/placeholder-store.ts管理 Phase 1+ 占位数据(项目/风格集/角色卡/训练任务)并在app-context.tsx中统一暴露;globals.css补充phase-chip/placeholder-card/hide-scrollbar/section-divider工具类。 - why: INFOLAYER.md 规划了 Phase 0–3 完整产品形态,其中 Phase 1+(多项目、风格库/角色库、训练中心、Task Card、候选评估等)尚未具备后端能力。用户希望先把完整交互结构和界面落地,"数据或展示上写占位"即可,方便内部演示、收集反馈以及后续按 Phase 增量接入真实后端。
- decisions: (1) 占位数据用独立的
placeholder-store.ts+localStorage持久化,与现有store.ts解耦,首次加载注入示例数据(2 个项目 / 4 个风格集 / 2 个角色 / 4 个训练任务)。(2) 详情页直接用[id]/page.tsx同时处理new与编辑两种模式(判断params.id === 'new'),不再新增/styles/new等专用子路由。(3) Task Card 作为独立可复用组件 (components/chat/task-card.tsx),不强制接入当前 chat loop,等真实 IR 管线就绪时再 wire up。(4) 候选评估面板从"最后一条含 images 的 assistant 消息"取数据做 demo。(5) Gallery 风格/角色过滤用 prompt 关键词子串匹配做占位,未来等后端补 asset 归因字段后再切到真实字段。(6) 所有 Phase 1+ 功能按钮均挂phase-chip(P1/P2/P3)明示阶段,避免用户误以为已可用。 - notes: 所有"应用到会话""Fork""训练""批量下载"等交互都是
alert("(占位)..."),不产生真实后端调用;真实接入时搜索(占位)或phase-chip可快速定位需要替换的位置。AppContext新增字段:projects/activeProjectId/stylePacks/characters/trainingTasks/sessionStyleId/sessionCharacterId/sessionAssetSpec及对应 upsert/delete 方法;切换用户时会重新加载全部占位数据。/styles/[id]和/characters/[id]的"生成图片"列表也是基于关键词从现有 assets 里筛的占位结果,不是真正的归因。ChatMessage未扩展,Task Card 目前仅以组件形式存在,实际渲染位置需要后续决定(工作台消息流或独立抽屉)。 - source_chat: 实现 INFOLAYER 完整 UI 骨架
[CL-20260416-1600] 2026-04-16 16:00 — 视觉风格从暗绿底全面转为白底青绿(绢本山水)
- tags: 前端, 视觉风格, 色彩体系, CSS变量, 白底, 千里江山图
- affected_files:
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/login/page.tsx
- art-agent/frontend/src/app/gallery/page.tsx
- art-agent/frontend/src/components/chat/chat-messages.tsx
- art-agent/frontend/src/components/chat/chat-input.tsx
- 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/profile-modal.tsx
- art-agent/frontend/src/components/detail/image-detail-panel.tsx
- art-agent/frontend/src/components/ui/ambient-particles.tsx
- docs/art-agent/VISUAL-STYLE-GUIDE.md
- what: 整个应用从暗色(墨绿黑 #0C1210)底色改为白色(绢本白 #FAFAF7)底色,文字改为墨黑(#1A1A1A),框体和光效保留千里江山图的蓝绿配色但色值加深适配白底
- why: 用户反馈当前风格"太偏绿了"。评估后认同——原版将背景、面板、文字全都带绿调,导致全屏绿色信息过载。千里江山图本身是青绿设色于白色绢本之上,白底才更贴近原画精髓
- decisions:
- 背景 #0C1210 → #FAFAF7(绢本微暖白),面板从绿底毛玻璃改为白色毛玻璃
- accent #4DB8A4 → #2E8B7A(加深以确保白底上对比度)
- 文字 #E0E8E2 → #1A1A1A(墨黑),次级文字 #7A9485 → #6B7B8A(灰蓝去绿调)
- 所有 rgba(77,184,164,...) 硬编码统一替换为 rgba(46,139,122,...)
- 云雾背景透明度大幅降低(白底上需要更淡才不会突兀)
- Logo 内部颜色从 #0C1210 改为 white(石青底白字)
- 保留所有动效系统(云烟缭绕、萤火粒子、水波纹等),仅调整颜色参数
- notes:
- surface-1/2/3 从深色半透明改为白色半透明,阴影也从重阴影改为轻阴影
- 网格底纹 opacity 从 0.03 降为 0.018
- VISUAL-STYLE-GUIDE.md 文档同步重写
- source_chat: [白底青绿视觉风格改造]
[CL-20260416-1500] 2026-04-16 15:00 — UX风格指南实现度补全:接入涟漪/sendBounce/会话切换动画/Error飘散/全屏云雾加载/流式opacity阶梯等10项
- tags: 前端, UX补全, 动效, framer-motion, 微交互, 排版使用, 实现状态文档
- affected_files:
- art-agent/frontend/src/components/chat/chat-input.tsx
- art-agent/frontend/src/components/chat/chat-messages.tsx
- art-agent/frontend/src/components/chat/image-grid.tsx
- art-agent/frontend/src/components/ui/motion-presets.ts
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/login/page.tsx
- art-agent/frontend/src/app/gallery/page.tsx
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/lib/app-context.tsx
- art-agent/frontend/src/components/sidebar/sidebar.tsx
- art-agent/frontend/src/components/profile-modal.tsx
- docs/art-agent/UX-STYLE-GUIDE.md
- what: 补全 UX-STYLE-GUIDE.md 中描述但未实现的 10 项特性,并在文档中标注每项实现状态
- why: 分析发现风格指南完成度约 65-70%,多项微交互只写了文档描述或代码定义了但未接入业务
- decisions: 对每项未实现特性逐一补全代码,同时更新文档加入 ✅/🔶/⬜ 状态标记
- notes: 补全项包括:(1) ripple-effect.tsx 接入发送按钮 (2) sendBounce 替代 buttonTap (3) 会话切换 sessionSlide 滑入滑出 (4) Error errorFloat 飘散消失+4s自动清除 (5) 回到底部按钮水滴形 (6) 全屏加载改云雾凝聚 mist-loader (7) AI回复 StreamingLines 逐行 opacity 阶梯 (8) typo-h1/h2/strong 实际使用到组件 (9) 用户消息 .user-bubble 左上切角 (10) 图片占位 .stone-placeholder 石青渐变呼吸脉冲。文档每个 section 加入实现文件路径和状态标记。
[CL-20260416-1330] 2026-04-16 13:30 — 交互体验全面升级:信息层级 + 科技质感 + 微交互趣味性
- tags: 前端, UX升级, 动效, framer-motion, 排版体系, 微交互, 视觉层级, 科技感, 粒子, 流光
- affected_files:
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/login/page.tsx
- art-agent/frontend/src/app/gallery/page.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/chat/model-selector.tsx
- art-agent/frontend/src/components/sidebar/sidebar.tsx
- art-agent/frontend/src/components/sidebar/session-list.tsx
- art-agent/frontend/src/components/layout/top-nav.tsx
- art-agent/frontend/src/components/detail/image-detail-panel.tsx
- art-agent/frontend/src/components/profile-modal.tsx
- art-agent/frontend/src/components/ui/motion-presets.ts (新建)
- art-agent/frontend/src/components/ui/ambient-particles.tsx (新建)
- art-agent/frontend/src/components/ui/data-stream.tsx (新建)
- art-agent/frontend/src/components/ui/ripple-effect.tsx (新建)
- art-agent/frontend/src/components/ui/firefly-burst.tsx (新建)
- docs/art-agent/UX-STYLE-GUIDE.md (新建)
- what: 四阶段全面升级交互体验:信息层级体系、科技质感注入、微交互趣味性、统一状态反馈
- why: 用户要求信息层级差异更突出、画面更高级典雅且具科技感、交互有巧思让使用心情愉悦
- decisions:
- 新增 framer-motion 作为动画引擎(~30KB gzipped),不引入其他 UI 库
- 排版从 5 级扩展到 7 级(Display 渐变文字 / H1 底部石青线 / H2 左侧竖线 / Body Strong 石青色 / Caption / Micro 大写加宽字间距),CSS 工具类
.typo-* - Surface 层级从 1 级扩展到 4 级(Surface-0 背景 → Surface-3 弹窗),每级递增 backdrop-blur、border 亮度、box-shadow
- 背景网格底纹(radial-gradient 点阵 opacity 0.03)从 body::before 实现,原云雾改为独立 DOM 元素
.fog-layer-far/near - 环境粒子纯 CSS 实现(15-28 个随机漂浮+闪烁的石青色小光点),空态时增多营造"未探索深山"感
- 数据流光(石青色 linear-gradient 横向流动 2px),AI 活动时出现在输入栏上方,生图时加速
- 用户消息改为右对齐渐变背景+右上切角,助手消息改为 surface-2 + 左侧 2px 石青竖线
- 消息入场动画:用户消息 translateY+scale spring,助手消息 blur 消散(云雾凝聚)
- 发送按钮 whileTap scale 0.85,可发送时呼吸脉冲发光
- 输入框水面感应:焦点时底部 scaleX 扩展线 + box-shadow 随输入长度增强
- 收藏星标改为赭金色,点击时 5-7 个金色粒子向四周飘散(萤火点亮)
- 图片水墨晕染:skeleton shimmer 占位 → clip-path circle 从中心扩展揭示
- Loading 从 bounce 圆点改为水波纹 scaleY 动画,skeleton 用 shimmer 横向扫描
- 错误状态赭石红 + translateX 震动 2px × 4 次
- 空态使用 Display 级渐变文字 + stagger 入场动画
- 滚到底部按钮改圆形 + spring 弹入/退出
- 侧栏会话项 hover 时左侧 2px 竖线 slide-in
- 所有按钮 hover 添加 translateY(-1px) + shadow 加深浮起效果
- 所有弹出层改用 surface-3(强模糊+明显阴影+更亮边框)
- 统一动画参数:motion-presets.ts 定义 spring/springGentle、fadeIn/slideUp/mistReveal、messageEnter/assistantEnter、buttonTap/sendBounce、ripple/sparkle/inkReveal、staggerContainer/staggerItem
- notes:
- framer-motion 通过 npm install 添加(3 个包),前端 bundle 增加约 30KB gzipped
- 环境粒子使用 CSS animation 而非 Canvas,不影响性能
- 所有动画使用 transform/opacity/filter,避免触发 layout
- body::before 原来用于云雾现改为点阵网格,云雾改为 AmbientParticles 组件内的 DOM 元素
- 构建验证通过(next build 无错误),First Load JS 增量约 50KB(framer-motion)
- source_chat: 交互体验全面升级
[CL-20260416-1000] 2026-04-16 10:00 — 集成 Zero123++ 视角变换工具(单图→6视角)
- tags: 视角变换, Zero123++, Novel View Synthesis, 新工具, Replicate, 图片处理
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/app/agent/tools.py
- art-agent/backend/app/agent/loop.py
- art-agent/backend/app/services/view_transform.py
- what: 新增
transform_view工具,集成 Zero123++ 模型实现单图生成 6 个固定视角图片 - why: 用户需要将手绘建筑从侧面视角转为其他角度,视角变换是与生图完全不同的能力,需要独立工具
- decisions:
- 视角变换作为独立工具
transform_view而非generate_image的扩展——输入不需要 prompt,只需一张图 - 新增独立服务模块
view_transform.py,与image_gen.py分离——逻辑完全不同(无 prompt、grid 拆分) - 在
config.py中新增VIEW_TRANSFORM_MODELS独立注册表——与IMAGE_MODELS分离,未来可扩展更多视角变换模型 - Zero123++ 输出为 3x2 grid 拼接图,用 Pillow 拆分为 6 张独立图片返回
- 进阶方案(3D 重建 + ControlNet 风格还原 pipeline)记录到延期方案
[advanced-view-transform]
- 视角变换作为独立工具
- notes:
- Zero123++ 视角固定(Azimuth: 30/90/150/210/270/330°, Elevation: 30/-20 交替),不支持自定义角度
- 手绘风格保持度可能不理想(模型偏向写实/3D 风格),需实际测试验证
- 输入图片需为正方形,建议 >= 320x320
- Replicate 上的模型 ID:
jd7h/zero123plusplus:c69c6559...,~$0.074/次,~76s - loop.py 中对
transform_view做了特殊结果处理(返回带视角信息的图片列表)
[CL-20260416-0930] 2026-04-16 09:30 — 设备绑定配置与 Skill/Hooks 解耦(local-env.json)
- tags: 基础设施, local-env, 跨设备, hooks, project-launcher, Skill
- affected_files:
- .cursor/local-env.json
- .cursor/local-env.example.json
- .cursor/hooks.json
- .cursor/hooks/run-hook.ps1
- .cursor/hooks/session-init.sh
- .cursor/hooks/check-changelog.sh
- .cursor/hooks/README.md
- .cursor/rules/project/local-env-check.mdc
- .cursor/skills/project-launcher/SKILL.md
- .gitignore
- art-agent/ENVIRONMENT.md
- what: 将设备绑定的配置(Node.js 路径、shell 类型)从 Skill 和 hooks 中解耦到
.cursor/local-env.json(gitignored)。project-launcher Skill 改为从 local-env.json 读取路径;hooks 增加 run-hook.ps1 dispatcher + bash 版脚本支持跨平台;新增 local-env-check Rule 自动探测生成;ENVIRONMENT.md 去除平台硬编码改为通用说明 - why: Node.js 路径和 shell 类型在不同设备上不同,硬编码在 Skill/hooks 中导致换机或跨平台时需要修改 git tracked 文件
- decisions: 采用
.cursor/local-env.json(gitignored)+.cursor/local-env.example.json(tracked 模板)的模式,类似.env/.env.example的惯例;hooks.json 的跨平台靠为每个 hook 提供 .ps1 和 .sh 双版本 - notes: hooks.json 的 command 字段本身仍需按平台手动调整(Windows 用 powershell,macOS/Linux 用 bash),这是 Cursor hooks 机制的限制
- source_chat: [local-env 设备配置解耦]
[CL-20260416-0900] 2026-04-16 09:00 — 环境依赖全面修复 + 统一环境文档
- tags: 环境, 依赖, Node.js, Python, venv, npm, requirements, ENVIRONMENT
- affected_files:
- art-agent/backend/requirements.txt
- art-agent/backend/requirements-lock.txt
- art-agent/ENVIRONMENT.md
- .cursor/skills/project-launcher/SKILL.md
- what: 排查并修复缺失的前后端依赖(venv 和 node_modules 均不存在),安装 Node.js v22.15.0 到 %LOCALAPPDATA%\nodejs\,创建 Python venv 并安装全部后端依赖,npm install 前端依赖;requirements.txt 从范围版本更新为精确锁定版本;新增 requirements-lock.txt(完整依赖树 61 个包)和 ENVIRONMENT.md 统一环境文档
- why: venv 和 node_modules 被 gitignore,换机/重装后需要重新搭建;原 Node.js 安装已丢失;requirements.txt 仅用 >= 范围无版本锁定
- decisions: Node.js 采用 zip 解压到 %LOCALAPPDATA% 而非 MSI 安装(无管理员权限);requirements.txt 直接锁定到当前精确版本而非继续用范围约束
- notes: project-launcher Skill 中的 Node.js 路径已同步更新为 %LOCALAPPDATA%\nodejs\
- source_chat: [环境依赖修复与文档生成]
[CL-20260416-0830] 2026-04-16 08:30 — 生图按模型预处理 prompt(官网/社区策略对齐)
- tags: 后端, 生图, prompt, SDXL, Flux, GPT Image, Gemini, Replicate, IP-Adapter, image_gen, Agent
- affected_files:
- art-agent/backend/app/services/image_prompt_strategy.py
- art-agent/backend/app/services/image_gen.py
- art-agent/backend/app/agent/tools.py
- what: 为当前注册的全部 7 个生图模型增加「进入 provider 前」的 prompt 预处理层;SDXL 向 Replicate 传入独立
negative_prompt字段(与 stability-ai/sdxl API 一致) - why: 各后端对 prompt 的惯例不同(如 SDXL 双通道、FLUX 无 negative、IP-Adapter 重参考图语义),原先同一字符串直传难以发挥模型能力
- decisions:
- 新模块
image_prompt_strategy:策略按IMAGE_MODELS的id分发,docstring 注明依据(OpenAI Image 指南、BFL FLUX 文档、Replicate API 字段、Stability 系默认负向词) - SDXL:未拆分时使用模块内
DEFAULT_SDXL_NEGATIVE;可选default_params.negative_prompt覆盖;支持正文内---NEGATIVE---或|||NEG||| - Flux:剥除误粘贴的
negative prompt:等段,符合 BFL「用正向描述替代负向」的指引 GenerateResult携带effective_prompt/negative_prompt供调试与工具回传
- 新模块
- notes: 未引入二次 LLM 调用;复杂措辞重排仍依赖对话模型在工具参数里写好英文描述
[CL-20260416-0810] 2026-04-16 08:10 — 对话栏支持剪贴板粘贴图片为参考图
- tags: 前端, ChatInput, 剪贴板, paste, 参考图, UX
- affected_files:
- art-agent/frontend/src/components/chat/chat-input.tsx
- what: 在输入区通过 Ctrl+V 或右键粘贴时,若剪贴板含图片文件则自动上传为参考图(与拖拽/点选上传同一路径)
- why: 截图或复制图片后直接粘贴比保存再选文件更快
- decisions: 使用根容器
onPasteCapture在文本插入前拦截;仅当clipboardData.items中存在kind==="file"且type以image/开头时才preventDefault,避免影响纯文本粘贴 - notes: 依赖浏览器将剪贴板图片暴露为 file item;多图时逐项上传
[CL-20260416-0745] 2026-04-16 07:45 — Gemini 原生生图:延长超时 + 断连类错误自动重试
- tags: 后端, Gemini, httpx, 超时, 重试, RemoteProtocolError, 向量引擎, 稳定性
- affected_files:
- art-agent/backend/app/services/image_gen.py
- art-agent/backend/.env.example
- what: 缓解 Gemini 3.1 Flash Image 调用时出现
Server disconnected without sending a response的失败率 - why: 多参考图时请求体大、生图耗时长,上游或本地代理可能在响应返回前关闭连接,httpx 抛出
RemoteProtocolError;原先 read 仅 180s 且单次失败即返回 - decisions: 默认 read=600s、write=180s、connect/pool=60s;对
RemoteProtocolError、ConnectError、Read/Write/Connect/PoolTimeout最多重试 3 次、间隔 1s/2s;可通过VECTORENGINE_GEMINI_READ_TIMEOUT等环境变量覆盖;.env.example增加注释说明 - notes: 若仍频繁断连,需排查代理 idle 超时或向量引擎侧限流;用户可暂时换 GPT Image 1.5 对比
- source_chat: Gemini断连重试
[CL-20260416-0720] 2026-04-16 07:20 — 修复拖拽参考图到 ChatInput 区域后覆盖层卡住不消失
- tags: bug修复, 拖拽, 覆盖层, ChatInput, stopPropagation, UX, 事件冒泡
- affected_files:
- art-agent/frontend/src/components/chat/chat-input.tsx
- art-agent/frontend/src/app/page.tsx
- what: 修复拖拽图片到 ChatInput 区域松开后,全屏拖拽覆盖层("松开以添加参考图")不消失的 bug
- why: CL-20260416-0620 为防止重复上传在 ChatInput.handleDrop 中加了 stopPropagation,但这也阻止了事件冒泡到 page.tsx 的 main.onDrop,后者负责清除 mainDragging 状态和 dragCounter。覆盖层有 pointer-events-none,不直接接收事件,但它的显示由 mainDragging 控制
- decisions: 新增 onFileDrop 回调 prop 让 ChatInput 在 handleDrop 中通知父组件,而非移除 stopPropagation(移除会导致 CL-20260416-0620 修复的重复上传问题复发)
- notes: 这是 stopPropagation 的典型副作用 — 解决了一个冒泡问题的同时切断了另一个需要冒泡的事件链。onFileDrop 回调模式是解决此类问题的标准方式
- source_chat: 拖拽覆盖层卡住修复
[CL-20260416-0700] 2026-04-16 07:00 — 新增 GeminiNativeImageProvider:Gemini 原生 generateContent 接口对接
- tags: 后端, Gemini, Provider, 原生API, generateContent, 多图参考, 架构, 向量引擎
- affected_files:
- art-agent/backend/app/services/image_gen.py
- art-agent/backend/app/config.py
- what: 新增
GeminiNativeImageProvider类,对接 Gemini 原生generateContent接口,使 Gemini 3.1 Flash Image 恢复多图参考能力 - why: 向量引擎对 Gemini 不支持 OpenAI 兼容的
images/edit端点(CL-20260416-0620 回退了此方案)。Gemini 原生 API 通过generateContent+inline_data支持文字+图片混合输入,这是唯一可行的路径 - decisions:
- 选择直接 HTTP 调用向量引擎中转的
/v1beta/models/{model}:generateContent端点,而非引入google-genaiSDK 新依赖——避免依赖膨胀且向量引擎已提供中转 - 新增
_resolve_image_base64公共工具函数统一 data URI / 本地路径到 (mime, base64) 的解析,Gemini inline_data 和未来其他 provider 都可复用 - Gemini 响应中同时处理
inlineData(camelCase)和inline_data(snake_case)两种字段名,因为向量引擎中转可能改变命名风格
- 选择直接 HTTP 调用向量引擎中转的
- notes:
- Provider 注册表现有三个:
replicate、openai、gemini_native,分发逻辑无需改动 generate_images中 Replicate 独有的"无参考图报错"逻辑已有provider_name == "replicate"限定,Gemini 无参考图时正常走纯文生图- API key 复用
VECTORENGINE_API_KEY,base_url 从VECTORENGINE_BASE_URL推导(去掉/v1后拼/v1beta/...)
- Provider 注册表现有三个:
- source_chat: Gemini原生Provider实现
[CL-20260416-0620] 2026-04-16 06:20 — 修复拖拽上传重复 + Gemini 改回不支持参考图 + images.edit 参数修正
- tags: bug修复, 拖拽, 事件冒泡, Gemini, 向量引擎, images.edit, config, stopPropagation
- affected_files:
- art-agent/frontend/src/components/chat/chat-input.tsx
- art-agent/backend/app/config.py
- art-agent/backend/app/services/image_gen.py
- what: 三个问题修复:拖拽上传重复、Gemini 500 错误、images.edit quality 参数不支持
- why: 用户测试多图上传时发现拖 1 张图显示 2 张;Gemini 使用参考图时 500 报错
- decisions:
- 拖拽重复:ChatInput 的 handleDrop/handleDragOver 加 e.stopPropagation(),阻止事件冒泡到 page.tsx 的 onDrop
- Gemini 500:向量引擎的 Gemini 图片编辑走原生
/v1beta/models/xxx:generateContent端点,不支持 OpenAI 兼容的/v1/images/edits。Gemini 改回 supports_ref_image=False,有参考图时走纯文生图 - images.edit 的 quality 参数:OpenAI SDK images.edit 不接受 quality 参数(那是 images.generate 的),移除避免报错
- notes:
- Gemini 图片编辑需要对接原生 generateContent 接口才能支持参考图,作为未来方向
- GPT Image 1.5 通过向量引擎 /v1/images/edits 走通的前提是向量引擎正确转发 multipart/form-data
[CL-20260416-0600] 2026-04-16 06:00 — 多张参考图全链路支持
- tags: 前端, 后端, 参考图, 多图, OpenAI, images.edit, Provider, API, Agent Loop, UX
- affected_files:
- art-agent/frontend/src/components/chat/chat-input.tsx
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/components/chat/chat-messages.tsx
- art-agent/frontend/src/lib/api.ts
- art-agent/frontend/src/lib/types.ts
- 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/backend/app/config.py
- what: 从单张参考图扩展为多张参考图全链路支持(前端上传/显示 + API + Agent Loop + 生图 Provider)
- why: GPT Image 1.5 支持最多 16 张参考图(images.edit 端点),Gemini 支持 14 张,用户需要"保留图1主体+图2视角"等多图组合控制能力
- decisions:
- OpenAI Provider 有参考图时从 images.generate 切换到 images.edit 端点(而非统一用 edit)
- Replicate IP-Adapter 模型仍只支持单张,取 ref_image_urls[0] 兼容
- 后端 API 新增 ref_image_urls JSON 数组字段,保留旧 ref_image_url 单值字段向后兼容
- 前端 ChatMessage 新增 refImageUrls 数组字段,保留旧 refImageUrl 兼容已有聊天记录
- System Prompt 更新:LLM 需要理解多张参考图各自的角色并在 prompt 中传达
- notes:
- 向量引擎中转需支持 /v1/images/edits 端点才能真正生效,待验证
- Gemini 走 OpenAI 兼容 API 中转,images.edit 是否被正确转发取决于中转层实现
- _load_image_bytes 新函数用于将本地路径/data URI 转为 bytes 供 images.edit 使用
[CL-20260416-0510] 2026-04-16 05:10 — 生图模型新增 Gemini 3.1 Flash Image
- tags: 后端, 生图模型, Gemini, 向量引擎, config
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/.env
- what: IMAGE_MODELS 注册表新增 gemini-3.1-flash-image 条目,model_id 为 gemini-3.1-flash-image-preview,provider 为 openai
- why: 用户希望使用 Google Gemini 3.1 Flash 的原生生图能力,速度快价格低
- decisions: 复用已有的 OpenAIImageProvider(向量引擎中转兼容 OpenAI images/generations 端点),零代码改动
- notes: 如向量引擎对 Gemini 生图的返回格式有差异(如只返回 b64 不返回 URL),OpenAIImageProvider 已兼容两种格式
[CL-20260416-0500] 2026-04-16 05:00 — 全区域拖拽添加参考图
- tags: 前端, UX, 拖拽, 参考图, 上传
- affected_files:
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/components/chat/chat-input.tsx
- what: 将拖拽上传参考图的区域从底部输入栏扩展到整个对话区域(main),拖入时显示全屏半透明覆盖层提示"松开以添加参考图"
- why: 原先拖拽事件只绑在 ChatInput 组件(底部一小条),用户拖图片到对话区域无反应,体验不直觉
- decisions: ChatInput 改为 forwardRef,暴露
uploadFile方法(ChatInputHandle 接口)。page.tsx 的<main>处理 dragEnter/dragOver/dragLeave/drop,drop 时调用chatInputRef.current.uploadFile(file)复用已有上传逻辑。用 dragCounter ref 解决子元素 dragEnter/dragLeave 冒泡导致覆盖层闪烁的经典问题 - notes: ChatInput 自身的拖拽处理保留(作为 fallback),两套不冲突
[CL-20260416-0430] 2026-04-16 04:30 — 接入 GPT Image 1.5 生图模型(向量引擎中转)
- tags: 后端, 生图模型, GPT-Image, OpenAI, 向量引擎, provider
- affected_files:
- art-agent/backend/app/services/image_gen.py
- art-agent/backend/app/config.py
- art-agent/backend/.env
- what: 新增 OpenAIImageProvider,通过向量引擎 API 中转调用 GPT Image 1.5 模型生图。IMAGE_MODELS 注册表新增 gpt-image-1.5 条目,放在列表首位
- why: 用户希望使用 OpenAI 最新的 GPT Image 1.5 模型,该模型在 prompt 理解和文字渲染方面表现最好。通过向量引擎中转可复用已有的 API Key
- decisions: 新建
OpenAIImageProvider类(与ReplicateProvider并列),使用 OpenAI Python SDK 的images.generate端点,懒初始化客户端。支持 URL 和 base64 两种返回格式。Provider 注册键为"openai" - notes: GPT Image 系列不支持参考图(IP-Adapter),
supports_ref_image=False。如果后续需要接入 gpt-image-1 或 gpt-image-1-mini,只需在注册表新增条目,Provider 代码无需改动
[CL-20260416-0400] 2026-04-16 04:00 — Session 级参考图自动沿用 + 缺参考图前置校验
- tags: 前端, 后端, 参考图, UX, InstantStyle, 风格迁移
- affected_files:
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/components/chat/chat-input.tsx
- art-agent/backend/app/services/image_gen.py
- what: 发送消息使用参考图后,在 session 级别记住该参考图 URL,后续消息自动沿用。输入框上方显示"沿用上次参考图"提示条(含缩略图+清除按钮)。后端对需要参考图但未收到的模型(InstantStyle/Kolors 等)返回友好错误而非发给模型得到诡异结果
- why: 用户切换到 InstantStyle 模型后输入"再执行一次前面的任务",因前端每次发送后清除参考图状态,导致 InstantStyle 收不到 style_image 参数,模型返回 "No input, Save money" 文本。高频场景(换模型重试同任务)不应要求重新上传参考图
- decisions: 参考图以 per-session
Map<sessionId, serverUrl>存在useRef中(不持久化到 localStorage),切换 session 时恢复对应参考图。UI 提示条仅在无主动上传且有历史参考图时显示,用户可一键清除。后端在generate_images统一入口处加前置校验,比在各 provider 内部检查更统一 - notes: 参考图信息不写入 Session 类型定义(不持久化到 localStorage),因为服务端文件可能被清理;如果将来需要持久化,需考虑文件存在性校验
[CL-20260416-0300] 2026-04-16 03:00 — System Prompt 禁止 LLM 在回复中嵌入图片 Markdown 链接
- tags: 后端, agent-loop, system-prompt, LLM行为约束
- affected_files:
- art-agent/backend/app/agent/loop.py
- what: 在 System Prompt 注意事项中新增约束,禁止 LLM 在回复文字中使用
或sandbox:等 Markdown 图片语法 - why: GPT 系列模型在收到 generate_image 工具返回的本地路径后,会自行拼凑
sandbox:/generated/xxx.png格式的 Markdown 图片链接嵌入回复。该链接在前端不可渲染(sandbox:不是有效协议),且图片展示已由image_resultSSE 事件独立处理,文字中的链接纯属多余 - decisions: 通过 System Prompt 约束解决(而非后端过滤),因为这是 LLM 行为问题,从源头阻止更干净
- notes: 同时排查发现 gpt-5.4 在向量引擎中转后实际调用了 gpt-4o-mini,该问题属于向量引擎 API 侧的模型映射/降级,非代码 bug
[CL-20260416-0245] 2026-04-16 02:45 — 修复三点菜单被右侧对话区遮挡
- tags: 前端, UI, sidebar, 菜单, z-index
- affected_files:
- art-agent/frontend/src/components/sidebar/session-list.tsx
- what: 三点菜单从 fixed 右侧弹出改为 inline 下方展开,模型子菜单改为折叠式内联
- why: 菜单用 fixed 定位弹到侧栏右侧,被主内容区的 stacking context 遮挡
- decisions: 放弃 fixed+绝对坐标的弹出菜单方案,改为在会话项下方 inline 渲染。模型子菜单从右侧 absolute 弹出改为折叠展开式,带 max-h 240px 滚动防止过长
- notes: 移除了 menuPos 状态,简化了定位逻辑
[CL-20260416-0230] 2026-04-16 02:30 — LLM 模型注册表扩充:新增 5 个前沿模型,默认改为 GPT-5.4
- tags: 后端, config, LLM, 模型注册表
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/.env
- what: LLM_MODELS 注册表从 3 个模型扩充到 7 个,默认模型从 gpt-4o-mini 改为 gpt-5.4
- why: 用户需要使用 2026 年前沿模型(GPT-5.4, Claude Sonnet/Opus 4.6, Gemini 3.1 Pro, GLM-4.7)
- decisions: 所有新增模型走 vectorengine provider,model_id 使用向量引擎确认的名称;gpt-4o-mini 和 deepseek-chat 保留作为轻量/直连选项
- notes: 新增模型的 model_id 需向量引擎实际支持,如有出入需调整
[CL-20260416-0200] 2026-04-16 02:00 — 接入向量引擎中转 API + 对话级 LLM 模型切换
- tags: 后端, 前端, LLM, 向量引擎, 中转API, 模型切换, config, UI, session
- affected_files:
- art-agent/backend/app/config.py
- art-agent/backend/app/agent/loop.py
- art-agent/backend/app/api/chat.py
- art-agent/backend/.env
- art-agent/frontend/src/lib/types.ts
- art-agent/frontend/src/lib/api.ts
- art-agent/frontend/src/lib/app-context.tsx
- art-agent/frontend/src/components/sidebar/session-list.tsx
- art-agent/frontend/src/app/page.tsx
- what: 接入向量引擎中转 API(OpenAI 兼容格式),实现对话级 LLM 模型切换
- why: 用户需要通过中转 API 使用 OpenAI 系模型(GPT-4o/4o-mini),同时保留 DeepSeek 直连。每条对话应可独立选择不同模型
- decisions:
- 后端采用 LLM_MODELS 注册表 + provider 分发的架构(参考已有 IMAGE_MODELS 模式)
- provider 分为 vectorengine(向量引擎中转 OpenAI 系)和 deepseek(直连),各有独立的 API key 和 base_url
- 移除旧的 OPENAI_API_KEY/OPENAI_BASE_URL 全局配置,改为 provider 级别
- vision 能力检测改为从注册表读取,不再子串匹配模型名
- 前端对话列表的右键菜单改为显式三点按钮菜单,模型选择作为子菜单项
- Session 类型新增 llmModel 字段,llmModel 为空时使用后端默认
- notes:
- .env 中 VECTORENGINE_API_KEY 需要用户填入自己的 key
- Mem0 记忆系统仍使用 DEEPSEEK_API_KEY,不受影响
- 三点菜单预留了分享、归档等功能的扩展位
- 旧 localStorage 中没有 llmModel 字段的 Session 自动走默认模型
[CL-20260416-0030] 2026-04-16 00:30 — 青绿山水风格改造:从 Cyberpunk 霓虹转为千里江山图色调 + 云烟雾气动效
- tags: 前端, UI改造, 视觉风格, 青绿山水, 千里江山图, CSS动画, 色彩体系, 云烟, 雾气
- affected_files:
- art-agent/frontend/src/app/globals.css
- 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/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/profile-modal.tsx
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/gallery/page.tsx
- art-agent/frontend/src/app/login/page.tsx
- docs/art-agent/VISUAL-STYLE-GUIDE.md
- what: 全站视觉风格从 Cyberpunk Dark Neon 改为"青绿山水"——以《千里江山图》矿物质颜料色为灵感的现代演绎。色彩体系、动画效果、背景光晕全面重做
- why: 用户希望美术风格从赛博朋克调整为中国传统青绿山水的现代演绎,发光效果添加"雾气感"
- decisions:
- 色彩体系:深墨绿底色(#0C1210)+ 石青绿高亮(#4DB8A4)+ 石绿蓝辅助(#3A8FB7)+ 赭石红(#C4654A)+ 赭金(#B8935A)
- 新增 --mist 和 --gold CSS 变量
- 背景改为双层漂移云雾(body::before 60s + body::after 45s),inset 负值防漂移露白
- .glow-border hover 效果:conic-gradient 弧段从 20% 扩大到 40-50%(更柔和),旋转从 3s 减慢到 6s,blur 从 6px 增到 12px,transition 从 500ms 增到 600ms
- .glass-panel 加内层渐变 + blur 从 20px 增到 24px + saturate(1.1)
- 新增 .fog-scroll 类(mask-image 渐变遮罩),应用到聊天区和会话列表
- 11 个组件文件中的硬编码 rgba(0,229,160,...) 全部替换为 rgba(77,184,164,...)
- 动画命名从 glowSpin 改为 mistSpin
- notes:
- profile-modal.tsx 也有硬编码颜色,在计划外被一并修复
- Logo SVG 中的 #0B0E14 替换为新的 --bg-primary 色值 #0C1210
- VISUAL-STYLE-GUIDE.md 完全重写,包含新旧风格对比表
- source_chat: [青绿山水风格改造]
[CL-20260415-2330] 2026-04-15 23:30 — 用户个人信息 + 记忆查看面板:右上角菜单弹窗
- tags: 前端, 后端, 用户信息, 记忆系统, Mem0, Modal, ProfileModal, API
- affected_files:
- art-agent/backend/app/api/memory.py
- art-agent/backend/app/main.py
- art-agent/frontend/src/components/profile-modal.tsx
- art-agent/frontend/src/components/layout/top-nav.tsx
- what: 新增用户个人信息弹窗,展示用户基本信息和 Mem0 记忆系统中按时间分组的记忆列表
- why: 用户希望能查看记忆系统为自己记录了哪些信息,右上角菜单是最自然的入口
- decisions:
- 后端新建独立路由文件 api/memory.py(职责分离),暴露 GET /api/memory/list 端点
- 使用 Mem0 OSS
Memory.get_all(user_id=..., limit=200)获取全部记忆 - 同步调用通过
run_in_executor包装避免阻塞事件循环 - 前端用居中 Modal(非 Drawer/独立页),复用 glass-panel + Cyberpunk 风格
- 记忆按 created_at 时间分组(今天/最近7天/更早),不依赖 Mem0 不稳定的 categories 字段
- 只读模式,不实现删除功能
- notes:
- TopNav 的 username 行从静态文本改为可点击按钮,点击打开 ProfileModal
- TopNav return 改为 Fragment 以容纳 header + Modal 两个顶级元素
- ESC 键可关闭弹窗,点击遮罩层也可关闭
- source_chat: [用户个人信息记忆面板]
[CL-20260415-2230] 2026-04-15 22:30 — 光影流动边框效果:导航活动项 + 活动会话项
- tags: 前端, CSS动画, 视觉效果, conic-gradient, 霓虹边框, 导航, 侧边栏
- affected_files:
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/components/layout/top-nav.tsx
- art-agent/frontend/src/components/sidebar/session-list.tsx
- art-agent/frontend/src/components/sidebar/sidebar.tsx
- what: 实现 Dribbble 设计图中的边框光影流动效果,应用到导航活动 Tab 和活动会话项
- why: 用户指出按钮/标签缺少设计图中的发光+光影流动感。需要用 CSS 动画实现旋转锥形渐变边框
- decisions:
- 用
@property --glow-angle+conic-gradient实现旋转光线,3s 一圈 - 双伪元素方案:
::before做精确渐变边框(mask-composite 裁掉内部),::after做外层模糊扩散光晕 - 提供两个 CSS 类:
.glow-border(动画旋转,用于活动态)、.glow-border-static(静态发光,用于 hover) - 导航非活动项 hover 时用静态发光,不抢活动项视觉焦点
- 用
- notes:
@property需要浏览器支持 CSS Houdini(Chrome 85+, Edge 85+, Safari 15.4+),对目标用户群体覆盖足够- 光影流动 3s 周期可通过修改
glowSpin动画时长调整
- source_chat: [光影流动边框效果实现]
[CL-20260415-2200] 2026-04-15 22:00 — 全站 UI 风格改造:Cyberpunk Dark Neon 主题
- tags: 前端, UI改造, 视觉风格, Cyberpunk, Neon, 毛玻璃, 全局样式, 组件重构
- affected_files:
- art-agent/frontend/src/app/globals.css
- art-agent/frontend/src/app/page.tsx
- art-agent/frontend/src/app/gallery/page.tsx
- art-agent/frontend/src/app/login/page.tsx
- 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/chat/model-selector.tsx
- art-agent/frontend/src/components/detail/image-detail-panel.tsx
- docs/art-agent/VISUAL-STYLE-GUIDE.md
- what: 参考 SolCasino Dribbble 设计,将全站 UI 从朴素暗色主题改造为 Cyberpunk Dark Neon 风格
- why: 用户希望产品页面更具视觉冲击力,参考了 Dribbble 上的加密赌场 Cases 页面设计,提取其核心视觉语言(霓虹发光、毛玻璃、环境光晕、卡片网格)应用到美术 Agent 工具
- decisions:
- 主色从紫色(#6366f1)改为青绿霓虹(#00E5A0),更符合 Cyberpunk 调性
- 新增 CSS 变量:
--bg-card,--hot,--accent-secondary,--border-glow - 背景加 body::before 环境光晕(两团 radial-gradient 光斑)
- 全部面板改为 backdrop-blur 毛玻璃效果
- 圆角从 rounded-lg/2xl 统一升级为 rounded-xl/2xl
- 新增
.glass-panel和.neon-border全局 CSS 类 - 图片卡片 hover 时有微缩放(1.03) + 霓虹边框发光
- 登录页加独立环境光背景 + 毛玻璃表单容器
- Gallery 卡片改为 neon-border 风格,选中时霓虹发光阴影
- 收藏色从金色改为 accent 青绿色,保持色彩一致性
- notes:
- 同时编写了视觉风格文档
docs/art-agent/VISUAL-STYLE-GUIDE.md,记录完整的设计规范 - annotation-canvas.tsx 未做改动(工具型组件,暗色已适配)
- 所有组件保留原有功能和交互逻辑,仅变更视觉层
- 同时编写了视觉风格文档
- source_chat: [Cyberpunk UI 风格改造]
[CL-20260414-2350] 2026-04-14 23:50 — 修复登录后 useApp must be used within AppProvider 报错
- tags: bug修复, 前端, 认证, AppProvider, 初始化时序
- affected_files:
- art-agent/frontend/src/lib/app-context.tsx
- what: 修复登录成功跳转到主页时 useApp() 拿到 null context 报错
- why: AppProvider 在
!initialized时直接渲染<>{children}</>(不提供 AppContext),但已登录用户跳转到/后page.tsx立即调用useApp(),而useEffect中的setInitialized(true)还没执行,导致 context 为 null - decisions: 区分两种
!initialized场景——已认证时显示加载状态(不渲染需要 context 的子组件),未认证时渲染 children(供 /login 页面使用) - notes: 这是 React 的 useEffect 异步特性导致的时序问题,登录跳转瞬间路由已变但 state 还没更新
- source_chat: 修复 AppProvider 初始化时序
[CL-20260414-2340] 2026-04-14 23:40 — 用 pwdlib (Argon2id) 替换 passlib (bcrypt),彻底解决兼容性问题
- tags: 重构, 安全, 密码哈希, Argon2, pwdlib, passlib, 依赖升级
- affected_files:
- art-agent/backend/app/auth.py
- art-agent/backend/requirements.txt
- docs/art-agent/USER-SYSTEM.md
- what: 将密码哈希从 passlib+bcrypt 替换为 pwdlib+Argon2id
- why: passlib 已停止维护,Python 3.13+ 不可用,且与 bcrypt 5.x 有兼容性问题。pwdlib 是 passlib 的现代替代,原生支持 bcrypt 5.x
- decisions: 选择 pwdlib[argon2,bcrypt] 而非纯 argon2-cffi:pwdlib 提供与 passlib 类似的统一接口,同时内置 BcryptHasher 兼容已有旧哈希。新密码一律 Argon2id,旧 bcrypt 哈希仍可验证(过渡期)
- notes: 已有 admin 用户的 bcrypt 哈希无需迁移,下次改密时自动升级为 Argon2id 格式。不再需要锁定 bcrypt 版本
- source_chat: 替换 passlib 为 pwdlib
[CL-20260414-2330] 2026-04-14 23:30 — 修复 passlib + bcrypt 5.x 兼容性问题,锁定 bcrypt<4.1
- tags: bug修复, 依赖, bcrypt, passlib, 用户系统
- affected_files:
- art-agent/backend/requirements.txt
- docs/art-agent/USER-SYSTEM.md
- what: 修复 passlib 1.7.4 与 bcrypt 5.0.0 不兼容导致后端启动失败
- why: passlib 已停止维护,bcrypt 4.1+ 移除了
__about__属性且改变了密码长度校验行为,导致 passlib 加载后端报AttributeError+ValueError - decisions: 降级 bcrypt 到 4.0.1 并锁定
>=4.0.1,<4.1,而非替换 passlib(改动最小) - notes: 如果未来需要升级 bcrypt,需替换 passlib 为 bcrypt 直接调用或改用 argon2-cffi 等不依赖 passlib 的方案
- source_chat: 修复 bcrypt 兼容性
[CL-20260414-2300] 2026-04-14 23:00 — 用户系统:管理员邀请制 + JWT 认证 + 数据按用户隔离
- tags: 用户系统, 认证, JWT, SQLite, 数据隔离, 多用户, 安全
- affected_files:
- art-agent/backend/requirements.txt
- art-agent/backend/app/db.py(新建)
- art-agent/backend/app/auth.py(新建)
- art-agent/backend/app/api/auth.py(新建)
- art-agent/backend/app/api/admin.py(新建)
- art-agent/backend/app/api/chat.py
- art-agent/backend/app/main.py
- art-agent/backend/app/agent/loop.py
- art-agent/backend/.env.example
- art-agent/frontend/src/lib/auth-context.tsx(新建)
- art-agent/frontend/src/lib/auth-guard.tsx(新建)
- art-agent/frontend/src/lib/api.ts
- art-agent/frontend/src/lib/store.ts
- art-agent/frontend/src/lib/app-context.tsx
- art-agent/frontend/src/app/layout.tsx
- art-agent/frontend/src/app/login/page.tsx(新建)
- art-agent/frontend/src/components/layout/top-nav.tsx
- art-agent/frontend/src/components/chat/model-selector.tsx
- what: 为 EPEEKit 添加完整用户系统:管理员创建账号、JWT 登录、所有 API 鉴权、Mem0 记忆按用户隔离、前端 localStorage 按用户隔离
- why: 之前无认证,所有访问者共享
default_user的记忆,多人通过穿透使用时数据互相混杂 - decisions:
- SQLite(data/epeekit.db)存用户表,零部署成本,与 Qdrant 数据目录在同一 data/ 下
- JWT access token (30min) + refresh token (7d),passlib bcrypt 密码哈希
- 不开放注册,管理员通过 /api/admin/users 创建账号
- 首次启动自动创建 admin 账号(密码从 ADMIN_DEFAULT_PASSWORD 读取或随机生成打印到控制台)
- 前端 localStorage 键名加 user_id 前缀(如
epeekit-{userId}-sessions)实现数据隔离 - Mem0 的 user_id 从 JWT 中解析的真实用户 ID 传入,替代硬编码
default_user - AppProvider 未登录时返回
<>{children}</>而非 null,确保 /login 页面可渲染
- notes:
- 旧 Mem0 数据仍在
default_user下,不自动迁移,新用户从零积累 - 前端 401 响应自动跳转 /login,XHR(上传)和 fetch 均处理
- TopNav 右侧新增用户菜单(显示名称 + 退出登录)
- 需在 .env 中配置 JWT_SECRET,否则启动时 RuntimeError
- 旧 Mem0 数据仍在
[CL-20260414-2230] 2026-04-14 22:30 — 对话区域新增"回到底部"浮动按钮
- tags: 前端, UX, 滚动, 浮动按钮
- affected_files:
- art-agent/frontend/src/app/page.tsx
- what: 当用户在对话中间位置时,显示一个浮动的向下箭头按钮,点击平滑滚到最新消息
- why: 长对话中浏览历史消息后,缺少快速回到最新内容的入口
- decisions: 复用已有的 scroll 事件监听器检测距底部距离(阈值 200px),按钮定位在输入框上方右侧。切换会话恢复位置后也主动检查按钮显示状态。使用已有的
fadeInkeyframes 动画 - notes: 按钮使用
absolute定位在main容器内,bottom-16避开输入框
[CL-20260414-2220] 2026-04-14 22:20 — 修复长对话切换回来滚动位置上移:scroll 事件实时保存取代 effect 延迟保存
- tags: 前端, bug修复, 滚动, 多会话, UX, scroll 事件
- affected_files:
- art-agent/frontend/src/app/page.tsx
- what: 修复长对话切换回来后滚动位置上移的 bug
- why: 之前在 useEffect 中保存旧会话的 scrollTop,但 effect 执行时 DOM 已经渲染了新会话(短对话)的内容,scrollHeight 变小,浏览器自动将 scrollTop 钳位(clamp)到
scrollHeight - clientHeight,导致保存的值远小于真实位置。最长的对话受影响最大,因为它的 scrollTop 差值最大 - decisions: 改用 scroll 事件监听器实时保存当前会话的 scrollTop 到 Map 中。这样在任何 state 变更或 DOM 重渲染之前,最新的滚动位置已经被记录。切换期间(
isSwitching=true)跳过 scroll handler 写入,避免恢复过程中的中间值覆盖正确值 - notes: 这是 CL-20260414-2210 的后续修复。scroll 事件加了
{ passive: true }避免影响滚动性能。cleanup 函数在activeSessionId变化时正确移除旧 listener
[CL-20260414-2210] 2026-04-14 22:10 — 修复会话切换时滚动位置跳动:解决时序竞争 + 抑制切换期间 smooth scroll
- tags: 前端, bug修复, 滚动, 多会话, UX, 时序
- affected_files:
- art-agent/frontend/src/app/page.tsx
- what: 修复会话切换时滚动条频繁跳动的问题
- why: 上一版修复(CL-20260414-2200)存在时序竞争:
useEffect中requestAnimationFrame执行时 React 可能还没将新会话的 messages 渲染到 DOM 中,导致scrollHeight是旧值。另外切换期间scrollToBottom(smooth)没有被抑制,smooth 动画与位置恢复互相干扰 - decisions:
- 拆为两个 effect:第一个(依赖
activeSessionId)只做保存旧位置 + 设isSwitching标记;第二个(依赖activeSessionId+messages.length)在 messages 内容更新后才恢复位置 - 新增
isSwitchingref,切换期间抑制非 instant 的scrollToBottom调用,避免 smooth 动画干扰 - instant 模式改为直接赋值
el.scrollTop = ...,不再走 setTimeout,消除额外异步 messages声明提前到 effect 之前,确保messages.length可在 effect 依赖中使用
- 拆为两个 effect:第一个(依赖
- notes: 图片异步加载仍可能导致 scrollHeight 变化使恢复位置偏移,但这是更深层问题,当前改动先解决核心的切换跳动
[CL-20260414-2200] 2026-04-14 22:00 — 修复多会话滚动位置共享 bug:per-session 独立记录 + 切换恢复
- tags: 前端, bug修复, 滚动, 多会话, UX
- affected_files:
- art-agent/frontend/src/app/page.tsx
- what: 修复多会话切换时滚动条位置共享的 bug,实现 per-session 滚动位置独立记录与恢复
- why: 所有会话共用同一个 scrollRef DOM 元素,切换会话时既不保存旧会话的 scrollTop,也不恢复目标会话的位置。导致:从长会话切到短会话可能看到空白底部,从短会话切到长会话不会停在最新消息处
- decisions: 用
useRef<Map<string, number>>在内存中记录每个会话的 scrollTop(而非 localStorage),因为滚动位置是临时状态,刷新页面后重新滚到底部是合理的默认行为 - notes:
- 切换会话时,先保存离开会话的 scrollTop,再用
requestAnimationFrame等 DOM 渲染完新消息后恢复目标位置 - 无记录的会话(首次进入或新建)使用
scrollToBottom(true)即时滚到底部 scrollToBottom改为useCallback并新增instant参数,恢复位置时用 instant 避免视觉跳动,正常对话流用 smooth- 将
scrollToBottom加入handleSend的依赖数组,修复潜在的陈旧闭包问题
- 切换会话时,先保存离开会话的 scrollTop,再用
[CL-20260413-2320] 2026-04-13 23:20 — 集成 Mem0 记忆系统:滑动窗口 + 跨会话长期记忆
- tags: Mem0, 记忆系统, 上下文管理, 长期记忆, Ollama, embedding, 滑动窗口
- affected_files:
- art-agent/backend/app/memory.py(新建)
- art-agent/backend/app/agent/loop.py
- art-agent/backend/app/config.py
- art-agent/backend/app/api/chat.py
- art-agent/backend/requirements.txt
- art-agent/backend/.env
- art-agent/frontend/src/lib/api.ts
- art-agent/frontend/src/app/page.tsx
- .gitignore
- what: 集成 Mem0 开源版作为统一记忆方案,一次性解决两个延期方案:上下文管理(滑动窗口截断)和跨会话长期记忆(事实提取+语义检索)
- why: 当前对话全量透传无截断,长对话会超 token 上限且费用线性增长;且每次会话从零开始无法记住用户偏好
- decisions:
- 选择 Mem0 OSS 自部署(非平台托管),完全本地化,数据在 ./data/qdrant
- DeepSeek 作为事实提取 LLM(复用现有 key,成本极低)
- Ollama nomic-embed-text 作为本地 embedding(免费,768维,性能足够)
- 不做静默降级:Ollama 不可用 → 阻止启动;search 失败 → SSE error 中断对话;add 失败 → SSE warning 告知用户
- 用 asyncio.run_in_executor 异步执行 memory.add(),不阻塞用户体验
- notes:
- 测试时创建了 data/qdrant 目录(含测试记忆数据),已加入 .gitignore
- Ollama 需要作为后台服务保持运行
- 首次 Mem0 初始化约需 5-8 秒(创建 Qdrant collection)
- 解决了延期方案 [context-window-management] 和 [mem0-long-term-memory]
- source_chat: Mem0 记忆系统集成
[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: waitheader,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=300header → 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=60header(合法);(2) 内部 timeoutread=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),模型名称通过 SSEimage_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_errorSSE 事件类型,将图片生成工具的具体错误信息(如 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_urlcontent 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 传给
<img>标签 - 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 后进行