实验报告.md•14.2 kB
# 《数据库实践》期末项目实验报告
## 基于大模型与 MCP 服务的自然语言数据库查询系统
---
## 1. 项目概述,包括代码结构说明
### 1.1 项目概述
本项目实现了一个基于Model Context Protocol (MCP) 和大语言模型的自然语言数据库查询系统。系统采用分层架构设计,将自然语言处理、SQL生成、数据库操作和安全控制有机结合,为用户提供直观、安全、高效的数据库查询体验。
### 1.2 技术架构
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 用户界面层 │ │ AI代理层 │ │ 数据访问层 │
│ │ │ │ │ │
│ • Mastra Web UI │────│ • MySQL Agent │────│ • MCP Server │
│ • 自然语言输入 │ │ • Prompt工程 │ │ • MySQL Tools │
│ • 结果展示 │ │ • 多轮对话 │ │ • 安全控制 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
↓
┌─────────────────┐
│ 数据库层 │
│ │
│ • MySQL数据库 │
│ • College数据库 │
│ • 事务管理 │
└─────────────────┘
```
### 1.3 代码结构详细说明
#### 1.3.1 MCP服务层 (`src/mysql-mcp/`)
**主要职责**: 提供数据库操作的标准化接口,实现安全控制和功能增强
- **`index.js`**: MCP服务器主入口
- 初始化MCP服务器实例
- 注册所有工具集
- 配置服务器元信息和启动参数
- **`config/database.js`**: 数据库连接配置
- MySQL连接参数管理
- 环境变量集成
- 连接池配置
- **`tools/mysql-tools.js`**: 核心工具集实现
- `mysqlQueryTool`: SELECT查询执行器,包含安全过滤
- `mysqlSchemaTool`: 表结构信息获取器,支持按表过滤
- `mysqlDdlTool`: DDL语句生成器
- `paginationTool`: 分页缓存查询,会根据查询结果集大小自动分页
- `queryLogTool`: 查询日志记录和查看器
#### 1.3.2 AI代理层 (`src/mastra/`)
**主要职责**: 处理自然语言输入,生成SQL查询,管理对话上下文
- **`index.ts`**: Mastra框架主配置
- 集成AI代理、存储、日志组件
- 配置LibSQL存储用于会话管理
- 设置Pino日志记录器
- **`agents/mysql-agent.ts`**: MySQL智能代理
- 集成通义千问模型
- 配置MCP工具集
- 设置记忆系统支持多轮对话
- **`model/index.ts`**: 模型配置
- 通义千问API配置
- 模型参数设置
- **`prompts/mysql-agent-prompt.ts`**: Prompt工程
- 详细的角色定义和任务说明
- Few-shot示例
- SQL生成规则和优化指导
#### 1.3.3 数据结构设计
**MCP工具标准化接口**:
```javascript
{
name: string, // 工具名称
description: string, // 工具描述
inputSchema: object, // 输入参数schema
handler: async function // 异步处理函数
}
```
**查询日志数据结构**:
```javascript
{
timestamp: string, // 查询时间戳
sql: string, // 执行的SQL语句
executionTime: number, // 执行时间(ms)
rowCount: number, // 返回行数
success: boolean // 执行状态
}
```
### 1.4 核心流程
1. **用户输入**: 通过Mastra Web界面输入自然语言查询
2. **语言理解**: MySQL Agent分析用户意图
3. **Schema获取**: 通过MCP工具获取数据库表结构
4. **SQL生成**: 基于prompt模板和上下文生成SQL
5. **安全验证**: MCP服务器验证SQL安全性
6. **查询执行**: 执行SQL并记录日志
7. **结果返回**: 格式化结果并提供优化建议
---
## 2. MCP 功能增强设计说明
### 2.1 查询日志记录功能
#### 2.1.1 设计目标
- 记录所有SQL执行历史
- 提供查询性能分析数据
- 支持故障排查和审计
#### 2.1.2 实现方案
**日志存储结构**:
```javascript
// logs/query_log.json
{
"logs": [
{
"id": "uuid",
"timestamp": "2024-01-15T10:30:00.000Z",
"sql": "SELECT name FROM courses ORDER BY title, credits",
"executionTime": 45,
"rowCount": 25,
"success": true,
"error": null
}
]
}
```
**核心实现**:
```javascript
export const queryLogTool = {
name: "query_log",
description: "查看和管理查询日志",
inputSchema: {
type: "object",
properties: {
action: { type: "string", enum: ["view", "clear"] },
limit: { type: "number", default: 10 },
},
},
async handler(args) {
// 日志文件操作逻辑
// 支持查看最近查询、清除日志等操作
},
};
```

### 2.2 查询结果分页功能
#### 2.2.1 设计目标
- 优化大结果集的显示性能
- 提供用户友好的浏览体验
- 减少内存占用
#### 2.2.2 实现方案
**分页参数管理**:
```javascript
export const paginationTool = {
name: "paginated_query",
description: "执行分页查询",
inputSchema: {
type: "object",
properties: {
sql: { type: "string" },
page: { type: "number", default: 1 },
pageSize: { type: "number", default: 20 },
},
},
async handler(args) {
const offset = (args.page - 1) * args.pageSize;
const paginatedSql = `${args.sql} LIMIT ${args.pageSize} OFFSET ${offset}`;
// 执行分页查询并返回结果
},
};
```
**分页信息响应**:
```javascript
{
"data": [...], // 当前页数据
"pagination": {
"currentPage": 1,
"pageSize": 20,
"totalRows": 150,
"totalPages": 8,
"hasNext": true,
"hasPrev": false
}
}
```


### 2.3 表结构简化输出功能
#### 2.3.1 设计目标
- 按需获取特定表的结构信息
- 减少不必要的数据传输
- 提供更精准的schema信息
#### 2.3.2 实现方案
**增强的Schema工具**:
```javascript
export const mysqlSchemaTool = {
name: "mysql_schema",
description: "获取数据库表结构信息",
inputSchema: {
type: "object",
properties: {
tableName: {
type: "string",
description: "指定表名,为空则返回所有表",
},
includeIndexes: {
type: "boolean",
default: false,
description: "是否包含索引信息",
},
},
},
async handler(args) {
let query = `
SELECT
TABLE_NAME,
COLUMN_NAME,
DATA_TYPE,
IS_NULLABLE,
COLUMN_KEY,
COLUMN_DEFAULT,
EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ?
`;
if (args.tableName) {
query += ` AND TABLE_NAME = ?`;
}
// 执行查询并格式化结果
},
};
```

---
### 2.4 MCP 字段安全性设计

## 3. 大模型交互流程与 prompt 优化方法
### 3.1 大模型交互流程设计
#### 3.1.1 整体交互架构
```
用户输入 → Prompt构造 → 模型推理 → 工具调用 → 结果处理 → 响应生成
↑ ↓
└──────────── 多轮对话上下文管理 ←─────────────────────────┘
```
#### 3.1.2 详细交互流程
**第一阶段: 输入预处理**
1. 接收用户自然语言输入
2. 检查是否需要上下文信息
3. 构造完整的prompt上下文
**第二阶段: 模型推理**
1. 将处理后的prompt发送给通义千问模型
2. 模型分析用户意图并规划工具调用
3. 生成结构化的响应计划
**第三阶段: 工具执行**
1. 按序执行MCP工具调用
2. 处理工具返回结果
3. 异常处理和重试机制
**第四阶段: 结果综合**
1. 整合所有工具调用结果
2. 生成用户友好的响应
3. 提供SQL优化建议
### 3.2 Prompt 工程优化
#### 3.2.1 分层Prompt设计
**系统角色定义层**:
```
你是一个专业的MySQL数据库查询助手,能够将自然语言查询转换为精确的SQL语句。
```
**任务指令层**:
```
## 你的主要职责:
1. 理解用户的自然语言查询需求
2. 根据数据库schema生成准确的SQL查询语句
3. 执行查询并解释结果
4. 主动提供SQL优化建议
5. 在多轮对话中,自动利用历史上下文理解用户意图
```
**规则约束层**:
```
## SQL生成规则:
- 只生成SELECT查询语句,确保数据安全
- 使用适当的JOIN语句连接相关表
- 严格避免复杂的子查询嵌套,优先使用JOIN
- 生成SQL后,输出一句简要的SQL优化建议
```
#### 3.2.2 Few-shot 示例设计
**示例结构**:
```
用户: "列出所有课程的名称,按标题和学分排序"
助手: 我来为您查询课程信息。首先让我获取数据库结构,然后生成相应的SQL查询。
SQL: SELECT name FROM courses ORDER BY title, credits;
优化建议: 可为ORDER BY字段建立索引提升排序效率。
```
**多轮对话示例**:
```
用户: "查找选修了张三同学的所有课程名称"
助手: 正在为您查询张三同学的选课信息。
SQL: SELECT c.name FROM students s JOIN enrollments e ON s.id = e.student_id JOIN courses c ON e.course_id = c.id WHERE s.name = '张三';
用户: "再帮我查下这些课程的学分"
助手: 根据您的上文需求,补充查询课程学分。
SQL: SELECT c.name, c.credits FROM students s JOIN enrollments e ON s.id = e.student_id JOIN courses c ON e.course_id = c.id WHERE s.name = '张三';
```
#### 3.2.3 动态Prompt优化策略
**上下文感知优化**:
- 自动保留最近10轮对话历史
- 智能识别代词和省略的表名/字段名
- 基于历史查询优化后续SQL生成
**性能导向优化**:
- 内置SQL性能最佳实践
- 自动建议索引优化
- 避免N+1查询和复杂子查询
**安全优先优化**:
- 强制只读查询约束
- 敏感字段自动过滤
- SQL注入模式识别



### 3.3 记忆系统与多轮对话
#### 3.3.1 记忆系统架构
```javascript
memory: new Memory({
storage: new LibSQLStore({
url: "file:../mastra.db",
}),
options: {
lastMessages: 10, // 保留最近10条消息
threads: {
generateTitle: true, // 自动生成对话标题
},
},
});
```

#### 3.3.2 对话状态管理
**会话信息结构**:
```javascript
{
threadId: 'uuid',
messages: [
{
role: 'user',
content: '用户输入',
timestamp: '2024-01-15T10:30:00.000Z'
},
{
role: 'assistant',
content: '助手回复',
tools_called: ['mysql_schema', 'mysql_query'],
timestamp: '2024-01-15T10:30:05.000Z'
}
],
context: {
currentDatabase: 'college',
recentTables: ['courses', 'students', 'enrollments'],
userPreferences: {
preferredPageSize: 20
}
}
}
```
### 3.4 优化效果评估
#### 3.4.1 准确性提升策略
1. **Schema感知**: 在生成SQL前强制获取表结构
2. **约束驱动**: 通过严格的规则约束减少错误
3. **示例学习**: Few-shot示例提供标准化模板
4. **多轮纠错**: 支持在对话中修正和完善查询
#### 3.4.2 性能优化指导
1. **JOIN优化**: 优先使用JOIN替代子查询
2. **索引建议**: 自动分析查询并建议索引
3. **分页控制**: 大结果集自动分页处理
4. **查询复杂度控制**: 避免过度复杂的嵌套查询
---

## 4. 测试案例(不少于 5 条)与结果展示
### 4.1 List the names of all courses ordered by their titles and credits.


### 4.2 What is the title, credit value, and department name for courses with more than one prerequisite? (medium)



### 4.3 What are the names of students who have more than one advisor? (medium)



### 4.4 What are the titles of courses without prerequisites? (hard)



### 4.5 人数最多的前5门课程是?并显示课程名称、学分、以及授课教师姓名



## 5. 困难与解决方案
- 回答质量
- 实验初期模型回答时,直接输出简单的md文档,可读性差,有时候会忘记调用工具,或者调用工具后突然显示对话结束。
- **解决方案** 通过优化prompt,给出回答问题的流程模板和对话样例,并对输出格式、生成的SQL的安全要求、回复语言加以限制。
- 模型选择
- 不同模型对MCP的运用能力不同,例如qwen-max会更倾向于自我猜测表结构,当遇到错误时容易逃避纠正错误和避免工具调用,而qwq等响应较慢,对工具调用支持不足。
- **解决方案** 经过反复比较,qwen-plus模型响应速度均衡,一般情况下能够连贯多次调用并自主纠正。
- 分页功能
- 大模型在查询得到结果前,是无法感知数据结果大小的,因此不能提前判断是否需要启用分页,会返回大量数据,造成响应缓慢,token消耗过快
- **解决方案** 设计自动分页和默认页大小,并启用缓存会话ID,在返回结果前过滤数据,辅助大模型在不同会话中呈现分页效果,并提供了多种分页查询函数
满足翻页,跳页功能。