Tool Patterns

Dynamic Tool Catalogs

Select relevant tools at runtime when a static tool list is too large.

Use dynamic tools when an agent has a large catalog of possible actions but only a small subset should be sent to the model on each turn. This is common for internal platforms, operations agents, research agents, and backoffice assistants with many read-only service tools.

Do not use dynamic tools to hide permission checks. The selected tool still needs to enforce permissions in code.

Scenario

A support operations agent can inspect orders, refunds, tickets, subscriptions, feature flags, warehouse records, fraud signals, and policy documents. Sending every tool definition on every turn is noisy and expensive. Dynamic tools let Anvia search a tool index with the current prompt and send only the matching definitions.

When to Use It

Use dynamic toolsPrefer static tools
many tools compete for the same agentthe agent has a small stable tool set
tools are discoverable by name, description, and schemaevery tool is needed in almost every run
the prompt usually needs only a few toolsmissing one tool would be more costly than sending all tools
tool catalog can be indexed at startup or deploy timetools are created entirely per request

Architecture Shape

LayerResponsibility
ToolSetowns the full catalog and direct test calls
embedding modelembeds provider-facing tool definitions
createToolIndex(...)builds a searchable dynamic tool index
AgentBuilder.dynamicTools(...)selects top matching tools per prompt
tool codestill enforces permissions, tenant scope, and side-effect policy

Code Example

import { AgentBuilder, ToolSet, createToolIndex } from "@anvia/core";
import { embeddingModel, model } from "./models";
import { createAdminTools, createBillingTools, createSupportTools } from "./tools";

const toolCatalog = ToolSet.fromTools([
  ...createSupportTools(),
  ...createBillingTools(),
  ...createAdminTools(),
]);

const toolIndex = await createToolIndex(embeddingModel, toolCatalog, {
  content: (tool, definition) => [
    definition.name,
    definition.description,
    JSON.stringify(definition.parameters),
  ],
  metadata: (tool) => ({
    name: tool.name,
    domain: tool.name.split("_")[0],
  }),
});

export const operationsAgent = new AgentBuilder("operations", model)
  .instructions("Use the most relevant tool for the user's operational request.")
  .dynamicTools(toolIndex, {
    topK: 6,
    threshold: 0.7,
  })
  .defaultMaxTurns(4)
  .build();

Static tools can still be registered when they should always be available.

const agent = new AgentBuilder("operations", model)
  .tool(createThinkTool())
  .dynamicTools(toolIndex, { topK: 6, threshold: 0.7 })
  .build();

Validate Tool Selection

Test the catalog before testing model behavior. Search the index with representative prompts and assert that the expected tool ids appear.

const matches = await toolIndex.searchIds({
  query: "refund order A-100 because it was duplicated",
  topK: 6,
  threshold: 0.7,
});

expect(matches.map((match) => match.id)).toContain("issue_refund");

Then test the selected tool directly through the catalog.

const result = await toolCatalog.call(
  "issue_refund",
  JSON.stringify({
    orderId: "A-100",
    amount: 25,
    reason: "duplicate_charge",
  }),
);

expect(JSON.parse(result).status).toBe("refunded");

Failure Modes

FailureFix
model cannot find the right toolimprove tool name, description, schema text, or content embedding text
too many unrelated tools are sentraise threshold, lower topK, or split catalogs by domain
needed tools are missinglower threshold, add domain words to descriptions, or keep critical tools static
permission leakenforce user and tenant checks inside every tool
catalog changes at runtimerebuild the index or use a new indexed catalog for future agents

Test Checklist

  • Search the index with common prompts and assert expected tool ids.
  • Call high-risk tools directly through ToolSet.call(...).
  • Verify each tool still enforces permissions with fake users and tenants.
  • Use Studio traces to inspect which dynamic tools were sent during real runs.
  • Add evals for prompts where tool selection is part of the expected behavior.