diff --git a/modules/exams/rule-packs/default/contract.template.md b/modules/exams/rule-packs/default/contract.template.md index 6d75b9e..b0f9b20 100644 --- a/modules/exams/rule-packs/default/contract.template.md +++ b/modules/exams/rule-packs/default/contract.template.md @@ -15,6 +15,9 @@ ## Matching Rule +- The user may upload multiple Leistung files in the same message. +- Treat every uploaded file as one separate Leistung unless the user explicitly says that multiple files belong together. +- Process each Leistung independently and keep its correction data isolated. - The user does not need to provide a Leistung `chatRef`. - Resolve the matching Leistung `chatRef` from the submitted Leistung and the candidate data listed under `Chat References`. - Use visible candidate information from the submitted Leistung for this matching step. diff --git a/modules/exams/rule-packs/default/prompt.template.md b/modules/exams/rule-packs/default/prompt.template.md index fb09b83..b6d8579 100644 --- a/modules/exams/rule-packs/default/prompt.template.md +++ b/modules/exams/rule-packs/default/prompt.template.md @@ -1,10 +1,12 @@ You are assisting with a correction session based on a structured contract. Initial response: -- `Bereit. Bitte laden Sie die erste Leistung hoch.` +- `Bereit. Bitte laden Sie die erste Leistung oder mehrere Leistungen gleichzeitig hoch.` Session workflow (generic and strict): -- process exactly one Leistung at a time +- the user may upload multiple Leistung files in the same message +- treat every uploaded file as one separate Leistung unless the user explicitly says that multiple files belong together +- process one Leistung at a time internally, even when multiple files were uploaded together - keep each Leistung isolated; do not mix data between Leistungen - use the submitted Leistung only to resolve the matching candidate and Leistung `chatRef` - do not require the user to provide a Leistung `chatRef` @@ -56,6 +58,58 @@ Required import bundle fields: - include `importedTaskScores` - include optional fields such as `rulePack`, `evidence`, or `metadata` only when supported by the loaded contract, rules, and import bundle schema +JSON structure to output for `Zwischenexport`: +{ + "contract": , + "chatRef": "chat-0001", + "importedTaskScores": [ + { + "taskId": "task-1", + "points": 0, + "maxPoints": 0, + "scoringUnitId": "task-1.score", + "comment": "Kurzbegründung mit Bezug zur Leistung", + "confidence": 0.8, + "evidenceIds": ["evidence-1"] + } + ], + "evidence": [ + { + "id": "evidence-1", + "kind": "quote", + "value": "Kurzer Beleg aus der Leistung" + } + ], + "metadata": { + "generalComment": "Kurzer Gesamtkommentar" + } +} + +JSON structure to output for `Ende Korrektur`: +[ + { + "contract": , + "chatRef": "chat-0001", + "importedTaskScores": [] + }, + { + "contract": , + "chatRef": "chat-0002", + "importedTaskScores": [] + } +] + +Formatting notes for the JSON structures: +- replace `` with the full JSON object provided in the Contract JSON section below +- replace example `chatRef`, `taskId`, `scoringUnitId`, points, comments, evidence, and metadata with the resolved values +- use only task IDs and scoring-unit IDs that exist in the loaded contract +- omit optional fields when they are empty or unsupported +- for `Ende Korrektur`, output only the array and include one object per resolved Leistung + +## Contract JSON + +{{contractJson}} + ## Contract {{contractMarkdown}} diff --git a/modules/exams/src/rule-packs/default-pack.ts b/modules/exams/src/rule-packs/default-pack.ts index d4ad31c..97c8676 100644 --- a/modules/exams/src/rule-packs/default-pack.ts +++ b/modules/exams/src/rule-packs/default-pack.ts @@ -83,6 +83,9 @@ const DEFAULT_CONTRACT_TEMPLATE = `# Correction Session Contract ## Matching Rule +- The user may upload multiple Leistung files in the same message. +- Treat every uploaded file as one separate Leistung unless the user explicitly says that multiple files belong together. +- Process each Leistung independently and keep its correction data isolated. - The user does not need to provide a Leistung \`chatRef\`. - Resolve the matching Leistung \`chatRef\` from the submitted Leistung and the candidate data listed under \`Chat References\`. - Use visible candidate information from the submitted Leistung for this matching step. @@ -125,10 +128,12 @@ const DEFAULT_CONTRACT_TEMPLATE = `# Correction Session Contract const DEFAULT_PROMPT_TEMPLATE = `You are assisting with a correction session based on a structured contract. Initial response: -- \`Bereit. Bitte laden Sie die erste Leistung hoch.\` +- \`Bereit. Bitte laden Sie die erste Leistung oder mehrere Leistungen gleichzeitig hoch.\` Session workflow (generic and strict): -- process exactly one Leistung at a time +- the user may upload multiple Leistung files in the same message +- treat every uploaded file as one separate Leistung unless the user explicitly says that multiple files belong together +- process one Leistung at a time internally, even when multiple files were uploaded together - keep each Leistung isolated; do not mix data between Leistungen - use the submitted Leistung only to resolve the matching candidate and Leistung \`chatRef\` - do not require the user to provide a Leistung \`chatRef\` @@ -180,6 +185,58 @@ Required import bundle fields: - include \`importedTaskScores\` - include optional fields such as \`rulePack\`, \`evidence\`, or \`metadata\` only when supported by the loaded contract, rules, and import bundle schema +JSON structure to output for \`Zwischenexport\`: +{ + "contract": , + "chatRef": "chat-0001", + "importedTaskScores": [ + { + "taskId": "task-1", + "points": 0, + "maxPoints": 0, + "scoringUnitId": "task-1.score", + "comment": "Kurzbegründung mit Bezug zur Leistung", + "confidence": 0.8, + "evidenceIds": ["evidence-1"] + } + ], + "evidence": [ + { + "id": "evidence-1", + "kind": "quote", + "value": "Kurzer Beleg aus der Leistung" + } + ], + "metadata": { + "generalComment": "Kurzer Gesamtkommentar" + } +} + +JSON structure to output for \`Ende Korrektur\`: +[ + { + "contract": , + "chatRef": "chat-0001", + "importedTaskScores": [] + }, + { + "contract": , + "chatRef": "chat-0002", + "importedTaskScores": [] + } +] + +Formatting notes for the JSON structures: +- replace \`\` with the full JSON object provided in the Contract JSON section below +- replace example \`chatRef\`, \`taskId\`, \`scoringUnitId\`, points, comments, evidence, and metadata with the resolved values +- use only task IDs and scoring-unit IDs that exist in the loaded contract +- omit optional fields when they are empty or unsupported +- for \`Ende Korrektur\`, output only the array and include one object per resolved Leistung + +## Contract JSON + +{{contractJson}} + ## Contract {{contractMarkdown}} diff --git a/modules/exams/src/use-cases/export-correction-session.use-case.ts b/modules/exams/src/use-cases/export-correction-session.use-case.ts index bfb3c40..5134b80 100644 --- a/modules/exams/src/use-cases/export-correction-session.use-case.ts +++ b/modules/exams/src/use-cases/export-correction-session.use-case.ts @@ -95,6 +95,7 @@ function buildPromptArtifacts( ): string { return renderTemplate(rulePack.templates.prompt, { contractMarkdown, + contractJson: JSON.stringify(contract, null, 2), importBundleSchema: JSON.stringify(rulePack.importBundleSchema, null, 2), rulePackManifest: JSON.stringify(rulePack.manifest, null, 2), rulePackRules: JSON.stringify(rulePack.rules, null, 2), diff --git a/modules/exams/tests/export-correction-session.use-case.test.ts b/modules/exams/tests/export-correction-session.use-case.test.ts index 644c8df..85cd472 100644 --- a/modules/exams/tests/export-correction-session.use-case.test.ts +++ b/modules/exams/tests/export-correction-session.use-case.test.ts @@ -215,6 +215,31 @@ describe('ExportCorrectionSessionArtifactsUseCase', () => { expect(artifact.promptFile.content).toContain('"importedTaskScores"'); expect(artifact.promptFile.content).toContain('must return exactly one raw JSON array'); expect(artifact.promptFile.content).toContain('do not invent a wrapper object'); + expect(artifact.promptFile.content).toContain( + 'Bitte laden Sie die erste Leistung oder mehrere Leistungen gleichzeitig hoch.' + ); + expect(artifact.promptFile.content).toContain( + 'the user may upload multiple Leistung files in the same message' + ); + expect(artifact.promptFile.content).toContain('JSON structure to output for `Zwischenexport`'); + expect(artifact.promptFile.content).toContain('JSON structure to output for `Ende Korrektur`'); + expect(artifact.promptFile.content).toContain('"contract": '); + expect(artifact.promptFile.content).toContain('## Contract JSON'); + expect(artifact.promptFile.content).toContain('"id": "contract-session-session-2026-04-17"'); + expect( + artifact.promptFile.content.match(/"id": "contract-session-session-2026-04-17"/g) + ).toHaveLength(1); + expect(artifact.promptFile.content).toContain('"chatRef": "chat-0001"'); + expect(artifact.promptFile.content).toContain('"importedTaskScores": ['); + expect(artifact.promptFile.content).toContain('"evidence": ['); + expect(artifact.promptFile.content).toContain('"metadata": {'); + expect(artifact.promptFile.content).toContain('Kurzbegründung mit Bezug zur Leistung'); + expect(artifact.contractFile.content).toContain( + 'The user may upload multiple Leistung files in the same message.' + ); + expect(artifact.contractFile.content).toContain( + 'Process each Leistung independently and keep its correction data isolated.' + ); }); it('does not require users to provide Leistung chatRefs before evaluation', () => {