Syntax
Der Block
Ein Block ist eine Quarto-Fence der Sprache famulus: YAML-Präambel, Trenner ---, danach die Instruktion als Freitext.
Die Präambel ist vollständig. Der Block liest keinen Kontext, den weder die Präambel noch die Paketdatei famulus.yml nennt. Ein Schlüssel in der Präambel ersetzt denselben Schlüssel der Paketdatei. Listen werden ersetzt, nicht aneinandergereiht.
Die YAML-Präambel der .fam-Datei bleibt die Quarto-Präambel des Dokuments; ihr Schlüssel famulus trägt id, uses und chapters (siehe Projekt).
Pflicht id, und genau eines von subtask und partiturs
```{famulus}
---
id: <eindeutig im Paket>
# Identität, Scope, Datenzugriff, Plan, Artefakte, Prüfung
---
Instruktion als Freitext. Platzhalter {{ref:<id>.<name>}} werden vor dem Lauf ersetzt.
```1 Identität und Ausführung
| Attribut | Typ | Pflicht | Vorgabe | Abbildung |
|---|---|---|---|---|
id |
Zeichenkette, eindeutig im Paket | ja | — | RunRequest.goal_id; Name des Log-Ordners |
client |
Modell-Tag | nein | default_model() |
RunRequest.model |
depends_on |
Liste von Block-id oder Verweisen |
nein | leere Liste | Reihenfolge; ein Verweis mit Version macht den Block von dieser Version abhängig (Referenzen) |
rerun |
if-missing, always, never |
nein | if-missing |
Adapter, vor dem Lauf |
rerun |
if-stale, always, never |
nein | if-stale |
wann der Runner den Block ausführt (Projekt) |
default_model() liest die Umgebungsvariable ORCH_PROVIDER und sonst ollama:glm-5.3:cloud. Query-Blöcke setzen client auf ein Modell der Allowlist. Der Default der Query-Pipeline ist gemini-3.5-flash-lite.
depends_on verlangt, dass jeder genannte Block ein manifest.json hat. Fehlt es, bricht der Adapter ab und schreibt kein Artefakt. rerun: if-missing überspringt den Lauf, wenn manifest.json schon vorliegt und seine Eingaben gleich geblieben sind: Präambel, Instruktion und Größe und Änderungszeit jeder Datei, die der Block liest (duckdb-Anhänge, Pfade in read_json_auto(…), read_parquet(…), read_csv(…), bei LWgrammar-Schritten der Regelspeicher). always spielt den Block neu. never verlangt das vorhandene Log und bricht ab, wenn es fehlt. famulus-notebook run --force spielt alle Blöcke eines Pakets neu, etwa nach einer Änderung am Code einer Fachkraft, die keine Datei des Blocks berührt.
2 Formulierung und Sprachinstitution
| Attribut | Typ | Pflicht | Vorgabe | Abbildung |
|---|---|---|---|---|
guard |
{task, lang, author, family, text, evidence} |
nein | keine Prüfung | LWgrammar lw_evaluate nach dem Oracle |
partiturs[].id: lwgrammar:<werkzeug> |
Werkzeug der Sprachinstitution | nein | — | MCP-Aufruf an Port 5116 |
guard stellt den Text, den ein Block erzeugt, unter eine Auftragsform der Sprachinstitution (notebook-text als Vorgabe, synthesize-answer für die Antwort der Abfrage-Pipeline). text nennt das Feld; Vorgabe ist die Antwort, die die Ergebniskarte zeigt. Zahlen und Zitate des Textes müssen in seinen Belegen stehen; Belege sind die Zeilen und Messwerte des Blocks, sofern evidence sie nicht ersetzt. Ein nicht bestandener Befund macht aus pass das Urteil fail; ist die Sprachinstitution nicht erreichbar, endet der Block als Fehler, und der Text bleibt als ungeprüft markiert. Ein Block ohne erzeugten Text vermerkt das, statt als geprüft zu gelten. Der Viewer zeigt das Ergebnis als Leiste unter dem Urteil.
Ein Schritt lwgrammar:<werkzeug> ruft ein Werkzeug der Sprachinstitution als Partitur-Schritt, etwa lwgrammar:lw_evaluate, lwgrammar:lw_sense_history oder lwgrammar:lw_situate_specimen. text_from: <spalte> setzt als text die Zeilen dieser Spalte aus dem vorigen Schritt; rows_from: <schlüssel> zeigt diese Liste der Antwort als Tabelle. Skalare Felder der Antwort und die Längen ihrer Listen (n_<schlüssel>) sind Messwerte für das Oracle. Beispiele: LWgrammar: Formulierungsprüfung, Kepler: anima und vis.
3 Scope
Diese Schlüssel werden zum Setting (ai_query.setting.Setting) und, soweit das Setting sie nicht trägt, zu inputs.
| Attribut | Typ | Pflicht | Vorgabe | Abbildung |
|---|---|---|---|---|
collections |
Liste von Sammlungsnamen | nein | aus famulus.yml |
Setting.collections |
locked |
Boolesch | nein | false |
Setting.locked |
reading_doc |
doc_id oder leer |
nein | leer | Setting.reading_doc |
documents |
Liste von doc_id |
nein | alle Dokumente der Sammlungen | inputs.documents |
evidence |
grounding, secondary, both |
nein | grounding |
inputs.evidence |
language |
la, de, en, fr oder Liste |
nein | keine Sprachfilterung | Filter auf stz_sentences.lang |
years |
{from, to} |
nein | offen | inputs.years; bei Briefregistern date_when |
persons |
Liste kanonischer Namen | nein | leer | inputs.persons, nur über ent_canonical |
lemmata |
Liste von Lemmata | nein | leer | inputs.lemmata |
counting_mode |
canonical, family, family+adj |
bei Lemma-Zählung ja | — | inputs.counting_mode |
person_id |
Zeichenkette | nein | leer | Setting.person_id |
project |
Zeichenkette | nein | leer | Setting.project |
locked: true lässt die genannten Sammlungen über einen Anker im Fragetext gewinnen. Eine Einzelsammlungs-Arbeit setzt es.
evidence: grounding liest die in collections genannten Primärsammlungen. secondary liest die Geschwisterordner mit dem Suffix -sec. both liest beide und hält die Belege auseinander: Grounding Evidence aus den Primärdokumenten, Sekundärevidenz aus der Sekundärliteratur. Die Primärsammlung Pirckheimer heißt pirckheimer, die Sekundärsammlung pirckheimer-sec. Kepler heißt kepler-prim und kepler-sec.
Personennamen stehen in persons und werden über ent_canonical und die Schreibvarianten der Entitätenregistrierung aufgelöst. Sie gehören nicht in lemmata. Ein Lemma-Zählblock nennt counting_mode. canonical zählt das Lemma. family nimmt registrierte Varianten hinzu. family+adj nimmt zusätzlich adjektivische Lemmata hinzu. naturalis ist ein eigenes Adjektivlemma und keine Variante von natura.
Die Satzidentität in allen Filtern ist das Tripel (doc_id, pidx, sent_idx). sent_idx beginnt auf jeder Seite neu.
4 Datenzugriff
duckdb ist eine Liste von Anhängen. Jeder Eintrag hat alias, path und read_only. read_only ist true. Der Adapter öffnet die Verbindung für die Lesung und schließt sie danach.
duckdb:
- alias: main
path: $PDAI_COLLECTIONS/pirckheimer/pirckheimer.duckdb
read_only: true
- alias: stz
path: $PDAI_COLLECTIONS/pirckheimer/pirckheimer_stanza.duckdb
read_only: true
- alias: lemma
path: $PDAI_COLLECTIONS/kepler-prim/lemma_index.parquet
read_only: true$PDAI_COLLECTIONS ist die Sammlungswurzel, Vorgabe /Volumes/PDAI-1/pdai-collections. Ein Parquet-Pfad wird als read_parquet gelesen, eine DuckDB-Datei mit ATTACH … (READ_ONLY).
Datei unter <coll>/ |
Inhalt, den ein Block lesen darf |
|---|---|
<coll>_stanza.duckdb |
stz_sentences(doc_id, pidx, sent_idx, lang, text), stz_tokens mit tid, stz_entities |
lemma_index.parquet |
lemma, upos, doc_id, pidx, sent_idx, tid |
<coll>.duckdb |
letters_resolved, ent_canonical(ent_id, canonical, kind), letter_pages |
status.json |
overall, artifacts, gaps |
assessment/grounding_index.duckdb |
collection_capabilities, person_registry, letter_pairs, collection_aliases |
Der letzte Pfad liegt im ai-query-Arbeitsverzeichnis, nicht in der Sammlung. Die Präambel nennt ihn mit absolutem Pfad, wenn ein Block ihn braucht.
5 Plan
Genau eines von subtask und partiturs.
subtask wählt die Partitur ai-query-phase1. Die Instruktion wird RunRequest.goal und inputs.instruction. Der Subtask-Name wird inputs.subtask.
subtask: correspondence-countRegistrierte Werte, Tabelle subtasks in agent_memory.duckdb:
subtask |
Frageform | Typisches Artefakt |
|---|---|---|
correspondence-count |
Anzahl der Briefpartner oder Briefe | report |
correspondence-roster |
Liste der Briefpartner | roster-table |
letter-subject |
Gegenstand der Briefe | markdown |
mention-in-letters |
Wer nennt eine Person in Briefen an wen | roster-table |
terminology-find |
Belege eines Lemmas oder Terminus | kwic |
concept-statements |
Aussagen über einen Begriff | markdown |
statement-mention |
Alias von concept-statements |
markdown |
source-attestation |
Belegstelle einer Quelle | citation-list |
sentence-mention |
Sätze, die eine Zeichenkette enthalten | kwic |
cooccurrence |
Sätze mit mehreren Lemmata | kwic |
manuscript-inventory |
Manuskripte: Datum, Umfang, Titel | roster-table |
register-inventory |
Bestand eines Registers | roster-table |
entity-mention-count |
Anzahl der Nennungen einer Entität | report |
attestation kommt in expected_route des Golden-Manifests vor und ist kein Wert von subtask. Lemma-Belege dieser Route schreiben die Blöcke als terminology-find oder cooccurrence.
partiturs ist eine Liste. Jeder Eintrag ist ein eigener Lauf, in Listenreihenfolge. Der nächste Lauf beginnt, nachdem das Artefakt des vorigen im Log steht. Erreicht ein Lauf das Ziel nicht, endet die Kette.
| Schlüssel | Typ | Pflicht | Abbildung |
|---|---|---|---|
id |
Register-Id, goal_path, oder adapter-lokaler Schritt |
ja | partitur_path oder Adapter-Schritt |
setting |
Pfad einer Setting-Datei | nein | RunRequest.setting |
input |
Abbildung von Namen auf Werte | nein | RunRequest.inputs |
artefact |
name aus artefacts |
nein | Zieldatei dieses Laufs |
Ein Wert in input darf ein Querverweis sein: {ref: <id>.<name>}. Die Auflösung steht in der Projektbeschreibung.
partiturs:
- id: query/historical/stage-one
setting: settings/pirckheimer-1520-1530.setting.yaml
input:
subtask: correspondence-roster
artefact: rosterZwei Ids interpretiert der Adapter selbst. Sie stehen nicht im Register.
| Id | Verhalten |
|---|---|
duckdb-read |
Führt input.sql auf den Anhängen aus duckdb aus. $PDAI_COLLECTIONS wird auch in input.sql ersetzt. Nur lesend, Verbindung kurzlebig. Schreibt eine Tabelle. |
artifact:<kind> |
Ruft den Builder dieses kind in ai_query.pipeline.artifact auf. Eingabe ist input. Kein Conductor-Lauf. |
6 Zielentfaltung
Ein Block mit ziel: entfalten wird nicht als Partitur gespielt, sondern als Ziel entfaltet (Protokoll famulus-zielentfaltung): der Evidenzraum wird gefiltert und bewiesen, die Fundlage gelesen, das Ziel normalisiert, dann öffnet ein Planer Teilziele nach Befund, Anfrageform und Budget, und das Urteil ist graduiert. Die Schlüssel des Vorspanns:
| Schlüssel | Werte | Pflicht | Standard | Wirkung |
|---|---|---|---|---|
ziel |
entfalten |
ja | — | wählt die Entfaltung |
form |
erwaehnung, korrespondenz, eponym, thema |
nein | aus der Frage gelesen | die Anfrageform: Slots, Teilziele, kritische Fragen (famulus_proof/entfaltung/rules/formen.yaml) |
slots |
{verfasser: …, erwaehnte_person: …, zeitraum: …} |
nein | aus der Frage | Werte der Slots vom Eigentümer; mit form braucht der Filter keinen Modellaufruf |
bind |
{slot: ind_id} |
nein | Register | Bindung eines Namens an ein Einzelding |
lesart |
{schluessel: Art} |
nein | — | die Lesung des Eigentümers für einen Kandidaten (geht Regel, Cache und Modell vor) |
verfeinerung |
{streng: b2}, {offen: b2}, {knoten: …, wert: …} |
nein | — | die Antwort auf eine Rückfrage des Filters |
budget |
{zeit_s, modellaufrufe, kandidaten_max} |
nein | 300 s, 200, 1000 | was die Entfaltung ausgeben darf; was nicht passt, wird zurückgestellt und zählt als Rest |
rest_max |
Zahl 0–1 | nein | 0.05 | der Anteil offener Kandidaten, den validiert-mit-rest noch trägt |
annahmegrad_verfasser |
schwach, mittel, stark, gewiss |
nein | mittel |
wie sicher der Verfasser eines Belegs belegt sein muss (Leiter: Register gewiss, Rohangabe stark, Mappe und Randsatz mittel, Regest und Bestand schwach); darunter bleibt der Kandidat im Rest |
abgeschaltet |
Liste von Teilzielen | nein | leer | Teilziele, die dieser Lauf nicht spielt |
erwartung |
Pfad einer YAML-Datei | nein | — | das Gate: Slots, Bedingungen, Kanäle, urteil: {in: […]}, grad_min, rest_max, Recall und Präzision gegen eine Referenzsuche |
Das Urteil ist validiert, validiert-mit-rest, Lücke, nicht validiert oder Rückfrage. Das Oracle vergleicht validiert (nur das exakte Urteil), validiert_mit_rest (beide gültigen Urteile), grad, rest_quote, n_rest, n_zurueckgestellt, rueckfrage, n_belege, n_unaccounted, gate_match. Ein Block mit dem Status Rückfrage nennt die Frage des Filters; die Antwort steht im Vorspann (verfeinerung: oder form: mit slots:).
Die Abschnitte des Protokolls (artifact:entfaltung-teil, teil:): ziel, evidenzbasis, trichter, kanaele, entfaltung, planung, ausschluss, urteil, rest.
7 Prüfungen
Ein Block mit pruefen: satz lässt einen Satz eines Aufsatzes vom Lektor prüfen (satz: {paper, hash, text}), ein Block mit pruefen: messwert eine Messaussage gegen einen registrierten Messkörper (aussage: {koerper, objekt, groesse, methode, wert, toleranz}); beide legen ihr Urteil als Bericht famulus-enfolding/1 ab (Sachliche Validität).
8 Artefakte
artefacts ist eine Liste von {name, kind, file}. name ist der Name im Querverweis. file liegt unter artefacts/ der jeweiligen Version (_famulus/store/<notebook>/<id>/vNNNN/). report.json schreibt der Adapter bei jedem Lauf zusätzlich in denselben Versionsordner, auch wenn die Liste es nicht nennt.
kind |
Builder heute | data |
|---|---|---|
report |
_build_report |
question, answer, verdict, method, counts, findings, by_document, specification, provenance |
roster-table |
_build_roster_table |
entries mit name, canonical, variants, kind, count, n_docs, doc_ids |
kwic |
_build_kwic |
rows mit doc_id, page, text, url |
citation-list |
_build_citation_list |
rows mit doc_id, collection, n_sentences, excerpts, url |
d3-graph |
_build_d3_graph |
nodes, edges; Kraftlayout |
proof-structure |
_build_proof_structure |
nodes, edges, verdict, synthesis |
markdown |
direkt aus der Synthesis | content |
table |
adapter-lokal aus duckdb-read |
Zeilen und Spaltennamen |
json |
adapter-lokal, das Vertragsergebnis unverändert | Objekt |
figure |
Datei, die ein Lauf als Abbildung schreibt | Pfad der Abbildung |
timeline |
keiner | Wahl fällt in artifact.py auf markdown |
Ein Befund in report.json trägt kind, collection, doc_id, pidx, sent_idx, page, text, url. page ist pidx + 1. Die Viewer-URL hat die Form <basis>/<collection>/<doc_id>?p=<page>. Ein Absatzindex ist in diesem Schema nicht vorhanden.
artefacts:
- name: roster
kind: roster-table
file: roster.json
- name: netz
kind: d3-graph
file: netz.json9 Prüfung
golden nennt eine golden_id aus agent_memory.duckdb, Tabelle golden_ground. oracle nennt die erwartete Antwort an Ort und Stelle, in denselben Formen wie expected_answer. Verglichen wird nur mit oracle: golden ist ein Verweis auf die Zeile und lädt deren Erwartung nicht. famulus-notebook goldens überträgt die Erwartung beim Erzeugen in oracle; ein von Hand geschriebener Block trägt sie selbst. Der Vergleich steht in verdict.json der Version.
| Form | Beispiel | Vergleich |
|---|---|---|
| Ganze Zahl | n_correspondents: 75 |
Gleichheit |
| Spanne | n_sentences: {min: 15} |
Wert liegt in der Spanne |
| Teilzeichenkette | subject_contains: saphea |
Die Antwort enthält die Zeichenkette |
| Liste | terms: [saphea, horologium] |
Überlappung mit der Ergebnisliste |
Schlüssel note und status im gespeicherten Oracle werden beim Vergleich übersprungen, wie in golden.py.
golden: golden-c782fab71c
oracle:
n_correspondents: 75verdict.json hat die Schlüssel id, golden, status, oracle, observed. status ist pass, fail, gap oder error. gap gilt, wenn die Golden-Zeile eine anerkannte Fähigkeitslücke ist und der Lauf diese Lücke meldet. Ein stilles leeres Ergebnis ist error.
10 Instruktion
Der Text nach der schließenden --- ist die Handlungsanweisung. Er wird RunRequest.goal. Platzhalter {{ref:<id>.<name>}} ersetzt der Adapter vor dem Aufruf durch den absoluten Pfad der Artefaktdatei. Ein Platzhalter ohne Logeintrag ist ein unaufgelöster Verweis.
11 Fehler
Der Adapter bricht vor dem Conductor ab, schreibt verdict.json mit status: error und kein Artefakt.
| Lage | Meldung |
|---|---|
id fehlt oder ist im Paket doppelt |
Name der Präambel |
subtask und partiturs beide gesetzt oder beide leer |
die Ausschlussregel |
subtask steht nicht in der Registrierung |
der unbekannte Name |
ref oder der Platzhalter {{ref:…}} hat kein Artefakt |
die unaufgelöste Id und der Name |
$PDAI_COLLECTIONS oder eine genannte Datei ist nicht da |
der Pfad |
counting_mode fehlt bei einer Lemma-Zählung |
der Block-id |
| Eine erforderliche Partitur-Eingabe bleibt ungebunden | der Eingabename |
Ein Lauf, der das im Vertrag beschriebene Ergebnis nicht erreicht, beendet die Kette an dieser Stelle. Die Trace bleibt im Log. Ein Artefakt dieses Laufs entsteht nicht.