Skip to content

[routing] Auto-generated ChoiceGraph from tool semantics via LLM analysis #155

@dgenio

Description

@dgenio

Context

The ChoiceGraph DAG is the navigation structure that presents bounded tool choices to the LLM. Currently, it is manually constructed: TreeBuilder creates a tree based on tag overlap (Jaccard similarity) and namespace prefixes. The graph topology is entirely determined by metadata the tool author provided.

This works for curated catalogs but breaks down when:

  • Tools have inconsistent or missing tags
  • The tag vocabulary doesn't capture meaningful relationships
  • The catalog is large (500+) and no human curates the taxonomy
  • Tool descriptions reveal relationships that tags miss (e.g., "sends Slack notifications" and "posts to Teams" belong together under "messaging" even if neither is tagged that way)

Why it matters

This is the centerpiece of the Pillar 3 vision: "eventually use an LM to better understand the relationship between tools and create optimized aggregations." An auto-generated ChoiceGraph means:

  1. Zero manual curation — point contextweaver at 1000 MCP tools, and the LLM produces an intuitive navigation tree.
  2. Optimized for LLM navigation — the LLM understands what groupings help another LLM make decisions, producing better trees than human-authored tag taxonomies.
  3. Adaptive — as tools are added/removed, the graph regenerates without manual intervention.

Proposal

AutoGraphBuilder — LLM-powered ChoiceGraph generation

class AutoGraphBuilder:
    def __init__(
        self,
        llm_fn: Callable[[str], str],
        *,
        clustering: ClusteringEngine | None = None,  # from #47/#153
        max_depth: int = 3,
        max_children: int = 8,
    ) -> None: ...
    
    def build(self, items: list[SelectableItem]) -> ChoiceGraph:
        """Use an LLM to produce an optimal navigation tree for the given tools."""
        ...

Two-phase approach

  1. Cluster — Use LLMClusteringEngine ([routing] Add LLM-powered ClusteringEngine implementation for semantic tool grouping #153) or the LLM directly to group tools into semantic categories with meaningful names. Recursive for multi-level trees.
  2. Structure — Organize clusters into a DAG with LLM guidance on optimal depth, grouping names, and item ordering within groups.

Output

A ChoiceGraph that can be used identically to a manually-built graph — same navigation, same beam search, same card rendering.

Acceptance Criteria

  • AutoGraphBuilder class that produces a ChoiceGraph from a list of SelectableItems
  • Uses llm_fn: Callable[[str], str] — no LLM provider dependency
  • Builds a multi-level tree (configurable max_depth, max_children)
  • Generated graph node labels are meaningful category names (not "Group 1")
  • Produces valid ChoiceGraph that passes existing graph validation
  • Can use LLMClusteringEngine ([routing] Add LLM-powered ClusteringEngine implementation for semantic tool grouping #153) for the clustering phase, or built-in clustering
  • Offline/build-time operation — results can be serialized and reloaded via graph I/O
  • Fallback: if LLM fails, falls back to TreeBuilder (tag-based)
  • Unit tests with mock llm_fn covering: small catalog, large catalog, no tools, single-level result
  • Example script demonstrating auto-graph generation from sample_catalog.json

Implementation Notes

The prompt should include:

  • All tool names, descriptions, and tags
  • Instructions: "Organize these tools into a navigation hierarchy of at most {max_depth} levels, with at most {max_children} options per level. Each group should have a clear, descriptive name."
  • Output format: structured JSON tree

Files likely touched:

  • src/contextweaver/routing/auto_builder.py — new module
  • src/contextweaver/routing/__init__.py — export
  • tests/test_auto_builder.py — new
  • examples/auto_graph_demo.py — new example

Dependencies

Notes

  • This is analogous to ChainWeaver's ChainAnalyzer (docs: add LlamaIndex ReActAgent integration guide + example #77) — both use offline static/LLM analysis to discover structure. ChainWeaver discovers sequential relationships (chains); contextweaver discovers categorical relationships (groups).
  • Consider caching: the LLM-generated graph should be serializable so it doesn't need to be regenerated every startup.
  • Consider incremental updates: when a few tools are added, regenerate only the affected subtree rather than the full graph.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/routingRouting engine: catalog, graph, router, cardscomplexity/complexCross-cutting, significant design or riskenhancementNew feature or requestpriority/highHigh priority — closes a critical gap

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions