Fallback after malformed structured LLM retries
This commit is contained in:
@@ -916,6 +916,28 @@ function buildScoringFallbackBegruendung(
|
||||
return `${farbe} (${meaning}). Staerkste Bereiche: ${strongestDimensions || "keine"}.${missing}`;
|
||||
}
|
||||
|
||||
function fallbackScoringAssessment(reason: string): Partial<LlmScoringAssessment> & { begruendung?: string } {
|
||||
return {
|
||||
begruendung: `Die automatische Detailbewertung konnte nicht vollstaendig ausgewertet werden: ${reason}`,
|
||||
ausschlussgruende: [],
|
||||
dimensionen: SCORING_MODEL.map((dimension) => ({
|
||||
id: dimension.id,
|
||||
subcriteria: dimension.subcriteria.map((subcriterion) => ({
|
||||
id: subcriterion.id,
|
||||
farbe: "unbewertbar",
|
||||
evidence: "",
|
||||
begruendung: "",
|
||||
confidence: 0,
|
||||
missingReason: `Modellantwort konnte nach ${MAX_LLM_RETRY_ATTEMPTS} Versuchen nicht als gueltiges JSON gelesen werden.`,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
async function assessTrafficLight(
|
||||
client: OpenAI,
|
||||
model: string,
|
||||
@@ -932,12 +954,7 @@ async function assessTrafficLight(
|
||||
scoringModel: SCORING_MODEL,
|
||||
};
|
||||
|
||||
const parsed = await createParsedStructuredCompletion<Partial<LlmScoringAssessment> & {
|
||||
begruendung?: string;
|
||||
}>(
|
||||
client,
|
||||
model,
|
||||
[
|
||||
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
@@ -971,7 +988,15 @@ Wichtige Vorgaben:
|
||||
Bewerbungsdaten:
|
||||
${JSON.stringify(data, null, 2)}`,
|
||||
},
|
||||
],
|
||||
];
|
||||
let parsed: Partial<LlmScoringAssessment> & { begruendung?: string };
|
||||
try {
|
||||
parsed = await createParsedStructuredCompletion<Partial<LlmScoringAssessment> & {
|
||||
begruendung?: string;
|
||||
}>(
|
||||
client,
|
||||
model,
|
||||
messages,
|
||||
"ampelbewertung",
|
||||
{
|
||||
type: "object",
|
||||
@@ -1015,7 +1040,10 @@ ${JSON.stringify(data, null, 2)}`,
|
||||
"Could not parse traffic light assessment JSON",
|
||||
sessionId,
|
||||
{ operation: "scoring", label: "Ampelbewertung", onLlmCall },
|
||||
);
|
||||
);
|
||||
} catch (error) {
|
||||
parsed = fallbackScoringAssessment(errorMessage(error));
|
||||
}
|
||||
const ausschlussgruende = Array.isArray(parsed.ausschlussgruende)
|
||||
? parsed.ausschlussgruende.map((flag) => String(flag).trim()).filter(Boolean)
|
||||
: [];
|
||||
@@ -1049,10 +1077,7 @@ async function segmentMultiQuestionAnswer(
|
||||
return fragen;
|
||||
}
|
||||
|
||||
const parsed = await createParsedStructuredCompletion<{ fragen?: Array<Partial<FrageMitAntwort>> }>(
|
||||
client,
|
||||
model,
|
||||
[
|
||||
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
@@ -1081,7 +1106,13 @@ Vorgaben:
|
||||
- Mehrfachnennungen sind erlaubt, wenn ein Abschnitt mehrere Unterfragen klar beantwortet.
|
||||
- Lasse keine Unterfrage aus.`,
|
||||
},
|
||||
],
|
||||
];
|
||||
let parsed: { fragen?: Array<Partial<FrageMitAntwort>> };
|
||||
try {
|
||||
parsed = await createParsedStructuredCompletion<{ fragen?: Array<Partial<FrageMitAntwort>> }>(
|
||||
client,
|
||||
model,
|
||||
messages,
|
||||
"segmentierung_unterfragen",
|
||||
{
|
||||
type: "object",
|
||||
@@ -1107,7 +1138,17 @@ Vorgaben:
|
||||
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
|
||||
sessionId,
|
||||
{ operation: "segmentierung", label: fallbackLabel, onLlmCall },
|
||||
);
|
||||
);
|
||||
} catch {
|
||||
parsed = {
|
||||
fragen: fragen.map((frage) => ({
|
||||
id: frage.id,
|
||||
text: frage.text,
|
||||
antwort: "",
|
||||
confidence: 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
const byId = new Map((parsed.fragen ?? []).map((frage) => [String(frage.id ?? ""), frage]));
|
||||
|
||||
return fragen.map((frage) => {
|
||||
@@ -1139,10 +1180,7 @@ async function analyzeSWOT(
|
||||
sessionId?: string,
|
||||
onLlmCall?: SummarizeCompanyOptions["onLlmCall"],
|
||||
): Promise<SWOTAnalyse> {
|
||||
const parsed = await createParsedStructuredCompletion<Partial<SWOTAnalyse>>(
|
||||
client,
|
||||
model,
|
||||
[
|
||||
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
@@ -1165,7 +1203,13 @@ Vorgaben:
|
||||
Bewerbungsdaten:
|
||||
${JSON.stringify(data, null, 2)}`,
|
||||
},
|
||||
],
|
||||
];
|
||||
let parsed: Partial<SWOTAnalyse>;
|
||||
try {
|
||||
parsed = await createParsedStructuredCompletion<Partial<SWOTAnalyse>>(
|
||||
client,
|
||||
model,
|
||||
messages,
|
||||
"swot_analyse",
|
||||
{
|
||||
type: "object",
|
||||
@@ -1181,7 +1225,15 @@ ${JSON.stringify(data, null, 2)}`,
|
||||
"Could not parse SWOT analysis JSON",
|
||||
sessionId,
|
||||
{ operation: "swot", label: "SWOT-Analyse", onLlmCall },
|
||||
);
|
||||
);
|
||||
} catch (error) {
|
||||
parsed = {
|
||||
staerken: [],
|
||||
schwaechen: [],
|
||||
chancen: [],
|
||||
risiken: [`SWOT-Analyse konnte nicht automatisch erstellt werden: ${errorMessage(error)}`],
|
||||
};
|
||||
}
|
||||
const toStringArray = (value: unknown): string[] =>
|
||||
Array.isArray(value) ? value.map((item) => String(item).trim()).filter(Boolean) : [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user