--- title: "02-执行计划准备的入口与整体流程" created: 2026-05-25 aliases: - 执行计划准备的入口与整体流程 tags: - 项目 --- # 执行计划准备的入口与整体流程 在讲解具体执行器的选择和执行流程之前,还有一个非常重要的环节还没有讲——**执行计划的准备**。在 `buildConversationExecution` 方法中,有这样一行代码: ```text return Mono.fromCallable(() -> prepareExecutionPlan(taskInfo)) .subscribeOn(Schedulers.boundedElastic()) ``` 这个 `prepareExecutionPlan` 方法是整个问答链路的"大脑",它负责把用户的原始问题整理成后续执行器可以消费的执行计划。这篇就来详细拆解这个方法的完整流程。 ## prepareExecutionPlan 方法的入口 先看 `BusinessChatService` 中的 `prepareExecutionPlan` 方法: ```java /** * 准备本轮对话真正要执行的计划,并将其结果同步回 TaskInfo 与上下文。 * * @param taskInfo 当前任务 * @return 执行计划 */ private ConversationExecutionPlan prepareExecutionPlan(TaskInfo taskInfo) { // 在这里统一完成问题改写、历史压缩、模式判定、检索规划等准备动作。 ConversationExecutionPlan executionPlan = chatPreparationOrchestrator.prepare(taskInfo); // 基于准备好的执行计划重新渲染 Agent 问题,确保时间锚定和历史摘要都被带入提示词。 executionPlan.setAgentQuestion(buildAgentQuestion(executionPlan)); if (executionPlan.getSelectedDocumentId() != null && !Objects.equals(executionPlan.getSelectedDocumentId(), taskInfo.selectedDocumentId())) { // 如果执行计划重新选择了文档范围,需要同步刷新持久层会话范围和运行时上下文。 conversationArchiveStore.refreshSessionScope( taskInfo.conversationId(), executionPlan.getChatMode(), executionPlan.getSelectedDocumentId(), executionPlan.getSelectedDocumentName() ); putContextIfNotNull(taskInfo.runnableConfig(), ChatContextKeys.SELECTED_DOCUMENT_ID, executionPlan.getSelectedDocumentId()); putContextIfNotBlank(taskInfo.runnableConfig(), ChatContextKeys.SELECTED_DOCUMENT_NAME, executionPlan.getSelectedDocumentName()); putContextIfNotNull(taskInfo.runnableConfig(), ChatContextKeys.SELECTED_TASK_ID, executionPlan.getSelectedTaskId()); } // 把最新执行计划与调试信息写回任务对象,供执行器和最终收尾逻辑统一读取。 taskInfo.setExecutionPlan(executionPlan); taskInfo.setDebugTrace(initializeDebugTrace(executionPlan)); taskInfo.runnableConfig().context().put(ChatContextKeys.DEBUG_TRACE, taskInfo.debugTrace()); return executionPlan; } ``` 这个方法看起来很简单,核心逻辑只有一行: ```java ConversationExecutionPlan executionPlan = chatPreparationOrchestrator.prepare(taskInfo); ``` 真正的准备工作都在 `ChatPreparationOrchestrator.prepare` 方法中完成。后续的代码主要是: - **渲染 Agent 问题**——把时间锚定和历史摘要带入提示词 - **同步文档范围**——如果执行计划重新选择了文档,需要同步到持久层和运行时上下文 - **回写执行计划**——把执行计划和调试信息写回 TaskInfo ## ChatPreparationOrchestrator 的职责 `ChatPreparationOrchestrator` 是聊天准备编排器,它的职责非常明确: ```java /** * 聊天准备编排器。 * 该组件负责把一轮原始对话请求整理成后续执行器真正可以消费的 {@link ConversationExecutionPlan}。 * 核心职责包括: * 1. 装载会话记忆与历史上下文 * 2. 生成面向检索的改写问题 * 3. 在自动文档模式下进行知识范围路由 * 4. 判断是否需要澄清 * 5. 判定最终执行模式是 Agent、RAG、结构图直答还是结构图取证 * 6. 汇总所有中间结果并生成最终执行计划 */ @Slf4j @Service public class ChatPreparationOrchestrator { // ... 依赖注入 } ``` 从注释可以看出,这个编排器要做 6 件事情,每一件都很重要。 ## prepare 方法的整体流程 让我们先看 `prepare` 方法的完整签名和主要步骤: ```java /** * 准备一轮对话执行计划。 * 这是进入真正执行器之前的总编排入口,会把用户问题、历史记忆、路由结果、改写结果和时间敏感性判断统一汇总。 * * @param taskInfo 当前会话运行态 * @return 后续执行器可直接消费的执行计划 */ public ConversationExecutionPlan prepare(TaskInfo taskInfo) { // 先把本轮编排需要反复使用的运行态字段拆出来,避免后续链路里层层从 taskInfo 取值影响可读性。 String conversationId = taskInfo.conversationId(); String question = taskInfo.question(); ChatQueryMode chatMode = taskInfo.chatMode(); Long selectedDocumentId = taskInfo.selectedDocumentId(); String selectedDocumentName = taskInfo.selectedDocumentName(); Long selectedTaskId = taskInfo.selectedTaskId(); LocalDate currentDate = taskInfo.currentDate(); String currentDateText = taskInfo.currentDateText(); ConversationTraceRecorder traceRecorder = taskInfo.traceRecorder(); // 第一步:装载会话记忆 ConversationMemoryContext memoryContext = summarizeHistory(conversationId, traceRecorder); // 第二步:构建历史上下文 HistoryPlanningContext historyPlanningContext = buildHistoryPlanningContext(memoryContext); String historySummary = buildPlanningHistory(memoryContext, historyPlanningContext); AnswerHistoryContext answerHistoryContext = buildAnswerHistoryContext(question, memoryContext.getAnswerRecentTranscript()); // 第三步:识别时间敏感性 boolean requiresCurrentDateAnchoring = TimeSensitiveQueryHelper.requiresCurrentDateAnchoring(question); boolean requiresFreshSearch = TimeSensitiveQueryHelper.requiresFreshSearch(question); // 第四步:根据 chatMode 判断路由 if (chatMode == ChatQueryMode.OPEN_CHAT) { // 开放式问答直接走 ReactAgent 路径 return basePlan(...).mode(ExecutionMode.REACT_AGENT).build(); } // 第五步:问题改写 RagRewriteResult rewriteResult = chatQueryRewriteService.rewrite(question, historySummary, traceRecorder); // 第六步:文档路由(AUTO_DOCUMENT 模式) if (chatMode == ChatQueryMode.AUTO_DOCUMENT) { KnowledgeRouteDecision routeDecision = knowledgeRouteService.route(question, rewriteQuestion); // 判断是否需要澄清 if (shouldAskClarification(routeDecision, candidateDocuments)) { return basePlan(...).mode(ExecutionMode.CLARIFICATION).build(); } } // 第七步:文档问题路由 DocumentNavigationDecision navigationDecision = documentQuestionRouter.route(routedDocumentId, question, rewriteResult); // 第八步:确定最终执行模式 ExecutionMode executionMode = navigationDecision.getExecutionMode(); // 第九步:构建执行计划 return basePlan(...).mode(executionMode).build(); } ``` 这个方法的流程可以分成 9 个步骤,我们用一张流程图来展示: ![[Fr0FCwhsiB-lIbMzUTmJycn2V3vp-372ab340.png]] ## 核心步骤概述 下面简单介绍每个步骤的作用,后续文档会详细讲解: ### 步骤1:装载会话记忆 调用 `summarizeHistory` 方法,加载会话的长期摘要、近期对话窗口、回答窗口和压缩信息。这些信息会用于后续的问题改写和历史上下文构建。 ### 步骤2:构建历史上下文 基于会话记忆构建两种历史上下文: - **面向编排的历史摘要**:用于问题改写和路由判断 - **面向回答的历史上下文**:用于最终的答案生成 ### 步骤3:识别时间敏感性 判断用户问题是否包含"今天"、"最新"、"现在"等时间敏感词,这会影响后续的路由和提示词。 ### 步骤4:根据 chatMode 判断路由 如果是 `OPEN_CHAT` 模式(开放式问答),直接返回 `ReactAgent` 执行计划,不需要后续的文档检索流程。 ### 步骤5:问题改写 调用 `chatQueryRewriteService.rewrite` 方法,把用户的口语化问题改写成更适合检索的问题。比如: - 原问题:"它的价格是多少?" - 改写后:"Java 开发工具的价格是多少?" ### 步骤6:文档路由(AUTO\_DOCUMENT 模式) 如果是 `AUTO_DOCUMENT` 模式(自动知识问答),系统会自动判断应该在哪个文档中检索。如果候选文档不稳定或置信度不足,会返回澄清执行计划,让用户选择具体文档。 ### 步骤7:文档问题路由 调用 `documentQuestionRouter.route` 方法,判断这个问题应该用什么方式回答: - **结构图直答**:直接从文档结构图中提取答案 - **结构图取证**:先从结构图定位,再检索详细内容 - **混合检索**:标准的 RAG 路径 ### 步骤8:确定最终执行模式 根据文档问题路由的结果,确定最终的执行模式(`RETRIEVAL`、`GRAPH_ONLY`、`GRAPH_THEN_EVIDENCE` 等)。 ### 步骤9:构建执行计划 把前面所有步骤的结果汇总,构建一个完整的 `ConversationExecutionPlan` 对象,返回给上层。 ## 小结 `prepareExecutionPlan` 方法是整个问答链路的"大脑",它通过 `ChatPreparationOrchestrator.prepare` 方法完成了 9 个核心步骤: - 装载会话记忆 - 构建历史上下文 - 识别时间敏感性 - 根据 chatMode 判断路由 - 问题改写 - 文档路由(AUTO\_DOCUMENT 模式) - 文档问题路由 - 确定最终执行模式 - 构建执行计划 后续文档会详细讲解每个步骤的实现细节。 --- **企业级项目导航**:⬅️ [[01-文档上传到RAG检索完整链路讲解|01-文档上传到RAG检索完整链路讲解]] | 02-执行计划准备的入口与整体流程 | ➡️ [[01-Agent 对话功能如何使用|01-Agent 对话功能如何使用]]