feat: 增强 Agent 系统和完善项目结构
主要改进: - Agent 增强: 订单查询、售后支持、客服路由等功能优化 - 新增语言检测和 Token 管理模块 - 改进 Chatwoot webhook 处理和用户标识 - MCP 服务器增强: 订单 MCP 和 Strapi MCP 功能扩展 - 新增商城客户端、知识库、缓存和同步模块 - 添加多语言提示词系统 (YAML) - 完善项目结构: 整理文档、脚本和测试文件 - 新增调试和测试工具脚本 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,3 +13,7 @@ python-dotenv>=1.0.0
|
||||
|
||||
# Logging
|
||||
structlog>=24.1.0
|
||||
|
||||
# Web Framework
|
||||
fastapi>=0.100.0
|
||||
uvicorn>=0.23.0
|
||||
|
||||
@@ -19,8 +19,17 @@ class Settings(BaseSettings):
|
||||
"""Server configuration"""
|
||||
hyperf_api_url: str
|
||||
hyperf_api_token: str
|
||||
|
||||
# Mall API 配置
|
||||
mall_api_url: str = "https://apicn.qa1.gaia888.com"
|
||||
mall_api_token: str = ""
|
||||
mall_tenant_id: str = "2"
|
||||
mall_currency_code: str = "EUR"
|
||||
mall_language_id: str = "1"
|
||||
mall_source: str = "us.qa1.gaia888.com"
|
||||
|
||||
log_level: str = "INFO"
|
||||
|
||||
|
||||
model_config = ConfigDict(env_file=".env")
|
||||
|
||||
|
||||
@@ -31,12 +40,35 @@ mcp = FastMCP(
|
||||
"Order Management"
|
||||
)
|
||||
|
||||
# Tool registry for HTTP access
|
||||
_tools = {}
|
||||
|
||||
|
||||
# Hyperf client for this server
|
||||
from shared.hyperf_client import HyperfClient
|
||||
hyperf = HyperfClient(settings.hyperf_api_url, settings.hyperf_api_token)
|
||||
|
||||
# Mall API client
|
||||
from shared.mall_client import MallClient
|
||||
mall = MallClient(
|
||||
api_url=getattr(settings, 'mall_api_url', 'https://apicn.qa1.gaia888.com'),
|
||||
api_token=getattr(settings, 'mall_api_token', ''),
|
||||
tenant_id=getattr(settings, 'mall_tenant_id', '2'),
|
||||
currency_code=getattr(settings, 'mall_currency_code', 'EUR'),
|
||||
language_id=getattr(settings, 'mall_language_id', '1'),
|
||||
source=getattr(settings, 'mall_source', 'us.qa1.gaia888.com')
|
||||
)
|
||||
|
||||
|
||||
def register_tool(name: str):
|
||||
"""Register a tool for HTTP access"""
|
||||
def decorator(func):
|
||||
_tools[name] = func
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
@register_tool("query_order")
|
||||
@mcp.tool()
|
||||
async def query_order(
|
||||
user_id: str,
|
||||
@@ -96,6 +128,7 @@ async def query_order(
|
||||
}
|
||||
|
||||
|
||||
@register_tool("track_logistics")
|
||||
@mcp.tool()
|
||||
async def track_logistics(
|
||||
order_id: str,
|
||||
@@ -134,6 +167,7 @@ async def track_logistics(
|
||||
}
|
||||
|
||||
|
||||
@register_tool("modify_order")
|
||||
@mcp.tool()
|
||||
async def modify_order(
|
||||
order_id: str,
|
||||
@@ -177,6 +211,7 @@ async def modify_order(
|
||||
}
|
||||
|
||||
|
||||
@register_tool("cancel_order")
|
||||
@mcp.tool()
|
||||
async def cancel_order(
|
||||
order_id: str,
|
||||
@@ -217,17 +252,18 @@ async def cancel_order(
|
||||
}
|
||||
|
||||
|
||||
@register_tool("get_invoice")
|
||||
@mcp.tool()
|
||||
async def get_invoice(
|
||||
order_id: str,
|
||||
invoice_type: str = "normal"
|
||||
) -> dict:
|
||||
"""Get invoice for an order
|
||||
|
||||
|
||||
Args:
|
||||
order_id: Order ID
|
||||
invoice_type: Invoice type ('normal' for regular invoice, 'vat' for VAT invoice)
|
||||
|
||||
|
||||
Returns:
|
||||
Invoice information and download URL
|
||||
"""
|
||||
@@ -236,7 +272,7 @@ async def get_invoice(
|
||||
f"/orders/{order_id}/invoice",
|
||||
params={"type": invoice_type}
|
||||
)
|
||||
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"order_id": order_id,
|
||||
@@ -255,7 +291,64 @@ async def get_invoice(
|
||||
}
|
||||
|
||||
|
||||
@register_tool("get_mall_order")
|
||||
@mcp.tool()
|
||||
async def get_mall_order(
|
||||
order_id: str,
|
||||
user_token: str = None,
|
||||
user_id: str = None,
|
||||
account_id: str = None
|
||||
) -> dict:
|
||||
"""Query order from Mall API by order ID
|
||||
|
||||
从商城 API 查询订单详情
|
||||
|
||||
Args:
|
||||
order_id: 订单号 (e.g., "202071324")
|
||||
user_token: 用户 JWT token(可选,如果提供则使用该 token 进行查询)
|
||||
user_id: 用户 ID(自动注入,此工具不使用)
|
||||
account_id: 账户 ID(自动注入,此工具不使用)
|
||||
|
||||
Returns:
|
||||
订单详情,包含订单号、状态、商品信息、金额、物流信息等
|
||||
Order details including order ID, status, items, amount, logistics info, etc.
|
||||
"""
|
||||
try:
|
||||
# 如果提供了 user_token,使用用户自己的 token
|
||||
if user_token:
|
||||
client = 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
|
||||
)
|
||||
else:
|
||||
# 否则使用默认的 mall 实例
|
||||
client = mall
|
||||
|
||||
result = await client.get_order_by_id(order_id)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"order": result,
|
||||
"order_id": order_id
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"order_id": order_id
|
||||
}
|
||||
finally:
|
||||
# 如果创建了临时客户端,关闭它
|
||||
if user_token:
|
||||
await client.close()
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
@register_tool("health_check")
|
||||
@mcp.tool()
|
||||
async def health_check() -> dict:
|
||||
"""Check server health status"""
|
||||
@@ -268,17 +361,75 @@ async def health_check() -> dict:
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
# Create FastAPI app from MCP
|
||||
app = mcp.http_app()
|
||||
|
||||
# Add health endpoint
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.routing import Route
|
||||
|
||||
# Health check endpoint
|
||||
async def health_check(request):
|
||||
return JSONResponse({"status": "healthy"})
|
||||
|
||||
# Add the route to the app
|
||||
from starlette.routing import Route
|
||||
app.router.routes.append(Route('/health', health_check, methods=['GET']))
|
||||
|
||||
|
||||
# Tool execution endpoint
|
||||
async def execute_tool(request: Request):
|
||||
"""Execute an MCP tool via HTTP"""
|
||||
tool_name = request.path_params["tool_name"]
|
||||
|
||||
try:
|
||||
# Get arguments from request body
|
||||
arguments = await request.json()
|
||||
|
||||
# Get tool function from registry
|
||||
if tool_name not in _tools:
|
||||
return JSONResponse({
|
||||
"success": False,
|
||||
"error": f"Tool '{tool_name}' not found"
|
||||
}, status_code=404)
|
||||
|
||||
tool_obj = _tools[tool_name]
|
||||
|
||||
# Call the tool with arguments
|
||||
# FastMCP FunctionTool.run() takes a dict of arguments
|
||||
tool_result = await tool_obj.run(arguments)
|
||||
|
||||
# Extract content from ToolResult
|
||||
# ToolResult.content is a list of TextContent objects with a 'text' attribute
|
||||
if tool_result.content and len(tool_result.content) > 0:
|
||||
content = tool_result.content[0].text
|
||||
# Try to parse as JSON if possible
|
||||
try:
|
||||
import json
|
||||
result = json.loads(content)
|
||||
except:
|
||||
result = content
|
||||
else:
|
||||
result = None
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"result": result
|
||||
})
|
||||
except TypeError as e:
|
||||
return JSONResponse({
|
||||
"success": False,
|
||||
"error": f"Invalid arguments: {str(e)}"
|
||||
}, status_code=400)
|
||||
except Exception as e:
|
||||
return JSONResponse({
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}, status_code=500)
|
||||
|
||||
# Create routes list
|
||||
routes = [
|
||||
Route('/health', health_check, methods=['GET']),
|
||||
Route('/tools/{tool_name}', execute_tool, methods=['POST'])
|
||||
]
|
||||
|
||||
# Create app from MCP with custom routes
|
||||
app = mcp.http_app()
|
||||
|
||||
# Add our custom routes to the existing app
|
||||
for route in routes:
|
||||
app.router.routes.append(route)
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=8002)
|
||||
|
||||
Reference in New Issue
Block a user