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
@@ -12,3 +12,4 @@ phase5b done (frontend vite/webpack/fe-testing-perf)
|
|||||||
phase5c done (backend mongodb/elasticsearch/data-pipeline/nodejs)
|
phase5c done (backend mongodb/elasticsearch/data-pipeline/nodejs)
|
||||||
phase5d done (java-spring awareness, k8s, run_examples java support)
|
phase5d done (java-spring awareness, k8s, run_examples java support)
|
||||||
phase5 done: all 43 stacks pass --strict (cs algorithms/network-os/web-security added)
|
phase5 done: all 43 stacks pass --strict (cs algorithms/network-os/web-security added)
|
||||||
|
phase6a done (8 cross scenarios)
|
||||||
+416
-1
@@ -4097,7 +4097,9 @@
|
|||||||
"backend.mongodb.document-model"
|
"backend.mongodb.document-model"
|
||||||
],
|
],
|
||||||
"related": [
|
"related": [
|
||||||
"backend.mongodb.interview"
|
"backend.mongodb.interview",
|
||||||
|
"scenario.data-sync",
|
||||||
|
"scenario.customer-service-kb"
|
||||||
],
|
],
|
||||||
"est_hours": 2,
|
"est_hours": 2,
|
||||||
"week": [
|
"week": [
|
||||||
@@ -9191,6 +9193,419 @@
|
|||||||
"path": "research/index.md",
|
"path": "research/index.md",
|
||||||
"sections": []
|
"sections": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.langgraph.scenario.refund-approval",
|
||||||
|
"ai.function-calling.scenario.safe-business-tools",
|
||||||
|
"ai.agent-design.index"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
11,
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/approval-human-in-loop.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.content-moderation",
|
||||||
|
"title": "内容审核:规则 + 模型的分层审核、异步流水线、人工复审、申诉和效果度量",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"mq",
|
||||||
|
"redis",
|
||||||
|
"postgresql",
|
||||||
|
"prompt-engineering",
|
||||||
|
"agent-ops",
|
||||||
|
"fastapi",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"内容审核",
|
||||||
|
"分层过滤",
|
||||||
|
"敏感词",
|
||||||
|
"大模型审核",
|
||||||
|
"人工复审",
|
||||||
|
"异步",
|
||||||
|
"召回率",
|
||||||
|
"误杀率"
|
||||||
|
],
|
||||||
|
"level": "intermediate",
|
||||||
|
"prerequisites": [
|
||||||
|
"backend.mq.index",
|
||||||
|
"ai.prompt-engineering.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.prompt-engineering.scenario.injection-defense",
|
||||||
|
"ai.agent-ops.scenario.agent-quality-gate",
|
||||||
|
"backend.mq.reliability"
|
||||||
|
],
|
||||||
|
"est_hours": 2.5,
|
||||||
|
"week": [
|
||||||
|
13,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/content-moderation.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.customer-service-kb",
|
||||||
|
"title": "客服知识库问答:从文档接入到带引用的流式回答、权限、评测和转人工",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"rag",
|
||||||
|
"vector-db",
|
||||||
|
"elasticsearch",
|
||||||
|
"fastapi",
|
||||||
|
"vue",
|
||||||
|
"redis",
|
||||||
|
"data-pipeline",
|
||||||
|
"agent-ops"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"RAG",
|
||||||
|
"混合检索",
|
||||||
|
"权限",
|
||||||
|
"流式输出",
|
||||||
|
"评测",
|
||||||
|
"转人工",
|
||||||
|
"引用"
|
||||||
|
],
|
||||||
|
"level": "intermediate",
|
||||||
|
"prerequisites": [
|
||||||
|
"ai.rag.index",
|
||||||
|
"python.fastapi.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.rag.scenario.permission-aware-kb",
|
||||||
|
"backend.elasticsearch.scenario.kb-search-sync",
|
||||||
|
"frontend.vue.scenario.ai-chat-stream",
|
||||||
|
"ai.agent-ops.scenario.agent-quality-gate"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
13,
|
||||||
|
15
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/customer-service-kb.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.data-sync",
|
||||||
|
"title": "数据同步:业务库到搜索引擎 / 向量库 / 报表库,outbox 与 CDC、幂等、顺序、对账和重建",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"data-pipeline",
|
||||||
|
"postgresql",
|
||||||
|
"mongodb",
|
||||||
|
"elasticsearch",
|
||||||
|
"vector-db",
|
||||||
|
"mq"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"数据同步",
|
||||||
|
"outbox",
|
||||||
|
"CDC",
|
||||||
|
"变更流",
|
||||||
|
"幂等",
|
||||||
|
"顺序",
|
||||||
|
"对账",
|
||||||
|
"全量重建"
|
||||||
|
],
|
||||||
|
"level": "advanced",
|
||||||
|
"prerequisites": [
|
||||||
|
"backend.data-pipeline.index",
|
||||||
|
"backend.mq.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"backend.elasticsearch.scenario.kb-search-sync",
|
||||||
|
"backend.data-pipeline.scenario.third-party-sync",
|
||||||
|
"backend.mongodb.scenario.conversation-store",
|
||||||
|
"backend.mq.reliability"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
6,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/data-sync.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.flash-sale",
|
||||||
|
"title": "秒杀 / 高并发抢购:流量削峰、Redis 原子扣库存、异步下单、防超卖和防刷",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"redis",
|
||||||
|
"mq",
|
||||||
|
"postgresql",
|
||||||
|
"gin",
|
||||||
|
"nginx",
|
||||||
|
"vue"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"秒杀",
|
||||||
|
"高并发",
|
||||||
|
"库存",
|
||||||
|
"Lua",
|
||||||
|
"削峰",
|
||||||
|
"异步下单",
|
||||||
|
"幂等",
|
||||||
|
"限流",
|
||||||
|
"防刷"
|
||||||
|
],
|
||||||
|
"level": "advanced",
|
||||||
|
"prerequisites": [
|
||||||
|
"backend.redis.index",
|
||||||
|
"backend.mq.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"backend.redis.scenario.stock-reservation",
|
||||||
|
"backend.mq.scenario.delayed-order-cancel",
|
||||||
|
"backend.redis.lock-ratelimit"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
3,
|
||||||
|
14,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/flash-sale.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.multi-tenant-saas",
|
||||||
|
"title": "多租户 SaaS:数据隔离方案、租户上下文、配额与限流、AI 成本分摊和租户级配置",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"postgresql",
|
||||||
|
"vector-db",
|
||||||
|
"redis",
|
||||||
|
"fastapi",
|
||||||
|
"gin",
|
||||||
|
"llm-serving",
|
||||||
|
"k8s"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"多租户",
|
||||||
|
"数据隔离",
|
||||||
|
"行级安全",
|
||||||
|
"租户上下文",
|
||||||
|
"配额",
|
||||||
|
"限流",
|
||||||
|
"成本分摊",
|
||||||
|
"噪声邻居"
|
||||||
|
],
|
||||||
|
"level": "advanced",
|
||||||
|
"prerequisites": [
|
||||||
|
"backend.postgresql.index",
|
||||||
|
"ai.vector-db.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.vector-db.scenario.multi-tenant-index",
|
||||||
|
"ai.rag.scenario.permission-aware-kb",
|
||||||
|
"ai.llm-serving.scenario.multi-provider-gateway"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
6,
|
||||||
|
13,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/multi-tenant-saas.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.report-agent",
|
||||||
|
"title": "报表生成 Agent:自然语言查数、安全的 Text-to-SQL、结果校验、图表和成本控制",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"agent-design",
|
||||||
|
"function-calling",
|
||||||
|
"postgresql",
|
||||||
|
"data-pipeline",
|
||||||
|
"web-security",
|
||||||
|
"react",
|
||||||
|
"agent-ops"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Text-to-SQL",
|
||||||
|
"报表",
|
||||||
|
"只读账号",
|
||||||
|
"SQL 校验",
|
||||||
|
"语义层",
|
||||||
|
"行级安全",
|
||||||
|
"评测"
|
||||||
|
],
|
||||||
|
"level": "advanced",
|
||||||
|
"prerequisites": [
|
||||||
|
"ai.agent-design.index",
|
||||||
|
"backend.postgresql.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.agent-design.scenario.report-agent",
|
||||||
|
"cs.web-security.scenario.ai-app-security",
|
||||||
|
"backend.data-pipeline.scenario.third-party-sync"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
10,
|
||||||
|
13,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/report-agent.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scenario.ticket-system",
|
||||||
|
"title": "工单系统:AI 自动分类与路由、状态机、SLA 超时升级和坐席协作",
|
||||||
|
"type": "scenario",
|
||||||
|
"domain": "cross",
|
||||||
|
"stack": [
|
||||||
|
"postgresql",
|
||||||
|
"mq",
|
||||||
|
"redis",
|
||||||
|
"langchain",
|
||||||
|
"gin",
|
||||||
|
"fastapi",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"工单",
|
||||||
|
"状态机",
|
||||||
|
"自动分类",
|
||||||
|
"路由",
|
||||||
|
"SLA",
|
||||||
|
"延迟队列",
|
||||||
|
"审计",
|
||||||
|
"乐观锁"
|
||||||
|
],
|
||||||
|
"level": "intermediate",
|
||||||
|
"prerequisites": [
|
||||||
|
"backend.postgresql.index",
|
||||||
|
"ai.langchain.index"
|
||||||
|
],
|
||||||
|
"related": [
|
||||||
|
"ai.langchain.scenario.ticket-triage",
|
||||||
|
"backend.postgresql.scenario.order-state-audit",
|
||||||
|
"backend.mq.scenario.delayed-order-cancel",
|
||||||
|
"frontend.react.scenario.long-task-ui"
|
||||||
|
],
|
||||||
|
"est_hours": 3,
|
||||||
|
"week": [
|
||||||
|
3,
|
||||||
|
11,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"order": null,
|
||||||
|
"path": "scenarios/ticket-system.md",
|
||||||
|
"sections": [
|
||||||
|
"业务背景与问题",
|
||||||
|
"约束",
|
||||||
|
"方案设计",
|
||||||
|
"关键代码",
|
||||||
|
"取舍",
|
||||||
|
"易错点",
|
||||||
|
"面试怎么讲"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "scenarios.index",
|
"id": "scenarios.index",
|
||||||
"title": "跨技术栈企业服务场景",
|
"title": "跨技术栈企业服务场景",
|
||||||
|
|||||||
@@ -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 加步骤名作为幂等键,恢复或重试都不会重复退款。超时由延迟任务以驳回恢复,每一步都写审计事件。”
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
id: scenario.content-moderation
|
||||||
|
title: 内容审核:规则 + 模型的分层审核、异步流水线、人工复审、申诉和效果度量
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [mq, redis, postgresql, prompt-engineering, agent-ops, fastapi, react]
|
||||||
|
tags: [内容审核, 分层过滤, 敏感词, 大模型审核, 人工复审, 异步, 召回率, 误杀率]
|
||||||
|
level: intermediate
|
||||||
|
prerequisites: [backend.mq.index, ai.prompt-engineering.index]
|
||||||
|
est_hours: 2.5
|
||||||
|
related: [ai.prompt-engineering.scenario.injection-defense, ai.agent-ops.scenario.agent-quality-gate, backend.mq.reliability]
|
||||||
|
week: [13, 14]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 内容审核:规则 + 模型的分层审核、异步流水线、人工复审、申诉和效果度量
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
社区产品允许用户发帖、评论,同时 AI 助手会生成回复。监管和平台要求:
|
||||||
|
|
||||||
|
1. 违规内容(色情、暴力、诈骗广告、个人信息泄露)要及时拦截;
|
||||||
|
2. 纯人工审核成本高、速度慢;纯关键词误杀多(“打击诈骗”被当成诈骗);
|
||||||
|
3. AI 生成的内容同样要审核;
|
||||||
|
4. 用户对误判可以申诉。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 发帖体验不能明显变慢。
|
||||||
|
- 高风险内容宁可误杀;低风险内容宁可放过后再处理。
|
||||||
|
- 审核标准会随政策变化。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **分层审核**:
|
||||||
|
- 第一层(同步,毫秒级):敏感词(AC 自动机 / 词库服务)、黑名单 URL、正则(手机号、身份证号)→ 命中高危直接拒绝;
|
||||||
|
- 第二层(异步,秒级):大模型按审核标准分类,输出结构化结果(类别、风险等级、证据片段、理由);
|
||||||
|
- 第三层(人工):中风险和模型不确定的内容进入人工复审队列,按风险和时效排序。
|
||||||
|
2. **发布策略**:低风险先发布(“先发后审”),中风险“仅自己可见”直到审核完成,高风险拒绝。
|
||||||
|
3. **异步流水线**:发帖写库后发消息,审核服务消费(重试、死信、幂等:按内容 id + 内容哈希);结果回写并通知。
|
||||||
|
4. **AI 生成内容**:输出前过第一层;流式输出时按句缓冲检测,命中即中止并替换为提示语。
|
||||||
|
5. **审核标准即配置**:审核提示词和类别定义版本化管理;修改后用标注集回归(召回率、误杀率)通过才上线。
|
||||||
|
6. **申诉与反馈**:申诉进入人工复审;复审结论回流到标注集,用于评测和改进规则。
|
||||||
|
7. **防对抗**:谐音、拆字、图片中的文字(OCR);提示词注入(帖子内容里写“忽略审核规则”),审核提示词中把用户内容作为数据隔离。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
审核结果结构与决策(示意):
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ModerationResult(BaseModel):
|
||||||
|
category: Literal["ok", "porn", "violence", "fraud", "pii", "other"]
|
||||||
|
risk: Literal["low", "medium", "high"]
|
||||||
|
evidence: list[str] = Field(max_length=5) # 命中的原文片段,方便复审
|
||||||
|
reason: str
|
||||||
|
|
||||||
|
def decide(rule_hit: RuleHit | None, model: ModerationResult | None) -> str:
|
||||||
|
if rule_hit and rule_hit.level == "high":
|
||||||
|
return "reject"
|
||||||
|
if model is None: # 模型超时 / 不可用:进入人工,不自动放行中高风险
|
||||||
|
return "manual_review"
|
||||||
|
if model.risk == "high":
|
||||||
|
return "reject"
|
||||||
|
if model.risk == "medium" or (rule_hit and rule_hit.level == "medium"):
|
||||||
|
return "manual_review"
|
||||||
|
return "publish"
|
||||||
|
```
|
||||||
|
|
||||||
|
审核提示词中隔离用户内容(防注入)的写法见[提示词注入防护](../topics/ai/prompt-engineering/scenarios/injection-defense.md);评测和门禁见 [Agent 质量门禁](../topics/ai/agent-ops/scenarios/agent-quality-gate.md)。
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 原因 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 审核方式 | 规则 + 模型 + 人工分层 | 兼顾速度、成本和准确率 |
|
||||||
|
| 发布时机 | 按风险分级 | 体验和安全的平衡 |
|
||||||
|
| 模型故障 | 降级为人工 | 不能因故障放行风险内容 |
|
||||||
|
| 标准变更 | 标注集回归 | 防止改一条规则引起大量误杀 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 只看召回率(拦住多少违规),不看误杀率(误伤多少正常内容)。
|
||||||
|
- 审核结果覆盖了用户后来编辑过的新内容(用内容哈希校验版本)。
|
||||||
|
- 把用户内容直接拼进审核提示词,被注入。
|
||||||
|
- 审核日志中保存了大量个人信息,没有脱敏和保留期限。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “内容审核我做了三层:同步的敏感词和正则直接拦高危;异步的大模型按审核标准输出类别、风险和证据;中风险和不确定的进人工复审。发布按风险分级,模型故障时降级到人工而不是放行。审核标准作为版本化配置,修改后用标注集回归召回率和误杀率。申诉结论回流到标注集。”
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
id: scenario.customer-service-kb
|
||||||
|
title: 客服知识库问答:从文档接入到带引用的流式回答、权限、评测和转人工
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [rag, vector-db, elasticsearch, fastapi, vue, redis, data-pipeline, agent-ops]
|
||||||
|
tags: [RAG, 混合检索, 权限, 流式输出, 评测, 转人工, 引用]
|
||||||
|
level: intermediate
|
||||||
|
prerequisites: [ai.rag.index, python.fastapi.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [ai.rag.scenario.permission-aware-kb, backend.elasticsearch.scenario.kb-search-sync, frontend.vue.scenario.ai-chat-stream, ai.agent-ops.scenario.agent-quality-gate]
|
||||||
|
week: [5, 6, 13, 15]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 客服知识库问答:从文档接入到带引用的流式回答、权限、评测和转人工
|
||||||
|
|
||||||
|
> 跨技术栈的综合场景,串联前端、后端、AI 和运维。BOSS直聘 2026-10-23 的调研中,Agent(22 个岗位)、RAG(17 个)是出现最多的要求,这是最常见的落地形态,也适合作为作品集项目。
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
一家 SaaS 公司的客服每天处理大量重复问题(退货、发票、账号、错误码)。要做一个 AI 客服:
|
||||||
|
|
||||||
|
1. 回答必须基于公司文档,不能编造,要给出引用来源;
|
||||||
|
2. 不同客户(租户)、不同部门的文档权限不同;
|
||||||
|
3. 文档经常更新,更新后很快生效;
|
||||||
|
4. 回答不了或用户不满意时转人工,并把上下文交给坐席;
|
||||||
|
5. 需要证明效果:上线前和每次改动后都要有数据。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 首 token 延迟 2 秒以内,回答流式输出。
|
||||||
|
- 模型调用有成本预算。
|
||||||
|
- 数据不能出公司(或只能使用指定的模型服务)。
|
||||||
|
- 团队:1 名前端、1 名后端、你(全栈 + AI)。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
| 层 | 方案 | 专题 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 文档接入 | 连接器增量拉取 → 解析(PDF / Word / 网页,表格单独处理)→ 按标题层级分块 → 嵌入 → 写入;outbox + 队列 + 幂等消费者,每日对账 | [数据管道](../topics/backend/data-pipeline/index.md)、[RAG 切分](../topics/ai/rag/01-pipeline-chunking.md) |
|
||||||
|
| 检索 | ES BM25 + 向量 kNN 两路召回,RRF 融合,重排取前 5;租户和部门权限作为 filter | [ES 混合检索](../topics/backend/elasticsearch/scenarios/kb-search-sync.md)、[权限感知知识库](../topics/ai/rag/scenarios/permission-aware-kb.md) |
|
||||||
|
| 生成 | 只依据资料回答、资料不足时拒答,输出带引用编号;结构化输出(答案、引用、置信度、是否建议转人工) | [提示词与结构化输出](../topics/ai/prompt-engineering/index.md) |
|
||||||
|
| 接口 | FastAPI SSE 流式接口,事件:`citations`、`token`、`done`、`error`;断开时取消生成 | [FastAPI 流式接口](../topics/python/fastapi/scenarios/streaming-chat-api.md) |
|
||||||
|
| 前端 | Vue 对话组件,增量渲染 Markdown(安全清洗),引用可点击看原文,“没解决”按钮转人工 | [Vue AI 对话流](../topics/frontend/vue/scenarios/ai-chat-stream.md)、[AI 应用安全](../topics/cs/web-security/scenarios/ai-app-security.md) |
|
||||||
|
| 缓存与限流 | 通用问题语义缓存(按租户隔离、文档更新时失效);按用户限流 | [Redis](../topics/backend/redis/index.md) |
|
||||||
|
| 转人工 | 置信度低 / 用户点击 / 反复追问时创建工单,附对话摘要和检索资料 | [工单系统](ticket-system.md) |
|
||||||
|
| 评测与监控 | 标注集(问题 → 应引用文档 → 参考答案),CI 中跑召回率@K、引用正确率、拒答正确率;线上记录 Bad Case、成本、延迟 | [Agent 质量门禁](../topics/ai/agent-ops/scenarios/agent-quality-gate.md) |
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
接口骨架(示意;完整实现见第 5~6 周项目和各专题示例):
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/chat")
|
||||||
|
async def chat(req: ChatRequest, user: User = Depends(current_user)):
|
||||||
|
async def events():
|
||||||
|
hits = await retrieve(req.question, acl=user.acl, k=20) # 两路召回 + 权限过滤
|
||||||
|
top = await rerank(req.question, hits, k=5)
|
||||||
|
if not top or top[0].score < MIN_SCORE:
|
||||||
|
await create_ticket(user, req, reason="no_context")
|
||||||
|
yield sse("done", {"answer": "抱歉,资料中没有找到相关内容,已为你转人工。", "handoff": True})
|
||||||
|
return
|
||||||
|
yield sse("citations", [c.public() for c in top]) # 先发引用,前端先展示来源
|
||||||
|
async for token in llm.stream(build_prompt(req.question, top, history=req.history)):
|
||||||
|
yield sse("token", {"text": token})
|
||||||
|
yield sse("done", {"handoff": False})
|
||||||
|
return StreamingResponse(events(), media_type="text/event-stream")
|
||||||
|
```
|
||||||
|
|
||||||
|
评测门禁(CI 中运行,比基线差则失败):
|
||||||
|
|
||||||
|
```python
|
||||||
|
report = evaluate(dataset="eval/kb_v3.jsonl", retriever=retrieve, generator=answer)
|
||||||
|
assert report.recall_at_5 >= baseline.recall_at_5
|
||||||
|
assert report.citation_accuracy >= 0.9
|
||||||
|
assert report.refusal_accuracy >= baseline.refusal_accuracy # 该拒答的要拒答
|
||||||
|
```
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 放弃的 | 原因 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 检索 | 混合检索 + 重排 | 只用向量 | 错误码、型号等精确词召回差 |
|
||||||
|
| 框架 | 主流程手写,部分组件用 LangChain | 全部用框架 | 便于调试和讲清原理 |
|
||||||
|
| 低代码 | Dify 做 POC,正式版写代码 | 直接上线 Dify | 权限、评测、定制 UI 受限,见 [POC 到生产](../topics/ai/lowcode/scenarios/poc-to-production.md) |
|
||||||
|
| 缓存 | 只缓存无个人信息的通用问题 | 所有问题都缓存 | 防止串数据 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 没有评测集,靠感觉调参。
|
||||||
|
- 权限只在检索后过滤,导致结果不足或泄露。
|
||||||
|
- 文档删除、权限变更没有同步。
|
||||||
|
- 回答渲染成 HTML 时没有清洗。
|
||||||
|
- 转人工时没有带上下文,用户要从头再说一遍。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “这是我做的客服知识库问答。文档通过增量同步进 ES,同时存文本和向量;查询时 BM25 和向量两路召回、RRF 融合、重排取前 5,租户和部门权限作为 filter。生成只依据资料、带引用编号,资料不足就拒答并转人工。接口是 FastAPI 的 SSE,先发引用再流式发答案。我维护了一份评测集,CI 里跑召回率、引用正确率和拒答正确率,任何改动比基线差就不合并。”(数字请用你自己项目的实测结果。)
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
---
|
||||||
|
id: scenario.data-sync
|
||||||
|
title: 数据同步:业务库到搜索引擎 / 向量库 / 报表库,outbox 与 CDC、幂等、顺序、对账和重建
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [data-pipeline, postgresql, mongodb, elasticsearch, vector-db, mq]
|
||||||
|
tags: [数据同步, outbox, CDC, 变更流, 幂等, 顺序, 对账, 全量重建]
|
||||||
|
level: advanced
|
||||||
|
prerequisites: [backend.data-pipeline.index, backend.mq.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [backend.elasticsearch.scenario.kb-search-sync, backend.data-pipeline.scenario.third-party-sync, backend.mongodb.scenario.conversation-store, backend.mq.reliability]
|
||||||
|
week: [6, 14]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 数据同步:业务库到搜索引擎 / 向量库 / 报表库,outbox 与 CDC、幂等、顺序、对账和重建
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
业务数据在 PostgreSQL(订单、知识库文档)和 MongoDB(对话记录),需要同步到:ES(全文检索)、向量库(RAG)、报表库(报表 Agent)。现状是业务代码里“写完数据库顺手写 ES”:
|
||||||
|
|
||||||
|
1. 写 ES 失败时数据库已提交,两边不一致;
|
||||||
|
2. 同一条数据快速修改两次,ES 中留下的是旧版本;
|
||||||
|
3. 删除没有同步,搜索出已下架的商品;
|
||||||
|
4. 修改映射或换嵌入模型时不知道怎么重建。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 允许秒级到分钟级的延迟。
|
||||||
|
- 不能明显增加业务写入的耗时。
|
||||||
|
- 下游可以从源头完全重建(源头是唯一可信的数据)。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **捕获变更**(二选一或组合):
|
||||||
|
- **outbox 模式**:业务事务中同时写 outbox 表(事件类型、实体 id、版本号),后台进程投递到消息队列 → 保证“数据库提交 ⇔ 有事件”;
|
||||||
|
- **CDC**:读取数据库日志(PostgreSQL 逻辑复制 / Debezium,MongoDB 变更流),业务代码零侵入。
|
||||||
|
2. **消费与写入**:
|
||||||
|
- 消息只带 id 和版本,消费者回源读取最新数据再写下游(避免消息里的旧快照覆盖新数据);
|
||||||
|
- 下游文档 id = 业务主键,写入带外部版本号,旧版本被拒绝;
|
||||||
|
- 删除事件 → 删除下游文档(或标记删除);
|
||||||
|
- 同一实体的事件按实体 id 分区,保证顺序;
|
||||||
|
- 失败重试、死信、告警。
|
||||||
|
3. **对账**:每天比较源头与下游的数量和版本(按分片哈希),差异自动修复。
|
||||||
|
4. **全量重建**:新建索引 / 集合 → 全量导入(记录开始时间)→ 回放该时间之后的增量 → 校验 → 切换别名 → 保留旧索引回滚。
|
||||||
|
5. **监控**:同步延迟(事件产生到写入下游的时间)、积压量、失败数、对账差异。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
outbox(同一事务中写业务数据和事件):
|
||||||
|
|
||||||
|
```sql
|
||||||
|
BEGIN;
|
||||||
|
UPDATE documents SET body = $2, version = version + 1, updated_at = now() WHERE id = $1;
|
||||||
|
INSERT INTO outbox (aggregate, aggregate_id, event, version)
|
||||||
|
SELECT 'document', id, 'updated', version FROM documents WHERE id = $1;
|
||||||
|
COMMIT;
|
||||||
|
```
|
||||||
|
|
||||||
|
消费者:回源 + 外部版本号(Elasticsearch Python 客户端,示意):
|
||||||
|
|
||||||
|
```python
|
||||||
|
def handle(event: dict) -> None:
|
||||||
|
doc = repo.get(event["aggregate_id"]) # 回源读取最新数据
|
||||||
|
if doc is None or doc.deleted:
|
||||||
|
es.options(ignore_status=404).delete(index="kb", id=event["aggregate_id"])
|
||||||
|
return
|
||||||
|
es.index(index="kb", id=doc.id, document=to_search_doc(doc),
|
||||||
|
version=doc.version, version_type="external") # 版本小于等于已有版本时 ES 返回冲突,直接忽略
|
||||||
|
```
|
||||||
|
|
||||||
|
基础组件的可运行示例:[增量同步管道](../topics/backend/data-pipeline/examples/incremental-sync/)、[MongoDB 变更流](../topics/backend/mongodb/examples/conversations/)、[ES 别名切换](../topics/backend/elasticsearch/examples/hybrid-search/)。
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | outbox | CDC |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 侵入性 | 需要改业务代码 | 业务零侵入 |
|
||||||
|
| 事件语义 | 业务事件,含义清晰 | 行级变更,需要转换 |
|
||||||
|
| 运维 | 简单 | 需要部署和维护 CDC 组件、处理日志保留 |
|
||||||
|
| 适合 | 中小团队、事件少 | 表多、多个下游 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 业务代码中双写(数据库 + ES),没有事务保证。
|
||||||
|
- 消息中带完整快照,乱序时旧快照覆盖新数据。
|
||||||
|
- 忘记同步删除和权限变更。
|
||||||
|
- 重建期间的增量没有回放,切换后丢数据。
|
||||||
|
- 没有同步延迟监控,下游落后几小时才发现。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “我们把‘写库顺手写 ES’改成了 outbox:业务事务里同时写 outbox 表,后台投递到队列,保证提交和事件一致。消费者只拿 id 回源读最新数据,用业务主键作为文档 id、版本号作为外部版本,乱序和重复都不会让旧数据覆盖新数据;删除也同步。每天按分片哈希对账;换映射或嵌入模型时新建索引全量导入、回放增量、校验后切别名。监控同步延迟和积压。”
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
id: scenario.flash-sale
|
||||||
|
title: 秒杀 / 高并发抢购:流量削峰、Redis 原子扣库存、异步下单、防超卖和防刷
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [redis, mq, postgresql, gin, nginx, vue]
|
||||||
|
tags: [秒杀, 高并发, 库存, Lua, 削峰, 异步下单, 幂等, 限流, 防刷]
|
||||||
|
level: advanced
|
||||||
|
prerequisites: [backend.redis.index, backend.mq.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [backend.redis.scenario.stock-reservation, backend.mq.scenario.delayed-order-cancel, backend.redis.lock-ratelimit]
|
||||||
|
week: [3, 14, 16]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 秒杀 / 高并发抢购:流量削峰、Redis 原子扣库存、异步下单、防超卖和防刷
|
||||||
|
|
||||||
|
> 经典的后端面试场景题。你的主线是 AI 应用,但“高并发怎么设计”几乎每个后端 / 全栈面试都会问。
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
电商平台做限量 1000 件的秒杀,预计瞬间几十万请求:
|
||||||
|
|
||||||
|
1. 上次活动数据库被打垮,整个商城不可用;
|
||||||
|
2. 出现超卖(卖出 1003 件);
|
||||||
|
3. 同一用户用脚本抢到多件;
|
||||||
|
4. 用户付款超时后库存没有释放。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 活动时间固定,流量可预期。
|
||||||
|
- 不能影响商城的其他功能。
|
||||||
|
- 库存数据最终以数据库为准。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **前端和网关削峰**:静态页面走 CDN;按钮到点才可点击、点击后置灰;验证码或答题打散请求;Nginx / 网关按 IP 和用户限流。
|
||||||
|
2. **独立部署**:秒杀服务独立,和主站隔离,挂了不影响主站。
|
||||||
|
3. **预热**:活动前把库存加载到 Redis。
|
||||||
|
4. **原子扣减**:Redis Lua 脚本一次完成“检查是否已购买 → 检查库存 → 扣减 → 记录用户”,避免超卖和重复购买。
|
||||||
|
5. **异步下单**:扣减成功后发消息,下单服务按数据库能承受的速率消费;数据库层 `UPDATE stock SET n = n - 1 WHERE id = ? AND n > 0` 再兜底一次,订单表用户 + 活动唯一约束保证幂等。
|
||||||
|
6. **结果查询**:前端轮询“排队中 / 成功 / 失败”。
|
||||||
|
7. **超时释放**:订单创建时投递延迟消息,到期未支付则取消订单、库存加回 Redis 和数据库。
|
||||||
|
8. **防刷**:登录态 + 设备指纹 + 风控规则;隐藏真实下单地址(到点才下发带签名的令牌)。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
Redis Lua 原子扣减(可以直接在 Redis 中运行):
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- KEYS[1]=库存 key KEYS[2]=已购用户集合 ARGV[1]=用户 id
|
||||||
|
if redis.call('SISMEMBER', KEYS[2], ARGV[1]) == 1 then
|
||||||
|
return -1 -- 已经抢过
|
||||||
|
end
|
||||||
|
local stock = tonumber(redis.call('GET', KEYS[1]) or '0')
|
||||||
|
if stock <= 0 then
|
||||||
|
return 0 -- 售罄
|
||||||
|
end
|
||||||
|
redis.call('DECR', KEYS[1])
|
||||||
|
redis.call('SADD', KEYS[2], ARGV[1])
|
||||||
|
return 1 -- 成功,随后发消息异步下单
|
||||||
|
```
|
||||||
|
|
||||||
|
预占库存与超时释放的完整讨论见 [Redis 库存预占](../topics/backend/redis/scenarios/stock-reservation.md)、[延迟消息取消订单](../topics/backend/mq/scenarios/delayed-order-cancel.md)。
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 原因 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 扣库存位置 | Redis 原子扣减 + 数据库兜底 | 数据库扛不住瞬时流量 |
|
||||||
|
| 下单 | 异步(消息队列) | 削峰,按数据库能力消费 |
|
||||||
|
| 一致性 | 最终一致 + 对账 | Redis 和数据库之间可能短暂不一致 |
|
||||||
|
| 用户体验 | 排队中 + 轮询结果 | 换取系统稳定 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 先查库存再扣减(两步操作),并发下超卖。
|
||||||
|
- Redis 扣减成功但消息发送失败,库存“丢失”:需要补偿(本地消息表 / 对账)。
|
||||||
|
- 数据库下单没有唯一约束,消息重复消费产生重复订单。
|
||||||
|
- 超时取消时没有检查订单是否已支付。
|
||||||
|
- 秒杀服务和主站共用数据库连接池。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “秒杀我分层削峰:CDN 静态页、按钮控制和验证码打散、网关限流;秒杀服务独立部署。库存提前加载到 Redis,用 Lua 脚本原子完成‘是否已购、库存检查、扣减、记录用户’,杜绝超卖和重复购买。扣减成功后发消息异步下单,数据库用条件更新再兜底,订单表唯一约束保证幂等;未支付由延迟消息超时释放库存。Redis 和数据库之间通过对账保证最终一致。”
|
||||||
+12
-10
@@ -8,15 +8,17 @@ stack: []
|
|||||||
|
|
||||||
# 跨技术栈企业服务场景
|
# 跨技术栈企业服务场景
|
||||||
|
|
||||||
每个场景按“业务背景与问题 → 约束 → 方案设计 → 关键代码 → 取舍 → 易错点 → 面试怎么讲”组织,综合多个技术栈。
|
每个场景按“业务背景与问题 → 约束 → 方案设计 → 关键代码 → 取舍 → 易错点 → 面试怎么讲”组织,综合多个技术栈,并链接到各专题中更细的场景和可运行示例。面试中的“系统设计题”“讲一个你做过的项目”都可以从这里取材。
|
||||||
|
|
||||||
> 建设中,计划包含:
|
| 场景 | 主要技术栈 | 对应周 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [客服知识库问答](customer-service-kb.md) | RAG、ES 混合检索、FastAPI SSE、Vue、评测 | 5、6、13、15 |
|
||||||
|
| [工单系统](ticket-system.md) | PostgreSQL 状态机、MQ、LangChain 分类、React | 3、11、14 |
|
||||||
|
| [审批流 + 人工介入的 Agent](approval-human-in-loop.md) | LangGraph interrupt、检查点、幂等 | 11、12 |
|
||||||
|
| [报表生成 Agent](report-agent.md) | Text-to-SQL、语义层、RLS、评测 | 10、13、14 |
|
||||||
|
| [秒杀 / 高并发抢购](flash-sale.md) | Redis Lua、MQ、数据库兜底 | 3、14、16 |
|
||||||
|
| [多租户 SaaS](multi-tenant-saas.md) | RLS、租户上下文、配额、成本分摊 | 6、13、14 |
|
||||||
|
| [内容审核](content-moderation.md) | 规则 + 模型 + 人工、异步流水线 | 13、14 |
|
||||||
|
| [数据同步](data-sync.md) | outbox、CDC、ES / 向量库、对账、重建 | 6、14 |
|
||||||
|
|
||||||
- 客服知识库问答(`customer-service-kb`)
|
各技术栈内的单栈场景见各专题的 `scenarios/` 目录([专题总览](../topics/index.md))。
|
||||||
- 工单系统(智能分类与流转)(`ticket-system`)
|
|
||||||
- 审批流 + 人工介入的 Agent(`approval-human-in-loop`)
|
|
||||||
- 报表生成 Agent(自然语言查数)(`report-agent`)
|
|
||||||
- 秒杀 / 高并发抢购(`flash-sale`)
|
|
||||||
- 多租户 SaaS(`multi-tenant-saas`)
|
|
||||||
- 内容审核(`content-moderation`)
|
|
||||||
- 数据同步(数据库到搜索 / 向量库)(`data-sync`)
|
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
id: scenario.multi-tenant-saas
|
||||||
|
title: 多租户 SaaS:数据隔离方案、租户上下文、配额与限流、AI 成本分摊和租户级配置
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [postgresql, vector-db, redis, fastapi, gin, llm-serving, k8s]
|
||||||
|
tags: [多租户, 数据隔离, 行级安全, 租户上下文, 配额, 限流, 成本分摊, 噪声邻居]
|
||||||
|
level: advanced
|
||||||
|
prerequisites: [backend.postgresql.index, ai.vector-db.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [ai.vector-db.scenario.multi-tenant-index, ai.rag.scenario.permission-aware-kb, ai.llm-serving.scenario.multi-provider-gateway]
|
||||||
|
week: [6, 13, 14]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 多租户 SaaS:数据隔离方案、租户上下文、配额与限流、AI 成本分摊和租户级配置
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
AI 客服做成了 SaaS,服务几百家企业客户:
|
||||||
|
|
||||||
|
1. 一个开发者漏写了租户条件,A 公司看到了 B 公司的工单(严重事故);
|
||||||
|
2. 一个大客户批量导入文档,向量化任务占满资源,其他客户的问答变慢(噪声邻居);
|
||||||
|
3. 模型费用无法按客户核算,定价没有依据;
|
||||||
|
4. 大客户要求数据物理隔离、使用自己的模型密钥。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 大多数客户规模小,单独部署成本太高。
|
||||||
|
- 少数大客户愿意为隔离付费。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **隔离分级**:
|
||||||
|
| 级别 | 方式 | 适合 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 共享表 + `tenant_id` + 行级安全 | 成本最低 | 大多数客户 |
|
||||||
|
| 每租户独立 schema / 向量集合 | 中等 | 中型客户 |
|
||||||
|
| 独立数据库 / 独立部署 | 最高 | 有合规要求的大客户 |
|
||||||
|
通过“租户 → 数据源”的路由表决定访问哪里,代码对上层透明。
|
||||||
|
2. **租户上下文**:网关从令牌中解析租户,放入请求上下文;数据访问层自动注入(ORM 全局过滤 + PostgreSQL RLS 兜底);缓存 key、向量检索 filter、对象存储路径、日志都带租户 id。见[多租户向量索引](../topics/ai/vector-db/scenarios/multi-tenant-index.md)。
|
||||||
|
3. **防噪声邻居**:按租户限流(Redis);后台任务队列按租户公平调度(每个租户的并发上限);大批量导入走单独队列。
|
||||||
|
4. **配额与计费**:每次模型调用记录租户、模型、输入 / 输出 token、成本;按套餐设置月度额度,超额降级到小模型或拒绝;报表给销售和财务。见[多模型网关](../topics/ai/llm-serving/scenarios/multi-provider-gateway.md)。
|
||||||
|
5. **租户级配置**:提示词、模型选择、品牌样式、功能开关按租户配置;支持客户自带模型密钥(加密存储,按租户使用)。
|
||||||
|
6. **测试**:跨租户访问的自动化测试(用 A 的令牌访问 B 的每类资源,必须 404 / 403)放进 CI。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
租户上下文 + RLS(FastAPI + PostgreSQL,示意):
|
||||||
|
|
||||||
|
```python
|
||||||
|
tenant_ctx: ContextVar[str] = ContextVar("tenant_id")
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def tenant_middleware(request: Request, call_next):
|
||||||
|
claims = verify_jwt(request.headers.get("authorization"))
|
||||||
|
token = tenant_ctx.set(claims["tenant_id"])
|
||||||
|
try:
|
||||||
|
return await call_next(request)
|
||||||
|
finally:
|
||||||
|
tenant_ctx.reset(token)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def tenant_conn():
|
||||||
|
with pool.connection() as conn, conn.transaction():
|
||||||
|
conn.execute("SELECT set_config('app.tenant_id', %s, true)", (tenant_ctx.get(),)) # RLS 策略读取它
|
||||||
|
yield conn
|
||||||
|
|
||||||
|
def cache_key(*parts: str) -> str:
|
||||||
|
return ":".join(["t", tenant_ctx.get(), *parts]) # 缓存 key 带租户,防止串数据
|
||||||
|
```
|
||||||
|
|
||||||
|
跨租户访问测试:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@pytest.mark.parametrize("path", ["/tickets/{id}", "/docs/{id}", "/conversations/{id}"])
|
||||||
|
def test_cross_tenant_access_denied(path, tenant_a_token, tenant_b_resources):
|
||||||
|
r = client.get(path.format(id=tenant_b_resources[path]), headers=auth(tenant_a_token))
|
||||||
|
assert r.status_code in (403, 404)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 原因 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 隔离方式 | 分级(共享为主,大客户独立) | 平衡成本和合规 |
|
||||||
|
| 租户过滤 | 应用层自动注入 + 数据库 RLS 兜底 | 一层失误还有另一层 |
|
||||||
|
| 资源公平 | 租户级限流和任务并发上限 | 防噪声邻居 |
|
||||||
|
| 计费 | 按 token 记录到租户 | 定价有依据 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 只依赖开发者记得写 `WHERE tenant_id = ?`。
|
||||||
|
- 缓存 key、向量检索、文件路径漏了租户维度。
|
||||||
|
- 异步任务中丢失租户上下文(ContextVar 不会自动跨进程 / 跨消息传递,需要在消息中显式携带)。
|
||||||
|
- 管理后台的“超级管理员”接口没有审计。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “多租户我做了分级隔离:大多数客户共享表加 tenant_id,数据库层用行级安全兜底;有合规要求的大客户独立库,通过路由表透明切换。租户从令牌解析放进上下文,数据访问层自动注入,缓存 key、向量检索、文件路径都带租户。防噪声邻居靠租户级限流和任务并发上限;每次模型调用按租户记 token 和成本,支撑套餐定价。CI 里有跨租户访问测试。”
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
id: scenario.report-agent
|
||||||
|
title: 报表生成 Agent:自然语言查数、安全的 Text-to-SQL、结果校验、图表和成本控制
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [agent-design, function-calling, postgresql, data-pipeline, web-security, react, agent-ops]
|
||||||
|
tags: [Text-to-SQL, 报表, 只读账号, SQL 校验, 语义层, 行级安全, 评测]
|
||||||
|
level: advanced
|
||||||
|
prerequisites: [ai.agent-design.index, backend.postgresql.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [ai.agent-design.scenario.report-agent, cs.web-security.scenario.ai-app-security, backend.data-pipeline.scenario.third-party-sync]
|
||||||
|
week: [10, 13, 14]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 报表生成 Agent:自然语言查数、安全的 Text-to-SQL、结果校验、图表和成本控制
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
运营和商家希望用一句话查数据(“上个月各渠道的退款率,按周看趋势”)。早期原型直接让模型写 SQL 执行:
|
||||||
|
|
||||||
|
1. 生成的 SQL 有时口径错误,结果看起来合理但是错的;
|
||||||
|
2. 有一次生成了没有租户条件的查询,看到了其他商家的数据;
|
||||||
|
3. 复杂查询拖慢了业务主库;
|
||||||
|
4. 一个问题调用模型多次,成本不可控。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 数据来自同步过来的订单库(见[数据同步](data-sync.md))。
|
||||||
|
- 只读;结果要能解释(口径、SQL)。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **语义层**:数据团队以 YAML 定义指标(退款率 = 退款订单数 / 支付订单数)和维度(渠道、周);模型优先选择“指标 + 维度 + 过滤条件”,由代码生成 SQL;覆盖不了的才让模型写 SQL。
|
||||||
|
2. **SQL 安全**(见 [AI 应用安全](../topics/cs/web-security/scenarios/ai-app-security.md)):只读账号、只授权报表视图、连接只读副本;SQL 解析后只允许单条 SELECT 和白名单表;**租户隔离由数据库行级安全(RLS)保证**,会话变量由代码设置,模型无法绕过;强制 LIMIT、语句超时。
|
||||||
|
3. **Agent 流程**:理解 → 澄清(时间范围不明确时先问)→ 选指标 / 生成 SQL → 校验 → 执行 → 结果检查(空结果、异常值)→ 图表配置和文字结论。见[报表 Agent](../topics/ai/agent-design/scenarios/report-agent.md)。
|
||||||
|
4. **可解释**:附口径说明和 SQL(可折叠),用户可“报告错误”进入 Bad Case 库。
|
||||||
|
5. **成本**:模型调用次数上限(中间件)、常见问题缓存、简单问题路由到小模型(见[多模型网关](../topics/ai/llm-serving/scenarios/multi-provider-gateway.md))。
|
||||||
|
6. **评测**:问题 → 期望结果(不是期望 SQL),比较执行结果;CI 中运行。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
数据库层的租户隔离(PostgreSQL RLS):
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE rpt_orders ENABLE ROW LEVEL SECURITY;
|
||||||
|
CREATE POLICY tenant_isolation ON rpt_orders
|
||||||
|
USING (tenant_id = current_setting('app.tenant_id'));
|
||||||
|
GRANT SELECT ON rpt_orders TO report_reader; -- 报表账号只读,且不是表的所有者(所有者默认不受 RLS 限制)
|
||||||
|
```
|
||||||
|
|
||||||
|
执行模型生成的 SQL(示意):
|
||||||
|
|
||||||
|
```python
|
||||||
|
def run_report_sql(sql: str, tenant_id: str) -> list[tuple]:
|
||||||
|
check_select_only(sql, allowed_tables={"rpt_orders", "rpt_refunds"}) # 用 SQL 解析库(如 sqlglot)做结构校验,不用正则
|
||||||
|
with readonly_pool.connection() as conn, conn.transaction():
|
||||||
|
conn.execute("SET LOCAL statement_timeout = '5s'")
|
||||||
|
conn.execute("SELECT set_config('app.tenant_id', %s, true)", (tenant_id,)) # 事务内有效,由代码设置
|
||||||
|
return conn.execute(f"SELECT * FROM ({sql}) AS q LIMIT 1000").fetchall()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 原因 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 生成方式 | 语义层优先,SQL 兜底 | 口径一致、可控 |
|
||||||
|
| 租户隔离 | RLS + 会话变量 | 不依赖模型写对条件,子查询 / CTE 也覆盖 |
|
||||||
|
| 执行位置 | 只读副本 | 不影响主库 |
|
||||||
|
| 评测方式 | 比较执行结果 | 同一问题有多种正确 SQL |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- 让模型自己写租户条件。
|
||||||
|
- 用正则判断 SQL 是否只读(注释、多语句可绕过)。
|
||||||
|
- 报表账号是表的所有者,RLS 不生效(需要 `FORCE ROW LEVEL SECURITY` 或换账号)。
|
||||||
|
- 结果为空时模型“编”一个结论。
|
||||||
|
- 只评测 SQL 能否执行,不评测结果是否正确。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “报表 Agent 的核心是不信任模型生成的 SQL。常见指标走语义层由代码生成 SQL;必须让模型写时,解析后只允许单条 SELECT 和白名单表,租户隔离交给 PostgreSQL 的行级安全,会话变量由代码设置,模型绕不过去;只读账号连只读副本并设超时。评测集比较执行结果而不是 SQL 文本,每个回答附口径和 SQL,用户一键报错进 Bad Case 库。”
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
id: scenario.ticket-system
|
||||||
|
title: 工单系统:AI 自动分类与路由、状态机、SLA 超时升级和坐席协作
|
||||||
|
type: scenario
|
||||||
|
domain: cross
|
||||||
|
stack: [postgresql, mq, redis, langchain, gin, fastapi, react]
|
||||||
|
tags: [工单, 状态机, 自动分类, 路由, SLA, 延迟队列, 审计, 乐观锁]
|
||||||
|
level: intermediate
|
||||||
|
prerequisites: [backend.postgresql.index, ai.langchain.index]
|
||||||
|
est_hours: 3
|
||||||
|
related: [ai.langchain.scenario.ticket-triage, backend.postgresql.scenario.order-state-audit, backend.mq.scenario.delayed-order-cancel, frontend.react.scenario.long-task-ui]
|
||||||
|
week: [3, 11, 14]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 工单系统:AI 自动分类与路由、状态机、SLA 超时升级和坐席协作
|
||||||
|
|
||||||
|
## 业务背景与问题
|
||||||
|
|
||||||
|
客服中心的工单靠人工分派:
|
||||||
|
|
||||||
|
1. 分类不准、分派慢,首次响应时间长;
|
||||||
|
2. 工单状态混乱(已关闭的又被改回处理中),没有操作记录;
|
||||||
|
3. 超过 SLA 没人知道;
|
||||||
|
4. 两个坐席同时处理同一工单,互相覆盖。
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 分类错误会耽误处理,但人工可以纠正。
|
||||||
|
- 需要审计:谁在什么时候做了什么。
|
||||||
|
- AI 不可用时系统照常运行(降级为人工分派)。
|
||||||
|
|
||||||
|
## 方案设计
|
||||||
|
|
||||||
|
1. **状态机与审计**(PostgreSQL):`new → triaged → assigned → in_progress → waiting_customer → resolved → closed`,只允许合法转换;每次转换在同一事务中写事件表;`version` 字段做乐观锁。见[订单状态与审计](../topics/backend/postgresql/scenarios/order-state-audit.md)。
|
||||||
|
2. **AI 分类**(LangChain 结构化输出):类别、优先级、建议处理组、置信度、理由;置信度低于阈值进入“待人工分类”;坐席的纠正记录为评测数据。见[工单智能分诊](../topics/ai/langchain/scenarios/ticket-triage.md)。
|
||||||
|
3. **异步流程**:创建工单后发消息,分类服务消费(重试、死信);回写结果时校验状态和版本,不覆盖人工修改。
|
||||||
|
4. **SLA**:按优先级计算截止时间,投递延迟消息;到期时**再检查当前状态**,仍未处理才升级。见[延迟消息](../topics/backend/mq/scenarios/delayed-order-cancel.md)。
|
||||||
|
5. **坐席界面**(React):列表实时更新,显示 AI 分类理由,一键采纳或纠正;处理中用 Redis 软锁(带过期)提示其他人。见[长任务界面](../topics/frontend/react/scenarios/long-task-ui.md)。
|
||||||
|
6. **降级**:分类服务超时或不可用时,工单直接进入人工分派队列。
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
状态转换 + 乐观锁 + 审计(一个事务中):
|
||||||
|
|
||||||
|
```sql
|
||||||
|
UPDATE tickets
|
||||||
|
SET status = 'assigned', assignee_id = $3, version = version + 1, updated_at = now()
|
||||||
|
WHERE id = $1 AND version = $2 AND status IN ('new', 'triaged');
|
||||||
|
-- 影响行数为 0:状态不允许或已被别人修改,返回 409 让前端刷新
|
||||||
|
|
||||||
|
INSERT INTO ticket_events (ticket_id, actor, action, from_status, to_status, payload)
|
||||||
|
VALUES ($1, $4, 'assign', 'triaged', 'assigned', jsonb_build_object('assignee', $3));
|
||||||
|
```
|
||||||
|
|
||||||
|
分类结果结构(示意):
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Triage(BaseModel):
|
||||||
|
category: Literal["refund", "invoice", "account", "bug", "other"]
|
||||||
|
priority: Literal["P1", "P2", "P3"]
|
||||||
|
group: str
|
||||||
|
confidence: float = Field(ge=0, le=1)
|
||||||
|
reason: str = Field(max_length=200)
|
||||||
|
|
||||||
|
result = triage_chain.invoke({"title": t.title, "body": t.body}) # model.with_structured_output(Triage)
|
||||||
|
if result.confidence < 0.7:
|
||||||
|
route_to_manual_queue(t.id, suggestion=result)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 取舍
|
||||||
|
|
||||||
|
| 决策 | 选择 | 原因 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 分类方式 | 大模型结构化输出 + 置信度阈值 | 冷启动快;纠正数据积累后可训练小模型降成本 |
|
||||||
|
| 并发控制 | 乐观锁 + 软锁提示 | 冲突少,悲观锁会让坐席等待 |
|
||||||
|
| SLA | 延迟消息 + 到期检查,定时扫描兜底 | 及时且可靠 |
|
||||||
|
| 语言 | 工单核心用 Go(Gin),AI 分类用 Python | 发挥两者优势;全部 Python 也可以 |
|
||||||
|
|
||||||
|
## 易错点
|
||||||
|
|
||||||
|
- AI 分类结果覆盖了人工已修改的字段。
|
||||||
|
- 状态转换只靠前端隐藏按钮,数据库层没有约束。
|
||||||
|
- 延迟消息到期时不检查当前状态,已处理的工单也被升级。
|
||||||
|
- 审计日志和业务更新不在同一事务中。
|
||||||
|
|
||||||
|
## 面试怎么讲
|
||||||
|
|
||||||
|
> “工单系统用状态机约束转换,每次转换在同一事务中写审计事件,乐观锁防止坐席互相覆盖。AI 分类用结构化输出给出类别、优先级、建议组和置信度,低于阈值进入人工队列,纠正记录作为评测数据。SLA 用延迟消息,到期再检查状态决定是否升级。分类服务挂了就直接人工分派,不影响主流程。”
|
||||||
@@ -8,7 +8,7 @@ tags: [对话记录, 文档建模, TTL, 归档, 变更流, 多租户]
|
|||||||
level: intermediate
|
level: intermediate
|
||||||
prerequisites: [backend.mongodb.document-model]
|
prerequisites: [backend.mongodb.document-model]
|
||||||
est_hours: 2
|
est_hours: 2
|
||||||
related: [backend.mongodb.interview]
|
related: [backend.mongodb.interview, scenario.data-sync, scenario.customer-service-kb]
|
||||||
week: [14]
|
week: [14]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user