feat: 初始化 B2B AI Shopping Assistant 项目

- 配置 Docker Compose 多服务编排
- 实现 Chatwoot + Agent 集成
- 配置 Strapi MCP 知识库
- 支持 7 种语言的 FAQ 系统
- 实现 LangGraph AI 工作流

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
wl
2026-01-14 19:25:22 +08:00
commit 3ad6eee0d9
59 changed files with 8078 additions and 0 deletions

272
agent/core/state.py Normal file
View File

@@ -0,0 +1,272 @@
"""
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
# ============ 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
# ============ 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
) -> 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
Returns:
Initialized AgentState
"""
return AgentState(
# Session
conversation_id=conversation_id,
user_id=user_id,
account_id=account_id,
# Messages
messages=messages or [],
current_message=current_message,
# Intent
intent=None,
intent_confidence=0.0,
sub_intent=None,
# 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