Skip to content

LangChain详解 - Java/Spring Boot版本

1927字约6分钟

AILangChain大语言模型LLM

2025-01-27

1. 框架简介

LangChain4j 是 LangChain 的 Java 版本,专为 Java 开发者设计,提供了与 Python 版本相同的核心功能。它基于 Spring Boot 生态系统,帮助开发者快速构建 AI 应用,包括聊天机器人、智能代理、RAG 系统等。

核心特点

  • Java 原生支持:完全用 Java 编写,与 Spring Boot 无缝集成
  • 模块化设计:提供可组合的组件,如 LLM、提示模板、记忆、索引等
  • 多模型支持:支持 OpenAI、Anthropic、本地模型等多种 LLM 提供商
  • 链式调用:通过 Chain 概念实现复杂的 AI 工作流
  • RAG 支持:内置检索增强生成功能,支持多种向量数据库
  • Spring Boot 集成:提供 Spring Boot Starter 和自动配置

2. 环境准备

2.1 环境要求
  • JDK: 17 或更高版本
  • Maven: 3.6.0 或更高版本
  • Spring Boot: 3.2.x 或更高版本
  • IDE: 推荐使用 IntelliJ IDEA 或 Eclipse
2.2 Maven 依赖配置

pom.xml 中添加以下依赖:

<dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Validation -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
    <!-- LangChain4j 核心依赖 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>0.27.1</version>
    </dependency>
    
    <!-- OpenAI 集成 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
        <version>0.27.1</version>
    </dependency>
    
    <!-- Anthropic Claude 集成 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-anthropic</artifactId>
        <version>0.27.1</version>
    </dependency>
    
    <!-- 向量数据库支持 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-embeddings-store-redis</artifactId>
        <version>0.27.1</version>
    </dependency>
    
    <!-- 文档处理 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
        <version>0.27.1</version>
    </dependency>
    
    <!-- 文本分割 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-text-splitter</artifactId>
        <version>0.27.1</version>
    </dependency>
</dependencies>
2.3 配置文件

application.yml 中配置 API 密钥:

spring:
  application:
    name: langchain-demo

# OpenAI 配置
openai:
  api-key: ${OPENAI_API_KEY:your-openai-api-key}
  model: gpt-3.5-turbo
  temperature: 0.7
  timeout: 60s

# Anthropic 配置
anthropic:
  api-key: ${ANTHROPIC_API_KEY:your-anthropic-api-key}
  model: claude-3-sonnet-20240229
  timeout: 60s

# LangChain 配置
langchain:
  tracing:
    enabled: true
    endpoint: https://api.smith.langchain.com
    api-key: ${LANGCHAIN_API_KEY:your-langchain-api-key}

3. 基本使用

3.1 简单的 LLM 调用

创建一个简单的聊天服务:

@Service
public class ChatService {
    
    private final ChatLanguageModel chatModel;
    
    public ChatService(@Value("${openai.api-key}") String apiKey) {
        this.chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .temperature(0.7)
                .timeout(Duration.ofSeconds(60))
                .build();
    }
    
    public String chat(String message) {
        return chatModel.generate(message);
    }
    
    public List<ChatMessage> chatWithHistory(List<ChatMessage> messages) {
        return chatModel.generate(messages);
    }
}

创建 REST 控制器:

@RestController
@RequestMapping("/api/chat")
@Validated
public class ChatController {
    
    private final ChatService chatService;
    
    public ChatController(ChatService chatService) {
        this.chatService = chatService;
    }
    
    @PostMapping("/simple")
    public ResponseEntity<String> simpleChat(@RequestBody @Valid ChatRequest request) {
        String response = chatService.chat(request.getMessage());
        return ResponseEntity.ok(response);
    }
    
    @PostMapping("/conversation")
    public ResponseEntity<List<ChatMessage>> conversationChat(@RequestBody @Valid ConversationRequest request) {
        List<ChatMessage> messages = request.getMessages().stream()
                .map(msg -> new HumanMessage(msg.getContent()))
                .collect(Collectors.toList());
        
        List<ChatMessage> response = chatService.chatWithHistory(messages);
        return ResponseEntity.ok(response);
    }
}

请求/响应 DTO:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatRequest {
    @NotBlank(message = "消息不能为空")
    private String message;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ConversationRequest {
    @NotEmpty(message = "消息列表不能为空")
    private List<MessageDto> messages;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageDto {
    private String content;
    private String role;
}
3.2 使用提示模板

创建提示模板服务:

@Service
public class PromptTemplateService {
    
    private final ChatLanguageModel chatModel;
    
    public PromptTemplateService(@Value("${openai.api-key}") String apiKey) {
        this.chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .build();
    }
    
    public String generateContent(String topic, String requirements) {
        PromptTemplate promptTemplate = PromptTemplate.from(
                "你是一个专业的内容创作者。请写一篇关于{{topic}}的文章,要求:{{requirements}}"
        );
        
        Prompt prompt = promptTemplate.apply(Map.of(
                "topic", topic,
                "requirements", requirements
        ));
        
        return chatModel.generate(prompt.text());
    }
    
    public String translateText(String text, String targetLanguage) {
        PromptTemplate promptTemplate = PromptTemplate.from(
                "请将以下文本翻译成{{targetLanguage}}:\n\n{{text}}"
        );
        
        Prompt prompt = promptTemplate.apply(Map.of(
                "targetLanguage", targetLanguage,
                "text", text
        ));
        
        return chatModel.generate(prompt.text());
    }
}

4. 高级特性

4.1 智能代理 (Agents)

创建工具接口和实现:

public interface CalculatorTool extends Tool {
    
    @Override
    default String name() {
        return "calculator";
    }
    
    @Override
    default String description() {
        return "用于执行数学计算的工具";
    }
    
    @Override
    default Object execute(Object... args) {
        if (args.length != 1) {
            throw new IllegalArgumentException("需要提供一个数学表达式");
        }
        
        String expression = args[0].toString();
        try {
            // 使用 ScriptEngine 执行数学表达式
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("JavaScript");
            Object result = engine.eval(expression);
            return result.toString();
        } catch (Exception e) {
            return "计算错误: " + e.getMessage();
        }
    }
}

创建代理服务:

@Service
public class AgentService {
    
    private final ChatLanguageModel chatModel;
    private final List<Tool> tools;
    
    public AgentService(@Value("${openai.api-key}") String apiKey) {
        this.chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .build();
        
        this.tools = Arrays.asList(new CalculatorTool());
    }
    
    public String executeWithAgent(String userMessage) {
        Agent agent = Agent.builder()
                .chatLanguageModel(chatModel)
                .tools(tools)
                .build();
        
        return agent.execute(userMessage);
    }
}
4.2 RAG 系统 (检索增强生成)

创建文档加载和分割服务:

@Service
public class DocumentService {
    
    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;
    
    public DocumentService(@Value("${openai.api-key}") String apiKey) {
        this.embeddingModel = OpenAiEmbeddingModel.builder()
                .apiKey(apiKey)
                .modelName("text-embedding-ada-002")
                .build();
        
        this.embeddingStore = InMemoryEmbeddingStore.create();
    }
    
    public void loadDocument(String content, String metadata) {
        // 分割文档
        DocumentSplitter splitter = DocumentSplitters.recursive(100, 0);
        List<TextSegment> segments = splitter.split(Document.from(content, metadata));
        
        // 生成嵌入向量并存储
        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
        embeddingStore.addAll(embeddings, segments);
    }
    
    public String queryDocument(String question) {
        // 生成问题的嵌入向量
        Embedding questionEmbedding = embeddingModel.embed(question).content();
        
        // 检索相关文档片段
        List<EmbeddingMatch<TextSegment>> relevantSegments = embeddingStore
                .findRelevant(questionEmbedding, 3);
        
        // 构建上下文
        String context = relevantSegments.stream()
                .map(match -> match.embedded().text())
                .collect(Collectors.joining("\n\n"));
        
        // 使用 LLM 生成答案
        ChatLanguageModel chatModel = OpenAiChatModel.builder()
                .apiKey("your-api-key")
                .modelName("gpt-3.5-turbo")
                .build();
        
        String prompt = String.format(
                "基于以下上下文回答问题:\n\n上下文:%s\n\n问题:%s",
                context, question
        );
        
        return chatModel.generate(prompt);
    }
}
4.3 记忆功能

创建对话记忆服务:

@Service
public class ConversationMemoryService {
    
    private final Map<String, List<ChatMessage>> conversationMemories = new ConcurrentHashMap<>();
    
    public void addMessage(String sessionId, ChatMessage message) {
        conversationMemories.computeIfAbsent(sessionId, k -> new ArrayList<>())
                .add(message);
    }
    
    public List<ChatMessage> getConversationHistory(String sessionId) {
        return conversationMemories.getOrDefault(sessionId, new ArrayList<>());
    }
    
    public void clearConversation(String sessionId) {
        conversationMemories.remove(sessionId);
    }
    
    public String generateResponseWithMemory(String sessionId, String userMessage, 
                                          ChatLanguageModel chatModel) {
        List<ChatMessage> history = getConversationHistory(sessionId);
        
        // 添加用户新消息
        history.add(new HumanMessage(userMessage));
        
        // 生成回复
        List<ChatMessage> response = chatModel.generate(history);
        
        // 保存对话历史
        addMessage(sessionId, response.get(response.size() - 1));
        
        return response.get(response.size() - 1).text();
    }
}

5. 实际应用场景

5.1 智能客服机器人

创建客服机器人服务:

@Service
public class CustomerServiceBot {
    
    private final ChatLanguageModel chatModel;
    private final ConversationMemoryService memoryService;
    
    public CustomerServiceBot(@Value("${openai.api-key}") String apiKey,
                            ConversationMemoryService memoryService) {
        this.chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .build();
        
        this.memoryService = memoryService;
    }
    
    public String handleCustomerInquiry(String sessionId, String inquiry) {
        // 构建客服提示
        String systemPrompt = "你是一个专业的客服代表。请根据客户问题提供专业、友好的回答。" +
                "如果无法解决,请建议联系人工客服。保持礼貌和耐心。";
        
        // 获取对话历史
        List<ChatMessage> history = memoryService.getConversationHistory(sessionId);
        
        // 构建完整对话
        List<ChatMessage> fullConversation = new ArrayList<>();
        fullConversation.add(new SystemMessage(systemPrompt));
        fullConversation.addAll(history);
        fullConversation.add(new HumanMessage(inquiry));
        
        // 生成回复
        List<ChatMessage> response = chatModel.generate(fullConversation);
        ChatMessage botResponse = response.get(response.size() - 1);
        
        // 保存对话
        memoryService.addMessage(sessionId, new HumanMessage(inquiry));
        memoryService.addMessage(sessionId, botResponse);
        
        return botResponse.text();
    }
}
5.2 文档问答系统

创建文档问答服务:

@Service
public class DocumentQAService {
    
    private final DocumentService documentService;
    private final ChatLanguageModel chatModel;
    
    public DocumentQAService(DocumentService documentService,
                           @Value("${openai.api-key}") String apiKey) {
        this.documentService = documentService;
        this.chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .build();
    }
    
    public void uploadDocument(String content, String documentName) {
        documentService.loadDocument(content, documentName);
    }
    
    public String askQuestion(String question) {
        return documentService.queryDocument(question);
    }
    
    public List<String> batchQuery(List<String> questions) {
        return questions.stream()
                .map(this::askQuestion)
                .collect(Collectors.toList());
    }
}

6. 最佳实践

6.1 性能优化
@Configuration
public class LangChainConfig {
    
    @Bean
    @Primary
    public ChatLanguageModel chatLanguageModel(@Value("${openai.api-key}") String apiKey) {
        return OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName("gpt-3.5-turbo")
                .temperature(0.7)
                .timeout(Duration.ofSeconds(60))
                .maxTokens(2000)
                .build();
    }
    
    @Bean
    public EmbeddingModel embeddingModel(@Value("${openai.api-key}") String apiKey) {
        return OpenAiEmbeddingModel.builder()
                .apiKey(apiKey)
                .modelName("text-embedding-ada-002")
                .timeout(Duration.ofSeconds(60))
                .build();
    }
}
6.2 错误处理
@ControllerAdvice
public class LangChainExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
        ErrorResponse error = new ErrorResponse(
                "INTERNAL_ERROR",
                "处理请求时发生错误: " + e.getMessage(),
                LocalDateTime.now()
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException e) {
        ErrorResponse error = new ErrorResponse(
                "INVALID_INPUT",
                "输入参数无效: " + e.getMessage(),
                LocalDateTime.now()
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {
    private String code;
    private String message;
    private LocalDateTime timestamp;
}
6.3 安全考虑
@Component
public class SecurityValidator {
    
    public void validateInput(String input) {
        // 检查输入长度
        if (input.length() > 10000) {
            throw new IllegalArgumentException("输入内容过长,请控制在10000字符以内");
        }
        
        // 检查敏感词汇
        List<String> sensitiveWords = Arrays.asList("密码", "token", "密钥", "admin");
        for (String word : sensitiveWords) {
            if (input.toLowerCase().contains(word.toLowerCase())) {
                throw new IllegalArgumentException("输入包含敏感词汇");
            }
        }
        
        // 检查 SQL 注入
        if (input.toLowerCase().contains("select") || 
            input.toLowerCase().contains("insert") ||
            input.toLowerCase().contains("delete") ||
            input.toLowerCase().contains("update")) {
            throw new IllegalArgumentException("输入包含非法字符");
        }
    }
}

7. 部署和监控

7.1 使用 LangSmith 监控
@Configuration
public class LangSmithConfig {
    
    @Bean
    @ConditionalOnProperty(name = "langchain.tracing.enabled", havingValue = "true")
    public LangChainTracer langChainTracer(@Value("${langchain.tracing.api-key}") String apiKey,
                                         @Value("${langchain.tracing.endpoint}") String endpoint) {
        return LangChainTracer.builder()
                .apiKey(apiKey)
                .endpoint(endpoint)
                .build();
    }
}
7.2 生产环境部署
# application-prod.yml
spring:
  profiles:
    active: prod
  
  # 数据库配置
  datasource:
    url: ${DATABASE_URL}
    username: ${DATABASE_USERNAME}
    password: ${DATABASE_PASSWORD}
  
  # Redis 配置
  redis:
    host: ${REDIS_HOST}
    port: ${REDIS_PORT}
    password: ${REDIS_PASSWORD}

# 日志配置
logging:
  level:
    dev.langchain4j: INFO
    com.example: DEBUG
  file:
    name: logs/langchain-app.log
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 监控配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

贡献者: Yibz