Unit Mesh:一種適合于 AI 編程時(shí)代的軟件架構(gòu)
Unit Mesh是一種基于人工智能生成的分布式架構(gòu),與傳統(tǒng)的分布式架構(gòu)不同,Unit Mesh 中的服務(wù)單元 (Unit) 是由 AI 生成的,應(yīng)用程序中的服務(wù)和數(shù)據(jù)抽象為一個(gè)個(gè)獨(dú)立的單元,并通過統(tǒng)一的控制平面進(jìn)行管理和部署。
PS:之所以叫 Unit Mesh,是因?yàn)槲覀儗懥艘粋€(gè)底層服務(wù)叫 UnitServer ,還有參考了 Service Mesh 和 Data Mesh 架構(gòu)理念,所以 AI 取建議我們叫 Unit Mesh 。
TLDR 版本
我們初步定義的這個(gè)版本(0.1 ,稱之為 UnitGenius)的核心三個(gè)特性:
語(yǔ)言與框架的 DSL(領(lǐng)域特定語(yǔ)言) 抽象:抽象非的編程語(yǔ)言和框架特性,以簡(jiǎn)化出錯(cuò)的可能性。
REPL 即服務(wù):運(yùn)行 AI 生成的代碼,并提供對(duì)應(yīng)的 API 服務(wù)。
AI 設(shè)計(jì)的適應(yīng)性結(jié)構(gòu):自我適應(yīng)的 API 服務(wù)架構(gòu),以在不同的環(huán)境下自動(dòng)調(diào)整和優(yōu)化。
開發(fā)者可以通過與 AI 交互,生成一定程度的 DSL 抽象化代碼,然后在 REPL 即 Serverless 服務(wù)上運(yùn)行和測(cè)試這些代碼。開發(fā)者還可以將這些代碼提交給 AI進(jìn) 行自動(dòng)化運(yùn)維,AI 會(huì)對(duì)代碼進(jìn)行優(yōu)化和調(diào)整,從而進(jìn)一步提高 API 服務(wù)的性能和可靠性。
開始正文的廢話版本。
Unit Mesh 初步 Demo:DSL + ?REPL = Unit Server
詳細(xì)過程,見本文的后半部分。
前端頁(yè)面:https://prompt.phodal.com/zh-CN/click-flow/unit-mesh-unit-server/
首先,你需要克隆一下,Unit Server 的代碼:https://github.com/prompt-engineering/unit-server ,然后,選擇 kotlin-repl 或者 typescript-repl 對(duì)應(yīng) Kotlin、TypeScript 兩種語(yǔ)言。
然后,按對(duì)應(yīng)的 README 運(yùn)行起你的 Unit Server。
接著,在 ChatFlow 里讓 ChatGPT 生成如下的代碼,并點(diǎn)擊 Run
按鈕:
%spring@RestControllerobject Pages { ? @GetMapping("/") ? fun main() = "It works!"}
最后,你就可以得到一個(gè)正在運(yùn)行的服務(wù)(該功能還在開發(fā)中):http://localhost:8080/ ,訪問該服務(wù)后,如果的應(yīng)該是 It works。
PS:這里有一個(gè)手動(dòng)加入調(diào)用 Application 類和調(diào)用 ?main 方法的代碼,因?yàn)樾枰鲮o態(tài)分析,才能確定使用的框架,暫時(shí)沒寫在 Unit Server 代碼中。
Unit Mesh 架構(gòu)
再重復(fù)一下定義:
Unit Mesh是一種基于人工智能生成的分布式架構(gòu),與傳統(tǒng)的分布式架構(gòu)不同,Unit Mesh 中的服務(wù)單元 (Unit) 是由 AI 生成的,應(yīng)用程序中的服務(wù)和數(shù)據(jù)抽象為一個(gè)個(gè)獨(dú)立的單元,并通過統(tǒng)一的控制平面進(jìn)行管理和部署。
Unit Mesh 核心思想:AI 生成的代碼即 Unit
Unit Mesh 是圍繞于 Unit 為核心的架構(gòu)模式。
AI 生成 Unit。即 AI 應(yīng)該生成的代碼都應(yīng)該是可運(yùn)行的 Unit,上到 React 組件、下到后端服務(wù)都是可運(yùn)行的。
校驗(yàn) Unit。由人類來檢查和校驗(yàn) Unit,如果 AI 生成的代碼有問題,那么人類只需要修復(fù)即可。
Unit 自適應(yīng)部署架構(gòu)。在部署時(shí),Unit 可以組成 Serverless 架構(gòu)、微服務(wù)架構(gòu)、單體架構(gòu)、Mesh 架構(gòu),而不需要人類來干預(yù)。
碳基嘛,就適合當(dāng)一個(gè) Verifier。
Unit Mesh 架構(gòu)核心要素
結(jié)合我們?cè)O(shè)計(jì)的 Unit Server,我們?cè)O(shè)計(jì)的 Unit Mesh 架構(gòu)由以下三要素構(gòu)成:
語(yǔ)言與框架的 DSL 抽象:封裝不穩(wěn)定的抽象
由于 AI 生成的代碼會(huì)有各種問題,諸如于無法對(duì)接內(nèi)部的云平臺(tái)、出錯(cuò)的 imports 等等,所以我們要設(shè)計(jì)領(lǐng)域特定語(yǔ)言來解決這個(gè)問題,并封裝抽象。
簡(jiǎn)單來說:我們需要抽象將所有不穩(wěn)定的元素,便能構(gòu)建出穩(wěn)定的元素。
詳細(xì)的設(shè)計(jì)會(huì)在后面的 Unit Server 部分展開。
PS:而由于大語(yǔ)言模型是有上下文能力限制的,像我這樣的、搞不到充值的就只配 4k。因此,我設(shè)計(jì)的 Unit 要稱之為 4k Unit Mesh,我設(shè)計(jì)的 DSL 要稱之為 4k Unit ?DSL,有的人可能就是 99k DSL。
REPL 即服務(wù):AI 代碼修復(fù)師的
在有了 DSL 之后,我們還需要一個(gè) REPL (Read-Eval-Print Loop)服務(wù),能直接運(yùn)行起 AI 生成 的 Unit,然后讓人類來測(cè)試生成的代碼是否是正確。如果生成的 AI 有錯(cuò)誤,就需要 AI 代碼修復(fù)師來對(duì)代碼進(jìn)行修復(fù)。
而對(duì)于一個(gè)服務(wù)來,如果我們是一個(gè) API,就需要是 Serverless 服務(wù),這就是為什么我們?cè)趫D里稱之為:REPL 即 Serverless 服務(wù)。詳細(xì)可以參見后面設(shè)計(jì)的 Unit Server。
AI 設(shè)計(jì)的適應(yīng)性結(jié)構(gòu)
人類設(shè)計(jì)系統(tǒng)的一個(gè)缺點(diǎn)是,如果設(shè)計(jì)時(shí)、開發(fā)時(shí)、運(yùn)行時(shí)的單元不一樣,那么就會(huì)出現(xiàn)各種疑慮。于是,我們會(huì)偏向于設(shè)計(jì)成三態(tài)一致的架構(gòu)模式,而這本身對(duì)于架構(gòu)的適應(yīng)性優(yōu)化就是個(gè)問題。
而既然,代碼都是 Unit。那么,設(shè)計(jì)時(shí)可以是微服務(wù),開發(fā)時(shí)可以是 Serverless,線上可以是單體。正如 Google 的 Service Waver 所做的事情,我們不決定運(yùn)行時(shí)的架構(gòu),讓你來選擇。
所以,AI 怎么運(yùn)行我們的 Unit,就讓 AI 來決定吧。
PS:本來吧,標(biāo)題應(yīng)該是適應(yīng)性架構(gòu)(Adaptive Architecture),但是我想了想就只是代碼結(jié)構(gòu)之類的,又重新考慮了一下。
Unit Mesh 設(shè)計(jì)心得:反直覺才是出路
在去年年底,研究低延遲架構(gòu)之時(shí),便被這個(gè)領(lǐng)域的各種反直覺架構(gòu)模式所震撼,諸如于:GC 是問題那就不要 GC。
因此當(dāng)設(shè)計(jì) Unit Mesh ?時(shí),我們的問題依舊是:如何 ?Open your mind。即拋開現(xiàn)有的思維模式和固有知識(shí),打破常規(guī)思考,所以我們的主要挑戰(zhàn)是如何拓展思維,開放心智。
要點(diǎn) 1:如果分層架構(gòu)是瓶頸,那么就不要分層架構(gòu)
在那篇《未來可期的 AI 編程里》分層架構(gòu)是我們最大的挑戰(zhàn),于是,提出理想的方式就是 Serverless + FaaS 的方式,而這種方式則是基于現(xiàn)有的械,又過于理想化。
而隨著我們寫了 UnitServer 之后,我們發(fā)現(xiàn),還可以 Class as a Service 的方式嘛(手動(dòng)狗頭)。
既然我們的代碼運(yùn)行在云端,由 AI 生成的,那么人類還要看代碼嗎?人類要在什么時(shí)候看代碼?無非就是檢入的時(shí)候,還有審查架構(gòu)的時(shí)候,所以只需要在審查的時(shí)候,生成架構(gòu)不就行了。
示例:我想分析 xx 服務(wù)的調(diào)用情況,以及對(duì)應(yīng)的代碼,請(qǐng)幫我調(diào)取出來。
要點(diǎn) 2:如果依賴是問題,那么就不要依賴
我們遇到的第二個(gè)挑戰(zhàn)是依賴問題,而依賴是兩個(gè)問題:
項(xiàng)目的庫(kù)依賴。即類似于 Gradle、Maven、NPM 這一層的庫(kù)依賴
代碼依賴。即代碼源文件的
import
復(fù)讀機(jī) ChatGPT 并不能很好解決問題,所以就要讓 GPT 忘記這些。理想的編程體驗(yàn),應(yīng)該是我要用 Spring,智能就會(huì)自動(dòng)分析依賴,如 Intelij IDEA。所以,我們?cè)?UnitServer 中采用了 % spring
樣的 Jupyter magic 語(yǔ)法 ,以自動(dòng)解決這兩類問題。
要點(diǎn) 3:如果 Serverless 部署是問題,那么就不用 Serverless 部署
起初在 Unit Server 里,我們把 Unit Server 設(shè)計(jì)成了一個(gè)類 Serverless 架構(gòu),所以我們遇到了一個(gè)問題:Serverless 架構(gòu)的成本并非所有的人能接受的。所以,我們只需要在測(cè)試 Unit 時(shí),采用 Serverless 作為開發(fā)時(shí),在線上合并成一個(gè)單體或者微服務(wù)架構(gòu),那么就能完美解決這個(gè)問題。
而在這時(shí),還需要突破剛才的分層架構(gòu),既然每次代碼是生成的,那么我們只需要一個(gè)包名即可,諸如于: org.clickprompt.unitmesh
,所有的代碼都在這個(gè)包下;又或者,我們可以通過業(yè)務(wù)進(jìn)一步劃分成不同的包,結(jié)合工具來對(duì)代碼進(jìn)行歸類。
Unit Mesh 探索之路:從 REPL 到 UnitServer
上面講的太理論了,來看看我們的探索之路,一共分為四步:
從最小的 Hello, world 開始優(yōu)化
構(gòu)建一個(gè) REPL 環(huán)境
抽象、簡(jiǎn)化設(shè)計(jì) ← 重復(fù)。
接入真實(shí)世界的 Prompt。
詳細(xì)可以查看 Unit Server 和 ChatFlow 的提交紀(jì)錄。
從最小的 Hello, world 開始
首先,讓我們看一個(gè) ?Kotlin Script 編寫的 Spring 的 Hello, World:
@file:DependsOn("org.springframework.boot:spring-boot-starter-web:2.7.9") import ...import java.util.*@Controllerclass HelloController { ? ?@GetMapping("/hello") ? ?fun helloKotlin(): String { ? ? ? ?return "hello world" ? ?} } @SpringBootApplicationopen class ReplApplicationfun main(args: Array<String>) { ? ?... } main(arrayOf("--server.port=8083"))
在這個(gè)示例里,你會(huì)發(fā)現(xiàn)一系列的無用代碼,依賴信息、import 信息、main 函數(shù)。而作為一個(gè) 4k Unit Mesh 的創(chuàng)作者,我必須把這些不穩(wěn)定的無用信息去掉,才能正確運(yùn)行,所以它變成了:
%use spring @Controllerclass HelloController { ? ?@GetMapping("/hello") ? ?fun helloKotlin(): String { ? ? ? ?return "hello world" ? ?} }
這樣一來,我只需要讓 ChatGPT 返回 Controller 即可。
構(gòu)建 ?REPL 環(huán)境:WebSocket + %magic
既然,我們已經(jīng)有了一個(gè)簡(jiǎn)化的 DSL,接下來就是引入 Kotlin Script 來構(gòu)建一個(gè) Unit Serverless 服務(wù)器,也就是我們的: https://github.com/prompt-engineering/unit-server 。
Unit Server 的源碼是基于 Kotlin Jupyter ?API 所構(gòu)建的,而 Kotlin Jupyter 則是封裝了 Kotlin 的 REPL 環(huán)境。我們之所謂基于 Kotlin Jupyter 而不是 Kotlin REPL 的主要原因是,可以使用 magic 和 DSL 來抽象細(xì)節(jié),諸如于:
"spring" to Json.encodeToString( ? ?SimpleLibraryDefinition( ? ? ? ?imports = listOf( ? ? ? ? ? ?"org.springframework.boot.*", ? ? ? ? ? ?"org.springframework.boot.autoconfigure.*", ? ? ? ? ? ?"org.springframework.web.bind.annotation.*", ? ? ? ? ? ?"org.springframework.context.annotation.ComponentScan", ? ? ? ? ? ?"org.springframework.context.annotation.Configuration" ? ? ? ?), ? ? ? ?dependencies = listOf( ? ? ? ? ? ?"org.springframework.boot:spring-boot-starter-web:2.7.9" ? ? ? ?) ? ?) )
即可以自動(dòng)添加 Spring 的依賴和 Import 信息,就可以支持步驟的 Hello, World 方式。除了 Spring,我們還需要其它的庫(kù)的 magic。
最后,再使用 WebSocket 暴露出這個(gè)接口,以提供給 ChatFlow 使用。
抽象、簡(jiǎn)化設(shè)計(jì) ← 循環(huán)
當(dāng)然了,只是有一個(gè) hello, world 是不夠的,所以我們需要更多的例子,諸如于接入數(shù)據(jù)庫(kù)。而由于 Spring 的掃描機(jī)制影響,外加我們并不想(主要是不會(huì))針對(duì) Spring 做太多的特化,所以我們換成了 Kotlin 里 Kotr 框架。
PS:值得注意的是,我們還需要對(duì)框架進(jìn)行抽象,但是 Ktor 對(duì)我們預(yù)期的好一點(diǎn)。所以,我們的第二個(gè)版本來了:
%use kotless%use exposeddata class User(val id: Int, val username: String)class Server : KotlessAWS() { ? ?override fun prepare(app: Application) { ? ? ? ?Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver") ? ? ? ?transaction { ? ? ? ? ? ?SchemaUtils.create(Users) ? ? ? ?} ? ? ? ?app.routing { ? ? ? ? ? ?post("/register") { ? ? ? ? ? ? ? ?val user = call.receive<User>() ? ? ? ? ? ? ? ?val id = transaction { ? ? ? ? ? ? ? ? ? ?// Insert the new user into the database ? ? ? ? ? ? ? ? ? ?Users.insert { ? ? ? ? ? ? ? ? ? ? ? ?it[username] = user.username ? ? ? ? ? ? ? ? ? ?} get Users.id ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ?val newUser = User(id, user.username) ? ? ? ? ? ? ? ?call.respond(newUser) ? ? ? ? ? ?} ? ? ? ?} ? ?} }object Users : org.jetbrains.exposed.sql.Table("users") { ? ?val id = integer("id").autoIncrement() ? ?val username = varchar("username", 50).uniqueIndex() ? ?override val primaryKey = PrimaryKey(id, name = "PK_User_ID") }
在這個(gè)版本里,我們使用了 Exposed 作為數(shù)據(jù)庫(kù)的 ORM,使用 H2 作為數(shù)據(jù)庫(kù)。當(dāng)然,要拿這個(gè)代碼作為 Unit 還差了 10% 的距離,不過,基本上已經(jīng)可以解決大部分的 CRUD 場(chǎng)景。
PS 1:這里的 KotlessAWS 只是一個(gè) AWS Serverless 的抽象,并不影響我們的操作,我們可以直接封裝一個(gè) UnitMesh 的類,就是懶。
PS 2:我們只需要通過靜態(tài)分析拿出 routing 中的代碼,再優(yōu)化即可。更多的探索過程代碼可以見:_samples 。
一個(gè)真實(shí)世界的 Prompt
現(xiàn)在,讓我們來結(jié)合 AI 跑一下:
請(qǐng)幫我使用 Ktor + Kotlin + Exposed 實(shí)現(xiàn)一個(gè)用戶注冊(cè)的 RESTful API,要求如下:- 涉及到數(shù)據(jù)庫(kù)的地方,請(qǐng)直接使用 Database.connect。- 只返回核心邏輯,并寫在 Server 類里,我要部署在 Serverless 服務(wù)器里。- 請(qǐng)使用 Kotlin DSL 的方式編寫代碼。- 不返回其它的無關(guān)代碼,如:注釋、依賴、import 等。 最后,你只返回類的代碼,返回格式如下:class Server : KotlessAWS() { ? ?override fun prepare(app: Application) { ? ? ? ?Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "root", password = "") ? ? ? ?transaction { ? ? ? ? ? ?SchemaUtils.create(Users) ? ? ? ?} ? ? ? ?app.routing { ? ? ? ? ? ?{{{}}} ? ? ? ?} ? ?} }
人生苦短,歡迎加入我們的 Watchlist,一起討論未來。
## Join Waitlist
狗頭,現(xiàn)在 Waitlist 工程師們,你可以就加入 Unit Mesh 的 Watchlist:
https://github.com/prompt-engineering/unit-mesh