Reduce AI agent tool costs with progressive context disclosure
Context engineering layer that indexes an agent's tools and skills, injecting only what each turn needs instead of loading everything upfront.
ercel-ai-sdk-v0.5.0Add to Favorites
Why it matters
Ratel helps AI agents dynamically load only the tools and skills needed for each turn, cutting token costs and improving accuracy by avoiding tool overload. Instead of sending every capability upfront, it indexes tools and skills into searchable catalogs that agents query on-demand using BM25 or semantic search.
Outcomes
What it gets done
Index tools and skills into searchable catalogs without requiring a vector database
Search and retrieve only relevant capabilities per agent turn using BM25 or semantic ranking
Inject matching tool schemas and skill instructions dynamically instead of loading everything upfront
Reduce per-call token usage and recover accuracy lost to context overload across local and frontier models
Source
Get it from source
Spark does not host a copy of it.
Open sourceReports
Agent outcome reports
No reports yet
Capabilities
Tools your agent gets
Search tool and skill indexes to find relevant capabilities for the current agent turn.
Invoke a registered tool by ID with the provided arguments.
Retrieve the full content and instructions for a specific skill.
Overview
Ratel is a context engineering layer that indexes an AI agent's tools and skills into BM25-searchable catalogs, injecting only what each turn needs instead of loading everything upfront. It cuts per-call token cost and recovers accuracy lost to tool overload, with SDKs for TypeScript and Python. Use it when an agent's tool schemas and skills have grown large enough to raise per-call cost or degrade its tool-selection accuracy.
What it does
Ratel is a context engineering layer for AI agents that selects only the tools and skills relevant to each turn, recovering accuracy lost to tool overload and cutting per-call token cost, with no vector database or extra infrastructure required. Sending every tool schema, every skill and a growing list of instructions in the system prompt on every turn means paying for tokens the turn doesn't need, and models get worse as that unused context grows and crowds out the option the turn actually needs. Ratel indexes tools and skills into a catalog the agent progressively discloses, searching for what each turn needs and injecting only matching capabilities; constant grounding an agent always needs (a shop's address, a brand's voice) is registered as facts and re-injected only when it isn't already fresh in the transcript.
When to use - and when NOT to
Use it when an agent's tool schemas, skills and instructions have grown large enough to raise per-call cost or degrade the model's tool choice and task focus. It requires registering tools and skills into its catalog structure rather than working as a zero-config drop-in, and semantic/hybrid ranking needs an embedding model or endpoint configured beyond the default BM25 index.
Capabilities
An agent calls search_capabilities when it needs to act; Ratel searches separate tool and skill indexes and returns focused results from each. Tools can be invoked directly by ID, while skill instructions stay out of context until the agent explicitly loads a relevant playbook with get_skill_content. Indexes use BM25 by default - the same algorithm behind most search engines, applied to each tool's name, description and argument names, and to skill names, descriptions and tags - giving fast, deterministic retrieval; semantic and hybrid ranking are opt-in per catalog or per call, with SDK callers registering embeddings and searching dense indexes asynchronously via an in-process model or an OpenAI-compatible embedding endpoint. Across local, open-source and frontier model setups, published benchmarks (benchmark.ratel.sh) show it cutting token usage and recovering accuracy lost to tool overload.
SDKs are available for TypeScript (@ratel-ai/sdk, NAPI-bound) and Python (ratel-ai, PyO3-bound), both built on a Rust retrieval engine (ratel-ai-core). A ToolCatalog and SkillCatalog register tools (with an ID, description, input/output schema and executor) and skills (with an ID, description, the tool IDs it uses, and its instruction body); three functions - search_capabilities_tool, invoke_tool_tool and get_skill_content_tool - are then wired into the agent framework as ordinary tools. Framework adapters exist for the Vercel AI SDK (@ratel-ai/vercel-ai-sdk) and Mastra (@ratel-ai/mastra). Ratel can also be wired into the Vercel AI SDK's ToolLoopAgent and into Pydantic AI, and both wirings run in a diagnostic mode with no API key that prints the recall hits Ratel would inject before any model call. A related project, ratel-local (ratel-ai/ratel-mcp), is a local distribution that puts Ratel in front of an existing MCP setup for coding agents.
How to install
TypeScript:
pnpm add @ratel-ai/sdk
The Python package is ratel-ai, installed with pip.
Then register a ToolCatalog and SkillCatalog and expose search_capabilities_tool, invoke_tool_tool and get_skill_content_tool (or their Python equivalents) as tools in the agent framework. Framework-specific adapters and examples exist for the Vercel AI SDK and Pydantic AI.
The ratel-ai-core engine is Apache-2.0 licensed (an explicit patent grant for embedders); the SDKs, telemetry helpers and examples are MIT licensed.
Who it's for
It suits teams building AI agents with many tools or skills who need to cut per-call token cost and recover tool-selection accuracy without standing up a vector database.
Source README
Ratel
Introduction
The context engineering layer for AI agents. Selects only the tools and skills relevant to each turn, recovering accuracy lost to tool overload and cutting what you pay per call. No vector DB, no infra.
Why
- Cost: Every tool schema, every skill, and a growing list of instructions in the system prompt are tokens you pay for on every call. Send them all up front and you pay for them all, every turn.
- Accuracy: Models get worse as that context grows. Crowd it with tools, skills, and instructions a turn doesn't need and the model picks the wrong option and drifts off task.
- Ratel fixes both: it indexes your tools and skills into a catalog the agent progressively discloses, searching for what each turn needs and injecting only the matching capabilities instead of loading everything up front. Constant grounding your agent always needs - a shop's address, a brand's voice - is registered as facts and pushed into context, re-injected only when it isn't already fresh in the transcript.
Across local, open-source, and frontier model setups, Ratel cuts token usage and recovers accuracy lost to tool overload, with no vector DB required. Full results: benchmark.ratel.sh
Quickstart
Guides: Quickstart · TypeScript SDK · Python SDK
Examples: Vercel AI SDK · Pydantic AI
Typescript
Install the SDK first:
pnpm add @ratel-ai/sdk
Then create and use your Catalogs:
import { readFile } from "node:fs/promises";
import {
SkillCatalog,
ToolCatalog,
getSkillContentTool,
invokeToolTool,
searchCapabilitiesTool,
} from "@ratel-ai/sdk";
const catalog = new ToolCatalog();
catalog.register({
id: "read_file",
name: "read_file",
description: "Read a file from local disk.",
inputSchema: { type: "object", properties: { path: { type: "string" } } },
outputSchema: { type: "object", properties: { contents: { type: "string" } } },
execute: async ({ path }) => ({ contents: await readFile(path, "utf8") }),
});
const skills = new SkillCatalog();
skills.register({
id: "inspect-local-file",
name: "inspect-local-file",
description: "Inspect a local file before answering questions about it.",
tools: ["read_file"],
body: "Read the requested file, then ground your answer in its contents.",
});
// use the following as tools in your agent framework
const search = searchCapabilitiesTool(catalog, skills);
const invoke = invokeToolTool(catalog);
const loadSkill = getSkillContentTool(skills);
Python
Install the SDK first:
pip install ratel-ai
Then create and use your Catalogs:
from ratel_ai import (
ExecutableTool,
Skill,
SkillCatalog,
ToolCatalog,
get_skill_content_tool,
invoke_tool_tool,
search_capabilities_tool,
)
catalog = ToolCatalog()
catalog.register(ExecutableTool(
id="read_file",
name="read_file",
description="Read a file from local disk.",
input_schema={"properties": {"path": {"type": "string"}}},
execute=lambda args: {"contents": open(args["path"]).read()},
))
skills = SkillCatalog()
skills.register(Skill(
id="inspect-local-file",
name="inspect-local-file",
description="Inspect a local file before answering questions about it.",
tools=["read_file"],
body="Read the requested file, then ground your answer in its contents.",
))
# use the following as tools in your agent framework
search = search_capabilities_tool(catalog, skills)
invoke = invoke_tool_tool(catalog)
load_skill = get_skill_content_tool(skills)
How it works
When your agent needs to act, it calls search_capabilities. Ratel searches separate tool and skill indexes and returns focused results from each. Tools can be invoked by id; skill instructions stay out of context until the agent loads a relevant playbook with get_skill_content.
The indexes use BM25 by default, the same algorithm behind most search engines, applied to each tool's name, description and argument names, and to skill names, descriptions, and tags. Retrieval is fast and deterministic. Semantic and hybrid ranking are opt-in per catalog or per call; SDK callers register (which embeds) and search dense indexes asynchronously, using either an in-process model or an OpenAI-compatible embedding endpoint.
Related projects
Related open-source projects extend and validate this repository:
| Project | Repo | What it is |
|---|---|---|
| ratel-local | ratel-ai/ratel-mcp | The local distribution for your Coding Agents: Ratel in front of your MCP setup. |
| ratel-bench | ratel-ai/ratel-bench | The benchmark harness behind benchmark.ratel.sh. |
Repo layout
src/
├── core/ # ratel-ai-core — Rust retrieval engine
├── sdk/ts/ # @ratel-ai/sdk — TypeScript SDK (NAPI-bound)
├── sdk/python/ # ratel-ai — Python SDK (PyO3-bound)
├── adapters/ts-vercel-ai-sdk/ # @ratel-ai/vercel-ai-sdk — Vercel AI SDK adapter
├── adapters/ts-mastra/ # @ratel-ai/mastra — Mastra adapter
└── telemetry/ # OTel conventions + helper packages
protocol/ # catalog-source wire contract
examples/ # End-to-end SDK examples
docs/
├── adr/ # Architecture decision records
└── assets/ # Images and other static assets
Build & test
Prerequisites: Rust stable, Node 24+, pnpm 10.28+. Python SDK: Python 3.9+ and uv.
cargo build --workspace && cargo test --workspace # Rust
pnpm install && pnpm -r build && pnpm -r test # TypeScript
# Python: see src/sdk/python/README.md
examples/ai-sdk/README.md
examples/ai-sdk - Ratel + Vercel AI SDK
Demonstrates Ratel wired into the Vercel AI SDK through @ratel-ai/vercel-ai-sdk: AI SDK-native tool() definitions go straight into the shared catalog via ratel().adaptTo(aiSdk()), the model sees only the three capability tools (view.modelTools()), and view.prepareStep injects the per-turn recall pair - no hand-written conversion glue anywhere. See Capability tools and Framework integrations for the protocol.
The agent loop is AI SDK v6's ToolLoopAgent with stopWhen: stepCountIs(N), so it can chain tool calls and a final answer inside one .generate() call.
Setup
export OPENAI_API_KEY=sk-...
pnpm install
pnpm -F @ratel-ai/example-ai-sdk start
# or with a custom prompt:
pnpm -F @ratel-ai/example-ai-sdk start "send an email to alice@example.com saying ship it"
Without OPENAI_API_KEY the script runs in diagnostic mode - prints the recall hits Ratel would inject and exits before any model call.
Override the model with AI_MODEL=gpt-4o. To swap providers, replace the import in src/index.ts (e.g. @ai-sdk/anthropic).
Layout
src/tools.ts AI SDK-native tool() definitions — the demo catalog, no Ratel shapes
src/agent.ts createRatelView (core + aiSdk() view) and runAgent (ToolLoopAgent loop)
src/index.ts entry — parse argv, build model + view, run, print
test/agent.test.ts model-free test: scripted mock drives search -> invoke -> answer
Splitting tools.ts and agent.ts keeps the catalog declarative and the loop readable; nothing about the wiring is OpenAI-specific (runAgent accepts any LanguageModel).
The example pins ai@^6 on purpose: the adapter's own package verifies v5-v7 (exact floor and latest rows in CI), while this app proves the wiring on the middle major. Its tsconfig.json maps ai to the example's own install so the workspace-linked adapter's types resolve the same single ai a published install would.
Why it's a separate workspace package
Examples don't ship in @ratel-ai/sdk - keeping them out of the published artifact keeps the public API surface narrow and dependency-free. The example pulls ai and @ai-sdk/openai only here.
examples/pydantic-ai/README.md
examples/pydantic-ai - Ratel + Pydantic AI
The Python mirror of examples/ai-sdk: the ratel-ai SDK wired into Pydantic AI. Each run exposes the prompt's top-K ToolCatalog matches directly and keeps search_capabilities plus invoke_tool available for the rest of the catalog. See Capability tools and Framework integrations for the protocol and reusable wiring pattern.
Tools are built from the catalog's JSON schemas via Pydantic AI's Tool.from_schema, so the schema the model sees is the same one Ratel ranks.
Setup
export OPENAI_API_KEY=sk-...
uv run main.py
# or with a custom prompt:
uv run main.py "send an email to alice@example.com saying ship it"
uv run resolves ratel-ai from this monorepo (see [tool.uv.sources] in pyproject.toml) and pydantic-ai from PyPI, building the native extension on first run.
Without a model API key the script runs in diagnostic mode - prints the initial Ratel BM25 filter output and exits before any model call. Override the model with RATEL_EXAMPLE_MODEL (a Pydantic AI model id, e.g. anthropic:claude-sonnet-4-6 or openai:gpt-5-mini); supported key env vars include OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, GROQ_API_KEY.
Layout
tools.py catalog + helpers — six stub tools registered into a ToolCatalog
agent.py run_agent — assembles the tool set (top-K + capability tools), runs the Pydantic AI Agent
main.py entry — parse argv, diagnostic mode or model run, print
test_agent.py model-free test of direct top-K tool invocation
Splitting tools.py and agent.py keeps the catalog declarative and the loop readable; nothing about the wiring is provider-specific (run_agent accepts any Pydantic AI model id).
Why it's a separate package
Examples don't ship in ratel-ai - keeping them out of the published wheel keeps the public API surface narrow and OTel-SDK-free. This example pulls pydantic-ai only here.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.