refactor: 将 search_spu_products 重命名为 search_products

## 修改内容

### 1. Product Agent prompt
- 将工具名从 `search_spu_products` 改为 `search_products`
- 更新所有示例代码
- 保持功能说明不变(Mall API SPU 搜索)

### 2. Product Agent 代码
**文件**: agent/agents/product.py

**修改**:
- 第 24 行:工具名改为 `search_products`
- 第 65、77 行:示例中的工具名更新
- 第 219-230 行:注入逻辑改为检查 `search_products`
- 第 284 行:工具结果检查改为 `search_products`
- 第 279-333 行:变量名 `spu_products` → `products`
- 第 280 行:`has_spu_search_result` → `has_product_search_result`

### 3. Product MCP Server
**文件**: mcp_servers/product_mcp/server.py

**修改**:
- 第 292 行:函数名 `search_spu_products` → `search_products`
- 第 300 行:文档字符串更新
- 功能完全相同,只是重命名

### 4. 移除映射逻辑
- 移除了 `search_products` → `search_spu_products` 的工具名映射
- 保留了 `query` → `keyword` 的参数映射(向后兼容)

## 好处

1. **简化命名**:`search_products` 比 `search_spu_products` 更简洁
2. **统一接口**:与系统中其他搜索工具命名一致
3. **降低复杂度**:减少名称长度和冗余

## 向后兼容

参数映射保留:
```python
# 仍然支持旧参数名
{"query": "ring"} → {"keyword": "ring"}
```

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
wangliang
2026-01-26 18:22:23 +08:00
parent 1aeb17fcce
commit 7b676f8015
2 changed files with 109 additions and 35 deletions

View File

@@ -21,7 +21,7 @@ PRODUCT_AGENT_PROMPT = """你是一个专业的 B2B 商品顾问助手。
## 可用工具
1. **search_spu_products** - 搜索商品
1. **search_products** - 搜索商品
- keyword: 搜索关键词(商品名称、编号等)
- page_size: 每页数量(默认 60最大 100
- page: 页码(默认 1
@@ -62,7 +62,7 @@ PRODUCT_AGENT_PROMPT = """你是一个专业的 B2B 商品顾问助手。
```json
{
"action": "call_tool",
"tool_name": "search_spu_products",
"tool_name": "search_products",
"arguments": {
"keyword": "ring"
}
@@ -74,7 +74,7 @@ PRODUCT_AGENT_PROMPT = """你是一个专业的 B2B 商品顾问助手。
```json
{
"action": "call_tool",
"tool_name": "search_spu_products",
"tool_name": "search_products",
"arguments": {
"keyword": "手机"
}
@@ -192,14 +192,6 @@ 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 = {
@@ -218,21 +210,13 @@ 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":
arguments = result.get("arguments", {})
# Inject context for SPU product search (Mall API)
if result["tool_name"] == "search_spu_products":
# Inject context for product search (Mall API)
if result["tool_name"] == "search_products":
arguments["user_token"] = state.get("user_token")
arguments["user_id"] = state["user_id"]
arguments["account_id"] = state["account_id"]
@@ -292,25 +276,25 @@ async def product_agent(state: AgentState) -> AgentState:
async def _generate_product_response(state: AgentState) -> AgentState:
"""Generate response based on product tool results"""
# 特殊处理:如果是 search_spu_products 工具返回,直接发送商品卡片
has_spu_search_result = False
spu_products = []
# 特殊处理:如果是 search_products 工具返回,直接发送商品卡片
has_product_search_result = False
products = []
for result in state["tool_results"]:
if result["success"] and result["tool_name"] == "search_spu_products":
if result["success"] and result["tool_name"] == "search_products":
data = result["data"]
if isinstance(data, dict) and data.get("success"):
spu_products = data.get("products", [])
has_spu_search_result = True
products = data.get("products", [])
has_product_search_result = True
logger.info(
"SPU product search results found",
products_count=len(spu_products),
"Product search results found",
products_count=len(products),
keyword=data.get("keyword", "")
)
break
# 如果有 SPU 搜索结果,直接发送商品卡片
if has_spu_search_result and spu_products:
# 如果有商品搜索结果,直接发送商品卡片
if has_product_search_result and products:
try:
from integrations.chatwoot import ChatwootClient
from core.language_detector import detect_language
@@ -325,14 +309,14 @@ async def _generate_product_response(state: AgentState) -> AgentState:
if conversation_id:
await chatwoot.send_product_cards(
conversation_id=int(conversation_id),
products=spu_products,
products=products,
language=detected_language
)
logger.info(
"Product cards sent successfully",
conversation_id=conversation_id,
products_count=len(spu_products),
products_count=len(products),
language=detected_language
)
@@ -345,7 +329,7 @@ async def _generate_product_response(state: AgentState) -> AgentState:
logger.error(
"Failed to send product cards, falling back to text response",
error=str(e),
products_count=len(spu_products)
products_count=len(products)
)
# 常规处理:生成文本响应

View File

@@ -288,6 +288,96 @@ async def get_categories() -> dict:
}
@mcp.tool()
async def search_products(
keyword: str,
page_size: int = 60,
page: int = 1,
user_token: str = None,
user_id: str = None,
account_id: str = None
) -> dict:
"""Search products from Mall API
从 Mall API 搜索商品 SPU根据关键词
Args:
keyword: 搜索关键词(商品名称、编号等)
page_size: 每页数量 (default: 60, max 100)
page: 页码 (default: 1)
user_token: 用户 JWT token必需用于 Mall API 认证)
user_id: 用户 ID自动注入
account_id: 账户 ID自动注入
Returns:
商品列表,包含 SPU 信息、商品图片、价格等
Product list including SPU ID, name, image, price, etc.
"""
if not user_token:
return {
"success": False,
"error": "用户未登录,请先登录账户以搜索商品",
"products": [],
"total": 0,
"require_login": True
}
try:
from shared.mall_client import MallClient
# 使用用户 token 创建 Mall 客户端
mall = MallClient(
api_url=settings.mall_api_url,
api_token=user_token,
tenant_id=settings.mall_tenant_id,
currency_code=settings.mall_currency_code,
language_id=settings.mall_language_id,
source=settings.mall_source
)
result = await mall.search_spu_products(
keyword=keyword,
page_size=page_size,
page=page
)
# 解析返回结果
products = result.get("list", [])
total = result.get("total", 0)
# 格式化商品数据
formatted_products = []
for product in products:
formatted_products.append({
"spu_id": product.get("spuId"),
"spu_sn": product.get("spuSn"),
"product_name": product.get("productName"),
"product_image": product.get("productImage"),
"price": product.get("price"),
"special_price": product.get("specialPrice"),
"stock": product.get("stock"),
"sales_count": product.get("salesCount", 0)
})
return {
"success": True,
"products": formatted_products,
"total": total,
"keyword": keyword
}
except Exception as e:
return {
"success": False,
"error": str(e),
"products": [],
"total": 0
}
finally:
# 关闭客户端
if 'client' in dir() and 'mall' in dir():
await mall.close()
# Health check endpoint
@mcp.tool()
async def health_check() -> dict: