Этап 4 / Волна 10: корректировка settlement-кейса — доменная фиксация синтеза, честное покрытие, удержание фокуса / Этап 4 / Волна 11: бизнес-якоря, доменное заземление и устранение утечки дебага
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
FEATURE_ASSISTANT_STAGE2_EVAL_V1,
|
||||
REPORTS_DIR
|
||||
} from "../config";
|
||||
import { P0EvalRunner } from "../eval/p0_eval_runner";
|
||||
import type { AssistantMessageResponsePayload } from "../types/assistant";
|
||||
import type {
|
||||
EvalTarget,
|
||||
@@ -328,6 +329,98 @@ const ASSISTANT_STAGE1_COMPARISON_SCHEMA_VERSION = "assistant_stage1_eval_compar
|
||||
const DEFAULT_ASSISTANT_STAGE2_SUITE_FILE = "assistant_stage2_canonical_v0_1.json";
|
||||
const ASSISTANT_STAGE2_RUN_SCHEMA_VERSION = "assistant_stage2_eval_run_v0_1";
|
||||
const ASSISTANT_STAGE2_COMPARISON_SCHEMA_VERSION = "assistant_stage2_eval_comparison_v0_1";
|
||||
const INMEM_EVAL_REPORT_PREFIX = "inmem_eval_report:";
|
||||
const INMEM_EVAL_REPORTS = new Map<string, Record<string, unknown>>();
|
||||
|
||||
function isNoSpaceError(error: unknown): boolean {
|
||||
const code = (error as { code?: unknown } | null)?.code;
|
||||
return code === "ENOSPC";
|
||||
}
|
||||
|
||||
function tryWriteJsonFile(pathname: string, value: unknown): boolean {
|
||||
try {
|
||||
writeJsonFile(pathname, value);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (isNoSpaceError(error)) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function tryWriteTextFile(pathname: string, value: string): boolean {
|
||||
try {
|
||||
fs.writeFileSync(pathname, value, "utf-8");
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (isNoSpaceError(error)) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function putInMemoryEvalReport(report: Record<string, unknown>): string {
|
||||
const key = `${INMEM_EVAL_REPORT_PREFIX}${nanoid(12)}`;
|
||||
INMEM_EVAL_REPORTS.set(key, report);
|
||||
return key;
|
||||
}
|
||||
|
||||
function readEvalReportByRef(ref: string): { report: Record<string, unknown>; resolved_path: string } {
|
||||
if (ref.startsWith(INMEM_EVAL_REPORT_PREFIX)) {
|
||||
const report = INMEM_EVAL_REPORTS.get(ref);
|
||||
if (!report) {
|
||||
throw new Error(`In-memory eval report not found: ${ref}`);
|
||||
}
|
||||
return {
|
||||
report,
|
||||
resolved_path: ref
|
||||
};
|
||||
}
|
||||
|
||||
const resolvedPath = resolveReadablePath(ref);
|
||||
const report = JSON.parse(fs.readFileSync(resolvedPath, "utf-8")) as Record<string, unknown>;
|
||||
return {
|
||||
report,
|
||||
resolved_path: resolvedPath
|
||||
};
|
||||
}
|
||||
|
||||
function compactAssistantStage1Report(report: Record<string, unknown>): Record<string, unknown> {
|
||||
const results = Array.isArray(report.results) ? (report.results as Array<Record<string, unknown>>) : [];
|
||||
const compactResults = results.map((item) => ({
|
||||
case_id: item.case_id ?? null,
|
||||
scenario_tag: item.scenario_tag ?? null,
|
||||
accountant_usefulness_score: item.accountant_usefulness_score ?? null,
|
||||
accountant_metrics:
|
||||
typeof item.accountant_metrics === "object" && item.accountant_metrics !== null ? item.accountant_metrics : null
|
||||
}));
|
||||
return {
|
||||
...report,
|
||||
results: compactResults
|
||||
};
|
||||
}
|
||||
|
||||
function compactAssistantStage2Report(report: Record<string, unknown>): Record<string, unknown> {
|
||||
const results = Array.isArray(report.results) ? (report.results as Array<Record<string, unknown>>) : [];
|
||||
const compactResults = results.map((item) => {
|
||||
const metricSubscores = (item.metric_subscores ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
case_id: item.case_id ?? null,
|
||||
metric_subscores: {
|
||||
problem_clarity_score: metricSubscores.problem_clarity_score ?? null,
|
||||
mechanism_coherence_score: metricSubscores.mechanism_coherence_score ?? null,
|
||||
problem_first_answer_rate: metricSubscores.problem_first_answer_rate ?? null,
|
||||
entity_leakage_rate: metricSubscores.entity_leakage_rate ?? null
|
||||
}
|
||||
};
|
||||
});
|
||||
return {
|
||||
...report,
|
||||
results: compactResults
|
||||
};
|
||||
}
|
||||
|
||||
type AssistantMetricKey = keyof AssistantEvalMetricVector;
|
||||
type AssistantStage2MetricKey = keyof AssistantStage2MetricVector;
|
||||
@@ -1140,7 +1233,7 @@ export class EvalService {
|
||||
};
|
||||
|
||||
ensureDir(EVAL_CASES_DIR);
|
||||
writeJsonFile(path.resolve(EVAL_CASES_DIR, `${runId}.report.json`), report);
|
||||
tryWriteJsonFile(path.resolve(EVAL_CASES_DIR, `${runId}.report.json`), report);
|
||||
|
||||
return report;
|
||||
}
|
||||
@@ -1534,8 +1627,9 @@ export class EvalService {
|
||||
currentReport: Record<string, unknown>;
|
||||
baselineReportFile: string;
|
||||
}): Record<string, unknown> {
|
||||
const baselinePath = resolveReadablePath(input.baselineReportFile);
|
||||
const baselineReport = JSON.parse(fs.readFileSync(baselinePath, "utf-8")) as Record<string, unknown>;
|
||||
const baselineRef = readEvalReportByRef(input.baselineReportFile);
|
||||
const baselinePath = baselineRef.resolved_path;
|
||||
const baselineReport = baselineRef.report;
|
||||
const currentReport = input.currentReport;
|
||||
const metricKeys: AssistantMetricKey[] = [
|
||||
"retrieval_differentiation_rate",
|
||||
@@ -1633,14 +1727,15 @@ export class EvalService {
|
||||
ensureDir(REPORTS_DIR);
|
||||
const jsonPath = path.resolve(REPORTS_DIR, `${comparisonId}.json`);
|
||||
const mdPath = path.resolve(REPORTS_DIR, `${comparisonId}.md`);
|
||||
writeJsonFile(jsonPath, comparisonReport);
|
||||
fs.writeFileSync(mdPath, buildAssistantComparisonMarkdownReport(comparisonReport), "utf-8");
|
||||
const jsonWritten = tryWriteJsonFile(jsonPath, comparisonReport);
|
||||
const mdWritten = tryWriteTextFile(mdPath, buildAssistantComparisonMarkdownReport(comparisonReport));
|
||||
const comparisonRef = jsonWritten ? jsonPath : putInMemoryEvalReport(comparisonReport);
|
||||
|
||||
return {
|
||||
...comparisonReport,
|
||||
artifacts: {
|
||||
comparison_report_json_path: jsonPath,
|
||||
comparison_report_md_path: mdPath
|
||||
comparison_report_json_path: comparisonRef,
|
||||
comparison_report_md_path: mdWritten ? mdPath : null
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1649,8 +1744,9 @@ export class EvalService {
|
||||
currentReport: Record<string, unknown>;
|
||||
baselineReportFile: string;
|
||||
}): Record<string, unknown> {
|
||||
const baselinePath = resolveReadablePath(input.baselineReportFile);
|
||||
const baselineReport = JSON.parse(fs.readFileSync(baselinePath, "utf-8")) as Record<string, unknown>;
|
||||
const baselineRef = readEvalReportByRef(input.baselineReportFile);
|
||||
const baselinePath = baselineRef.resolved_path;
|
||||
const baselineReport = baselineRef.report;
|
||||
const currentReport = input.currentReport;
|
||||
const metricKeys: AssistantStage2MetricKey[] = [
|
||||
"problem_unit_precision",
|
||||
@@ -1760,14 +1856,15 @@ export class EvalService {
|
||||
ensureDir(REPORTS_DIR);
|
||||
const jsonPath = path.resolve(REPORTS_DIR, `${comparisonId}.json`);
|
||||
const mdPath = path.resolve(REPORTS_DIR, `${comparisonId}.md`);
|
||||
writeJsonFile(jsonPath, comparisonReport);
|
||||
fs.writeFileSync(mdPath, buildAssistantStage2ComparisonMarkdownReport(comparisonReport), "utf-8");
|
||||
const jsonWritten = tryWriteJsonFile(jsonPath, comparisonReport);
|
||||
const mdWritten = tryWriteTextFile(mdPath, buildAssistantStage2ComparisonMarkdownReport(comparisonReport));
|
||||
const comparisonRef = jsonWritten ? jsonPath : putInMemoryEvalReport(comparisonReport);
|
||||
|
||||
return {
|
||||
...comparisonReport,
|
||||
artifacts: {
|
||||
comparison_report_json_path: jsonPath,
|
||||
comparison_report_md_path: mdPath
|
||||
comparison_report_json_path: comparisonRef,
|
||||
comparison_report_md_path: mdWritten ? mdPath : null
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1803,7 +1900,7 @@ export class EvalService {
|
||||
|
||||
try {
|
||||
for (const turn of suiteCase.turns) {
|
||||
const response = await assistantService.handleMessage({
|
||||
const response = (await assistantService.handleMessage({
|
||||
session_id: sessionId,
|
||||
user_message: turn.user_message,
|
||||
message: turn.user_message,
|
||||
@@ -1819,7 +1916,7 @@ export class EvalService {
|
||||
domainPrompt: payload.normalizeConfig.domainPrompt,
|
||||
fewShotExamples: payload.normalizeConfig.fewShotExamples,
|
||||
useMock: payload.useMock
|
||||
});
|
||||
})) as AssistantMessageResponsePayload;
|
||||
turnResponses.push(response);
|
||||
requestsTotal += 1;
|
||||
}
|
||||
@@ -2099,12 +2196,14 @@ export class EvalService {
|
||||
ensureDir(REPORTS_DIR);
|
||||
const runJsonPath = path.resolve(REPORTS_DIR, `${runId}.json`);
|
||||
const runMdPath = path.resolve(REPORTS_DIR, `${runId}.md`);
|
||||
writeJsonFile(runJsonPath, report);
|
||||
fs.writeFileSync(runMdPath, buildAssistantEvalMarkdownReport(report), "utf-8");
|
||||
const compactReport = compactAssistantStage1Report(report);
|
||||
const jsonWritten = tryWriteJsonFile(runJsonPath, compactReport);
|
||||
const mdWritten = tryWriteTextFile(runMdPath, buildAssistantEvalMarkdownReport(compactReport));
|
||||
const runReportRef = jsonWritten ? runJsonPath : putInMemoryEvalReport(compactReport);
|
||||
|
||||
report.artifacts = {
|
||||
run_report_json_path: runJsonPath,
|
||||
run_report_md_path: runMdPath
|
||||
run_report_json_path: runReportRef,
|
||||
run_report_md_path: mdWritten ? runMdPath : null
|
||||
};
|
||||
|
||||
if (payload.compareWithReportFile) {
|
||||
@@ -2151,7 +2250,7 @@ export class EvalService {
|
||||
|
||||
try {
|
||||
for (const turn of suiteCase.turns) {
|
||||
const response = await assistantService.handleMessage({
|
||||
const response = (await assistantService.handleMessage({
|
||||
session_id: sessionId,
|
||||
user_message: turn.user_message,
|
||||
message: turn.user_message,
|
||||
@@ -2167,7 +2266,7 @@ export class EvalService {
|
||||
domainPrompt: payload.normalizeConfig.domainPrompt,
|
||||
fewShotExamples: payload.normalizeConfig.fewShotExamples,
|
||||
useMock: payload.useMock
|
||||
});
|
||||
})) as AssistantMessageResponsePayload;
|
||||
turnResponses.push(response);
|
||||
requestsTotal += 1;
|
||||
}
|
||||
@@ -2393,12 +2492,14 @@ export class EvalService {
|
||||
ensureDir(REPORTS_DIR);
|
||||
const runJsonPath = path.resolve(REPORTS_DIR, `${runId}.json`);
|
||||
const runMdPath = path.resolve(REPORTS_DIR, `${runId}.md`);
|
||||
writeJsonFile(runJsonPath, report);
|
||||
fs.writeFileSync(runMdPath, buildAssistantStage2EvalMarkdownReport(report), "utf-8");
|
||||
const compactReport = compactAssistantStage2Report(report);
|
||||
const jsonWritten = tryWriteJsonFile(runJsonPath, compactReport);
|
||||
const mdWritten = tryWriteTextFile(runMdPath, buildAssistantStage2EvalMarkdownReport(compactReport));
|
||||
const runReportRef = jsonWritten ? runJsonPath : putInMemoryEvalReport(compactReport);
|
||||
|
||||
report.artifacts = {
|
||||
run_report_json_path: runJsonPath,
|
||||
run_report_md_path: runMdPath
|
||||
run_report_json_path: runReportRef,
|
||||
run_report_md_path: mdWritten ? runMdPath : null
|
||||
};
|
||||
|
||||
if (payload.compareWithReportFile) {
|
||||
@@ -2411,6 +2512,33 @@ export class EvalService {
|
||||
return report;
|
||||
}
|
||||
|
||||
private async runAssistantP0(payload: {
|
||||
normalizeConfig: Omit<NormalizeRequestPayload, "userQuestion" | "context">;
|
||||
caseIds?: string[];
|
||||
useMock?: boolean;
|
||||
mode: EvalRunMode;
|
||||
caseSetFile?: string;
|
||||
compareWithReportFile?: string;
|
||||
}): Promise<Record<string, unknown>> {
|
||||
if (!FEATURE_ASSISTANT_STAGE2_EVAL_V1) {
|
||||
throw new ApiError(
|
||||
"ASSISTANT_P0_EVAL_DISABLED",
|
||||
"Assistant P0 eval target is disabled by FEATURE_ASSISTANT_STAGE2_EVAL_V1.",
|
||||
409
|
||||
);
|
||||
}
|
||||
|
||||
const runner = new P0EvalRunner(this.normalizerService);
|
||||
return runner.run({
|
||||
normalizeConfig: payload.normalizeConfig,
|
||||
caseIds: payload.caseIds,
|
||||
useMock: payload.useMock,
|
||||
mode: payload.mode,
|
||||
caseSetFile: payload.caseSetFile,
|
||||
compareWithReportFile: payload.compareWithReportFile
|
||||
});
|
||||
}
|
||||
|
||||
public async run(payload: {
|
||||
normalizeConfig: Omit<NormalizeRequestPayload, "userQuestion" | "context">;
|
||||
caseIds?: string[];
|
||||
@@ -2446,6 +2574,17 @@ export class EvalService {
|
||||
});
|
||||
}
|
||||
|
||||
if (evalTarget === "assistant_p0") {
|
||||
return this.runAssistantP0({
|
||||
normalizeConfig: payload.normalizeConfig,
|
||||
caseIds: payload.caseIds,
|
||||
useMock: payload.useMock,
|
||||
mode,
|
||||
caseSetFile: payload.caseSetFile,
|
||||
compareWithReportFile: payload.compareWithReportFile
|
||||
});
|
||||
}
|
||||
|
||||
const promptVersion = String(payload.normalizeConfig.promptVersion ?? "").toLowerCase();
|
||||
const schemaVersion = String(payload.normalizeConfig.schemaVersion ?? "").toLowerCase();
|
||||
const isV2 =
|
||||
@@ -2659,7 +2798,7 @@ export class EvalService {
|
||||
};
|
||||
|
||||
ensureDir(EVAL_CASES_DIR);
|
||||
writeJsonFile(path.resolve(EVAL_CASES_DIR, `${runId}.report.json`), report);
|
||||
tryWriteJsonFile(path.resolve(EVAL_CASES_DIR, `${runId}.report.json`), report);
|
||||
|
||||
const shouldWriteV11Artifacts =
|
||||
mode === "single-pass-strict" &&
|
||||
@@ -2668,14 +2807,13 @@ export class EvalService {
|
||||
|
||||
if (shouldWriteV11Artifacts) {
|
||||
ensureDir(REPORTS_DIR);
|
||||
writeJsonFile(path.resolve(REPORTS_DIR, "normalizer_eval_v1_1_run.json"), report);
|
||||
fs.writeFileSync(
|
||||
tryWriteJsonFile(path.resolve(REPORTS_DIR, "normalizer_eval_v1_1_run.json"), report);
|
||||
tryWriteTextFile(
|
||||
path.resolve(REPORTS_DIR, "normalizer_eval_v1_1_run.md"),
|
||||
buildMarkdownReport({
|
||||
...report,
|
||||
report_title: "LLM Normalizer v1.1 Eval Run"
|
||||
}),
|
||||
"utf-8"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2687,14 +2825,13 @@ export class EvalService {
|
||||
|
||||
if (shouldWriteV1121EvalArtifacts) {
|
||||
ensureDir(REPORTS_DIR);
|
||||
writeJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_2_1_eval.json"), report);
|
||||
fs.writeFileSync(
|
||||
tryWriteJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_2_1_eval.json"), report);
|
||||
tryWriteTextFile(
|
||||
path.resolve(REPORTS_DIR, "normalizer_v1_1_2_1_eval.md"),
|
||||
buildMarkdownReport({
|
||||
...report,
|
||||
report_title: "LLM Normalizer v1.1.2.1 Eval Run"
|
||||
}),
|
||||
"utf-8"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2705,14 +2842,13 @@ export class EvalService {
|
||||
|
||||
if (shouldWriteV111MicroArtifacts) {
|
||||
ensureDir(REPORTS_DIR);
|
||||
writeJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_1_micro_eval.json"), report);
|
||||
fs.writeFileSync(
|
||||
tryWriteJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_1_micro_eval.json"), report);
|
||||
tryWriteTextFile(
|
||||
path.resolve(REPORTS_DIR, "normalizer_v1_1_1_micro_eval.md"),
|
||||
buildMarkdownReport({
|
||||
...report,
|
||||
report_title: "LLM Normalizer v1.1.1 Micro Eval"
|
||||
}),
|
||||
"utf-8"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2723,14 +2859,13 @@ export class EvalService {
|
||||
|
||||
if (shouldWriteV112MicroArtifacts) {
|
||||
ensureDir(REPORTS_DIR);
|
||||
writeJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_2_micro_eval.json"), report);
|
||||
fs.writeFileSync(
|
||||
tryWriteJsonFile(path.resolve(REPORTS_DIR, "normalizer_v1_1_2_micro_eval.json"), report);
|
||||
tryWriteTextFile(
|
||||
path.resolve(REPORTS_DIR, "normalizer_v1_1_2_micro_eval.md"),
|
||||
buildMarkdownReport({
|
||||
...report,
|
||||
report_title: "LLM Normalizer v1.1.2 Micro Eval"
|
||||
}),
|
||||
"utf-8"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user