update
This commit is contained in:
101
backend/chat.py
101
backend/chat.py
@@ -11,6 +11,15 @@ funktioniert, Annehmen schreibt die Datei.
|
||||
Das Modell schreibt nie selbst in eine Datei. Es beschreibt den Sollzustand einer
|
||||
Karte; was sich dadurch ändert, rechnet der Code aus. Das ist dieselbe Beweislast wie
|
||||
bei der Prüfung und der Grund, warum ein Missverständnis hier nichts kaputt macht.
|
||||
|
||||
Gefragt wird über die Claude-CLI, nicht über MiniMax: das Gespräch soll planen helfen.
|
||||
Ein Modell, das nur Kataloge abarbeitet, gibt hier die Frage zurück statt Vorschläge.
|
||||
|
||||
Zum Planen gehört der Quelltext, nicht das Web. Wer eine Karte entwirft, muss wissen,
|
||||
was heute schon dasteht — das steht im Arbeitsverzeichnis und nirgends sonst. Fremdes
|
||||
Wissen kommt aus der Recherche und ist ein eigener Raum (`recherche.py`): was dort
|
||||
gefunden wird, bringt die Person selbst mit, statt dass es unbemerkt in einen Entwurf
|
||||
sickert.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -20,20 +29,28 @@ import features
|
||||
import parser
|
||||
import projekte
|
||||
import pruefung
|
||||
import verzeichnis
|
||||
|
||||
DATEI = ".chat.json"
|
||||
# So viele Wortmeldungen bleiben stehen. Mehr wäre teuer je Anfrage und hilft wenig:
|
||||
# was vor zwanzig Schritten besprochen wurde, steht längst auf den Karten.
|
||||
TIEFE = 30
|
||||
# Antworten in einem Rutsch. Ein Gespräch verträgt keine sechs Stimmen — es soll
|
||||
# schnell sein, und was falsch ist, sieht man am Vorschlag.
|
||||
TEMPERATUR = 0.3
|
||||
# schnell sein, und was falsch ist, sieht man am Vorschlag. Dafür das starke Modell:
|
||||
# hier wird geplant, nicht ein Katalog abgearbeitet.
|
||||
|
||||
SYSTEM = """Du entwickelst mit einer Person zusammen die Feature-Karten eines Projekts.
|
||||
|
||||
Eine Karte ist ein Auftrag: die Person will ein Problem gelöst haben. Die Lösung
|
||||
besteht aus Regeln und Funktionen. Eine Funktion ist etwas, das man für sich testen
|
||||
kann. Was nur die Umsetzung betrifft, gehört auf keine Karte.
|
||||
kann. Wie eine Sache gebaut ist, gehört auf keine Karte — wie man sie bedient, immer.
|
||||
|
||||
Bedienung ist nicht nur Maus und Menü. Einrichten, Installieren und Starten sind
|
||||
Funktionen wie jede andere; ihr Auslöser ist ein Kommando. Setze es wörtlich in
|
||||
Anführungszeichen, damit die Person es abtippen kann: `Befehl "make install"
|
||||
ausführen`. Was das Kommando innen tut, bleibt draußen.
|
||||
|
||||
{code}
|
||||
|
||||
{stil}
|
||||
|
||||
@@ -53,16 +70,55 @@ Antworte ausschließlich mit JSON:
|
||||
VOLLSTÄNDIG, so wie sie danach aussehen sollen. Der Stand oben hat dieselbe Form:
|
||||
kopiere die Karte von dort und ändere nur, was sich ändern soll. Jede Zeile, die
|
||||
du nicht wörtlich mit abschreibst, gilt als gestrichen.
|
||||
- art: „kern", wenn die Anwendung ohne die Karte keinen Sinn hätte, sonst „optional"
|
||||
- ziel, regeln und testen dürfen leer sein oder fehlen
|
||||
- art: „kern", wenn die Anwendung ohne die Karte nicht laufen kann, sonst „optional"
|
||||
- ziel und regeln dürfen leer sein oder fehlen
|
||||
- testen ist Pflicht bei jeder Funktion: der Ablauf, an dem man sie prüft. Weißt du
|
||||
ihn nicht, gehört die Funktion nicht auf die Karte — dann frag danach
|
||||
|
||||
Ist nichts zu ändern, ist karten leer. Ändere nur, wonach gefragt wurde; Karten,
|
||||
die niemand erwähnt hat, lässt du weg.
|
||||
|
||||
Frag nach, was du nicht weißt — aber leg trotzdem einen Entwurf daneben. Eine Frage
|
||||
ohne Vorschlag hilft niemandem: an einem Entwurf sieht die Person sofort, was du
|
||||
falsch verstanden hast. Erfinde dabei nichts, was nicht gesagt wurde; lass eine
|
||||
Funktion lieber knapp als ausgedacht."""
|
||||
Zwei Arten von Wortmeldung, und sie werden verschieden behandelt.
|
||||
|
||||
Eine Frage will eine Antwort, keinen Entwurf. „Wie ist das heute?", „Muss ich noch
|
||||
etwas tun?", „Wozu dient das?" — antworte in Prosa und lass karten leer. Ungefragt
|
||||
eine Karte anzufassen ist ein Fehler, auch wenn dir dabei etwas auffällt. Sag es in
|
||||
einem Satz und frag, ob du es aufnehmen sollst.
|
||||
|
||||
Ein Wunsch will einen Entwurf. Sobald etwas anders werden soll oder dazukommen soll,
|
||||
leg die Karten daneben, wie sie danach aussehen. Fehlt dir dafür eine Angabe, frag
|
||||
danach — aber leg trotzdem einen Entwurf daneben. An einem Entwurf sieht die Person
|
||||
sofort, was du falsch verstanden hast, und er gilt erst, wenn sie ihn annimmt.
|
||||
|
||||
Bei einem Wunsch gibst du die Frage nie unbeantwortet zurück. Wer nach Namen fragt
|
||||
— für Kommandos, Funktionen oder Karten — bekommt konkrete Namen, keine Gegenfrage.
|
||||
Nenne das Übliche für diese Art von Anwendung, sag in einem Satz, worauf du dich
|
||||
stützt, und frag danach, was daran nicht passt. „Das kann ich nicht vorschlagen" ist
|
||||
keine zulässige Antwort.
|
||||
|
||||
Gehört etwas nicht auf eine Karte, sag in einem Satz, wohin es stattdessen gehört.
|
||||
|
||||
Beim Bestand bleibst du bei der Wahrheit: über das, was das Projekt heute kann,
|
||||
erfindest du nichts. Ein Entwurf für etwas Neues kommt von dir."""
|
||||
|
||||
# Zwei Fassungen desselben Absatzes. Ohne eingestelltes Arbeitsverzeichnis gibt es
|
||||
# nichts zu lesen, und ein Angebot, das ins Leere greift, wäre schlimmer als keines:
|
||||
# das Modell behauptete sonst, nachgesehen zu haben.
|
||||
CODE_MIT = """Der Quelltext des Projekts liegt in deinem Arbeitsverzeichnis. Du darfst
|
||||
darin lesen und suchen, und du sollst es tun, sobald dein Entwurf davon abhängt, wie
|
||||
etwas heute gebaut ist: ob es eine Funktion schon gibt, wie eine Bedienstelle heißt,
|
||||
was ein Ablauf wirklich auslöst. Rate nicht, sieh nach.
|
||||
|
||||
Was du dort findest, ist ein Beleg, kein Kartentext. Wie eine Sache gebaut ist, gehört
|
||||
auf keine Karte — dorthin kommt nur, was die Person davon merkt. Stützt du dich auf
|
||||
eine Stelle, nenne im Antwortsatz die Datei.
|
||||
|
||||
Widerspricht der Code den Karten, schreib die Karte nicht still um. Sag in einem Satz,
|
||||
was du gefunden hast, und frag, welche Seite gilt."""
|
||||
|
||||
CODE_OHNE = """Für dieses Projekt ist kein Arbeitsverzeichnis eingestellt. Du kannst
|
||||
den Quelltext also nicht ansehen. Was das Projekt heute kann, weißt du nur aus den
|
||||
Karten unten — behaupte nichts darüber hinaus."""
|
||||
|
||||
# Die Formatregel steht auch hier, nicht nur im System-Prompt. Bei langen Antworten
|
||||
# vergaß das Modell sie sonst und schrieb reinen Fließtext — die ganze Wortmeldung
|
||||
@@ -80,6 +136,14 @@ Schreibe sie noch einmal, ausschließlich als das JSON-Objekt aus der Vorgabe
|
||||
keine Einleitung, kein Nachsatz, keine Code-Zäune."""
|
||||
|
||||
|
||||
def _arbeitsordner(projekt: str) -> str | None:
|
||||
"""Wo der Quelltext liegt — oder nichts, wenn dort nichts zu holen ist."""
|
||||
try:
|
||||
return str(verzeichnis.pruefe(verzeichnis.lies(projekt)))
|
||||
except (ValueError, FileNotFoundError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def _pfad(projekt: str):
|
||||
return projekte.wurzel(projekt) / DATEI
|
||||
|
||||
@@ -239,9 +303,11 @@ def vorschlaege(projekt: str, karten: list) -> list:
|
||||
if bestand is None:
|
||||
# Neue Karte: es gibt keine Stelle, an der man ansetzen könnte —
|
||||
# deshalb ein eigener Vorschlag, der die ganze Datei mitbringt.
|
||||
# `sektionen` liegt daneben: nur am Titel lässt sich nicht entscheiden,
|
||||
# ob die Karte taugt. Zerlegt wird hier, damit es keinen zweiten Parser gibt.
|
||||
raus.append({"feld": "neue_karte", "original": "", "vorschlag": titel,
|
||||
"roh": text, "ganzes_feld": True, "feature": "",
|
||||
"funktion": "", "regel": "Aus dem Gespräch",
|
||||
"roh": text, "sektionen": neu, "ganzes_feld": True,
|
||||
"feature": "", "funktion": "", "regel": "Aus dem Gespräch",
|
||||
"problem": "Neue Karte"})
|
||||
continue
|
||||
grund = f"Aus dem Gespräch: {titel}"
|
||||
@@ -256,24 +322,29 @@ async def frage(projekt: str, text: str) -> dict:
|
||||
text = (text or "").strip()
|
||||
if not text:
|
||||
raise ValueError("Nachricht ist leer")
|
||||
agents.pruefe_konfiguration()
|
||||
|
||||
bisher = verlauf(projekt)
|
||||
gespraech = "\n\n".join(f'{"Person" if e["rolle"] == "person" else "Du"}: {e["text"]}'
|
||||
for e in bisher) or "Noch nichts besprochen."
|
||||
# Der Ordner entscheidet über beides zugleich: was im Prompt angeboten wird und
|
||||
# was die CLI überhaupt zulässt. Ein toter Pfad zählt wie keiner — der Prozess
|
||||
# ließe sich darin nicht einmal starten.
|
||||
ordner = _arbeitsordner(projekt)
|
||||
# Beide Kataloge: der Chat schreibt Karten UND Funktionen. Vorher kannte er nur
|
||||
# die Kartenregeln und erfand Funktionstitel wie „Galerie im Modal" — ein
|
||||
# Bereich statt einer Handlung, und die Prüfung merkte es erst hinterher.
|
||||
system = SYSTEM.format(
|
||||
code=CODE_MIT if ordner else CODE_OHNE,
|
||||
stil="\n\n".join(pruefung._stil_lesen(b) for b in pruefung.BEREICHE),
|
||||
karten=_kartenstand(projekt))
|
||||
nachricht = NACHRICHT.format(verlauf=gespraech, text=text)
|
||||
kommando = agents.PLANUNG_KOMMANDO if ordner else agents.PLANUNG_OHNE_CODE
|
||||
|
||||
# Ein Ausrutscher beim JSON kostet sonst die ganze Wortmeldung. Zweiter Versuch
|
||||
# mit etwas mehr Streuung; danach fliegt der Fehler nach oben und ist sichtbar.
|
||||
# Ein Ausrutscher beim JSON kostet sonst die ganze Wortmeldung. Danach fliegt der
|
||||
# Fehler nach oben und ist sichtbar.
|
||||
daten = None
|
||||
for versuch in range(3):
|
||||
antwort = await agents.frage(system, nachricht, TEMPERATUR + 0.1 * versuch)
|
||||
antwort = await agents.frage_lokal(system, nachricht, kommando, ordner)
|
||||
try:
|
||||
daten = pruefung._json_lesen(antwort)
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user