譯者 | 李睿
審校 | 重樓
為了普及知識和推廣技術(shù),人工智能專家、本文作者Sean Falconer主持著兩個播客——《Software Engineering Daily》和《Software Huddle》,并經(jīng)常以嘉賓身份亮相于其他節(jié)目。無論是主持播客還是作為嘉賓,他推廣的節(jié)目都有助于凸顯其精彩的對話內(nèi)容。然而對他來說,在緊張的工作和生活中騰出時間為每期節(jié)目撰寫一篇具有思想深度的LinkedIn帖子是一項挑戰(zhàn),并非每次都能實現(xiàn)。
為了讓工作和生活更輕松,同時也讓每一期節(jié)目更精彩,他構(gòu)建了一個人工智能驅(qū)動的LinkedIn帖子生成器。該生成器可以下載播客片段,將音頻轉(zhuǎn)換為文本,并用它來創(chuàng)建帖子。這一工具節(jié)省了更多的時間,保持了內(nèi)容的一致性,并確保每一期節(jié)目都獲得應(yīng)有的關(guān)注。
Falconer在本文中將詳細介紹他如何使用Next.js、OpenAI的GPT和Whisper模型、Apache Kafka和Apache Flink構(gòu)建這個工具。更重要的是,將展示Kafka和Flink如何支持事件驅(qū)動的架構(gòu),使其具有可擴展性和響應(yīng)性——這是實時人工智能應(yīng)用的關(guān)鍵模式。
注:如果只想看代碼,可以在這里查看GitHub repo。
設(shè)計LinkedIn帖子助手
設(shè)計這個應(yīng)用程序的目標非常明確:在不占用Falconer太多時間的情況下,幫助他主持的播客或作為嘉賓參與的播客節(jié)目創(chuàng)建LinkedIn帖子。
為了滿足需求,F(xiàn)alconer希望能夠為播客提要提供一個URL,可以獲取所有播客節(jié)目的詳盡列表,然后為選擇的任何一期節(jié)目生成一個LinkedIn帖子。這很簡單,是吧?當(dāng)然,要讓這一切正常工作,還有一些繁重的工作要做:
- 下載所選播客節(jié)目的MP3文件。
- 使用OpenAI的Whisper模型將音頻轉(zhuǎn)換為文本。
- 由于Whisper有25MB的文件大小限制,如果需要,可以將MP3文件分成更小的塊。
- 最后,使用轉(zhuǎn)錄文本組裝一個提示,并采用LLM生成LinkedIn帖子。
除了這些功能之外,還有另一個重要的目標:保持前端應(yīng)用程序與人工智能工作流程完全解耦。為什么?因為在現(xiàn)實世界的人工智能應(yīng)用程序中,開發(fā)團隊通常會處理堆棧的不同部分。前端開發(fā)人員不需要了解任何關(guān)于人工智能的知識來構(gòu)建面向用戶的應(yīng)用程序。此外,還希望具備以下靈活性:
- 獨立擴展系統(tǒng)的不同部分。
- 隨著不斷增長的生成式人工智能堆棧的發(fā)展,能夠更換模型或框架。
為了實現(xiàn)這些目標使用Confluent Cloud實現(xiàn)了一個事件驅(qū)動的架構(gòu)。這種方法不僅使架構(gòu)保持模塊化,而且隨著人工智能技術(shù)不可避免地發(fā)生變化,為面向未來的應(yīng)用程序奠定了基礎(chǔ)。
人工智能為什么采用事件驅(qū)動架構(gòu)?
事件驅(qū)動架構(gòu)(EDA)的出現(xiàn)是對依賴于嚴格的同步通信模式的傳統(tǒng)單體系統(tǒng)局限性的回應(yīng)。在計算的早期,應(yīng)用程序是圍繞靜態(tài)工作流構(gòu)建的,通常與批處理或緊密耦合的交互聯(lián)系在一起。
單體服務(wù)器的架構(gòu)
隨著技術(shù)的發(fā)展和對可擴展性和適應(yīng)性的需求的增長,特別是隨著分布式系統(tǒng)和微服務(wù)的興起,EDA成為一種自然的解決方案。
通過將事件(例如狀態(tài)更改、用戶操作或系統(tǒng)觸發(fā)器)作為交互的核心單元,EDA使系統(tǒng)能夠解耦組件并進行異步通信。
這種方法使用數(shù)據(jù)流,生產(chǎn)者和消費者通過共享的、不可變的日志進行交互。事件以有保證的順序持久化,允許系統(tǒng)動態(tài)地、獨立地處理和響應(yīng)更改。
事件生產(chǎn)者和消費者的高級概述
將My Web App與人工智能工作流解耦
回到當(dāng)前的任務(wù),My Web App不需要了解任何關(guān)于人工智能的知識。
為了將面向用戶的應(yīng)用程序與人工智能工作流解耦,F(xiàn)alconer使用了Confluent Cloud的數(shù)據(jù)流平臺,該平臺支持Kafka、Flink和人工智能模型視為核心組件,使構(gòu)建真正可擴展的人工智能應(yīng)用程序變得容易。
當(dāng)用戶點擊播客列表時,該應(yīng)用程序會要求服務(wù)器檢查后端緩存中是否存在LinkedIn帖子。如果找到,它將返回并顯示該帖子。
雖然可以將這些LinkedIn帖子存儲在數(shù)據(jù)庫中,但Falconer選擇了臨時緩存,因為實際上不需要長時間保存這些帖子。
如果沒有LinkedIn帖子,后端將事件寫入Kafka主題,包括MP3的URL和節(jié)目描述。這將觸發(fā)生成LinkedIn帖子的工作流。
下圖展示了這個事件驅(qū)動系統(tǒng)的完整架構(gòu),下一節(jié)中將對此進行更詳細的解釋。
人工智能生成的LinkedIn帖子的事件驅(qū)動工作流
下載和生成轉(zhuǎn)錄文本
工作流程的這一部分相當(dāng)簡單。Web應(yīng)用程序請求寫入名為LinkedIn Post Request的Kafka主題。使用Confluent Cloud,配置了一個HTTP Sink Connector來將新消息轉(zhuǎn)發(fā)到API端點。
API端點使用提供的URL下載MP3文件,如果有必要,可以將MP3文件分割成25MB的塊,并使用Whisper處理音頻以生成轉(zhuǎn)錄文本。在轉(zhuǎn)錄完成之后,它們被寫入另一個名為“播客轉(zhuǎn)錄文本”的Kafka主題。
這就是工作流變得有趣的地方——流處理開始處理繁重的工作。
生成LinkedIn帖子
Apache Flink是一個開源流處理框架,旨在實時處理大量數(shù)據(jù)。它擅長于高吞吐量,低延遲的場景,使其非常適合實時人工智能應(yīng)用程序。如果用戶對數(shù)據(jù)庫有所了解,可以將Flink SQL視為類似于標準SQL,但不同的是,F(xiàn)link SQL查詢的是數(shù)據(jù)流而非數(shù)據(jù)庫表。
為了使用Flink將播客內(nèi)容轉(zhuǎn)化為LinkedIn帖子,需要整合一個外部LLM。Flink SQL允許為廣泛使用的LLM定義模型,從而簡化了這一過程??梢灾付ㄈ蝿?wù)(例如,text_generation)并提供一個系統(tǒng)提示符來指導(dǎo)輸出,如下所示:
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 );
為了創(chuàng)建LinkedIn帖子,首先根據(jù)MP3的URL加入了LinkedIn帖子請求主題和播客轉(zhuǎn)錄主題,將播客節(jié)目描述和轉(zhuǎn)錄組合成一個提示值,并存儲在視圖中。使用視圖可以提高可讀性和可維護性;雖然可以直接在ml_predict調(diào)用中嵌入字符串連接,但這樣做會使工作流更難修改。
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語句通過將提示傳遞給之前設(shè)置的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帖子發(fā)送到API端點。該端點將數(shù)據(jù)寫入后端緩存。
一旦寫入緩存,LinkedIn帖子就可以供前端應(yīng)用程序使用,一旦準備就緒,前端應(yīng)用程序就會自動顯示結(jié)果。
關(guān)鍵要點
構(gòu)建人工智能驅(qū)動的LinkedIn帖子生成器,不僅僅是節(jié)省時間的一種方法,更是設(shè)計一個現(xiàn)代的、可擴展的、解耦的事件驅(qū)動系統(tǒng)的實踐。
與任何軟件項目一樣,預(yù)先選擇合適的架構(gòu)至關(guān)重要。生成式人工智能領(lǐng)域正在迅速發(fā)展,新的模型、框架和工具不斷涌現(xiàn)。通過解耦組件并采用事件驅(qū)動的設(shè)計,可以使系統(tǒng)面向未來,從而更容易采用新技術(shù),而無需徹底重建整個技術(shù)棧。
解耦工作流,采用事件驅(qū)動的系統(tǒng),并確保架構(gòu)能夠無縫擴展和適應(yīng)。無論是構(gòu)建LinkedIn帖子生成器,還是處理更復(fù)雜的人工智能用例,這些原則都是通用的。
如果對這個項目感興趣,可以在GitHub上探索代碼,或在LinkedIn進一步討論。
原文標題:Automating Podcast Promotion With AI and Event-Driven Design,作者:Sean Falconer