主要改动: - 订单列表:使用 order_list 格式,展示 5 个订单(全部状态) - 订单详情:使用 order_detail 格式,优化价格和时间显示 - 物流信息:使用 logistics 格式,根据 track id 动态生成步骤 - 商品图片:从 orderProduct.imageUrl 字段获取 - 时间格式:统一为 YYYY-MM-DD HH:MM:SS - 多语言支持:amountLabel、orderTime 支持中英文 - 配置管理:新增 FRONTEND_URL 环境变量 - API 集成:改进 Mall API tracks 数据解析 - 认证优化:account_id 从 webhook 动态获取 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
307 lines
8.9 KiB
Python
307 lines
8.9 KiB
Python
"""
|
|
Agent state definitions for LangGraph workflow
|
|
"""
|
|
from typing import Any, Optional, Literal
|
|
from typing_extensions import TypedDict, Annotated
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
|
|
|
|
class Intent(str, Enum):
|
|
"""User intent categories"""
|
|
CUSTOMER_SERVICE = "customer_service" # FAQ, general inquiries
|
|
ORDER = "order" # Order query, tracking, modify, cancel
|
|
AFTERSALE = "aftersale" # Return, exchange, complaint
|
|
PRODUCT = "product" # Search, recommend, quote
|
|
HUMAN_HANDOFF = "human_handoff" # Transfer to human agent
|
|
UNKNOWN = "unknown" # Cannot determine intent
|
|
|
|
|
|
class ConversationState(str, Enum):
|
|
"""Conversation state machine"""
|
|
INITIAL = "initial"
|
|
CLASSIFYING = "classifying"
|
|
PROCESSING = "processing"
|
|
AWAITING_INFO = "awaiting_info"
|
|
TOOL_CALLING = "tool_calling"
|
|
GENERATING = "generating"
|
|
HUMAN_REVIEW = "human_review"
|
|
COMPLETED = "completed"
|
|
ERROR = "error"
|
|
|
|
|
|
@dataclass
|
|
class Entity:
|
|
"""Extracted entity from user message"""
|
|
type: str # Entity type (order_id, product_id, date, etc.)
|
|
value: Any # Entity value
|
|
confidence: float # Extraction confidence (0-1)
|
|
|
|
|
|
@dataclass
|
|
class ToolCall:
|
|
"""MCP tool call record"""
|
|
tool_name: str
|
|
arguments: dict[str, Any]
|
|
server: str # MCP server name (strapi, order, aftersale, product)
|
|
|
|
|
|
@dataclass
|
|
class ToolResult:
|
|
"""MCP tool execution result"""
|
|
tool_name: str
|
|
success: bool
|
|
data: Any
|
|
error: Optional[str] = None
|
|
|
|
|
|
class AgentState(TypedDict):
|
|
"""Main agent state for LangGraph workflow
|
|
|
|
This state is passed through all nodes in the workflow graph.
|
|
"""
|
|
|
|
# ============ Session Information ============
|
|
conversation_id: str # Chatwoot conversation ID
|
|
user_id: str # User identifier
|
|
account_id: str # B2B account identifier
|
|
user_token: Optional[str] # User JWT token for API calls
|
|
mall_token: Optional[str] # Mall API token (if different from user_token)
|
|
|
|
# ============ Message Content ============
|
|
messages: list[dict[str, Any]] # Conversation history [{role, content}]
|
|
current_message: str # Current user message being processed
|
|
|
|
# ============ Intent Recognition ============
|
|
intent: Optional[str] # Recognized intent (Intent enum value)
|
|
intent_confidence: float # Intent confidence score (0-1)
|
|
sub_intent: Optional[str] # Sub-intent for more specific routing
|
|
|
|
# ============ Language Detection ============
|
|
detected_language: Optional[str] # Detected user language (en, nl, de, etc.)
|
|
language_confidence: float # Language detection confidence (0-1)
|
|
|
|
# ============ Entity Extraction ============
|
|
entities: dict[str, Any] # Extracted entities {type: value}
|
|
|
|
# ============ Agent Routing ============
|
|
current_agent: Optional[str] # Current processing agent name
|
|
agent_history: list[str] # History of agents involved
|
|
|
|
# ============ Tool Calling ============
|
|
tool_calls: list[dict[str, Any]] # Pending tool calls
|
|
tool_results: list[dict[str, Any]] # Tool execution results
|
|
|
|
# ============ Response Generation ============
|
|
response: Optional[str] # Generated response text
|
|
response_type: str # Response type (text, rich, action)
|
|
|
|
# ============ Human Handoff ============
|
|
requires_human: bool # Whether human intervention is needed
|
|
handoff_reason: Optional[str] # Reason for human handoff
|
|
|
|
# ============ Conversation Context ============
|
|
context: dict[str, Any] # Accumulated context (order details, etc.)
|
|
|
|
# ============ State Control ============
|
|
state: str # Current conversation state
|
|
step_count: int # Number of steps taken
|
|
max_steps: int # Maximum allowed steps
|
|
error: Optional[str] # Error message if any
|
|
finished: bool # Whether processing is complete
|
|
|
|
|
|
def create_initial_state(
|
|
conversation_id: str,
|
|
user_id: str,
|
|
account_id: str,
|
|
current_message: str,
|
|
messages: Optional[list[dict[str, Any]]] = None,
|
|
context: Optional[dict[str, Any]] = None,
|
|
user_token: Optional[str] = None,
|
|
mall_token: Optional[str] = None
|
|
) -> AgentState:
|
|
"""Create initial agent state for a new message
|
|
|
|
Args:
|
|
conversation_id: Chatwoot conversation ID
|
|
user_id: User identifier
|
|
account_id: B2B account identifier
|
|
current_message: User's message to process
|
|
messages: Previous conversation history
|
|
context: Existing conversation context
|
|
user_token: User JWT token for API calls
|
|
mall_token: Mall API token (if different from user_token)
|
|
|
|
Returns:
|
|
Initialized AgentState
|
|
"""
|
|
return AgentState(
|
|
# Session
|
|
conversation_id=conversation_id,
|
|
user_id=user_id,
|
|
account_id=account_id,
|
|
user_token=user_token,
|
|
mall_token=mall_token,
|
|
|
|
# Messages
|
|
messages=messages or [],
|
|
current_message=current_message,
|
|
|
|
# Intent
|
|
intent=None,
|
|
intent_confidence=0.0,
|
|
sub_intent=None,
|
|
|
|
# Language
|
|
detected_language=None,
|
|
language_confidence=0.0,
|
|
|
|
# Entities
|
|
entities={},
|
|
|
|
# Routing
|
|
current_agent=None,
|
|
agent_history=[],
|
|
|
|
# Tools
|
|
tool_calls=[],
|
|
tool_results=[],
|
|
|
|
# Response
|
|
response=None,
|
|
response_type="text",
|
|
|
|
# Human handoff
|
|
requires_human=False,
|
|
handoff_reason=None,
|
|
|
|
# Context
|
|
context=context or {},
|
|
|
|
# Control
|
|
state=ConversationState.INITIAL.value,
|
|
step_count=0,
|
|
max_steps=10,
|
|
error=None,
|
|
finished=False
|
|
)
|
|
|
|
|
|
# ============ State Update Helpers ============
|
|
|
|
def add_message(state: AgentState, role: str, content: str) -> AgentState:
|
|
"""Add a message to the conversation history"""
|
|
state["messages"].append({"role": role, "content": content})
|
|
return state
|
|
|
|
|
|
def set_intent(
|
|
state: AgentState,
|
|
intent: Intent,
|
|
confidence: float,
|
|
sub_intent: Optional[str] = None
|
|
) -> AgentState:
|
|
"""Set the recognized intent"""
|
|
state["intent"] = intent.value
|
|
state["intent_confidence"] = confidence
|
|
state["sub_intent"] = sub_intent
|
|
return state
|
|
|
|
|
|
def add_entity(state: AgentState, entity_type: str, value: Any) -> AgentState:
|
|
"""Add an extracted entity"""
|
|
state["entities"][entity_type] = value
|
|
return state
|
|
|
|
|
|
def add_tool_call(
|
|
state: AgentState,
|
|
tool_name: str,
|
|
arguments: dict[str, Any],
|
|
server: str
|
|
) -> AgentState:
|
|
"""Add a tool call to pending calls"""
|
|
state["tool_calls"].append({
|
|
"tool_name": tool_name,
|
|
"arguments": arguments,
|
|
"server": server
|
|
})
|
|
return state
|
|
|
|
|
|
def add_tool_result(
|
|
state: AgentState,
|
|
tool_name: str,
|
|
success: bool,
|
|
data: Any,
|
|
error: Optional[str] = None
|
|
) -> AgentState:
|
|
"""Add a tool execution result"""
|
|
state["tool_results"].append({
|
|
"tool_name": tool_name,
|
|
"success": success,
|
|
"data": data,
|
|
"error": error
|
|
})
|
|
return state
|
|
|
|
|
|
def set_response(
|
|
state: AgentState,
|
|
response: str,
|
|
response_type: str = "text"
|
|
) -> AgentState:
|
|
"""Set the generated response"""
|
|
state["response"] = response
|
|
state["response_type"] = response_type
|
|
return state
|
|
|
|
|
|
def request_human_handoff(
|
|
state: AgentState,
|
|
reason: str
|
|
) -> AgentState:
|
|
"""Request transfer to human agent"""
|
|
state["requires_human"] = True
|
|
state["handoff_reason"] = reason
|
|
return state
|
|
|
|
|
|
def update_context(state: AgentState, updates: dict[str, Any]) -> AgentState:
|
|
"""Update conversation context"""
|
|
state["context"].update(updates)
|
|
return state
|
|
|
|
|
|
def set_error(state: AgentState, error: str) -> AgentState:
|
|
"""Set error state"""
|
|
state["error"] = error
|
|
state["state"] = ConversationState.ERROR.value
|
|
return state
|
|
|
|
|
|
def mark_finished(state: AgentState) -> AgentState:
|
|
"""Mark processing as complete"""
|
|
state["finished"] = True
|
|
state["state"] = ConversationState.COMPLETED.value
|
|
return state
|
|
|
|
|
|
def set_language(state: AgentState, language: str, confidence: float) -> AgentState:
|
|
"""Set the detected language in state
|
|
|
|
Args:
|
|
state: Agent state
|
|
language: Detected locale code (en, nl, de, etc.)
|
|
confidence: Detection confidence (0-1)
|
|
|
|
Returns:
|
|
Updated state
|
|
"""
|
|
state["detected_language"] = language
|
|
state["language_confidence"] = confidence
|
|
# Also cache in context for future reference
|
|
state["context"]["language"] = language
|
|
return state
|