news 2026/6/11 10:53:03

[MAF的Harness-02]HarnessAgent究竟整合了哪些Harness手段?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[MAF的Harness-02]HarnessAgent究竟整合了哪些Harness手段?

正如穷举MAF所有可能的Harness手段所说,LangChain平台预定义的Harness手段整合在Deep Agents中,具体体现在利用create_deep_agent方法创建的Deep Agent中。MAF则将这些Harness手段整合在HarnessAgent这个Agent中间件中,HarnessAgent就是MAF中的Deep Agent。由于Agent的特性和能力决定于底层构建的Agent管道,所以可用的Harness手段是Agent管道定制手段的子集。前面一篇文章穷举了所有用于定制Agent管道的手段,主要体现在如下三个方面。

  • 注册Agent中间件
  • 配置ChatClientOptions,其中包括但不限于:
    • 注册用于持久化对话历史的ChatMessageHistoryProvider
    • 注册用于LLM输入输出增强的AIContextProvider
    • ChatOptions上指定系统指令
    • ChatOptions上注册工具
  • 注册ChatClient中间件
    接下来我们将看看究竟有哪些手段被使用在HarnessAgent`中。

1. 被HarnessAgent代理的是个怎样的Agent?

针对上述的三个方面,我们来剖析一下使用默认配置选项创建的HarnessAgent,它注册了哪些Agent中间件和ChatClient中间件,它绑定了怎样的ChatClientOptions。为此我们定义了如下这个静态的辅助方法PrettyPrintAgent,在这个方法中我们利用反射获取了HarnessAgent中注册的Agent中间件、ChatClient中间件以及ChatClientAgentOptions的详细信息,并以较为友好的格式打印出来。

staticvoidPrettyPrintAgent(DelegatingAIAgentharnessAgent){PrintAgentMiddlewares(harnessAgent,true);var(chatClientAgent,agentOptions)=PrintAgentOptions(harnessAgent);PrintChatClientMiddlewares(chatClientAgent.ChatClient,true);Console.WriteLine($"\nInstructions: \n{agentOptions.ChatOptions?.Instructions}");}staticvoidPrintAgentMiddlewares(DelegatingAIAgent?harnessAgent,booldisplayTitle=false){if(displayTitle)Console.WriteLine("AgentMiddlewares:");if(harnessAgentisnotnull){Console.WriteLine($"{harnessAgent.GetType().Name}");PrintAgentMiddlewares(GetNonPublicPropertyFieldValue(harnessAgent,"InnerAgent")asDelegatingAIAgent);}}static(ChatClientAgent,ChatClientAgentOptions)PrintAgentOptions(DelegatingAIAgent?harnessAgent){ChatClientAgent?chatClientAgent=null;object?agent=harnessAgent;while(chatClientAgentisnull){agent=GetNonPublicPropertyFieldValue(agent!,"InnerAgent");chatClientAgent=agentasChatClientAgent;}varagentOptions=(ChatClientAgentOptions)GetNonPublicPropertyFieldValue(chatClientAgent!,"_agentOptions")!;Console.WriteLine($""" ChatClientAgentOptions:Id:{agentOptions.Id??"null"}Name:{agentOptions.Name??"null"}Description:{agentOptions.Description??"null"}UseProvidedChatClientAsIs:{agentOptions.UseProvidedChatClientAsIs}ClearOnChatHistoryProviderConflict:{agentOptions.ClearOnChatHistoryProviderConflict}WarnOnChatHistoryProviderConflict:{agentOptions.WarnOnChatHistoryProviderConflict}ThrowOnChatHistoryProviderConflict:{agentOptions.ThrowOnChatHistoryProviderConflict}RequirePerServiceCallChatHistoryPersistence:{agentOptions.RequirePerServiceCallChatHistoryPersistence}EnableMessageInjection:{agentOptions.EnableMessageInjection}ChatHistoryProvider:{agentOptions.ChatHistoryProvider?.GetType().Name}AIContextProviders:""");foreach(varaiContextProviderinchatClientAgent.AIContextProviders!){Console.WriteLine($"{aiContextProvider.GetType().Name}");}varchatOptions=agentOptions.ChatOptions!;Console.WriteLine($""" ChatOptions:Temperature:{chatOptions.Temperature?.ToString()??"null"}MaxOutputTokens:{chatOptions.MaxOutputTokens?.ToString()??"null"}TopP:{chatOptions.TopP?.ToString()??"null"}TopK:{chatOptions.TopK?.ToString()??"null"}FrequencyPenalty:{chatOptions.FrequencyPenalty?.ToString()??"null"}PresencePenalty:{chatOptions.PresencePenalty?.ToString()??"null"}Seed:{chatOptions.Seed?.ToString()??"null"}Reasoning:Effort:{chatOptions.Reasoning?.Effort?.ToString()??"null"}Output:{chatOptions.Reasoning?.Output?.ToString()??"null"}ResponseFormat:{chatOptions.ResponseFormat?.ToString()??"null"}ModelId:{chatOptions.ModelId?.ToString()??"null"}StopSequences:{string.Join(',',chatOptions.StopSequences??[])}AllowMultipleToolCalls:{chatOptions.AllowMultipleToolCalls?.ToString()??"null"}ToolMode:{chatOptions.ToolMode?.ToString()??"null"}Tools:""");foreach(vartoolinchatOptions.Tools??[]){Console.WriteLine($"{tool.GetType().Name}");}return(chatClientAgent,agentOptions);}staticvoidPrintChatClientMiddlewares(IChatClient?chatClient,booldisplayTitle=false){if(displayTitle)Console.WriteLine("\nChatClientMiddlewares:");if(chatClientisDelegatingChatClientmiddleware){Console.WriteLine($"{chatClient.GetType().Name}");PrintChatClientMiddlewares(GetNonPublicPropertyFieldValue(chatClient,"InnerClient")asIChatClient);}}staticobject?GetNonPublicPropertyFieldValue(objectobj,stringpropertyName){vartype=obj.GetType();varproperty=type.GetProperty(propertyName,System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance);if(propertyisnotnull){returnproperty.GetValue(obj);}varfield=type.GetField(propertyName,System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance);returnfield?.GetValue(obj);}

在如下的演示程序中,我们将创建的OpenAIClient对象转换成IChatClient对象后,直接调用AsHarnessAgent方法创建了一个默认配置选项的HarnessAgent,并将其传入PrettyPrintAgent方法中进行剖析。

usingdotenv.net;usingMicrosoft.Agents.AI;usingMicrosoft.Extensions.AI;usingOpenAI;usingSystem.ClientModel;DotEnv.Load();varmodel=Environment.GetEnvironmentVariable("MODEL")!;varapiKey=Environment.GetEnvironmentVariable("API_KEY")!;varendpoint=Environment.GetEnvironmentVariable("OPENAI_URL")!;varharnessAgent=newOpenAIClient(newApiKeyCredential(apiKey),newOpenAIClientOptions{Endpoint=newUri(endpoint)}).GetChatClient(model:model).AsIChatClient().AsHarnessAgent(maxContextWindowTokens:128000,maxOutputTokens:16000);PrettyPrintAgent(harnessAgent);

输出:

AgentMiddlewares: HarnessAgent ToolApprovalAgent OpenTelemetryAgent ChatClientAgentOptions: Id: null Name: null Description: null UseProvidedChatClientAsIs: True ClearOnChatHistoryProviderConflict: True WarnOnChatHistoryProviderConflict: False ThrowOnChatHistoryProviderConflict: False RequirePerServiceCallChatHistoryPersistence: True EnableMessageInjection: False ChatHistoryProvider: InMemoryChatHistoryProvider AIContextProviders: TodoProvider AgentModeProvider FileMemoryProvider FileAccessProvider AgentSkillsProvider ChatOptions: Temperature: null MaxOutputTokens: 16000 TopP: null TopK: null FrequencyPenalty: null PresencePenalty: null Seed: null Reasoning: Effort: null Output: null ResponseFormat:null ModelId: null StopSequences: AllowMultipleToolCalls: null ToolMode: null Tools: HostedWebSearchTool ChatClientMiddlewares: FunctionInvokingChatClient MessageInjectingChatClient PerServiceCallChatHistoryPersistingChatClient AIContextProviderChatClient Instructions: You are a helpful AI assistant that uses tools to complete tasks. ## General guidelines - Think through the task before acting. Break complex work into clear steps. - Use the tools available to you to gather information, perform actions, and verify results. - Explain your reasoning and thought process as you work through tasks. - Explain what you learned and what you are going to do next between tool calls, so the user can follow along with your thought process. - Avoid making more than 4 tool calls in a row without explaining what you are doing. - If a tool call fails or returns unexpected results, adapt your approach rather than repeating the same call. - When you have completed the task, present a clear and concise summary of what you did and what you found.

2. 剖析默认的HarnessAgent

我们通过上面演示程序的输出可用看出它注册的Agent中间件和ChatClient中间件,绑定了怎样的ChatClientAgentOptions,以及默认指定的系统指令。具体来说,它默认注册了如下的Agent中间件:

  • ToolApprovalAgent:实现了dont ask again模式的工具调用审批,详细内容可参考我的文章TodoProvider:用TodoList驱动Agent的任务执行
  • OpenTelemetryAgent:实现了针对Agent调用的调用链跟踪和针对LLM调用和Token使用的指标收集,详细内容可参考我的文章OpenTelemetryAgent:基于Agent的调用链跟踪和性能监控

默认注册了InMemoryChatHistoryProvider,实现了基于内存存储的对话历史持久化。由于默认注册了HostedWebSearchTool,所以Agent可以使用网络搜索。注册的AIContextProvider包括:

  • TodoProvider:提供了基于TodoList的推理和任务解决方法,详细内容可参考我的文章TodoProvider:用TodoList驱动Agent的任务执行
  • AgentModeProvider:实现在根据当前推理任务自由切换适合的行为模式,详细内容可参考我的文章AgentModeProvider:自由切换行为模式
  • FileMemoryProvider:在Session范围内提供了基于文件存储的短期记忆,详细内容可参考我的文章FileMemoryProvider:为Agent提供可解释、可回溯的记忆能力
  • FileAccessProvider:为Agent提供了一个文件系统,详细内容可参考我的文章FileAccessProvider:为Agent提供文件读写能力
  • AgentSkillsProvider:将Agent Skill引入MAF,详细内容可参考我的文章AgentSkillsProvider:将Skills引入MAF

除了我们用于连接指定LLM的IChatClient对象,ChatClient管道还包含如下的中间件:

  • FunctionInvokingChatClient:实现最为核心的ReAct循环和基于人机交互的工具调用审批功能,详细内容可参考我的文章FunctionInvokingChatClient:ReAct循环和工具审批实现者
  • MessageInjectingChatClient:赋予了工具函数动态注入消息的能力,详细内容可参考我的文章MessageInjectingChatClient:赋予工具消息注入的能力
  • PerServiceCallChatHistoryPersistingChatClient:实现基于ReAct循环的一步一存档,详细内容可参考我的文章PerServiceCallChatHistoryPersistingChatClient:一步一存档
  • AIContextProviderChatClient:利用注册的AIContextProvider来对请求消息进行再加工,详细内容可参考我的文章动态修改对话配置的两种解决方案

3. 系统指令解读

我们可以进一步解读一下HarnessAgent默认提供的系统指令,完整的指令文本如下:

You are a helpful AI assistant that uses tools to complete tasks. ## General guidelines - Think through the task before acting. Break complex work into clear steps. - Use the tools available to you to gather information, perform actions, and verify results. - Explain your reasoning and thought process as you work through tasks. - Explain what you learned and what you are going to do next between tool calls, so the user can follow along with your thought process. - Avoid making more than 4 tool calls in a row without explaining what you are doing. - If a tool call fails or returns unexpected results, adapt your approach rather than repeating the same call. - When you have completed the task, present a clear and concise summary of what you did and what you found.

这段系统指令的核心目的,是为了抑制LLM的鲁莽幻觉,将其塑造成一个高可靠性、有条理、可审计的交付型专家

3.1 任务前置思考与拆解设

强制开启思维链(CoT:Chain of Thought)机制。LLM在直接生成代码或执行命令时容易管中窥豹。通过这条指令,要求 Agent 在调用任何工具前,必须先在内部生成一个全局的任务规划。将长周期、复杂的模糊任务拆解为确定性的SOP(标准作业程序),能呈数倍地提升最终任务的成功率。

Think through the task before acting. Break complex work into clear steps.

3.2 闭环执行与结果验证

构建信息获取=>执行动作=>结果验证的闭环思维。传统LLM往往自信且盲目,生成结果后便认为任务已结束。此指令的核心在于verify results(验证结果)。它要求Agent在执行完动作(如:写入文件、调用 API、修改配置)后,必须主动调用检查工具(如:读取文件、运行测试、查看状态)来确认动作是否真的生效,属于典型的**防呆(Poka-yoke)**设计。

Use the tools available to you to gather information, perform actions, and verify results.

3.3 白盒化推理过程设计意图

提高Agent的可解释性与可观测性。将LLM的隐式推理转变为显式文本。一方面,这方便人类用户在中途或事后进行审计,了解Agent为什么这么做;另一方面,LLM把思考过程写出来本身就能作为其下一步行动的上下文,降低后续决策的出错率。

Explain your reasoning and thought process as you work through tasks.

3.4 动态上下文桥接设计意图

保持用户与Agent的信息同步,建立信任感。在连续的ReAct迭代中,LLM容易陷入无声的底层逻辑中。这条指令强制Agent在两次工具调用中间充当解说员——既是对上一步工具返回结果的总结提炼,又是对下一步行动的预告。这种走一步、看一步、说一步的节奏,是多步复杂Agent的标准控制范式。

Explain what you learned and what you are going to do next between tool calls, so the user can follow along with your thought process.

3.5 熔断机制

硬性限制,防止Agent陷入幻觉死循环工具滥用。在自动化运维、代码生成中,Agent极易因为某个长尾Bug导致疯狂调用工具(例如死循环地ls或尝试编译)。限制连续调用不得超过4次,强制其必须停下来向人类汇报解释,起到了安全熔断器的作用,不仅保护了用户的系统,也节省了Token资费。

Avoid making more than 4 tool calls in a row without explaining what you are doing.

3.6 容错与动态自适应

打破LLM的惯性思维。原生LLM在遇到报错时,本能反应常常是复读机式地用完全相同的参数重新尝试,这会导致无意义的重试。指令强制要求其在失败时必须调整策略,例如换一个工具、修改调用参数、或者退回上一步重新规划。

If a tool call fails or returns unexpected results, adapt your approach rather than repeating the same call.

3.7 结构化交付

提供结构化的最终交付物。复杂任务执行完后,底层的日志和工具返回数据可能成千上万行。人类用户不需要看冗长的过程,只需要看结论。这条指令要求Agent在收尾时进行信息提炼,提供做了什么(过程摘要)发现了什么/产出了什么(结果交付)的高干预度报告。

When you have completed the task, present a clear and concise summary of what you did and what you found.

MAF的这套Harness指令,是典型的高约束、高透明、注重安全与闭环的工业级Agent提示词模板。它将LLM从一个纯聊天的AI,规范成了一个步步有回应、件件有着落、出错懂绕行、事毕有归档的靠谱虚拟员工。

4. HarnessAgent的进一步定制

到目前为止,我们已经指导采用默认配置选项创建的HarnessAgent,其背后的Agent管道大体长什么样子。接下来我们将看看如何通过使用HarnessAgentOptions对这个管道做出进一步的定制。从上面的输出苦于看出,对于默认的HarnessAgent,其Id、Name和Description都是null,这些都可以通过HarnessAgentOptions如下所示的三个属性来指定。

publicsealedclassHarnessAgentOptions{publicstring?Id{get;set;}publicstring?Name{get;set;}publicstring?Description{get;set;}}

如何我们觉得默认的系统指令不够好,可以通过HarnessInstructions属性指定新的指令。由于默认使用InMemoryChatHistoryProvider来持久化对话历史,再生成环境一般会利用ChatHistoryProvider属性设置一个支持分布式部署环境的ChatHistoryProvider。通过AIContextProviders属性提供的AIContextProvider会添加(不是覆盖)到Agent的AIContextProvider列表中。我们也可以对ChatOptions做进一步设置,比如注册工具以及其他与LLM交互相关的参数设置。

publicsealedclassHarnessAgentOptions{publicChatOptions?ChatOptions{get;set;}publicstring?HarnessInstructions{get;set;}publicChatHistoryProvider?ChatHistoryProvider{get;set;}publicIEnumerable<AIContextProvider>?AIContextProviders{get;set;}

HarnessAgentOptions提供了如下这些布尔类型的属性,它们会作为开关开启或者关闭某些特性。

publicsealedclassHarnessAgentOptions{publicboolDisableToolApproval{get;set;}publicboolDisableFileMemory{get;set;}publicboolDisableFileAccess{get;set;}publicboolDisableWebSearch{get;set;}publicboolDisableTodoProvider{get;set;}publicboolDisableAgentModeProvider{get;set;}publicboolDisableAgentSkillsProvider{get;set;}publicboolDisableOpenTelemetry{get;set;}}

这些开关说明如下:

  • DisableToolApproval:关闭基于don’t ask again模式的工具调用审批功能,也就是不再注册ToolApprovalAgent中间件;
  • DisableFileMemory:关闭基于文件存储的短期记忆功能,也就是不再注册FileMemoryProvider这个AIContextProvider
  • DisableFileAccess:关闭文件系统访问功能,也就是不再注册FileAccessProvider这个AIContextProvider
  • DisableWebSearch:关闭网络搜索功能,也就是不再注册HostedWebSearchTool这个服务端执行的工具。
  • DisableTodoProvider:关闭基于TodoList的推理和任务解决方法,也就是不再注册TodoProvider这个AIContextProvider
  • DisableAgentModeProvider:关闭Agent行为模式提供功能,也就是不再注册AgentModeProvider这个AIContextProvider
  • DisableAgentSkillsProvider:关闭Agent Skills提供功能,也就是不再注册AgentSkillsProvider这个AIContextProvider
  • DisableOpenTelemetry:关闭OpenTelemetry功能,也就是不再注册OpenTelemetryAgent中间件。
  • OpenTelemetrySourceName:指定OpenTelemetry的SourceName,只有在DisableOpenTelemetry为false时才会生效。

HarnessAgentOptions还提供了如下这些属性:

publicsealedclassHarnessAgentOptions{publicint?MaximumIterationsPerRequest{get;set;}publicAgentFileStore?FileMemoryStore{get;set;}publicAgentFileStore?FileAccessStore{get;set;}publicAgentModeProviderOptions?AgentModeProviderOptions{get;set;}publicAgentSkillsSource?AgentSkillsSource{get;set;}publicstring?OpenTelemetrySourceName{get;set;}publicIEnumerable<AIAgent>?BackgroundAgents{get;set;}publicBackgroundAgentsProviderOptions?BackgroundAgentsProviderOptions{get;set;}publicShellEnvironmentProviderOptions?ShellEnvironmentProviderOptions{get;set;}publicShellExecutor?ShellExecutor{get;set;}}

这些属性说明如下:

  • MaximumIterationsPerRequest:限制每次请求的最大迭代次数,迭代指的是ReAct循环中的工具调用次数。当达到这个限制时,Agent会停止继续调用工具并返回当前的结果。
  • FileMemoryStore:指定用于FileMemoryProviderAgentFileStore实例;
  • FileAccessStore:指定用于FileAccessProviderAgentFileStore实例;
  • AgentModeProviderOptions:指定AgentModeProvider的配置选项;
  • AgentSkillsSource:指定AgentSkillsProvider的技能来源;
  • OpenTelemetrySourceName:指定OpenTelemetrySourceName
  • BackgroundAgents:指定一组会在HarnessAgent内部作为后台Agent运行的AIAgent实例,如果设置,将会据此注册一个BackgroundAgentsProvider实现异步执行这组SubAgent
  • BackgroundAgentsProviderOptions:为BackgroundAgentsProvider指定的配置选项;
  • ShellEnvironmentProviderOptions:如果设置,将会据此注册一个ShellEnvironmentProviderLLM提供Shell环境信息;
  • ShellExecutor:为ShellEnvironmentProvider指定的ShellExecutor实例用来探测Shell执行环境信息;
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 10:52:16

3步解决多数据库迁移难题:SQLines实战完整指南

3步解决多数据库迁移难题&#xff1a;SQLines实战完整指南 【免费下载链接】sqlines SQLines Open Source Database Migration Tools 项目地址: https://gitcode.com/gh_mirrors/sq/sqlines 数据库迁移是现代软件开发中最具挑战性的任务之一。当你的应用需要从Oracle迁移…

作者头像 李华
网站建设 2026/6/11 10:50:13

STM32F302CB上用FreeRTOS+DMA实现SBUS/PPM遥控数据稳定接收与解析

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;基于STM32F302CB芯片&#xff0c;采用FreeRTOS实时操作系统配合DMA方式驱动UART外设&#xff0c;实现串口数据的环形缓冲循环接收&#xff0c;彻底规避CPU轮询&#xff0c;保障高频率遥控信号&#xff08;如SBU…

作者头像 李华
网站建设 2026/6/11 10:47:09

0610鸿蒙上课

用户登录界面注册界面完善个人信息界面按钮界面图片轮播图图片文本作业

作者头像 李华
网站建设 2026/6/11 10:47:02

如何通过开源工具提升开发效率:OpenSpeedy项目的完整指南

如何通过开源工具提升开发效率&#xff1a;OpenSpeedy项目的完整指南 【免费下载链接】OpenSpeedy &#x1f3ae; An open-source game speed modifier. 项目地址: https://gitcode.com/gh_mirrors/op/OpenSpeedy 在当今快节奏的开发环境中&#xff0c;你是否曾经遇到过…

作者头像 李华