← Documentation index Architecture guide › grammar

Metnos

The planner grammar
How probabilistic output is brought back to an executable contract.

The model proposes a plan, but it does not freely choose the command shape. Metnos gives it a restricted executor pool and, when the model service supports GBNF, constrains decoding to the Framework structure and admitted names. Parsing, validation, and Vaglio then inspect the result before anything runs.

Contents

  1. An example
  2. The two grammar levels
  3. From executor pool to plan
  4. Guarantees and boundaries
  5. Compatibility constraints
  6. Determinism and cost
  7. Verification
  8. Relationship with model services
  9. Canonical sources

1. An example

Ask Metnos with a request such as:

Find the PDFs modified this week in the Projects folder,
compute each file's SHA-256 digest, and show me the result.

The planner must not answer with a vague explanation. It must produce a structure like this, restricted to names in the pool computed for the turn:

{
  "steps": [
    {"tool": "find_files", "args": {"path": "Projects", "extension": "pdf"}},
    {"tool": "compute_signatures", "args": {"algorithm": "sha256"}}
  ]
}

This example illustrates shape; it neither authorises paths nor replaces argument resolution. Relative dates, references between steps, and filesystem scope are resolved and checked by later layers.

2. The two grammar levels

LevelStatus and role
build_framework_grammarThis is the ordinary path when METNOS_PROPOSER_GRAMMAR=1, which is the default. It constrains Framework shape and the tool field to the effective pool; final_answer is included as a valid terminal.
build_framework_grammar_typedWith METNOS_PROPOSER_GRAMMAR_ARGS=1, it also binds each name to its argument schema. Arguments marked runtime_resolved are excluded because they belong to the runtime. This mode is not the default.

runtime/tool_grammar.py contains the JSON Schema-to-GBNF translator, call validation, and semantic filters reused by the engine. The generate_tool_grammar API generates a single tool_call; the planner instead uses the Framework grammar, which may contain several steps.

3. From executor pool to plan

  1. The engine extracts intent, object, and semantic signals from the request.
  2. Prefiltering reduces the catalog to executors relevant and available to that user.
  3. Deterministic filters retain required producers, exclude special escape routes that were not requested, and select a provider variant only when the request names that provider.
  4. The generator builds GBNF from exactly the names that remain.
  5. The model service decodes a Framework compatible with that grammar.
  6. The parser reconstructs the plan; validators, argument filling, policy, and Vaglio check what grammar cannot decide.

Natural-language signals—such as undo, recurrence, proximity, skills, and provider names—come from the translatable detection lexicon. A new language requires lexical data for that language, not conditions hard-coded for individual phrases.

3.1 Discriminated argument unions

When argument constraints are enabled, every step has its own branch: the name compute_signatures can only be followed by arguments derived from its schema. Closed enums become GBNF alternatives; strings, numbers, booleans, arrays, and nested objects become their corresponding primitives. If a schema cannot be typed, that branch uses free JSON arguments and leaves validation to the executor.

4. Guarantees and boundaries

Grammar guaranteesGrammar does not guarantee
A JSON structure admitted by the generated profile.That the plan truly satisfies the user's request.
On the ordinary path, executor names that belong to the turn's pool.That the user authorised the action or named path.
In typed mode, name-to-argument-schema correspondence for translatable branches.Postconditions, real effects, service availability, or data correctness.
Output that is easier to parse and reject explicitly.Absolute reproducibility of language reasoning or free-form content.

Schema translation has a depth limit. Beyond it, or for complex constructs, the generator falls back to generic JSON. Post-decoding validation checks the top-level shape, name, required arguments, and synthetic terminals; deep validation remains the responsibility of the executor and the common contract.

5. Compatibility constraints

Grammar construction observes several requirements of the current local model service:

If a service does not accept the grammar parameter, the switch to grammar-free mode is logged. The plan must still pass parsing and validation, but no longer benefits from the decoding-time constraint.

6. Determinism and cost

Given the same ordered pool and schemas, the generated GBNF string is deterministic. This makes the model-visible surface comparable and prevents an absent name from appearing on the constrained path. It does not make the model's whole response deterministic: free text, semantic values, and new plans still come from probabilistic inference.

The grammar is built in memory and makes no network call. Its main cost is during decoding, when the service removes inadmissible tokens at each step. Metnos limits that cost by building grammar for the turn's pool rather than the whole catalog, and gives the proposer an output budget derived from request complexity.

7. Verification

The documentation does not freeze a test count that would quickly become obsolete. The main suites are:

tests/runtime/engine/test_tool_grammar.py
tests/runtime/engine/test_grammar_args_typed.py
tests/runtime/engine/test_routing_pool.py
tests/runtime/engine/test_tool_call_parser.py

They cover deterministic generation, name-to-argument unions, nested schemas, closed enums, intent and provider filters, synthetic terminals, duplicate-rule removal, and truncated-output recovery. Engine tests also verify that a plan cannot name executors excluded from the pool.

8. Relationship with model services

GBNF is the mechanism used by the llama.cpp-compatible local service. Other services may offer constrained JSON Schema or native tool calling. The Metnos contract does not depend on a provider's commercial name: an adapter is suitable only if it states precisely which constraint level it enforces and returns data that the common parser and validators can inspect.

Model virtualization explains how logical roles map to configured services. Changing a service must not change the executor vocabulary, user authority, or Vaglio rules.

9. Canonical sources