api-server/src/modules/ai-job/feynman-executor.ts
wangdl 8987598eb8 feat(M-AI-05): track Feynman unified engine migration implementation
23 files (+4676/-10):
- Contract: m-ai-05-feynman-migration-contract.md (737 lines)
- Gate audit: m-ai-05-gate-audit.md (318 lines)
- Job Definition + Snapshot Builder + Registration
- Executor + BusinessValidator + ReferenceValidator
- Projector (atomic transaction + 3-layer idempotency)
- ExecutionRouter (FeatureFlag + idempotencyKey)
- ObservabilityService (structured logging + counters)
- Engine: feynman_evaluation execution branch
- AiJobCreationService: feynman_evaluation safety branch
- Controller/Module: Router injection
- CI: path detection for m-ai-05
- E2E: 8 HTTP-layer scenarios (14 total)
- Unit tests: 104 new tests (5 spec files)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-21 17:44:58 +08:00

100 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
*
* 兼容性:
* - 使用与旧链路相同的 promptKeyfeynman-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;
}
}