import { Injectable, Logger } from '@nestjs/common'; import { AiGatewayService } from '../ai/gateway/ai-gateway.service'; import { FeynmanEvaluationResultSchema } from '../ai/prompts/schemas/feynman-evaluation.schema'; import type { FeynmanEvaluationResult } from '../ai/prompts/schemas/feynman-evaluation.schema'; import type { FeynmanSnapshot } from './feynman-snapshot-builder'; /** * M-AI-05-03: Feynman Executor * * 将 Feynman 评估输入快照适配到统一 Job Engine 的 EXECUTE 阶段。 * * 职责: * 1. 从 Snapshot 构造模型请求消息(复用现有 Feynman prompt 模板逻辑) * 2. 通过 AiGatewayService 调用模型(不直接导入 Provider SDK) * 3. 接收 timeout → 委托给 AiGatewayService 内部的 AbortController * 4. 返回 AiGatewayService 的原始响应(parsed output) * * 不负责(由 Engine 统一处理): * - 写数据库(无副作用) * - 写 Job 状态 * - 重试逻辑 * - 写 Artifact * - 解析 Credential * * 兼容性: * - 使用与旧链路相同的 promptKey(feynman-evaluation)和 outputSchema * - 消息构造逻辑与 FeynmanEvaluationWorkflow.execute() 一致 * (src/modules/ai/workflows/feynman-evaluation.workflow.ts:18-29) */ @Injectable() export class FeynmanExecutor { private readonly logger = new Logger(FeynmanExecutor.name); constructor(private readonly aiGateway: AiGatewayService) {} /** * 执行 Feynman 评估 AI 分析。 * * @param snapshot - FeynmanSnapshot(由 FeynmanSnapshotBuilder 产出) * @param timeoutMs - 超时毫秒数(来自 Definition.execution.timeoutMs) * @returns AiGateway 响应(含 parsed + usage) */ async execute( snapshot: FeynmanSnapshot, timeoutMs: number, ) { const s = snapshot.snapshot; // 构造用户消息(与旧链路 FeynmanEvaluationWorkflow.execute() 一致) // workflow.ts:18-29 的消息格式: // 【知识点标题】+ title + 【知识点原文】+ content + 【用户的费曼解释】+ explanation const userMessage = [ `【知识点标题】`, s.knowledgeItemTitle, '', `【知识点原文】`, s.knowledgeItemContent, '', `【用户的费曼解释】`, s.userExplanation, '', `请评估以上费曼解释的质量,严格按照 JSON Schema 输出。`, ].join('\n'); this.logger.log( `Feynman Executor calling AI: userId=${s.userId} ` + `knowledgeItemId=${s.knowledgeItemId} ` + `submissionId=${s.submissionId} ` + `promptKey=${s.promptKey} promptVersion=${s.promptVersion} ` + `modelTier=${s.modelTier} timeoutMs=${timeoutMs}`, ); const response = await this.aiGateway.generate( { feature: 'feynman-evaluation', userId: s.userId, tier: s.modelTier as any, promptKey: s.promptKey, promptVersion: s.promptVersion, messages: [ { role: 'user' as const, content: userMessage }, ], outputSchema: FeynmanEvaluationResultSchema, maxTokens: 4096, }, timeoutMs, ); this.logger.log( `Feynman Executor completed: userId=${s.userId} ` + `knowledgeItemId=${s.knowledgeItemId} ` + `score=${(response.parsed as any)?.score} ` + `tokens=${response.usage.inputTokens}/${response.usage.outputTokens}`, ); return response; } }