经典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. 给出不同实现方法的适用场景建议

需要重点关注的是代码中可能存在的问题,如边界情况处理、错误处理完善性、性能优化点以及代码健壮性等方面。
相关推荐
Chasing Aurora1 小时前
Python后端开发之旅(二)
开发语言·python·语言模型·langchain·ai编程
archko1 小时前
用rust写了一个桌面app,就不再想用kmp了
开发语言·后端·rust
华仔啊1 小时前
RabbitMQ 的 6 种工作模式你都掌握了吗?附完整可运行代码
java·后端·rabbitmq
星释1 小时前
Rust 练习册 109:深入探索列表关系判断
开发语言·后端·rust
老华带你飞1 小时前
作业管理|基于Java作业管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端
星释1 小时前
Rust 练习册 110:探索倍数之和的数学之美
开发语言·后端·rust
fanruitian2 小时前
springboot-mybatisplus-demo
spring boot·后端·mybatis·mybatisplus
倔强菜鸟2 小时前
2025.11.21-GO语言入门(一)
开发语言·后端·golang
风生u2 小时前
Go: Gin的用法
golang·xcode·gin