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,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)
|
||||
|
||||
Reference in New Issue
Block a user