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

@@ -6,76 +6,20 @@ from typing import Any
from core.state import AgentState, ConversationState, add_tool_call, set_response
from core.llm import get_llm_client, Message
from prompts import get_prompt
from utils.logger import get_logger
logger = get_logger(__name__)
CUSTOMER_SERVICE_PROMPT = """你是一个专业的 B2B 购物网站客服助手。
你的职责是回答用户的一般性问题,包括:
- 常见问题解答 (FAQ)
- 公司信息查询
- 政策咨询(退换货政策、隐私政策等)
- 产品使用指南
- 其他一般性咨询
## 可用工具
你可以使用以下工具获取信息:
1. **query_faq** - 搜索 FAQ 常见问题
- query: 搜索关键词
- category: 分类(可选)
2. **get_company_info** - 获取公司信息
- section: 信息类别about_us, contact, etc.
3. **get_policy** - 获取政策文档
- policy_type: 政策类型return_policy, privacy_policy, etc.
## 工具调用格式
当需要使用工具时,请返回 JSON 格式:
```json
{
"action": "call_tool",
"tool_name": "工具名称",
"arguments": {
"参数名": "参数值"
}
}
```
当可以直接回答时,请返回:
```json
{
"action": "respond",
"response": "回复内容"
}
```
当需要转人工时,请返回:
```json
{
"action": "handoff",
"reason": "转人工原因"
}
```
## 注意事项
- 保持专业、友好的语气
- 如果不确定答案,建议用户联系人工客服
- 不要编造信息,只使用工具返回的数据
"""
async def customer_service_agent(state: AgentState) -> AgentState:
"""Customer service agent node
Handles FAQ, company info, and general inquiries using Strapi MCP tools.
Args:
state: Current agent state
Returns:
Updated state with tool calls or response
"""
@@ -83,18 +27,87 @@ async def customer_service_agent(state: AgentState) -> AgentState:
"Customer service agent processing",
conversation_id=state["conversation_id"]
)
state["current_agent"] = "customer_service"
state["agent_history"].append("customer_service")
state["state"] = ConversationState.PROCESSING.value
# Check if we have tool results to process
if state["tool_results"]:
return await _generate_response_from_results(state)
# Get detected language
locale = state.get("detected_language", "en")
# Auto-detect category and query FAQ
message_lower = state["current_message"].lower()
# 定义分类关键词
category_keywords = {
"register": ["register", "sign up", "account", "login", "password", "forgot"],
"order": ["order", "place order", "cancel order", "modify order", "change order"],
"payment": ["pay", "payment", "checkout", "voucher", "discount", "promo"],
"shipment": ["ship", "shipping", "delivery", "courier", "transit", "logistics", "tracking"],
"return": ["return", "refund", "exchange", "defective", "damaged"],
}
# 检测分类
detected_category = None
for category, keywords in category_keywords.items():
if any(keyword in message_lower for keyword in keywords):
detected_category = category
break
# 检查是否已经查询过 FAQ
tool_calls = state.get("tool_calls", [])
has_faq_query = any(tc.get("tool_name") in ["query_faq", "search_knowledge_base"] for tc in tool_calls)
# 如果检测到分类且未查询过 FAQ自动查询
if detected_category and not has_faq_query:
logger.info(
f"Auto-querying FAQ for category: {detected_category}",
conversation_id=state["conversation_id"]
)
# 自动添加 FAQ 工具调用
state = add_tool_call(
state,
tool_name="query_faq",
arguments={
"category": detected_category,
"locale": locale,
"limit": 5
},
server="strapi"
)
state["state"] = ConversationState.TOOL_CALLING.value
return state
# 如果询问营业时间或联系方式,自动查询公司信息
if any(keyword in message_lower for keyword in ["opening hour", "contact", "address", "phone", "email"]) and not has_faq_query:
logger.info(
"Auto-querying company info",
conversation_id=state["conversation_id"]
)
state = add_tool_call(
state,
tool_name="get_company_info",
arguments={
"section": "contact",
"locale": locale
},
server="strapi"
)
state["state"] = ConversationState.TOOL_CALLING.value
return state
# Build messages for LLM
# Load prompt in detected language
system_prompt = get_prompt("customer_service", locale)
messages = [
Message(role="system", content=CUSTOMER_SERVICE_PROMPT),
Message(role="system", content=system_prompt),
]
# Add conversation history
@@ -151,37 +164,37 @@ async def customer_service_agent(state: AgentState) -> AgentState:
async def _generate_response_from_results(state: AgentState) -> AgentState:
"""Generate response based on tool results"""
# Build context from tool results
tool_context = []
for result in state["tool_results"]:
if result["success"]:
tool_context.append(f"工具 {result['tool_name']} 返回:\n{json.dumps(result['data'], ensure_ascii=False, indent=2)}")
tool_context.append(f"Tool {result['tool_name']} returned:\n{json.dumps(result['data'], ensure_ascii=False, indent=2)}")
else:
tool_context.append(f"工具 {result['tool_name']} 执行失败: {result['error']}")
prompt = f"""基于以下工具返回的信息,生成对用户的回复。
tool_context.append(f"Tool {result['tool_name']} failed: {result['error']}")
用户问题: {state["current_message"]}
prompt = f"""Based on the following tool returned information, generate a response to the user.
工具返回信息:
User question: {state["current_message"]}
Tool returned information:
{chr(10).join(tool_context)}
请生成一个友好、专业的回复。如果工具没有返回有用信息,请诚实告知用户并建议其他方式获取帮助。
只返回回复内容,不要返回 JSON"""
Please generate a friendly and professional response. If the tool did not return useful information, honestly inform the user and suggest other ways to get help.
Return only the response content, do not return JSON."""
messages = [
Message(role="system", content="你是一个专业的 B2B 客服助手,请根据工具返回的信息回答用户问题。"),
Message(role="system", content="You are a professional B2B customer service assistant, please answer user questions based on tool returned information."),
Message(role="user", content=prompt)
]
try:
llm = get_llm_client()
response = await llm.chat(messages, temperature=0.7)
state = set_response(state, response.content)
return state
except Exception as e:
logger.error("Response generation failed", error=str(e))
state = set_response(state, "抱歉,处理您的请求时遇到问题。请稍后重试或联系人工客服。")
state = set_response(state, "Sorry, there was a problem processing your request. Please try again later or contact customer support.")
return state