用 LangChain4j 构建代码智能体:Supervisor 与 Workflow 的调试取舍

2026-07-24 25 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

让代码智能体自己编写、测试并修复代码,难点通常不在于一次模型调用,而在于如何组织多轮行动。围绕 LangChain4j 文档进行的这项实验表明,智能体框架的架构选择会直接影响调试任务的执行速度、可控性和应对异常的能力。

实验聚焦于一个能够自主完成“写代码、跑测试、定位问题、修改代码、再次验证”的编码框架。其中值得关注的是两种常见组织方式:由中央决策者协调的 Supervisor,以及预先编排步骤的 Workflow。

Supervisor:把下一步交给协调者决定

Supervisor 模式中,多个专职能力通常被拆开:代码生成、测试执行、错误分析、修复建议等。一个主管智能体读取当前状态,决定下一步该调用哪个能力,并把结果写回共享上下文。

这种方式适合调试路径不确定的任务。例如测试失败后,系统可能需要:

  • 先重新读取相关实现和测试;
  • 检查编译错误还是断言失败;
  • 查询依赖或框架文档;
  • 生成补丁后选择局部测试或完整回归。

它的优势是灵活。错误信息出现新的线索时,Supervisor 可以改变路线,而不必强行走完固定步骤。代价也很明确:每一轮决策都可能增加模型调用、上下文整理和工具编排成本;如果缺少停止条件,还可能在“分析 - 修复 - 再分析”之间循环。

Workflow:让高频路径走固定流水线

Workflow 模式将步骤提前定义,例如:生成实现、执行测试、提取失败信息、修复、复测。每一步的输入输出更稳定,执行也更快,尤其适合有明确验收条件的重复工作。

对于常规编码任务,固定工作流可以减少主管决策的开销:测试通过就结束,测试失败才进入修复分支。它也更容易监控,因为每个节点都有清晰的职责和可观测结果。

不过,工作流的边界就是它的约束。当失败原因超出预设分支,例如环境异常、依赖版本冲突或需求本身含糊时,系统往往需要补充大量分支,最终又接近一个隐式的 Supervisor。

来源中的结论不是“某一种架构总是更好”,而是两者在调试中交换了不同能力:Supervisor 换取适应性,Workflow 换取执行速度和确定性。

可以这样实践:先把路由逻辑写成可测试代码

下面的示例不依赖具体模型 SDK,用纯 Java 模拟编码智能体的两种调度方式。它可以直接运行,用来验证你对状态、停止条件和重试次数的设计;接入 LangChain4j 时,将 runTestrepairanalyze 替换为工具调用或 AI Service 即可。

将文件保存为 WorkflowVsSupervisor.java,然后运行 javac WorkflowVsSupervisor.java && java WorkflowVsSupervisor

import java.util.ArrayList;
import java.util.List;

public class WorkflowVsSupervisor {
    record State(String code, boolean testsPass, int attempts, List<String> log) {
        State next(String code, boolean testsPass, String event) {
            List<String> nextLog = new ArrayList<>(log);
            nextLog.add(event);
            return new State(code, testsPass, attempts + 1, nextLog);
        }
    }

    static State runTest(State state) {
        boolean pass = state.code().contains("return a + b;");
        return state.next(state.code(), pass, pass ? "tests: pass" : "tests: add() returns the wrong value");
    }

    static State repair(State state) {
        String fixed = "int add(int a, int b) { return a + b; }";
        return state.next(fixed, false, "repair: replaced implementation");
    }

    static State workflow(State initial) {
        State tested = runTest(initial);
        return tested.testsPass() ? tested : runTest(repair(tested));
    }

    static State supervisor(State initial) {
        State current = initial;
        while (!current.testsPass() && current.attempts() < 4) {
            current = current.log().isEmpty() ? runTest(current) : repair(current);
            if (!current.testsPass() && current.code().contains("return a + b;")) {
                current = runTest(current);
            }
        }
        return current;
    }

    public static void main(String[] args) {
        State initial = new State("int add(int a, int b) { return a - b; }", false, 0, List.of());
        System.out.println("workflow  = " + workflow(initial).log());
        System.out.println("supervisor = " + supervisor(initial).log());
    }
}

示例刻意保留了两个工程约束。attempts < 4 是硬停止条件,避免自主调试无限循环;状态中的 log 则是最小审计记录。生产环境还应保存模型输入、工具输出、补丁 diff、测试命令和退出码,才能复盘智能体为何做出某个修复决定。

LangChain4j 接入时的职责划分

接入 LangChain4j 后,可以将模型限制在它擅长的判断与生成环节,而将副作用操作交给可控工具:

  • readFilesearchCode 提供受限的代码上下文;
  • runTests 在隔离环境中执行,并返回结构化结果;
  • applyPatch 只接受统一 diff,且限制可修改目录;
  • Supervisor 只返回下一个动作及理由,不能直接绕过工具修改工作区;
  • Workflow 对已知的“修改后必须测试”规则做强制编排。

这类边界比提示词本身更重要。若允许模型任意执行 shell 命令、读取全部密钥或跳过测试,智能体即使短期看起来高效,也会带来明显的安全与质量风险。

选择建议

可以从 Workflow 起步:它适合格式化、生成样板代码、执行既定测试和处理常见失败。等到任务经常遇到分支不明、需要跨文件调查或必须选择不同工具时,再在工作流外层加入 Supervisor。

实际系统常常采用混合方案:Workflow 负责确定性步骤,Supervisor 只处理异常路由和复杂诊断。上线前至少检查三件事:每个循环是否有预算,工具权限是否最小化,以及每次补丁是否经过可重复执行的验证。这会比单纯增加模型能力更直接地提升代码智能体的可靠性。


相关推荐