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:
wangliang
2026-01-16 16:28:47 +08:00
parent 0e59f3067e
commit e093995368
48 changed files with 5263 additions and 395 deletions

View File

@@ -13,3 +13,7 @@ python-dotenv>=1.0.0
# Logging
structlog>=24.1.0
# Web Framework
fastapi>=0.100.0
uvicorn>=0.23.0

View File

@@ -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)