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}`;
|
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(
|
async function assessTrafficLight(
|
||||||
client: OpenAI,
|
client: OpenAI,
|
||||||
model: string,
|
model: string,
|
||||||
@@ -932,12 +954,7 @@ async function assessTrafficLight(
|
|||||||
scoringModel: SCORING_MODEL,
|
scoringModel: SCORING_MODEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const parsed = await createParsedStructuredCompletion<Partial<LlmScoringAssessment> & {
|
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||||
begruendung?: string;
|
|
||||||
}>(
|
|
||||||
client,
|
|
||||||
model,
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content:
|
content:
|
||||||
@@ -971,7 +988,15 @@ Wichtige Vorgaben:
|
|||||||
Bewerbungsdaten:
|
Bewerbungsdaten:
|
||||||
${JSON.stringify(data, null, 2)}`,
|
${JSON.stringify(data, null, 2)}`,
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
let parsed: Partial<LlmScoringAssessment> & { begruendung?: string };
|
||||||
|
try {
|
||||||
|
parsed = await createParsedStructuredCompletion<Partial<LlmScoringAssessment> & {
|
||||||
|
begruendung?: string;
|
||||||
|
}>(
|
||||||
|
client,
|
||||||
|
model,
|
||||||
|
messages,
|
||||||
"ampelbewertung",
|
"ampelbewertung",
|
||||||
{
|
{
|
||||||
type: "object",
|
type: "object",
|
||||||
@@ -1015,7 +1040,10 @@ ${JSON.stringify(data, null, 2)}`,
|
|||||||
"Could not parse traffic light assessment JSON",
|
"Could not parse traffic light assessment JSON",
|
||||||
sessionId,
|
sessionId,
|
||||||
{ operation: "scoring", label: "Ampelbewertung", onLlmCall },
|
{ operation: "scoring", label: "Ampelbewertung", onLlmCall },
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
parsed = fallbackScoringAssessment(errorMessage(error));
|
||||||
|
}
|
||||||
const ausschlussgruende = Array.isArray(parsed.ausschlussgruende)
|
const ausschlussgruende = Array.isArray(parsed.ausschlussgruende)
|
||||||
? parsed.ausschlussgruende.map((flag) => String(flag).trim()).filter(Boolean)
|
? parsed.ausschlussgruende.map((flag) => String(flag).trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
@@ -1049,10 +1077,7 @@ async function segmentMultiQuestionAnswer(
|
|||||||
return fragen;
|
return fragen;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = await createParsedStructuredCompletion<{ fragen?: Array<Partial<FrageMitAntwort>> }>(
|
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||||
client,
|
|
||||||
model,
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content:
|
content:
|
||||||
@@ -1081,7 +1106,13 @@ Vorgaben:
|
|||||||
- Mehrfachnennungen sind erlaubt, wenn ein Abschnitt mehrere Unterfragen klar beantwortet.
|
- Mehrfachnennungen sind erlaubt, wenn ein Abschnitt mehrere Unterfragen klar beantwortet.
|
||||||
- Lasse keine Unterfrage aus.`,
|
- Lasse keine Unterfrage aus.`,
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
let parsed: { fragen?: Array<Partial<FrageMitAntwort>> };
|
||||||
|
try {
|
||||||
|
parsed = await createParsedStructuredCompletion<{ fragen?: Array<Partial<FrageMitAntwort>> }>(
|
||||||
|
client,
|
||||||
|
model,
|
||||||
|
messages,
|
||||||
"segmentierung_unterfragen",
|
"segmentierung_unterfragen",
|
||||||
{
|
{
|
||||||
type: "object",
|
type: "object",
|
||||||
@@ -1107,7 +1138,17 @@ Vorgaben:
|
|||||||
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
|
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
|
||||||
sessionId,
|
sessionId,
|
||||||
{ operation: "segmentierung", label: fallbackLabel, onLlmCall },
|
{ 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]));
|
const byId = new Map((parsed.fragen ?? []).map((frage) => [String(frage.id ?? ""), frage]));
|
||||||
|
|
||||||
return fragen.map((frage) => {
|
return fragen.map((frage) => {
|
||||||
@@ -1139,10 +1180,7 @@ async function analyzeSWOT(
|
|||||||
sessionId?: string,
|
sessionId?: string,
|
||||||
onLlmCall?: SummarizeCompanyOptions["onLlmCall"],
|
onLlmCall?: SummarizeCompanyOptions["onLlmCall"],
|
||||||
): Promise<SWOTAnalyse> {
|
): Promise<SWOTAnalyse> {
|
||||||
const parsed = await createParsedStructuredCompletion<Partial<SWOTAnalyse>>(
|
const messages: Array<{ role: "system" | "user"; content: string }> = [
|
||||||
client,
|
|
||||||
model,
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content:
|
content:
|
||||||
@@ -1165,7 +1203,13 @@ Vorgaben:
|
|||||||
Bewerbungsdaten:
|
Bewerbungsdaten:
|
||||||
${JSON.stringify(data, null, 2)}`,
|
${JSON.stringify(data, null, 2)}`,
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
let parsed: Partial<SWOTAnalyse>;
|
||||||
|
try {
|
||||||
|
parsed = await createParsedStructuredCompletion<Partial<SWOTAnalyse>>(
|
||||||
|
client,
|
||||||
|
model,
|
||||||
|
messages,
|
||||||
"swot_analyse",
|
"swot_analyse",
|
||||||
{
|
{
|
||||||
type: "object",
|
type: "object",
|
||||||
@@ -1181,7 +1225,15 @@ ${JSON.stringify(data, null, 2)}`,
|
|||||||
"Could not parse SWOT analysis JSON",
|
"Could not parse SWOT analysis JSON",
|
||||||
sessionId,
|
sessionId,
|
||||||
{ operation: "swot", label: "SWOT-Analyse", onLlmCall },
|
{ 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[] =>
|
const toStringArray = (value: unknown): string[] =>
|
||||||
Array.isArray(value) ? value.map((item) => String(item).trim()).filter(Boolean) : [];
|
Array.isArray(value) ? value.map((item) => String(item).trim()).filter(Boolean) : [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user