事件驱动的 ASR → LLM → ES 回写:设计拆解与实践
引言
最近在学习一个语音分析系统的架构设计,整个流程是:录音转成文字,然后用大模型做智能分析,最后把结果存到 Elasticsearch 里供检索。流程看起来简单,但真正落地时会遇到不少问题:
- ASR 识别结果和业务规则耦合在一起,改个过滤逻辑就要动识别器代码
- LLM 调用散落在各处,模型切换时改得头昏脑涨
- ES 写入失败后没有补偿机制,数据就丢了
- 各个阶段的状态流转混乱,出问题时很难排查
这篇文章记录一下这个系统是怎么通过事件驱动的方式解决这些问题的,重点分析设计思想和实现模式。
核心设计思路
整个系统可以抽象成三个核心阶段:语音识别 → 智能分析 → 索引回写。关键是要让每个阶段职责清晰、互不干扰,同时保证数据最终一致性。
系统采用了事件驱动架构,把每个阶段的完成作为事件发布出去,由专门的处理器负责后续工作。这样做的好处是:
- 各阶段可以独立演进,识别器换了不影响分析逻辑
- 某个阶段失败不会阻塞其他阶段
- 每个阶段都有明确的输入输出,便于测试和排查
- 失败了可以重试,不会丢数据
模块职责划分
先梳理一下各个模块的职责,这样后面看代码会更清晰。
ASR Gateway:负责把音频提交到识别引擎,获取转写结果。这一层只管识别,不管业务逻辑。
ASR Post-Processor:对转写结果做业务相关的处理,比如过滤自动播报、去除噪声标签、敏感词替换等。这是一个扩展点,不同场景可以插不同的实现。
Analysis Orchestrator:编排整个分析流程,协调摘要生成、场景识别、质检、字段提取等多个分析任务,维护分析过程中的上下文信息。
LLM Client:统一封装大模型调用,管理模型选择、参数配置、返回解析等,让业务代码不用关心调用细节。
ES Document Indexer:提供统一的 ES 索引操作接口,处理写入失败的情况。
Fail Record + Retry Job:记录 ES 同步失败的事件,定期扫描并重试。
Event Framework:事件发布订阅机制,把阶段性事件分发给各个处理器。
三个核心环节的设计要点
整个链路可以拆成三个相对独立的环节,每个环节都有值得深入思考的设计点。
ASR 识别与结果清洗
第一个要解决的问题是:识别和业务规则混在一起。早期代码里,识别器里直接写了各种过滤逻辑,结果换个场景就要改识别器,改着改着就乱了。
更合理的做法是把流程拆成两段:识别阶段只负责把音频转成文字,后处理阶段负责把文本整理成后续分析能直接消费的形式。后处理设计成扩展点,不同场景注册不同的实现。
关于这部分的详细设计,可以看 从 ASR 结果到可用输入:清洗、归一化与扩展点设计。
LLM 调用与分析编排
LLM 调用看起来简单,但工程化时会遇到一堆问题:模型要切换、不同场景用不同的参数、返回结果格式不稳定、超时和重试策略要统一管理。
更重要的是,当一个系统里需要同时完成摘要、场景识别、质检分析和字段提取时,流程怎么组织就成了关键问题。如果把所有分析都塞进一次调用里,输出很难稳定,失败后也难以重试局部步骤。
更好的做法是把任务拆成多个阶段,用事件把每一段结果串起来。关于这部分的详细设计,可以看 把 LLM 放进分析链路:阶段编排与事件触发。
ES 回写与失败补偿
ES 写入失败是常态:网络抖动、集群拒绝写入、数据格式问题都可能导致失败。关键是要有补偿机制。
一个比较稳妥的方式是:先把状态改成”正在分析”,再逐步写入各阶段结果,全部完成后再进入结束态。同时,失败时要记录足够多的上下文,后续不管是人工排查还是自动补偿,都有依据。
关于这部分的详细设计,可以看 分析结果如何可靠回写 ES:状态流转与失败补偿。
状态流转与可观测性
状态设计
常见的状态流转:
1 | TO_ANALYZE → ANALYZING → ANALYZED |
状态要同时写在 ES 和主 DB,两端保持一致。更新时用乐观锁或幂等操作,避免并发问题。
可观测性
每个阶段的开始、结束、异常都要记录日志和指标。一般用 Prometheus,主要指标:
- 各阶段耗时分布
- 成功率和失败率
- ES 写入延迟
- 补偿任务执行情况
日志要带 traceId,方便链路追踪。
遇到的问题与解决
| 问题 | 解决方案 |
|---|---|
| LLM 输出格式不稳定 | 强制输出 JSON,用 schema 校验 |
| 事件乱序导致数据覆盖 | 在事件中携带时间戳,回写时比较版本 |
| ES 写入高并发压垮集群 | 使用 Bulk API,加限速机制 |
| 补偿任务重试风暴 | 分批执行,加速率限制 |
| 状态不一致 | ES 和 DB 双写,用补偿任务兜底 |
总结
把 ASR、LLM 和 ES 的处理解耦并以事件为边界来组织,可以获得清晰的职责分离、较好的可扩展性和可靠的补偿能力。实践中要重点关注:
- 数据格式与校验:LLM 输出要结构化,关键字段要校验
- 事件顺序与幂等:处理并发和乱序问题
- 失败补偿:记录失败、定期重试、速率控制
- 链路可观测:traceId 贯穿全链路,指标覆盖关键节点
这套架构的设计思想可以应用到很多类似的场景:多阶段数据处理、外部服务调用编排、异步结果回写等。理解了事件驱动的设计模式,再遇到类似问题时就能有清晰的解决思路。

