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,6 +13,7 @@ from core.graph import process_message
from integrations.chatwoot import get_chatwoot_client, ConversationStatus
from utils.cache import get_cache_manager
from utils.logger import get_logger
from utils.token_manager import TokenManager
logger = get_logger(__name__)
@@ -50,6 +51,7 @@ class WebhookConversation(BaseModel):
additional_attributes: Optional[dict] = None
can_reply: Optional[bool] = None
channel: Optional[str] = None
meta: Optional[dict] = None # Contains sender info including custom_attributes
class WebhookContact(BaseModel):
@@ -111,24 +113,25 @@ def verify_webhook_signature(payload: bytes, signature: str) -> bool:
# ============ Message Processing ============
async def handle_incoming_message(payload: ChatwootWebhookPayload) -> None:
async def handle_incoming_message(payload: ChatwootWebhookPayload, cookie_token: str = None) -> None:
"""Process incoming message from Chatwoot
Args:
payload: Webhook payload
cookie_token: User token from request cookies
"""
conversation = payload.conversation
if not conversation:
logger.warning("No conversation in payload")
return
conversation_id = str(conversation.id)
content = payload.content
if not content:
logger.debug("Empty message content, skipping")
return
# Get user/contact info
contact = payload.contact or payload.sender
user_id = str(contact.id) if contact else "unknown"
@@ -137,21 +140,54 @@ async def handle_incoming_message(payload: ChatwootWebhookPayload) -> None:
# Chatwoot webhook includes account info at the top level
account_obj = payload.account
account_id = str(account_obj.get("id")) if account_obj else "1"
# 优先使用 Cookie 中的 token
user_token = cookie_token
# 如果 Cookie 中没有,尝试从多个来源提取 token
if not user_token:
# 1. 尝试从 contact/custom_attributes 获取
if contact:
contact_dict = contact.model_dump() if hasattr(contact, 'model_dump') else contact.__dict__
user_token = TokenManager.extract_token_from_contact(contact_dict)
logger.debug("Extracted token from contact", has_token=bool(user_token))
# 2. 尝试从 conversation.meta.sender.custom_attributes 获取Chatwoot SDK setUser 设置的位置)
if not user_token and conversation:
# 记录 conversation 的类型和内容用于调试
logger.debug("Conversation object type", type=str(type(conversation)))
if hasattr(conversation, 'model_dump'):
conv_dict = conversation.model_dump()
logger.debug("Conversation dict keys", keys=list(conv_dict.keys()))
logger.debug("Has meta", has_meta='meta' in conv_dict)
meta_sender = conv_dict.get('meta', {}).get('sender', {})
if meta_sender.get('custom_attributes'):
user_token = TokenManager.extract_token_from_contact({'custom_attributes': meta_sender['custom_attributes']})
logger.info("Token found in conversation.meta.sender.custom_attributes", token_prefix=user_token[:20] if user_token else None)
if user_token:
logger.info("JWT token found", user_id=user_id, source="cookie" if cookie_token else "contact")
logger.info(
"Processing incoming message",
conversation_id=conversation_id,
user_id=user_id,
has_token=bool(user_token),
message_length=len(content)
)
# Load conversation context from cache
cache = get_cache_manager()
await cache.connect()
context = await cache.get_context(conversation_id)
context = await cache.get_context(conversation_id) or {}
history = await cache.get_messages(conversation_id)
# Add token to context if available
if user_token:
context["user_token"] = user_token
try:
# Process message through agent workflow
final_state = await process_message(
@@ -160,7 +196,8 @@ async def handle_incoming_message(payload: ChatwootWebhookPayload) -> None:
account_id=account_id,
message=content,
history=history,
context=context
context=context,
user_token=user_token
)
# Get response
@@ -306,11 +343,16 @@ async def chatwoot_webhook(
background_tasks: BackgroundTasks
):
"""Chatwoot webhook endpoint
Receives events from Chatwoot and processes them asynchronously.
"""
# Get raw body for signature verification
body = await request.body()
# 尝试从请求 Cookie 中获取用户 Token
user_token = request.cookies.get("token") # 从 Cookie 读取 token
if user_token:
logger.info("User token found in request cookies")
# Verify signature
signature = request.headers.get("X-Chatwoot-Signature", "")
@@ -340,7 +382,7 @@ async def chatwoot_webhook(
if event == "message_created":
# Only process incoming messages from contacts
if payload.message_type == "incoming":
background_tasks.add_task(handle_incoming_message, payload)
background_tasks.add_task(handle_incoming_message, payload, user_token)
elif event == "conversation_created":
background_tasks.add_task(handle_conversation_created, payload)