1. 项目背景与核心价值
在构建客服自动化系统时,我们常常面临一个关键矛盾:既要保持AI的响应效率,又要确保高风险操作得到人工控制。传统方案通常采用事后审核或全自动处理两种极端方式,前者影响用户体验,后者则可能带来业务风险。
这个项目通过LangGraph实现了审批与自动执行的动态平衡。当用户请求涉及投诉建单、转人工等敏感操作时,工作流会自动暂停并触发审批流程;而对于普通咨询类请求,则保持全自动处理。这种设计既保留了AI的效率优势,又在关键节点设置了安全闸门。
关键创新点:将审批机制作为工作流的一等公民(first-class citizen)设计,而非事后补救措施。这使得系统状态可以精确暂停和恢复,避免重复执行带来的副作用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构解析
2.1 整体工作流设计
系统采用分层架构设计,核心流程如下:
code复制[HTTP请求] ->
[路由决策] ->
[记忆加载] ->
[意图识别] ->
{
低风险: [工具执行] -> [响应生成]
高风险: [审批触发] -> [人工决策] -> [恢复执行]
} ->
[结果流式返回]
这种设计有三大优势:
- 控制流与业务逻辑分离,便于维护扩展
- 审批状态持久化,支持断点续传
- 全链路可观测,便于问题排查
2.2 关键组件实现
2.2.1 状态管理模块
采用双层存储设计:
- 会话级状态:使用MemorySaver保存当前工作流上下文
- 用户级状态:通过InMemoryStore持久化用户偏好
python复制class AgentState(TypedDict):
user_id: str
thread_id: str
messages: List[dict]
pending_review: Optional[dict]
2.2.2 审批中断机制
当检测到高风险操作时,通过interrupt()暂停工作流:
python复制def human_review_node(state):
if needs_review(state):
return {
"pending_review": build_review_payload(state),
"__interrupt__": True # LangGraph特殊标记
}
return {"pending_review": None}
2.2.3 恢复执行设计
审批通过后,携带原始thread_id重新触发执行:
python复制@app.post("/api/review")
async def handle_review(review: ReviewDecision):
if review.approved:
return StreamingResponse(
stream_resume_turn(review.thread_id),
media_type="application/x-ndjson"
)
3. 核心实现细节
3.1 动态路由决策
planner_node采用混合决策模式:
python复制def planner_node(state):
# 优先使用LLM进行意图识别
try:
intent = llm_analyze_intent(state["messages"])
except Exception:
# 降级到规则引擎
intent = rule_based_intent_analysis(state["messages"])
return {
"needs_review": intent in HIGH_RISK_INTENTS,
"next_node": "human_review_node" if needs_review else "tool_node"
}
3.2 审批流程设计
审批系统实现要点:
- 前端展示专用审批UI组件
- 服务端保留完整执行上下文
- 支持审批意见回传
javascript复制// 前端审批组件示例
function ReviewCard({ pendingAction }) {
return (
<div className="review-card">
<h3>待审批操作: {pendingAction.type}</h3>
<p>请求内容: {pendingAction.detail}</p>
<button onClick={() => approve(pendingAction.id)}>批准</button>
<button onClick={() => reject(pendingAction.id)}>拒绝</button>
</div>
);
}
3.3 状态恢复机制
恢复执行时的工作流处理:
- 从MemorySaver加载原始状态
- 注入审批结果到上下文
- 从中断节点继续执行
python复制def resume_workflow(thread_id, review_result):
state = memory_saver.load(thread_id)
state["review_result"] = review_result
return graph.run(state, start_node="post_review_node")
4. 生产环境考量
4.1 性能优化方案
-
记忆缓存策略:
- 会话级状态:内存缓存+LRU淘汰
- 用户级状态:Redis集群存储
-
审批超时处理:
python复制async def check_review_timeout():
while True:
expired_reviews = get_expired_reviews()
for review in expired_reviews:
await auto_reject(review)
await asyncio.sleep(60)
4.2 监控指标设计
建议监控的关键指标:
| 指标名称 | 类型 | 说明 |
|---|---|---|
| workflow_duration | 直方图 | 从请求到完成的耗时分布 |
| review_pending_time | 直方图 | 审批等待时长分布 |
| node_exec_count | 计数器 | 各节点执行次数统计 |
| fallback_triggered | 计数器 | 降级逻辑触发次数 |
4.3 安全增强建议
- 审批操作审计日志:
python复制def log_review_action(action):
audit_log = {
"timestamp": datetime.utcnow(),
"action": action.type,
"operator": action.operator,
"decision": action.decision,
"metadata": action.metadata
}
secure_logger.info(json.dumps(audit_log))
- 敏感操作二次验证:
python复制def validate_sensitive_action(state):
if state.get("requires_2fa"):
if not state.get("otp_verified"):
raise PermissionError("需要二次验证")
5. 典型问题排查指南
5.1 审批流程卡住
检查步骤:
- 确认MemorySaver存储可用
- 检查pending_review状态是否正确设置
- 验证前端是否正确轮询审批状态
5.2 状态恢复异常
常见原因:
- thread_id不匹配导致状态加载失败
- 审批结果未正确注入上下文
- 节点配置错误导致跳转异常
调试方法:
python复制# 在graph.compile()前添加调试回调
def debug_state(state):
print(f"[DEBUG] Current state: {state}")
return state
graph = workflow.compile(checkpointer=memory_saver, interrupt_before=["human_review_node"])
graph = graph.add_node_debugger(debug_state)
6. 扩展与演进方向
6.1 多级审批流程
实现方案:
python复制class MultiLevelReview:
def __init__(self, levels):
self.levels = levels
async def run(self, state):
for i, (role, threshold) in enumerate(self.levels):
state['current_review_level'] = i
await wait_for_review(role, threshold)
if not state.get('approved'):
break
return state
6.2 与现有系统集成
CRM集成示例:
python复制class CRMTool(BaseTool):
name = "create_ticket"
def run(self, input):
# 调用企业CRM API
response = crm_client.create_ticket(
title=input["title"],
content=input["content"],
priority=input.get("priority", "normal")
)
return {"ticket_id": response["id"]}
在实际部署中,我们通过给高风险工具添加@require_approval装饰器来实现自动拦截:
python复制def require_approval(tool_class):
original_run = tool_class.run
def wrapped_run(self, input):
if self.ctx.needs_review:
raise InterruptionRequired(f"{tool_class.name} requires approval")
return original_run(self, input)
tool_class.run = wrapped_run
return tool_class
这种设计模式使得审批逻辑与业务工具解耦,后续新增工具时只需关注核心功能实现,无需重复编写审批相关代码。
