phase 6 part 1: eight cross-stack enterprise scenarios and scenarios index
This commit is contained in:
1 parent
691f738f5f
commit
5b1a7a430f
12 files changed
+1145
-12
No files matched your search
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: scenario.approval-human-in-loop
|
||||
title: 审批流 + 人工介入的 Agent:高风险操作暂停审批、断点续跑、超时和幂等执行
|
||||
type: scenario
|
||||
domain: cross
|
||||
stack: [langgraph, function-calling, postgresql, redis, fastapi, vue]
|
||||
tags: [人机协作, interrupt, 检查点, 审批, 幂等, 超时, 审计]
|
||||
level: advanced
|
||||
prerequisites: [ai.langgraph.index, ai.function-calling.index]
|
||||
est_hours: 3
|
||||
related: [ai.langgraph.scenario.refund-approval, ai.function-calling.scenario.safe-business-tools, ai.agent-design.index]
|
||||
week: [11, 12]
|
||||
---
|
||||
|
||||
# 审批流 + 人工介入的 Agent:高风险操作暂停审批、断点续跑、超时和幂等执行
|
||||
|
||||
## 业务背景与问题
|
||||
|
||||
AI 客服 Agent 可以办理退款、改地址、补发商品。业务要求:
|
||||
|
||||
1. 金额超过阈值或命中风险规则的操作必须人工审批;
|
||||
2. 审批可能几小时后才完成,期间服务可能重启;
|
||||
3. 审批通过后只能执行一次(不能重复退款);
|
||||
4. 审批超时自动拒绝并通知用户;
|
||||
5. 全程可审计:谁发起、模型建议了什么、谁批准、实际执行了什么。
|
||||
|
||||
## 约束
|
||||
|
||||
- Agent 不能自己“决定”跳过审批,不能依赖提示词约束。
|
||||
- 审批人在后台系统操作,不在对话中。
|
||||
|
||||
## 方案设计
|
||||
|
||||
1. **流程建模(LangGraph)**:理解意图 → 查订单 → 计算方案 → 风险判断 → [需审批] `interrupt` 暂停 → 执行 → 通知。风险判断是**代码规则**(金额、频次、用户等级),不是模型判断。
|
||||
2. **持久化**:检查点存数据库(开发 SQLite、生产 PostgreSQL),`thread_id` 与审批单关联,重启后从检查点恢复。
|
||||
3. **审批单**:暂停时创建审批记录(方案、模型理由、风险命中项、截止时间)推送到后台;审批后用 `Command(resume=...)` 恢复对应线程。
|
||||
4. **幂等执行**:执行节点调用退款接口时带幂等键(`thread_id + 步骤名`),恢复或重试不会重复退款。
|
||||
5. **超时**:延迟任务到期检查审批状态,未处理则以“驳回(超时)”恢复。
|
||||
6. **前端**:用户端显示“已提交审核”,状态变化推送更新;审批后台展示对话摘要和方案。
|
||||
7. **审计**:每步写事件表,包括模型输出、工具参数和结果。
|
||||
|
||||
## 关键代码
|
||||
|
||||
审批节点(示意;可运行版本见 [examples/ticket-flow](../topics/ai/langgraph/examples/ticket-flow/),含 SqliteSaver、interrupt、崩溃恢复和幂等 Mailer 的测试):
|
||||
|
||||
```python
|
||||
from langgraph.types import Command, interrupt
|
||||
|
||||
def risk_check(state: RefundState) -> dict:
|
||||
return {"need_approval": state["amount"] > 500 or state["user_refunds_30d"] >= 3} # 规则写在代码里
|
||||
|
||||
def approval(state: RefundState) -> dict:
|
||||
decision = interrupt({"ticket": state["ticket_id"], "amount": state["amount"], "reason": state["plan_reason"]})
|
||||
return {"approved": decision["approved"], "approver": decision["by"]}
|
||||
|
||||
def execute(state: RefundState) -> dict:
|
||||
if state["need_approval"] and not state.get("approved"):
|
||||
return {"result": "rejected"}
|
||||
key = f"{state['thread_id']}:refund" # 幂等键
|
||||
return {"result": refund_api(state["order_id"], state["amount"], idempotency_key=key)}
|
||||
|
||||
def on_approval(thread_id: str, approved: bool, by: str) -> None: # 审批后台回调
|
||||
graph.invoke(Command(resume={"approved": approved, "by": by}),
|
||||
config={"configurable": {"thread_id": thread_id}})
|
||||
```
|
||||
|
||||
## 取舍
|
||||
|
||||
| 决策 | 选择 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| 是否需要审批 | 代码规则 | 模型可能被注入或误判 |
|
||||
| 流程引擎 | LangGraph(interrupt + 检查点) | 与 Agent 天然结合;Temporal 等工作流引擎更重 |
|
||||
| 恢复方式 | 审批回调触发 resume | 比轮询及时 |
|
||||
| 执行保障 | 幂等键 + 状态检查 | 恢复和重试都可能重复执行节点 |
|
||||
|
||||
## 易错点
|
||||
|
||||
- 把“是否需要审批”交给模型判断。
|
||||
- `interrupt` 所在节点恢复时会**从头重新执行**,节点内 interrupt 之前的副作用会重复——副作用放在 interrupt 之后的节点,或保证幂等。
|
||||
- 审批超时没有处理,线程永远挂起。
|
||||
- 审批人看到的信息不足,只能盲批。
|
||||
|
||||
## 面试怎么讲
|
||||
|
||||
> “高风险操作我用 LangGraph 的 interrupt 做人工审批。是否需要审批由代码规则决定,不交给模型。暂停时创建审批单,检查点存数据库,服务重启不丢;审批回调用 Command(resume) 恢复线程。执行退款用 thread_id 加步骤名作为幂等键,恢复或重试都不会重复退款。超时由延迟任务以驳回恢复,每一步都写审计事件。”
|
||||
Reference in new issue
Block a user