No description
Find a file
Cole Medin d03e6fabf8 Interactive CLI, grounded KB, and judgment-based escalation
- cli.py: interactive rich REPL. After each answer it prints a "capabilities
  used this turn" panel that maps every tool call back to the capability that
  owns it, and /faq swaps to the reuse widget live. (adds rich dependency)
- capabilities.py: record which capabilities fire per turn (CAPABILITIES_FIRED);
  gate escalation on the NATURE of the request (billing dispute, account issue,
  bug, explicit human ask) instead of on any knowledge-base miss.
- orbit_kb.py: add grounded articles - "What is Orbit?" (KB-07) and an
  integrations article (KB-08) that addresses Slack honestly - and point the
  billing article at support for specific charges. Existing IDs unchanged.
- main.py: per-response "composed from / fired" readout; realistic demo
  questions (KB answer, real billing escalation, grounded Slack question).
- test_agent.py: assert real escalation escalates and an answerable question
  does not. Full suite green (11 unit + 7 live).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 12:27:59 -05:00
.gitignore Baseline: Orbit support agent (Pydantic AI 2.0 capabilities) + GitHub-native PIV skill kit 2026-07-05 08:10:55 -05:00
.python-version Baseline: Orbit support agent (Pydantic AI 2.0 capabilities) + GitHub-native PIV skill kit 2026-07-05 08:10:55 -05:00
agent_v1_style.py Refocus demo on the capability pattern; single bloated v1 for contrast 2026-07-08 10:54:42 -05:00
capabilities.py Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
cli.py Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
main.py Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
orbit_kb.py Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
pyproject.toml Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
README.md Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
sample_output.txt Baseline: Orbit support agent (Pydantic AI 2.0 capabilities) + GitHub-native PIV skill kit 2026-07-05 08:10:55 -05:00
test_agent.py Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00
uv.lock Interactive CLI, grounded KB, and judgment-based escalation 2026-07-08 12:27:59 -05:00

Orbit Support Agent — the Pydantic AI 2.0 capability pattern

A small, runnable demo of the one idea that changed in Pydantic AI 2.0: you stop configuring an agent through a giant constructor and start composing it out of capabilities.

The idea

A capability is one composable brick that bundles everything that used to be scattered across the Agent(...) call — its instructions, its tools, its model settings, and its lifecycle hooks — into a single self-contained unit. You build an agent by snapping bricks together, and the same brick drops into a completely different agent, unchanged.

from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking

agent = Agent(
    "openrouter:anthropic/claude-sonnet-4.6",
    capabilities=[
        KnowledgeBaseCapability(),   # its instructions + its tool + model settings + a guardrail
        EscalationCapability(),      # its own tool + its own instructions
        Thinking(effort="low"),      # a built-in capability, one line
        audit_hooks(),               # lifecycle hooks, no subclass needed
    ],
)

That is the whole thesis: compose, don't configure. Each brick carries its own instructions, tools, settings, and hooks, so nothing has to be hand-wired onto the agent and nothing is welded to one model. The payoff is reuse — pick a brick up and drop it into the next agent without touching it.

What's in here

File What it shows
cli.py An interactive REPL. After every answer it prints a "capabilities used this turn" panel that maps each tool call back to the capability that owns it. /faq swaps to an agent that reuses the KnowledgeBase brick, live.
main.py The agent composed from capabilities, then the reuse payoff: the same brick dropped into a second, unrelated agent without a single change.
capabilities.py The capabilities themselves — each one a small class bundling its own instructions, tool, settings, and hooks.
agent_v1_style.py The "before", for contrast: the identical behavior crammed into one bloated constructor, with every cross-cutting concern hand-wired because there's nowhere for it to live.
test_agent.py Deterministic unit checks + optional live behavioral checks.

Run it

uv sync

uv run python cli.py                   # interactive REPL — see which capabilities fire per turn
uv run python main.py                  # scripted, non-interactive version of the same demo
uv run python agent_v1_style.py        # the bloated "before", for contrast

uv run python test_agent.py            # unit tests (no API)
uv run python test_agent.py --live     # + end-to-end LLM checks

In the REPL, type a question and watch the "capabilities used this turn" panel. Useful commands: /faq (switch to the agent that reuses the KnowledgeBase brick), /support (switch back), /caps, /reset, /help, /exit.

Or send a single prompt non-interactively:

uv run python main.py "How do I export my data before I cancel?"
uv run python agent_v1_style.py "How do I export my data before I cancel?"

Model / keys. Defaults to Claude Sonnet 4.6 via OpenRouter; keys load from a local .env and are never printed. Override the model with MODEL=...:

MODEL=anthropic:claude-sonnet-4-6 uv run python main.py            # direct Anthropic key
MODEL=openrouter:anthropic/claude-haiku-4.5 uv run python main.py  # cheaper