Loop Engineering是2026年人工智能圈最火爆的核心概念,它指的是设计、运营和持续改进反馈循环(Feedback Loops)的AI工程化实践。NVIDIA 首席执行官黄仁勋将其形容为定义2026下半年AI发展的重大转变为:现在没人写提示词了,大家都在编写和管理循环(Loops)。它的核心思想是让你从一个向AI发送指令的人,变成一个设计自动运转系统,让AIAgent自主完成任务并自我修正的工程师。其实Loop Engineering没有很多说得那么玄乎,这篇文章我们会通过一个简单的例子通过几行代码实现针对Agent的循环调用。

1. Loop Engineering概述

这两年来,AI圈子没几天就会出现一个新词,加上很多人为了博取流量,与不惊人死不休,动不动就是XXX已死、YYY当立,导致很多人觉得:怎么昨天我刚听前沿技术,今天就成了明日黄花了。我前几天看了一篇文章的标题就是《Harness Engineering已死、Loop Engineering才是王道》,简直莫名其妙。不过很多人将AI工程演化分成四代

  • Prompt Engineering
  • Context Engineering
  • Harness Engineering
  • Loop Engineering

我个人是不同意这种说法的。还记得定义Harness Engineering的如下这个公式吗?按照这个公式,Harness Engineering本质上就是在LLM基础上构建理想Agent的所有工程化手段。而LLM本身就是独立的,那岂不是Harness Engineering = Agent Engineering,我们应用在Agent上所有设计模式都是一种Harness手段,自然也包括Prompt Engineering和Context Engineering优化提示词和上下文的手段,也包括Loop Engineering根据动态评估反馈自动调用Agent的手段。

Agent = LLM + Harness

所以我的意思是,如果我们认同Harness Engineering的说法,意味着在它之前以及之后出现的所有Engineering都是它的子集,自然也包括Loop Engineering。 这种说法可以在MAF中得到印证:用于实现Loop Engineering的LoopAgent中间件已经集成在HarnessAgent之中。

在Loop Engineering之前,我们是这样使用Agent的:

  • 我们采用提示词来描述推理任务,并将其作为输入调用Agent,而且一般我们会使用Session保持与Agent对话的语境;
  • 单次Agent的调用一般会在一个ReAct循环中执行,并最终输出响应;
  • 我们根据输出响应,再次撰写提示词作为输入调用Agent。

整个过程完全是由用户主导,一切由输入的提示词来驱动。Loop Engineering引入了一种基于评估反馈的迭代机制,我们通过注入一个评估器对每一轮调用的结果进行评估,并由评估结果决定是否继续调用Agent。如果评估器决定继续,它一般会提供评估结果(比如理由和建议)并作为下次Agent调用的输入。

2. 利用MAF的LoopAgent实现针对Agent的循环调用

Loop Engineering在MAF中是通过LoopAgent这个继承自DelegatingAgent的Agent中间件实现的,它已经被集成在HarnessAgent中,当然它也可以被单独使用。接下来我们将使用它实现如下的应用场景:我们创建一个用于宋词创作的Agent,它能根据我们提供的主题和题材创作一首宋词。我们同时创建一个评估器,它能对每一轮Agent的创作结果进行评估,并根据评估结果决定是否继续创作。整个循环调用的过程如下:

  • 调用Agent进行创作;
  • 评估器对创作结果进行评估,评估的结果包括一个得分和一段评语:
    • 如果得到达到执行的分数,整个循环结束;
    • 否则,针对反馈的评语,重新调用Agent进行创作。

如下所示的是这个评估器类型ScoreBasedEvaluator的定义。它继承自LoopEvaluator,构造函数接收一个AIAgent对象作为评估器和一个目标分数。在重写得EvaluateAsync方法中,它将当前LoopContext中的初始消息和最后一次响应消息作为输入,调用评估器进行评估。我们通过结构化输出的方式让AIAgent返回一个Review类型的对象,它包含一个整数类型的得分和一个字符串类型的评语。

class ScoreBasedEvaluator(AIAgent reviwer, int score) : LoopEvaluator
{
    public override async ValueTask<LoopEvaluation> EvaluateAsync(LoopContext context, CancellationToken cancellationToken = default)
    {
        ChatMessage[] input =
        [
            ..context.InitialMessages,
            ..context.LastResponse.Messages
        ]; 

        var review = (await reviwer.RunAsync<Review>(input)).Result;
        Console.WriteLine($"""
            {new string('-', 30)}Iteration{context.Iteration}{new string('-', 30)}
            {context.LastResponse}


            得分: {review.Score}
            评语:{review.Feeback}
            
            """);
        return review.Score >= score
            ? LoopEvaluation.Stop()
            : LoopEvaluation.Continue(review.Feeback);
    }
}

[Description("针对创作诗词的评估结果")]
public class Review
{
    [Description("得分,采用百分制")]
    public int Score { get; set; }

    [Description("评语")]
    public string? Feeback { get; set; }
}

在得到作为评估结果的Review对象后:

  • 如果得分达到预设的阈值,LoopEvaluator将返回通过调用LoopEvaluation.Stop()方法创建的LoopEvaluation对象,整个循环调用结束;
  • 如果得分未达到预设的阈值,LoopEvaluator将评语组为反馈调用LoopEvaluation.Continue(feedback)方法,并返回该方法创建的LoopEvaluation对象,整个循环调用继续,并将评语作为下一轮Agent调用的输入。

如下三个常量分别定义了创作Agent的提示词、评审Agent的提示词和整个循环调用的初始输入。创作Agent的提示词要求它根据提供的主题和意境以词牌名相见欢创作一首词。评审Agent的提示词要求它根据四个维度对创作的这首词进行评价,并给出一个得分和一段评语。初始输入是《卫风·氓》的原文。

const string ComposerInstructions = "你是一个精通宋词创作的智能体,负责根据提供的主题和意境以词牌名**相见欢**创作一首词";
const string ReviwerInstrucctions = """
    你是一个深谙古典文化和诗词的文化大家,请按照如下的标准对创作的这首词进行评价。评语保持200字以内,尽可能简洁。
    
    1. 格律规范 (Metrical Accuracy) —— 30分
    
    检测AI生成的词是否真正符合该词牌的“说明书”。
    
    - 词牌结构(20分):总字数、分片、每句的字数长短必须与词牌名保持一致。
    - 平仄规范(5分):关键位置的平仄(平声、仄声)必须严格符合词牌要求。
    - 押韵规则(5分):检查是否在规定位置押韵,是否混淆了平仄韵,有无出韵。
    
    2. 意境创设 (Imagery & Conception) —— 30分
    
    评估词作是否具备古典美感,是否能画出**画面感**- 意象选用(15分):是否恰当使用了符合古典美学的意象。意象之间是否逻辑自洽,没有现代感违和物。
    - 流派风格(15分):整体意境的深远程度。
    
    3. 语言艺术 (Linguistic Artistry) —— 20分
    
    评估遣词造句的功底和文字的流畅度。
    
    - 用词典雅(10分):遣词造句需有**词味**,杜绝使用现代大白话或过于生硬的拼凑词。
    - 对仗与过片(10分):若词牌要求对仗,需检查是否工整;重点评估**过片**(上下片过渡)是否自然流转,有无断层。
    
    4. 情感寄托与创新 (Emotional Depth & Innovation) —— 20分
    
    评估诗词的灵魂,拒绝纯粹的字词堆砌。
    
    - 情感共鸣(10分):词中所表达的悲欢离合、家国情怀或羁旅之思是否真挚饱满。
    - 陈词翻新(10分):是否只会堆砌********等陈词滥调。优秀的词作应当在传统框架下有独特的视角或新颖的构思。
    """;
const string Prompt = """
    基于如下这首《卫风·氓》的背景和情感基调,分别创作一首一首宋词。
    
    原文如下:

    氓之蚩蚩,抱布贸丝。匪来贸丝,来即我谋。
    送子涉淇,至于顿丘。匪我愆期,子无良媒。
    将子无怒,秋以为期。
    
    乘彼垝垣,以望复关。不见复关,泣涕涟涟。
    既见复关,载笑载言。尔卜尔筮,体无咎言。
    以尔车来,以我贿迁。
    
    桑之未落,其叶沃若。于嗟鸠兮,无食桑葚!
    于嗟女兮,无与士耽!士之耽兮,犹可说也;
    女之耽兮,不可说也。
    
    桑之落矣,其黄而陨。自我徂尔,三岁食贫。
    淇水汤汤,渐车帷裳。女也不爽,士贰其行。
    士也罔极,二三其德。
    
    三岁为妇,靡室劳矣;夙兴夜寐,靡有朝矣。
    言既遂矣,至于暴怒。兄弟不知,咥其笑矣。
    静言思之,躬自悼矣。
    
    及尔偕老,老使我怨。淇则有岸,隰则有泮。
    总角之宴,言笑晏晏。信誓旦旦,不思其反。
    反是不思,亦已焉哉!
    """;

整个程序其实很简单,首先我们创建两个AIAgent,一个是创作Agent,一个是评审Agent,后者用来创建作为评估器的ScoreBasedEvaluator对象,并指定一个分数阈值(85分)。然后我们将创作Agent和评估器封装在LoopAgent中。最后我们调用LoopAgentRunAsync方法,并传入初始输入Prompt。整个循环调用的过程会在控制台打印出每一轮的创作结果、评审结果和得分。

var composer = CreateAgent(
    name: "SongLyricsComposer",
    model: "gpt-5.2-chat",
    instructions: ComposerInstructions);

var reviewer = CreateAgent(
    name: "SongLyricsReviwer",
    model: "gpt-5.2-chat",
    instructions: ReviwerInstrucctions);

var agent = new LoopAgent(composer, new ScoreBasedEvaluator(reviewer, 85));
var response = await agent.RunAsync(Prompt);
var index = 1;
foreach (var message in response.Messages)
{
    Console.WriteLine($"""
        {new string('-', 20)}Message{index++}{new string('-', 20)}
        {message}

        """);
}

AIAgent CreateAgent(string name,string instructions, string model)
{
    var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
    var endpoint = Environment.GetEnvironmentVariable("PROJECT_URL")!;
    return new AIProjectClient(new Uri(endpoint), new AzureCliCredential())
        .AsAIAgent(
            name: name,
            model: model,
            instructions: instructions);
}

整个输出总体分为两段,第一段是每次迭代后调用评估器的输出,包含创作结果、得分和评语。从如下的输出可以看出,整个流程经历了两次迭代,第一次评估得分为78分,评语指出了创作中存在的问题,并给出了改进建议。第二次迭代后,评估得分为86分,评语表明创作已经达到了预期的标准,整个循环调用结束。

------------------------------Iteration1------------------------------
相见欢

淇桥初见匆匆。意朦胧。
笑把秋期轻许、趁东风。

桑阴翠,流年坠,人情空。
回首当时盟誓、已成鸿。


得分: 78
评语:此作依《相见欢》小令体式,句读大体齐整,然“笑把秋期轻许、趁东风”字数稍近散文化,平仄亦未尽合谱,韵脚统一尚可。意象取“淇桥”“桑阴”等,能扣《氓》本事,前后情绪递转自然。下片“流年坠,人情空”有兴衰之感,结句“盟誓已成鸿”含怨而不滞。语言清婉流畅,但部分措辞略近今人口吻,词味尚可再凝炼;情感层次由相悦至幻灭,虽较含蓄,仍具余韵。

------------------------------Iteration2------------------------------
相见欢

淇边细雨初晴。柳烟轻。
曾约复关南路、候君行。

桑青后,花零久,两心惊。
一片当时明月、照空盟。


得分: 86
评语:此作依《相见欢》小令体制,句式大体合拍,押“轻、行、惊、盟”庚青韵,尚称稳妥。上片写淇边候约,点出《氓》旧事;下片以“桑青后,花零久”暗承桑叶兴衰,意象简洁而有层次。语言清婉自然,有宋词婉约之致。“一片当时明月、照空盟”收束含怨不露,颇有余味。惟情感推进稍快,对负心与悔悟之沉痛着墨略浅,过片亦可更曲折深婉。

第二段是LoopAgent最终响应包含的消息。一共包括两条消息,第一条为第一次创作的结果,第二条为评估器给出的反馈,第三条为第二次创作的结果。由于此次传作业已经达到了预期的标准,整个循环调用结束。

--------------------Message1--------------------
相见欢

淇桥初见匆匆。意朦胧。
笑把秋期轻许、趁东风。

桑阴翠,流年坠,人情空。
回首当时盟誓、已成鸿。

--------------------Message2--------------------
此作依《相见欢》小令体式,句读大体齐整,然“笑把秋期轻许、趁东风”字数稍近散文化,平仄亦未尽合谱,韵脚统一尚可。意象取“淇桥”“桑阴”等,能扣《氓》本事,前后情绪递转自然。下片“流年坠,人情空”有兴衰之感,结句“盟誓已成鸿”含怨而不滞。语言清婉流畅,但部分措辞略近今人口吻,词味尚可再凝炼;情感层次由相悦至幻灭,虽较含蓄,仍具余韵。

--------------------Message3--------------------
相见欢

淇边细雨初晴。柳烟轻。
曾约复关南路、候君行。

桑青后,花零久,两心惊。
一片当时明月、照空盟。

3. 直接使用HarnessAgent实现Loop Engineering

我们已经说过,LoopAgent已经被集成在HarnessAgent中,所以我们可以直接使用HarnessAgent来实现Loop Engineering。如下所示,我们创建了一个用于创作的IChatClient对象,和一个用于评估的AIAgent对象,后者被用于创建一个ScoreBasedEvaluator对象。然后我们将创作的IChatClient对象和评估器封装在HarnessAgent中。

var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_BASE_URL")!;

var reviewer = new OpenAIClient(new ApiKeyCredential(apiKey), new OpenAIClientOptions {  Endpoint = new Uri(endpoint)})
    .GetChatClient(model: "gpt-5.2-chat")
    .AsIChatClient()
    .AsAIAgent(    
    name: "SongLyricsReviwer",
    instructions: ReviwerInstrucctions);

var chatClient = new OpenAIClient(new ApiKeyCredential(apiKey), new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
    .GetChatClient(model: "gpt-5.2-chat")
    .AsIChatClient();

var agent = new HarnessAgent(chatClient, new HarnessAgentOptions
{
    Name = "SongLyricsComposer",
    HarnessInstructions = ComposerInstructions,
    LoopEvaluators = [new ScoreBasedEvaluator(reviewer,85)],
    DisableWebSearch = true,
    DisableAgentModeProvider = true,
    DisableTodoProvider = true,
    DisableFileMemory = true,
    DisableFileAccess = true,
});
var response = await agent.RunAsync(Prompt);
var index = 1;
foreach (var message in response.Messages)
{
    Console.WriteLine($"""
        {new string('-', 20)}Message{index++}{new string('-', 20)}
        {message}

        """);
}

由于HarnessAgent会整和很多用于实现响应Harness手段的中间件,对此由兴趣的可以参考我的文章MAF的Harness-02:HarnessAgent究竟整合了哪些Harness手段?。为了屏蔽它们的干扰,我们在给定的HarnessAgentOptions中禁用了某些中间件。程序运行之后,也能得到我们希望的输出(这次进行了4次迭代):

------------------------------Iteration1------------------------------
《相见欢·淇上初盟》

垝垣斜日轻红,望归鸿。犹记顿丘携手、语匆匆。

桑叶碧,春水急,意相从。谁料旧盟难守、各西东。

《相见欢·桑落》

桑黄淇水东流,冷汀洲。三载辛勤空付、旧时柔。

灯半灭,人未歇,泪还收。当日旦旦深誓、总成秋。


得分: 78
评语:两首皆能扣合《氓》之情事,由初盟至决绝,层次清楚。《相见欢》体式基本合拍,句读亦稳,押“ong”“ou”韵较自然。上阕“垝垣斜日”“桑黄淇水”皆有《诗经》遗意,画面感尚佳。然词味稍浅,“旧盟难守”“空付旧时柔”近于直白,少含蓄蕴藉。过片承转尚顺,但对人物情绪的递进尚未深挖,未能尽显《氓》由痴恋、怨怼至决绝的复杂波澜。整体清婉流畅,具宋词小令风致。

------------------------------Iteration2------------------------------
《相见欢·复关》

垝垣斜倚残曛,望归人。淇水悠悠不断、暮云深。

桑未落,鬓先损,泪沾襟。当日笑言犹在、却难寻。

《相见欢·已焉》

桑阴摇雨初秋,冷汀洲。帘外淇波犹自、向东流。

灯影薄,人语寂,恨难休。誓语当年如梦、一回收。


得分: 78
评语:两首皆能扣《氓》之弃妇哀怨,意象如“淇水”“桑阴”“垝垣”承袭《诗经》本色,情绪流转自然,有宋词婉约之致。《相见欢》词牌字数与分片基本合格,押“人、深、襟、寻”“秋、洲、流、休、收”亦较稳。然平仄未尽严谨,个别句法略近小令常套。上篇“鬓先损”较直白,下篇“誓语当年如梦”稍近熟语。整体胜在情感真切,能由盼望写至决绝,保留《氓》原作由爱生怨的层次。

------------------------------Iteration3------------------------------
《相见欢·淇上》

轻寒细雨孤村,掩重门。犹忆送郎淇上、语温存。

桑渐老,水空远,暮烟昏。回首复关何处、月微痕。

《相见欢·弃盟》

秋声吹冷蘅皋,夜迢迢。独倚疏灯无语、听寒潮。

当日意,而今恨,总难消。淇岸有涯人事、竟无聊。


得分: 82
评语:两首皆能扣《氓》之情脉,由相恋而怨别,意绪连贯。《相见欢》词牌字数大体合式,句读亦稳,然“犹忆送郎淇上”“独倚疏灯无语”等处平仄未尽严谨,韵脚稍嫌单调。意象承《诗经》之“淇水”“桑”“复关”,古典气息较足,尤“月微痕”“淇岸有涯”有余味。语言清婉流畅,但个别句略近小令常套,创新稍弱。第二首结句“人事、竟无聊”情感到位,然“无聊”略近今语,减损词味。总体能得哀而不怨之旨。

------------------------------Iteration4------------------------------
《相见欢·望复关》

晚云低护荒台,雨声催。独倚残垣凝望、雁初回。

桑叶尽,人情改,镜尘堆。惟有淇流如旧、去难回。

《相见欢·决绝》

露凉风紧蘅皋,夜萧骚。旧约依稀还似、梦魂遥。

灯影暗,佩声杳,恨迢迢。淇岸纵宽终有、此情消。


得分: 86
评语:两首皆合《相见欢》小令体制,句读齐整,押平声韵较稳,格律基本无失。上篇以“残垣”“雁回”“淇流”等意象承《氓》之望复关、情渐冷,画面清婉;下篇转入决绝,蘅皋、灯影、佩声等语颇具词味。语言凝练,无明显俗句,过片自然。惟情感层次稍简,未能充分展开《氓》中由热恋至怨弃的复杂心理;“镜尘堆”“梦魂遥”等语略近常套,创新度尚可再进。

--------------------Message1--------------------
《相见欢·淇上初盟》

垝垣斜日轻红,望归鸿。犹记顿丘携手、语匆匆。

桑叶碧,春水急,意相从。谁料旧盟难守、各西东。

《相见欢·桑落》

桑黄淇水东流,冷汀洲。三载辛勤空付、旧时柔。

灯半灭,人未歇,泪还收。当日旦旦深誓、总成秋。

--------------------Message2--------------------
两首皆能扣合《氓》之情事,由初盟至决绝,层次清楚。《相见欢》体式基本合拍,句读亦稳,押“ong”“ou”韵较自然。上阕“垝垣斜日”“桑黄淇水”皆有《诗经》遗意,画面感尚佳。然词味稍浅,“旧盟难守”“空付旧时柔”近于直白,少含蓄蕴藉。过片承转尚顺,但对人物情绪的递进尚未深挖,未能尽显《氓》由痴恋、怨怼至决绝的复杂波澜。整体清婉流畅,具宋词小令风致。

--------------------Message3--------------------
《相见欢·复关》

垝垣斜倚残曛,望归人。淇水悠悠不断、暮云深。

桑未落,鬓先损,泪沾襟。当日笑言犹在、却难寻。

《相见欢·已焉》

桑阴摇雨初秋,冷汀洲。帘外淇波犹自、向东流。

灯影薄,人语寂,恨难休。誓语当年如梦、一回收。

--------------------Message4--------------------
两首皆能扣《氓》之弃妇哀怨,意象如“淇水”“桑阴”“垝垣”承袭《诗经》本色,情绪流转自然,有宋词婉约之致。《相见欢》词牌字数与分片基本合格,押“人、深、襟、寻”“秋、洲、流、休、收”亦较稳。然平仄未尽严谨,个别句法略近小令常套。上篇“鬓先损”较直白,下篇“誓语当年如梦”稍近熟语。整体胜在情感真切,能由盼望写至决绝,保留《氓》原作由爱生怨的层次。

--------------------Message5--------------------
《相见欢·淇上》

轻寒细雨孤村,掩重门。犹忆送郎淇上、语温存。

桑渐老,水空远,暮烟昏。回首复关何处、月微痕。

《相见欢·弃盟》

秋声吹冷蘅皋,夜迢迢。独倚疏灯无语、听寒潮。

当日意,而今恨,总难消。淇岸有涯人事、竟无聊。

--------------------Message6--------------------
两首皆能扣《氓》之情脉,由相恋而怨别,意绪连贯。《相见欢》词牌字数大体合式,句读亦稳,然“犹忆送郎淇上”“独倚疏灯无语”等处平仄未尽严谨,韵脚稍嫌单调。意象承《诗经》之“淇水”“桑”“复关”,古典气息较足,尤“月微痕”“淇岸有涯”有余味。语言清婉流畅,但个别句略近小令常套,创新稍弱。第二首结句“人事、竟无聊”情感到位,然“无聊”略近今语,减损词味。总体能得哀而不怨之旨。

--------------------Message7--------------------
《相见欢·望复关》

晚云低护荒台,雨声催。独倚残垣凝望、雁初回。

桑叶尽,人情改,镜尘堆。惟有淇流如旧、去难回。

《相见欢·决绝》

露凉风紧蘅皋,夜萧骚。旧约依稀还似、梦魂遥。

灯影暗,佩声杳,恨迢迢。淇岸纵宽终有、此情消。

更多推荐