KI-Agent: Änderungen erscheinen in Live-Vorschau und PDF
- Dateien des Agenten werden sofort committet (die Vorschau liest in anderer Sitzung; vorher KeyError/unverändert) - Beispielprotokoll der PDF-Vorschau gehört zum Arbeitsstand: neues Tool set_sample, run_prompt_test setzt das LLM-Ergebnis als Beispiel; im Vorschlag und bei der Freigabe übernommen - Vorschau wechselt bei PDF-relevanten Änderungen automatisch auf die PDF-Ansicht - Regressionstests inkl. echter SSE-/Freigabe-Strecke Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,11 +19,11 @@ from app.services import llm, store
|
||||
MAX_TURNS = 14
|
||||
|
||||
# Arbeitsstände laufender/letzter Agent-Läufe (für die Live-Vorschau: source = "ws:<run_id>"). Nur im Speicher, höchstens 24.
|
||||
WORKSPACES: "OrderedDict[str, tuple[dict, dict]]" = OrderedDict()
|
||||
WORKSPACES: "OrderedDict[str, tuple[dict, dict, str | None]]" = OrderedDict()
|
||||
|
||||
|
||||
def remember(run_id: str, ws: Workspace) -> None:
|
||||
WORKSPACES[run_id] = (deepcopy(ws.cfg), dict(ws.files))
|
||||
WORKSPACES[run_id] = (deepcopy(ws.cfg), dict(ws.files), ws.sample)
|
||||
WORKSPACES.move_to_end(run_id)
|
||||
while len(WORKSPACES) > 24:
|
||||
WORKSPACES.popitem(last=False)
|
||||
@@ -39,6 +39,7 @@ Arbeitsweise:
|
||||
- Lies zuerst, was du ändern willst (read_file / read_core_default). Ist eine Datei nicht im Tenant, gilt der Core-Standard – lege bei Bedarf eine eigene Kopie an (write_file) und ändere diese.
|
||||
- Ändere minimal und gezielt (edit_file statt write_file, wenn möglich). Behalte bestehende Prompt-Struktur, Platzhalter (@@ORG@@, @@ORG_SHORT@@, @@ORG_PROFILE@@) und Metadatenblöcke (===TERMINART=== …) bei, sofern nicht ausdrücklich anders gewünscht.
|
||||
- Rufe nach Änderungen validate auf (echtes Core-Rendering). Bei PDF-Änderungen zusätzlich render_pdf. Beheb Fehler, bevor du antwortest.
|
||||
- Die PDF-Vorschau rendert ein BEISPIELPROTOKOLL. Prompt-Änderungen wirken sich darauf nicht aus: Willst du ihre Wirkung im PDF zeigen, rufe run_prompt_test auf (das Ergebnis wird zum Beispiel) – für Layout-Tests mit passendem Inhalt nutze set_sample.
|
||||
- Hook-Code (overrides.py) nur, wenn es mit Daten/Flags nicht geht; schreibe ihn knapp und nutze nur die Hooks build_prompt, transform_transcript, pdf_context, extra_routes.
|
||||
- Dateien mit personenbezogenen Daten (Namenslisten) sind gesperrt. Secrets, Keys und Zugangsdaten kennst du nicht und fasst du nie an.
|
||||
- Schreibe Prompt-Inhalte auf Deutsch, im Ton der vorhandenen Datei. Erfinde keine Fakten über den Kunden.
|
||||
@@ -89,7 +90,7 @@ def run(db: Session, tenant: Tenant, user_message: str, backend=None) -> Iterato
|
||||
ws = Workspace(db, tenant)
|
||||
run_id = uuid.uuid4().hex[:12]
|
||||
remember(run_id, ws)
|
||||
last_sig = json.dumps([ws.cfg, ws.files], sort_keys=True)
|
||||
last_sig, last_snap = json.dumps([ws.cfg, ws.files, ws.sample], sort_keys=True), ws.snapshot()
|
||||
runner = ToolRunner(ws)
|
||||
system = SYSTEM + "\n\n" + _context(tenant, ws)
|
||||
messages = _history(db, tenant.id)
|
||||
@@ -130,11 +131,12 @@ def run(db: Session, tenant: Tenant, user_message: str, backend=None) -> Iterato
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
yield {"type": "step", "tool": name, "arg": arg, "result": short, "error": res.startswith("FEHLER")}
|
||||
sig = json.dumps([ws.cfg, ws.files], sort_keys=True)
|
||||
sig = json.dumps([ws.cfg, ws.files, ws.sample], sort_keys=True)
|
||||
if sig != last_sig: # Arbeitsstand hat sich geändert → Live-Vorschau neu rendern lassen
|
||||
last_sig = sig
|
||||
paths = ws.changed_paths(last_snap)
|
||||
last_sig, last_snap = sig, ws.snapshot()
|
||||
remember(run_id, ws)
|
||||
yield {"type": "changed", "run": run_id}
|
||||
yield {"type": "changed", "run": run_id, "paths": paths}
|
||||
results.append({"type": "tool_result", "tool_use_id": c["id"], "content": res})
|
||||
messages.append({"role": "user", "content": results})
|
||||
else:
|
||||
@@ -162,6 +164,8 @@ def apply_proposal(db: Session, tenant: Tenant, proposal: dict) -> None:
|
||||
store.set_file(db, tenant, path, store.get_blob(db, sha))
|
||||
for path in proposal.get("deleted") or []:
|
||||
store.delete_file(db, tenant, path)
|
||||
if proposal.get("sample") is not None:
|
||||
tenant.preview_sample = proposal["sample"]
|
||||
|
||||
|
||||
def proposal_content(db: Session, tenant: Tenant, proposal: dict) -> tuple[dict, dict]:
|
||||
|
||||
Reference in New Issue
Block a user