经典Agent架构实战之反思模型(Reflection)

背景

前段时间看到了一个开源项目

https://github.com/FareedKhan-dev/all-agentic-architectures/tree/main

介绍了一些经典的agent架构,但是是使用python的langchain和lang graph框架实现的,这里通过golang的大模型开发框架 Eino去实现其基本功能

Reflection介绍

原文:https://github.com/FareedKhan-dev/all-agentic-architectures/blob/main/01_reflection.ipynb

这种模式将大型语言模型(LLM)从简单的单次生成器提升为更为有意识和稳健的推理器。反思型代理不仅仅提供最初的答案,而是退一步,批判、分析并完善自己的工作。这种自我提升的迭代过程是构建更可靠、更高质量人工智能系统的基石。

定义

反射架构涉及代理在返回最终答案前对自身输出进行批评和修订。它不是单次生成,而是进行多步骤的内部独白:生产、评估和改进。这模拟了人类起草、审阅和编辑的过程,以发现错误并提升质量。

工作流程

  1. 生成 (Generate): 智能体根据用户提示生成初始草稿或解决方案。
  2. 批评 (Critique): 智能体切换角色为批评者,进行自我提问,例如:"这个答案有什么问题?"、"是否最优?"、"有没有逻辑错误或 Bug?"
  3. 完善 (Refine): 利用自我批评的见解,智能体生成最终的、改进后的版本。

场景

代码生成: 初始代码可能存在 bug、效率低下或缺少注释。反思让代理能够作为自己的代码审查者,发现错误并改进风格,然后再呈现最终脚本。

复杂总结: 在总结晦涩复杂的文档时,第一遍可能会遗漏细节或关键细节。反思步骤有助于确保摘要全面且准确。

创意写作与内容创作: 电子邮件、博客文章或故事的初稿总可以改进。反射让代理人能够精炼其音色、清晰度和冲击力。

关键实现代码

完整代码参考github:https://github.com/windofbarcelona/all-agentic-architectures-golang

主要编排代码

go 复制代码
type ReflectionState struct {
	DraftCode   string
	Critique    string
	RefinedCode string
	query       string
}

func buildReflectionRunnable() (compose.Runnable[map[string]any, *schema.Message], error) {
	sg := compose.NewGraph[map[string]any, *schema.Message](compose.WithGenLocalState(func(ctx context.Context) (state ReflectionState) {
		return ReflectionState{}
	}))
	temp := DraftCodeTemplate()
	model, err := GetModel()
	if err != nil {
		return nil, err
	}
	sg.AddChatTemplateNode("DraftCodeTemplate", &temp, compose.WithStatePreHandler(func(ctx context.Context, in map[string]any, state ReflectionState) (map[string]any, error) {
		//fmt.Println("DraftCodeTemplate input:", in)
		state.query = in["user_query"].(string)
		return in, nil
	}))
	sg.AddChatModelNode("DraftCodeModel", model, compose.WithStatePostHandler(func(ctx context.Context, out *schema.Message, state ReflectionState) (*schema.Message, error) {
		state.DraftCode = out.Content
		fmt.Println("DraftCodeNode output:", out)
		return out, nil
	}))

	sg.AddLambdaNode("parseDraftCodeTemplate", compose.InvokableLambda(func(ctx context.Context, input *schema.Message) (output map[string]any, err error) {
		// some logic
		output = make(map[string]any, 2)
		output["DraftCode"] = input.Content
		return output, nil
	}), compose.WithStatePostHandler(func(ctx context.Context, output map[string]any, state ReflectionState) (map[string]any, error) {
		output["user_query"] = state.query
		return output, nil
	}))

	temp_2 := CritiqueTemplate()
	model2, err := GetModel()
	sg.AddChatTemplateNode("CritiqueNodeTemplate", &temp_2)
	sg.AddChatModelNode("CritiqueNodeModel", model2, compose.WithStatePostHandler(func(ctx context.Context, out *schema.Message, state ReflectionState) (*schema.Message, error) {
		state.Critique = out.Content
		fmt.Println("CritiqueNodeModel output:", out)
		return out, nil
	}))

	sg.AddLambdaNode("parseCritiqueNodeTemplate", compose.InvokableLambda(func(ctx context.Context, input *schema.Message) (output map[string]any, err error) {
		// some logic
		output = make(map[string]any, 3)
		output["Critique"] = input.Content
		return output, nil
	}), compose.WithStatePostHandler(func(ctx context.Context, output map[string]any, state ReflectionState) (map[string]any, error) {
		output["user_query"] = state.query
		output["DraftCode"] = state.DraftCode
		return output, nil
	}))

	temp_3 := RefinedCodeTemplate()
	model3, err := GetModel()
	sg.AddChatTemplateNode("RefinedCodeTemplate", &temp_3)
	sg.AddChatModelNode("RefinedCodeModel", model3, compose.WithStatePostHandler(func(ctx context.Context, out *schema.Message, state ReflectionState) (*schema.Message, error) {
		state.DraftCode = out.Content
		fmt.Println("RefinedCodeModel output:", out)
		return out, nil
	}))

	sg.AddEdge(compose.START, "DraftCodeTemplate")
	sg.AddEdge("DraftCodeTemplate", "DraftCodeModel")
	//sg.AddEdge("DraftCodeModel", compose.END)
	sg.AddEdge("DraftCodeModel", "parseDraftCodeTemplate")
	sg.AddEdge("parseDraftCodeTemplate", "CritiqueNodeTemplate")
	sg.AddEdge("CritiqueNodeTemplate", "CritiqueNodeModel")
	sg.AddEdge("CritiqueNodeModel", "parseCritiqueNodeTemplate")
	sg.AddEdge("parseCritiqueNodeTemplate", "RefinedCodeTemplate")
	sg.AddEdge("RefinedCodeTemplate", "RefinedCodeModel")
	sg.AddEdge("RefinedCodeModel", compose.END)

	reflectionRunnable, err := sg.Compile(context.Background())
	return reflectionRunnable, err
}

中间夹杂了一些用于debug的输出,大家自行忽略

prompt管理

go 复制代码
func RefinedCodeTemplate() prompt.DefaultChatTemplate {
	systemTpl := `
	角色:你是一个精通golang编程语言的高级软件工程师,同时熟悉计算机编程的所有范式和最佳实践。
	任务:根据初版代码及修改意见对代码进行修改,并返回修改后的代码。
	数据:
		1. 原始代码: {DraftCode}
		2. 修改意见: {Critique}
	要求:
	1. 严格遵照修改意见进行代码修改。
	2. 如果没有修改意见,请返回原始代码。
	`
	chatTpl := prompt.FromMessages(schema.FString,
		schema.SystemMessage(systemTpl),
		//schema.MessagesPlaceholder("message_histories", true),
		schema.UserMessage("{user_query}"),
	)
	return *chatTpl
}

通过debug代码可以看到模型对于初版代码提出的修改意见是

go 复制代码
用户的原始需求是提供一个Go语言实现的斐波那契数列求解程序,并通过多种不同算法实现(递归、迭代、备忘录法、矩阵快速幂)来展示各自的优缺点及适用场景,同时包含性能对比和使用说明。

从代码内容来看,用户希望达成以下目标:
1. 展示斐波那契数列的多种经典实现方法
2. 对比不同实现的时间复杂度和空间复杂度
3. 提供错误处理机制(如输入负数检查)
4. 通过实际运行展示不同算法的性能差异
5. 给出不同实现方法的适用场景建议

需要重点关注的是代码中可能存在的问题,如边界情况处理、错误处理完善性、性能优化点以及代码健壮性等方面。
相关推荐
IT_陈寒7 分钟前
Java线程池用错参数,我的服务居然悄悄崩溃了
前端·人工智能·后端
光影少年18 分钟前
Express 中间件原理
后端·node.js·express
aramae1 小时前
MySQL内置函数(7)
开发语言·笔记·后端·mysql·其他
徐小夕2 小时前
存量.doc 文档怎么办?JitWord 开源转换库打通旧文档到在线协同全链路
后端·架构·github
ee又momo2 小时前
一次 JWT Token 过期续期的踩坑记录
后端
五彩小白2 小时前
GO语言装饰器语法
golang
二月龙2 小时前
App 瘦身实战:图片、so 库、资源压缩,安装包体积减小 40%
后端
大勇前进2 小时前
Kotlin 协程实战避坑:90% 的人都会踩的生命周期泄漏问题
后端
苏三说技术3 小时前
Spring AI、LangChain4j、AgentScope、Embabel,哪个AI框架更好?
后端
烽学长3 小时前
(附源码)基于Springboot+vue的图书阅读分享系统的设计与实现
java·spring boot·后端