Skip to main content
QitOS v0.4 keeps the same AgentModule + Engine kernel, but adds a new authoring layer:
  • FamilyPreset
  • HarnessPolicy
  • ModelAdapter
  • ToolPolicy
  • ContextPolicy
This layer exists so one agent can switch model families without rewriting:
  • the agent state
  • the tool surface
  • the runtime loop
  • the tracing stack

The idea in one sentence

A family preset bundles the research defaults for one model family — the transport, protocol, fallback chain, tool delivery mode, and context settings that work best out of the box. It says:
  • which transport (the client that communicates with the model API) to use
  • which protocol (the output format contract between model and parser) to prefer
  • which fallback chain (the ordered list of protocols to try if the first one fails) to keep
  • how to deliver tool schemas
  • what context defaults should apply

What stays stable

The preset layer does not replace the kernel. It only resolves model-facing policy before the run starts:
family preset -> harness policy -> model transport + protocol + parser
The rest stays the same:
task -> state -> prepare -> llm -> parser -> Decision -> tools -> reduce -> trace

The public v0.4 surface

from qitos.harness import (
    FamilyPreset,
    HarnessPolicy,
    ModelAdapter,
    ToolPolicy,
    ContextPolicy,
    resolve_family_preset,
    build_harness_policy,
    build_model_for_preset,
)

Gold presets in v0.4

The first QitOS gold presets are:
  • qwen
  • kimi
  • minimax
  • gpt-oss
  • gemma-4
In addition, five compatibility presets provide correct defaults for broader model families:
  • openai
  • anthropic
  • gemini
  • deepseek
  • glm
All 10 currently target OpenAI-compatible serving, but they do not all share the same protocol defaults. That protocol default is not always the full story. Qwen now prefers a native tool-call lane when an OpenAI-compatible endpoint returns structured tool_calls. So for Qwen:
  • json_decision_v1 is still the default text protocol
  • but native tool calls are preferred before the text parser chain
  • and xml_decision_v1 -> react_text_v1 remain the stable fallback path
That difference matters because QitOS now treats model family and transport as separate concerns.

Why not just instantiate OpenAICompatibleModel(...) directly?

You still can. That is the right choice when:
  • you are hand-authoring one model path
  • you do not need family-level defaults
  • you are debugging a single provider integration
But for research comparisons and reusable examples, presets are better because they keep:
  • protocol choice explicit
  • fallback chains stable
  • tool delivery mode visible in traces
  • context defaults reproducible

Where preset metadata appears

Preset resolution is recorded into trace metadata through RunSpec.metadata. That means qita can show:
  • family preset
  • protocol
  • parser
  • tool delivery mode
  • decision source
  • native tool-call usage
  • context policy
without inventing a second trace format.

Override a preset

When a built-in preset does not match your needs, use preset.override() to create a customized copy without editing source:
from dataclasses import replace
from qitos.harness import resolve_family_preset

qwen = resolve_family_preset("qwen")
custom = qwen.override(
    context_policy=replace(qwen.context_policy, context_window_hint=256_000),
    notes="Extended context for long-horizon runs",
)
The override() method returns a new FamilyPreset — the original is never mutated. Nested policies (tool_policy, context_policy) are replaced wholesale, so use replace() on the policy first.

Advisory defaults

Gold presets include optional advisory fields for research baselines:
FieldTypeMeaning
recommended_max_stepsint | NoneSuggested step budget per run
recommended_max_tokensint | NoneSuggested total token budget
recommended_retry_budgetint | NoneMax critic-retry attempts per step
recommended_temperaturefloat | NoneDefault sampling temperature
These fields are advisory only — the engine does not auto-apply them. They exist so that researchers can reference a documented baseline without guessing. Use them as starting points, then tune for your specific task. These fields appear in preset.to_dict() and in trace metadata, so you can always see what defaults were suggested even if you chose different values.