LangChain4j AI Services 深度解析:声明式 API 与接口驱动开发

LangChain4j AI Services 深度解析:声明式 API 与接口驱动开发

前言

LangChain4j 的 AI Services 是框架的核心特性之一,它采用声明式 API 设计,让开发者只需定义接口,无需编写实现类即可调用 LLM。这种设计类似于 Spring Data JPA 或 Retrofit,极大地简化了 AI 应用的开发复杂度。

本文将深入剖析 AI Services 的核心注解、模板语法、结构化输出、流式响应,以及多模型路由等高级特性。

一、核心注解详解

1.1 @AiService 注解

@AiService 是 AI Services 的核心注解,用于标记 AI Service 接口。

java 复制代码
import dev.langchain4j.service.AiService;

@AiService
public interface Assistant {
    String chat(String message);
}

使用示例:

java 复制代码
// 创建 AI Service 实例
Assistant assistant = AiServices.builder(Assistant.class)
        .chatLanguageModel(model)
        .build();

// 调用
String response = assistant.chat("你好");

1.2 @SystemMessage 注解

定义系统提示词,设定 AI 的角色和行为。

java 复制代码
import dev.langchain4j.service.SystemMessage;

@AiService
public interface CodingAssistant {
    
    @SystemMessage("你是一个专业的Java开发工程师,擅长Spring Boot和微服务架构")
    String codeReview(String code);
    
    @SystemMessage("""
        你是一个技术文档撰写专家,要求:
        1. 结构清晰,使用 Markdown 格式
        2. 包含代码示例和最佳实践
        3. 适合中级开发者阅读
        """)
    String writeDoc(String topic);
}

1.3 @UserMessage 注解

定义用户消息模板,支持变量插值。

java 复制代码
import dev.langchain4j.service.UserMessage;

@AiService
public interface QuestionAssistant {
    
    // 使用 {{it}} 表示唯一参数
    @UserMessage("请解释:{{it}}")
    String explain(String concept);
    
    // 使用 {{var}} 表示命名变量
    @UserMessage("请比较{{lang1}}和{{lang2}}的区别")
    String compare(
        @V("lang1") String language1,
        @V("lang2") String language2
    );
    
    // 复杂模板
    @UserMessage("""
        请帮我分析以下情况:
        
        用户:{{username}}
        问题类型:{{issueType}}
        问题描述:{{description}}
        
        请提供3个解决方案,每个方案包含:
        1. 方案描述
        2. 实施步骤
        3. 预期效果
        """)
    String analyzeIssue(
        @V("username") String username,
        @V("issueType") String issueType,
        @V("description") String description
    );
}

1.4 @AssistantMessage 注解

定义 AI 的预设响应,用于少样本学习(Few-shot Learning)。

java 复制代码
import dev.langchain4j.service.AssistantMessage;

@AiService
public interface FewShotAssistant {
    
    @UserMessage("1 + 1 = ?")
    @AssistantMessage("1 + 1 = 2")
    
    @UserMessage("2 + 2 = ?")
    @AssistantMessage("2 + 2 = 4")
    
    @UserMessage("3 + 3 = ?")
    String solve(String expression);
}

// 调用
String answer = assistant.solve("5 + 5 = ?");
// 预期输出: "5 + 5 = 10"

二、模板语法详解

2.1 Mustache 风格模板

LangChain4j 支持 Mustache 风格的模板语法:

语法 说明 示例
{``{it}} 表示方法唯一参数 @UserMessage("请解释:{``{it}}")
{``{var}} 表示命名变量 @UserMessage("Hello, {``{name}}") + @V("name")
{``{#condition}}...{``{/condition}} 条件渲染 {``{#hasDetails}}...{``{/hasDetails}}
{``{#array}}...{``{/array}} 循环渲染 {``{#items}}- {``{.}}{``{/items}}

2.2 基础模板示例

java 复制代码
@AiService
public interface TemplateAssistant {
    
    // 单参数模板
    @UserMessage("请翻译成英文:{{it}}")
    String translate(String text);
    
    // 多参数模板
    @UserMessage("请写一封{{tone}}的邮件给{{recipient}},内容是:{{content}}")
    String writeEmail(
        @V("tone") String tone,
        @V("recipient") String recipient,
        @V("content") String content
    );
    
    // 条件渲染
    @UserMessage("""
        请生成{{#isProfessional}}专业{{/isProfessional}}{{^isProfessional}}通俗{{/isProfessional}}版本的解释:
        {{topic}}
        """)
    String explain(
        @V("topic") String topic,
        @V("isProfessional") boolean isProfessional
    );
}

// 调用示例
String email = assistant.writeEmail("礼貌", "张经理", "明天会议延期");
// 输出: "张经理,您好!关于明天会议...(礼貌语气)"

String explanation = assistant.explain("微服务架构", true);
// 输出: "专业版本的解释..."

2.3 循环渲染

java 复制代码
@AiService
public interface LoopAssistant {
    
    @UserMessage("""
        请评价以下技术栈:
        {{#techs}}
        - {{name}}:{{description}}
        {{/techs}}
        
        给出评分(1-5分)和建议。
        """)
    String evaluateTechStack(
        @V("techs") List<Technology> techs
    );
}

record Technology(String name, String description) {}

// 调用
List<Technology> techs = List.of(
    new Technology("Spring Boot", "快速开发框架"),
    new Technology("React", "前端 UI 框架"),
    new Technology("PostgreSQL", "关系型数据库")
);

String evaluation = assistant.evaluateTechStack(techs);

2.4 从资源文件加载模板

java 复制代码
// prompts/code-review.txt
请你作为代码审查专家,审查以下代码:
```java
{{code}}

审查要点:

  1. 代码风格
  2. 潜在 Bug
  3. 性能优化
  4. 安全问题

请以 Markdown 格式输出审查结果。

复制代码
```java
@AiService
public interface CodeReviewAssistant {
    
    @UserMessage(fromResource = "prompts/code-review.txt")
    String review(@V("code") String code);
}

三、结构化输出

3.1 POJO 映射

LangChain4j 支持将 LLM 返回的 JSON 自动映射为 POJO。

java 复制代码
@AiService
public interface StructuredOutputAssistant {
    
    // 返回 POJO
    User analyzeUser(String text);
}

record User(
    String name,
    int age,
    String email,
    List<String> interests
) {}

// 调用
User user = assistant.analyzeUser("我叫张三,25岁,邮箱是zhangsan@example.com,喜欢编程和阅读");
// user.name() == "张三"
// user.age() == 25

3.2 枚举类型

java 复制代码
@AiService
public interface EnumAssistant {
    
    Sentiment analyzeSentiment(String text);
}

enum Sentiment {
    POSITIVE, NEGATIVE, NEUTRAL
}

// 调用
Sentiment sentiment = assistant.analyzeSentiment("今天天气真不错!");
// sentiment == Sentiment.POSITIVE

3.3 Boolean 类型

java 复制代码
@AiService
public interface BooleanAssistant {
    
    boolean isSpam(String message);
    
    boolean containsCode(String text);
}

// 调用
boolean spam = assistant.isSpam("恭喜您中奖100万!");
boolean code = assistant.containsCode("public static void main(String[] args) {}");

3.4 @StructuredPrompt 注解

用于更复杂的结构化输出场景。

java 复制代码
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.StructuredPrompt;

@StructuredPrompt("""
    请分析以下招聘需求:
    
    职位:{{jobTitle}}
    公司:{{company}}
    要求:{{requirements}}
    """)
record JobAnalysis(
    String jobTitle,
    String company,
    String requirements
) {}

@AiService
public interface JobAssistant {
    
    JobAnalysisResponse analyze(JobAnalysis job);
}

record JobAnalysisResponse(
    String summary,
    List<String> keySkills,
    List<String> missingSkills,
    int experienceLevel  // 1-5
) {}

3.5 结构化输出流程图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                 结构化输出处理流程                            │
└─────────────────────────────────────────────────────────────┘

┌─────────┐    ┌──────────────┐    ┌──────────────┐    ┌───────┐
│ 用户调用  │ -> │ @AiService  │ -> │ ChatLanguage │ -> │  LLM   │
│ 返回POJO  │    │  接口代理    │ -> │    Model     │ -> │ API   │
└─────────┘    └──────────────┘    └──────────────┘    └───────┘
    │                │                  │               │
    │ 1. 定义返回类型  │ 2. 构建Prompt    │ 3. 发送请求   │ 4. 返回JSON
    │    POJO         │   包含类型提示    │               │
    └────────────────┴──────────────────┴───────────────┘
                           │
                    5. JSON解析
                    6. 映射到POJO
                    7. 返回给用户

详细步骤:
1. 用户调用 assistant.analyzeUser(...)
2. @AiService 代理根据返回类型 User 生成类型提示
3. 构建完整 Prompt(系统提示 + 类型说明 + 用户输入)
4. 发送到 LLM
5. LLM 返回 JSON 格式:{"name": "张三", "age": 25, ...}
6. LangChain4j 使用 Jackson 解析 JSON
7. 映射到 User POJO 并返回

四、流式响应

4.1 TokenStream 接口

java 复制代码
import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.output.TokenStream;
import dev.langchain4j.model.output.Response;

@AiService
public interface StreamingAssistant {
    
    TokenStream chat(@UserMessage String message);
}

// 使用示例
StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
        .streamingChatLanguageModel(streamingModel)
        .build();

assistant.chat("请写一篇关于Java的文章")
    .onNext(token -> {
        System.out.print(token);  // 逐词输出
    })
    .onComplete(response -> {
        System.out.println("\n完成!");
        System.out.println("Total tokens: " + response.tokenUsage().totalTokenCount());
    })
    .onError(error -> {
        error.printStackTrace();
    });

4.2 Flux 集成(Reactive Streams)

java 复制代码
import reactor.core.publisher.Flux;

@AiService
public interface ReactiveAssistant {
    
    Flux<String> chat(String message);
}

// 使用示例
Flux<String> responseFlux = assistant.chat("写一首诗");

responseFlux
    .buffer(100)  // 每 100 个字符一批
    .subscribe(batch -> {
        System.out.println(batch);
    });

4.3 流式响应流程图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                   流式响应处理流程                             │
└─────────────────────────────────────────────────────────────┘

用户                    LLM                    @AiService
 │                      │                       │
 │ 1. 发起请求           │                       │
 ├─────────────────────>│                       │
 │  "请写一篇文章"        │                       │
 │                      │                       │
 │                      │ 2. 生成并流式返回Token  │
 │                      ├────────────────────>│
 │                      │  "LangChain4j..."     │
 │                      │  "是..."              │
 │                      │  "一个..."            │
 │                      │                       │
 │ 3. onNext(TOKEN)     │                       │
 │<─────────────────────┤                       │
 │  输出 "LangChain4j"   │                       │
 │                      │                       │
 │ 4. onNext(TOKEN)     │                       │
 │<─────────────────────┤                       │
 │  输出 "是"           │                       │
 │                      │                       │
 │ ...                  │ ...                    │
 │                      │                       │
 │ 5. onComplete        │                       │
 │<─────────────────────┤                       │
 │  完成,返回 TokenUsage│                       │
 │                      │                       │
 └──────────────────────┴───────────────────────┘

优势:
- 降低延迟:首字输出时间短
- 实时反馈:用户可即时看到输出
- 更好的用户体验:类似 ChatGPT 的打字效果

五、多模型路由

5.1 基于方法的模型路由

java 复制代码
@AiService
public interface MultiModelAssistant {
    
    // 轻量查询使用 Ollama
    @ModelNameProvider("ollama")
    String simpleQuestion(@UserMessage String question);
    
    // 复杂任务使用 GPT-4
    @ModelNameProvider("openai-gpt4")
    String complexTask(@UserMessage String task);
}

5.2 动态模型选择

java 复制代码
public class ModelRouter {
    
    private final ChatLanguageModel ollamaModel;
    private final ChatLanguageModel gpt4Model;
    
    public ModelRouter() {
        this.ollamaModel = OllamaChatModel.builder()
                .baseUrl("http://localhost:11434")
                .modelName("llama3.1")
                .build();
        
        this.gpt4Model = OpenAiChatModel.builder()
                .apiKey(System.getenv("OPENAI_API_KEY"))
                .modelName("gpt-4")
                .build();
    }
    
    public Assistant createAssistant(String modelType) {
        ChatLanguageModel model = switch (modelType) {
            case "lightweight" -> ollamaModel;
            case "heavyweight" -> gpt4Model;
            default -> throw new IllegalArgumentException("Unknown model type");
        };
        
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(model)
                .build();
    }
}

// 使用
ModelRouter router = new ModelRouter();

// 轻量任务
Assistant lightweightAssistant = router.createAssistant("lightweight");
String simpleAnswer = lightweightAssistant.chat("你好");

// 复杂任务
Assistant heavyweightAssistant = router.createAssistant("heavyweight");
String complexAnswer = heavyweightAssistant.chat("请分析以下代码并给出优化建议:...");

5.3 基于成本的模型选择

java 复制代码
public class CostAwareRouter {
    
    private final ChatLanguageModel cheapModel;
    private final ChatLanguageModel expensiveModel;
    
    // 成本阈值(字符数)
    private static final int COST_THRESHOLD = 1000;
    
    public String chat(String message) {
        // 短文本使用廉价模型
        if (message.length() < COST_THRESHOLD) {
            return AiServices.builder(Assistant.class)
                    .chatLanguageModel(cheapModel)
                    .build()
                    .chat(message);
        }
        // 长文本使用高质量模型
        else {
            return AiServices.builder(Assistant.class)
                    .chatLanguageModel(expensiveModel)
                    .build()
                    .chat(message);
        }
    }
}

六、高级特性

6.1 超时控制

java 复制代码
@AiService
public interface TimeoutAssistant {
    
    @Timeout(Duration.ofSeconds(30))
    String quickAnswer(String question);
    
    @Timeout(Duration.ofMinutes(5))
    String longTask(String task);
}

6.2 温度控制

java 复制代码
@AiService
public interface TemperatureAssistant {
    
    @Temperature(0.2)  // 低温度,确定性输出
    String codeGeneration(String description);
    
    @Temperature(0.8)  // 高温度,创造性输出
    String creativeWriting(String topic);
}

6.3 最大 Token 控制

java 复制代码
@AiService
public interface MaxTokensAssistant {
    
    @MaxTokens(100)  // 短响应
    String summary(String text);
    
    @MaxTokens(2000)  // 长响应
    String detailedExplanation(String topic);
}

6.4 聊天记忆

java 复制代码
@AiService
public interface MemoryAssistant {
    
    @MemoryId  // 指定记忆 ID
    String chat(@MemoryId String userId, String message);
}

// 使用
String userId = "user123";
String response1 = assistant.chat(userId, "你好,我叫张三");
String response2 = assistant.chat(userId, "我的名字是什么?");
// AI 会记住之前的对话:"你的名字是张三"

七、常见问题

Q1: 如何处理 LLM 返回的无效 JSON?

A: LangChain4j 会自动重试和修复:

java 复制代码
// 配置 JSON 修复策略
AiServices.builder(Assistant.class)
    .chatLanguageModel(model)
    .jsonRepairStrategy(JsonRepairStrategy.AUTO_FIX)
    .build();

Q2: 流式响应可以中途取消吗?

A: 可以,使用 CancellationToken

java 复制代码
CancellationToken token = new SimpleCancellationToken();

assistant.chat("写一篇长文章")
    .onNext(token -> {
        if (shouldCancel(token)) {
            token.cancel();
        }
    });

Q3: 如何实现多个 AI Service 共享状态?

A: 使用共享的 ChatMemoryProvider:

java 复制代码
ChatMemoryProvider memoryProvider = MessageWindowChatMemory.builder()
    .maxMessages(10)
    .build();

Assistant assistant1 = AiServices.builder(Assistant.class)
    .chatLanguageModel(model)
    .chatMemoryProvider(memoryProvider)
    .build();

Assistant assistant2 = AiServices.builder(Assistant.class)
    .chatLanguageModel(model)
    .chatMemoryProvider(memoryProvider)
    .build();

// assistant1 和 assistant2 共享记忆

八、小结

本文深入剖析了 LangChain4j AI Services 的核心特性,包括:

  1. 核心注解:@AiService、@SystemMessage、@UserMessage、@AssistantMessage
  2. 模板语法:Mustache 风格、条件渲染、循环渲染、资源文件加载
  3. 结构化输出:POJO 映射、枚举类型、Boolean 类型、@StructuredPrompt
  4. 流式响应:TokenStream 接口、Flux 集成、实时输出
  5. 多模型路由:基于方法的路由、动态选择、成本感知路由
  6. 高级特性:超时控制、温度控制、最大 Token 控制、聊天记忆

核心思想: 声明式 API 设计,让开发者只需定义接口,无需编写实现类即可调用 LLM。

下一步学习:

  • 文章 5:《LangChain4j 与 OpenAI 深度集成:Function Calling、JSON Mode 与结构化输出》

参考文献:

相关推荐
Dfreedom.2 小时前
工具箱思维:在计算机视觉中如何选对工具、用好工具(计算机视觉篇)
人工智能·计算机视觉·目标跟踪
腾科IT教育2 小时前
人工智能三级好考吗?考试难度解析
人工智能·ai训练师·人工智能算法工程师
w_t_y_y2 小时前
Claude Code(六)Sub Agents(2)运行
人工智能
RFID舜识物联网2 小时前
RFID技术重构医疗试剂管理:从“人工时代”到“智能时代”的跨越
大数据·人工智能·科技·物联网·安全
不懒不懒2 小时前
【实战 OpenCV 身份证号码识别】
人工智能·opencv·计算机视觉
weixin_307779132 小时前
OpenClaw-CN 安全增强方案:从理念到落地的全面剖析
开发语言·人工智能·算法·安全·语言模型
BullSmall2 小时前
借助AI高效推动性能测试
大数据·人工智能
ECT-OS-JiuHuaShan2 小时前
朱梁万有递归元定理重构《鬼谷子》
人工智能·重构
码路飞2 小时前
GTC 2026 最后一天,老黄扔了个 NemoClaw 出来,我连夜装上试了一下
人工智能·llm·nvidia