- 新增WechatService(企业微信推送服务,支持文本/卡片/Markdown消息) - 新增WechatCallbackController(异步回复模式,5秒内响应) - 完善iit_quality_check Worker(调用WechatService推送通知) - 新增企业微信回调路由(GET验证+POST接收消息) - 实现LLM意图识别(query_weekly_summary/query_patient_info等) - 安装依赖:@wecom/crypto, xml2js - 更新开发记录文档和MVP开发计划 技术要点: - 使用异步回复模式规避企业微信5秒超时限制 - 使用@wecom/crypto官方库处理XML加解密 - 使用setImmediate实现后台异步处理 - 支持主动推送消息返回LLM处理结果 - 完善审计日志记录(WECHAT_NOTIFICATION_SENT/WECHAT_INTERACTION) 相关文档: - docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md - docs/03-业务模块/IIT Manager Agent/04-开发计划/最小MVP闭环开发计划.md - docs/03-业务模块/IIT Manager Agent/00-模块当前状态与开发指南.md
26 KiB
26 KiB
工具C Day 3 开发计划 - AI代码生成服务
文档版本: V1.0
创建日期: 2025-12-06
开发目标: AI代码生成 + Python执行 + 自我修正
预计工时: 5.5-6小时
依赖: Day 2已完成(Session管理)
📋 核心决策总结
决策1: 对话存储方式 ✅
方案选择: 创建独立表 dc_tool_c_ai_history
理由:
- 未来模块可能独立销售或独立部署
- 符合Schema隔离原则
- Tool C有特殊字段需求(code、executeResult)
数据库Schema:
model DcToolCAiHistory {
id String @id @default(uuid())
sessionId String // 关联Tool C Session
userId String
role String // user/assistant/system
content String @db.Text
// Tool C特有字段
generatedCode String? @db.Text // AI生成的代码
codeExplanation String? @db.Text // 代码解释
executeStatus String? // success/failed/pending
executeResult Json? // 执行结果
executeError String? @db.Text // 错误信息
retryCount Int @default(0)
model String? // deepseek-v3
createdAt DateTime @default(now())
@@index([sessionId])
@@index([userId])
@@map("dc_tool_c_ai_history")
@@schema("dc_schema")
}
决策2: AI代码执行流程 ✅
方案选择: 用户确认后执行(方案A)
流程:
用户输入自然语言
↓
AI生成代码 + 解释
↓
前端展示代码(高亮)
↓
用户点击"执行"按钮 ← 用户确认
↓
Python服务执行代码
↓
返回结果 + 数据预览(前50行)
理由:
- ✅ 用户可审查代码(安全可控)
- ✅ 符合"AI辅助"而非"AI自动"的定位
- ✅ 降低执行错误风险
决策3: System Prompt设计 ✅
方案选择: 完整版10个Few-shot示例
示例分布:
| 级别 | 数量 | 示例编号 | 场景 |
|---|---|---|---|
| Level 1 | 2个 | 1-2 | 缺失值统一、数值清洗 |
| Level 2 | 2个 | 3-4 | 编码、分箱 |
| Level 3 | 3个 | 5-7 | BMI、日期、筛选 |
| Level 4 | 3个 | 8-10 | 简单填补、多重插补、去重 |
核心亮点:
- ✅ 包含缺失值处理(示例8)
- ✅ 包含多重插补MICE(示例9)⭐ 重点
- ✅ 覆盖从基础到高级全梯度
文档位置: 工具C_AI_Few-shot示例库.md
决策4: 数据状态管理 ✅
方案选择: Python内存维护(方案C)
架构:
Session创建 → 数据加载到Python内存
↓
AI操作1 → 修改内存中的DataFrame
↓
AI操作2 → 继续修改DataFrame(累积)
↓
AI操作N → ...
↓
用户点击"导出" → 保存到OSS
技术债务:
- 📝 Python重启会丢失状态
- 📝 未来优化:持久化到Redis或OSS
- 📝 文档位置: 技术债务清单
决策5: AI自我修正机制 ✅
方案选择: 最多3次重试(方案B)
流程:
attempt = 0
while attempt < 3:
# 生成代码
code = generate_code(user_message + error_feedback)
# 执行代码
result = execute_code(code)
if result.success:
return result # ✅ 成功
error_feedback = result.error
attempt += 1
# ❌ 3次仍失败,返回友好错误
return "执行失败,请调整需求后重试"
决策6: LLM模型选择 ✅
优先使用: DeepSeek-V3
配置:
const llm = LLMFactory.createAdapter('deepseek-v3');
const response = await llm.chat(messages, {
temperature: 0.1, // 低温度,确保代码准确性
maxTokens: 2000, // 足够生成代码+解释
topP: 0.9
});
备选方案:
- Qwen3-72B: 中文理解更好
- GPT-5-Pro: 代码质量最高(成本高)
决策7: 上下文传递 ✅
配置: 传递最近5轮对话
实现:
async getConversationHistory(sessionId: string, limit: number = 5) {
return await prisma.dcToolCAiHistory.findMany({
where: { sessionId },
orderBy: { createdAt: 'desc' },
take: limit * 2, // user + assistant 成对
});
}
// 构建消息上下文
const messages = [
{ role: 'system', content: systemPrompt },
...history.reverse(), // 最近5轮
{ role: 'user', content: userMessage }
];
决策8: 执行结果展示 ✅
配置: 返回前50行预览
原因:
- 50行足够查看数据变化
- 不会过大影响性能
- 符合医疗数据场景(通常几十到几百行)
决策9: Few-shot示例确认 ✅
最终10个示例:
- 统一缺失值标记
- 数值列清洗(检验值符号处理)
- 分类变量编码(性别→1/0)
- 连续变量分箱(年龄分组)
- BMI计算与分类
- 日期计算(住院天数)
- 条件筛选(入组标准)
- 简单缺失值填补(中位数)
- 多重插补MICE ⭐ 核心
- 智能去重(按日期保留最新)
🏗️ 技术架构设计
整体架构
┌─────────────────────────────────────────────────┐
│ Frontend (React) │
│ - 对话界面(Tool C专用) │
│ - 代码展示(高亮) │
│ - 执行按钮 │
│ - 结果预览(AG Grid) │
└──────────────┬──────────────────────────────────┘
│ REST API
┌──────────────▼──────────────────────────────────┐
│ Node.js Backend (Fastify) │
│ ┌─────────────────────────────────────────┐ │
│ │ AICodeService │ │
│ │ - generateCode() │ │
│ │ - executeCode() │ │
│ │ - generateAndExecute() (带重试) │ │
│ │ - getHistory() │ │
│ └──────────┬───────────────┬──────────────┘ │
│ │ │ │
│ ┌───────▼─────┐ ┌────▼──────────┐ │
│ │ LLMFactory │ │ PythonExecutor│ │
│ │ (通用层复用)│ │ Service │ │
│ └─────────────┘ └───────┬───────┘ │
└──────────────────────────────┼─────────────────┘
│ HTTP
┌───────────▼──────────────────┐
│ Python Service (FastAPI) │
│ - /api/dc/validate (AST检查) │
│ - /api/dc/execute (执行代码) │
│ - Session状态管理(内存) │
└──────────────────────────────┘
核心服务设计
AICodeService (新建,~400行)
// backend/src/modules/dc/tool-c/services/AICodeService.ts
export class AICodeService {
// ==================== 核心方法 ====================
/**
* 生成Pandas代码
* @param sessionId - Tool C Session ID
* @param userMessage - 用户自然语言需求
* @returns { code, explanation, messageId }
*/
async generateCode(
sessionId: string,
userMessage: string
): Promise<GenerateCodeResult> {
// 1. 获取Session信息(数据集元数据)
const session = await sessionService.getSession(sessionId);
// 2. 构建System Prompt(含10个Few-shot)
const systemPrompt = this.buildSystemPrompt(session);
// 3. 获取对话历史(最近5轮)
const history = await this.getHistory(sessionId, 5);
// 4. 调用LLM(复用LLMFactory)
const llm = LLMFactory.createAdapter('deepseek-v3');
const response = await llm.chat([
{ role: 'system', content: systemPrompt },
...history,
{ role: 'user', content: userMessage }
], {
temperature: 0.1,
maxTokens: 2000
});
// 5. 解析AI回复(提取code和explanation)
const parsed = this.parseAIResponse(response.content);
// 6. 保存到数据库
const messageId = await this.saveMessages(
sessionId,
userMessage,
parsed.code,
parsed.explanation
);
return {
code: parsed.code,
explanation: parsed.explanation,
messageId
};
}
/**
* 执行Python代码
* @param sessionId - Tool C Session ID
* @param code - Python代码
* @param messageId - 关联的消息ID
* @returns { success, result, newDataPreview }
*/
async executeCode(
sessionId: string,
code: string,
messageId: string
): Promise<ExecuteCodeResult> {
// 1. 调用Python服务执行
const result = await pythonExecutorService.executeCode(code, {
sessionId // Python服务维护Session状态
});
// 2. 更新消息状态
await prisma.dcToolCAiHistory.update({
where: { id: messageId },
data: {
executeStatus: result.success ? 'success' : 'failed',
executeResult: result.data,
executeError: result.error
}
});
// 3. 如果成功,获取新数据预览(前50行)
if (result.success) {
const preview = result.data?.slice(0, 50);
return {
success: true,
result: result.data,
newDataPreview: preview
};
}
return {
success: false,
error: result.error
};
}
/**
* 生成并执行(带自我修正)
* @param sessionId - Tool C Session ID
* @param userMessage - 用户需求
* @param maxRetries - 最大重试次数(默认3)
* @returns { code, explanation, executeResult, retryCount }
*/
async generateAndExecute(
sessionId: string,
userMessage: string,
maxRetries: number = 3
): Promise<ProcessResult> {
let attempt = 0;
let lastError: string | null = null;
let generated: GenerateCodeResult | null = null;
while (attempt < maxRetries) {
try {
// 构建带错误反馈的提示词
const enhancedMessage = attempt === 0
? userMessage
: `${userMessage}\n\n上次执行错误:${lastError}\n请修正代码。`;
// 生成代码
generated = await this.generateCode(sessionId, enhancedMessage);
// 执行代码
const executeResult = await this.executeCode(
sessionId,
generated.code,
generated.messageId
);
if (executeResult.success) {
// ✅ 成功
logger.info(`[AICodeService] 执行成功(尝试${attempt + 1}次)`);
return {
...generated,
executeResult,
retryCount: attempt
};
}
// ❌ 失败,准备重试
lastError = executeResult.error || '未知错误';
attempt++;
logger.warn(`[AICodeService] 执行失败(尝试${attempt}/${maxRetries}): ${lastError}`);
} catch (error: any) {
logger.error(`[AICodeService] 异常: ${error.message}`);
lastError = error.message;
attempt++;
}
}
// 3次仍失败
throw new Error(
`代码执行失败(已重试${maxRetries}次)。最后错误:${lastError}。` +
`建议:请调整需求描述或手动修改代码。`
);
}
/**
* 获取对话历史
*/
async getHistory(sessionId: string, limit: number = 5): Promise<Message[]> {
const records = await prisma.dcToolCAiHistory.findMany({
where: { sessionId },
orderBy: { createdAt: 'desc' },
take: limit * 2 // user + assistant 成对
});
return records.reverse().map(r => ({
role: r.role,
content: r.content
}));
}
// ==================== 辅助方法 ====================
/**
* 构建System Prompt(含10个Few-shot)
*/
private buildSystemPrompt(session: SessionData): string {
return `你是医疗科研数据清洗专家,负责生成Pandas代码来清洗整理数据。
## 当前数据集信息
- 文件名: ${session.fileName}
- 行数: ${session.totalRows}
- 列数: ${session.totalCols}
- 列名: ${session.columns.join(', ')}
## 安全规则(强制)
1. 只能操作df变量,不能修改其他变量
2. 禁止导入os、sys、subprocess等危险模块
3. 禁止使用eval、exec、__import__等危险函数
4. 必须进行异常处理
5. 返回格式必须是JSON: {"code": "...", "explanation": "..."}
## Few-shot示例
### 示例1: 统一缺失值标记
用户: 把所有代表缺失的符号(-、不详、NA、N/A)统一替换为标准空值
代码:
\`\`\`python
df = df.replace(['-', '不详', 'NA', 'N/A', '\\\\', '未查'], np.nan)
\`\`\`
说明: 将多种缺失值表示统一为NaN,便于后续统计分析
### 示例2: 数值列清洗
用户: 把肌酐列里的非数字符号去掉,<0.1按0.05处理,转为数值类型
代码:
\`\`\`python
df['creatinine'] = df['creatinine'].astype(str).str.replace('>', '').str.replace('<', '')
df.loc[df['creatinine'] == '0.1', 'creatinine'] = '0.05'
df['creatinine'] = pd.to_numeric(df['creatinine'], errors='coerce')
\`\`\`
说明: 检验科数据常含符号,需清理后才能计算
[... 示例3-8 ...]
### 示例9: 多重插补(MICE)⭐ 重点
用户: 使用多重插补法对BMI、年龄、肌酐列的缺失值进行填补
代码:
\`\`\`python
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
# 选择需要插补的数值列
cols = ['BMI', 'age', 'creatinine']
# 多重插补
imputer = IterativeImputer(max_iter=10, random_state=0)
df[cols] = imputer.fit_transform(df[cols])
\`\`\`
说明: 利用变量间相关性预测缺失值,保持数据分布特征,适用于MAR(随机缺失)
### 示例10: 智能去重
用户: 按患者ID去重,保留检查日期最新的记录
代码:
\`\`\`python
df['check_date'] = pd.to_datetime(df['check_date'])
df = df.sort_values('check_date').drop_duplicates(subset=['patient_id'], keep='last')
\`\`\`
说明: 先按日期排序,再去重保留最后一条(最新)
## 用户当前请求
请根据以上示例和当前数据集信息,生成代码并解释。
`;
}
/**
* 解析AI回复(提取code和explanation)
*/
private parseAIResponse(content: string): { code: string; explanation: string } {
try {
// 方法1:尝试解析JSON
const json = JSON.parse(content);
if (json.code && json.explanation) {
return { code: json.code, explanation: json.explanation };
}
} catch {
// 方法2:正则提取代码块
const codeMatch = content.match(/```python\n([\s\S]+?)\n```/);
const code = codeMatch ? codeMatch[1] : '';
// 提取解释(代码块之外的文本)
const explanation = content.replace(/```python[\s\S]+?```/g, '').trim();
if (code) {
return { code, explanation };
}
}
throw new Error('AI回复格式错误,无法提取代码');
}
/**
* 保存消息到数据库
*/
private async saveMessages(
sessionId: string,
userMessage: string,
code: string,
explanation: string
): Promise<string> {
// 保存用户消息
await prisma.dcToolCAiHistory.create({
data: {
sessionId,
userId: 'test-user', // TODO: 从JWT获取
role: 'user',
content: userMessage
}
});
// 保存AI回复
const assistantMessage = await prisma.dcToolCAiHistory.create({
data: {
sessionId,
userId: 'test-user',
role: 'assistant',
content: explanation,
generatedCode: code,
codeExplanation: explanation,
executeStatus: 'pending',
model: 'deepseek-v3'
}
});
return assistantMessage.id;
}
}
// 导出单例
export const aiCodeService = new AICodeService();
AIController (新建,~200行)
// backend/src/modules/dc/tool-c/controllers/AIController.ts
export class AIController {
/**
* POST /api/v1/dc/tool-c/ai/generate
* 生成代码(不执行)
*/
async generateCode(request: FastifyRequest, reply: FastifyReply) {
try {
const { sessionId, message } = request.body as any;
// 参数验证
if (!sessionId || !message) {
return reply.code(400).send({
success: false,
error: '缺少必要参数:sessionId、message'
});
}
// 生成代码
const result = await aiCodeService.generateCode(sessionId, message);
return reply.code(200).send({
success: true,
data: result
});
} catch (error: any) {
logger.error(`[AIController] generateCode失败: ${error.message}`);
return reply.code(500).send({
success: false,
error: error.message
});
}
}
/**
* POST /api/v1/dc/tool-c/ai/execute
* 执行代码
*/
async executeCode(request: FastifyRequest, reply: FastifyReply) {
try {
const { sessionId, code, messageId } = request.body as any;
const result = await aiCodeService.executeCode(sessionId, code, messageId);
return reply.code(200).send({
success: true,
data: result
});
} 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 any;
const result = await aiCodeService.generateAndExecute(
sessionId,
message,
maxRetries
);
return reply.code(200).send({
success: true,
data: result
});
} 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: { sessionId: string } }>,
reply: FastifyReply
) {
try {
const { sessionId } = request.params;
const history = await aiCodeService.getHistory(sessionId, 10);
return reply.code(200).send({
success: true,
data: { history }
});
} catch (error: any) {
logger.error(`[AIController] getHistory失败: ${error.message}`);
return reply.code(500).send({
success: false,
error: error.message
});
}
}
}
export const aiController = new AIController();
📅 开发计划
阶段1: 数据库设计(30分钟)⏰ 09:00-09:30
任务清单:
- 更新
backend/prisma/schema.prisma(添加DcToolCAiHistory模型) - 创建数据库迁移脚本
create-tool-c-ai-history-table.mjs - 执行迁移(创建表)
- 验证表结构
- 生成Prisma Client (
npx prisma generate)
交付物:
- ✅
dc_schema.dc_tool_c_ai_history表创建成功 - ✅ Prisma Client更新完成
阶段2: AICodeService实现(2小时)⏰ 09:30-11:30
任务清单:
- 创建
AICodeService.ts基础结构 - 实现
buildSystemPrompt()- 10个Few-shot示例集成 - 实现
generateCode()- AI生成代码 - 实现
parseAIResponse()- 解析AI回复 - 实现
executeCode()- 执行Python代码 - 实现
generateAndExecute()- 生成+执行+重试 - 实现
getHistory()- 获取对话历史 - 实现
saveMessages()- 保存消息到数据库 - 添加完整错误处理和日志
交付物:
- ✅
AICodeService.ts完整实现(~400行) - ✅ 单元测试通过
阶段3: AIController实现(1小时)⏰ 11:30-12:30
任务清单:
- 创建
AIController.ts - 实现
POST /ai/generate- 生成代码 - 实现
POST /ai/execute- 执行代码 - 实现
POST /ai/process- 一步到位 - 实现
GET /ai/history/:sessionId- 对话历史 - 添加参数验证
- 添加错误处理
交付物:
- ✅
AIController.ts完整实现(~200行) - ✅ 4个API端点就绪
午休 ⏰ 12:30-13:30
阶段4: 路由配置(15分钟)⏰ 13:30-13:45
任务清单:
- 更新
routes/index.ts - 注册AI相关路由
- 测试路由可访问性
交付物:
- ✅ AI路由注册完成
- ✅ Swagger文档更新(如有)
阶段5: 测试验收(1.5小时)⏰ 13:45-15:15
5.1 基础测试(30分钟)
测试用例:
- 测试1: 统一缺失值标记
- 测试2: 数值列清洗
- 测试3: 性别编码
- 测试4: 年龄分组
验收标准:
- AI能正确生成代码
- 代码可执行
- 结果符合预期
5.2 中级测试(30分钟)
测试用例: 5. [ ] 测试5: BMI计算 6. [ ] 测试6: 住院天数计算 7. [ ] 测试7: 条件筛选
5.3 高级测试(30分钟)
测试用例: 8. [ ] 测试8: 中位数填补 9. [ ] 测试9: 多重插补MICE ⭐ 10. [ ] 测试10: 智能去重
5.4 特殊测试(30分钟)
测试用例: 11. [ ] 自我修正测试(故意错误,验证重试机制) 12. [ ] 边界测试(列不存在、全部缺失等) 13. [ ] 并发测试(多用户同时使用) 14. [ ] 端到端测试(上传→AI处理→结果验证)
交付物:
- ✅ 测试脚本
test-tool-c-day3.mjs - ✅ 测试报告(通过率≥90%)
阶段6: 文档与优化(30分钟)⏰ 15:15-15:45
任务清单:
- 创建技术债务清单
Tool-C技术债务清单.md - 更新模块状态文档
00-工具C当前状态与开发指南.md - 创建Day 3开发完成总结
- 提交Git并推送
交付物:
- ✅ 技术债务文档
- ✅ Day 3开发记录
- ✅ Git提交成功
🎯 验收标准
功能验收
| 功能 | 验收标准 | 状态 |
|---|---|---|
| AI代码生成 | 10个示例场景100%可生成正确代码 | ⏸️ |
| 代码执行 | 生成的代码可成功执行 | ⏸️ |
| 自我修正 | 失败后能自动重试(最多3次) | ⏸️ |
| 对话历史 | 能获取最近5轮对话 | ⏸️ |
| 数据预览 | 执行后返回前50行预览 | ⏸️ |
技术验收
| 指标 | 目标 | 状态 |
|---|---|---|
| 代码质量 | 无TypeScript错误 | ⏸️ |
| 云原生规范 | 100%符合 | ⏸️ |
| 错误处理 | 所有异常都有处理 | ⏸️ |
| 日志完整性 | 关键操作都有日志 | ⏸️ |
| 测试覆盖率 | ≥80% | ⏸️ |
性能验收
| 指标 | 目标 | 状态 |
|---|---|---|
| AI生成时间 | <5秒 | ⏸️ |
| 代码执行时间 | <3秒(简单操作) | ⏸️ |
| 端到端时间 | <10秒 | ⏸️ |
📦 交付清单
代码文件(6个)
- ✅
backend/prisma/schema.prisma- 新增DcToolCAiHistory模型 - ✅
backend/scripts/create-tool-c-ai-history-table.mjs- 建表脚本 - ✅
backend/src/modules/dc/tool-c/services/AICodeService.ts- 400行 - ✅
backend/src/modules/dc/tool-c/controllers/AIController.ts- 200行 - ✅
backend/src/modules/dc/tool-c/routes/index.ts- 更新 - ✅
backend/test-tool-c-day3.mjs- 测试脚本
文档文件(4个)
- ✅
工具C_AI_Few-shot示例库.md- 10个示例详解 - ✅
工具C_Day3开发计划.md- 本文档 - ✅
Tool-C技术债务清单.md- 待优化项 - ✅
2025-12-06_工具C_Day3开发完成总结.md- 总结报告
🔗 相关文档
🔄 风险管理
风险1: AI生成代码质量不稳定
应对措施:
- ✅ 使用10个Few-shot示例提升质量
- ✅ 降低temperature至0.1
- ✅ 实施3次重试机制
- ✅ 添加AST静态检查(Python服务)
风险2: LLM调用超时
应对措施:
- ✅ 设置合理的timeout(10秒)
- ✅ 前端显示加载状态
- ✅ 添加重试机制
风险3: Python执行失败
应对措施:
- ✅ AI自我修正(最多3次)
- ✅ 友好错误提示
- ✅ 建议用户调整需求
📊 预期成果
Day 3完成后:
- ✅ Tool C用户可通过自然语言清洗数据
- ✅ AI能生成90%场景的正确代码
- ✅ 失败场景有自动重试机制
- ✅ 完整的对话历史管理
整体进度:
- Day 1: Python微服务 ✅
- Day 2: Session管理 ✅
- Day 3: AI代码生成 ⏸️
- Day 4-5: 前端开发
- Day 6: 端到端测试
📝 更新记录
| 日期 | 版本 | 更新内容 | 更新人 |
|---|---|---|---|
| 2025-12-06 | V1.0 | 初始创建,明确9大决策和开发计划 | AI Assistant |
文档状态: ✅ 已确认
下一步: 开始执行开发计划(预计5.5-6小时)
准备开始开发! 🚀