求几少 发表于 2026-1-4 19:15:00

MAF快速入门(10)循环工作流

大家好,我是Edison。
最近我一直在跟着圣杰的《.NET+AI智能体开发进阶》课程学习MAF的开发技巧,我强烈推荐你也上车跟我一起出发!
上一篇,我们学习了MAF中如何进行switch-case类型的多条件路由。本篇,我们来了解下在MAF中如何实现循环(自我修正)工作流。
1 循环与自我修正

在实际业务场景中,往往需要在工作流中设置一些循环与自我修正的机制,构建出一个“生成→审核→修复”的闭环,来确保AI产出的内容能够满足企业级质量标准。
在MAF中,我们可以使用 Loop Edge 即 循环边 来实现这个目的,如下代码片段所示:
var workflow = new WorkflowBuilder(draftExecutor)
    .AddEdge(draftExecutor, qcExecutor)
    .AddEdge(qcExecutor, draftExecutor)
    .WithOutputFrom(qcExecutor)
    .Build();可以看到,我们添加了两个边关联关系,这样就形成了一个循环。
2 循环工作流实验案例

假设我们是一个电商平台的客服中心,每天需要处理产生的大量客服工单进行回复。这里我们希望AI助手先帮我们生成一个回复草稿,然后经过多维度的质检(礼貌度、准确性、合规性),不合格的则自动进行改进,直到满足标准 或者是 转给人工处理。因此,我们的目标是:配置一个自动迭代的循环“生成→审核→修复”,达标即可退出循环,否则转人工处理。
2.1 关键依赖包引入

在今天的这个案例中,我们仍然创建了一个.NET控制台应用程序,安装了以下NuGet包:

[*]Microsoft.Agents.AI.OpenAI
[*]Microsoft.Agents.AI.Workflows
[*]Microsoft.Extensions.AI.OpenAI
2.2 定义数据传输模型

首先,我们定义一下在这个工作流中需要生成传递的数据模型:
(1)QualityReportDto :质检结果输出模型DTO
// 质检结果的结构化输出模型
internal class QualityReportDto
{
   
    public int PolitenessScore { get; set; }
   
    public int AccuracyScore { get; set; }
   
    public bool CompliancePassed { get; set; }
   
    public List<QualityIssueDto> Issues { get; set; } = new();
}
internal class QualityIssueDto
{
   
    public string Type { get; set; } = string.Empty;
   
    public string Description { get; set; } = string.Empty;
   
    public int ScoreImpact { get; set; }
}(2)一些值对象,不赘述了
internal record ReplyDraft(
    string TicketId,
    string Content,
    int Attempt);
internal record TicketRequest(
    string Id,
    string Query,
    string Category,
    string Priority);
internal record TicketOutcome(
    string TicketId,
    string Status,
    int Attempts,
    QualityReport FinalReport);
internal sealed record QualityScoreTimelineItem(
    int Attempt,
    int PolitenessScore,
    int AccuracyScore,
    string ComplianceStatus);
internal record QualityIssue(
    string Type,
    string Description,
    int ScoreImpact);
internal record QualityReport(
    string TicketId,
    int PolitenessScore,
    int AccuracyScore,
    bool CompliancePassed,
    IReadOnlyList<QualityIssue> Issues);(3)一些常量:用于定义一些阈值标准
public static class QualityCheckConstants
{
    // 质检标准:礼貌度 ≥ 85,准确性 ≥ 90,合规性必须 100%
    // 注意:阈值设置较高,配合第一次生成简化版本,确保能体现循环改进过程
    public const int PolitenessThreshold = 85;
    public const int AccuracyThreshold = 90;
}

internal enum QualityCheckSignal
{
    Init,
    Revise
}2.3 定义自定义工作流事件

其次,我们定义一下在这个工作流中需要产生的自定义事件:
(1)AdaptiveQualityScoreEvent :质检评分已完成事件
internal sealed class AdaptiveQualityScoreEvent : WorkflowEvent
{
    public AdaptiveQualityScoreEvent(string ticketId,
      int attempt,
      int politenessScore,
      int accuracyScore,
      bool compliancePassed)
      : base(new { TicketId = ticketId, Attempt = attempt, PolitenessScore = politenessScore, AccuracyScore = accuracyScore, CompliancePassed = compliancePassed })
    {
    }
}(2)AdaptiveMaxAttemptsReachedEvent :质检超过最大次数事件
internal sealed class AdaptiveMaxAttemptsReachedEvent : WorkflowEvent
{
    public AdaptiveMaxAttemptsReachedEvent(string ticketId, int maxAttempts)
      : base(new { TicketId = ticketId, MaxAttempts = maxAttempts })
    {
    }
}2.4 定义Agents

(1)回复生成:模这里通过执行器的方式包裹一个回复生成的Agent,假设其用于客服工单的自动回复:
internal sealed class AdaptiveReplyDraftExecutor : Executor{    private readonly TicketRequest _ticket;    private readonly IChatClient _chatClient;    public AdaptiveReplyDraftExecutor(TicketRequest ticket, IChatClient chatClient) : base("AdaptiveReplyDraft")    {      _ticket = ticket;      _chatClient = chatClient;    }    public override async ValueTask HandleAsync(QualityCheckSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)    {      int attempt = await context.ReadOrInitStateAsync("attempt", () => 0, cancellationToken);      attempt++;      await context.QueueStateUpdateAsync("attempt", attempt, cancellationToken);      // 使用 AI 生成客服回复(渐进式生成策略)      var prompt = attempt == 1            ? $"""            你是一位电商客服。请针对以下客户问题生成一条简短回复(刻意保持简短、缺少礼貌用语):            客户问题:{_ticket.Query}            产品类别:{_ticket.Category}            要求:            1. 只用1-2句话回答,不要称呼语和感谢语            2. 只说结论,不提供具体处理时间            3. 字数控制在30字以内            直接返回回复内容,不要添加任何前缀或说明。            """            : $"""            你是一位专业的电商客服。请针对以下客户问题生成一条改进后的回复:            客户问题:{_ticket.Query}            产品类别:{_ticket.Category}            优先级:{_ticket.Priority}            要求:            1. 语气亲和、专业,使用恰当的称呼和感谢语            2. 提供具体的解决方案或处理时间            3. 符合客服规范,不包含敏感词            4. 字数控制在80-100字            直接返回回复内容,不要添加任何前缀或说明。            """;      var response = await _chatClient.GetResponseAsync(prompt, cancellationToken: cancellationToken);      var content = response.Text ?? "抱歉,我们会尽快处理您的问题。";      Console.WriteLine($"✍️ 第 {attempt} 次生成回复草稿 (策略: {(attempt == 1 ? "简化版" : "完整版")})");      Console.WriteLine($"
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

班嘉淑 发表于 2026-1-14 08:39:50

过来提前占个楼

扔飒 发表于 2026-1-14 23:25:35

感谢分享,学习下。

咫噎 发表于 2026-1-15 11:18:58

这个有用。

金娅鸣 发表于 2026-1-21 10:45:17

喜欢鼓捣这些软件,现在用得少,谢谢分享!

零幸 发表于 2026-1-22 22:06:56

新版吗?好像是停更了吧。

轩辕琳芳 发表于 2026-1-23 02:03:00

懂技术并乐意极积无私分享的人越来越少。珍惜

士沌 发表于 2026-1-24 12:34:35

感谢发布原创作品,程序园因你更精彩

鞭氅 发表于 2026-1-28 09:59:24

用心讨论,共获提升!

饨篦 发表于 2026-1-29 04:27:11

喜欢鼓捣这些软件,现在用得少,谢谢分享!

赊朗爆 发表于 2026-2-4 10:50:09

感谢,下载保存了

当贵 发表于 2026-2-8 17:40:50

很好很强大我过来先占个楼 待编辑

炳裘垦 发表于 2026-2-9 07:05:01

过来提前占个楼

瘴锲如 发表于 2026-2-9 10:30:11

谢谢楼主提供!

怃膝镁 发表于 2026-2-10 04:35:16

新版吗?好像是停更了吧。

百杲憔 发表于 2026-2-10 05:55:25

谢谢楼主提供!

焦听云 发表于 2026-2-10 16:30:45

很好很强大我过来先占个楼 待编辑

辉伫 发表于 2026-2-11 03:34:26

感谢发布原创作品,程序园因你更精彩

松菊 发表于 2026-2-11 13:14:03

感谢发布原创作品,程序园因你更精彩

秦晓曼 发表于 2026-2-11 14:54:57

感谢分享,下载保存了,貌似很强大
页: [1] 2
查看完整版本: MAF快速入门(10)循环工作流