# 知识库子系统设计文档

> 本文档为「知识库功能」的完整架构与实施指南。所有代码改动以此为准。

## 一、需求背景与边界

### 用户原始需求
> 增加知识库功能，包括**文档数据管理**、**结构化数据管理**、**知识图谱管理**。
> - 文档数据：上传后分类管理 → 直接向量化入库
> - 结构化数据：参考 beekeeper-studio 的数据源管理（含 SQL Console）
> - 知识图谱：图谱数据源管理 + 图谱查看

### 已锁定的核心架构决策（用户确认）

| 子系统 | 选型 | 部署模式 |
|--------|------|----------|
| 文档向量化 | **Milvus** + Spring AI starter | 外部独立服务（默认 `localhost:19530`） |
| 知识图谱 | **Neo4j Community** | 外部独立服务（默认 `bolt://localhost:7687`） |
| 结构化数据 | **完整 SQL Console**（Monaco Editor） | JDBC 多驱动 + HikariCP 动态连接池 |
| 与工作流集成 | **本期独立**，RAG / 图谱查询节点留待下期 | - |

### 当前实例状态（开发期）
- ❌ Milvus 实例未部署
- ❌ Neo4j 实例未部署
- ❌ 智谱 embedding-2 模型未开通

→ **降级策略见第六章**。所有外部依赖必须支持「未就绪时优雅降级」，应用能正常启动、UI 可见、提交时给出明确提示。

---

## 二、技术栈现状

### 已具备
- Spring Boot 3.3.6 + Java 17
- Spring AI 1.0.0 BOM（OpenAI 兼容，目前用智谱 GLM-5.2 chat）
- Spring Data JPA + H2 文件库（`./data/agent-runs`）
- JSON 文件存储（`AbstractJsonRepository`）
- ChatClient 缓存机制（`AiModelServiceImpl.clientCache`）
- 前端：Vue 3 + Vue Flow + naive-ui + monaco-editor

### 待新增依赖

#### `backend/pom.xml`

| 依赖 | 版本 | 用途 |
|------|------|------|
| `org.springframework.ai:spring-ai-starter-vector-store-milvus` | 1.0.0（已有 BOM） | Milvus 集成 + EmbeddingModel 自动配置 |
| `org.neo4j.driver:neo4j-java-driver` | 5.27.x | Neo4j Cypher 客户端 |
| `org.apache.tika:tika-parsers-standard-package` | 2.9.2 | PDF/Word/Excel/PPT/HTML 解析 |
| `org.springframework.boot:spring-boot-starter-jdbc` | 已随 boot | 动态数据源（HikariCP） |
| `mysql:mysql-connector-j` | 8.4.x | MySQL 驱动 |
| `org.postgresql:postgresql` | 42.7.x | PG 驱动 |
| `com.oracle.database.jdbc:ojdbc11` | 23.5.x | Oracle 驱动 |
| `com.microsoft.sqlserver:mssql-jdbc` | 12.8.x | SQL Server 驱动 |
| `org.xerial:sqlite-jdbc` | 3.46.x | SQLite 驱动 |
| `com.github.ulisesbocchio:jasypt-spring-boot-starter` | 3.0.5 | 密码加密（数据源凭据） |

#### `frontend/package.json`

| 依赖 | 用途 |
|------|------|
| `cytoscape` | 图谱可视化核心 |
| `cytoscape-cose-bilkent` | 自动布局算法 |
| `sql-formatter` | SQL 美化（4.x ncompact 版本） |

### AiModel 表扩展
新增 `modelType` 字段（`chat` / `embedding`），现有 chat 模型默认 `chat`。`EmbeddingService` 复用 `AiModelServiceImpl` 的 clientCache 模式构建独立的 `EmbeddingModel` 缓存。

---

## 三、`application.yml` 新增配置

```yaml
app:
  kb:
    enabled: true                    # 总开关：false 时全部 kb 接口返回 503
    upload-dir: ./uploads/kb
    max-file-size: 52428800          # 50MB
    chunk-size: 1000                 # 字符数
    chunk-overlap: 200
    allowed-mime-types:
      - application/pdf
      - application/vnd.openxmlformats-officedocument.wordprocessingml.document
      - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
      - application/vnd.openxmlformats-officedocument.presentationml.presentation
      - text/plain
      - text/markdown
      - text/html
  milvus:
    enabled: true                    # false 时跳过 bean 创建
    host: localhost
    port: 19530
    collection: kb_documents
    vector-dimension: 1024           # 智谱 embedding-2 输出维度
  neo4j:
    enabled: true
    # 默认连接（用户也可在 UI 配置多个）
    default-uri: bolt://localhost:7687
    default-user: neo4j
    default-password: ${NEO4J_PASSWORD:}

spring:
  ai:
    openai:
      embedding:
        options:
          model: embedding-2         # 智谱 embedding

jasypt:
  encryptor:
    password: ${JASYPT_KEY:dev-only-key-replace-me-in-prod}
```

---

## 四、模块清单（共 ~50 个文件）

### Phase 1：文档数据管理（13 个文件）

#### 后端
- `model/entity/KbCategory.java` — 树形分类（id, name, parentId, sortOrder）
- `model/entity/KbDocument.java` — 文档元数据（id, name, categoryId, sourcePath, fileSize, mimeType, status, chunkCount, vectorCount, errorMessage, createdAt, updatedAt）
- `model/entity/KbChunk.java` — 文档分块（id, documentId, chunkIndex, content, vectorId, charCount）
- `repository/KbCategoryRepository.java`、`KbDocumentRepository.java`、`KbChunkRepository.java`
- `service/KbCategoryService.java` + `impl/KbCategoryServiceImpl.java` — 分类 CRUD
- `service/DocumentService.java` + `impl/DocumentServiceImpl.java` — 上传/解析/分块/向量化
- `service/EmbeddingService.java` + `impl/EmbeddingServiceImpl.java` — 调用 embedding-2（懒加载，复用 AiModelServiceImpl 模式）
- `service/VectorStoreService.java` + `impl/VectorStoreServiceImpl.java` — Milvus 操作（懒加载 + 降级）
- `controller/KbCategoryController.java` — 分类 CRUD
- `controller/KbDocumentController.java` — 上传/列表/分类切换/删除/重新向量化
- `config/MilvusConfig.java` — `@ConditionalOnProperty("app.milvus.enabled")`

#### 前端
- `views/kb/DocumentManagement.vue` — 主页：左分类树 + 右文档列表 + 状态徽章
- `components/kb/DocumentUploader.vue` — 多文件上传 + 进度 + 分类选择
- `components/kb/CategoryTree.vue`（可复用现有 `components/skill/CategoryTree.vue`，或单独建）
- `api/kb.js` — API 客户端

#### 核心流程
```
上传文件 → 校验 MIME → 落盘 uploads/kb/ → INSERT KbDocument(status=PENDING)
                                          ↓
                                  异步处理：
                                  1. Tika 解析为纯文本
                                  2. 递归字符分块 (1000 + overlap 200)
                                  3. INSERT KbChunk 批量
                                  4. 批量 embedding (status=EMBEDDING)
                                  5. Milvus upsert (status=VECTORIZING)
                                  6. status=READY / FAILED
```

#### 状态机
`PENDING` → `PARSING` → `CHUNKING` → `EMBEDDING` → `VECTORIZING` → `READY`
任一步失败 → `FAILED`（记录 errorMessage）

---

### Phase 2：结构化数据源管理（18 个文件，工作量最大）

#### 后端
- `model/entity/DataSource.java` — id, name, type(MYSQL/PG/ORACLE/SQLSERVER/SQLITE), jdbcUrl, username, passwordEnc, createdAt
- `repository/DataSourceRepository.java`
- `service/DataSourceService.java` + Impl — CRUD + 测试连接
- `service/DynamicJdbcService.java` — 按 datasourceId 维护 HikariCP 连接池缓存（LRU + 最大 10 池）
- `service/SchemaExplorerService.java` + Impl — listSchemas / listTables / describeTable / countRows / pageQuery
- `service/SqlGuardService.java` — SQL 白名单校验
- `controller/DataSourceController.java` — CRUD + 测试连接
- `controller/DataSourceQueryController.java` — executeSql / listTables / describeTable / pageQuery

#### 前端
- `views/kb/DataSourceManagement.vue` — 主页：左数据源列表 + 右侧 Tab
- `components/kb/DataSourceForm.vue` — 新增/编辑表单 + 测试连接
- `components/kb/TableExplorer.vue` — 表/字段树
- `components/kb/SqlConsole.vue` — Monaco 编辑器 + 执行按钮 + 历史记录
- `components/kb/QueryResult.vue` — 虚拟滚动结果表
- `components/kb/QueryHistory.vue` — 查询历史持久化
- `api/datasource.js`

#### SQL 安全
```java
// SqlGuardService 白名单
ALLOWED_PREFIXES = ["SELECT", "WITH", "EXPLAIN"]
FORBIDDEN_KEYWORDS = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
                      "CREATE", "TRUNCATE", "GRANT", "REVOKE", "MERGE"]

// 限制
- 单次查询硬超时 30s
- 返回行数硬限 10000
- 禁止系统库（如 information_schema、mysql、sys）
```

---

### Phase 3：知识图谱管理（11 个文件）

#### 后端
- `model/entity/GraphSource.java` — id, name, uri, username, passwordEnc, createdAt
- `repository/GraphSourceRepository.java`
- `service/GraphSourceService.java` + Impl — CRUD + 测试连接
- `service/Neo4jExecutorService.java` + Impl — 动态 driver 缓存（按 graphId）
- `controller/GraphSourceController.java` — CRUD + 图谱数据查询（返回 cytoscape elements 格式）

#### 前端
- `views/kb/GraphSourceManagement.vue` — 左图谱列表 + 右图谱可视化
- `components/kb/GraphSourceForm.vue` — 连接配置表单
- `components/kb/GraphViewer.vue` — cytoscape + cose-bilkent 布局 + 节点点击详情
- `components/kb/NodeDetailPanel.vue` — 节点属性展示
- `api/graphsource.js`

#### Cypher 默认查询
```cypher
MATCH (n)-[r]->(m)
RETURN n, r, m
LIMIT 500
```
后端转换为 cytoscape elements：
```js
{
  nodes: [{ data: { id, label, ...properties } }],
  edges: [{ data: { id, source, target, label, ...properties } }]
}
```

---

## 五、路由 + 菜单

### `router/index.js`
```js
{ path: '/kb/documents', name: 'KbDocumentManagement',
  component: () => import('../views/kb/DocumentManagement.vue') },
{ path: '/kb/datasources', name: 'KbDataSourceManagement',
  component: () => import('../views/kb/DataSourceManagement.vue') },
{ path: '/kb/graphs', name: 'KbGraphSourceManagement',
  component: () => import('../views/kb/GraphSourceManagement.vue') },
```

### `components/layout/AppSidebar.vue`
新增「知识库」分组，3 个子菜单：
- 文档数据（`/kb/documents`，`DocumentTextOutline`）
- 结构化数据（`/kb/datasources`，`ServerOutline`）
- 知识图谱（`/kb/graphs`，`GitNetworkOutline`）

---

## 六、外部依赖未就绪时的降级策略 ⚠️ 开发期必须实现

### 设计原则
- **应用必须能启动**，无论 Milvus/Neo4j 是否在线
- **代码必须完整**，等实例开通后零改动可用
- **UI 必须可见**，提交时给出明确错误提示

### 实现方式

#### Milvus 降级
```java
@Configuration
@ConditionalOnProperty(name = "app.milvus.enabled", havingValue = "true", matchIfMissing = false)
public class MilvusConfig {
    @Bean
    public VectorStore vectorStore(...) { ... }
}

// VectorStoreServiceImpl 中所有方法都先检查 bean 是否存在
@Override
public List<String> upsert(List<float[]> vectors, List<Map<String,Object>> metadatas) {
    if (!vectorStoreEnabled) {
        throw new ServiceException("Milvus 未启用或未连接，请在 application.yml 配置 app.milvus.enabled=true");
    }
    ...
}
```

#### Neo4j 降级
```java
// Neo4jExecutorService 用懒加载 driver，不预连接
// 调用时 try-catch ServiceUnavailable，转换为业务异常
```

#### Embedding 降级
```java
// EmbeddingServiceImpl 调用智谱 embedding-2 失败时
// 1. 不抛出致命错误
// 2. 文档状态置 FAILED + errorMessage="embedding 调用失败：xxx"
// 3. 用户在 UI 看到失败状态，可点击"重新向量化"重试
```

#### 全局开关
`app.kb.enabled=false` 时所有 kb Controller 返回 503，便于运维应急降级。

### 验证方式
开发期：Milvus/Neo4j/embedding 全部不可用时：
- ✅ 应用正常启动
- ✅ 文档可上传、可分类、可查看
- ✅ 文档状态显示"向量化失败：xxx"
- ✅ 数据源可保存、可测试连接（JDBC 直连）
- ✅ 图谱连接可保存（不预连）
- ❌ 向量化、图谱查询、SQL 执行在执行时给出明确提示

---

## 七、安全设计

### 密码加密
- DataSource / GraphSource 的 `password` 字段使用 Jasypt 加密入库
- 实体 `@Transient passwordPlain` 仅在内存中临时持有
- Controller 返回时**永远不返回** password 字段

### SQL 注入防护
1. 强制使用 HikariCP prepared statement
2. SqlGuardService 白名单（仅 SELECT/WITH/EXPLAIN）
3. 单次查询超时 30s、返回行数硬限 10000
4. 禁止系统库访问

### 文件上传安全
- 白名单 MIME 类型
- 单文件大小限制 50MB
- 文件名 sanitize（去除 `../` 等）
- 存储目录单独隔离（`uploads/kb/`，不允许外部直接访问）

---

## 八、分阶段实施计划

| Phase | 内容 | 文件数 | 验证条件 | 当前状态 |
|-------|------|--------|----------|----------|
| **Phase 1** | 基础设施 + 文档数据管理 | 13 | 文档上传 + 分类 + 向量化入库 | ⏳ 进行中 |
| **Phase 2** | 结构化数据源管理 | 18 | 5 类 DB 连接 + SQL Console | ⬜ 待启动 |
| **Phase 3** | 知识图谱管理 | 11 | Neo4j 连接 + cytoscape 可视化 | ⬜ 待启动 |

每个 Phase 都能独立运行、独立验证。**完成一个 Phase 再启动下一个。**

---

## 九、运维 checklist（实例开通后）

### Milvus
```bash
# Docker 快速启动
docker run -d --name milvus-standalone \
  -p 19530:19530 -p 9091:9091 \
  -v ./milvus/data:/var/lib/milvus \
  milvusdb/milvus:latest standalone

# 健康检查
curl http://localhost:9091/healthz
```

### Neo4j
```bash
# Docker 快速启动
docker run -d --name neo4j \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/change-me \
  -v ./neo4j/data:/data \
  neo4j:5-community

# Web UI: http://localhost:7474
```

### 智谱 embedding-2 开通
登录智谱开放平台 → 模型广场 → embedding-2 → 申请开通 → API Key 即复用现有 GLM 的 key

### 验证步骤
1. 启动后端，看启动日志无 Milvus/Neo4j 连接失败
2. 上传测试 PDF，等待状态变 READY
3. 创建数据源 → 测试连接 → 表浏览 → SQL 查询
4. 创建图谱源 → 查看图谱 → 点击节点

---

## 十、后续扩展（下期）

### RAG 节点（接入工作流）
- 新增工作流节点类型 `kbRetrieval`
- 配置：选择知识库分类 + topK + similarity threshold
- 执行：从 Milvus 检索相关 chunks → 拼装上下文 → 喂给下游 LLM

### 图谱查询节点
- 新增工作流节点类型 `graphQuery`
- 配置：选择图谱源 + Cypher 模板
- 执行：返回查询结果作为 LLM 上下文

### 自动知识抽取
- 从已上传文档抽取实体/关系 → 自动入 Neo4j
- 从结构化数据源外键关系 → 自动生成图谱

---

## 十一、风险与权衡

| 风险 | 应对 |
|------|------|
| Milvus Spring AI starter 版本兼容 | 优先 1.0.0 已有 BOM 锁定版本，开发时验证 |
| Oracle/SQL Server 驱动体积大 | 仅按需引入，发布版可裁剪 |
| H2 与多数据源共存 | DynamicJdbcService 完全独立，使用单独的 DataSource 实例，不与主 EntityManagerFactory 冲突 |
| Tika 依赖膨胀（~30MB） | 文档管理是核心需求，必须接受 |
| 智谱 embedding-2 维度 1024 | Milvus collection 初始化时硬编码，后期不可变 |

---

**文档版本**：v1.0
**创建日期**：2026-06-15
**最后更新**：2026-06-15
**负责人**：开发协作（Claude + 用户）
