Quillwright / CONTEXT.md
Aarya2004
Deploy: sync hosted Space to local app (chat, document capture, Modal backends, pages, mobile/QR)
47b2a99
|
Raw
History Blame Contribute Delete
13.7 kB
# Quillwright
An on-device, human-supervised AI agent for tradespeople that turns a field capture (photos + voice note) into a finished, editable estimate or service report β€” the paperwork techs otherwise hand-write after a job.
## Language
**Capture**:
The raw multimodal input from a job β€” photos plus a voice note (and optional typed note) β€” normalized into one object the agent works from.
_Avoid_: upload, input, submission
**Observation**:
A single fact the perception model extracts from a photo β€” an item, part, damage, or piece of read text (e.g. a nameplate model number).
_Avoid_: detection, finding, result
**Document Capture**:
A second capture path: a document the tech or customer hands over β€” a spec sheet, supplier quote, or old written estimate β€” read by the Extraction Model Role (Nemotron Parse) into structured text + tables that feed the same Estimate pipeline. Distinct from a job-site photo (different input, different model); entered via its own control, never auto-classified.
_Avoid_: scan, OCR (Document Capture is the agent-facing capability; OCR is one mechanism)
**Proposed Line Item**:
A Line Item whose price came from a Document Capture, not the catalog β€” surfaced to the human as a proposal to confirm or edit before it enters the Estimate. The document is the _source_, but the price only becomes customer-facing once a human confirms it (an Agent Pause), preserving Facts-from-Tools.
_Avoid_: draft line (a Line Item is already a draft; "Proposed" specifically means awaiting human confirmation of a document-read price)
**Agent Brain**:
The orchestrator model that runs the plan β†’ act β†’ self-check loop and decides which Tools to call. There is exactly one.
_Avoid_: orchestrator, controller, LLM
**Tool**:
A callable capability the Agent Brain invokes. Tools are where facts (prices, math) come from; the Brain does routing and judgment, not arithmetic. The v1 set: `perceive`, `search_past_jobs`, `lookup_price`, `compute`, `draft_line_item`, `flag_for_human`, `translate`, `update_profile`.
_Avoid_: function, plugin, skill
**Facts-from-Tools**:
The correctness rule: any number that reaches the customer (price, quantity, markup, tax, total) must come from a Tool (`lookup_price`, `compute`) or user-confirmed data β€” never from the Agent Brain's free generation. This extends to **Document Capture**: a price read off a document by Nemotron Parse is a _model_ output, so it is never used directly β€” it becomes a Proposed Line Item the human confirms (the document is the source; the human is the gate).
_Avoid_: no-hallucination (too vague)
**Line Item**:
One priced row of an Estimate β€” description, quantity, unit, rate, subtotal.
_Avoid_: entry, row, charge
**Estimate**:
The hero Deliverable β€” an itemized, priced quote for a job. Editable draft, not a system-of-record invoice.
_Avoid_: quote, invoice, bill
**Service Report**:
The secondary Deliverable β€” a written record of findings, work done, and captioned photos. (Stretch beyond the Estimate.)
_Avoid_: inspection report (a regulated cousin we are deliberately not building)
**Deliverable**:
The finished work product the agent produces β€” an Estimate or a Service Report β€” that the human edits and exports.
_Avoid_: output, document, artifact
**Mode**:
The execution tier the user selects, distinguished by model class and data path (both hosted on Modal). **Private Stack** resolves every Model Role to an open small model (≀32B, no third-party AI APIs β€” the contest's "no cloud APIs" ask). **Best Stack** resolves roles to larger hosted sponsor models for higher quality. The two modes differ ONLY in which model each role resolves to β€” same loop, tools, UI, and Deliverable.
_Avoid_: Local/Connected (renamed β€” "Local" wrongly implied on-device inference; a hosted Gradio Space runs models server-side), offline/online, tier
**Airplane-Mode Proof**:
A separately filmed demonstration of the Private Stack running on a real local machine (via llama.cpp) with the network actually off β€” the honesty anchor for "small models genuinely work offline," distinct from the Modal-hosted Space.
_Avoid_: offline mode, local mode (those wrongly describe the hosted Space)
**Model Role**:
A named slot in the pipeline (Perception, Audio, Agent Brain, Multilingual, Embedding, Visual) that resolves to a concrete model depending on Mode.
_Avoid_: model (a Model Role is the slot; a model is what fills it)
**Trace**:
The recorded sequence of the Agent Brain's steps (thought, Tool call, result, confidence) β€” shown live in the UI and exportable to the Hub.
_Avoid_: log, history
**Workspace**:
The main two-panel screen β€” Trace streaming on the left, Deliverable building on the right β€” where the human watches and supervises the run. Laptop-first, gracefully stacked on mobile.
_Avoid_: dashboard, canvas, screen
**Run**:
One execution of the Agent Brain over a single Capture, from plan to finished Deliverable.
_Avoid_: session, job (a "job" is the real-world work; a Run is one agent execution over its Capture)
**Agent Pause**:
The agent stopping mid-Run to ask the human for a low-confidence or missing-info decision, then resuming.
_Avoid_: prompt, interrupt (reserve "interrupt" for the human-initiated kind)
**Interrupt**:
A human-initiated change during a Run (edit a Line Item, correct an Observation, redirect). Applied to shared state and picked up by the agent at the start of its next step β€” no restart.
_Avoid_: barge-in, cancel
**Profile Memory**:
A small persistent record of one tech's defaults β€” trade, common Line Items, markup, report tone, language. Read at the start of a Run (resolves the trade, user-overridable) and updated after.
_Avoid_: settings, preferences, config
**Episodic Memory**:
An append-only store of past Runs (Capture summary + final Deliverable + key Line Items) the agent can recall to inform a new Run.
_Avoid_: history, log, cache
**Recall**:
The agent retrieving relevant past Runs from Episodic Memory via hybrid search β€” a keyword/structured pre-filter then a semantic re-rank β€” exposed as the `search_past_jobs` Tool.
_Avoid_: lookup, query, retrieval (use "Recall" for this specific agent-facing capability)
**Account**:
The single owner of all stored data β€” saved Estimates, their Refinement Threads, and Profile Memory. The product targets solo tradespeople, so an Account == the one Tech == the business; there is no organization layer and no multi-tech sharing. Multi-tenant-_shaped_ (everything is keyed by `account_id`) but bound to one fixed demo Account (`account_id = "demo"`) β€” no login, no auth. A real login that swaps the fixed key for a session lookup is a deliberate post-hackathon extension (ADR-0013).
_Avoid_: User, organization, workspace (Workspace is the screen), Customer (the homeowner the Estimate is _sent to_ β€” a different party)
**Saved Estimate**:
An Estimate persisted to the Estimate Store under an Account so the Tech can reopen, edit, and re-export it across sessions β€” distinct from a Run (the lossy Episodic-Memory record that feeds Recall). Auto-saved when the forge finishes and explicitly Save-able mid-draft; updated in place on edit; deleted by Discard. No "finalized/locked" status (an Estimate is not an invoice).
_Avoid_: invoice, quote (an Estimate stays an editable draft), Run (a Run is the agent's memory record, not a reopenable Deliverable)
**Estimate Store**:
The per-Account store of Saved Estimates + their Refinement Threads β€” separate from Episodic Memory (which stays a pure, append-only Recall corpus). The two have different jobs: Episodic Memory serves the _agent_ (gets smarter), the Estimate Store serves the _user_ (file and reopen work).
_Avoid_: database, history (Episodic Memory is the history-for-the-agent)
**Refinement Thread**:
The persisted, ordered record of post-forge chat turns for one Saved Estimate (human message + the operation taken, e.g. "set labor to 2h β†’ qty 2"). Stored _sanitized_ β€” intents and operations only, never dollar figures β€” so resuming the conversation can never feed a stale number back to the model (Facts-from-Tools). A sibling of Trace, not part of it: Trace is the Agent Brain's forge steps; the Refinement Thread is the human-driven editing conversation that follows.
_Avoid_: chat log, conversation history, Trace (Trace is the forge-step record)
**Thread Compaction**:
Keeping a long Refinement Thread inside the small model's context window by folding the oldest turns into one mechanical "earlier in this estimate: …" line β€” done _deterministically in code_ from the stored operations (the last K turns stay verbatim), never by asking a model to summarize. A second instance of Facts-from-Tools: even the conversation's own compression is code-owned, so the model never restates its own pricing history.
_Avoid_: summarization (implies a model writes it β€” it does not), truncation (compaction folds, it doesn't drop facts)
## Relationships
- A **Capture** is turned into **Observations** by the Perception **Model Role**.
- The **Agent Brain** consumes **Observations** + the voice transcript, calls **Tools**, and produces **Line Items** β†’ a **Deliverable**.
- Every **Model Role** resolves to a concrete model based on the active **Mode**.
- The **Agent Brain** emits a **Trace** of its steps.
- A human supervises: confirms **Observations**, answers low-confidence prompts, edits the **Deliverable** before export.
- An **Account** owns its **Saved Estimates**, **Profile Memory**, and **Episodic Memory**; everything stored is keyed by `account_id` (one fixed demo Account in the hackathon).
- A **Saved Estimate** lives in the **Estimate Store** and has one **Refinement Thread** (its post-forge chat turns); the Thread is sanitized and **Thread Compaction** keeps it bounded.
- The **Refinement Thread** is replayed to the **Agent Brain** on resume for reference (pronoun) resolution; the model gets sanitized history for _context_ but the **current Line Items** for _numbers_ β€” never historical dollars (**Facts-from-Tools**).
## Flagged ambiguities
- "voice handling" was unspecified β€” resolved: the **Audio** Model Role transcribes the voice note (Cohere Transcribe 2B in Private Stack; Nemotron 3 ASR / Nemotron Omni audio in Best Stack β€” see ADR-0009).
- "online/offline" was overloaded β€” resolved into the single term **Mode** (Private Stack vs Best Stack), an endpoint swap only. "Local Mode" was retired because a hosted Gradio Space runs models server-side (see ADR-0005); the true-offline claim lives in the **Airplane-Mode Proof**.
- "adapt prices" was ambiguous β€” resolved: deterministic learning from user-confirmed edits/prefs only; novel items are flagged, never LLM-guessed (**Facts-from-Tools**).
- "translate tool vs language toggle" overlapped β€” resolved: one underlying translate function, two entry points (human toggle + autonomous agent call).
- "Local/on-device" (the hero) vs hosting reality β€” resolved: real compute on Modal; "small/private" = open models + no third-party APIs; literal offline = the filmed **Airplane-Mode Proof**.
- "account" (previously listed as a term to _avoid_ under **Customer**) β€” resolved: **Account** is now the data-owner term (the Tech/business), distinct from **Customer** (the homeowner an Estimate is sent to). For a solo-tradesperson product, Account == Tech == business; no organization layer.
- "remember the chat" was ambiguous (audit trail vs resumable) β€” resolved: the **Refinement Thread** is both _persisted_ (reopen shows it) and _resumable_ (replayed to the model on continue), but **sanitized** (no dollars) with numbers always taken live from current Line Items. Cross-session resume is bounded by deterministic **Thread Compaction**.
- "chat history vs Trace" overlapped β€” resolved: **Trace** = the Agent Brain's forge steps; the **Refinement Thread** = the human-driven post-forge editing conversation. Siblings, separately stored.
## Scope note
The **Irreducible Core** (ADR-0007) is the must-ship, polished slice: Capture β†’ supervised agent builds a correct Estimate live β†’ one Agent Pause + one Interrupt β†’ editable Estimate β†’ PDF, Private Stack only. All other designed features (second Mode, memory, multilingual, fine-tune, service report, video, FLUX) are layered on after the core is flawless and are cut from the end under time pressure.
## Implementation reality (as built)
- **Frontend** is a bespoke HTML/CSS/JS app served by `gradio.Server` (FastAPI), not Gradio components β€” the rule-compliant way to a smooth custom UI (the 🎨 Off-Brand path).
- **Private Stack runs genuinely locally via Ollama** (llama.cpp) on the dev machine β€” Perception = MiniCPM-V, Agent Brain = nemotron-3-nano:4b (NVIDIA Nemotron β€” NOT gpt-oss; the spec's gpt-oss mapping is superseded by ADR-0009) β€” gated by `FF_REAL_MODELS=1`; otherwise a deterministic/keyword stub. This realizes the "no cloud" claim directly on-device; Modal (ADR-0005) remains the option for hosted-Space compute.
- **Model orchestra (ADR-0009):** Perception = MiniCPM-V (protects the OpenBMB track; Nemotron Omni is a selectable Best-Stack alternative), Brain = Nemotron 3 Nano, Audio = Cohere Transcribe, Multilingual = Cohere Aya, Embedding = Llama-Nemotron-Embed-1B (un-tuned). The one shipped fine-tune is MiniCPM-V on CORD/SROIE (ADR-0006).
- The **Agent Brain is LLM-driven tool-calling** over a narrow surface (`add_priced_item` + `finish`); deterministic tools still own all numbers (Facts-from-Tools). Accuracy is tracked by an eval set (`data/brain_evalset.json`, `scripts/run_brain_eval.py`).