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

需要重点关注的是代码中可能存在的问题,如边界情况处理、错误处理完善性、性能优化点以及代码健壮性等方面。
相关推荐
程序员敲代码吗1 分钟前
Spring Boot与Tomcat整合的内部机制与优化
spring boot·后端·tomcat
参.商.28 分钟前
【Day 27】121.买卖股票的最佳时机 122.买卖股票的最佳时机II
leetcode·golang
牛奔31 分钟前
如何理解 Go 的调度模型,以及 G / M / P 各自的职责
开发语言·后端·golang
chilavert31833 分钟前
技术演进中的开发沉思-357:重排序(下)
java·后端
Boop_wu40 分钟前
Spring生态
java·后端·spring
jzheng861041 分钟前
Spring Boot(快速上手)
java·spring boot·后端
怒放吧德德1 小时前
Python3基础:基础实战巩固,从“会用”到“活用”
后端·python
量子-Alex1 小时前
【大模型RLHF】Training language models to follow instructions with human feedback
人工智能·语言模型·自然语言处理
阿杰学AI2 小时前
AI核心知识92——大语言模型之 Self-Attention Mechanism(简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·aigc·transformer·自注意力机制
苏三说技术2 小时前
xxl-job 和 elastic-job,哪个更好?
后端