From febe04a5691033c88b43bb5aa5ca3da8fa5440c5 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 20 May 2026 16:33:47 +0800 Subject: [PATCH] =?UTF-8?q?ui(media):=20tool=20=E7=BB=93=E6=9E=9C=E4=B8=8E?= =?UTF-8?q?=20assistant=20=E6=AD=A3=E6=96=87=E5=90=8C=E8=B7=AF=E5=BE=84=20?= =?UTF-8?q?chip/inline=20=E5=9B=BE=E5=8E=BB=E9=87=8D=20=E2=80=94=20Set=20O?= =?UTF-8?q?(n)=20+=20CLAUDE.md=20=E5=8A=A0=20"=E5=AE=9E=E6=96=BD=E5=89=8D?= =?UTF-8?q?=E5=85=88=E5=AF=B9=E6=96=B9=E6=A1=88"=20=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 11 +++++++++++ PROGRESS.md | 2 ++ web/static/dev.html | 28 ++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ebd41d1..357ccf7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,6 +6,17 @@ - 跑脚本 / 测试一律用 `.venv/Scripts/python.exe ...`,**不要用全局 `python`**(没装 litellm/python-pptx 等会报 ModuleNotFoundError) - requirements 见 `requirements.txt` +## 实施前先对方案 + +非平凡改动(改 >1 个文件 / 涉及行为变化 / 多个候选方案有取舍)动手前**先用自然语言把方案讲给用户确认,认可后再写代码**。一次性 bug 修复 / 改字面量 / 样式微调 / 加日志这类无歧义动作可以直接动手。 + +讲方案时要包含: +- 问题定位(指到具体文件 / 行号 / 当前行为) +- 至少 1 个替代方案 + 当前方案为什么更优 +- 涉及性能 / 兼容 / 数据迁移时主动说 + +理由:开发期需求漂移快,写到一半被推翻代价高 —— 口头对齐方案是最低成本的纠偏机会。 + ## 开发阶段心智 当前处于**开发测试期**(开发自用 + 内部测试,DB 已有真实测试数据)。改需求 / 重构时,**以最优实现为准,不为旧数据 / 旧字段 / 旧 API 留兼容层**,但**不删现有数据**: diff --git a/PROGRESS.md b/PROGRESS.md index 3aae313..66e07c6 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -23,6 +23,8 @@ ### 2026-05-20 +- **dev SPA 中间产物 chip / inline 图去重 + CLAUDE.md 新增"实施前先对方案"段**:用户报"工具结果里挂了一张图,后面 assistant 正文又挂了一张同图,有点重复"。根因:`renderArtifactBarHtml(extractArtifactRels(...))` 在 5 个渲染点都跑过 — `renderMessages` 里 tool 结果卡 / assistant 正文 / assistant tool_calls args 各一处,`handleSseEvent` 里 tool_call / tool_result 各一处。同一 rel 在 tool 结果与紧随 assistant 正文里同时出现(模型 echo 路径)→ 历史回放渲两次。修法:`renderMessages` 顶部建 `const seenRels = new Set()` + `pickFresh(rels)` 闭包,3 个调用点(tool 结果 / assistant 正文 / tool_calls args)全部包一层 — chronological 顺序,首次出现保留(tool 结果常在前),后续重复丢;SSE `ctx` 加 `seenRels: new Set()`,tool_call / tool_result 两 handler 共享去重。**对比 querySelector 版**:DOM 查询版 O(n²)(每条 card 渲染时扫 wrap 已有 `[data-rel]`),Set 版 O(n) 无查询,代码量相同还把"什么是 source of truth"明确(不依赖 DOM 已挂 chip 这个隐式状态)。**CLAUDE.md 增段**:开发期需求漂移快,非平凡改动(改 >1 文件 / 行为变化 / 多候选取舍)动手前先用自然语言把方案讲给用户确认,认可后再写代码;一次性 bug 修 / 字面量 / 样式微调可直接动手。方案描述要包含问题定位(文件 / 行号)+ 至少 1 个替代方案 + 涉及性能 / 兼容 / 数据迁移时主动说。**没动**:`extractArtifactRels` / `renderArtifactBarHtml` 实现(它们内部本身已 Set 去重单次调用内重复)、`_workingDirName` / chip 点击委托 / 媒体 blob 缓存、后端、DESIGN(纯前端 UX 修复)、RUN(无对外行为变化)。 + - **dev SPA 顶栏加生图模型下拉 + 中间产物图片/视频内联展示**:用户要 ① 项目栏右侧的模型选区加一个生图模型选择(目前只 seedream,默认选上),② 中间产物若是图片/视频直接在对话区展示(不只点击预览)。**生图选择范式判断**:不入 task 列(seedream/seedance 是 tool 范畴,non-chat,task 切粒度太粗;且现在仅一个 variant,加 DB 列纯负债)→ 走**消息级**:UI 下拉的选择跟 `POST /v1/tasks/{id}/messages` body 的 `image_model` 字段一起发,`_run_agent_bg` → `build_agent(image_variant=...)` → seedream tool 装配时按 key 挑 yaml 里 `image` 段的对应 variant_cfg;不入 DB,本 run 内多次 tool call 共用,下条消息可重选。**后端新接口** `GET /v1/image_models`(scan `config/media/doubao.yaml` image 段返 `{variant, display_name, model_id, price_cny_per_image, is_default}` 列表;不要求 `ARK_API_KEY` 已设 — UI 只展示元数据,真调时 `ArkConfig.load()` 那侧再过 key 检查),`_resolve_image_model(variant)` 校验存在性(空串 → 透传走 fallback,非空 → 必须命中 yaml,否则 400)。`agent_builder.build_agent` 新参 `image_variant: str = ""`:非空且命中 → 用它装 SeedreamTool;不命中(yaml 改动后旧选择 stale)静默回 fallback;空 → 沿用"取第一个 variant"。**前端**:`state.imageModels` + `state.imageModel`(per-session,不持久);`loadModels()` 同时拉 `/v1/image_models` 并锁第一个为默认;`renderImageModelDropdown()` 在 `renderModelDropdown` 旁画一个 `生图 [▾]`(yaml 无 variant 时不画);`onChangeImageModel` 纯前端 state 更新无 PATCH;`sendMessage` 把 `state.imageModel` 跟在 POST body 上发出去。**内联媒体**:`_EXT_GROUPS` 加 `video: {mp4,webm,mov,mkv,m4v}` 集合;`renderArtifactBarHtml` 按 `_categorize(rel)` 分支:image/video → 占位 ``,其他 → 沿用 `.art-chip`;新 `upgradeMediaArtifacts(root)` DOM walk 把占位异步换 ``/`