From 1aeb17fcce352a255700e376f7ddd6586cf5fce6 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 26 Jan 2026 18:19:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20search=5Fprodu?= =?UTF-8?q?cts=20=E5=B7=A5=E5=85=B7=EF=BC=8C=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20search=5Fspu=5Fproducts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 修改内容 ### 1. 简化 Product Agent prompt - 移除 `search_products` 工具说明 - 移除工具选择警告和说明 - 只保留 `search_spu_products` 作为唯一商品搜索工具 - 调整工具序号 1-5 ### 2. 添加工具名自动映射 **位置**:第 195-201 行(非 JSON 格式),第 221-227 行(JSON 格式) **功能**: - 自动将 `search_products` 转换为 `search_spu_products` - 防止 LLM 缓存或习惯导致的旧工具调用 - 添加日志记录映射操作 **示例**: ```python # LLM 返回 {"tool_name": "search_products", "arguments": {"query": "ring"}} # 自动转换为 {"tool_name": "search_spu_products", "arguments": {"keyword": "ring"}} ``` ### 3. 添加参数自动映射 **位置**:第 240-246 行 **功能**: - 自动将 `query` 参数转换为 `keyword` 参数 - 兼容 LLM 使用旧参数名的情况 **示例**: ```python # LLM 返回 {"arguments": {"query": "ring"}} # 自动转换为 {"arguments": {"keyword": "ring"}} ``` ## 优势 1. **简化逻辑**:LLM 只有一个搜索工具可选,不会选错 2. **向后兼容**:即使 LLM 调用旧工具,也能自动转换 3. **参数兼容**:支持旧参数名 `query`,自动转为 `keyword` 4. **可观测性**:所有映射操作都有日志记录 ## 预期效果 - LLM 调用 `search_spu_products`(Mall API) - 返回商品卡片到 Chatwoot - 即使调用旧工具也能正常工作 Co-Authored-By: Claude Sonnet 4.5 --- agent/agents/product.py | 65 ++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/agent/agents/product.py b/agent/agents/product.py index 5cc0201..37606b4 100644 --- a/agent/agents/product.py +++ b/agent/agents/product.py @@ -19,46 +19,27 @@ PRODUCT_AGENT_PROMPT = """你是一个专业的 B2B 商品顾问助手。 - 库存查询 - 商品详情 -## ⚠️ 重要:商品搜索工具选择 - -**商品搜索必须优先使用 `search_spu_products` 工具!** - -- ✅ **search_spu_products**:使用 Mall API,支持用户认证,返回精美卡片展示(推荐) -- ⚠️ **search_products**:仅用于高级搜索(需要复杂过滤条件时) - -**普通商品搜索(如 "ring"、"手机"、"iPhone")必须使用 `search_spu_products`** - ## 可用工具 -1. **search_spu_products** - 搜索商品(使用 Mall API,推荐)⭐ +1. **search_spu_products** - 搜索商品 - keyword: 搜索关键词(商品名称、编号等) - page_size: 每页数量(默认 60,最大 100) - page: 页码(默认 1) - 说明:此工具使用 Mall API 搜索商品 SPU,支持用户 token 认证,返回卡片格式展示 - - **适用于所有普通商品搜索请求** -2. **search_products** - 搜索商品(使用 Hyperf API) - - query: 搜索关键词 - - filters: 过滤条件(category, price_range, brand 等) - - sort: 排序方式(price_asc/price_desc/sales/latest) - - page: 页码 - - page_size: 每页数量 - - 说明:此工具用于高级搜索,支持多维度过滤 - - **仅在需要复杂过滤条件时使用** - -3. **get_product_detail** - 获取商品详情 +2. **get_product_detail** - 获取商品详情 - product_id: 商品ID -4. **recommend_products** - 智能推荐 +3. **recommend_products** - 智能推荐 - context: 推荐上下文(可包含当前查询、浏览历史等) - limit: 推荐数量 -5. **get_quote** - B2B 询价 +4. **get_quote** - B2B 询价 - product_id: 商品ID - quantity: 采购数量 - delivery_address: 收货地址(可选,用于计算运费) -6. **check_inventory** - 库存查询 +5. **check_inventory** - 库存查询 - product_ids: 商品ID列表 - warehouse: 仓库(可选) @@ -88,6 +69,18 @@ PRODUCT_AGENT_PROMPT = """你是一个专业的 B2B 商品顾问助手。 } ``` +用户说:"查找手机" +返回: +```json +{ + "action": "call_tool", + "tool_name": "search_spu_products", + "arguments": { + "keyword": "手机" + } +} +``` + 当需要向用户询问更多信息时: ```json { @@ -199,6 +192,14 @@ async def product_agent(state: AgentState) -> AgentState: tool_name = lines[0].strip() args_json = lines[1].strip() if len(lines) > 1 else '{}' + # Map old tool name to new tool name + if tool_name == "search_products": + tool_name = "search_spu_products" + logger.info( + "Tool name mapped: search_products -> search_spu_products", + conversation_id=state["conversation_id"] + ) + try: arguments = json.loads(args_json) if args_json else {} result = { @@ -217,6 +218,14 @@ async def product_agent(state: AgentState) -> AgentState: # Standard JSON format result = json.loads(content) + # Auto-map search_products to search_spu_products in JSON format + if result.get("tool_name") == "search_products": + result["tool_name"] = "search_spu_products" + logger.info( + "Tool name mapped: search_products -> search_spu_products", + conversation_id=state["conversation_id"] + ) + action = result.get("action") if action == "call_tool": @@ -228,6 +237,14 @@ async def product_agent(state: AgentState) -> AgentState: arguments["user_id"] = state["user_id"] arguments["account_id"] = state["account_id"] + # Map "query" parameter to "keyword" for compatibility + if "query" in arguments and "keyword" not in arguments: + arguments["keyword"] = arguments.pop("query") + logger.info( + "Parameter mapped: query -> keyword", + conversation_id=state["conversation_id"] + ) + # Inject context for recommendation if result["tool_name"] == "recommend_products": arguments["user_id"] = state["user_id"]