译者 | 李睿
审校 | 重楼
为了普及知识和推广技术,人工智能专家、本文作者Sean Falconer主持着两个播客——《Software Engineering Daily》和《Software Huddle》,并经常以嘉宾身份亮相于其他节目。无论是主持播客还是作为嘉宾,他推广的节目都有助于凸显其精彩的对话内容。然而对他来说,在紧张的工作和生活中腾出时间为每期节目撰写一篇具有思想深度的LinkedIn帖子是一项挑战,并非每次都能实现。
为了让工作和生活更轻松,同时也让每一期节目更精彩,他构建了一个人工智能驱动的LinkedIn帖子生成器。该生成器可以下载播客片段,将音频转换为文本,并用它来创建帖子。这一工具节省了更多的时间,保持了内容的一致性,并确保每一期节目都获得应有的关注。
Falconer在本文中将详细介绍他如何使用Next.js、OpenAI的GPT和Whisper模型、Apache Kafka和Apache Flink构建这个工具。更重要的是,将展示Kafka和Flink如何支持事件驱动的架构,使其具有可扩展性和响应性——这是实时人工智能应用的关键模式。
注:如果只想看代码,可以在这里查看GitHub repo。
设计LinkedIn帖子助手
设计这个应用程序的目标非常明确:在不占用Falconer太多时间的情况下,帮助他主持的播客或作为嘉宾参与的播客节目创建LinkedIn帖子。
为了满足需求,Falconer希望能够为播客提要提供一个URL,可以获取所有播客节目的详尽列表,然后为选择的任何一期节目生成一个LinkedIn帖子。这很简单,是吧?当然,要让这一切正常工作,还有一些繁重的工作要做:
- 下载所选播客节目的MP3文件。
- 使用OpenAI的Whisper模型将音频转换为文本。
- 由于Whisper有25MB的文件大小限制,如果需要,可以将MP3文件分成更小的块。
- 最后,使用转录文本组装一个提示,并采用LLM生成LinkedIn帖子。
除了这些功能之外,还有另一个重要的目标:保持前端应用程序与人工智能工作流程完全解耦。为什么?因为在现实世界的人工智能应用程序中,开发团队通常会处理堆栈的不同部分。前端开发人员不需要了解任何关于人工智能的知识来构建面向用户的应用程序。此外,还希望具备以下灵活性:
- 独立扩展系统的不同部分。
- 随着不断增长的生成式人工智能堆栈的发展,能够更换模型或框架。
为了实现这些目标使用Confluent Cloud实现了一个事件驱动的架构。这种方法不仅使架构保持模块化,而且随着人工智能技术不可避免地发生变化,为面向未来的应用程序奠定了基础。
人工智能为什么采用事件驱动架构?
事件驱动架构(EDA)的出现是对依赖于严格的同步通信模式的传统单体系统局限性的回应。在计算的早期,应用程序是围绕静态工作流构建的,通常与批处理或紧密耦合的交互联系在一起。
单体服务器的架构
随着技术的发展和对可扩展性和适应性的需求的增长,特别是随着分布式系统和微服务的兴起,EDA成为一种自然的解决方案。
通过将事件(例如状态更改、用户操作或系统触发器)作为交互的核心单元,EDA使系统能够解耦组件并进行异步通信。
这种方法使用数据流,生产者和消费者通过共享的、不可变的日志进行交互。事件以有保证的顺序持久化,允许系统动态地、独立地处理和响应更改。
事件生产者和消费者的高级概述
将My Web App与人工智能工作流解耦
回到当前的任务,My Web App不需要了解任何关于人工智能的知识。
为了将面向用户的应用程序与人工智能工作流解耦,Falconer使用了Confluent Cloud的数据流平台,该平台支持Kafka、Flink和人工智能模型视为核心组件,使构建真正可扩展的人工智能应用程序变得容易。
当用户点击播客列表时,该应用程序会要求服务器检查后端缓存中是否存在LinkedIn帖子。如果找到,它将返回并显示该帖子。
虽然可以将这些LinkedIn帖子存储在数据库中,但Falconer选择了临时缓存,因为实际上不需要长时间保存这些帖子。
如果没有LinkedIn帖子,后端将事件写入Kafka主题,包括MP3的URL和节目描述。这将触发生成LinkedIn帖子的工作流。
下图展示了这个事件驱动系统的完整架构,下一节中将对此进行更详细的解释。
人工智能生成的LinkedIn帖子的事件驱动工作流
下载和生成转录文本
工作流程的这一部分相当简单。Web应用程序请求写入名为LinkedIn Post Request的Kafka主题。使用Confluent Cloud,配置了一个HTTP Sink Connector来将新消息转发到API端点。
API端点使用提供的URL下载MP3文件,如果有必要,可以将MP3文件分割成25MB的块,并使用Whisper处理音频以生成转录文本。在转录完成之后,它们被写入另一个名为“播客转录文本”的Kafka主题。
这就是工作流变得有趣的地方——流处理开始处理繁重的工作。
生成LinkedIn帖子
Apache Flink是一个开源流处理框架,旨在实时处理大量数据。它擅长于高吞吐量,低延迟的场景,使其非常适合实时人工智能应用程序。如果用户对数据库有所了解,可以将Flink SQL视为类似于标准SQL,但不同的是,Flink SQL查询的是数据流而非数据库表。
为了使用Flink将播客内容转化为LinkedIn帖子,需要整合一个外部LLM。Flink SQL允许为广泛使用的LLM定义模型,从而简化了这一过程。可以指定任务(例如,text_generation)并提供一个系统提示符来指导输出,如下所示:
复制1 CREATE MODEL `linkedin_post_generation` 2 INPUT (text STRING) 3 OUTPUT (response STRING) 4 WITH ( 5 'openai.connection'='openai-connection', 6 'provider'='openai', 7 'task'='text_generation', 8 'openai.model_version' = 'gpt-4', 9 'openai.system_prompt' = 'You are an expert in AI, databases, and data engineering. 10 You need to write a LinkedIn post based on the following podcast transcription and description. 11 The post should summarize the key points, be concise, direct, free of jargon, but thought-provoking. 12 The post should demonstrate a deep understanding of the material, adding your own takes on the material. 13 Speak plainly and avoid language that might feel like a marketing person wrote it. 14 Avoid words like "delve", "thought-provoking". 15 Make sure to mention the guest by name and the company they work for. 16 Keep the tone professional and engaging, and tailor the post to a technical audience. Use emojis sparingly.' 17 );
为了创建LinkedIn帖子,首先根据MP3的URL加入了LinkedIn帖子请求主题和播客转录主题,将播客节目描述和转录组合成一个提示值,并存储在视图中。使用视图可以提高可读性和可维护性;虽然可以直接在ml_predict调用中嵌入字符串连接,但这样做会使工作流更难修改。
复制1 CREATE VIEW podcast_prompt AS 2 SELECT 3 mp3.key AS key, 4 mp3.mp3Url AS mp3Url, 5 CONCAT( 6 'Generate a concise LinkedIn post that highlights the main points of the podcast while mentioning the guest and their company.', 7 CHR(13), CHR(13), 8 'Podcast Description:', CHR(13), 9 rqst.episodeDescription, CHR(13), CHR(13), 10 'Podcast Transcript:', CHR(13), 11 mp3.transcriptionText 12 ) AS prompt 13 FROM 14 `linkedin-podcast-mp3` AS mp3 15 JOIN 16 `linkedin-generation-request` AS rqst 17 ON 18 mp3.mp3Url = rqst.mp3Url 19 WHERE 20 mp3.transcriptionText IS NOT NULL;
在视图中准备好提示之后,使用另一个Flink SQL语句通过将提示传递给之前设置的LLM模型来生成LinkedIn帖子。完成的帖子然后被写入新的Kafka主题——“已完成的LinkedIn帖子”。这种方法简化了流程,同时保持了工作流的可扩展性和灵活性。
复制1 INSERT INTO `linkedin-request-complete` 2 SELECT 3 podcast.key, 4 podcast.mp3Url, 5 prediction.response 6 FROM 7 `podcast_prompt` AS podcast 8 CROSS JOIN 9 LATERAL TABLE ( 10 ml_predict( 11 'linkedin_post_generation', 12 podcast.prompt 13 ) 14 ) AS prediction;
将帖子写入缓存
最后一步是在Confluent Cloud中配置另一个HTTP Sink Connector,将完成的LinkedIn帖子发送到API端点。该端点将数据写入后端缓存。
一旦写入缓存,LinkedIn帖子就可以供前端应用程序使用,一旦准备就绪,前端应用程序就会自动显示结果。
关键要点
构建人工智能驱动的LinkedIn帖子生成器,不仅仅是节省时间的一种方法,更是设计一个现代的、可扩展的、解耦的事件驱动系统的实践。
与任何软件项目一样,预先选择合适的架构至关重要。生成式人工智能领域正在迅速发展,新的模型、框架和工具不断涌现。通过解耦组件并采用事件驱动的设计,可以使系统面向未来,从而更容易采用新技术,而无需彻底重建整个技术栈。
解耦工作流,采用事件驱动的系统,并确保架构能够无缝扩展和适应。无论是构建LinkedIn帖子生成器,还是处理更复杂的人工智能用例,这些原则都是通用的。
如果对这个项目感兴趣,可以在GitHub上探索代码,或在LinkedIn进一步讨论。
原文标题:Automating Podcast Promotion With AI and Event-Driven Design,作者:Sean Falconer