6.2 标准 Harness 工程的设计清单
一句话定义
Harness = Agent + Memory + Skill + Rule + Tool + EvalCheck + MonitorHarness 不是一个单独的组件,是把前面几章的零件按 LLM 的五个缺陷组织起来的治理体系。
框架基础
本 Harness 工程基于微软 MAAI 框架构建,具体由两层组成:
| 框架层 | 命名空间 | 提供的核心抽象 |
|---|---|---|
| Microsoft.Extensions.AI (MEAI) | Microsoft.Extensions.AI | IChatClient、ChatMessage、AITool、FunctionInvokingChatClient 等底层 AI 交互抽象 |
| Microsoft.Agents.AI (MAF) | Microsoft.Agents.AI | ChatClientAgent、AIContextProvider(Invoking/Invoked 生命周期)、AgentSession 等 Agent 编排抽象 |
在此基础上,项目自行构建了业务组件层:
| 自建组件 | 基于的框架抽象 | 职责 |
|---|---|---|
AgentFactory | ChatClientAgent + ChatClientAgentOptions | 统一创建 LeadAgent/SubAgent,自动组装 Provider 链和装饰器管道 |
MemoryFactsContextProvider | AIContextProvider | 注入记忆事实为 SystemMessage |
MonitoringContextProvider | AIContextProvider | 在调用前后发布事件、记录指标 |
TitleContextProvider | AIContextProvider | 首轮对话自动生成会话标题 |
CompactionProvider | AIContextProvider | 上下文窗口超阈值时按策略压缩 |
EvalCheck 委托模型 | 自建(不依赖框架) | 函数式输出校验,可组合、可批量评估 |
InMemoryEventBus + MetricsAccumulator | 自建(不依赖框架) | 事件驱动的监控体系 |

Harness 全景图
┌─────────────────────────────────────────────────────────┐
│ Harness 全景 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Agent │ │ Memory │ │ Skill │ │
│ │ (大脑) │ │ (记忆) │ │ (肌肉) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 上下文提供者链 │ │
│ │ MemoryFacts + Monitoring + Skill + ... │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Agent 执行引擎 │ │
│ │ LeadAgent → SubAgent → Tool 调用循环 │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
│ │ Rule │ │ Tool │ │EvalCheck │ │
│ │ (围栏) │ │ (手) │ │ (质检) │ │
│ └─────────┘ └─────────┘ └──────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Monitor (监控) │ │
│ │ 事件总线 + 指标累积 + 诊断追踪 │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘七个组件各做什么
| 组件 | 职责 | 对应 LLM 缺陷 |
|---|---|---|
| Agent | 大脑,负责决策和编排 | — |
| Memory | 记忆,存储用户偏好和历史事实 | 有自己的偏好(用记忆覆盖) |
| Skill | 肌肉记忆,标准化操作流程 | 会偷懒(用流程防止跳步) |
| Rule | 围栏,定义行为边界 | 没有边界(用规则约束) |
| Tool | 手,执行具体操作 | — |
| EvalCheck | 质检,输出后校验 | 会迎合用户(用检查拦截) |
| Monitor | 监控,追踪执行过程 | 会一根筋(用超时中断) |
Agent 知识体系的四层结构
Agent 的知识来源 = 检索 + 记忆 + 规范 + 能力
检索层:
├── RepoWiki(结构化摘要)→ 项目全貌
├── AGENTS.md(手工策展)→ 最重要的上下文
└── Memory Facts(记忆事实)→ 用户偏好和决策
规范层:
├── Rule(编码约束)→ 不能怎么做
└── Skill(操作流程)→ 怎么做
能力层:
├── Tool(工具)→ 能做什么
└── SubAgent(分身)→ 专业领域执行
治理层:
├── EvalCheck(输出校验)→ 做得对不对
└── Monitor(监控追踪)→ 做没做到一次完整的 Agent 调用链路
下面以 LeadAgent 处理一条用户消息为例,展示完整的调用链路。标注了【框架】的是 MAAI 框架自动完成的部分,标注了【自建】的是项目自行实现的组件。
1. 用户发消息
↓
2. LeadAgentManager 确保会话存在 【自建】
↓
3. AgentFactory.CreateLeadAgentAsync() 【自建】
│
├── 创建 IChatClient 原始连接(通过 IModelProvider) 【自建】
│
├── 构建 IChatClient 装饰器管道(MEAI Decorator 模式): 【框架+自建】
│ ├── LoggingChatClient(日志记录) 【自建】
│ ├── OpenTelemetryChatClient(分布式追踪) 【框架】
│ ├── CustomRetryingChatClient(429/5xx 指数退避重试) 【自建】
│ └── FunctionInvokingChatClient(工具调用循环) 【框架】
│
├── 构建 AIContextProvider 链(MAF 生命周期管理): 【框架+自建】
│ ├── ChatHistoryProvider(对话历史加载/存储) 【框架】
│ ├── CompactionProvider(上下文压缩) 【自建】
│ ├── MemoryFactsContextProvider(注入记忆事实) 【自建】
│ ├── MonitoringContextProvider(事件发布+指标记录) 【自建】
│ ├── TitleContextProvider(标题生成) 【自建】
│ └── AgentSkillsProvider(注入 Skill 文档) 【自建】
│
└── 返回 ChatClientAgent 实例 【框架】
↓
4. agent.RunAsync() 【框架】
│
├── 框架自动执行所有 Provider 的 Invoking 阶段 【框架】
│ ├── ChatHistoryProvider → 加载历史消息
│ ├── CompactionProvider → 检查是否需要压缩
│ ├── MemoryFactsContextProvider → 注入记忆事实
│ ├── MonitoringContextProvider → 发布 AgentInvokedEvent
│ └── TitleContextProvider → (首轮时准备生成标题)
│
├── 构建完整上下文 → 调用 LLM 【框架】
│
├── LLM 返回工具调用 → 执行工具 → 再次调用 LLM(循环) 【框架】
│ └── 每个工具调用被 ObservingAIFunction 包装 【框架】
│
├── 每次 LLM 输出经过 EvalCheck 校验 【自建】
│
└── 框架自动执行所有 Provider 的 Invoked 阶段 【框架】
├── ChatHistoryProvider → 持久化消息到 MemoryStore
├── MonitoringContextProvider → 发布完成/失败事件
└── TitleContextProvider → 首轮对话生成会话标题
↓
5. 结果返回给用户EvalCheck:输出后校验
LLM 输出不可控,但可以在输出之后做检查。
EvalCheck 是项目自建组件,不依赖 MAAI 框架,可以独立使用。核心思想:把校验逻辑解耦为独立的检查函数,每个 Check 只关注一个维度,通过组合多个 Check 形成完整的校验策略。
csharp
// EvalCheck 委托:输入评估项,输出校验结果
public delegate EvalCheckResult EvalCheck(EvalItem item);
// 校验结果
public class EvalCheckResult
{
public bool Passed { get; set; }
public string Reason { get; set; }
public string CheckName { get; set; }
}为什么用 delegate 而不是接口?因为 Check 应该是轻量的、函数式的。一个 Check 就是一个函数——输入评估项,输出通过/失败。
多个 Check 可以组合:
csharp
// 组合多个检查
var checks = new List<EvalCheck> { jsonFormatCheck, requiredFieldCheck, contentCheck };
// 依次执行,收集结果
var results = checks.Select(c => c(item)).ToList();
var allPassed = results.All(r => r.Passed);校验失败时的处理:
- 自动重试:把失败原因注入到下一轮对话,让 LLM 修正
- 降级:如果重试次数超限,返回部分结果
- 人工介入:标记为需要人工审核
Monitor:监控体系
监控体系是项目自建组件,基于 MAF 的 AIContextProvider 生命周期钩子(Invoking/Invoked)实现非侵入式接入。由三层组成:
| 层 | 组件 | 职责 |
|---|---|---|
| 事件层 | InMemoryEventBus | 发布/订阅模式,解耦监控和业务 |
| 指标层 | MetricsAccumulator | 记录请求数、耗时、错误率 |
| 追踪层 | WorkflowDiagnosticContext | OpenTelemetry 桥接,支持分布式追踪 |
csharp
// 监控上下文提供者:继承 MAF 框架的 AIContextProvider,在 Agent 调用前后发布事件
internal class MonitoringContextProvider : AIContextProvider // ← MAF 框架抽象基类
{
// Invoking 阶段:框架在调用 LLM 前自动执行
protected override ValueTask<AIContext> ProvideAIContextAsync(...)
{
_eventBuffer.Add(new AgentInvokedEvent { AgentName = agentName, ... });
context.Session.StateBag.SetValue(MonitoringStartTicksKey, ...);
return new ValueTask<AIContext>(new AIContext());
}
// Invoked 阶段:框架在 LLM 调用完成后自动执行(成功/失败都会执行)
protected override async ValueTask InvokedCoreAsync(...)
{
var durationMs = GetDurationMs(context.Session);
if (context.InvokeException is not null)
{
_metricsAccumulator.RecordRequest(durationMs, 0, isError: true);
await _eventBus.PublishAsync(new AgentFailedEvent { Error = ... });
}
else
{
_metricsAccumulator.RecordRequest(durationMs, 0, isError: false);
await _eventBus.PublishAsync(new AgentCompletedEvent { Duration = ... });
}
}
}关键设计:MonitoringContextProvider 重写了 InvokedCoreAsync 而非 StoreAIContextAsync,因为 MAF 框架默认在失败时会跳过 StoreAIContextAsync,而监控需要在成功和失败两种场景都执行。
设计清单总结
设计一个 Harness 工程,需要回答以下问题:
| 维度 | 要回答的问题 | 对应组件 |
|---|---|---|
| 知识 | Agent 怎么获取项目信息? | AGENTS.md + RepoWiki + Memory |
| 规范 | Agent 做事必须遵守什么? | Rule + Skill |
| 能力 | Agent 能做什么操作? | Tool + SubAgent |
| 校验 | 怎么判断 Agent 做得对不对? | EvalCheck |
| 监控 | 怎么追踪 Agent 的执行过程? | Monitor |
| 边界 | Agent 不能做什么? | Rule + AGENTS.md + SubAgent 隔离 |
这六个维度,就是 Harness 工程的设计清单。缺任何一个,Agent 都可能失控。