LangChain4j Java AI 应用开发实战(二十):循环工作流 - 自动迭代直到质量达标

系列篇章💥

No.文章
1LangChain4j Java AI 应用开发实战(一):LangChain4j 快速入门指南
2LangChain4j Java AI 应用开发实战(二):大模型参数调优实战:Temperature、TopP、MaxTokens 深度解析
3LangChain4j Java AI 应用开发实战(三):多模态 AI 开发 - 图片理解与图像生成实战
4LangChain4j Java AI 应用开发实战(四):提示词工程进阶 - 模板化与结构化 Prompt 设计
5LangChain4j Java AI 应用开发实战(五):流式响应与对话记忆 - 提升用户体验的关键技术
6LangChain4j Java AI 应用开发实战(六):声明式 AI Service - LangChain4j 的核心编程模型
7LangChain4j Java AI 应用开发实战(七):结构化输出实战 - 从非结构化文本提取 POJO 对象
8LangChain4j Java AI 应用开发实战(八):用户隔离与持久化记忆 - 企业级对话系统设计
9LangChain4j Java AI 应用开发实战(九):Few-Shot Learning - 少样本提示提升模型准确率
10LangChain4j Java AI 应用开发实战(十):Embedding 模型与文本分类语义向量化
11LangChain4j Java AI 应用开发实战(十一):Function Calling 工具调用 - 让 AI 执行真实操作
12LangChain4j Java AI 应用开发实战(十二):向量数据Chroma/Qdrant/Milvus实践对比
13LangChain4j Java AI 应用开发实战(十三):3 行代码实现 RAG - Easy RAG 框架详解
14LangChain4j Java AI 应用开发实战(十四):手写 RAG 全流程 - 深入理解每个环节
15LangChain4j Java AI 应用开发实战(十五):高级 RAG 技术(上)- 查询压缩、路由与重排序
16LangChain4j Java AI 应用开发实战(十六):高级 RAG 技术(下)- 元数据过滤与多检索器融合
17LangChain4j Java AI 应用开发实战(十七):企业知识库实战 - 从文档导入到智能问答
18LangChain4j Java AI 应用开发实战(十八):Agentic AI 入门 - 理解核心概念与单步 Agent
19LangChain4j Java AI 应用开发实战(十九):顺序工作流 - 多阶段流水线设计模式
20LangChain4j Java AI 应用开发实战(二十):循环工作流 - 自动迭代直到质量达标


前言

在第19篇文章中,我们学习了顺序工作流,它将多个 Agent 按固定顺序串联执行。但在实际业务场景中,往往需要反复迭代才能完成任务:

场景 1:代码审查

AI 生成代码 → 人工审查 → 发现问题 → 重新生成 → 再次审查...

问题:
- 需要迭代多少次?❓
- 什么时候停止?❓
- 如果一直达不到要求怎么办?❓

场景 2:内容创作

AI 写文章 → 编辑审核 → 修改建议 → 重写 → 再审...

挑战:
- 质量评估标准如何量化?❓
- 如何避免无限循环?❓

这就是循环工作流的价值所在!

什么是循环工作流?

循环工作流(Loop Workflow)是一种带有退出条件的迭代执行模式,它让多个 Agent 反复执行,直到满足某个条件或达到最大迭代次数。

┌─────────────┐
│  初始化状态  │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│  Agent A    │ ←───┐
└──────┬──────┘     │
       │            │
       ▼            │
┌─────────────┐     │
│  Agent B    │     │
└──────┬──────┘     │
       │            │
       ▼            │
┌─────────────┐     │
│ 退出条件判断 │     │
└──┬──────┬───┘     │
   │      │         │
  YES    NO ────────┘
   │
   ▼
┌─────────────┐
│  输出结果    │
└─────────────┘

核心优势

自我修正:根据反馈不断优化结果
自动化迭代:无需人工干预的持续改进
质量保证:通过明确的退出条件确保结果达标
灵活控制:最大迭代次数防止无限循环


一、循环工作流的核心概念

1.1 三大核心组件

① 子代理列表(Sub Agents)

循环工作流由多个 Agent 组成,它们会按顺序重复执行

CvReviewer cvReviewer = AgenticServices.agentBuilder(CvReviewer.class)
    .chatModel(openAiChatModel)
    .outputKey("cvReview")  // 评审结果
    .build();

ScoredCvTailor scoredCvTailor = AgenticServices.agentBuilder(ScoredCvTailor.class)
    .chatModel(openAiChatModel)
    .outputKey("cv")  // 优化后的简历
    .build();

// 循环执行顺序:cvReviewer → scoredCvTailor → cvReviewer → scoredCvTailor → ...

执行流程

第1轮:cvReviewer(初始简历) → scoredCvTailor(优化) 
第2轮:cvReviewer(优化后简历) → scoredCvTailor(再优化)
第3轮:cvReviewer(再优化后简历) → scoredCvTailor(最终版本)
...

② 退出条件(Exit Condition)

退出条件是循环工作流的灵魂,它决定了何时停止迭代:

.exitCondition(agenticScope -> {
    CvReview review = (CvReview) agenticScope.readState("cvReview");
    System.out.println("检查退出条件,评分=" + review.score);
    return review.score > 0.8;  // 评分超过 0.8 就停止
})

常见退出条件

  • ✅ 质量评分达到阈值(如 score > 0.8)
  • ✅ 特定关键词出现(如 “PASS”、“APPROVED”)
  • ✅ 连续两次迭代结果差异小于某个值
  • ✅ 外部 API 返回成功标志

③ 最大迭代次数(Max Iterations)

这是安全保护机制,防止退出条件永远无法满足导致死循环:

.maxIterations(3)  // 最多迭代 3 次

为什么需要?

  • 🛡️ 防止 AI 永远无法达到理想状态
  • 🛡️ 避免无限消耗 Token 和费用
  • 🛡️ 保证程序最终会终止

二、实战案例:简历评审与优化循环

2.1 业务场景

我们要构建一个智能简历优化系统

输入:
- 原始简历(master_cv.txt)
- 职位描述(job_description_backend.txt)

处理流程:
1. 评审员(CvReviewer):根据职位描述评审简历,给出评分和反馈
2. 优化器(ScoredCvTailor):根据评审反馈优化简历
3. 重复步骤 1-2,直到评分 > 0.8 或达到最大迭代次数

输出:
- 优化后的简历
- 完整的评审历史

2.2 定义 Agent 接口

① 简历评审器(CvReviewer)

package com.langchain4j.agentic._03_loop_workflow;

import com.langchain4j.domain.CvReview;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

/**
 * 简历评审器接口 - 根据职位描述评审简历,给出评分和反馈
 */
public interface CvReviewer {

    @Agent("根据特定指令评审简历,给出反馈和评分。考虑简历与职位的匹配程度")
    @SystemMessage("""
            你是以下职位的招聘经理:
            {{jobDescription}}
            你需要评审申请人的简历,并决定从众多申请人中邀请谁参加现场面试。
            你会给每份简历一个评分和反馈(包括优点和缺点)。
            你可以忽略缺少地址和占位符等内容。
            """)
    @UserMessage("""
            请评审这份简历:{{cv}}
            """)
    CvReview reviewCv(@V("cv") String cv, @V("jobDescription") String jobDescription);
}

关键点

  • 返回类型是 CvReview(结构化输出),包含 scorefeedback
  • SystemMessage 定义了角色和评审标准
  • UserMessage 传入待评审的简历

② 带评分的简历优化器(ScoredCvTailor)

package com.langchain4j.agentic._03_loop_workflow;

import com.langchain4j.domain.CvReview;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

/**
 * 带评分的简历定制器接口 - 根据评审反馈定制简历
 */
public interface ScoredCvTailor {

    @Agent("根据特定指令定制简历")
    @SystemMessage("""
            以下是一份需要根据特定职位描述、反馈或其他指令进行定制的简历。
            你可以优化简历以满足要求,但不要虚构事实。
            如果删除不相关的内容能使简历更好地符合指令,可以删除。
            目标是让申请人获得面试机会,并能够在面试中展现简历中的能力。
            当前简历:{{cv}}
            """)
    @UserMessage("""
            以下是定制简历的指令和反馈:
            (再次强调,不要发明原始简历中不存在的事实。
            如果申请人不适合,突出其现有特征中最匹配的部分,但不要编造事实)
            评审结果:{{cvReview}}
            """)
    String tailorCv(@V("cv") String cv, @V("cvReview") CvReview cvReview);
}

关键点

  • 接收两个参数:当前简历 cv 和评审结果 cvReview
  • 根据评审反馈进行针对性优化
  • 强调"不要虚构事实",保持真实性

③ 评审结果数据结构(CvReview)

package com.langchain4j.domain;

import dev.langchain4j.model.output.structured.Description;

/**
 * 简历评审结果类 - 存储简历评审的评分和反馈信息
 */
public class CvReview {
    @Description("邀请该候选人参加面试的可能性评分,范围从 0 到 1")
    public double score;

    @Description("对简历的反馈,包括优点、需要改进的地方、缺失的技能、警示信号等")
    public String feedback;

    public CvReview() {} // 反序列化需要无参构造函数

    public CvReview(double score, String feedback) {
        this.score = score;
        this.feedback = feedback;
    }

    @Override
    public String toString() {
        return "\n简历评审: " +
                " - 评分 = " + score +
                "\n- 反馈 = \"" + feedback + "\"\n";
    }
}

关键点

  • 使用 @Description 注解指导 AI 生成结构化输出
  • score 字段用于退出条件判断
  • feedback 字段用于指导下一次优化

2.3 构建循环工作流(基础版)

完整代码

package com.langchain4j;

import com.langchain4j.agentic._03_loop_workflow.CvReviewer;
import com.langchain4j.agentic._03_loop_workflow.ScoredCvTailor;
import com.langchain4j.domain.CvReview;
import com.langchain4j.util.StringLoader;
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Map;

@SpringBootTest
public class _03_LoopWorkflowTest {

    @Autowired
    private OpenAiChatModel openAiChatModel;

    /**
     * 测试无类型代理的循环工作流
     * 使用 UntypedAgent 创建循环工作流,通过 Map 传递参数
     */
    @Test
    public void testLoop() throws Exception {

        // 1. 创建子代理
        CvReviewer cvReviewer = AgenticServices.agentBuilder(CvReviewer.class)
                .chatModel(openAiChatModel)
                .outputKey("cvReview")  // 评审结果存入 AgenticScope
                .build();
        
        ScoredCvTailor scoredCvTailor = AgenticServices.agentBuilder(ScoredCvTailor.class)
                .chatModel(openAiChatModel)
                .outputKey("cv")  // 优化后的简历存入 AgenticScope
                .build();

        // 2. 构建循环工作流
        UntypedAgent reviewedCvGenerator = AgenticServices
                .loopBuilder()
                .subAgents(cvReviewer, scoredCvTailor)  // 按顺序循环执行
                .outputKey("cv")  // 最终输出优化后的简历
                .exitCondition(agenticScope -> {
                    CvReview review = (CvReview) agenticScope.readState("cvReview");
                    System.out.println("检查退出条件,评分=" + review.score);
                    return review.score > 0.8;  // 评分超过 0.8 就停止
                })
                .maxIterations(3)  // 最多迭代 3 次,防止死循环
                .build();

        // 3. 加载输入数据
        String masterCv = StringLoader.loadFromResource("/documents/master_cv.txt");
        String jobDescription = StringLoader.loadFromResource("/documents/job_description_backend.txt");

        // 4. 准备参数
        Map<String, Object> arguments = Map.of(
                "cv", masterCv,  // 从原始简历开始
                "jobDescription", jobDescription
        );

        // 5. 调用工作流
        String tailoredCv = (String) reviewedCvGenerator.invoke(arguments);

        // 6. 输出结果
        System.out.println("=== 评审后的简历(无类型) ===");
        System.out.println(tailoredCv);
    }
}

代码解析

关键步骤拆解

① 创建子代理
CvReviewer cvReviewer = AgenticServices.agentBuilder(CvReviewer.class)
    .chatModel(openAiChatModel)
    .outputKey("cvReview")  // 这会在每次迭代中更新,为下一次定制提供新的反馈
    .build();

作用

  • outputKey("cvReview"):将评审结果存入 AgenticScope
  • 每次迭代都会更新这个值,供下一轮的 ScoredCvTailor 使用
② 构建循环工作流
UntypedAgent reviewedCvGenerator = AgenticServices
    .loopBuilder()
    .subAgents(cvReviewer, scoredCvTailor)  // 可以添加任意数量的子代理,顺序很重要
    .outputKey("cv")  // 这是我们想要观察的最终输出(改进后的简历)
    .exitCondition(agenticScope -> {
        CvReview review = (CvReview) agenticScope.readState("cvReview");
        System.out.println("检查退出条件,评分=" + review.score);
        return review.score > 0.8;
    })
    .maxIterations(3)  // 安全措施,避免无限循环
    .build();

参数说明

  • subAgents():指定循环执行的 Agent 列表
  • outputKey("cv"):指定最终输出的键名
  • exitCondition():退出条件,每次迭代后都会检查
  • maxIterations(3):最大迭代次数
③ 调用工作流
Map<String, Object> arguments = Map.of(
    "cv", masterCv,  // 从主简历开始,它会被持续改进
    "jobDescription", jobDescription
);

String tailoredCv = (String) reviewedCvGenerator.invoke(arguments);

注意

  • 使用 UntypedAgent 时,需要通过 Map 传递参数
  • 返回值需要强制转换为对应类型

2.4 运行效果

检查退出条件,评分=0.0
检查退出条件,评分=0.3
检查退出条件,评分=0.8

=== 评审后的简历(无类型) ===
John Doe
后端开发工程师

个人简介
热衷于构建可扩展后端系统的软件工程师,拥有分布式系统和微服务架构的专业知识...

工作经历
- 设计并实现了基于 Spring Boot 的微服务架构...
- 领导了数据库优化项目,将查询性能提升了 40%...

技能
- 精通 Java、Spring Boot、微服务架构...
- 熟悉 Docker、Kubernetes、CI/CD...

执行过程分析

第1轮:
- CvReviewer 评分:0.0(完全不匹配)
- ScoredCvTailor 根据反馈优化简历

第2轮:
- CvReviewer 评分:0.3(有所改进,但仍不足)
- ScoredCvTailor 继续优化

第3轮:
- CvReviewer 评分:0.8(达到阈值,退出循环)
- 输出最终优化后的简历

三、高级用法:追踪迭代历史

3.1 问题:如何获取完整的评审历史?

在基础版中,我们只能看到最终的简历和最后一次评审。但有时我们需要:

  • 📊 查看每一次迭代的评分变化趋势
  • 📝 对比不同版本的优化内容
  • 🔍 分析 AI 的决策过程

3.2 解决方案:自定义输出处理器

@Test
public void testLoopWithHistory() throws Exception {
    
    // 1. 创建子代理(与之前相同)
    CvReviewer cvReviewer = AgenticServices.agentBuilder(CvReviewer.class)
            .chatModel(openAiChatModel)
            .outputKey("cvReview")
            .build();
    
    ScoredCvTailor scoredCvTailor = AgenticServices.agentBuilder(ScoredCvTailor.class)
            .chatModel(openAiChatModel)
            .outputKey("cv")
            .build();

    // 2. 创建历史记录列表
    List<CvReview> reviewHistory = new ArrayList<>();

    // 3. 构建循环工作流(增强版)
    UntypedAgent reviewedCvGenerator = AgenticServices
            .loopBuilder()
            .subAgents(cvReviewer, scoredCvTailor)
            .outputKey("cvAndReview")  // 自定义输出键
            .output(agenticScope -> {
                // 自定义输出处理器:提取简历和最终评审
                Map<String, Object> cvAndReview = Map.of(
                        "cv", agenticScope.readState("cv"),
                        "finalReview", agenticScope.readState("cvReview")
                );
                return cvAndReview;
            })
            .exitCondition(scope -> {
                CvReview review = (CvReview) scope.readState("cvReview");
                reviewHistory.add(review);  // ⭐ 捕获每次迭代的评审结果
                System.out.println("退出检查,评分=" + review.score);
                return review.score >= 0.8;
            })
            .maxIterations(3)
            .build();

    // 4. 加载输入数据
    String masterCv = StringLoader.loadFromResource("/documents/master_cv.txt");
    String fluteJobDescription = "我们正在寻找一位热情的长笛老师加入我们的音乐学院。";

    // 5. 准备参数
    Map<String, Object> arguments = Map.of(
            "cv", masterCv,
            "jobDescription", fluteJobDescription
    );

    // 6. 调用工作流
    Map<String, Object> cvAndReview = (Map<String, Object>) reviewedCvGenerator.invoke(arguments);

    // 7. 输出结果
    System.out.println("=== 长笛老师的评审后简历 ===");
    System.out.println(cvAndReview.get("cv"));

    // 8. 检查最终评审
    CvReview review = (CvReview) cvAndReview.get("finalReview");
    System.out.println("=== 长笛老师的最终评审 ===");
    System.out.println("简历" + (review.score >= 0.8 ? "通过" : "未通过") + ",评分=" + review.score);
    System.out.println("最终反馈:" + review.feedback);

    // 9. 输出完整评审历史
    System.out.println("=== 长笛老师的完整评审历史 ===");
    for (int i = 0; i < reviewHistory.size(); i++) {
        System.out.println("第 " + (i + 1) + " 轮:" + reviewHistory.get(i));
    }
}

3.3 运行效果

退出检查,评分=0.0
退出检查,评分=0.3
退出检查,评分=0.4

=== 长笛老师的评审后简历 ===
John Doe
音乐教育爱好者

个人简介
充满热情的音乐爱好者,具备良好的沟通能力和教学经验...

=== 长笛老师的最终评审 ===
简历未通过,评分=0.4
最终反馈:John Doe 展示了强大的软技能和指导经验,但缺乏正式的音乐培训...

=== 长笛老师的完整评审历史 ===
第 1 轮:
简历评审:  - 评分 = 0.0
- 反馈 = "这份简历不适合我们音乐学院的长笛老师职位,完全没有音乐相关背景..."

第 2 轮:
简历评审:  - 评分 = 0.3
- 反馈 = "John 的简历展示了强大的软技能,如沟通、耐心和适应能力,这些在教学角色中很重要。然而,缺乏正式的音乐培训..."

第 3 轮:
简历评审:  - 评分 = 0.4
- 反馈 = "John Doe 展示了强大的软技能和指导经验,但仍然没有音乐资质..."

关键改进

  • reviewHistory 列表记录了每次迭代的评审结果
  • .output() 方法自定义了输出格式
  • ✅ 可以看到评分的变化趋势(0.0 → 0.3 → 0.4)

四、核心机制深度解析

4.1 AgenticScope 状态管理

AgenticScope 是循环工作流的共享状态容器,它在整个迭代过程中持续存在。

工作原理

初始化阶段:
AgenticScope {
    "cv": "原始简历内容",
    "jobDescription": "职位描述"
}

第1轮迭代后:
AgenticScope {
    "cv": "优化后的简历 v1",
    "jobDescription": "职位描述",
    "cvReview": { score: 0.0, feedback: "..." }  // 新增
}

第2轮迭代后:
AgenticScope {
    "cv": "优化后的简历 v2",  // 更新
    "jobDescription": "职位描述",
    "cvReview": { score: 0.3, feedback: "..." }  // 更新
}

第3轮迭代后:
AgenticScope {
    "cv": "优化后的简历 v3",  // 更新
    "jobDescription": "职位描述",
    "cvReview": { score: 0.8, feedback: "..." }  // 更新
}

读取和写入状态

// 读取状态
CvReview review = (CvReview) agenticScope.readState("cvReview");
String cv = (String) agenticScope.readState("cv");

// 写入状态(通常由 outputKey 自动完成)
// 当 Agent 执行完成后,结果会自动存入 AgenticScope

4.2 退出条件的执行时机

重要:退出条件在每次 Agent 调用后都会检查,而不仅仅是在整个循环结束后。

执行顺序:
1. 执行 cvReviewer
2. 检查退出条件 ❌ 不满足
3. 执行 scoredCvTailor
4. 检查退出条件 ❌ 不满足
5. 执行 cvReviewer(第2轮)
6. 检查退出条件 ❌ 不满足
7. 执行 scoredCvTailor(第2轮)
8. 检查退出条件 ✅ 满足,退出循环

验证方法

.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    System.out.println("退出检查,评分=" + review.score);  // 每次都会打印
    return review.score >= 0.8;
})

输出:

退出检查,评分=0.0  // cvReviewer 执行后
退出检查,评分=0.0  // scoredCvTailor 执行后(cvReview 未更新)
退出检查,评分=0.3  // cvReviewer 执行后
退出检查,评分=0.3  // scoredCvTailor 执行后
退出检查,评分=0.8  // cvReviewer 执行后,满足条件,退出

4.3 最大迭代次数的计算

maxIterations(3) 表示最多执行 3 轮完整的循环

计算方式

1 轮循环 = 所有子 Agent 各执行一次

如果有 2 个子 Agent:
- maxIterations(3) = 最多执行 6 次 Agent 调用(2 × 3)

示例

.loopBuilder()
    .subAgents(agentA, agentB, agentC)  // 3 个子 Agent
    .maxIterations(3)  // 最多 3 轮
    .build();

// 最多执行次数:3 × 3 = 9 次 Agent 调用

五、常见问题与避坑指南

5.1 问题 1:退出条件永远无法满足(死循环)

症状

退出检查,评分=0.2
退出检查,评分=0.3
退出检查,评分=0.3
退出检查,评分=0.3
...(一直循环)

原因

  • AI 无法达到设定的质量标准
  • 退出条件设置过高(如 score > 0.95)
  • Agent 陷入局部最优

解决方案

// ✅ 方案 1:设置合理的 maxIterations
.maxIterations(3)  // 最多迭代 3 次

// ✅ 方案 2:降低退出条件阈值
.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    return review.score >= 0.6;  // 从 0.8 降低到 0.6
})

// ✅ 方案 3:添加超时控制
.timeout(Duration.ofMinutes(5))  // 最多执行 5 分钟

5.2 问题 2:无法访问中间结果

症状

// 只能获取最终结果,看不到迭代过程
String result = (String) workflow.invoke(arguments);

解决方案

// ✅ 使用 .output() 自定义输出处理器
.output(agenticScope -> {
    Map<String, Object> result = Map.of(
        "cv", agenticScope.readState("cv"),
        "finalReview", agenticScope.readState("cvReview"),
        "allReviews", reviewHistory  // 包含完整历史
    );
    return result;
})

5.3 问题 3:参数传递失败

症状

Exception: Parameter 'cv' not found in AgenticScope

原因

  • 初始参数中没有提供必需的键
  • outputKey 配置错误

解决方案

// ✅ 确保初始参数包含所有必需的键
Map<String, Object> arguments = Map.of(
    "cv", masterCv,  // 必须提供
    "jobDescription", jobDescription  // 必须提供
);

// ✅ 确保 outputKey 与 Agent 的参数名匹配
.outputKey("cv")  // 对应 @V("cv") String cv

5.4 问题 4:评分停滞不前

症状

第1轮:评分=0.3
第2轮:评分=0.3
第3轮:评分=0.3

原因

  • AI 无法找到进一步优化的方向
  • 反馈不够具体

解决方案

// ✅ 方案 1:提供更详细的反馈要求
@SystemMessage("""
    请给出具体的改进建议,包括:
    1. 缺少哪些关键技能
    2. 哪些经历需要强调
    3. 哪些内容应该删除
    """)

// ✅ 方案 2:增加迭代次数
.maxIterations(5)  // 从 3 增加到 5

// ✅ 方案 3:调整评分标准
.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    // 如果连续两次评分相同,提前退出
    if (reviewHistory.size() >= 2) {
        CvReview lastReview = reviewHistory.get(reviewHistory.size() - 2);
        if (lastReview.score == review.score) {
            return true;  // 评分停滞,提前退出
        }
    }
    return review.score >= 0.8;
})

六、适用场景与性能分析

6.1 适用场景

场景说明示例
代码生成与审查反复迭代直到代码质量达标AI 写代码 → 静态检查 → 修复 → 再检查
内容创作多次修改直到满足编辑标准AI 写文章 → 编辑审核 → 修改 → 再审
数据清洗持续优化直到数据质量合格AI 清洗数据 → 验证规则 → 修正 → 再验证
设计优化根据反馈不断改进设计方案AI 设计 UI → 用户测试 → 改进 → 再测试
翻译校对多轮校对直到翻译质量达标AI 翻译 → 人工评分 → 修改 → 再评分

6.2 不适用场景

简单任务:一次性就能完成,不需要迭代
实时性要求高:循环迭代会增加延迟
成本敏感:多次迭代会消耗更多 Token
无法量化质量:没有明确的退出条件

6.3 性能对比

指标顺序工作流循环工作流
执行次数固定(N 个 Agent 执行 N 次)动态(取决于退出条件)
延迟低(一次性执行)高(可能多次迭代)
成本可控(已知执行次数)不可控(取决于迭代次数)
质量一般(无自我修正)高(持续优化)
复杂度中(需要设计退出条件)

七、最佳实践

7.1 设计退出条件

原则

  1. 可量化:使用数值评分、布尔值等明确指标
  2. 可达性:确保 AI 有能力达到该标准
  3. 容错性:设置最大迭代次数作为兜底

示例

// ✅ 好的退出条件
.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    return review.score >= 0.8;  // 明确的数值阈值
})

// ❌ 不好的退出条件
.exitCondition(scope -> {
    // 模糊的判断标准
    String cv = (String) scope.readState("cv");
    return cv.contains("perfect");  // AI 可能永远不会生成这个词
})

7.2 设置最大迭代次数

建议

  • 📌 对于简单任务:maxIterations(2-3)
  • 📌 对于中等复杂任务:maxIterations(5-7)
  • 📌 对于复杂任务:maxIterations(10+) + 超时控制
.loopBuilder()
    .subAgents(cvReviewer, scoredCvTailor)
    .maxIterations(3)  // 简单任务,3 次足够
    .timeout(Duration.ofMinutes(5))  // 超时保护
    .build();

7.3 记录迭代历史

价值

  • 📊 分析 AI 的优化轨迹
  • 🐛 调试问题时追溯原因
  • 📈 评估工作流的效果
List<CvReview> reviewHistory = new ArrayList<>();

.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    reviewHistory.add(review);  // 记录每次迭代
    return review.score >= 0.8;
})

7.4 提供清晰的反馈

原则

  • 📝 反馈要具体,指出具体问题
  • 🎯 给出明确的改进方向
  • ⚖️ 平衡正面和负面反馈

示例

@SystemMessage("""
    请给出具体的改进建议,包括:
    1. 缺少哪些关键技能(列举具体技术名称)
    2. 哪些经历需要强调(指出具体项目)
    3. 哪些内容应该删除(说明原因)
    
    同时也要指出简历的优点,鼓励候选人。
    """)

八、进阶技巧

8.1 动态调整退出条件

根据任务难度动态调整阈值:

.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    String jobDescription = (String) scope.readState("jobDescription");
    
    // 根据职位描述的难度调整阈值
    double threshold = jobDescription.contains("高级") ? 0.9 : 0.7;
    
    return review.score >= threshold;
})

8.2 结合并行工作流

在循环内部使用并行工作流加速执行:

// 伪代码示例
ParallelAgent parallelAgent = AgenticServices.parallelBuilder()
    .subAgents(reviewer1, reviewer2, reviewer3)  // 三个评审员并行
    .aggregator((results) -> {
        // 合并三个评审结果,取平均分
        double avgScore = results.stream()
            .mapToDouble(r -> r.score)
            .average()
            .orElse(0.0);
        return new CvReview(avgScore, "综合评审");
    })
    .build();

.loopBuilder()
    .subAgents(parallelAgent, scoredCvTailor)  // 并行评审 + 优化
    .exitCondition(scope -> ...)
    .build();

8.3 多条件退出

组合多个退出条件:

.exitCondition(scope -> {
    CvReview review = (CvReview) scope.readState("cvReview");
    
    // 条件 1:评分达标
    boolean scoreMet = review.score >= 0.8;
    
    // 条件 2:反馈中包含关键词
    boolean feedbackPositive = review.feedback.contains("推荐");
    
    // 条件 3:迭代次数达到上限
    boolean maxIterReached = reviewHistory.size() >= 5;
    
    // 满足任一条件就退出
    return scoreMet || feedbackPositive || maxIterReached;
})

九、结语

本文深入讲解了循环工作流的三大核心组件——子代理列表、退出条件与最大迭代次数,通过简历评审→优化的迭代实战,完整演示了"自动迭代直到质量达标"的设计模式。循环工作流的精髓在于以量化指标驱动自我修正:退出条件让系统知道何时停止,最大迭代次数提供安全兜底,迭代历史则让优化过程透明可追溯。至此,你已经掌握了顺序、循环两种基础编排模式,下一篇我们将学习并行工作流,看如何让多个 Agent 同时执行,将任务效率提升数倍。敬请期待!


在这里插入图片描述

🎯🔖更多专栏系列文章:AI大模型提示工程完全指南AI大模型探索之路(零基础入门)AI大模型预训练微调进阶AI大模型开源精选实践AI大模型Spring AI开发实战🔥🔥🔥 其他专栏可以查看博客主页

🔔 关于作者:资深程序老猿,10年+架构经验,现专注 AIGC 探索与实践。
👍 若文章对你有所触动,恳请点赞 ⭐ 关注 ⭐ 收藏!AI 浪潮已至,愿与你同行。
你同行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻道AI小兵

🐳 感谢你的巨浪支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值