CangjieMagic框架:使用华为仓颉编程语言编写,专门用于开发AI Agent,支持鸿蒙、Windows、macOS、Linux等系统。
这篇文章剖析一下 CangjieMagic 框架中的 PlanReactExecutor。
当你要组织一场生日派对时,你会怎么做?你不会一头扎进去就开始准备,而是会先:
PlanReactExecutor就是这样工作的!它不会直接尝试解决一个复杂问题,而是先分析、分解,然后一步步解决,最后整合答案。
我们先来看看PlanReactExecutor的构造函数:
protected class PlanReactExecutor <: AgentExecutor {
protected PlanReactExecutor() { }
// 其他成员...
}
这个构造函数非常简单,它背后连接了几个强大的组件:
就像一个专业厨师虽然看起来很简单地做了一道菜,但背后有一套完整的厨具和步骤一样,PlanReactExecutor看似简单,实则整合了多个专业组件。
现在,让我们看看同步执行函数的实现:
override public func run(agent: Agent, request: AgentRequest): AgentResponse {
// Extract necessary knowledge
let knowledge = knowledgeExtract(agent, request)
// Decompose the problem
let subtasks: Array = problemDecompose(agent, request)
let planTask = PlanTask(agent, request, subtasks, knowledge)
// Solve each subtask
for (subtask in subtasks) {
let worker = ReactWorker(planTask)
let result = worker.solve(subtask)
subtask.result = result
}
// Summarize the result
let result = resultSummarize(planTask)
LogUtils.info(agent.name, "Summarized answer: ${result}")
return AgentResponse(result)
}
我们来用一个房屋装修的例子来理解这个过程:
知识提取:就像设计师了解业主的喜好和需求
let knowledge = knowledgeExtract(agent, request)
问题分解:就像项目经理将装修分解为水电、木工、油漆等工序
let subtasks: Array = 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)
这个过程既有条理又高效,每个步骤都有明确的职责,就像一个专业的项目团队!
当我们需要实时查看任务执行进度时,就可以使用异步执行函数:
override public func asyncRun(agent: Agent, request: AgentRequest): AsyncAgentResponse {
let planTask = PlanTask(agent, request)
// Create the worker thread
let fut: Future> = 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
函数,这样主线程就不会被阻塞。用生活中的例子来说,这就像你在餐厅点菜后,服务员会给你一个电子呼叫器,你可以去做其他事情,等菜好了会通知你。
workFn
函数是实际执行异步工作的地方,让我们仔细看看它的实现:
private func workFn(planTask: PlanTask): Iterator {
let agent = planTask.agent
let request = planTask.request
// Extract necessary knowledge
planTask.knowledge = knowledgeExtract(agent, request)
// Decompose the problem
let subtasks: Array = problemDecompose(agent, request)
planTask.subtasks = subtasks
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))
}
// Solve each subtask
for (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 agent
for (data in asyncResp.execInfo.getOrThrow().verboseInfo) {
planTask.execInfo.verboseChannel.put(data)
}
}
subtask.result = asyncResp.content
if (request.verbose) {
planTask.execInfo.verboseChannel.put("# Subtask DONE!\n${subtask.toMarkdown()}".withTag(ReactTag.INFO))
}
}
if (request.verbose) {
planTask.execInfo.verboseChannel.close()
}
// Summarize the result
return 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))
}
这就像是告诉用户"现在正在搭建城堡的塔楼部分",让用户了解当前进度。
如果用餐厅来比喻三种执行器:
PlanReactExecutor最适合那些需要多步骤、多角度思考的复杂问题。
当用户请求研究神经网络的最新进展时,执行过程可能是:
想象一个用户想规划一次欧洲之旅:
用户: 请帮我规划一次为期7天的法国巴黎和意大利罗马的旅行,包括景点、住宿和交通。
使用PlanReactExecutor,执行过程可能是:
用户: 请求解下列方程组:
3x + 2y - z = 10
2x - 3y + 2z = -5
x + y + z = 7
使用PlanReactExecutor解决这个问题:
PlanReactExecutor不是独立工作的,它依赖几个核心组件:
knowledgeExtract:就像研究生开始论文前的文献综述,先了解相关知识
problemDecompose:就像建筑师在开工前绘制详细的施工图纸
ReactWorker:就像专业工人按照图纸完成具体工作
resultSummarize:就像编辑将多篇文章整合成一本完整的书
这些组件协同工作,使得PlanReactExecutor能够处理非常复杂的问题。
适合使用PlanReactExecutor的场景:
不适合的场景:
PlanReactExecutor就像一位经验丰富的项目经理,能够将复杂问题分解为可管理的小任务,然后一步步解决,最终整合成完整的解决方案。在处理复杂问题时,它的表现远超简单的执行器。
当你的AI应用需要处理复杂的多步骤问题时,不妨考虑使用PlanReactExecutor,它将帮助你的Agent像专业团队一样有条不紊地解决问题。