Establish prompt engineering baseline with Gold Standard tests
- introduce Gold Standard evaluation corpus - document decision taxonomy - define prompt-engineering methodology - add regression workflow - establish Prompt Version 2 baseline - validate decision_simple, decision_deferred and decision_none
This commit is contained in:
@@ -19,6 +19,8 @@ from typing import Any
|
||||
|
||||
import requests
|
||||
|
||||
from src.meeting_lab.llm.prompts import build_extraction_prompt
|
||||
|
||||
|
||||
DEFAULT_MODEL = "qwen3:8b"
|
||||
DEFAULT_ENDPOINT = "http://localhost:11434/api/generate"
|
||||
@@ -35,28 +37,6 @@ EXTRACTION_CATEGORIES = (
|
||||
NORMALIZED_CHUNK_RE = re.compile(r"^(chunk_\d+)_normalized\.txt$")
|
||||
|
||||
|
||||
SYSTEM_INSTRUCTION = """
|
||||
Du extrahierst Informationen aus Meeting-Transkripten.
|
||||
|
||||
Arbeite ausschließlich mit dem vorgelegten Transkript.
|
||||
Verwende kein eigenes Fachwissen, keine Vermutungen und keine üblichen
|
||||
Funktionsweisen technischer Systeme.
|
||||
|
||||
Regeln:
|
||||
1. Erfinde nichts.
|
||||
2. Interpretiere technische Aussagen nicht über den Wortlaut hinaus.
|
||||
3. Korrigiere keine Aussagen anhand vermeintlichen Weltwissens.
|
||||
4. Wenn etwas widersprüchlich oder unklar ist, kennzeichne es als unklar.
|
||||
5. Übernimm wichtige technische Aussagen möglichst nah am Wortlaut.
|
||||
6. Nenne bei Fakten nach Möglichkeit den Sprecher.
|
||||
7. Ein Beschluss ist nur dann ein Beschluss, wenn im Text eine Einigung,
|
||||
Freigabe oder verbindliche Festlegung erkennbar ist.
|
||||
8. Eine Aufgabe ist nur dann eine Aufgabe, wenn eine Handlung und möglichst
|
||||
eine verantwortliche Person oder Organisation erkennbar sind.
|
||||
9. Gib ausschließlich gültiges JSON aus. Kein Markdown, keine Erläuterungen.
|
||||
""".strip()
|
||||
|
||||
|
||||
OUTPUT_SCHEMA = {
|
||||
"chunk": {
|
||||
"source_file": "string",
|
||||
@@ -162,29 +142,7 @@ def parse_args() -> argparse.Namespace:
|
||||
|
||||
|
||||
def build_prompt(source_name: str, transcript: str) -> str:
|
||||
schema_text = json.dumps(OUTPUT_SCHEMA, ensure_ascii=False, indent=2)
|
||||
|
||||
return f"""{SYSTEM_INSTRUCTION}
|
||||
|
||||
Quelldatei:
|
||||
{source_name}
|
||||
|
||||
Erwartete JSON-Struktur:
|
||||
{schema_text}
|
||||
|
||||
Hinweise zur Ausgabe:
|
||||
- Alle obersten Schlüssel müssen vorhanden sein.
|
||||
- Verwende leere Listen, wenn keine Einträge vorhanden sind.
|
||||
- Verwende null, wenn Verantwortliche, Sprecher oder Termine nicht erkennbar sind.
|
||||
- "evidence" muss sich eng am Transkript orientieren.
|
||||
- Ersetze technische Aussagen niemals durch eine vermeintlich korrektere Erklärung.
|
||||
- Confidence-Werte sind ausdrücklich nicht erwünscht.
|
||||
|
||||
TRANSKRIPT:
|
||||
--- BEGINN TRANSKRIPT ---
|
||||
{transcript}
|
||||
--- ENDE TRANSKRIPT ---
|
||||
"""
|
||||
return build_extraction_prompt(source_name, transcript, OUTPUT_SCHEMA)
|
||||
|
||||
|
||||
def call_ollama(
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||||
PROMPTS_DIR = PROJECT_ROOT / "prompts"
|
||||
|
||||
|
||||
def load_prompt(name: str, prompts_dir: Path = PROMPTS_DIR) -> str:
|
||||
path = prompts_dir / name
|
||||
return path.read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def load_existing_prompts(
|
||||
names: Iterable[str],
|
||||
prompts_dir: Path = PROMPTS_DIR,
|
||||
) -> list[str]:
|
||||
prompts: list[str] = []
|
||||
for name in names:
|
||||
text = load_prompt(name, prompts_dir)
|
||||
if text:
|
||||
prompts.append(text)
|
||||
return prompts
|
||||
|
||||
|
||||
def build_extraction_prompt(
|
||||
source_name: str,
|
||||
transcript: str,
|
||||
output_schema: dict[str, Any],
|
||||
task_prompt_names: Iterable[str] = ("decisions.md",),
|
||||
prompts_dir: Path = PROMPTS_DIR,
|
||||
) -> str:
|
||||
schema_text = json.dumps(output_schema, ensure_ascii=False, indent=2)
|
||||
prompt_parts = [
|
||||
load_prompt("common.md", prompts_dir),
|
||||
*load_existing_prompts(task_prompt_names, prompts_dir),
|
||||
f"""Quelldatei:
|
||||
{source_name}
|
||||
|
||||
Erwartete JSON-Struktur:
|
||||
{schema_text}
|
||||
|
||||
TRANSKRIPT:
|
||||
--- BEGINN TRANSKRIPT ---
|
||||
{transcript}
|
||||
--- ENDE TRANSKRIPT ---""",
|
||||
]
|
||||
return "\n\n".join(part for part in prompt_parts if part).strip() + "\n"
|
||||
|
||||
Reference in New Issue
Block a user