一、什么是「关键事件」
「关键事件」是 GPTBots 内置的长程业务记忆能力:在用户与 Bot 多轮对话过程中,平台会调用 LLM 自动从对话里识别出有业务价值的事件(如取款、投诉、预约、退款……),按用户维度结构化存储下来,并在后续对话中作为上下文回注给 Bot,让 Bot「记得」这位用户过去发生了什么、当前事件处理到哪一步。
解决的核心问题是:
- 跨会话记忆缺失:传统 LLM 只能看到当前会话的短期记忆窗口,跨会话或长会话中关键业务动作被遗忘。
- 结构化数据沉淀:把零散对话沉淀为可查询、可统计、可审计的事件流,供风控、客服质检、用户画像复用。
- 状态推进追踪:每条事件有
PENDING / IN_PROGRESS / RESOLVED / CLOSED状态,能够在多次对话间持续推进。
二、设计理念
1. 把"对话"沉淀为"事件流"
对话是非结构化的,事件是结构化的。关键事件把"用户说过什么、做过什么"翻译成「分类 + 摘要 + 实体 + 状态 + 优先级 + 置信度」的事件记录,让 LLM 之外的下游系统(统计、风控、人工质检)也能消费这份记忆。
2. 双轨触发,避免漏抓也避免狂抓
单次对话里事件可能在中途出现,也可能在用户离开之后才需要补抽。系统采用消息阈值(实时)+ 空闲超时(批量)+ 手动触发(兜底)的三级方案,开发者可调参数权衡成本与时效。
3. 默认开启 LLM 抽取,可降级到便宜模型
事件抽取本身需要 LLM 推理,会消耗 Token。因此抽取模型与 Bot 主对话模型可以独立配置——例如主对话用 Claude / GPT,抽取用更便宜的小模型,单独计费、单独优化。
4. 用户身份双链路兜底
事件按用户维度归集。优先使用开发者透传的 userId(最高级标识),缺失时回退到平台生成的 anonymousId(如未登录用户)。即使开发者没传 userId,匿名访客的对话也能完成事件累积。
5. 严格隔离用户作用域,防 LLM 幻觉污染
LLM 在 UPDATE / DELETE / MERGE 时可能"编造"出别的用户的 eventId。代码内部对每条操作都做 matchesUserScope 校验,强制把 userId / anonymousId 作为硬过滤条件,防止跨用户事件污染。
6. Prompt 注入显式 + 隐式两条路径
- 显式:Prompt 里写
{{key_event_<eventType>}}占位符,按事件类型精确取值。 - 隐式:未写占位符时,自动追加
## Recent Key Events段落到 Prompt 末尾,零配置可用。
三、核心原理
3.1 数据模型
| 角色 | 备注 |
|---|---|
| 关键事件主表 | 按用户/类型/时间多维索引 |
| Bot 级配置(每个 Bot 唯一) | 控制开关、阈值、提取规则 |
| 会话级提取状态 | 记录待抽消息数、空闲时间、并发状态 |
| 提取执行记录 | 每次抽取的审计 + Token / 积分消耗 |
事件主结构(BotEventEntity)核心字段:
| 字段 | 含义 |
|---|---|
eventType |
事件分类,与 Bot 配置的字典严格一致(如 withdrawal / complaint) |
summary |
事件摘要,≤200 字 |
status |
PENDING(待处理)/ IN_PROGRESS(处理中)/ RESOLVED(已解决)/ CLOSED(已关闭) |
priority |
LOW / MEDIUM / HIGH / CRITICAL |
confidence |
LLM 自评的置信度:HIGH(API 数据)/ MEDIUM(双方确认)/ LOW(用户单方声称) |
credibilityLabel |
自然语言真实性标签("系统API确认"/"用户声称待核实"/"双方确认") |
disputeFlag |
是否存在矛盾信息(用户陈述 ↔ 系统数据冲突) |
entities |
灵活 KV 结构化实体(可存金额、订单号、时间等) |
relatedEventIds |
关联事件 ID(典型用于 MERGE 后的来源指针) |
sourceMsgIds |
触发该事件的消息 ID 列表 |
createdBy |
来源:LLM / API / UI |
3.2 三级触发机制
┌── Level 1:实时触发 ─────────────────┐
│ 每条用户消息 +1 计数 │
│ pendingMessageCount ≥ messageThreshold │
│ 立即调 LLM 抽取 │
└──────────────────────────────────────┘
┌── Level 2:空闲触发 ─────────────────┐
│ 定时扫描(30/60/300 秒一轮) │
│ 命中条件: │
│ - lastMessageTime > idleTimeout 前 │
│ - pendingMessageCount > 0 │
│ - extractionStatus == IDLE │
└──────────────────────────────────────┘
┌── Level 3:手动触发 ─────────────────┐
│ 调 API: │
│ POST /bot/event/execute/.../retry │
│ 或在执行记录页点"重试" │
└──────────────────────────────────────┘
三个入口最终都会进入同一个 pipeline(BotEventExtractionService.extract),通过 Redis 锁(bot:event:extract:{botId}:{conversationId},5 分钟超时)保证同一会话不会并发抽取。
3.3 提取流水线
1. 校验开关 → Redis 抢锁
2. 读 Track / Redis / c_conversation 三级兜底身份
3. 取自 lastExtractedMessageTime 之后的新消息(≤100 条)
+ 向上追加 10 条历史消息保证语境完整
+ 实时触发时再裁掉短期记忆窗口尾部(与主对话窗口对齐)
4. 拉取该用户最近 N 条历史事件(recentEventCount)
5. 按模板拼 Prompt:
- 用户自定义提取规则
- 允许的事件分类(name + description)
- 历史事件(去重比对用)
- 待分析对话
6. 选择提取模型,下发 JSON Schema(OpenAI 系才生效)
7. 解析 LLM 返回的 operations 数组:
CREATE → 新建事件
UPDATE → 推进既有事件状态/摘要
MERGE → 合并多条冗余事件
DELETE → 软删(用户撤销/否定)
8. 跨用户作用域强制校验
9. 写积分消耗(CONSUME_KEY_EVENT 类型)
10. 写执行记录 + 重置 Track
3.4 注入机制
模式 A:自动追加(零配置)
当 Bot 启用关键事件,且 Prompt 中未出现 {{key_event_*}} 占位符时,系统在每次对话调用 LLM 之前,把最近 N 条事件按以下格式自动拼到 Prompt 末尾:
## Recent Key Events
eventType: withdrawal | summary: 用户申请提现 5000 元 | status: 处理中 | priority: 高 | confidence: 高 | updateTime: 2026-04-28 09:21:33 | entities: {"amount":5000,"channel":"bank"}
eventType: complaint | summary: 用户投诉客服响应慢 | status: 已解决 | priority: 中 | confidence: 中 | updateTime: 2026-04-27 18:02:11
模式 B:占位符替换(精确控制)
在 Prompt / 工作流组件 / 规则中写:
{{key_event_withdrawal}} ← 仅注入"取款"类事件
{{key_event_complaint}} ← 仅注入"投诉"类事件
平台按 eventType 分组生成对应变量,未匹配的事件类型不会被注入。最多注入 30 条 / 变量。
Flow Bot:组件级独立控制
工作流的每个 LLM 节点都有自己的 FlowKeyEventConfig:可单独开关、单独选事件类型、单独设 recentEventCount(默认 5 条)。同一会话内事件只查一次(缓存在 ChatContext),多个节点共享数据。
四、使用指南
4.1 在 Bot 配置面板启用
进入路径:开发者控制台 → Bot 详情 → 用户管理(User Manage) → 「关键事件」抽屉
操作步骤:
- 打开总开关
- 提取模型:可选项;不选时使用 Bot 默认对话模型。建议选一个便宜模型(例如 GPT-4o-mini / Haiku 系列)。
- 提取规则(≤2000 字符):用自然语言告诉 LLM 这个 Bot 场景下"什么算关键事件"。例:只抽取与资金相关的业务动作(取款、存款、转账、退款)。 忽略闲聊和情绪表达。 实体字段必须包含金额(amount)、币种(currency)、订单号(orderId)。
只抽取与资金相关的业务动作(取款、存款、转账、退款)。 忽略闲聊和情绪表达。 实体字段必须包含金额(amount)、币种(currency)、订单号(orderId)。此代码块在浮窗中显示 - 最近事件数(0
50):每次对话注入 Prompt 的事件数。0 表示不注入,常见 510。 - 触发时机:
- 消息阈值(5~50):默认 10。
- 空闲超时(2~60 分钟):默认 3。
- 事件分类字典(最多 10 个):每个分类含「名称」(≤10 字)和「描述」(≤100 字,告诉 LLM 这个分类装什么)。
- 点保存。
⚠️ 警告:分类名一旦上线就不要随意改名 —— 历史事件依然存的是旧名字,会出现
{{key_event_<旧名>}}取不到值的情况。前端在分类已存在时会弹出橙色警示。
4.2 在 Prompt 中引用事件
通用 Bot Prompt
你是一个资金客服助手。
【该用户最近的资金动作】
{{key_event_withdrawal}}
【该用户最近的投诉】
{{key_event_complaint}}
请基于上述上下文回答用户问题。
Flow Bot 组件 Prompt
- 在画布中点击 LLM 驱动的节点 → 设置面板找到「记忆-关键事件」配置。
- 勾选
enable,选择当前节点要注入的事件类型,设置召回的最近事件数(默认 5)。
4.3 监控提取执行记录
进入路径:开发者控制台 → Bot 详情 → 用户管理 → 执行记录页签
可看到的信息:
- 每次抽取的状态(PENDING / RUNNING / COMPLETED / FAILED)
- 触发来源(REALTIME / SCHEDULED / MANUAL)
- 处理消息数 / 抽取出事件数
- Token 消耗 / 积分消耗
- 失败原因 + 「重试」按钮
- 详情抽屉:列出本次抽取产生 / 修改的所有事件
4.4 账单与计费
关键事件的 LLM 调用费用计入账单的 关键事件 项,与对话主流程的扣费分开统计,方便单独控制成本。
五、典型应用场景
| 场景 | 事件类型示例 | 价值 |
|---|---|---|
| 金融 / 出海支付客服 | withdrawal 取款 / deposit 存款 / kyc 身份认证 / dispute 争议 |
跨会话追踪资金动作;风控与人工坐席接管时一眼看到该用户的历史业务流 |
| 电商售后 | refund 退款 / return 退货 / complaint 投诉 |
自动维护用户售后状态机,多次对话中持续推进 |
| 预约 / 排程 | appointment 预约 / cancellation 取消 |
让 Bot 记得用户改过几次时间、当前确认的是哪个时段 |
| HR / 内部 IT 助手 | leave_request 请假 / it_ticket 工单 |
把对话沉淀为可查询的工单流 |
| 教育 / 学习陪伴 | homework_submission / quiz_score / weak_topic |
长期记录学生学习状态,生成个性化复习计划 |
| 医疗 / 健康咨询 | symptom 症状 / medication 用药 / appointment 复诊 |
跨次咨询保留病程信息,提升问诊连贯性 |
六、最佳实践
✅ 配置层
- 分类字典写描述——
name+description都要填。LLM 主要靠description判断"这条对话是不是这个分类"。description留空会显著降低分类准确率。 - 分类数量控制在 5~10 个。太多会让 LLM 抽取时纠结归类,且 Prompt 体积膨胀。同义类合并("取款" 和 "提现" 用一个)。
- 抽取规则要写"做什么"也要写"不做什么"。例如:只抽取已确认完成或正在进行的业务动作。 排除:假设性讨论("如果...怎么办")、纯情绪、闲聊。
只抽取已确认完成或正在进行的业务动作。 排除:假设性讨论("如果...怎么办")、纯情绪、闲聊。此代码块在浮窗中显示 - 抽取模型选小不选大。抽取是结构化输出任务,对推理能力要求低。GPT-4o-mini / Claude Haiku / DeepSeek-V3 通常够用,单次成本可比主对话模型低一个数量级。
- 不要一开始就把
recentEventCount拉满。注入太多事件会挤占主对话上下文。从 5 起步,发现 Bot 经常"想不起来"再加。
✅ 触发参数
- 长会话 / 客服场景:
messageThreshold = 10、idleTimeoutMinutes = 3一般合适。 - 短交互 / 任务型 Bot:把
idleTimeoutMinutes调到 2,避免用户对话结束后事件抽取迟迟不发生。 - 高频低价值对话(如普通问答):
messageThreshold提到 20~30,避免每轮都跑 LLM 抽取烧成本。
✅ Prompt 引用
- 优先用占位符而不是依赖自动追加。占位符可控、可按类型精确切片,自动追加适合"什么都想要"的兜底场景。
- Flow Bot 中不要每个节点都启用。只在真正需要事件上下文的节点(如客服回复节点、风控判断节点)启用,意图识别 / 路由节点没必要。
- 避免
{{key_event_xxx}}出现在系统级 Prompt 顶部——历史事件往往会随时间增长,如果它撑爆 Token,主指令会被截断。建议放在 Prompt 中段或 Few-shot 之前。
✅ 数据治理
- 定期人工 review LOW 置信度事件。LLM 把"用户单方声称"的内容也会抽出来,业务方应该过滤一遍——可以按
confidence=LOW+disputeFlag=true筛查。 - 重要业务流配合 API 写入。如订单系统已经有结构化数据时,直接
createdBy=API写入比让 LLM 从对话里抽取更准确,与 LLM 抽取结果共存。 - MERGE 操作小心使用。LLM 偶尔会把不该合并的事件合并掉。建议在执行记录里关注 MERGE 类操作,必要时通过 API 拆分恢复。
- 善用
entities字段。在抽取规则里明确要求 LLM 抽出关键 KV(金额、订单号、时间等),后续可直接结构化查询,比解析 summary 文本可靠。
⚠️ 排查清单
| 现象 | 排查路径 |
|---|---|
| 事件没抽出来 | ① 总开关是否开 ② Bot 是否有积分余额 ③ 执行记录是否有 FAILED ④ 提取规则是否过严 |
| 某类型关键事件内容为空 | ① 该用户是否有该类型事件 ② 是否正确创建该类型的事件 ③ 分类名拼写是否一致 |
| 跨用户串扰 | 检查是否传了正确的 userId;如未登录用户,检查 anonymousId 是否稳定 |
| 抽取费用偏高 | ① 降低提取频率② 切换更便宜的提取模型 |
| 抽取结果乱归类 | ① 给分类更准确的描述信息 ② 在提取规则栏里给若干 few-shot 示例 |
七、能力边界与注意事项
- 抽取不是实时的。即使是 Level 1 实时触发,也是在消息累计到阈值后才抽。严格强一致的业务(如交易)不能依赖事件流来下决策——事件是辅助记忆,不是真理之源。
- 抽取窗口最多 100 条消息(
MAX_MESSAGES_PER_BATCH)。超长会话被切成多个窗口处理,跨窗口的因果关系靠"历史事件比对"召回。 - 事件查询有 500ms 超时保护。慢查询不会拖慢聊天链路,但极端情况下会无事件注入。
- 匿名 ID 在端清理后会重置。匿名用户切换设备 / 清缓存后视为新用户,事件不会跟随。
- multi-agent 模式下事件流不参与(参见 oversea-ailab CLAUDE.md 的废弃声明)。
