CangjieMagic框架:使用華為倉頡編程語言編寫,專門用于開發AI Agent,支持鴻蒙、Windows、macOS、Linux等系統。
這篇文章剖析一下 CangjieMagic 框架中的 PlanReactExecutor。
1 PlanReactExecutor的工作原理
當你要組織一場生日派對時,你會怎么做?你不會一頭扎進去就開始準備,而是會先:
- 了解壽星的喜好(知識提取)
- 分解任務:場地、食物、娛樂、禮物(問題分解)
- 一個個完成這些子任務(執行子任務)
- 最后把所有準備工作整合起來,舉辦一場成功的派對(匯總結果)
PlanReactExecutor就是這樣工作的!它不會直接嘗試解決一個復雜問題,而是先分析、分解,然后一步步解決,最后整合答案。
2 深入代碼:構造函數和核心組件
我們先來看看PlanReactExecutor的構造函數:
protected class PlanReactExecutor <: AgentExecutor {protected PlanReactExecutor() { }// 其他成員...
}
這個構造函數非常簡單,它背后連接了幾個強大的組件:
就像一個專業廚師雖然看起來很簡單地做了一道菜,但背后有一套完整的廚具和步驟一樣,PlanReactExecutor看似簡單,實則整合了多個專業組件。
3 同步執行流程:像項目經理一樣工作
現在,讓我們看看同步執行函數的實現:
override public func run(agent: Agent, request: AgentRequest): AgentResponse {// Extract necessary knowledgelet knowledge = knowledgeExtract(agent, request)// Decompose the problemlet subtasks: Array<Subtask> = problemDecompose(agent, request)let planTask = PlanTask(agent, request, subtasks, knowledge)// Solve each subtaskfor (subtask in subtasks) {let worker = ReactWorker(planTask)let result = worker.solve(subtask)subtask.result = result}// Summarize the resultlet result = resultSummarize(planTask)LogUtils.info(agent.name, "Summarized answer: ${result}")return AgentResponse(result)
}
我們來用一個房屋裝修的例子來理解這個過程:
-
知識提取:就像設計師了解業主的喜好和需求
let knowledge = knowledgeExtract(agent, request)
-
問題分解:就像項目經理將裝修分解為水電、木工、油漆等工序
let subtasks: Array<Subtask> = problemDecompose(agent, request)
-
創建計劃任務:匯總需求和任務清單,形成施工方案
let planTask = PlanTask(agent, request, subtasks, knowledge)
-
執行每個子任務:安排工人團隊依次完成各個工序
for (subtask in subtasks) {let worker = ReactWorker(planTask)let result = worker.solve(subtask)subtask.result = result }
-
匯總結果:整合所有工作,形成最終成果
let result = resultSummarize(planTask)
-
返回結果:向業主交付完工的房子
return AgentResponse(result)
這個過程既有條理又高效,每個步驟都有明確的職責,就像一個專業的項目團隊!
4 異步執行流程:實時查看進度的魔力
當我們需要實時查看任務執行進度時,就可以使用異步執行函數:
override public func asyncRun(agent: Agent, request: AgentRequest): AsyncAgentResponse {let planTask = PlanTask(agent, request)// Create the worker threadlet fut: Future<Iterator<String>> = spawn {try {return workFn(planTask)} catch(ex: Exception) {planTask.execInfo.verboseChannel.close()throw ex}throw UnsupportedException("Unreachable")}return AsyncAgentResponse(IteratorWrapper(planTask, fut), execInfo: planTask.execInfo)
}
這就像你在手機APP上訂購一份外賣,可以實時看到"商家接單→廚師制作→騎手取餐→配送中→已送達"的全過程,而不是只能干等結果。
這里的關鍵是spawn
表達式,它創建了一個新的工作線程來執行workFn
函數,這樣主線程就不會被阻塞。用生活中的例子來說,這就像你在餐廳點菜后,服務員會給你一個電子呼叫器,你可以去做其他事情,等菜好了會通知你。
5 workFn函數:異步工作的實際執行者
workFn
函數是實際執行異步工作的地方,讓我們仔細看看它的實現:
private func workFn(planTask: PlanTask): Iterator<String> {let agent = planTask.agentlet request = planTask.request// Extract necessary knowledgeplanTask.knowledge = knowledgeExtract(agent, request)// Decompose the problemlet subtasks: Array<Subtask> = problemDecompose(agent, request)planTask.subtasks = subtasksif (request.verbose) {let strBuilder = StringBuilder()strBuilder.append("# The Plan\n")for (subtask in subtasks) {strBuilder.append(subtask.toMarkdown())strBuilder.append("\n")}planTask.execInfo.verboseChannel.put(strBuilder.toString().withTag(ReactTag.PLAN))}// Solve each subtaskfor (subtask in subtasks) {if (request.verbose) {planTask.execInfo.verboseChannel.put("Solve the subtask: ${subtask.name}".withTag(ReactTag.PLAN))}let worker = ReactWorker(planTask)let asyncResp = worker.asyncSolve(subtask, verbose: request.verbose)if (request.verbose) {// Transfer the internal information from the react worker to this agentfor (data in asyncResp.execInfo.getOrThrow().verboseInfo) {planTask.execInfo.verboseChannel.put(data)}}subtask.result = asyncResp.contentif (request.verbose) {planTask.execInfo.verboseChannel.put("# Subtask DONE!\n${subtask.toMarkdown()}".withTag(ReactTag.INFO))}}if (request.verbose) {planTask.execInfo.verboseChannel.close()}// Summarize the resultreturn asyncResultSummarize(planTask)
}
讓我們用一個建造樂高模型的例子來理解這個過程:
在這個過程中,最有價值的部分是用戶可以看到計劃和每個子任務的執行過程,這就是verbose
模式的作用:
if (request.verbose) {let strBuilder = StringBuilder()strBuilder.append("# The Plan\n")for (subtask in subtasks) {strBuilder.append(subtask.toMarkdown())strBuilder.append("\n")}planTask.execInfo.verboseChannel.put(strBuilder.toString().withTag(ReactTag.PLAN))
}
這段代碼就像是向用戶展示樂高說明書的全貌,讓用戶知道接下來會發生什么。然后在執行每個子任務時,不斷更新進度:
if (request.verbose) {planTask.execInfo.verboseChannel.put("Solve the subtask: ${subtask.name}".withTag(ReactTag.PLAN))
}
這就像是告訴用戶"現在正在搭建城堡的塔樓部分",讓用戶了解當前進度。
6 與其他執行器的對比
如果用餐廳來比喻三種執行器:
- NaiveExecutor:快餐店,直接點餐、直接出餐
- ReActExecutor:普通餐廳,廚師根據訂單現做現賣
- PlanReactExecutor:高檔餐廳,主廚先設計菜單,然后團隊分工協作制作多道菜肴,最后組合成一頓完美的大餐
PlanReactExecutor最適合那些需要多步驟、多角度思考的復雜問題。
7 實際應用案例
案例一:學術研究助手
當用戶請求研究神經網絡的最新進展時,執行過程可能是:
案例二:旅行規劃助手
想象一個用戶想規劃一次歐洲之旅:
用戶: 請幫我規劃一次為期7天的法國巴黎和意大利羅馬的旅行,包括景點、住宿和交通。
使用PlanReactExecutor,執行過程可能是:
- 知識提取:了解用戶想去巴黎和羅馬,時間為7天
- 問題分解:
- 子任務1:規劃巴黎部分的行程(3天)
- 子任務2:規劃羅馬部分的行程(3天)
- 子任務3:規劃兩地之間的交通(1天)
- 子任務4:提供住宿建議
- 子任務5:整合完整行程
- 執行每個子任務:分別解決每個子任務
- 匯總結果:生成完整的7天旅行計劃
案例三:復雜數學問題求解
用戶: 請求解下列方程組:
3x + 2y - z = 10
2x - 3y + 2z = -5
x + y + z = 7
使用PlanReactExecutor解決這個問題:
- 知識提取:確定這是一個三元一次方程組
- 問題分解:
- 子任務1:使用消元法消去z變量
- 子任務2:解出x和y的關系
- 子任務3:代回求解x、y、z的值
- 子任務4:驗證結果是否正確
- 執行子任務:依次解決每個數學步驟
- 匯總結果:提供完整的解答和解釋
8 核心組件深度解析
PlanReactExecutor不是獨立工作的,它依賴幾個核心組件:
-
knowledgeExtract:就像研究生開始論文前的文獻綜述,先了解相關知識
-
problemDecompose:就像建筑師在開工前繪制詳細的施工圖紙
-
ReactWorker:就像專業工人按照圖紙完成具體工作
-
resultSummarize:就像編輯將多篇文章整合成一本完整的書
這些組件協同工作,使得PlanReactExecutor能夠處理非常復雜的問題。
9 PlanReactExecutor的優勢與局限
優勢:
- 解決復雜問題:像專業律師處理復雜案件,有條不紊
- 思路清晰:用戶可以看到完整的思考過程,增強可信度
- 結果全面:考慮問題的多個方面,不會遺漏重要內容
- 可追蹤性:出現問題可以定位到具體哪個子任務
局限:
- 執行時間長:就像做一道復雜的菜肴,需要更多時間
- 資源消耗大:需要更多的API調用和計算資源
- 簡單問題反而復雜化:用大炮打蚊子,對簡單問題過度設計
10 何時選擇PlanReactExecutor?
適合使用PlanReactExecutor的場景:
- 多步驟問題:如規劃旅行、制定學習計劃
- 需要多角度分析:如進行SWOT分析、評估風險
- 需要綜合信息:如撰寫研究報告、市場分析
- 組織創作內容:如寫一本書的大綱、設計課程體系
不適合的場景:
- 簡單問答:如"今天天氣怎么樣"
- 單一工具調用:如"計算123 + 456"
- 快速響應場景:如緊急情況下的決策
11 總結
PlanReactExecutor就像一位經驗豐富的項目經理,能夠將復雜問題分解為可管理的小任務,然后一步步解決,最終整合成完整的解決方案。在處理復雜問題時,它的表現遠超簡單的執行器。
當你的AI應用需要處理復雜的多步驟問題時,不妨考慮使用PlanReactExecutor,它將幫助你的Agent像專業團隊一樣有條不紊地解決問題。