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:
@@ -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)
|
||||
)
|
||||
|
||||
# 常规处理:生成文本响应
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user