Skip to main content

概述

本文档详细描述LangChat Pro中各个核心场景的完整接口调用链路,帮助开发者理解从客户端请求到最终响应的完整流程。项目使用RESTFUL API接口进行通信。

场景1: 聊天对话(Chat)

完整链路

┌─────────────────────────────────────────────────────────────┐
│                        Client                          │
│                   (前端/Vue/React)                      │
└────────────────────┬────────────────────────────────────┘
                     │ HTTP / REST API
                     │ POST /api/chat/chat
                     │ ChatReq (JSON)

┌─────────────────────────────────────────────────────────────┐
│              LcChatController                         │
│  (REST API接口)                                      │
└────────────────────┬────────────────────────────────────┘

                     │ 转发请求

┌─────────────────────────────────────────────────────────────┐
│               ChatService                              │
│  (业务逻辑层)                                          │
│  - 参数验证                                            │
│  - 权限检查                                            │
│  - 敏感词过滤                                          │
│  - 消息保存                                            │
└────────────────────┬────────────────────────────────────┘

                     │ LcChatReq

┌─────────────────────────────────────────────────────────────┐
│              LcChatService                             │
│  (核心服务层)                                          │
│  - 上下文构建                                          │
│  - 模型获取                                            │
│  - AI Service组装                                       │
│  - 流式调用                                            │
└────────────────────┬────────────────────────────────────┘

         ┌───────────┼───────────┬───────────┐
         │           │           │           │
         ▼           ▼           ▼           ▼
┌──────────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐
│ContextBuilder│ │ModelFactory│ │AiService │ │RagBuilder│
│ (构建上下文)  │ │(获取模型) │ │Builder   │ │(构建RAG) │
└──────┬───────┘ └────┬─────┘ └────┬─────┘ └────┬──────┘
       │               │            │            │
       └───────────────┼────────────┴────────────┘


         ┌──────────────────────────┐
         │   LangChain4j Agent   │
         │      (AI推理)        │
         └───────────┬──────────┘

                     │ TokenStream

         ┌──────────────────────────┐
         │   ChatListener        │
         │  (事件监听)          │
         │  - Token统计          │
         │  - 消息记录          │
         └───────────┬──────────┘

                     │ 返回响应

         ┌──────────────────────────┐
         │       Client          │
         │    (接收响应)         │
         └───────────────────────┘

关键代码调用

1. REST API调用

// 前端 - HTTP请求
async function chat(chatReq) {
    const response = await fetch('/api/chat/chat', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + token
        },
        body: JSON.stringify(chatReq)
    });
    
    // 处理流式响应
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        const text = decoder.decode(value);
        console.log('收到消息', text);
    }
}

// 后端 - LcChatController
@PostMapping("/chat/chat")
public void chat(@RequestBody LcChatReq req, HttpServletResponse response) throws IOException {
    // 设置响应头
    response.setContentType("text/plain;charset=UTF-8");
    response.setHeader("Transfer-Encoding", "chunked");
    
    // 获取输出流
    PrintWriter writer = response.getWriter();
    
    // 调用聊天服务
    TokenStream stream = lcChatService.streamingChat(req);
    
    // 流式返回
    stream.onNext(token -> {
        writer.write(token);
        writer.flush();
    });
    
    stream.onComplete(answer -> {
        writer.write("\n[END]");
        writer.flush();
    });
    
    stream.onError(error -> {
        writer.write("\n[ERROR]" + error.getMessage());
    });
}

2. 核心服务构建

// LcChatService
public TokenStream streamingChat(LcChatReq req) {
    // 1. 生成chatId
    String chatId = LcIdUtil.getUUID();
    
    // 2. 构建ChatContext
    BaseChatContext context = contextBuilder.build(req);
    context.setChatId(chatId);
    
    // 3. 获取StreamingChatModel
    StreamingChatModel model = modelFactory.getStreamingModel(context, chatId);
    
    // 4. 构建Agent
    LangChatAgent agent = aiServiceBuilder.build(model, req, req.getConversationId());
    
    // 5. 调用Agent
    return agent.stream(req.getConversationId(), req.getMessage());
}

3. AI Service构建

// AiServiceBuilder
public LangChatAgent build(StreamingChatModel model, LcChatReq req, String conversationId) {
    var builder = AiServices.builder(LangChatAgent.class)
        .streamingChatModel(model)
        .chatMemoryProvider(id -> buildChatMemory(conversationId, req));
    
    // 系统提示词
    var prompt = promptProcessor.process(req);
    if (StrUtil.isNotBlank(prompt)) {
        builder.systemMessageProvider(memoryId -> prompt);
    }
    
    // 动态工具
    toolFactoryList.forEach(factory -> factory.dynamicTools(req, builder));
    
    // RAG
    var ragComponents = ragRetrieverBuilder.build(req);
    if (CollUtil.isNotEmpty(ragComponents.retrievers())) {
        var augmentor = DefaultRetrievalAugmentor.builder()
            .contentAggregator(ragComponents.aggregator())
            .queryRouter(new DefaultQueryRouter(ragComponents.retrievers()))
            .build();
        builder.retrievalAugmentor(augmentor);
    }
    
    return builder.build();
}

场景2: 知识库检索(RAG)

完整链路

┌─────────────────────────────────────────────────────────────┐
│                       Client                          │
│                (用户提问)                                │
└────────────────────┬────────────────────────────────────┘

                     │ POST /api/chat/chat
                     │ 包含knowledgeIds

┌─────────────────────────────────────────────────────────────┐
│            RagRetrieverBuilder                          │
│  (构建RAG组件)                                       │
└────────────────────┬────────────────────────────────────┘

         ┌───────────┼───────────┐
         │           │           │
         ▼           ▼           ▼
┌──────────────┐ ┌──────────┐ ┌────────────┐
│ Knowledge    │ │   SQL    │ │ Content    │
│ Retriever    │ │Retriever │ │ Aggregator │
│  (知识库检索)  │ │(SQL检索) │ │ (内容聚合) │
└──────┬───────┘ └────┬─────┘ └─────┬──────┘
       │               │              │
       │               │              ▼
       │               │      ┌──────────────┐
       │               │      │ QueryRouter  │
       │               │      │ (查询路由)   │
       │               │      └──────┬───────┘
       │               │             │
       └───────────────┼─────────────┘


         ┌──────────────────────────┐
         │  RetrievalAugmentor   │
         │    (检索增强器)        │
         └───────────┬──────────┘


         ┌──────────────────────────┐
         │    注入到Prompt      │
         └───────────┬──────────┘


         ┌──────────────────────────┐
         │         LLM          │
         │      (生成回答)       │
         └───────────────────────┘

知识库检索流程

1. 向量检索

// KnowledgeRetrieverBuilder
public ContentRetriever buildRetriever(AigcKnowledge knowledge) {
    // 1. 获取向量库
    AigcVectorStore vectorStore = vectorStoreService.getById(knowledge.getVectorStoreId());
    
    // 2. 获取Embedding模型
    EmbeddingModel embeddingModel = embeddingModelFactory.getEmbeddingModel(
        knowledge.getEmbeddingModelId()
    );
    
    // 3. 创建向量存储
    VectorStore vectorStore = createVectorStore(vectorStore, embeddingModel);
    
    // 4. 创建检索器
    return VectorStoreRetriever.from(vectorStore)
        .maxResults(knowledge.getTopK())
        .minScore(knowledge.getSimilarityThreshold())
        .build();
}

2. Rerank聚合

// ReRankingContentAggregator
public List<Content> aggregate(Query query, List<Retrieval> retrievals) {
    // 1. 合并所有检索结果
    List<Content> allContents = retrievals.stream()
        .flatMap(r -> r.contents().stream())
        .collect(Collectors.toList());
    
    // 2. 去重
    Set<String> seen = new HashSet<>();
    List<Content> uniqueContents = allContents.stream()
        .filter(c -> seen.add(c.textSegment().text()))
        .collect(Collectors.toList());
    
    // 3. Rerank
    List<ScoredText> scoredTexts = scoringModel.scoreAll(
        query.text(), 
        uniqueContents
    );
    
    // 4. 返回Top-N
    return scoredTexts.stream()
        .sorted((a, b) -> Double.compare(b.score(), a.score()))
        .limit(10)
        .map(st -> Content.from(st.text()))
        .collect(Collectors.toList());
}

场景3: 插件工具调用

完整链路

┌─────────────────────────────────────────────────────────────┐
│                     LLM                                   │
│                (分析问题)                              │
└────────────────────┬────────────────────────────────────┘

                     │ 识别需要调用工具

┌─────────────────────────────────────────────────────────────┐
│          AiServices (LangChain4j)                      │
│  (工具调用)                                          │
└────────────────────┬────────────────────────────────────┘

         ┌───────────┼───────────┐
         │           │           │
         ▼           ▼           ▼
┌──────────────┐ ┌──────────┐ ┌───────────┐
│ PluginTool  │ │ McpTool │ │ Custom    │
│ Factory     │ │ Factory  │ │ Factory   │
│  (插件工具)  │ │(MCP工具) │ │(自定义)   │
└──────┬───────┘ └────┬─────┘ └─────┬──────┘
       │               │              │
       │               │              │
       ▼               ▼              ▼
┌──────────┐   ┌──────────┐   ┌──────────┐
│ Time     │   │ Search   │   │ HTTP     │
│ Plugin   │   │ Plugin   │   │ Request  │
└────┬─────┘   └────┬─────┘   └────┬─────┘
     │              │              │
     └──────────────┼──────────────┘

                    │ 工具执行结果

         ┌──────────────────────────┐
         │         LLM          │
         │    (生成回答)       │
         └───────────────────────┘

插件工具调用

// PluginToolFactory
@Override
public void dynamicTools(LcChatReq req, AiServices<LangChatAgent> aiServices) {
    // 1. 获取启用的插件
    var plugins = pluginService.getEnabledPlugins(req.getUserId(), req.getAppId());
    
    // 2. 构建工具列表
    var tools = plugins.stream()
        .map(this::convertToTool)
        .collect(Collectors.toList());
    
    // 3. 添加到AiServices
    aiServices.tools(tools);
}

// 工具定义
private Tool convertToTool(AigcPlugin plugin) {
    return Tool.builder()
        .name(plugin.getName())
        .description(plugin.getDescription())
        .addParameter("input", 
            JsonSchemaProperty.string()
                .description("插件输入参数")
                .build())
        .execute(request -> {
            try {
                return executePlugin(plugin, request);
            } catch (Exception e) {
                log.error("插件执行失败: pluginName={}", plugin.getName(), e);
                return "插件执行失败: " + e.getMessage();
            }
        })
        .build();
}

场景4: 文档上传与向量化

完整链路

┌─────────────────────────────────────────────────────────────┐
│                       Client                          │
│                (上传文档)                                │
└────────────────────┬────────────────────────────────────┘

                     │ POST /api/docs/upload
                     │ MultipartFile

┌─────────────────────────────────────────────────────────────┐
│            AigcDocsController                          │
│  (文档上传接口)                                       │
└────────────────────┬────────────────────────────────────┘

                     │ 上传到OSS

┌─────────────────────────────────────────────────────────────┐
│              OSS存储                                   │
│  (阿里云OSS/腾讯云COS)                              │
└────────────────────┬────────────────────────────────────┘

                     │ 返回URL

┌─────────────────────────────────────────────────────────────┐
│            创建AigcDocs记录                             │
│  - 保存文件信息                                          │
│  - 状态: processing                                     │
└────────────────────┬────────────────────────────────────┘

                     │ 异步处理任务

┌─────────────────────────────────────────────────────────────┐
│              DocsProcessor                               │
│  (文档处理)                                           │
└────────────────────┬────────────────────────────────────┘

         ┌───────────┼───────────┬───────────┐
         │           │           │           │
         ▼           ▼           ▼           ▼
┌──────────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐
│  文档解析    │ │ 文本提取  │ │ 文本分块  │ │  向量化    │
│ (Tika/Poi) │ │           │ │ (Splitter)│ │(Embedding) │
└──────┬───────┘ └────┬─────┘ └────┬─────┘ └─────┬──────┘
       │               │              │              │
       └───────────────┼──────────────┼──────────────┘
                       │              │
                       ▼              ▼
         ┌──────────────────────────┐  ┌────────────┐
         │      文本分段          │  │   向量    │
         │   (AigcSegment)      │  │  列表     │
         └───────────┬──────────┘  └─────┬──────┘
                     │                    │
                     │                    │
                     └──────────┬─────────┘


         ┌──────────────────────────┐
         │   批量存储向量库      │
         │ (PGVector/Milvus)    │
         └───────────┬──────────┘

                     │ 更新状态

         ┌──────────────────────────┐
         │  更新AigcDocs状态     │
         │  - status: completed   │
         │  - segmentCount: N    │
         └───────────────────────┘

文档处理代码

@Async
public void process(String docsId) {
    AigcDocs docs = docsService.getById(docsId);
    
    try {
        // 1. 下载文件
        String filePath = downloadFile(docs.getFileUrl());
        
        // 2. 解析文档
        String text = docsParser.parse(filePath, docs.getFileType());
        
        // 3. 文本分块
        AigcKnowledge knowledge = knowledgeService.getById(docs.getKnowledgeId());
        List<String> chunks = textSplitter.split(
            text,
            knowledge.getSplitStrategy(),
            knowledge.getChunkSize(),
            knowledge.getOverlapSize()
        );
        
        // 4. 批量向量化
        List<List<Float>> embeddings = embeddingService.embedBatch(
            knowledge.getEmbeddingModelId(),
            chunks
        );
        
        // 5. 批量插入向量库
        List<AigcSegment> segments = createSegments(docsId, chunks, embeddings);
        vectorStoreService.batchInsert(knowledge.getVectorStoreId(), segments);
        
        // 6. 更新状态
        docs.setStatus("completed");
        docs.setSegmentCount(chunks.size());
        docsService.updateById(docs);
        
    } catch (Exception e) {
        docs.setStatus("failed");
        docs.setErrorMessage(e.getMessage());
        docsService.updateById(docs);
    }
}

场景5: Workflow执行

完整链路

┌─────────────────────────────────────────────────────────────┐
│                       Client                          │
│              (启动Workflow)                              │
└────────────────────┬────────────────────────────────────┘

                     │ POST /api/workflow/execute
                     │ Flow + input (JSON)

┌─────────────────────────────────────────────────────────────┐
│          WorkflowExecutor                                │
│  (工作流执行器)                                       │
└────────────────────┬────────────────────────────────────┘

                     │ 构建DAG

┌─────────────────────────────────────────────────────────────┐
│              DAG构建                                    │
│  - 添加节点                                            │
│  - 添加边                                              │
│  - 验证无环                                            │
└────────────────────┬────────────────────────────────────┘

                     │ 拓扑排序

┌─────────────────────────────────────────────────────────────┐
│           按顺序执行节点                                 │
└────────────────────┬────────────────────────────────────┘

         ┌───────────┼───────────┐
         │           │           │
         ▼           ▼           ▼
┌──────────────┐ ┌──────────┐ ┌───────────┐
│  Start Node  │ │ LLM Node │ │  If Node  │
│  (开始节点)  │ │(LLM调用) │ │ (条件判断) │
└──────┬───────┘ └────┬─────┘ └─────┬──────┘
       │               │              │
       │               │              │
       └───────────────┼──────────────┘


         ┌──────────────────────────┐
         │      分支执行         │
         │  - True Branch        │
         │  - False Branch       │
         └───────────┬──────────┘

                     │ 节点输出

         ┌──────────────────────────┐
         │      Merge Node       │
         │      (合并节点)       │
         └───────────┬──────────┘


         ┌──────────────────────────┐
         │       End Node       │
         │      (结束节点)       │
         └───────────┬──────────┘

                     │ 返回结果

         ┌──────────────────────────┐
         │      执行结果         │
         └───────────────────────┘

Workflow执行代码

public WorkflowResult execute(Flow flow, Map<String, Object> input, WorkflowExecutionListener listener) {
    // 1. 构建DAG
    DAG dag = buildDAG(flow);
    
    // 2. 创建执行上下文
    WorkflowContext context = new WorkflowContext();
    context.setFlow(flow);
    context.setInput(input);
    context.setExecutionId(LcIdUtil.getUUID());
    
    // 3. 保存初始状态
    stateStore.save(context.getExecutionId(), new WorkflowState());
    
    // 4. 按拓扑顺序执行节点
    List<String> executionOrder = dag.topologicalOrder();
    Map<String, Object> result = new HashMap<>(input);
    
    for (String nodeId : executionOrder) {
        FlowNode node = flow.getNodes().stream()
            .filter(n -> n.getId().equals(nodeId))
            .findFirst()
            .orElseThrow();
        
        // 执行节点
        listener.onNodeStart(nodeId);
        try {
            Map<String, Object> nodeInput = prepareNodeInput(node, result, dag);
            Map<String, Object> nodeOutput = executeNode(node, nodeInput, context);
            result.putAll(nodeOutput);
            listener.onNodeComplete(nodeId, nodeOutput);
        } catch (Exception e) {
            listener.onNodeError(nodeId, e);
            throw new WorkflowExecutionException("节点执行失败: " + nodeId, e);
        }
    }
    
    return new WorkflowResult(result, context.getExecutionId());
}

场景6: Embedding向量化

完整链路

┌─────────────────────────────────────────────────────────────┐
│                       Client                          │
│               (调用Embedding API)                        │
└────────────────────┬────────────────────────────────────┘

                     │ POST /api/embedding
                     │ modelId + text (JSON)

┌─────────────────────────────────────────────────────────────┐
│         LcEmbeddingService                            │
│  (Embedding服务)                                     │
└────────────────────┬────────────────────────────────────┘

                     │ 获取模型配置

┌─────────────────────────────────────────────────────────────┐
│        AigcModelService                               │
│  (获取模型配置)                                       │
└────────────────────┬────────────────────────────────────┘

                     │ 查询模型

┌─────────────────────────────────────────────────────────────┐
│       EmbeddingModelFactory                           │
│  (创建Embedding模型)                                  │
└────────────────────┬────────────────────────────────────┘

                     │ 检查缓存

         ┌──────────────────────────┐
         │   模型实例缓存         │
         │  (Caffeine Cache)     │
         └───────┬───────────────┘

                 │ 缓存未命中

         ┌──────────────────────────┐
         │  创建Embedding模型     │
         │  - OpenAI            │
         │  - Ollama            │
         │  - Qwen              │
         └───────┬──────────────┘

                 │ 调用模型API

         ┌──────────────────────────┐
         │    向量化完成          │
         │  返回向量列表         │
         └───────────────────────┘

性能优化点

1. 模型实例缓存

// ChatModelFactory
private Cache<String, ChatModel> chatModelCache;

public ChatModel getChatModel(String modelId) {
    String cacheKey = "chat:" + modelId;
    return chatModelCache.get(cacheKey, k -> createChatModel(modelId));
}

2. 批量向量化

// EmbeddingService
public List<List<Float>> embedBatch(String modelId, List<String> texts) {
    // 分批处理
    int batchSize = 100;
    List<List<Float>> results = new ArrayList<>();
    
    for (int i = 0; i < texts.size(); i += batchSize) {
        int end = Math.min(i + batchSize, texts.size());
        List<String> batch = texts.subList(i, end);
        results.addAll(embedBatchInternal(modelId, batch));
    }
    
    return results;
}

3. 异步处理

@Async("workflowExecutor")
public void process(String docsId) {
    // 异步处理文档
}

4. 连接池复用

// RestTemplate配置
@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        // 配置连接池
        HttpComponentsClientHttpRequestFactory factory = 
            new HttpComponentsClientHttpRequestFactory();
        
        CloseableHttpClient httpClient = HttpClients.custom()
            .setMaxConnTotal(200)
            .setMaxConnPerRoute(20)
            .build();
        
        factory.setHttpClient(httpClient);
        return new RestTemplate(factory);
    }
}

参考文档