logo
开发者文档
搜索
关键事件

一、什么是「关键事件」

「关键事件」是 GPTBots 内置的长程业务记忆能力:在用户与 Bot 多轮对话过程中,平台会调用 LLM 自动从对话里识别出有业务价值的事件(如取款、投诉、预约、退款……),按用户维度结构化存储下来,并在后续对话中作为上下文回注给 Bot,让 Bot「记得」这位用户过去发生了什么、当前事件处理到哪一步。

解决的核心问题是:

  • 跨会话记忆缺失:传统 LLM 只能看到当前会话的短期记忆窗口,跨会话或长会话中关键业务动作被遗忘。
  • 结构化数据沉淀:把零散对话沉淀为可查询、可统计、可审计的事件流,供风控、客服质检、用户画像复用。
  • 状态推进追踪:每条事件有 PENDING / IN_PROGRESS / RESOLVED / CLOSED 状态,能够在多次对话间持续推进。
    alt text

二、设计理念

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 │ │ 或在执行记录页点"重试" │ └──────────────────────────────────────┘
                      
                      ┌── 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
                      
                      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
                      
                      ## 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}} ← 仅注入"投诉"类事件
                      
                      {{key_event_withdrawal}}     ← 仅注入"取款"类事件
{{key_event_complaint}}      ← 仅注入"投诉"类事件

                    
此代码块在浮窗中显示

平台按 eventType 分组生成对应变量,未匹配的事件类型不会被注入。最多注入 30 条 / 变量。

Flow Bot:组件级独立控制

工作流的每个 LLM 节点都有自己的 FlowKeyEventConfig:可单独开关、单独选事件类型、单独设 recentEventCount(默认 5 条)。同一会话内事件只查一次(缓存在 ChatContext),多个节点共享数据。


四、使用指南

4.1 在 Bot 配置面板启用

进入路径:开发者控制台 → Bot 详情 → 用户管理(User Manage) → 「关键事件」抽屉

操作步骤:

  1. 打开总开关
  2. 提取模型:可选项;不选时使用 Bot 默认对话模型。建议选一个便宜模型(例如 GPT-4o-mini / Haiku 系列)。
  3. 提取规则(≤2000 字符):用自然语言告诉 LLM 这个 Bot 场景下"什么算关键事件"。例:
    只抽取与资金相关的业务动作(取款、存款、转账、退款)。 忽略闲聊和情绪表达。 实体字段必须包含金额(amount)、币种(currency)、订单号(orderId)。
                          
                          只抽取与资金相关的业务动作(取款、存款、转账、退款)。
    忽略闲聊和情绪表达。
    实体字段必须包含金额(amount)、币种(currency)、订单号(orderId)。
    
                        
    此代码块在浮窗中显示
  4. 最近事件数(050):每次对话注入 Prompt 的事件数。0 表示不注入,常见 510。
  5. 触发时机
    • 消息阈值(5~50):默认 10。
    • 空闲超时(2~60 分钟):默认 3。
  6. 事件分类字典(最多 10 个):每个分类含「名称」(≤10 字)和「描述」(≤100 字,告诉 LLM 这个分类装什么)。
  7. 点保存。

⚠️ 警告:分类名一旦上线就不要随意改名 —— 历史事件依然存的是旧名字,会出现 {{key_event_<旧名>}} 取不到值的情况。前端在分类已存在时会弹出橙色警示。

4.2 在 Prompt 中引用事件

通用 Bot Prompt

你是一个资金客服助手。 【该用户最近的资金动作】 {{key_event_withdrawal}} 【该用户最近的投诉】 {{key_event_complaint}} 请基于上述上下文回答用户问题。
                      
                      你是一个资金客服助手。

【该用户最近的资金动作】
{{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 复诊 跨次咨询保留病程信息,提升问诊连贯性

六、最佳实践

✅ 配置层

  1. 分类字典写描述——name + description 都要填。LLM 主要靠 description 判断"这条对话是不是这个分类"。description 留空会显著降低分类准确率。
  2. 分类数量控制在 5~10 个。太多会让 LLM 抽取时纠结归类,且 Prompt 体积膨胀。同义类合并("取款" 和 "提现" 用一个)。
  3. 抽取规则要写"做什么"也要写"不做什么"。例如:
    只抽取已确认完成或正在进行的业务动作。 排除:假设性讨论("如果...怎么办")、纯情绪、闲聊。
                          
                          只抽取已确认完成或正在进行的业务动作。
    排除:假设性讨论("如果...怎么办")、纯情绪、闲聊。
    
                        
    此代码块在浮窗中显示
  4. 抽取模型选小不选大。抽取是结构化输出任务,对推理能力要求低。GPT-4o-mini / Claude Haiku / DeepSeek-V3 通常够用,单次成本可比主对话模型低一个数量级。
  5. 不要一开始就把 recentEventCount 拉满。注入太多事件会挤占主对话上下文。从 5 起步,发现 Bot 经常"想不起来"再加。

✅ 触发参数

  1. 长会话 / 客服场景messageThreshold = 10idleTimeoutMinutes = 3 一般合适。
  2. 短交互 / 任务型 Bot:把 idleTimeoutMinutes 调到 2,避免用户对话结束后事件抽取迟迟不发生。
  3. 高频低价值对话(如普通问答)messageThreshold 提到 20~30,避免每轮都跑 LLM 抽取烧成本。

✅ Prompt 引用

  1. 优先用占位符而不是依赖自动追加。占位符可控、可按类型精确切片,自动追加适合"什么都想要"的兜底场景。
  2. Flow Bot 中不要每个节点都启用。只在真正需要事件上下文的节点(如客服回复节点、风控判断节点)启用,意图识别 / 路由节点没必要。
  3. 避免 {{key_event_xxx}} 出现在系统级 Prompt 顶部——历史事件往往会随时间增长,如果它撑爆 Token,主指令会被截断。建议放在 Prompt 中段或 Few-shot 之前。

✅ 数据治理

  1. 定期人工 review LOW 置信度事件。LLM 把"用户单方声称"的内容也会抽出来,业务方应该过滤一遍——可以按 confidence=LOW + disputeFlag=true 筛查。
  2. 重要业务流配合 API 写入。如订单系统已经有结构化数据时,直接 createdBy=API 写入比让 LLM 从对话里抽取更准确,与 LLM 抽取结果共存。
  3. MERGE 操作小心使用。LLM 偶尔会把不该合并的事件合并掉。建议在执行记录里关注 MERGE 类操作,必要时通过 API 拆分恢复。
  4. 善用 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 的废弃声明)。