---
status: "Active"
date: "2025-11-17"
author: "AI 助手"
---

# 设计文档：可配置的分析模板与编排器

## 1. 概述与目标

### 1.1. 问题陈述

我们当前基于 Rust 的后端缺少执行智能、多步骤财务分析所需的核心业务逻辑。`report-generator-service` 作为此逻辑的载体，其内部实现尚不完整。更重要的是，当前的系统设计缺少一个清晰的、可扩展的方式来管理和复用成套的分析流程，并且在配置初始化方面存在对本地文件的依赖，这不符合我们健壮的系统设计原则。

### 1.2. 目标

本任务旨在我们的 Rust 微服务架构中，设计并实现一个以**分析模板集（Analysis Template Sets）**为核心的、健壮的、可配置的**分析模块编排器**。该系统将允许我们创建、管理和执行多套独立的、包含复杂依赖关系的分析工作流。

为达成此目标，需要完成以下任务：
1.  **引入分析模板集**：在系统顶层设计中引入“分析模板集”的概念，每个模板集包含一套独立的分析模块及其配置。
2.  **实现前端模板化管理**：在前端配置中心实现对“分析模板集”的完整 CRUD 管理，并允许在每个模板集内部对分析模块进行 CRUD 管理。
3.  **构建健壮的后端编排器**：在 `report-generator-service` 中实现一个能够执行指定分析模板集的后端编排器，该编排器需基于拓扑排序来处理模块间的依赖关系。
4.  **实现无文件依赖的数据初始化**：通过在服务二进制文件中嵌入默认配置的方式，实现系统首次启动时的数据播种（Seeding），彻底移除对本地配置文件的依赖。

## 2. 新数据模型 (`common-contracts`)

为了支持“分析模板集”的概念，我们需要定义新的数据结构。

```rust
// common-contracts/src/config_models.rs

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// 整个系统的分析模板配置，作为顶级对象存储在数据库中
// Key: 模板ID (e.g., "standard_fundamentals")
pub type AnalysisTemplateSets = HashMap<String, AnalysisTemplateSet>;

// 单个分析模板集，代表一套完整的分析流程
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AnalysisTemplateSet {
    pub name: String, // 人类可读的模板名称, e.g., "标准基本面分析"
    // 该模板集包含的所有分析模块
    // Key: 模块ID (e.g., "fundamental_analysis")
    pub modules: HashMap<String, AnalysisModuleConfig>,
}

// 单个分析模块的配置 (与之前定义保持一致)
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AnalysisModuleConfig {
    pub name: String,
    pub provider_id: String,
    pub model_id: String,
    pub prompt_template: String,
    // 依赖关系列表，其中的字符串必须是同一个模板集内其他模块的ID
    pub dependencies: Vec<String>,
}
```

## 3. 系统架构与数据流

### 3.1. 高层数据流

1.  **配置流程**:
    *   **用户** 在 **前端** 与配置页面交互，创建或修改一个“分析模板集”。
    *   **前端** 向 **API 网关** 发送 `PUT /api/v1/configs/analysis_template_sets` 请求。
    *   **API 网关** 将请求代理至 **数据持久化服务**，由其将序列化后的 `AnalysisTemplateSets` 对象完整保存到数据库中。

2.  **执行流程**:
    *   **用户** 在 **前端** 选择一个**分析模板集**，然后为特定的股票代码触发分析。
    *   **前端** 向 **API 网关** 发送 `POST /api/v1/analysis-requests/{symbol}` 请求，请求体中包含所选的 `template_id`。
    *   **API 网关** 验证请求，并向 **NATS 消息总线** 发布一条包含 `symbol`, `template_id` 和 `request_id` 的 `GenerateReportCommand` 消息。
    *   **报告生成服务** 订阅该消息，并根据 `template_id` 启动指定的编排工作流。

## 4. 前端实施计划 (`/config` 页面)

前端配置页面需要重构为两级结构：

1.  **第一级：模板集管理**
    *   显示一个包含所有“分析模板集”的列表。
    *   提供“创建新模板集”、“重命名”、“删除模板集”的功能。
    *   用户选择一个模板集后，进入第二级管理界面。

2.  **第二级：分析模块管理 (在选定的模板集内)**
    *   **主界面**: 进入模板集后，主界面将以列表形式展示该模板集内所有的分析模块。每个模块将以一个独立的“卡片”形式呈现。
    *   **创建 (Create)**:
        *   在模块列表的顶部或底部，将设置一个“新增分析模块”按钮。
        *   点击后，将展开一个表单，要求用户输入新模块的**模块ID**（唯一的、机器可读的英文标识符）和**模块名称**（人类可读的显示名称）。
    *   **读取 (Read)**:
        *   每个模块卡片默认会显示其**模块名称**和**模块ID**。
        *   卡片可以被展开，以显示其详细配置。
    *   **更新 (Update)**:
        *   在展开的模块卡片内，所有配置项均可编辑：
            *   **LLM Provider**: 一个下拉菜单，选项为系统中所有已配置的LLM供应商。
            *   **Model**: 一个级联下拉菜单，根据所选的Provider动态加载其可用模型。
            *   **提示词模板**: 一个多行文本输入框，用于编辑模块的核心Prompt。
            *   **依赖关系**: 一个复选框列表，该列表**仅显示当前模板集内除本模块外的所有其他模块**，用于勾选依赖项。
    *   **删除 (Delete)**:
        *   每个模块卡片的右上角将设置一个“删除”按钮。
        *   点击后，会弹出一个确认对话框，防止用户误操作。

## 6. 数据库与数据结构设计

为了支撑上述功能，我们需要在 `data-persistence-service` 中明确两个核心的数据存储模型：一个用于存储**配置**，一个用于存储**结果**。

### 6.1. 配置存储：`system_config` 表

我们将利用现有的 `system_config` 表来存储整个分析模板集的配置。

-   **用途**: 作为所有分析模板集的“单一事实来源”。
-   **存储方式**:
    -   表中的一条记录。
    -   `config_key` (主键): `analysis_template_sets`
    -   `config_value` (类型: `JSONB`): 存储序列化后的 `AnalysisTemplateSets` (即 `HashMap<String, AnalysisTemplateSet>`) 对象。
-   **对应数据结构 (`common-contracts`)**: 我们在第2节中定义的 `AnalysisTemplateSets` 类型是此记录的直接映射。

### 6.2. 结果存储：`analysis_results` 表 (新)

为了存储每次分析工作流执行后，各个模块生成的具体内容，我们需要一张新表。

-   **表名**: `analysis_results`
-   **用途**: 持久化存储每一次分析运行的产出，便于历史追溯和未来查询。
-   **SQL Schema**:
    ```sql
    CREATE TABLE analysis_results (
        id BIGSERIAL PRIMARY KEY,
        request_id UUID NOT NULL,          -- 关联单次完整分析请求的ID
        symbol VARCHAR(32) NOT NULL,       -- 关联的股票代码
        template_id VARCHAR(64) NOT NULL,  -- 使用的分析模板集ID
        module_id VARCHAR(64) NOT NULL,    -- 产出此结果的模块ID
        content TEXT NOT NULL,             -- LLM生成的分析内容
        meta_data JSONB,                   -- 存储额外元数据 (e.g., model_name, tokens, elapsed_ms)
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        
        -- 建立索引以优化查询
        INDEX idx_analysis_results_request_id (request_id),
        INDEX idx_analysis_results_symbol_template (symbol, template_id)
    );
    ```
-   **对应数据结构 (`common-contracts`)**:
    ```rust
    // common-contracts/src/dtos.rs

    #[derive(Serialize, Deserialize, Debug, Clone)]
    pub struct NewAnalysisResult {
        pub request_id: Uuid,
        pub symbol: String,
        pub template_id: String,
        pub module_id: String,
        pub content: String,
        pub meta_data: serde_json::Value,
    }
    ```

## 5. 后端实施计划

### 5.1. `data-persistence-service`

-   **数据初始化 (无文件依赖)**: 实现一次性的、基于硬编码的启动逻辑。
    1.  在 `data-persistence-service` 的代码中，将 `config/analysis-config.json` 的内容硬编码为一个 Rust 字符串常量。
    2.  在服务启动时，检查 `system_config` 表中是否存在键为 `analysis_template_sets` 的记录。
    3.  如果**不存在**，则：
        a.  解析硬编码的字符串，构建一个默认的 `AnalysisTemplateSet` (例如，ID为 `default`, 名称为 “默认分析模板”)。
        b.  将这个默认模板集包装进一个 `AnalysisTemplateSets` 的 HashMap 中。
        c.  将序列化后的 `AnalysisTemplateSets` 对象写入数据库。
    4.  此机制确保系统在首次部署时，无需任何外部文件即可拥有一套功能完备的默认分析模板。
-   **新职责**: 实现对 `analysis_results` 表的CRUD操作API。

### 5.2. `api-gateway`

-   **端点更新**: `POST /api/v1/analysis-requests/{symbol}`。
-   **逻辑变更**:
    *   该端点现在需要从请求体中解析出 `template_id`。
    *   它构建的 `GenerateReportCommand` 消息中，必须包含 `template_id` 字段。

### 5.3. `report-generator-service` (核心任务)

`worker.rs` 中的编排逻辑需要进行如下调整和实现：

1.  **消息消费者**: 订阅的 `GenerateReportCommand` 消息现在会包含 `template_id`。

2.  **编排逻辑 (`run_report_generation_workflow`)**:
    *   **获取配置**: 从 `data-persistence-service` 获取完整的 `AnalysisTemplateSets` 对象。
    *   **选择模板**: 根据传入的 `template_id`，从 `AnalysisTemplateSets` 中选择出本次需要执行的 `AnalysisTemplateSet`。如果找不到，则记录错误并终止。
    *   **构建依赖图**: 使用所选模板集中的 `modules` 来构建有向图。强烈推荐使用 `petgraph` crate。
    *   **拓扑排序**: 对该图执行拓扑排序，**必须包含循环检测**。
    *   **顺序执行**: 遍历排序后的模块列表，后续的上下文注入、LLM调用和结果持久化逻辑与之前设计一致，但操作范围仅限于当前模板集内的模块。

3.  **补全缺失逻辑**:
    *   **实现结果持久化**: 调用 `data-persistence-service` 提供的API，将每个模块生成的 `NewAnalysisResult` 存入 `analysis_results` 表。

## 6. 未来工作

### 6.1. 演进至 "Deep Research" 模块

此设计为未来的 "Deep Research" 模块演进奠定了坚实的基础。当该模块准备就绪时，我们可以创建一个新的“分析模板集”，其中的某些模块（如 `news_analysis`）将不再直接调用 LLM，而是调用 Deep Research 服务。Deep Research 服务将执行复杂的数据挖掘，并将高度精炼的结果返回给编排器，再由编排器注入到后续的 LLM 调用中，从而实现“数据驱动”的分析范式。

### 6.2. 引入工具调用框架 (Tool Calling Framework)

为了以一种更通用和可扩展的方式向提示词模板中注入多样化的上下文数据，我们规划引入“工具调用”框架。

-   **概念**: “工具”是指一段独立的、用于获取特定类型数据的程序（例如，获取财务数据、获取实时股价、获取最新新闻等）。
-   **配置**: 在前端的模块配置界面，除了依赖关系外，我们还将为每个模块提供一个“可用工具”的复选框列表。用户可以为模块勾选需要调用的一个或多个工具。
-   **执行**:
    1.  在 `report-generator-service` 的编排器执行一个模块前，它会先检查该模块配置中启用了哪些“工具”。
    2.  编排器将按顺序执行这些工具。
    3.  每个工具的输出（例如，格式化为Markdown的财务数据表格）将被注入到一个统一的上下文字段中。
-   **首个工具**: 我们设想的第一个工具就是 **`财务数据注入工具`**。它将负责获取并格式化财务报表，其实现逻辑与本文档旧版本中描述的“核心逻辑细化”部分一致。

通过此框架，我们可以将数据注入的逻辑与编排器的核心逻辑解耦，使其更易于维护和扩展。**此项为远期规划，不在本轮实施范围之内。**

## 8. 实施清单 (Step-by-Step To-do List)

以下是为完成本项目所需的、按顺序排列的开发任务清单。

### 阶段一：数据模型与持久化层准备

-   [x] **任务 1.1**: 在 `common-contracts` crate 中，创建或更新 `src/config_models.rs`，定义 `AnalysisTemplateSets`, `AnalysisTemplateSet`, `AnalysisModuleConfig` 等新的数据结构。
-   [x] **任务 1.2**: 在 `common-contracts` crate 中，创建或更新 `src/dtos.rs`，定义用于写入分析结果的 `NewAnalysisResult` 数据传输对象 (DTO)。
-   [x] **任务 1.3**: 在 `data-persistence-service` 中，创建新的数据库迁移文件 (`migrations/`)，用于新增 `analysis_results` 表，其 schema 遵循本文档第6.2节的定义。
-   [x] **任务 1.4**: 在 `data-persistence-service` 中，实现 `analysis_results` 表的 CRUD API (至少需要 `create` 方法)。
-   [x] **任务 1.5**: 在 `data-persistence-service` 中，实现数据播种（Seeding）逻辑：在服务启动时，将硬编码的默认分析模板集写入数据库（如果尚不存在）。

### 阶段二：后端核心逻辑实现 (`report-generator-service`)

-   [x] **任务 2.1**: 为 `report-generator-service` 添加 `petgraph` crate 作为依赖，用于构建和处理依赖图。
-   [x] **任务 2.2**: 重构 `worker.rs` 中的 `run_report_generation_workflow` 函数，使其能够接收包含 `template_id` 的消息。
-   [x] **任务 2.3**: 在 `worker.rs` 中，**实现完整的拓扑排序算法**，用以替代当前简陋的循环实现。此算法必须包含循环依赖检测。
-   [x] **任务 2.4**: 更新编排器逻辑，使其能够根据 `template_id` 从获取到的 `AnalysisTemplateSets` 中选择正确的工作流进行处理。
-   [x] **任务 2.5**: 实现调用 `data-persistence-service` 的逻辑，将每个模块成功生成的 `NewAnalysisResult` 持久化到 `analysis_results` 表中。

### 阶段三：服务集成与端到端打通

-   [x] **任务 3.1**: 在 `api-gateway` 中，新增 `POST /api/v1/analysis-requests/{symbol}` 端点。
-   [x] **任务 3.2**: 在 `api-gateway` 的新端点中，实现接收前端请求（包含 `template_id`），并向 NATS 发布 `GenerateReportCommand` 消息的逻辑。
-   [x] **任务 3.3**: 在 `report-generator-service` 中，更新其 NATS 消费者，使其能够正确订阅和解析新的 `GenerateReportCommand` 消息。
-   [x] **任务 3.4**: 进行端到端集成测试，确保从前端触发的请求能够正确地启动 `report-generator-service` 并执行完整的分析流程（此时可不关心前端UI）。

### 阶段四：前端 UI 实现

-   [x] **任务 4.1**: 重构 `frontend/src/app/config/page.tsx` 页面，实现两级管理结构：先管理“分析模板集”。
-   [x] **任务 4.2**: 实现“分析模板集”的创建、重命名和删除功能，并调用对应的后端API。
-   [x] **任务 4.3**: 实现模板集内部的“分析模块”管理界面，包括模块的创建、更新（所有字段）和删除功能。
-   [x] **任务 4.4**: 确保在分析请求发起的页面（例如主查询页面），用户可以选择使用哪个“分析模板集”来执行分析。
-   [x] **任务 4.5**: 更新前端调用 `api-gateway` 的逻辑，在分析请求的 body 中附带上用户选择的 `template_id`。
