Make ambiguous routes machine-actionable: structured disambiguation options on RouteResult
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 9
- Forks
- 17
- Avg merge
- 21h 36m
- Merged PRs (30d)
- 22
Description
Summary
When the router flags ambiguity, populate a structured disambiguation field on RouteResult — a small list of options, each with the candidate tool_id, a short label, and the distinguishing signal — alongside the existing prose clarifying_question, so agent loops can render choice UIs, auto-answer from conversation context, or re-route on a user's pick without parsing prose.
Why this matters
Ambiguity handling is where deterministic routing meets the agent UX. Today the router's answer to "it's ambiguous" is a human-readable sentence; every consumer that wants to act on the ambiguity (present two buttons, ask the model to pick, re-route with the choice) must reverse-engineer structure out of prose. A typed option list turns the router's most interactive moment into a first-class API — and gives the gateway's tool_browse a structured payload clients can render.
Current evidence
src/contextweaver/routing/router.py:120—clarifying_question: str | Noneis the only disambiguation output onRouteResult;routing/filters.py:93(suggest_clarifying_question) builds the prose from the candidates it already has in structured form — the structure is computed and then discarded.RouteResult.is_ambiguousand the confidence-gap mechanism (router.py:457) decide when; nothing represents what the choices are in machine-readable form.- Related (all distinct layers): closed #226 added
explanation()— human-readable rationale rendering; newly filed #515 emits provider constrained-selection schemas for the route step — the model-facing selection contract; #500 routes low-confidence results to an optional reranker. None gives the caller a typed disambiguation payload; this issue should cross-reference all three.
External context
Structured "did you mean" options are the standard interactive-disambiguation contract in search and assistant UIs; MCP clients can render structured content but not parse free prose reliably.
Proposed implementation
- Add a small frozen dataclass (e.g.
DisambiguationOption:tool_id,label,distinction— the per-option signalsuggest_clarifying_questionalready derives, such as the differing namespace/verb/tags) inrouting/filters.pyor a sibling module. - Populate
RouteResult.disambiguation: list[DisambiguationOption](default empty) wheneveris_ambiguousis true, from the same inputs as the prose question; keepclarifying_questiongenerated from the structured options so the two can never disagree. - Serde: per-class
to_dict/from_dictfollowing the repo's split convention; regenerate published JSON Schemas (schemas/,_schema_gen.pydrift gate). - Surface through the gateway: include the options in the
tool_browseambiguous-response payload perdocs/gateway_spec.md, coordinating shape with #515's constrained-selection work. - Re-route ergonomics: document the loop (
route(query, context_hints=[chosen])or equivalent) so a user's pick feeds back deterministically.
AI-agent execution notes
- Inspect first:
routing/filters.py(suggest_clarifying_questioninternals — the option data already exists there),routing/router.pyRouteResult+ serde,schemas/generation,docs/gateway_spec.mdbrowse response shape, tests intests/test_router*.pycovering ambiguity. - Determinism: option ordering must follow the canonical tie-break (score, then ID); labels/distinctions derive only from catalog data (they enter prompts — respect the sanitization work in #480).
- Preserve: existing
clarifying_questiontext byte-identical for current fixtures (generate prose from structure carefully, or keep the existing generator and assert consistency in tests). - Edge cases: ambiguity among >2 candidates (cap options at top-k gap cluster); empty distinction signals (fall back to namespace); serialization round-trip with empty list default.
Acceptance criteria
- Ambiguous routes carry ≥2 structured options with non-empty
tool_id/label; non-ambiguous routes carry an empty list. - Prose
clarifying_questionand structured options are provably consistent (test asserts every option's tool referenced). - Round-trip serde + regenerated schemas pass the drift gates; gateway browse payload documented and tested.
- Existing ambiguity fixtures produce byte-identical prose output.
Test plan
Unit tests on option derivation per ambiguity fixture; serde round-trips; gateway wire-shape test; determinism test (repeated routes → identical options); full make ci including schema drift gates.
Documentation plan
docs/tool_router.md ambiguity section; gateway spec browse payload; cookbook recipe for the choice-then-re-route loop; CHANGELOG.
Migration and compatibility notes
Additive field with empty default — not expected to require migration. Schema version bump per the repo's schema-governance process.
Risks and tradeoffs
Slight RouteResult surface growth ahead of the 1.0 API manifest (#518) — coordinate timing; distinction-label quality depends on catalog metadata richness (degrade gracefully to namespace/kind). Alternative — leaving prose only — keeps every downstream consumer writing its own fragile parser.
Suggested labels
product, developer-experience, ai
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with routing/filters.py and its suggest_clarifying_question internals, then read RouteResult and serde in routing/router.py. Run the ambiguity tests in tests/test_router*.py and inspect schema generation plus docs/gateway_spec.md for the browse response shape. Done means deterministic structured options, compatible prose, serde and schema gates, gateway coverage, and documented re-routing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend-api-design, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100