94 lines
2.8 KiB
Plaintext
94 lines
2.8 KiB
Plaintext
|
|
%% REQ-坐席-010 AI回复三态开关 — 类图
|
|||
|
|
%% 架构师 高见远(Gao) 2026-08-04
|
|||
|
|
classDiagram
|
|||
|
|
class AiReplyMode {
|
|||
|
|
<<constants>>
|
|||
|
|
+str EMPLOYEE_ONLY$ = "employee_only"
|
|||
|
|
+str EMPLOYEE_AND_AGENT$ = "employee_and_agent"
|
|||
|
|
+str OFF$ = "off"
|
|||
|
|
+str DEFAULT$ = "employee_only"
|
|||
|
|
+frozenset ALL$
|
|||
|
|
+frozenset PATH_B_ALLOWED_STATUS$ = {ai_handling, queued}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class Conversation {
|
|||
|
|
+str id
|
|||
|
|
+str employee_id
|
|||
|
|
+str status
|
|||
|
|
+str ai_reply_mode
|
|||
|
|
+str assigned_agent_id
|
|||
|
|
+dict tags
|
|||
|
|
+int ai_substantive_reply_count
|
|||
|
|
+__repr__() str
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class AiReplyGate {
|
|||
|
|
<<module: app.services.ai_reply_gate>>
|
|||
|
|
+can_ai_auto_reply_path_a(conversation) bool
|
|||
|
|
+can_ai_auto_reply_path_b(conversation) bool
|
|||
|
|
+should_push_to_agent(conversation) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class AiReplyModeUpdate {
|
|||
|
|
<<pydantic BaseModel>>
|
|||
|
|
+str mode
|
|||
|
|
+validate_mode(v) str
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class AiReplyModeResponse {
|
|||
|
|
<<pydantic BaseModel>>
|
|||
|
|
+str conversation_id
|
|||
|
|
+str ai_reply_mode
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ConversationResponse {
|
|||
|
|
<<pydantic BaseModel>>
|
|||
|
|
+str id
|
|||
|
|
+str status
|
|||
|
|
+str ai_reply_mode
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class MessageRouter {
|
|||
|
|
<<app.services.message_router>>
|
|||
|
|
-AIHandler ai_handler
|
|||
|
|
-WecomService wecom_service
|
|||
|
|
+handle_employee_message(...) Conversation
|
|||
|
|
-_try_ai_reply(conversation, content, from_user_id) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class H5AiTask {
|
|||
|
|
<<app.tasks.h5_ai_task>>
|
|||
|
|
+process_h5_ai_reply(...) None
|
|||
|
|
-_persist_and_push(...) None
|
|||
|
|
-_persist_and_push_structured(...) None
|
|||
|
|
-_handle_byod_query(...) None
|
|||
|
|
-_step_call_dify(...) AIReplyResult
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ConnectionManager {
|
|||
|
|
<<app.services.ws_manager singleton>>
|
|||
|
|
+Dict~str,WebSocket~ active_connections
|
|||
|
|
+Dict~str,WebSocket~ employee_connections
|
|||
|
|
+broadcast(data) None
|
|||
|
|
+send_to_agent(agent_id, data) None
|
|||
|
|
+broadcast_to_employees(ids, data) None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ConversationsAPI {
|
|||
|
|
<<app.api.conversations>>
|
|||
|
|
+update_ai_reply_mode(conversation_id, body, db) dict
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
AiReplyGate ..> AiReplyMode : 读常量
|
|||
|
|
AiReplyGate ..> Conversation : 只读判定
|
|||
|
|
AiReplyModeUpdate ..> AiReplyMode : 值校验
|
|||
|
|
ConversationsAPI --> Conversation : 写 ai_reply_mode
|
|||
|
|
ConversationsAPI --> AiReplyModeUpdate : 请求体
|
|||
|
|
ConversationsAPI --> AiReplyModeResponse : 响应体
|
|||
|
|
ConversationsAPI --> ConnectionManager : broadcast(ai_reply_mode_changed)
|
|||
|
|
MessageRouter ..> AiReplyGate : 路径A门控 + 推坐席判定
|
|||
|
|
MessageRouter --> ConnectionManager : 新增 employee_and_agent 时广播
|
|||
|
|
H5AiTask ..> AiReplyGate : 推坐席判定
|
|||
|
|
H5AiTask --> ConnectionManager : 条件广播
|
|||
|
|
ConversationResponse ..> Conversation : from_attributes
|