当用户只想查看失败案例时,如何快速索引异常子轨迹?

解读

在 Agent 系统上线后,日均轨迹量可达百万级,其中失败案例往往不足 5%,却占用 80% 的排障时间。面试官想确认两点:

  1. 你能否在毫秒级从海量轨迹中精准捞出异常子轨迹
  2. 你能否把“失败”定义得可度量、可追踪、可复现,而不是简单抛异常。
    国内大厂现场常要求候选人先给出指标定义 → 再谈索引方案 → 最后落地到工程化工具链,缺一即视为思路不完整。

知识点

  1. 失败语义建模:业务失败(任务未达成)、系统失败(异常退出)、安全失败(违规调用)。
  2. 子轨迹切分:以“回合(Turn)”或“工具调用”为最小粒度,用**有向无环图(DAG)**存储,节点挂“成功位”。
  3. 多级索引
    • 一级倒排:Elasticsearch 只存“失败=1”的轨迹 ID,用布隆过滤器预筛,把磁盘随机读变成顺序读。
    • 二级位图:在 ClickHouse 建RoaringBitmap,把子轨迹 ID 映射到 32 位整数,压缩后单分区 <200 MB,单核可秒级扫描千万级子轨迹
  4. 特征签名:对异常栈、观测差异、奖励值进行SimHash,64 位指纹直接当主键,相同根因去重率 >90%
  5. 在线链路:Agent 框架埋点 → Kafka → Flink CEP 实时打标签 → Redis Set 缓存最近 30 分钟失败子轨迹 ID,接口 P99 <30 ms
  6. 合规要求:国内《生成式 AI 管理办法》要求关键异常留痕 6 个月,因此索引必须支持按天级分区、自动冷存至 OSS,并带可解释字段(Prompt 快照、工具返回、思考链)。

答案

回答采用“三步法”,总时长控制在 2 分钟内,先给结论再给数据,最后留扩展钩子。

第一步:定义失败
“我会把失败拆成三类标签:①业务失败——用户目标未达成,用奖励函数 < 阈值判定;②系统失败——代码抛未捕获异常;③安全失败——触发内容安全或权限拒绝。三类标签在轨迹节点以 2 bit 存储,查询时可任意组合。”

第二步:建多级索引
“线上采用 ES+CK 双栈:

  1. ES 只存‘含失败’的轨迹元数据,每日索引按‘场景+日期’滚动,字段压缩 + 布隆过滤器后,单分片体积 <20 GB,单节点可扛 2 万 QPS
  2. 子轨迹明细进 ClickHouse,本地表按(失败类型, 日期, 小时)分区,物化视图预聚合 SimHash,查询时直接 WHERE bitmapContains(fail_bitmap, sub_trace_id),95 百分位响应 80 ms
  3. 最近 30 分钟热数据写 Redis RoaringBitmap,接口层命中内存即可返回,P99 <30 ms。”

第三步:工程闭环
“整个流程通过 Flink CEP 实时打标签,异常发生 5 秒内即可被索引;同时提供‘失败根因聚类’页面,研发可一键拉取相同 SimHash 的全部子轨迹,排障效率提升 6 倍。最后按《生成式 AI 管理办法》把原始 Prompt、工具返回、思考链自动转储到 OSS,保存 180 天并加密,满足审计。”

拓展思考

  1. 如果失败案例不足千条,仍用 CK 位图会造成稀疏浪费,可切换到SQLite+内存映射,单文件 <100 MB,嵌入式部署到边缘节点。
  2. 当 Agent 引入多模态动作(如调用机械臂)时,子轨迹需挂视频指纹,可用感知哈希 + Annoy 索引,把高维向量降到 64 位再入位图,保持毫秒级检索
  3. 未来做强化学习微调,需要负样本采样权重,可在 CK 里加一列 weight,按失败严重程度动态调整,避免模型只学“简单负例”。