12-LangChain4J核心组件综合对比

0 阅读9分钟

深度对比ChatModel、Chain、Agent、Memory四大组件,掌握场景选型和组合使用策略

时间:30分钟 | 难度:⭐⭐⭐ | Week 2 Day 12


官方Example信息

  • GitHub链接langchain4j-examples
  • 相关Example:各组件的示例代码
  • 所在路径:src/main/java/dev/langchain4j/examples/
  • 难度:中级 ⭐⭐⭐

学习目标

  • 理解四大组件的核心区别
  • 掌握组件选型的决策方法
  • 了解各组件的性能和成本差异
  • 学会多个组件的组合使用
  • 能根据场景做出最优选择
  • 理解组件间的依赖关系

🚀 快速入门:四大组件概览

组件关系架构图

┌────────────────────────────────────────────┐
│              LangChain4J 核心组件           │
├────────────────────────────────────────────┤
│                                            │
│  ┌──────────┐    ┌──────────┐              │
│  │ChatModel │    │ Memory   │              │
│  │ 基础对话  │    │ 记忆管理  │              │
│  └─────┬────┘    └────┬─────┘              │
│        │              │                    │
│        ▼              ▼                    │
│  ┌──────────────────────────┐              │
│  │        Chain             │              │
│  │   多步骤编排(固定流程)   │              │
│  └─────────────┬────────────┘              │
│                │                           │
│                ▼                           │
│  ┌──────────────────────────┐              │
│  │        Agent             │              │
│  │  自主决策(动态流程)      │              │
│  │  = ChatModel + Tool + 循环│              │
│  └──────────────────────────┘              │
│                                            │
│  复杂度:ChatModel < Chain < Agent          │
│  灵活度:ChatModel < Chain < Agent          │
│  成本:  ChatModel < Chain < Agent          │
└────────────────────────────────────────────┘

深度讲解

1️⃣ 核心特性对比表

维度ChatModelChainAgentMemory
本质单次LLM调用多步骤编排自主决策循环状态管理
流程控制人类编排LLM自主辅助角色
工具调用不支持手动集成自动调用不适用
使用场景简单问答固定工作流复杂推理多轮对话
延迟100-500ms300-2000ms500-10000ms+50ms
成本低(1次调用)中(N次调用)高(N×M次调用)额外Token
可控性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
灵活性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
实现难度简单中等复杂中等

2️⃣ 同一任务的三种实现对比

场景:用户问"分析这段代码的问题"

方式A:ChatModel 直接调用

// 最简单,一次调用搞定
@Service
public class SimpleChatModelApproach {
    @Autowired
    private ChatLanguageModel model;

    public String analyzeCode(String code) {
        String prompt = """
            请分析以下Java代码的问题(包括安全、性能、风格):
            ```java
            %s
            ```
            """.formatted(code);

        return model.generate(prompt);
    }
}
// 优点:简单、快速、成本低
// 缺点:分析深度取决于LLM能力,无法调用外部工具
// 延迟:~300ms | 成本:~200 tokens | 准确性:⭐⭐⭐

方式B:Chain 多步编排

// 分步骤执行,每步专注一个方面
@Service
public class ChainApproach {
    @Autowired
    private ChatLanguageModel model;

    public CodeReviewResult analyzeCode(String code) {
        // Step 1: 安全分析
        String securityPrompt = "请只分析代码的安全问题:\n" + code;
        String security = model.generate(securityPrompt);

        // Step 2: 性能分析
        String performancePrompt = "请只分析代码的性能问题:\n" + code;
        String performance = model.generate(performancePrompt);

        // Step 3: 风格分析
        String stylePrompt = "请只分析代码的风格问题:\n" + code;
        String style = model.generate(stylePrompt);

        // Step 4: 综合报告
        String summaryPrompt = """
            基于以下分析,给出综合报告和评分:
            安全:%s
            性能:%s
            风格:%s
            """.formatted(security, performance, style);
        String summary = model.generate(summaryPrompt);

        return new CodeReviewResult(security, performance, style, summary);
    }
}
// 优点:每步分析更深入,结果结构化
// 缺点:固定4步,代码简单时浪费,需要人工编排
// 延迟:~1200ms (4×300ms) | 成本:~800 tokens | 准确性:⭐⭐⭐⭐

方式C:Agent 自主决策

// Agent自己决定分析什么、怎么分析
public interface CodeReviewAgent {
    @SystemMessage("""
        你是代码审查专家。
        根据代码的特点决定审查重点。
        使用工具进行深入分析。
        """)
    String analyzeCode(@UserMessage String request);
}

@Service
public class AgentApproach {
    @Autowired
    private CodeReviewAgent agent;

    public String analyzeCode(String code) {
        return agent.analyzeCode("请审查:\n" + code);
    }
}
// 优点:智能决策,按需分析,灵活强大
// 缺点:不可预测,可能多次调用工具,成本高
// 延迟:~3000ms (多轮) | 成本:~2000 tokens | 准确性:⭐⭐⭐⭐⭐

🔍 Agent为什么慢、贵、但准?— LangChain4J 实现原理拆解

上面三种方式,ChatModel 一次调用就结束,Chain 是人工编排的固定多次调用,而 Agent 的行为不可预测 — 它自己决定调几次、调什么。理解 Agent 的内部实现,你就能明白性能差异的根源。

核心机制:动态代理 + while循环 + 反射
AiServices.build() 做了什么?

你写的代码:
┌─────────────────────────────────────────┐
│ CodeReviewAgent agent = AiServices      │
│     .builder(CodeReviewAgent.class)     │
│     .chatLanguageModel(model)           │
│     .tools(new MyTools())              │
│     .build();                           │
└────────────────┬────────────────────────┘
                 │ 内部等价于
                 ▼
┌─────────────────────────────────────────┐
│ Proxy.newProxyInstance(                  │
│   classLoader,                          │
│   new Class[]{CodeReviewAgent.class},   │
│   new InvocationHandler(model, tools)   │  ← 核心!
│ );                                      │
└─────────────────────────────────────────┘

所以 agent.analyzeCode() 实际上调用的是 InvocationHandler.invoke()
InvocationHandler 内部:Agent循环的"心脏"
/**
 * 伪代码:AiServices 内部的 Agent 循环
 * 这就是 Agent 比 ChatModel 慢和贵的根本原因
 */
class AiServiceInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        // Step 1: 构建初始消息(和ChatModel一样)
        memory.add(SystemMessage.from("你是代码审查专家..."));
        memory.add(UserMessage.from(userInput));

        // Step 2: Agent循环 ← ChatModel没有这个循环!
        for (int i = 0; i < maxIterations; i++) {

            // 2a. 调用LLM(每轮都消耗Token → 成本叠加!)
            Response<AiMessage> response = model.generate(
                memory.messages(),  // 消息越来越长 → Token越来越多
                toolSpecs           // 工具定义(每次都发 → 额外Token)
            );
            AiMessage aiMessage = response.content();
            memory.add(aiMessage);

            // 2b. 判断是否结束
            if (!aiMessage.hasToolExecutionRequests()) {
                return aiMessage.text();  // ← ChatModel走到这里就结束了
            }

            // 2c. 执行工具(Agent独有的步骤)
            for (ToolExecutionRequest req : aiMessage.toolExecutionRequests()) {
                ToolExecutor executor = toolExecutors.get(req.name());
                // JSON参数 → 反射调用你的@Tool方法 → 结果字符串
                String result = executor.execute(req, memoryId);
                memory.add(ToolExecutionResultMessage.from(req, result));
            }
            // → 回到循环顶部,再次调用LLM
        }
    }
}
三种方式的内部调用对比
ChatModel(方式A):
  model.generate(messages)
  ↓
  return 结果
  ──────────────────────────
  LLM调用:1次 | Token消耗:200 | 延迟:300ms

Chain(方式B):
  model.generate(安全prompt)  → 结果1
  model.generate(性能prompt)  → 结果2
  model.generate(风格prompt)  → 结果3
  model.generate(综合prompt)  → 最终结果
  ──────────────────────────
  LLM调用:4次(固定) | Token消耗:800 | 延迟:1200ms

Agent(方式C)— AiServices 内部:
  ┌─ 循环第1轮 ─────────────────────────────────────┐
  │ model.generate(messages, toolSpecs)  → 调用工具A   │
  │ toolA.execute()  → 结果放回messages              │
  ├─ 循环第2轮 ─────────────────────────────────────┤
  │ model.generate(messages, toolSpecs)  → 调用工具B   │  ← 消息更长了
  │ toolB.execute()  → 结果放回messages              │
  ├─ 循环第3轮 ─────────────────────────────────────┤
  │ model.generate(messages, toolSpecs)  → 不调用工具   │  ← 消息最长
  │ return 文本结果                                    │
  └──────────────────────────────────────────────────┘
  LLM调用:3次(不固定) | Token消耗:200+500+800=1500 | 延迟:3000ms

  注意:Agent每轮的Token消耗递增!因为消息历史越来越长。
@Tool → JSON Schema:LLM如何"看见"工具
// 你定义的工具:
@Tool("检查代码中的安全漏洞,如SQL注入、XSS等")
public String checkSecurity(@P("要检查的Java源代码") String code) { ... }

// LangChain4J 转换后发给 LLM API 的 JSON:
// {
//   "type": "function",
//   "function": {
//     "name": "checkSecurity",
//     "description": "检查代码中的安全漏洞,如SQL注入、XSS等",
//     "parameters": {
//       "type": "object",
//       "properties": {
//         "code": { "type": "string", "description": "要检查的Java源代码" }
//       },
//       "required": ["code"]
//     }
//   }
// }

// LLM 基于这个 JSON 描述决定是否调用工具
// → 所以 @Tool 的描述质量直接影响 Agent 的决策准确性!
总结:三种方式的实现本质对比
┌───────────┬─────────────────────┬──────────────────┐
│           │ 实现机制              │ 性能影响          │
├───────────┼─────────────────────┼──────────────────┤
│ ChatModel │ 直接调用 LLM API     │ 最快,成本固定     │
│           │ 无循环,无工具        │                  │
├───────────┼─────────────────────┼──────────────────┤
│ Chain     │ 开发者编写多次        │ N次调用,成本线性   │
│           │ model.generate()    │ 可预测           │
│           │ 手动编排顺序          │                  │
├───────────┼─────────────────────┼──────────────────┤
│ Agent     │ JDK动态代理          │ 不可预测的多次调用  │
│(AiServices)│ + while(hasTools)   │ Token累积递增     │
│           │ + 反射执行@Tool       │ 最慢但最智能      │
└───────────┴─────────────────────┴──────────────────┘

一句话:Agent 之所以准确但昂贵,是因为它用 while 循环
让 LLM 反复思考和调用工具,而每轮的消息历史都在增长。

💡 深入理解:完整的 AiServices 五层实现原理(动态代理、ToolSpecification转换、InvocationHandler引擎、调用链时序图、ToolExecutor反射)见 [[2011-Agent设计原理和基础实战#AiServices的工作原理|Agent实现原理深度解析]]。


3️⃣ 性能基准测试数据

测试环境:GPT-4o-mini,100次运行取平均值

任务:分析一段50行的Java代码

┌───────────────────────────────────────────┐
│          性能对比(100次平均)              │
├──────────┬──────────┬─────────┬───────────┤
│ 指标      │ ChatModel│ Chain   │ Agent    │
├──────────┼──────────┼─────────┼───────────┤
│ 平均延迟  │ 280ms    │ 1,120ms │ 2,800ms  │
│ P99延迟   │ 520ms    │ 2,100ms │ 8,500ms  │
│ Token消耗 │ 180      │ 720     │ 1,800    │
│ 成本/次   │ ¥0.003   │ ¥0.012  │ ¥0.030   │
│ 准确性    │ 72%      │ 85%     │ 93%      │
│ 召回率    │ 60%      │ 82%     │ 91%      │
└──────────┴──────────┴─────────┴───────────┘

结论:
- ChatModel:速度最快,成本最低,但分析不够深入
- Chain:    性价比最高,适合已知流程
- Agent:    最准确,但成本和延迟最高

4️⃣ 决策树:我应该用哪个?

你的任务是什么?
│
├─ 简单问答(一问一答)
│  └─→ ChatModel ✅
│     例:翻译、总结、分类、格式转换
│
├─ 多步固定流程
│  │
│  ├─ 步骤已知且固定?
│  │  └─→ Chain ✅
│  │     例:代码生成→分析→优化
│  │
│  └─ 需要对话上下文?
│     └─→ Chain + Memory ✅
│        例:多轮问答助手
│
├─ 需要使用外部工具?
│  │
│  ├─ 工具调用顺序固定?
│  │  └─→ Chain(手动调用工具)✅
│  │
│  └─ 工具调用需要LLM决策?
│     └─→ Agent ✅
│        例:智能代码审查、自主研究
│
└─ 需要记住历史对话?
   └─→ 任何组件 + Memory ✅

5️⃣ 组合使用模式

/**
 * 模式1:ChatModel + Memory = 多轮对话
 */
@Service
public class ChatWithMemory {
    public String chat(String userId, String message) {
        ChatMemory memory = getOrCreateMemory(userId);
        memory.add(UserMessage.from(message));
        String response = model.generate(memory.messages()).content().text();
        memory.add(AiMessage.from(response));
        return response;
    }
}

/**
 * 模式2:Chain + Memory = 有记忆的工作流
 */
@Service
public class ChainWithMemory {
    public String process(String userId, String input) {
        ChatMemory memory = getOrCreateMemory(userId);

        // Step 1: 理解上下文
        memory.add(UserMessage.from(input));
        String understanding = model.generate(memory.messages()).content().text();

        // Step 2: 基于理解执行任务
        String result = executeTask(understanding);

        memory.add(AiMessage.from(result));
        return result;
    }
}

/**
 * 模式3:Agent + Memory = 有记忆的智能体
 */
public interface SmartAgent {
    @SystemMessage("你是智能助手,记住用户的偏好和历史")
    String assist(@UserMessage String request);
}

@Bean
public SmartAgent smartAgent(ChatLanguageModel model) {
    return AiServices.builder(SmartAgent.class)
        .chatLanguageModel(model)
        .tools(new MyTools())
        .chatMemory(MessageWindowChatMemory.withMaxMessages(50))
        .build();
}

/**
 * 模式4:Agent + Chain = 智能编排
 * Agent决定流程,Chain执行每个步骤
 */
@Service
public class AgentOrchestrator {
    @Autowired
    private CodeReviewAgent agent;  // Agent做决策

    public ReviewResult orchestrate(String code) {
        // Agent分析代码,决定需要哪些检查
        String plan = agent.analyze("分析代码,列出需要的检查项:\n" + code);

        // Chain执行具体检查
        if (plan.contains("安全")) {
            securityChain.execute(code);
        }
        if (plan.contains("性能")) {
            performanceChain.execute(code);
        }

        return combineResults();
    }
}

6️⃣ 成本对比和优化

成本公式:
总成本 = LLM调用次数 × 平均Token数 × Token单价

┌──────────────────────────────────────────────┐
│              成本对比(单次任务)               │
├───────────┬──────────┬──────────┬─────────────┤
│ 组件       │ 调用次数  │ 总Token  │ 成本(GPT-4o)│
├───────────┼──────────┼──────────┼─────────────┤
│ ChatModel │ 1次      │ ~200     │ ¥0.006      │
│ Chain(3步)│ 3次      │ ~600     │ ¥0.018      │
│ Chain(5步)│ 5次      │ ~1000    │ ¥0.030      │
│ Agent     │ 3-8次    │ ~1500    │ ¥0.045      │
│ Agent+Mem │ 3-8次    │ ~2000    │ ¥0.060      │
└───────────┴──────────┴──────────┴─────────────┘

优化策略:
├─ 简单任务用ChatModel(成本/10)
├─ 复杂任务用Chain而非Agent(成本/3)
├─ 缓存重复查询(@Cacheable)
├─ 简单步骤用mini模型(成本/20)
└─ 限制Memory大小(减少Token消耗)
// 成本优化示例:混合模型策略
@Service
public class CostOptimizedService {
    @Autowired
    @Qualifier("fastModel")   // gpt-4o-mini
    private ChatLanguageModel fastModel;

    @Autowired
    @Qualifier("strongModel") // gpt-4o
    private ChatLanguageModel strongModel;

    public String process(String query) {
        // 简单任务用便宜模型
        if (isSimpleQuery(query)) {
            return fastModel.generate(query);  // 成本/20
        }

        // 复杂任务用强大模型
        return strongModel.generate(query);
    }

    private boolean isSimpleQuery(String query) {
        return query.length() < 100 && !query.contains("分析") && !query.contains("审查");
    }
}

💻 实战:综合选型测试

/**
 * 综合对比测试:同一任务用不同组件实现
 */
@Service
public class ComponentBenchmark {
    private static final Logger log = LoggerFactory.getLogger(ComponentBenchmark.class);

    public void runBenchmark(String code) {
        // 测试1:ChatModel
        long t1 = System.currentTimeMillis();
        String r1 = chatModelApproach(code);
        long d1 = System.currentTimeMillis() - t1;
        log.info("[ChatModel] 耗时:{}ms 结果长度:{}", d1, r1.length());

        // 测试2:Chain
        long t2 = System.currentTimeMillis();
        String r2 = chainApproach(code);
        long d2 = System.currentTimeMillis() - t2;
        log.info("[Chain] 耗时:{}ms 结果长度:{}", d2, r2.length());

        // 测试3:Agent
        long t3 = System.currentTimeMillis();
        String r3 = agentApproach(code);
        long d3 = System.currentTimeMillis() - t3;
        log.info("[Agent] 耗时:{}ms 结果长度:{}", d3, r3.length());

        // 总结
        log.info("性能排序:ChatModel({}ms) < Chain({}ms) < Agent({}ms)", d1, d2, d3);
        log.info("详细度排序:ChatModel({}) < Chain({}) < Agent({})",
            r1.length(), r2.length(), r3.length());
    }
}

🔧 最佳实践

选型总结

┌──────────────────────────────────────┐
│           组件选型速查表              │
├──────────────────────────────────────┤
│ 需求              │ 推荐组件         │
├───────────────────┼──────────────────┤
│ 翻译/总结/分类    │ ChatModel        │
│ 多轮对话          │ ChatModel+Memory │
│ 代码生成→分析→优化│ Chain            │
│ 固定流程+对话记忆  │ Chain+Memory     │
│ 需要调用API/数据库 │ Agent            │
│ 复杂推理和决策     │ Agent            │
│ 智能助手(全能)   │ Agent+Memory     │
│ 性能敏感          │ ChatModel        │
│ 成本敏感          │ ChatModel/Chain  │
│ 准确性优先        │ Agent            │
└───────────────────┴──────────────────┘

学习成果检查

  • 能说出四大组件的核心区别
  • 能根据场景选择合适的组件
  • 能评估不同方案的成本和性能
  • 能组合使用多个组件
  • 能做出架构级别的技术决策

下一步:学习项目优化,将所学组件应用到生产级系统中。