Skip to main content
Tier 1 of the architecture is one optimizable dspy.Module per agent. Each lens wraps a signature. Where the app’s constraints matter (3–5 children, valid taxonomy types, non-empty labels), the lens is wrapped in dspy.Refine — the 3.x replacement for the removed Assert / Suggest. Refine retries with auto-generated feedback (hint_ field) until a deterministic reward function passes.

The lens set

Package: qlaw/lenses/.

Deterministic rewards for Refine

Reward functions live in qlaw/lenses/_validators.py and return 0.0 or 1.0. They enforce the app’s structural contract — item count, non-empty labels, allowed types — without an LM in the loop.

Example lens: SemanticInterpreter

Example lens: Decomposer (Refine-validated)

Signatures

Every lens ships with a typed dspy.Signature. Signatures live in qlaw/signatures.py and use pydantic fields for the output contract. The docstring is the instructions; the input and output fields are the schema. Output DTOs (SubNode, Concept, Trajectory, Risk, Intel) pin Literal[NodeType.…] so coercion enforces the type discipline at the boundary.

Why lenses are optimizable

Because each lens is a dspy.Module with a Signature, it can be:
  • Compiled with dspy.MIPROv2 or dspy.BootstrapFewShot from a per-lens trainset.
  • Evaluated with dspy.Evaluate on the matching devset in qlaw/datasets.py.
  • Scored with the six metrics in qlaw/metrics.py: taxonomy adherence, node validity, conciseness, novelty, coverage, and grounding.
  • Loaded back into the engine after compile, replacing the zero-shot module in-place.
See Optimization and evaluation for the compile pipeline.
Last modified on August 9, 2026