21 Feb, 2026

4 commits

  • tangwang
     
  • === 修改概要 ===
    
    1. 商品卡片 hover 操作:Similar products + 勾选
    2. 底部悬浮 Ask / Compare 操作条
    3. 右侧抽屉(Modal 式覆盖层):Similar 搜索结果 / Compare 占位
    4. 引用商品对话:输入区展示已引用商品(可删)、发送时注入后端格式前缀
    5. 抽屉关闭:HTML 按钮 + JS 即时隐藏 + replaceState,不刷背景页
    6. 多消息重复 [SEARCH_REF] 导致控件 key 冲突:按消息+块维度加唯一前缀
    
    --- 1. 会话状态 (initialize_session) ---
    
    - selected_products: dict, key -> 商品摘要(ref_id, spu_id, sku_id, title, price, tags, specifications),用于勾选与 Ask/Compare
    - side_panel: { visible, mode, payload },mode 为 "similar" | "compare",payload 为 ref_id/query 或已选商品列表
    - referenced_products: list[dict],当前输入区「已引用商品」,点击 Ask 后填入,发送后清空
    
    --- 2. 商品卡片 (display_product_card_from_item) ---
    
    - 新增参数 widget_prefix(默认 ""),与 _product_key 组成 key_suffix,用于 st.button / st.checkbox 的 key,避免跨消息重复
    - 卡片底部操作条(product-card-actions):Similar products 按钮、Select 勾选框
    - Similar 点击:_run_similar_search(product.title) 调用 make_search_products_tool,解析返回的 [SEARCH_REF:ref_id],打开 side_panel mode=similar
    - 勾选:更新 selected_products[pkey],pkey = _product_key(ref_id, index, product)
    
    --- 3. 底部操作条 (render_bottom_actions_bar) ---
    
    - 仅当 len(selected_products) > 0 时展示
    - Ask:将 selected 写入 referenced_products,rerun
    - Compare:打开 side_panel mode=compare,payload=已选列表,rerun
    
    --- 4. 右侧抽屉 (render_side_drawer) ---
    
    - 在 main() 中优先渲染(紧跟 initialize_session 与 query_params 处理),保证 fixed 定位相对视口
    - 结构:backdrop (fixed 全屏) + panel (fixed 右侧,top:56px 避顶栏),z-index 999998/999999
    - similar 模式:用 payload.ref_id 从 global_registry 取 SearchResult,渲染最多 12 条商品卡片(纯 HTML,无 Streamlit 控件)
    - compare 模式:列出已选商品标题/价格 +「对比功能暂未实现」提示
    - 关闭:panel 内 <button id="side-drawer-close-btn">✕ 关闭</button>;通过 streamlit.components.v1.html 注入 JS:
      - 点击按钮或 backdrop 时,将 backdrop/panel 设为 display:none(即时隐藏,不刷新)
      - history.replaceState 设置 ?close_side_panel=1(同页,不导航)
    - main() 顶部若检测到 close_side_panel,清除 side_panel 状态并 st.query_params.clear(),下次自然 rerun 时不再渲染抽屉
    
    --- 5. 引用商品对话 ---
    
    - _product_to_info:增加 sku_id(同 spu_id)、tags、specifications,供后端前缀使用
    - _build_reference_prefix(products):生成「引用 x 款商品:」+ 每款一行「sku_id=...; title=...; price=...; tags=...; specifications=...」
    - render_referenced_products_in_input:在输入框上方展示 referenced_products,每项可点 ✕ 移除
    - 发送逻辑:若有 referenced_products,agent_query = _build_reference_prefix(referenced_products) + "\n\n" + raw_user_query;消息列表存 raw_user_query;发送后 referenced_products = []
    
    --- 6. 多消息重复 key 修复 (StreamlitDuplicateElementKey) ---
    
    - 同一条 [SEARCH_REF:xxx] 在不同消息中会重复渲染,导致 similar_/select_ 的 key(仅 ref_id+index+spu_id)重复
    - 方案:为每个「消息+ref 块」引入唯一 widget_prefix
      - display_message(message, msg_index=0);main 中 for msg_idx, message in enumerate(messages): display_message(message, msg_index=msg_idx)
      - render_message_with_refs(..., msg_index=0);对每个 ref 段 widget_prefix = f"m{msg_index}_r{i}"(i 为 parts 下标)
      - render_search_result_block(result, widget_prefix="");将 widget_prefix 传入 display_product_card_from_item
      - display_product_card_from_item(..., widget_prefix="");key_suffix = f"{widget_prefix}_{pkey}" if widget_prefix else pkey;按钮/勾选框 key 使用 key_suffix
    
    --- 7. 其他 ---
    
    - Clear Chat 时一并清空 selected_products、referenced_products、side_panel
    - CSS:product-card-wrapper/actions、side-drawer-*、bottom-actions-bar、#side-drawer-close-btn 等
    - 依赖:streamlit.components.v1 用于注入抽屉关闭脚本
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    tangwang
     
  • tangwang
     
  • tangwang
     

20 Feb, 2026

4 commits

  • ## 搜索工具与质量评估
    - _assess_search_quality 仅返回 (labels, quality_summary):去掉 verdict(优质/一般/较差)及依赖逻辑;prompt 要求 LLM 输出 labels + quality_summary(1–2 句:结果主要包含什么、是否基本满足意图、匹配度)。
    - 工具返回格式统一为:【搜索完成】query='...' + 结果引用 [SEARCH_REF:ref_id] + 搜索结果质量情况(评估总条数、Highly Relevant / Partially Relevant 条数)+ results list(top10 标题)。
    - 精简 prompt 与日志:评估输入仅保留序号+标题;删除 verdict_hint、逐条 SEARCH_RESULT_ITEM/SEARCH_RESULT_PRODUCT 日志,保留单行注册日志。
    
    ## 三级标签改为英文
    - 完美匹配 → Highly Relevant;部分匹配 → Partially Relevant;不相关 → Not Relevant。
    - 全量替换:search_tools(prompt、valid、统计与过滤)、search_registry(ProductItem.match_label 默认及注释、SearchResult 注释)、app.py(卡片 label_style、结果块头部与筛选逻辑)。
    
    ## Registry 与 UI
    - SearchResult 移除 quality_verdict 字段;quality_summary 由 _assess_search_quality 的 LLM 返回写入。
    - 结果块头部不再展示 verdict 图标/文案,改为展示 query + Highly/Partially Relevant 件数 + quality_summary(若有)。
    
    ## Agent
    - 系统提示词调整:角色与原则、价值提供与信息收集、search_products 与 [SEARCH_REF:xxx] 使用说明。
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    tangwang
     
  • - Search image_url: parse results[].image_url, add _normalize_image_url() to
      convert protocol-less URLs (////host/path) to https://host/path; fix double
      slash (use https:// + url.lstrip("/") so normalized URL has single //).
    - Logging: log full LLM request/response (LLM_REQUEST, LLM_RESPONSE), full
      tool call results (TOOL_CALL_RESULT); for search tool log SEARCH_RESULT
      summary and per-item SEARCH_RESULT_ITEM (image_url_raw) and
      SEARCH_RESULT_PRODUCT (image_url_normalized).
    - Streamlit: replace deprecated use_container_width=True with width="stretch"
      for st.image and st.button.
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    tangwang
     
  • ## 搜索结果管理与人机回复引用
    - 新增 app/search_registry.py:SearchResultRegistry + SearchResult/ProductItem 数据结构,按 session 存储每次搜索的 query、质量评估与商品列表。
    - 搜索工具改为工厂 make_search_products_tool(session_id, registry):每次搜索后由 LLM 对 top20 打标(完美匹配/部分匹配/不相关),产出整体 verdict(优质/一般/较差),仅将「完美+部分」写入 registry 并返回摘要 + [SEARCH_REF:ref_id];不再向 Agent 返回完整商品列表。
    - 废除 extract_products_from_response:最终回复中通过内联 [SEARCH_REF:xxx] 引用「搜索结果块」,UI 用 SEARCH_REF_PATTERN 解析后从 registry 取对应 SearchResult 渲染 query 标题 + 商品卡片,避免 LLM 复述商品列表,节省 token 并减少错误。
    
    ## 系统提示与行为约束
    - 系统提示词通用化(不绑定时尚品类),明确四步:理解意图 → 规划 2~4 个 query → 执行搜索并评估 → 撰写回复。
    - 要求同一条回复中并行发起 2~4 次 search_products(不同 query),利用 LangGraph ToolNode 的并行执行缩短等待;禁止串行「搜一个看一个再搜下一个」。
    - 轮次上限:最多两轮搜索(两轮 = 两次「Agent 发 tool_calls → Tools 执行 → 返回」);若已有优质/一般结果则直接写回复,仅当全部较差时允许第二轮(最多再 1~2 个 query)。图逻辑增加 n_tool_rounds 状态与 agent_final 节点,两轮后强制进入「仅回复、不调工具」的 agent_final,避免无限重搜。
    
    ## 前端与工具导出
    - app.py:render_message_with_refs(content, session_id) 按 [SEARCH_REF:xxx] 切分并渲染;render_search_result_block 展示 query + 质量 + 商品卡片;display_product_card_from_item 支持 image_url/本地图/占位;Clear Chat 时 clear_session(registry)。
    - app/tools/__init__.py:改为导出 make_search_products_tool、web_search,不再导出已移除的 search_products 顶层名。
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    tangwang
     
  • tangwang
     

12 Feb, 2026

3 commits