AI原生应用开发新趋势:LangChain与Spring AI整合实战,打造智能企业级应用

 
更多

AI原生应用开发新趋势:LangChain与Spring AI整合实战,打造智能企业级应用

引言

随着人工智能技术的快速发展,AI原生应用已经成为企业数字化转型的重要方向。传统的软件开发模式正在被AI驱动的开发范式所取代,开发者需要掌握新的工具和框架来构建智能化的企业级应用。在这个背景下,LangChain和Spring AI作为两个重要的AI开发框架,为开发者提供了强大的工具集来集成大语言模型(LLM)、构建复杂的AI应用链路。

本文将深入探讨AI原生应用开发的最新趋势,详细介绍LangChain框架与Spring AI的整合方案,并通过实际代码示例展示如何在企业级应用中集成大语言模型、实施Prompt工程、使用向量数据库等核心技术。

AI原生应用的核心概念

什么是AI原生应用?

AI原生应用(AI-Native Applications)是指从设计之初就将人工智能能力作为核心组件的应用程序。这类应用不仅仅是传统应用加上AI功能,而是以AI为核心驱动力,重新设计应用架构和用户体验。

AI原生应用具有以下特征:

  • 智能决策:基于AI模型进行自动化决策
  • 自适应学习:能够根据用户行为和反馈持续优化
  • 自然交互:支持自然语言、语音等直观交互方式
  • 数据驱动:充分利用数据来提升应用智能水平

AI原生应用的技术架构

现代AI原生应用通常采用分层架构设计:

┌─────────────────────────────────────┐
│           应用层 (Application)        │
├─────────────────────────────────────┤
│           编排层 (Orchestration)     │
├─────────────────────────────────────┤
│           服务层 (Services)          │
├─────────────────────────────────────┤
│           模型层 (Models)            │
├─────────────────────────────────────┤
│           基础设施层 (Infrastructure)│
└─────────────────────────────────────┘

LangChain框架深度解析

LangChain概述

LangChain是一个用于构建语言模型应用的框架,它提供了一系列工具和组件来简化LLM应用的开发过程。LangChain的核心理念是将复杂的AI应用分解为可组合的组件,通过链式调用实现复杂功能。

核心组件介绍

1. Prompts(提示词模板)

Prompt工程是LLM应用开发的核心技能。LangChain提供了强大的Prompt模板系统:

from langchain.prompts import PromptTemplate

# 定义Prompt模板
template = """
你是一个专业的技术顾问。请根据以下信息回答用户问题:
用户问题:{question}
相关文档:{context}

请提供详细且准确的回答。
"""

prompt = PromptTemplate(
    input_variables=["question", "context"],
    template=template
)

# 使用Prompt模板
formatted_prompt = prompt.format(
    question="如何优化Spring Boot应用性能?",
    context="Spring Boot性能优化文档..."
)

2. Models(模型集成)

LangChain支持多种LLM提供商的集成:

from langchain.llms import OpenAI, HuggingFaceHub
from langchain.chat_models import ChatOpenAI

# OpenAI模型
openai_model = OpenAI(
    model_name="text-davinci-003",
    temperature=0.7,
    max_tokens=1000
)

# Chat模型
chat_model = ChatOpenAI(
    model_name="gpt-3.5-turbo",
    temperature=0.3
)

# 开源模型
huggingface_model = HuggingFaceHub(
    repo_id="google/flan-t5-xl",
    model_kwargs={"temperature": 0.8, "max_length": 500}
)

3. Chains(链式调用)

Chain是LangChain的核心概念,用于组合多个组件:

from langchain.chains import LLMChain

# 创建LLM链
llm_chain = LLMChain(
    llm=openai_model,
    prompt=prompt
)

# 执行链式调用
result = llm_chain.run({
    "question": "什么是微服务架构?",
    "context": "微服务架构是一种软件架构模式..."
})

高级功能

代理(Agents)

LangChain的Agent功能允许模型自主选择和执行工具:

from langchain.agents import initialize_agent, Tool
from langchain.utilities import SerpAPIWrapper

# 定义工具
search = SerpAPIWrapper()
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="用于搜索最新的技术信息"
    )
]

# 初始化代理
agent = initialize_agent(
    tools,
    openai_model,
    agent="zero-shot-react-description",
    verbose=True
)

# 使用代理
response = agent.run("查找最新的Spring Boot版本信息")

Spring AI框架详解

Spring AI简介

Spring AI是Spring团队推出的AI集成框架,旨在为Spring开发者提供熟悉的API来集成AI能力。它遵循Spring的设计哲学,提供了自动配置、依赖注入等特性。

核心特性

1. 模型客户端抽象

Spring AI提供了统一的模型客户端接口:

@Configuration
public class AiConfig {
    
    @Bean
    public OpenAiClient openAiClient() {
        return new OpenAiClientBuilder()
            .withApiKey("your-api-key")
            .withBaseUrl("https://api.openai.com")
            .build();
    }
}

2. Prompt模板支持

Spring AI支持基于模板的Prompt管理:

@Component
public class PromptService {
    
    private final PromptTemplate promptTemplate;
    
    public PromptService() {
        this.promptTemplate = new PromptTemplate(
            "请根据以下内容总结要点:{content}"
        );
    }
    
    public String generateSummary(String content) {
        Prompt prompt = promptTemplate.create(Map.of("content", content));
        return openAiClient.generate(prompt).getResult().getOutput().getContent();
    }
}

3. 向量存储集成

Spring AI提供了向量存储的抽象接口:

@Service
public class VectorStoreService {
    
    private final VectorStore vectorStore;
    
    public void storeDocument(String id, String content) {
        EmbeddingClient embeddingClient = new OpenAiEmbeddingClient();
        float[] embedding = embeddingClient.embed(content);
        vectorStore.add(Arrays.asList(new Document(id, content, embedding)));
    }
    
    public List<Document> searchSimilar(String query) {
        EmbeddingClient embeddingClient = new OpenAiEmbeddingClient();
        float[] queryEmbedding = embeddingClient.embed(query);
        return vectorStore.similaritySearch(queryEmbedding, 5);
    }
}

LangChain与Spring AI整合方案

整合架构设计

为了在企业级应用中充分发挥两个框架的优势,我们设计了以下整合架构:

┌─────────────────────────────────────────────────────────┐
│                    Web层 (Spring MVC)                     │
├─────────────────────────────────────────────────────────┤
│              业务逻辑层 (Spring Service)                 │
├─────────────────────────────────────────────────────────┤
│        AI编排层 (LangChain + Spring AI Client)           │
├─────────────────────────────────────────────────────────┤
│        模型访问层 (Spring AI Model Clients)              │
├─────────────────────────────────────────────────────────┤
│        数据存储层 (Vector DB + Relational DB)            │
└─────────────────────────────────────────────────────────┘

实现步骤

1. 项目依赖配置

首先在Spring Boot项目中添加必要的依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring AI Core -->
    <dependency>
        <groupId>org.springframework.experimental.ai</groupId>
        <artifactId>spring-ai-core</artifactId>
        <version>0.8.1</version>
    </dependency>
    
    <!-- Spring AI OpenAI -->
    <dependency>
        <groupId>org.springframework.experimental.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>0.8.1</version>
    </dependency>
    
    <!-- LangChain Java -->
    <dependency>
        <groupId>com.hw.langchain</groupId>
        <artifactId>langchain-core</artifactId>
        <version>0.1.0</version>
    </dependency>
</dependencies>

2. 配置文件设置

# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-3.5-turbo
          temperature: 0.7

langchain:
  openai:
    api-key: ${OPENAI_API_KEY}
    model: text-davinci-003

3. 核心服务实现

@Service
public class AiIntegrationService {
    
    private final OpenAiClient openAiClient;
    private final LLMChain llmChain;
    private final VectorStore vectorStore;
    
    public AiIntegrationService(OpenAiClient openAiClient, 
                               VectorStore vectorStore) {
        this.openAiClient = openAiClient;
        this.vectorStore = vectorStore;
        this.llmChain = createLLMChain();
    }
    
    private LLMChain createLLMChain() {
        // 创建LangChain的LLM实例
        OpenAI langchainOpenAI = OpenAI.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .model("text-davinci-003")
            .build();
        
        // 创建Prompt模板
        PromptTemplate template = new PromptTemplate(
            "基于以下文档内容回答问题:\n" +
            "文档:{context}\n" +
            "问题:{question}\n" +
            "回答:"
        );
        
        return new LLMChain(langchainOpenAI, template);
    }
    
    public String processQuery(String question) {
        // 1. 从向量数据库检索相关文档
        List<Document> relevantDocs = searchRelevantDocuments(question);
        
        // 2. 构建上下文
        String context = relevantDocs.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n"));
        
        // 3. 使用LangChain处理
        Map<String, Object> inputs = Map.of(
            "question", question,
            "context", context
        );
        
        return llmChain.run(inputs);
    }
    
    private List<Document> searchRelevantDocuments(String query) {
        EmbeddingClient embeddingClient = new OpenAiEmbeddingClient();
        float[] queryEmbedding = embeddingClient.embed(query);
        return vectorStore.similaritySearch(queryEmbedding, 3);
    }
}

大语言模型集成实践

模型选择策略

在企业级应用中,选择合适的LLM至关重要。需要考虑以下因素:

1. 成本效益

@Component
public class CostOptimizedModelSelector {
    
    public enum ModelType {
        HIGH_ACCURACY("gpt-4", 0.06, 0.012),
        BALANCED("gpt-3.5-turbo", 0.002, 0.002),
        LOW_COST("text-davinci-003", 0.02, 0.02);
        
        private final String modelName;
        private final double inputCost;
        private final double outputCost;
        
        ModelType(String modelName, double inputCost, double outputCost) {
            this.modelName = modelName;
            this.inputCost = inputCost;
            this.outputCost = outputCost;
        }
    }
    
    public String selectModel(String taskType, double budget) {
        switch (taskType) {
            case "complex_analysis":
                return budget > 0.1 ? ModelType.HIGH_ACCURACY.modelName : 
                       ModelType.BALANCED.modelName;
            case "simple_qa":
                return ModelType.LOW_COST.modelName;
            default:
                return ModelType.BALANCED.modelName;
        }
    }
}

2. 性能监控

@Aspect
@Component
public class ModelPerformanceAspect {
    
    private final MeterRegistry meterRegistry;
    
    public ModelPerformanceAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Around("@annotation(MonitorModelPerformance)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            
            long duration = System.currentTimeMillis() - startTime;
            String modelName = getModelName(joinPoint);
            
            // 记录性能指标
            Timer.Sample sample = Timer.start(meterRegistry);
            sample.stop(Timer.builder("ai.model.response.time")
                .tag("model", modelName)
                .register(meterRegistry));
            
            return result;
        } catch (Exception e) {
            Counter.builder("ai.model.errors")
                .tag("model", getModelName(joinPoint))
                .tag("error", e.getClass().getSimpleName())
                .register(meterRegistry)
                .increment();
            throw e;
        }
    }
    
    private String getModelName(ProceedingJoinPoint joinPoint) {
        // 从方法参数或注解中提取模型名称
        return "default-model";
    }
}

模型缓存机制

为了提高性能和降低成本,实现模型响应缓存:

@Service
public class ModelResponseCache {
    
    private final Cache<String, String> cache;
    private final ObjectMapper objectMapper;
    
    public ModelResponseCache(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        this.cache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(Duration.ofHours(1))
            .build();
    }
    
    public String getCachedResponse(String prompt, String model) {
        String cacheKey = generateCacheKey(prompt, model);
        return cache.getIfPresent(cacheKey);
    }
    
    public void cacheResponse(String prompt, String model, String response) {
        String cacheKey = generateCacheKey(prompt, model);
        cache.put(cacheKey, response);
    }
    
    private String generateCacheKey(String prompt, String model) {
        return DigestUtils.md5DigestAsHex((prompt + model).getBytes());
    }
}

Prompt工程最佳实践

Prompt设计原则

1. 明确性原则

@Component
public class PromptTemplateManager {
    
    public static final String TECHNICAL_QA_TEMPLATE = """
        你是一个专业的技术专家,请根据以下要求回答问题:
        
        1. 问题类型:{question_type}
        2. 技术领域:{tech_domain}
        3. 详细要求:{requirements}
        4. 回答格式:请使用清晰的结构化回答
        
        问题:{question}
        回答:
        """;
    
    public Prompt createTechnicalQAPrompt(String question, 
                                        String questionType, 
                                        String techDomain,
                                        String requirements) {
        Map<String, Object> variables = Map.of(
            "question", question,
            "question_type", questionType,
            "tech_domain", techDomain,
            "requirements", requirements
        );
        
        return new PromptTemplate(TECHNICAL_QA_TEMPLATE).create(variables);
    }
}

2. 上下文管理

@Service
public class ContextAwarePromptService {
    
    public String generateContextualPrompt(String basePrompt, 
                                         List<String> contextDocuments,
                                         Map<String, Object> additionalContext) {
        StringBuilder contextualPrompt = new StringBuilder();
        
        // 添加系统指令
        contextualPrompt.append("系统指令:请基于以下上下文信息回答问题\n\n");
        
        // 添加上下文文档
        contextualPrompt.append("=== 上下文信息 ===\n");
        for (int i = 0; i < contextDocuments.size(); i++) {
            contextualPrompt.append(String.format("[%d] %s\n", i + 1, contextDocuments.get(i)));
        }
        
        // 添加基础Prompt
        contextualPrompt.append("\n=== 问题 ===\n");
        contextualPrompt.append(basePrompt);
        
        // 添加额外上下文
        if (additionalContext != null && !additionalContext.isEmpty()) {
            contextualPrompt.append("\n=== 额外信息 ===\n");
            additionalContext.forEach((key, value) -> 
                contextualPrompt.append(String.format("%s: %s\n", key, value)));
        }
        
        return contextualPrompt.toString();
    }
}

动态Prompt优化

@Service
public class AdaptivePromptOptimizer {
    
    private final OpenAiClient openAiClient;
    private final MeterRegistry meterRegistry;
    
    public AdaptivePromptOptimizer(OpenAiClient openAiClient, 
                                 MeterRegistry meterRegistry) {
        this.openAiClient = openAiClient;
        this.meterRegistry = meterRegistry;
    }
    
    public String optimizePrompt(String originalPrompt, String feedback) {
        String optimizationPrompt = String.format("""
            请优化以下Prompt以提高回答质量:
            
            原始Prompt:
            %s
            
            用户反馈:
            %s
            
            请提供优化后的Prompt:
            """, originalPrompt, feedback);
        
        ChatResponse response = openAiClient.call(new Prompt(optimizationPrompt));
        return response.getResult().getOutput().getContent();
    }
    
    public void trackPromptEffectiveness(String promptId, double score) {
        Gauge.builder("prompt.effectiveness")
            .tag("prompt_id", promptId)
            .register(meterRegistry, score);
    }
}

向量数据库集成

向量存储选型

在企业级应用中,选择合适的向量数据库至关重要:

1. Pinecone集成

@Configuration
public class PineconeConfig {
    
    @Bean
    public PineconeClient pineconeClient() {
        return new PineconeClient.Builder()
            .withApiKey(System.getenv("PINECONE_API_KEY"))
            .withEnvironment(System.getenv("PINECONE_ENVIRONMENT"))
            .build();
    }
    
    @Bean
    public VectorStore pineconeVectorStore(PineconeClient pineconeClient) {
        return new PineconeVectorStore(pineconeClient, "my-index");
    }
}

2. 本地向量存储

@Service
public class InMemoryVectorStore implements VectorStore {
    
    private final Map<String, Document> documents = new ConcurrentHashMap<>();
    private final EmbeddingClient embeddingClient;
    
    public InMemoryVectorStore(EmbeddingClient embeddingClient) {
        this.embeddingClient = embeddingClient;
    }
    
    @Override
    public void add(List<Document> documents) {
        documents.forEach(doc -> this.documents.put(doc.getId(), doc));
    }
    
    @Override
    public List<Document> similaritySearch(float[] queryVector, int k) {
        return documents.values().stream()
            .sorted((d1, d2) -> Float.compare(
                cosineSimilarity(queryVector, d2.getEmbedding()),
                cosineSimilarity(queryVector, d1.getEmbedding())
            ))
            .limit(k)
            .collect(Collectors.toList());
    }
    
    private float cosineSimilarity(float[] a, float[] b) {
        float dotProduct = 0.0f;
        float normA = 0.0f;
        float normB = 0.0f;
        
        for (int i = 0; i < a.length; i++) {
            dotProduct += a[i] * b[i];
            normA += a[i] * a[i];
            normB += b[i] * b[i];
        }
        
        return dotProduct / (float)(Math.sqrt(normA) * Math.sqrt(normB));
    }
}

文档处理流水线

@Service
public class DocumentProcessingPipeline {
    
    private final EmbeddingClient embeddingClient;
    private final VectorStore vectorStore;
    private final TextSplitter textSplitter;
    
    public DocumentProcessingPipeline(EmbeddingClient embeddingClient,
                                    VectorStore vectorStore) {
        this.embeddingClient = embeddingClient;
        this.vectorStore = vectorStore;
        this.textSplitter = new RecursiveCharacterTextSplitter(1000, 200);
    }
    
    public void processAndStoreDocument(String documentId, String content) {
        // 1. 文本分割
        List<String> chunks = textSplitter.splitText(content);
        
        // 2. 向量化
        List<Document> documents = new ArrayList<>();
        for (int i = 0; i < chunks.size(); i++) {
            String chunkId = documentId + "_chunk_" + i;
            String chunkContent = chunks.get(i);
            float[] embedding = embeddingClient.embed(chunkContent);
            
            documents.add(new Document(chunkId, chunkContent, embedding));
        }
        
        // 3. 存储到向量数据库
        vectorStore.add(documents);
    }
    
    public List<Document> searchRelevantChunks(String query, int topK) {
        float[] queryEmbedding = embeddingClient.embed(query);
        return vectorStore.similaritySearch(queryEmbedding, topK);
    }
}

企业级应用实战案例

智能客服系统

系统架构

@RestController
@RequestMapping("/api/support")
public class SupportController {
    
    private final SupportService supportService;
    
    public SupportController(SupportService supportService) {
        this.supportService = supportService;
    }
    
    @PostMapping("/chat")
    public ResponseEntity<ChatResponse> handleChat(@RequestBody ChatRequest request) {
        try {
            String response = supportService.processUserQuery(
                request.getUserId(), 
                request.getMessage()
            );
            
            return ResponseEntity.ok(new ChatResponse(response));
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body(new ChatResponse("系统处理异常,请稍后重试"));
        }
    }
}

核心服务实现

@Service
public class SupportService {
    
    private final AiIntegrationService aiService;
    private final DocumentProcessingPipeline documentPipeline;
    private final ConversationHistoryService historyService;
    
    public SupportService(AiIntegrationService aiService,
                         DocumentProcessingPipeline documentPipeline,
                         ConversationHistoryService historyService) {
        this.aiService = aiService;
        this.documentPipeline = documentPipeline;
        this.historyService = historyService;
    }
    
    public String processUserQuery(String userId, String userMessage) {
        // 1. 获取对话历史
        List<ChatMessage> history = historyService.getConversationHistory(userId);
        
        // 2. 检索相关文档
        List<Document> relevantDocs = documentPipeline.searchRelevantChunks(userMessage, 3);
        
        // 3. 构建上下文
        String context = buildContext(history, relevantDocs);
        
        // 4. 生成回复
        String response = aiService.processQueryWithContext(userMessage, context);
        
        // 5. 保存对话历史
        historyService.saveMessage(userId, new ChatMessage("user", userMessage));
        historyService.saveMessage(userId, new ChatMessage("assistant", response));
        
        return response;
    }
    
    private String buildContext(List<ChatMessage> history, List<Document> documents) {
        StringBuilder context = new StringBuilder();
        
        // 添加历史对话
        context.append("=== 对话历史 ===\n");
        history.stream()
            .skip(Math.max(0, history.size() - 5)) // 只保留最近5条
            .forEach(msg -> context.append(String.format("%s: %s\n", 
                msg.getRole(), msg.getContent())));
        
        // 添加相关文档
        context.append("\n=== 相关文档 ===\n");
        documents.forEach(doc -> context.append(doc.getContent()).append("\n"));
        
        return context.toString();
    }
}

知识管理系统

文档索引服务

@Service
public class KnowledgeIndexService {
    
    private final DocumentProcessingPipeline documentPipeline;
    private final MetadataExtractor metadataExtractor;
    
    public KnowledgeIndexService(DocumentProcessingPipeline documentPipeline,
                               MetadataExtractor metadataExtractor) {
        this.documentPipeline = documentPipeline;
        this.metadataExtractor = metadataExtractor;
    }
    
    public void indexDocument(String documentId, MultipartFile file) throws IOException {
        // 1. 提取文档内容
        String content = extractContent(file);
        
        // 2. 提取元数据
        DocumentMetadata metadata = metadataExtractor.extract(file);
        
        // 3. 处理和存储
        documentPipeline.processAndStoreDocument(documentId, content);
        
        // 4. 存储元数据
        saveMetadata(documentId, metadata);
    }
    
    private String extractContent(MultipartFile file) throws IOException {
        // 根据文件类型选择不同的提取方法
        String contentType = file.getContentType();
        if ("application/pdf".equals(contentType)) {
            return extractPdfContent(file);
        } else if (contentType != null && contentType.startsWith("text/")) {
            return new String(file.getBytes(), StandardCharsets.UTF_8);
        } else {
            throw new IllegalArgumentException("不支持的文件类型: " + contentType);
        }
    }
    
    private String extractPdfContent(MultipartFile file) throws IOException {
        try (PDDocument document = PDDocument.load(file.getInputStream())) {
            PDFTextStripper stripper = new PDFTextStripper();
            return stripper.getText(document);
        }
    }
}

性能优化与监控

缓存策略

@Service
public class CachedAiService {
    
    private final AiIntegrationService aiService;
    private final Cache<String, String> responseCache;
    private final Cache<String, List<Document>> searchCache;
    
    public CachedAiService(AiIntegrationService aiService) {
        this.aiService = aiService;
        this.responseCache = Caffeine.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(Duration.ofMinutes(30))
            .build();
        this.searchCache = Caffeine.newBuilder()
            .maximumSize(5000)
            .expireAfterWrite(Duration.ofMinutes(15))
            .build();
    }
    
    public String processQuery(String query) {
        return responseCache.get(query, key -> aiService.processQuery(key));
    }
    
    public List<Document> searchDocuments(String query, int limit) {
        String cacheKey = query + "_" + limit;
        return searchCache.get(cacheKey, key -> 
            aiService.searchRelevantDocuments(query).stream()
                .limit(limit)
                .collect(Collectors.toList()));
    }
}

异步处理

@Service
public class AsyncAiProcessingService {
    
    private final TaskExecutor taskExecutor;
    private final AiIntegrationService aiService;
    
    public AsyncAiProcessingService(TaskExecutor taskExecutor,
                                  AiIntegrationService aiService) {
        this.taskExecutor = taskExecutor;
        this.aiService = aiService;
    }
    
    @Async
    public CompletableFuture<String> processQueryAsync(String query) {
        try {
            String result = aiService.processQuery(query);
            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            return CompletableFuture.failedFuture(e);
        }
    }
    
    public CompletableFuture<List<ProcessingResult>> batchProcess(
            List<String> queries) {
        List<CompletableFuture<ProcessingResult>> futures = queries.stream()
            .map(query -> processQueryAsync(query)
                .thenApply(result -> new ProcessingResult(query, result))
                .exceptionally(throwable -> 
                    new ProcessingResult(query, "处理失败: " + throwable.getMessage())))
            .collect(Collectors.toList());
        
        return CompletableFuture.allOf(futures.toArray

打赏

本文固定链接: https://www.cxy163.net/archives/10515 | 绝缘体

该日志由 绝缘体.. 于 2016年07月17日 发表在 未分类 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: AI原生应用开发新趋势:LangChain与Spring AI整合实战,打造智能企业级应用 | 绝缘体
关键字: , , , ,

AI原生应用开发新趋势:LangChain与Spring AI整合实战,打造智能企业级应用:等您坐沙发呢!

发表评论


快捷键:Ctrl+Enter