diff --git a/agent/agents/product.py b/agent/agents/product.py index 37606b4..ae05210 100644 --- a/agent/agents/product.py +++ b/agent/agents/product.py @@ -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) ) # 常规处理:生成文本响应 diff --git a/mcp_servers/product_mcp/server.py b/mcp_servers/product_mcp/server.py index a9d7547..a07a763 100644 --- a/mcp_servers/product_mcp/server.py +++ b/mcp_servers/product_mcp/server.py @@ -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: