[Loop Engineering在MAF中的实现-05]MAF预定义的评估器
除了利用AIJudgeLoopEvaluator让LLM当评委介绍的AIJudgeLoopEvaluator,MAF还提供了其他几种继承自LoopEvaluator的评估器类型,接下来我们会逐个介绍它们。
1. DelegateLoopEvaluator
DelegateLoopEvaluator非常简单,就是利用提供的Func<LoopContext, CancellationToken, ValueTask<LoopEvaluation>>类型的委托来实现EvaluateAsync方法的评估工作。
publicsealedclassDelegateLoopEvaluator:LoopEvaluator{privatereadonlyFunc<LoopContext,CancellationToken,ValueTask<LoopEvaluation>>_evaluate;publicDelegateLoopEvaluator(Func<LoopContext,CancellationToken,ValueTask<LoopEvaluation>>evaluate)=>_evaluate=evaluate;publicoverrideValueTask<LoopEvaluation>EvaluateAsync(LoopContextcontext,CancellationTokencancellationToken=default)=>_evaluate(context,cancellationToken);}2. CompletionMarkerLoopEvaluator
CompletionMarkerLoopEvaluator旨在确定任务是否结束来决定循环调用是否继续。它的做法很简单,在调用Agent提供的提示词中明确告诉LLM:如果它认为任务已经完成,就在返回的响应文本中包含指定的完成标记(Completion Marker)。CompletionMarkerLoopEvaluator通过确定最终的响应文本是否包含此标记来生成最终的LoopEvaluation。
publicsealedclassCompletionMarkerLoopEvaluator:LoopEvaluator{publicCompletionMarkerLoopEvaluator(stringcompletionMarker,CompletionMarkerLoopEvaluatorOptions?options=null);publicoverrideValueTask<LoopEvaluation>EvaluateAsync(LoopContextcontext,CancellationTokencancellationToken=default);}publicsealedclassCompletionMarkerLoopEvaluatorOptions{publicstring?FeedbackMessageTemplate{get;set;}}我们在调用构造函数的时候,通过completionMarker参数指定上述的完成标记,并通过提供可选的配置选项CompletionMarkerLoopEvaluatorOptions提供一个格式化反馈文本的模板。如果没有提供此配置选项,则使用如下这个默认模板,这就是我们上面介绍的让LLM在认为推理任务结束时在响应文本放置完成标记的提示词。如果原始的请求中没有类似的提示词,意味着针对首次迭代的评估必然会失败。
Continue working on the request. When you have fully completed the task, end your response with the marker '{completion_marker}' to indicate completion.下面的演示程序很好地体现了CompletionMarkerLoopEvaluator的评估逻辑。该程序同时指明了,CompletionMarkerLoopEvaluator在进行字符比较时,针对大小写是敏感的。
varevaluator1=newCompletionMarkerLoopEvaluator(completionMarker:"DONE");varevaluator2=newCompletionMarkerLoopEvaluator(completionMarker:"done");varcontext=newLoopContext(agent:newFakeAgent(),session:newFakeSession(),initialMessages:[],lastResponse:newAgentResponse(newChatMessage(ChatRole.Assistant,"DONE")));varevaluation=awaitevaluator1.EvaluateAsync(context);Debug.Assert(!evaluation.ShouldReinvoke);Debug.Assert(evaluation.Feedbackisnull);evaluation=awaitevaluator2.EvaluateAsync(context);Debug.Assert(evaluation.ShouldReinvoke);Debug.Assert(evaluation.Feedback=="Continue working on the request. When you have fully completed the task, end your response with the marker 'done' to indicate completion.");3. TodoCompletionLoopEvaluator
TodoCompletionLoopEvaluator会结合TodoProvider和AgentModeProvider一起使用,它们都是MAF两个自定义的AIContextProvider类型。前者利用提供的系统指令和工具指导和帮助LLM采用TodoList的模式执行任务,后者则为根据不同阶段的任务特性指定针对性的推理模式。在我的文章TodoProvider:用TodoList驱动Agent的任务执行和AgentModeProvider:自由切换行为模式具有这对这两个AIContextProvider类型的详细介绍。
如果采用TodoCompletionLoopEvaluator,意味着在执行任务的之前会制定一份TodoList,并按部就班地按照它来实施。TodoCompletionLoopEvaluator旨在避免在某些事项尚未执行就停止循环。如果同时还同时使用了AgentModeProvider,并且只希望LoopAgent只在指定的模式下才进行后续迭代,我们还可以利用TodoCompletionLoopEvaluatorOptions设置这个模式列表。
publicsealedclassTodoCompletionLoopEvaluatorOptions{publicIEnumerable<string>?Modes{get;set;}publicstring?FeedbackMessageTemplate{get;set;}}除了用于指定允许继续迭代的模式列表的Modes属性外,配置选项类型TodoCompletionLoopEvaluatorOptions还提供了FeedbackMessageTemplate属性用来定义评估反馈的文本模板。如果没有显式指定,默认采用如下的模板文本,占位符{remaining_todos}表示尚未完成的Todo事项。
You still have incomplete todo items. Continue working until every item is complete, marking each item as complete when finished. The following items are still open: {remaining_todos}TodoCompletionLoopEvaluator的构造函数具有唯一的可选参数options。如果没有通过这个参数显式指定一个TodoCompletionLoopEvaluatorOptions对象,意味着TodoCompletionLoopEvaluator的评估将不会考虑推理模式的因素。
publicsealedclassTodoCompletionLoopEvaluator:LoopEvaluator{publicTodoCompletionLoopEvaluator(TodoCompletionLoopEvaluatorOptions?options=null);publicoverrideasyncValueTask<LoopEvaluation>EvaluateAsync(LoopContextcontext,CancellationTokencancellationToken=default);}重写的EvaluateAsync会采用如下的方式实施评估:
- 如果设置了运行继续迭代的推理模式列表,利用
LoopContext上下文获取当前AIAgent,并调用其GetService<AgentModeProvider>方法获取注册的AgentModeProvider:- 如果
AgentModeProvider不存在,直接抛出异常; - 否则则继续调用它的
GetMode方法得到当前的推理模式,如果此推理模式不在设定的模式列表中,直接返回一个ShouldReinvoke为false的LoopEvaluation终止循环;
- 如果
- 按照相同的方式得到注册的
TodoProvider,- 如果
TodoProvider不存在,直接抛出异常; - 否则调用其
GetRemainingTodosAsync方法返回尚未完成的Todo事项:- 如果存在,利用设置的或者默认的模板生成反馈文本,并封装在返回的
ShouldReinvoke为true的LoopEvaluation中; - 否则返回一个
ShouldReinvoke为false的LoopEvaluation终止循环。
- 如果存在,利用设置的或者默认的模板生成反馈文本,并封装在返回的
- 如果
4. BackgroundTaskCompletionLoopEvaluator
BackgroundTaskCompletionLoopEvaluator和TodoCompletionLoopEvaluator其实有点类似,它们的目的都是为了避免在有些必要的任务尚未执行前就终止循环。具体来说,BackgroundTaskCompletionLoopEvaluator与BackgroundAgentsProvider这个AIContextProvider有关。BackgroundAgentsProvider提供了一系列的工具,使我们可以采用后台任务的形式执行指定的AIAgent,在我的文章BackgroundAgentsProvider:异步执行SubAgent对进行了详细介绍。
BackgroundTaskCompletionLoopEvaluator的目的在于:如果当前具有还在后台执行的任务,就不能终止循环。如下面的代码所示,我们创建BackgroundTaskCompletionLoopEvaluator时可以通过指定对应的BackgroundTaskCompletionLoopEvaluatorOptions,并利用后者的FeedbackMessageTemplate提供一个用于格式化反馈文本的模板。
publicsealedclassBackgroundTaskCompletionLoopEvaluator:LoopEvaluator{publicBackgroundTaskCompletionLoopEvaluator(BackgroundTaskCompletionLoopEvaluatorOptions?options=null);publicoverrideasyncValueTask<LoopEvaluation>EvaluateAsync(LoopContextcontext,CancellationTokencancellationToken=default);}publicsealedclassBackgroundTaskCompletionLoopEvaluatorOptions{publicstring?FeedbackMessageTemplate{get;set;}}如果反馈文本模板没有显示指定,默认会采用如下所示的模板文本,占位符{incomplete_task_count}表示尚未完成的后台任务的数量。
You still have {incomplete_task_count} background task(s) running that must finish before you can complete the work:\ {incomplete_tasks} Wait for these tasks to complete, retrieve their results, and incorporate them. Only stop once every background task has finished.重写的EvaluateAsync会按照如下的方式实施评估:
- 利用
LoopContext上下文获取当前AIAgent,并调用其GetService<BackgroundAgentsProvider>方法获取注册的BackgroundAgentsProvider; - 如果
BackgroundAgentsProvider不存在,直接抛出异常; - 否则调用
BackgroundAgentsProvider的GetIncompleteTasks方法的到尚未完成的后台任务:- 如果任务列表不为空:利用模板格式化反馈文本,并封装在返回的
ShouldReinvoke为true的LoopEvaluation中; - 否则返回一个
ShouldReinvoke为false的LoopEvaluation终止循环。
- 如果任务列表不为空:利用模板格式化反馈文本,并封装在返回的
