"大模型为开发者带来了更多提高效率的潜力,但受限于大模型的性能与吞吐,大模型往往适合被用于较低频率与较少吞吐量的 toB 或者 toD 场景中:例如咨询或者摘要。最近我尝试结合大模型最擅长的两种能力:模式匹配与代码生成,开发了一种新的文本日志结构化工具:ethe/bakalog,它展示了大模型在处理更大规模数据的潜力:智能且敏捷地处理数据,将临时代码编写效率提升十倍。"
将大模型应用于数据处理的尝试
我是一位字节跳动的后端开发,最近我发现不少同事已经在日常工作中使用 ChatGPT :他们通常会使用 ChatGPT 来总结文档并得到摘要、设计、注释和生成基本的代码。在观察了一段时间同事对 ChatGPT 的使用后,我尝试更深入地结合大模型与开发者的工作,而不只是辅助编写代码。我发现大模型(尤其是 GPT-4)在模式总结与提取、代码生成方面有惊艳的表现------ChatGPT 非常擅于在一些的文本中提取公共与差异内容,同时针对具有良好定义且功能明确的语言、能输出较高质量的代码。将大模型成功切入工作流的同事们都善用了大模型这两种能力。
因此我想到了能结合两种能力最佳应用场景:日志聚类与结构化。日志通常以文本形式输出,开发者可以基于文本很好地搜索过滤日志,但难以产生产生有价值的见解:每个用户在每个页面平均停留多长时间?故障前页面响应时间分布是否发生变化?这些都依赖将日志分类并结构化以处理计算。
后端开发者通常通过预处理让日志易于结构化:预先定义日志 header 等,这可以通过使用统一的 logger SDK 实现,但这不能解决全部问题:
-
大量不同的进程会占用同一个信道输出日志:stdout、stderr 或者是输出到同一个日志文件中;
-
即使是同一个进程、并使用统一的 logger 输出的日志,日志 content 中依然会存在不同的结构,例如:
swift081109 203615 148 INFO dfs.DataNode$PacketResponder: PacketResponder 1 for block blk_38865049064139660 terminating 081109 204005 35 INFO dfs.FSNamesystem: BLOCK* NameSystem.addStoredBlock: blockMap updated: 10.251.73.220:50010 is added to blk_7128370237687728475 size 67108864 081109 204106 329 INFO dfs.DataNode$PacketResponder: PacketResponder 2 for block blk_-6670958622368987959 terminating 081109 204132 26 INFO dfs.FSNamesystem: BLOCK* NameSystem.addStoredBlock: blockMap updated: 10.251.43.115:50010 is added to blk_3050920587428079149 size 67108864
- 它们其实是两类不同的日志:
markdown081109 203615 <*> INFO dfs.DataNode$PacketResponder: PacketResponder <*> for block <*> terminating 081109 204005 <*> INFO dfs.FSNamesystem: BLOCK* NameSystem.addStoredBlock: blockMap updated: <*> is added to <*> size <*>
每一个开发者或多或少都会使用 grep、awk 与 sed 快速构建一个临时的分析平台,尽管它们在高手手里完全能实现强大的数据分析效果,但写复杂的正则表达式绝对让人头秃。而大模型只需要非常简单的 prompt,就能非常好地分类并抽取日志变量:
"慢思考"的大模型难以在线处理数据
但大模型作为一种较慢的"思考",非常不适合直接用于处理流程中。因此我采用了一种间接的模式:依然使用正则表达式匹配并抽取日志,但通过 Chat-GPT 生成每类日志的正则。大模型通过代码生成能力"离线"作用于实时数据处理流程,以兼顾智能化与处理效率。
在大模型生成每类日志正则之前,我们还需要聚类日志。考虑到大模型会话上下文长度的限制(GPT-4 只支持 4k )与较低地处理效率,使用大模型分类全部日志文本难以工作。但 text embedding 模型将句子(例如日志)转换为固定长度的句子向量,所以我们可以简单基于余弦相似性来比较日志与日志之间的相似度,进而将日志文本转化为不同的日志群,基于 text embedding 的日志分类能保持理想的速度与良好的日志聚类能力:
scss
┌───────────┐
│ Log Files │
└─┬─────────┘
│
┌─▼────────────┐ ┌──────────────────────┐
│ Regex Triage │─────► Text Embedding Model │
└─┬─────────▲──┘ └──────────────────────┘
│ + +
│ + +
│ + ┌─▼─────┐
│ + + + + │ GPT-4 │
│ └───────┘
│
┌─▼───────────────┐
│ Structured Logs │
└─────────────────┘
────> log flow
+ + > pattern flow
所以最终我选择仅向 GPT-4 提供,由 text embedding 模型聚类后的日志群样本。让 GPT-4 归纳样本的模式并提取生成正则表达式,并将正则用于直接匹配日志。只有当日志不属于任意正则表达式时,再进行进一步的日志聚类并总结正则。这样的好处是只要经过一段冷启动时间,绝大多数日志都会被正则匹配并抽取而不再依赖 GPT-4。
总结
这只是一个小工具的 PoC,但我认为这是一种将大模型应用在更大规模数据处理的可行路径:大模型连续地离线观测与分析数据,再通过代码生成的方式嵌入与优化在线流程(例如 Regex),并不断循环迭代这个过程。
markdown
┌──────────┐
│ 大模型 │
└─┬──────▲─┘
│ │
代码生成 │ │ 观测
│ │
│ │
────┐ ┌─▼──────┴─┐ ┌───
│───►│ 数据处理 │──►│
────┘ └──────────┘ └───