Examples
Dynamic Tool Catalogs
A pattern for selecting relevant tools from a larger permissioned catalog.
Dynamic tool catalogs keep large tool sets out of every model request. The application embeds tool definitions, filters the catalog by tenant or role, and lets Anvia select the most relevant tools for the current prompt. Catalog selection narrows exposure; it does not replace tool-level authorization.
Scenario
An internal support agent has 80 admin tools. A refund question should expose refund and order tools, not deployment, billing-plan, or workspace-admin tools. High-risk tools still enforce approval and permissions if selected.
Flow
| Step | Owner |
|---|---|
| create request-scoped tools | app |
| embed tool definitions | Anvia createToolIndex(...) |
| filter by tenant/role | app policy expressed as vector metadata |
| select relevant tools per prompt | Anvia .dynamicTools(...) |
| authorize execution | each tool/service |
Example
import { AgentBuilder } from "@anvia/core";
import { createToolIndex } from "@anvia/core/tool";
import { vectorFilter } from "@anvia/core/vector-store";
const SUPPORT_ADMIN_INSTRUCTIONS = [
"Use the smallest relevant tool set for each request.",
"If no selected tool can safely perform the action, explain what is missing.",
"Do not assume a tool is authorized just because it was selected.",
].join("\n");
export async function createSupportAdminAgent(scope: SupportAdminScope) {
const tools = [
...createOrderTools(scope),
...createRefundTools(scope),
...createTicketTools(scope),
...createAccountTools(scope),
];
const toolIndex = await createToolIndex(scope.embeddingModel, tools, {
metadata: (tool) => ({
tenantId: scope.operator.tenantId,
role: scope.operator.role,
risk: tool.name.includes("refund") ? "high" : "normal",
}),
content: (_tool, definition) => [
definition.name,
definition.description,
JSON.stringify(definition.parameters),
],
});
const allowedToolFilter = vectorFilter.and(
vectorFilter.eq("tenantId", scope.operator.tenantId),
vectorFilter.eq("role", scope.operator.role),
);
return new AgentBuilder("support-admin", scope.model)
.instructions(SUPPORT_ADMIN_INSTRUCTIONS)
.dynamicTools(toolIndex, {
topK: 6,
threshold: 0.68,
filter: allowedToolFilter,
})
.defaultMaxTurns(4)
.build();
}
High-risk tools still guard themselves:
const requestRefund = createRefundTool({
user: scope.operator,
services: scope.services,
auditLog: scope.auditLog,
idempotencyKey: scope.idempotencyKey,
requiredPermission: "orders:refund",
});
Production Checks
| Check | Why |
|---|---|
| tool descriptions are specific | improves tool retrieval quality |
| metadata includes tenant and role | prevents irrelevant tools from being selected |
| high-risk tools keep approvals | selection is not authorization |
| selected tools are logged | makes unexpected tool exposure debuggable |
| catalog is rebuilt when tools change | keeps embeddings aligned with definitions |
Failure Modes
- Search threshold is so low that unrelated tools are exposed.
- Tool descriptions are vague, so retrieval picks the wrong capability.
- Dangerous tools rely only on catalog filtering.
- The catalog is global when tools should be tenant- or role-specific.
- Tool index creation happens in the hot path for every prompt.
