背景
前段时间看到了一个开源项目
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)从简单的单次生成器提升为更为有意识和稳健的推理器。反思型代理不仅仅提供最初的答案,而是退一步,批判、分析并完善自己的工作。这种自我提升的迭代过程是构建更可靠、更高质量人工智能系统的基石。
定义
反射架构涉及代理在返回最终答案前对自身输出进行批评和修订。它不是单次生成,而是进行多步骤的内部独白:生产、评估和改进。这模拟了人类起草、审阅和编辑的过程,以发现错误并提升质量。
工作流程
- 生成 (Generate): 智能体根据用户提示生成初始草稿或解决方案。
- 批评 (Critique): 智能体切换角色为批评者,进行自我提问,例如:"这个答案有什么问题?"、"是否最优?"、"有没有逻辑错误或 Bug?"
- 完善 (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. 给出不同实现方法的适用场景建议
需要重点关注的是代码中可能存在的问题,如边界情况处理、错误处理完善性、性能优化点以及代码健壮性等方面。