> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qredence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Lenses

> Tier 1 of Fleet Reasoner: one optimizable dspy.Module per agent, Refine-validated with deterministic reward functions where the app's constraints matter.

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/`.

```python theme={null}
from qlaw.lenses.semantic import SemanticInterpreter
from qlaw.lenses.decompose import Decomposer
from qlaw.lenses.ontology import OntologyArchitect
from qlaw.lenses.trajectories import TrajectoryStrategist
from qlaw.lenses.critic import Critic
from qlaw.lenses.enrich import GroundingEnricher
from qlaw.lenses.explain import Explainer
```

| Lens                   | Signature                | Predictor                 | Wrapped in `Refine`?       |
| ---------------------- | ------------------------ | ------------------------- | -------------------------- |
| `SemanticInterpreter`  | `SemanticInterpretation` | `ChainOfThought`          | No                         |
| `Decomposer`           | `Decompose`              | `ChainOfThought`          | Yes — `validate_decompose` |
| `OntologyArchitect`    | `Ontology`               | `ChainOfThought`          | Yes — `validate_ontology`  |
| `TrajectoryStrategist` | `Trajectories`           | `ChainOfThought`          | No                         |
| `Critic`               | `GapAnalysis`            | `ChainOfThought`          | No                         |
| `GroundingEnricher`    | `Enrich`                 | `ReActV2` + `search` tool | No (agentic loop)          |
| `Explainer`            | `Explain`                | `Predict`                 | No                         |

## 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.

```python theme={null}
from qlaw.graph import NodeType

ALLOWED_DECOMPOSE_TYPES = {NodeType.COMPONENT, NodeType.QUESTION, NodeType.DATA}
ALLOWED_ONTOLOGY_TYPES = {NodeType.CONCEPT}

def _basic_batch_reward(kwargs, outputs, *, allowed_types, min_items=3, max_items=5):
    items = outputs.sub_nodes if hasattr(outputs, "sub_nodes") else outputs.concepts
    if not items: return 0.0
    if not (min_items <= len(items) <= max_items): return 0.0
    if any(not (getattr(i, "label", "") or "").strip() for i in items): return 0.0
    if any(not (getattr(i, "description", "") or "").strip() for i in items): return 0.0
    if any(getattr(i, "type", None) not in allowed_types for i in items): return 0.0
    return 1.0

def validate_decompose(kwargs, outputs) -> float:
    return _basic_batch_reward(kwargs, outputs, allowed_types=ALLOWED_DECOMPOSE_TYPES)

def validate_ontology(kwargs, outputs) -> float:
    return _basic_batch_reward(kwargs, outputs, allowed_types=ALLOWED_ONTOLOGY_TYPES)
```

## Example lens: SemanticInterpreter

```python theme={null}
import dspy
from qlaw.signatures import SemanticInterpretation

class SemanticInterpreter(dspy.Module):
    """Prompt -> root seed. Replaces analyzePrompt(); model: deepseek-v4-flash."""
    def __init__(self):
        super().__init__()
        self.predict = dspy.ChainOfThought(SemanticInterpretation)

    def forward(self, prompt: str, **kwargs):
        return self.predict(prompt=prompt)  # Prediction: root_label, root_type, intent, entities
```

## Example lens: Decomposer (Refine-validated)

```python theme={null}
import dspy
from qlaw.signatures import Decompose
from qlaw.lenses._validators import validate_decompose

class Decomposer(dspy.Module):
    """Break a node into 3-5 parts. Replaces decomposeNode(); model: deepseek-v4-flash.
    Refine enforces the 3-5 / valid-type contract with retry + feedback."""
    def __init__(self):
        super().__init__()
        self.predict = dspy.ChainOfThought(Decompose)
        self.refined = dspy.Refine(self.predict, N=3, reward_fn=validate_decompose, threshold=0.5)

    def forward(self, node, **kwargs):
        return self.refined(node=node)  # Prediction: sub_nodes
```

## 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](/fleet-reasoner/optimization) for the compile pipeline.
