feat(dc/tool-c): 完成AI代码生成服务(Day 3 MVP)
核心功能: - 新增AICodeService(550行):AI代码生成核心服务 - 新增AIController(257行):4个API端点 - 新增dc_tool_c_ai_history表:存储对话历史 - 实现自我修正机制:最多3次智能重试 - 集成LLMFactory:复用通用能力层 - 10个Few-shot示例:覆盖Level 1-4场景 技术优化: - 修复NaN序列化问题(Python端转None) - 修复数据传递问题(从Session获取真实数据) - 优化System Prompt(明确环境信息) - 调整Few-shot示例(移除import语句) 测试结果: - 通过率:9/11(81.8%) 达到MVP标准 - 成功场景:缺失值处理、编码、分箱、BMI、筛选、填补、统计、分类 - 待优化:数值清洗、智能去重(已记录技术债务TD-C-006) API端点: - POST /api/v1/dc/tool-c/ai/generate(生成代码) - POST /api/v1/dc/tool-c/ai/execute(执行代码) - POST /api/v1/dc/tool-c/ai/process(生成并执行,一步到位) - GET /api/v1/dc/tool-c/ai/history/:sessionId(对话历史) 文档更新: - 新增Day 3开发完成总结(770行) - 新增复杂场景优化技术债务(TD-C-006) - 更新工具C当前状态文档 - 更新技术债务清单 影响范围: - backend/src/modules/dc/tool-c/*(新增2个文件,更新1个文件) - backend/scripts/create-tool-c-ai-history-table.mjs(新增) - backend/prisma/schema.prisma(新增DcToolCAiHistory模型) - extraction_service/services/dc_executor.py(NaN序列化修复) - docs/03-业务模块/DC-数据清洗整理/*(5份文档更新) Breaking Changes: 无 总代码行数:+950行 Refs: #Tool-C-Day3
This commit is contained in:
256
backend/src/modules/dc/tool-c/controllers/AIController.ts
Normal file
256
backend/src/modules/dc/tool-c/controllers/AIController.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* AI代码生成控制器
|
||||
*
|
||||
* API端点:
|
||||
* - POST /ai/generate 生成代码(不执行)
|
||||
* - POST /ai/execute 执行代码
|
||||
* - POST /ai/process 生成并执行(带重试)
|
||||
* - GET /ai/history/:sessionId 获取对话历史
|
||||
*
|
||||
* @module AIController
|
||||
*/
|
||||
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { logger } from '../../../../common/logging/index.js';
|
||||
import { aiCodeService } from '../services/AICodeService.js';
|
||||
|
||||
// ==================== 请求参数类型定义 ====================
|
||||
|
||||
interface GenerateCodeBody {
|
||||
sessionId: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ExecuteCodeBody {
|
||||
sessionId: string;
|
||||
code: string;
|
||||
messageId: string;
|
||||
}
|
||||
|
||||
interface ProcessBody {
|
||||
sessionId: string;
|
||||
message: string;
|
||||
maxRetries?: number;
|
||||
}
|
||||
|
||||
interface HistoryParams {
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
// ==================== 控制器 ====================
|
||||
|
||||
export class AIController {
|
||||
|
||||
/**
|
||||
* POST /api/v1/dc/tool-c/ai/generate
|
||||
* 生成代码(不执行)
|
||||
*/
|
||||
async generateCode(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const { sessionId, message } = request.body as GenerateCodeBody;
|
||||
|
||||
logger.info(`[AIController] 收到生成代码请求: sessionId=${sessionId}`);
|
||||
|
||||
// 参数验证
|
||||
if (!sessionId || !message) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '缺少必要参数:sessionId 或 message'
|
||||
});
|
||||
}
|
||||
|
||||
if (message.trim().length === 0) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '消息内容不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成代码
|
||||
const result = await aiCodeService.generateCode(sessionId, message);
|
||||
|
||||
logger.info(`[AIController] 代码生成成功: messageId=${result.messageId}`);
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
message: 'AI代码生成成功',
|
||||
data: {
|
||||
code: result.code,
|
||||
explanation: result.explanation,
|
||||
messageId: result.messageId
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`[AIController] generateCode失败: ${error.message}`);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
error: error.message || 'AI代码生成失败,请重试'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/dc/tool-c/ai/execute
|
||||
* 执行代码
|
||||
*/
|
||||
async executeCode(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const { sessionId, code, messageId } = request.body as ExecuteCodeBody;
|
||||
|
||||
logger.info(`[AIController] 收到执行代码请求: messageId=${messageId}`);
|
||||
|
||||
// 参数验证
|
||||
if (!sessionId || !code || !messageId) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '缺少必要参数:sessionId、code 或 messageId'
|
||||
});
|
||||
}
|
||||
|
||||
// 执行代码
|
||||
const result = await aiCodeService.executeCode(sessionId, code, messageId);
|
||||
|
||||
if (result.success) {
|
||||
logger.info(`[AIController] 代码执行成功: messageId=${messageId}`);
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
message: '代码执行成功',
|
||||
data: {
|
||||
result: result.result,
|
||||
newDataPreview: result.newDataPreview
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logger.warn(`[AIController] 代码执行失败: ${result.error}`);
|
||||
return reply.code(200).send({
|
||||
success: false,
|
||||
error: result.error || '代码执行失败',
|
||||
data: null
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error(`[AIController] executeCode失败: ${error.message}`);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
error: error.message || '代码执行失败,请重试'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/dc/tool-c/ai/process
|
||||
* 生成并执行(一步到位,带重试)
|
||||
*/
|
||||
async process(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const { sessionId, message, maxRetries = 3 } = request.body as ProcessBody;
|
||||
|
||||
logger.info(`[AIController] 收到处理请求: sessionId=${sessionId}, maxRetries=${maxRetries}`);
|
||||
|
||||
// 参数验证
|
||||
if (!sessionId || !message) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '缺少必要参数:sessionId 或 message'
|
||||
});
|
||||
}
|
||||
|
||||
if (message.trim().length === 0) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '消息内容不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
if (maxRetries < 1 || maxRetries > 5) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '重试次数必须在1-5之间'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成并执行(带重试)
|
||||
const result = await aiCodeService.generateAndExecute(
|
||||
sessionId,
|
||||
message,
|
||||
maxRetries
|
||||
);
|
||||
|
||||
logger.info(`[AIController] 处理成功: 重试${result.retryCount}次后成功`);
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
message: `代码执行成功${result.retryCount > 0 ? `(重试${result.retryCount}次)` : ''}`,
|
||||
data: {
|
||||
code: result.code,
|
||||
explanation: result.explanation,
|
||||
executeResult: result.executeResult,
|
||||
retryCount: result.retryCount,
|
||||
messageId: result.messageId
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`[AIController] process失败: ${error.message}`);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
error: error.message || '处理失败,请重试'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/dc/tool-c/ai/history/:sessionId
|
||||
* 获取对话历史
|
||||
*/
|
||||
async getHistory(
|
||||
request: FastifyRequest<{ Params: HistoryParams; Querystring: { limit?: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { sessionId } = request.params;
|
||||
const limit = request.query.limit ? parseInt(request.query.limit) : 10;
|
||||
|
||||
logger.info(`[AIController] 获取对话历史: sessionId=${sessionId}, limit=${limit}`);
|
||||
|
||||
// 参数验证
|
||||
if (!sessionId) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '缺少必要参数:sessionId'
|
||||
});
|
||||
}
|
||||
|
||||
if (limit < 1 || limit > 50) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: '历史记录数量必须在1-50之间'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取历史
|
||||
const history = await aiCodeService.getHistory(sessionId, limit);
|
||||
|
||||
logger.info(`[AIController] 获取历史成功: ${history.length}条`);
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: {
|
||||
sessionId,
|
||||
history,
|
||||
count: history.length
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`[AIController] getHistory失败: ${error.message}`);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
error: error.message || '获取对话历史失败'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 导出单例实例 ====================
|
||||
|
||||
export const aiController = new AIController();
|
||||
|
||||
Reference in New Issue
Block a user