.NET+AI | Harness | MAF 1.4 发布,Harness Engineering 如约而至,智能体工程化更进一步

从 Harness Engineering 到 MAF 1.4:微软是怎么把智能体开发往工程化上推了一步

很多人第一次看到 Harness 这个词,会把它理解成一个新功能,或者一个新的任务编排组件。

这样理解不算错,但只停在这里,基本会把它看小。

因为当智能体还只是"调一次模型,回一段答案"的时候,Prompt、Tool Calling、Memory 这些词确实差不多够用了。但一旦智能体开始进入真实任务,比如连续执行多步操作、访问文件系统、管理中间状态、处理中断、接受审批,甚至把部分任务委托给子智能体,问题就变了。

这时候真正决定系统能不能落地的,往往已经不是模型本身,而是模型外面那层执行骨架。

这层骨架,就是 Harness。

而 MAF 1.4 值得看的地方,也正是在这儿。它补的不是一个零散能力点,而是一层更接近工程现实的运行时控制面。

这篇文章我想按一个很顺的顺序来讲四件事:

  1. 什么是 Harness Engineering
  2. 它为什么会变成智能体工程里的关键概念
  3. MAF 1.4 是怎么把这套机制落下来的
  4. 我们怎么通过一个简单的 .NET 示例,看懂 Harness 的价值

一、什么是 Harness Engineering

先说我更认同的一个定义。

结合现有资料来看,Harness Engineering 本质上是在模型外面补一层"可执行的脚手架",把 AI 的能力约束成稳定产出。它不只是让模型更聪明,而是让系统更可执行。

再直白一点:

Harness Engineering 关心的,不是"模型会不会回答",而是"智能体能不能稳定把事情做完"。

如果按现有 Wiki 里的定义来压缩,它至少要满足四件事:有输入、有步骤、有验收、有兜底。

我觉得这个定义很准。

因为智能体一旦进入真实世界,马上就会遇到几类很具体的问题。

  1. 它怎么接任务

    不是所有输入都应该直接扔给模型。有些是业务任务,有些是控制命令,有些只是环境反馈。

  2. 它怎么拆任务

    复杂任务不能全靠模型一口气想完。你得拆步骤,知道先做什么、后做什么。

  3. 它怎么管理状态

    任务做到一半,当前进度是什么,已经产出了什么,中间文件在哪,哪些步骤完成了,这些都不能只靠上下文硬记。

  4. 它怎么安全访问环境

    只要智能体开始读写文件、调用外部工具、修改资源,就一定会碰到权限边界、路径校验、审批和回滚。

  5. 它怎么处理长上下文

    短对话里模型还能勉强记住过程。长任务一跑起来,上下文越来越长,噪声越来越多,没有压缩和收敛机制,最后大概率会跑偏。

  6. 它怎么失败后恢复

    如果每次出错都只能整段重来,那这个系统就谈不上工程价值。

所以我更愿意把 Harness Engineering 看成一层"执行治理"。

如果说 Prompt Engineering 更接近"怎么提问",那么 Harness Engineering 更接近"怎么让系统按流程、安全、稳定地执行"。

这里可以先用一张图,把这个概念压缩一下。
flowchart TD A[用户目标] --> B[Harness 执行脚手架] B --> C[任务输入治理] B --> D[步骤拆解] B --> E[状态管理] B --> F[环境访问边界] B --> G[验收与兜底] C --> H[模型/Agent] D --> H E --> H F --> H G --> H

这张图想表达的核心很简单。

模型当然还是核心能力源,但真正让它变成工程系统的,是外面这层 Harness。

这一节先收一下。

Harness Engineering 不是给智能体多装一个功能插件,而是在给智能体补一套"可执行、可治理、可验收"的运行壳。它解决的不是"模型答得好不好",而是"系统跑得稳不稳"。

二、MAF 1.4 在做什么:把 Harness 拉进主线

理解了 Harness Engineering,再回头看 MAF 1.4,就更容易抓住重点。

很多人看到 release note 里出现 Harness,第一反应可能是:微软又加了个新特性。

但如果顺着现有材料往下看,我觉得更准确的判断是:

MAF 1.4 不是简单"支持了 Harness",而是开始把智能体开发从"能力拼装"往"执行系统工程化"推。

这件事为什么重要?

因为 MAF 之前已经在补另一条线,就是 Skills。

Skills 更像能力包。它解决的是:智能体会什么。比如给它一套 instructions、scripts、references、assets,让它具备某个领域能力。

而 Harness 解决的是另一件事:智能体怎么干。

这两者不是替代关系,而是前后关系。

Skills 解决"会什么"。

Harness 解决"怎么稳定地把事做完"。

这也是为什么我会觉得,MAF 的路线到 1.4 开始清楚了不少:先把能力模块化,再把执行工程化。

换句话说,Skills 更像能力封装层,Harness 更像运行时执行层。

这一节的小结也很关键。

如果你只把 Harness 当成一个 API,或者一个 sample,就会看小 MAF 1.4。它真正补上的,是一层更像"控制面"的东西。

三、MAF 1.4 是怎么实现这套机制的

接下来把视角从概念切到实现。

从现有两篇材料里能看到,MAF 1.4 至少在四个方向上给了很明确的信号。

1. 命令平面:把任务输入和控制输入分开

这是我觉得最值得注意的一点。

从材料来看,MAF 在 Harness 相关实现里引入了 ICommandHandler 这样的控制接口,同时暴露了像 /mode、/todos 这样的命令入口。

这意味着什么?

意味着框架开始明确区分两类输入:

  1. 要交给智能体执行的任务输入
  2. 要交给运行时处理的控制命令

这个区分非常关键。

因为一旦所有输入都被当成自然语言 prompt,系统就永远停留在"聊天盒子"阶段。你没法让用户清晰地查看模式、检查待办、切换执行状态,也很难构建稳定的运行时交互。

但一旦有了命令平面,智能体就不再只是一个对话对象,而是一个可被操控的执行单元。

这其实就是 Harness 思维的典型体现。

不是所有东西都交给模型理解,而是把一部分系统行为显式拉回框架侧处理。

2. 状态平面:把状态从上下文里解耦出来

第二个很明显的信号,是一批 Provider 的出现或增强,比如:

  • TodoProvider
  • AgentModeProvider
  • FileMemoryProvider
  • FileAccessProvider

单看名字就能看出来,这不是在单纯扩 API,而是在把原本可能混在上下文里的东西,拆成独立可治理的状态模块。

这件事为什么重要?

因为很多智能体 demo 都有一个通病:所有东西都塞进聊天历史。

任务计划塞进去。

中间结果塞进去。

执行状态塞进去。

文件访问记录也塞进去。

短任务还能撑住,任务一长,系统就开始失真。

而 TodoProvider、AgentModeProvider 这一类组件的意义,就是把"任务计划""当前模式""外部文件状态"这些要素,从对话历史中拆出来,变成显式管理对象。

这说明 MAF 正在从"单轮推理器"往"带状态机的执行体"走。

我觉得这是个很关键的变化。

因为真正能做长任务的智能体,一定不是只靠上下文堆起来的。它必须有显式状态层。

3. 上下文治理:开始正面处理长任务问题

第三个信号,是 context compaction strategy。

这个词听起来很工程化,但翻译成人话其实很简单:

任务长了,上下文不能无上限膨胀,必须有压缩、摘要和收敛机制。

这一点我很认同。

因为很多人现在谈智能体,还停留在"模型上下文变长了,所以长任务就能解决"的想象里。真正做过的人都知道,不是这样。

长上下文不是银弹。

上下文一长,真正的问题会变成:

  • 历史信息越来越多
  • 噪声越来越多
  • 模型开始抓不住重点
  • 前面做过的决策和后面的执行越来越容易脱节

所以一个能跑长任务的系统,必须主动处理上下文膨胀。

不是一股脑全塞进去,而是要知道什么该保留,什么该摘要,什么该收敛成状态。

MAF 在 Harness 里引入这类机制,说明它已经开始正面处理长任务执行的核心难题,而不是只停留在 sample 层。

4. 环境边界与委托能力:从"会调工具"走向"安全执行任务"

第四个方向也很重要。

材料里能看到一些明显的边界治理信号,比如:

  • path validation
  • approval helpers
  • FileAccessProvider
  • subagents provider

这几件事放在一起看,其实就能看出 MAF 的判断了:

智能体真正进入环境执行层后,问题不再是"它能不能调用工具",而是"它能不能在边界内、安全地调用工具"。

这是两回事。

会调用工具,只说明模型有动作能力。

会在权限边界内调用工具,才说明系统具备工程能力。

同样,subagents provider 也不是简单"支持多 Agent"这么轻描淡写。

它真正说明的是:框架开始为任务分工、委托执行和协同收敛预留统一入口。

这意味着 Harness 不只是单体 Agent 的壳,更是在为执行网络做准备。

这里我建议用一张图,把上面四层机制合起来看。
flowchart TD A[用户任务] --> B[Harness 控制层] B --> C[命令处理 Command Handlers] B --> D[状态管理 Providers] B --> E[上下文压缩与收敛] B --> F[环境访问与权限边界] B --> G[子智能体委托] C --> H[Agent Runtime] D --> H E --> H F --> H G --> H H --> I[模型推理] H --> J[工具调用] H --> K[任务结果]

这张图背后的判断是:

MAF 1.4 补的不是一个零散 API,而是一层真正的运行时控制面。

这一节收一下。

如果你从 Harness Engineering 的视角回看 MAF 1.4,会发现它在做四件很"工程"的事:区分命令与任务、显式管理状态、治理长上下文、建立环境边界和委托能力。这几件事放在一起,智能体才开始像一个系统,而不只是一个聊天玩具。

四、一个最小 .NET 代码示例:Harness 到底比普通调用多了什么

光讲概念还是有点虚。

所以这一节我想用一个最小代码示例,把这个问题讲透。

场景很简单。

给定多个研究主题,先收集原始材料,再生成摘要。

这个场景看起来不复杂,但很适合说明 Harness 的价值。因为它天然包含两个阶段:

  1. 收集信息
  2. 汇总结果

如果不用 Harness,我们通常会怎么写?

大概率就是写一个普通方法,里面顺序调用两个对象。这样当然能跑,但任务名、输入输出、执行入口、后续扩展点,都会比较松散。

而用了 Harness 之后,重点不是"少写几行代码",而是先把执行骨架立起来。

先看代码。

1. 先定义两个最小子能力

csharp 复制代码
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public class ResearchAgent
{
    public Task<string[]> CollectAsync(string[] topics, CancellationToken cancellationToken)
    {
        var results = topics
            .Select(topic => $"Collected raw information about: {topic}")
            .ToArray();

        return Task.FromResult(results);
    }
}

public class SummaryAgent
{
    public Task<string[]> SummarizeAsync(string[] rawResults, CancellationToken cancellationToken)
    {
        var summaries = rawResults
            .Select(item => $"Summary => {item}")
            .ToArray();

        return Task.FromResult(summaries);
    }
}

这段代码很简单。

ResearchAgent 负责"收集原始信息"。

SummaryAgent 负责"汇总成摘要"。

如果只是写 demo,到这里其实已经够了。但问题也在这里:这只是两个普通类,还没有形成一个明确的任务执行结构。

所以第二步,定义 Harness。

2. 用 Harness 把任务注册起来

csharp 复制代码
using Microsoft.MAF.Harness;

public class ResearchHarness : TaskHarness
{
    public ResearchHarness()
    {
        RegisterTaskHandler<string[], string[]>(
            "ResearchAndSummarize",
            async (topics, token) =>
            {
                var researcher = new ResearchAgent();
                var summarizer = new SummaryAgent();

                var rawResults = await researcher.CollectAsync(topics, token);
                var summaries = await summarizer.SummarizeAsync(rawResults, token);

                return summaries;
            });
    }
}

这段代码里,我觉得有 4 个点特别值得注意。

第一,任务名被显式定义了。

不是"随手调一个方法",而是注册了一个明确的任务:ResearchAndSummarize。

这件事在 demo 里看起来像形式感,但在工程里很重要。因为任务一旦被命名,后面你才能围绕它做日志、状态、监控、审批和调度。

第二,输入输出边界被显式定义了。

这里的输入是 string[] topics,输出是 string[] summaries。

这意味着任务边界开始清晰了。系统知道这个任务吃什么、吐什么。后面你要做校验、测试、扩展,就都有抓手。

第三,步骤结构被显式固化了。

先 CollectAsync,再 SummarizeAsync。

这听起来像废话,其实不是。因为很多智能体系统一开始都是靠 prompt 让模型"自己想步骤"。这种方式灵活,但不稳定。Harness 的价值之一,就是把那些已经稳定下来的执行骨架显式化。

第四,后续扩展点变清楚了。

现在这个示例很小,但你已经很容易想象后面能加什么:

  • 在 rawResults 之后加中间状态持久化
  • 在 SummarizeAsync 之前加人工审批
  • 在执行中写入 todo 更新
  • 在任务过长时做上下文压缩
  • 把 CollectAsync 拆给多个 subagent 并行执行

这就是 Harness 的意义。

它不是让今天这段代码突然变聪明,而是让明天这段代码更容易演进成系统。

3. 统一执行入口

接着看执行代码。

csharp 复制代码
var harness = new ResearchHarness();

var topics = new[]
{
    "Microsoft Agent Framework",
    "Harness Engineering",
    ".NET Agent Runtime"
};

var summaries = await harness.ExecuteTaskAsync<string[], string[]>(
    "ResearchAndSummarize",
    topics,
    CancellationToken.None);

Console.WriteLine("Final Summaries:");
foreach (var summary in summaries)
{
    Console.WriteLine(summary);
}

如果你只看表面,这段代码和普通方法调用差别不大。

但真正值得关注的,是它背后的组织方式已经变了。

以前可能是:

main 方法

→ new 两个对象

→ 顺序调用

→ 输出结果

现在则变成了:

统一执行入口

→ 按任务名调度

→ 按输入输出边界执行

→ 产出统一结果

这背后的变化是:你已经不只是"调用几个类",而是在"执行一个被框架托管的任务"。

为了更直观一点,我把这个最小示例再画成一张时序图。
sequenceDiagram participant U as 调用方 participant H as ResearchHarness participant R as ResearchAgent participant S as SummaryAgent U->>H: ExecuteTaskAsync("ResearchAndSummarize", topics) H->>R: CollectAsync(topics) R-->>H: rawResults H->>S: SummarizeAsync(rawResults) S-->>H: summaries H-->>U: final summaries

这张图看起来很朴素,但已经足够说明一个关键差别:

Harness 不是替你完成业务逻辑,而是替你把业务逻辑装进一个可注册、可执行、可扩展的任务壳里。

4. 这段代码为什么比普通方法调用更"工程化"

我想把这一点再讲透一点。

因为很多人看到这种示例,第一反应会是:

"这不就是把普通方法包了一层吗?"

从代码行数看,确实像。

但从工程视角看,不是。

区别主要在这里:

  1. 普通方法调用更像临时脚本

    你当然能跑通,但任务身份不明确,执行结构也没有被系统化管理。

  2. Harness 注册更像运行时契约

    任务名、输入输出、执行入口都开始标准化。

  3. 普通方法不天然具备治理入口

    你后面想加日志、审批、恢复、模式切换,往往要自己东拼西补。

  4. Harness 天然适合继续往"控制面"演进

    这才是 MAF 1.4 真正值得关注的地方。

所以这段示例代码最重要的,不是它完成了"研究并汇总"这个动作,而是它展示了一个很关键的姿势:先把任务执行骨架搭起来,再往里面持续加状态、治理和边界。

这一节收一下。

Harness 的价值,不在于让一个简单 demo 看起来更高级,而在于让它从"一次性脚本"变成"可扩展的任务运行单元"。

五、怎么理解 Skills 和 Harness 的关系

写到这里,很多人会自然问一个问题:

那 Skills 和 Harness 到底是什么关系?

我更愿意用一句最简单的话来概括:

Skills 解决"会什么",Harness 解决"怎么干"。

这两者放在一起,刚好构成了智能体工程里最重要的两层。

第一层,能力层。

也就是 Skills。

把 instructions、scripts、references、assets 这些能力资产模块化封装起来,让智能体获得特定领域能力。

第二层,执行层。

也就是 Harness。

把任务输入、步骤编排、状态管理、上下文治理、权限边界、委托执行这些能力组织起来,让智能体能稳定执行。

所以我觉得,MAF 1.4 值得关注,不是因为它新增了一个术语,而是因为它让这两层开始形成配合关系了。

有 Skills,智能体才不至于空心。

有 Harness,智能体才不至于失控。

这一节也收一句更直白的。

没有 Skills,智能体可能什么都不会。

没有 Harness,智能体可能什么都想干,但干不稳。

六、最后怎么评价 MAF 1.4

如果让我做一个比较克制的判断,我会这么说:

MAF 1.4 释放出的方向是对的,而且信号很明确,但生态还在早期。

为什么说方向对?

因为它开始补的是智能体工程里真正难、也真正值钱的那部分能力:

  • 命令控制
  • 显式状态
  • 长任务上下文治理
  • 环境边界
  • 子任务委托

这些东西都不 flashy,也不适合拿来做特别炫的 demo,但它们恰恰决定了一个框架能不能进生产主线。

为什么又说还在早期?

因为从框架支持到真正形成成熟生态,中间还差很多层:

  • 更强的可观测性
  • 更完整的调试体验
  • 更稳定的治理工具
  • 更低的开发者心智负担
  • 更成熟的 best practice 和 sample 体系

所以我不会把 MAF 1.4 讲成"已经完全成熟"。

但我会说,它已经开始走在正确的路上了。

而且这条路,不再只是"让智能体接模型、调工具",而是"把智能体开发推进到工程化执行层"。

结尾

很多人今天看智能体框架,还是更容易盯着模型接入、Tool Calling、Workflow 编排这些显性能力。

但如果你真的开始做长任务、做真实执行、做生产场景,很快就会发现,真正拉开差距的,往往不是模型本身,而是外面那层 Harness。

所以如果让我用一句话总结 MAF 1.4 的意义,我会写成这样:

MAF 1.4 真正补上的,不是一个新功能,而是智能体从"会做事"走向"能稳定做成事"的那层工程骨架。

如果你最近也在关注 .NET 上的 Agent 开发,我建议你重点看两层:

一层是 Skills,决定智能体会什么。

一层是 Harness,决定智能体怎么稳定地把事做完。

如果这篇文章对你有启发,欢迎先收藏。我后面也会继续写 MAF、Hermes 和 .NET Agent 工程化这一条线,尽量把"能跑 demo"和"能做系统"之间那条分界线讲清楚。