Skip to main content
Qlaw’s agents become typed dspy.Signature + dspy.Module lenses over a pydantic graph state. The user-driven loop (Selection → Lens Invocation → Expansion) becomes the forward() of a single ReasoningEngine module. The Co-Pilot and the Enrich researcher become dspy.ReActV2 tool agents. Because everything is a DSPy module, every layer is:
  • Optimizable — via MIPROv2, BootstrapFewShot, or GEPA.
  • Evaluable — via dspy.Evaluate with custom metrics.
  • Refine-validated — deterministic reward functions retry until the contract passes.

Three tiers

DSPy 3.3.0 primitives used

Layer-to-DSPy mapping

Domain model

The graph is the typed state that flows through every DSPy module. It has two hard rules:
  1. Expansion is deterministic and pure. The LM never mutates the graph. It emits a NodeBatch, and GraphState.apply() does the wiring. The graph delta is ground truth a metric can check.
  2. The LM sees a distilled NodeContext, not raw state. Lenses receive node.label and node.description. The Co-Pilot gets a graph summary.
The node taxonomy mirrors the frontend’s NodeType: ROOT, PROBLEM, QUESTION, PLAN, PROJECT, COMPONENT, TRAJECTORY, DATA, RISK, INSIGHT, CONCEPT, VISUALIZATION. Lens output DTOs (SubNode, Concept, Trajectory, Risk, Intel) each pin a Literal[NodeType.…] so pydantic coercion enforces the contract at the boundary.

Model configuration

One model across all tiersdeepseek-v4-flash via the OpenAI-compatible endpoint, configured once at startup. Set model_type="chat" if the endpoint requires it. Any OpenAI-compatible model id can be swapped in via OPENAI_MODEL.

Design decisions

Last modified on August 9, 2026