O que é Roteador de Ferramentas?
Roteador de Ferramentas (Tool Router) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Ferramentas e ação. Escolher um subconjunto pequeno de ferramentas antes de chamar o modelo principal.
O Fábio cuida do assistente de back office de uma distribuidora. O agente concilia pagamentos, emite notas, consulta estoque, abre chamados com transportadoras e responde dúvidas de comissão.
Depois de fundir as ferramentas duplicadas, sobraram trinta e quatro. Todas legítimas, todas diferentes. E alguém usa cada uma delas.
Mas o agente ainda erra. Num pedido de conciliação, ele chama check stock em vez de find payment. As duas recebem um código de pedido, e as descrições começam igual. O trace mostra o modelo hesitando entre ferramentas que nem deviam estar na mesa.
E tem a conta. Toda chamada leva as trinta e quatro definições. Num loop de seis turnos, elas vão para o modelo seis vezes.
O Fábio não pode tirar ferramentas. O que ele pode mudar é quantas o modelo vê de cada vez. É esta aula.
Organize as ferramentas em grupos por assunto. Conciliação, notas, estoque e logística, cada um no seu grupo. Antes de o loop do agente começar, uma etapa curta decide de quais grupos esta tarefa precisa. E o agente principal roda só com esses grupos.
O movimento central é tirar uma escolha do modelo grande e colocar numa etapa pequena, barata e testável. O agente principal para de responder qual destas trinta e quatro, e passa a responder qual destas oito. E sempre existe um caminho de volta. Se o agente descobre que precisa de outro grupo, ele pode pedir.
A tarefa chega, como texto de uma pessoa ou um evento do sistema.
Um modelo pequeno lê a tarefa e a lista de grupos, uma linha de descrição para cada.
O catálogo guarda os grupos e as ferramentas de cada grupo, versionado.
O roteador devolve os grupos escolhidos, e a confiança dele.
O agente principal roda com as ferramentas desses grupos, mais uma ferramenta extra, para pedir outro grupo.
As ferramentas rodam normalmente.
E a resposta sai, com o trace registrando quais grupos foram usados.
Chega uma tarefa. Concilie o pagamento do pedido 88231 com o extrato de ontem e, se bater, dê baixa no boleto. O roteador recebe a tarefa e quatro linhas, uma por grupo.
Financeiro: use para conciliar pagamentos com extratos e dar baixa em boletos.
Notas, estoque e logística, cada um com o seu uso.
Ele devolve: financeiro, com 92% de confiança. O agente principal roda com as sete ferramentas de financeiro, mais a de pedir grupo. A ferramenta de estoque que causava a confusão nem aparece. E uma ferramenta que não está na lista não tem como ser escolhida por engano.
Agora a conta, com números ilustrativos. Digamos que cada definição de ferramenta ocupa uns duzentos e cinquenta tokens. As trinta e quatro somam uns oito mil e quinhentos tokens por chamada.
Num loop de seis turnos, são uns cinquenta e um mil tokens só de definições.
Com oito ferramentas, a mesma tarefa leva uns doze mil. O roteador custa uma chamada curta num modelo pequeno. Ele paga uma vez. A economia acontece em todo turno.
Uma pergunta para você. Uma semana depois de colocar o roteador no ar, o painel mostra que em 40% das tarefas de cobrança, o agente pede o grupo de notas no meio da execução. Qual é a correção mais direta?
Incluir notas na seleção inicial das tarefas de cobrança, ajustando a descrição do roteador ou os exemplos dele. Um grupo pedido com frequência, no mesmo tipo de tarefa, quer dizer que a seleção inicial está estreita demais. Expansão é saída de emergência, não o caminho normal.
Tirar a ferramenta de pedir grupo, porque ela deixa o agente lento, deixaria essas tarefas sem a ferramenta certa. E juntar todos os grupos num só desfaz o padrão. Cobrança e notas aparecem juntas, então entram juntas.
Se o agente tem poucas ferramentas, consolide primeiro. Foi a última aula.
Se as tarefas cruzam todos os assuntos o tempo todo, o roteador sempre escolheria tudo.
Se o seu provedor oferece busca de ferramentas sob demanda, e o seu único problema é custo de token, esse mecanismo resolve, sem uma etapa extra sua.
E se o canal de entrada já diz o assunto, como uma fila ou um tipo de evento, use uma regra fixa no código. Sem modelo, custo zero, e zero erro de classificação.
Agora as armadilhas comuns. Agrupar por sistema, não por tarefa. Tudo que fala com o ERP vira um grupo de vinte e cinco ferramentas. Agrupe pelo que a pessoa quer fazer. Como conciliar, emitir ou despachar um pedido.
Descrever o conteúdo, não o uso. Ferramentas de financeiro não ajuda o roteador. Use para conciliar pagamentos com extratos ajuda, porque bate com o jeito que as tarefas chegam.
Um conjunto padrão com efeito colateral. Confiança baixa quer dizer que o roteador está em dúvida. O padrão seguro é só leitura. Não tudo, por via das dúvidas.
E esquecer a saída de emergência. Sem um jeito de pedir grupo, um erro do roteador vira um agente que responde sem a ferramenta certa. Ou inventa o dado.
A falha mais cara aqui é a silenciosa. O grupo certo ficou de fora, e o agente não pediu. Ele responde não encontrei o pagamento, com toda a calma. Por isso a cobertura é o que mais importa. Em quantas tarefas os grupos escolhidos incluem toda ferramenta que a solução correta usa?
Mas cobertura e tamanho puxam para lados opostos. Um roteador que sempre escolhe tudo tem cobertura perfeita, e não serve para nada. Acompanhe os dois juntos.
E vigie a taxa de expansão por grupo. Um grupo pedido com frequência deve entrar na seleção inicial daquele tipo de tarefa.
Dá para montar o dataset do roteador com os traces que você já tem. Para cada tarefa resolvida com sucesso, os grupos esperados são os grupos das ferramentas que o agente chamou de fato. Inclua de propósito tarefas que cruzam assuntos, como emita a nota e agende a coleta. E vagas, como veja o que aconteceu com este pedido. E quando acrescentar uma ferramenta, rode de novo o eval do roteador. Uma descrição de grupo alterada pode desviar tarefas que antes iam para o lugar certo.
E um ponto de segurança. O roteador diminui a superfície. Ferramentas com efeito colateral só aparecem nas rotas que precisam delas. Uma pergunta sobre prazo de entrega não tem como disparar um estorno. Mas não trate isso como controle de acesso. Um texto malicioso dentro da tarefa consegue convencer o roteador a escolher outro grupo, e o agente pode pedir grupos sozinho. Permissão de verdade mora na ferramenta e no backend. Quem é o usuário, o que ele pode fazer e qual o limite de valor.
Então, a ideia para levar. Quando toda ferramenta é legítima, mas nenhuma tarefa precisa de todas, escolha a caixa de ferramentas antes de o trabalho começar. Uma etapa pequena escolhe os grupos, o agente trabalha com uma lista curta e um jeito de pedir mais, e você mede cobertura e tamanho juntos.
Na próxima aula, a gente sai das ferramentas e vai para a conversa em si, e o que acontece quando ela fica longa: context rot. Até lá.
Tool Router
Roteador de Ferramentas (Tool Router) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Ferramentas e ação. Escolher um subconjunto pequeno de ferramentas antes de chamar o modelo principal.
Escolher um subconjunto pequeno de ferramentas antes de chamar o modelo principal. Serve para agentes que cobrem muitos assuntos e precisam de muitas ferramentas no total, mas poucas em cada tarefa.
Um mecânico que leva para o carro só a maleta do conserto da vez, não a oficina inteira.
Fábio cuida do assistente de back-office de uma distribuidora. O agente concilia pagamentos, emite notas fiscais, consulta estoque, abre chamados com transportadoras e responde dúvidas de comissão. Depois de consolidar as ferramentas duplicadas, sobraram 34, todas legítimas e diferentes entre si.
Mesmo assim o agente erra. Num pedido de conciliação, ele chama consultar_estoque em vez de buscar_pagamento, porque as duas recebem um código de pedido e as descrições começam parecidas. O trace mostra o modelo hesitando entre ferramentas que nem deveriam estar na mesa.
Tem também a conta. Cada chamada leva as 34 definições, e o contexto de toda tarefa começa com milhares de tokens de ferramentas que não têm nada a ver com ela. Num loop de seis voltas, essas definições vão seis vezes para o modelo.
Fábio não pode remover ferramentas: todas são usadas por alguém. O que ele pode mudar é quantas o modelo vê de cada vez.
A pergunta que este padrão responde: como manter muitas ferramentas no sistema sem obrigar o modelo a escolher entre todas em toda chamada?
Organize as ferramentas em grupos por assunto (conciliação, notas, estoque, logística). Antes do loop do agente, um passo curto decide quais grupos a tarefa precisa. O agente principal roda só com esses grupos.
O roteador pode ser um modelo pequeno com saída estruturada, uma busca por similaridade entre a tarefa e a descrição dos grupos, ou regras simples quando o canal já diz o assunto. Sempre exista um caminho de volta: se o agente perceber que precisa de outro grupo, ele pode pedir.
A escolha que importa é tirada do modelo grande e colocada num passo pequeno, testável e barato. O agente principal deixa de responder “qual destas 34?” e passa a responder “qual destas 8?”.
Chega a tarefa: “Concilia o pagamento do pedido 88231 com o extrato de ontem e, se bater, dá baixa no título”. O roteador recebe a tarefa e quatro linhas de descrição, uma por grupo:
billing: Use para conciliar pagamentos com extratos e dar baixa em títulos.
invoices: Use para emitir, cancelar ou consultar notas fiscais (NF-e).
stock: Use para saldo, reserva e movimentação de estoque.
logistics: Use para frete, coleta e chamados com transportadoras.
E devolve uma saída estruturada curta:
{ "groups": ["billing"], "confidence": 0.92 }
O agente principal roda com as 7 ferramentas de billing e mais request_group. A ferramenta de estoque, que causava a confusão, nem aparece.
Agora a conta, com números ilustrativos. Suponha que cada definição de ferramenta ocupe uns 250 tokens. As 34 somam cerca de 8.500 tokens por chamada; num loop de seis voltas, são uns 51 mil tokens só de definições. Com 8 ferramentas, a mesma tarefa carrega uns 12 mil. O roteador custa uma chamada curta num modelo pequeno, com o texto da tarefa e quatro linhas de grupo.
Os números exatos variam com o seu catálogo e o seu provedor. O formato da conta não varia: o roteador paga uma vez, a economia acontece em toda volta do loop.
flowchart LR
IN(["1. Tarefa"]):::io --> R(["2. Roteador de ferramentas"]):::llm
C[("3. Catálogo de grupos")]:::store --> R
R --> S["4. Subconjunto escolhido"]:::tool
S --> A(["5. Agente principal"]):::llm
A --> T["6. Ferramentas do grupo"]:::tool
T --> A
A -->|"precisa de outro grupo"| R
A --> OUT(["7. Resposta"]):::io
schema Selection { groups: list["billing" | "invoices" | "stock" | "logistics"], confidence: float }
GROUPS = catalog.load("tool-groups@4")
function handle(task):
# 2. escolher grupos com modelo pequeno
sel = llm(ROUTER_PROMPT + GROUPS.summaries(), task) -> Selection
trace.span("tool_route", sel)
if sel.confidence < 0.6:
sel.groups = GROUPS.default_set() # 4. conjunto padrão seguro
tools = GROUPS.tools_for(sel.groups) + [request_group]
# 5 e 6. agente principal só com o subconjunto
while budget.check():
reply = llm_with_tools(AGENT_PROMPT, tools, state)
if reply.is_final:
return reply.text # 7
for call in reply.tool_calls:
if call.name == "request_group":
tools += GROUPS.tools_for([call.args.group])
trace.span("tool_route_expand", call.args)
state.add(execute(call))
request_group para pedir um grupo que ficou de fora.request_group, um erro do roteador vira um agente que responde sem a ferramenta certa, ou que inventa o dado.| Variação | Custo extra | Lida com ambiguidade | Quando escolher |
|---|---|---|---|
| Regra | nenhum | não | o canal já diz o assunto |
| Embedding | uma busca | pouco | muitos grupos, tarefas curtas e claras |
| Modelo pequeno | uma chamada | bem | tarefas em texto livre que misturam assuntos |
| Busca do provedor | nenhum passo seu | depende do provedor | o problema principal é token |
| Aspecto | O que acontece | Como aparece no trace |
|---|---|---|
| Chamadas | Uma extra por tarefa, no modelo pequeno | span tool_route antes do loop |
| Latência | O tempo do roteador soma ao início | duração de tool_route |
| Custo | Menos tokens de ferramenta em toda volta do loop compensa o roteador | tokens de entrada menores em cada step |
| Falha: grupo faltando | O agente não tem a ferramenta e responde sem ela | request_group frequente para o mesmo grupo |
| Falha: grupos demais | O roteador escolhe tudo por segurança | média de grupos por tarefa perto do total |
A falha mais cara é a silenciosa: o grupo certo ficou de fora e o agente não pediu. Ele responde “não encontrei o pagamento” com toda a calma do mundo. Por isso o eval de cobertura, abaixo, importa mais que o de tamanho.
{ "task": "Concilia o pagamento do pedido 88231 com o extrato de ontem", "expected_groups": ["billing"] }
Monte o dataset a partir dos traces que você já tem. Para cada tarefa resolvida com sucesso, os grupos esperados são os grupos das ferramentas que o agente de fato chamou. Inclua de propósito tarefas que cruzam assuntos (“emite a nota e agenda a coleta”) e tarefas vagas (“vê o que aconteceu com o pedido 88231”).
Cobertura e tamanho puxam para lados opostos. Um roteador que sempre escolhe tudo tem cobertura perfeita e não serve para nada. Acompanhe os dois juntos.
tool_route com grupos, confiança e versão do catálogo.request_group por grupo. Um grupo pedido com frequência deve entrar na seleção inicial daquele tipo de tarefa.O roteador reduz a superfície: ferramentas de efeito só aparecem nas rotas que precisam delas. Não trate isso como controle de acesso. Um texto malicioso dentro da tarefa pode convencer o roteador a escolher outro grupo, e o agente também pode pedir grupos via request_group.
A permissão de verdade fica na ferramenta e no backend: quem é o usuário, o que ele pode fazer, qual é o limite de valor. Veja Reduzir Permissões da Ferramenta.
| Prós | Contras |
|---|---|
| Menos confusão na escolha de ferramenta | Uma chamada extra no começo de cada tarefa |
| Menos tokens em toda volta do loop | Um grupo errado deixa o agente sem a ferramenta certa |
| Ferramentas de efeito ficam fora das tarefas que não precisam | O catálogo de grupos vira mais uma coisa para manter |
| Grupos novos entram sem mexer nos outros | Tarefas que cruzam assuntos ficam mais lentas |
As abas fazem a seleção com um modelo pequeno e rodam o agente só com os grupos escolhidos. Python e TypeScript montam a lista e chamam o modelo. LangGraph separa as duas peças: um nó escolhe os grupos, o nó seguinte cria o agente só com as ferramentas desses grupos.
Python. Use quando o agente roda em Python e você monta a lista de ferramentas a cada tarefa.
from typing import Literal
import anthropic
from pydantic import BaseModel
client = anthropic.Anthropic()
GROUPS = { # 3. catálogo: grupo -> (descrição, ferramentas)
"billing": ("Use para conciliar pagamentos com extratos e dar baixa em títulos.", BILLING_TOOLS),
"invoices": ("Use para emitir, cancelar ou consultar notas fiscais.", INVOICE_TOOLS),
"stock": ("Use para saldo, reserva e movimentação de estoque.", STOCK_TOOLS),
"logistics": ("Use para frete, coleta e chamados com transportadoras.", LOGISTICS_TOOLS),
}
Group = Literal["billing", "invoices", "stock", "logistics"]
class Selection(BaseModel):
groups: list[Group]
confidence: float
def select_tools(task: str) -> list[dict]:
summary = "\n".join(f"{name}: {desc}" for name, (desc, _) in GROUPS.items())
sel = client.messages.parse( # 2. roteador com modelo pequeno
model="claude-haiku-4-5",
max_tokens=200,
system=f"Escolha os grupos de ferramentas que a tarefa precisa.\n{summary}",
messages=[{"role": "user", "content": task}],
output_format=Selection,
).parsed_output
groups = sel.groups if sel.confidence >= 0.6 else ["billing"] # 4. padrão seguro
return [tool for g in groups for tool in GROUPS[g][1]]
def handle(task: str) -> str:
tools = select_tools(task)
return run_agent(task, tools) # 5. o mesmo loop de antes, com menos ferramentasO que você ganhou: a lista de ferramentas de cada tarefa fica explícita e testável. O que você perdeu: a expansão via request_group precisa ser tratada no loop.
TypeScript. Use quando o agente roda em Node e você quer os grupos tipados.
import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";
const client = new Anthropic();
const GROUPS = {
billing: { desc: "Use para conciliar pagamentos com extratos e dar baixa em títulos.", tools: BILLING_TOOLS },
invoices: { desc: "Use para emitir, cancelar ou consultar notas fiscais.", tools: INVOICE_TOOLS },
stock: { desc: "Use para saldo, reserva e movimentação de estoque.", tools: STOCK_TOOLS },
logistics: { desc: "Use para frete, coleta e chamados com transportadoras.", tools: LOGISTICS_TOOLS },
} as const;
type Group = keyof typeof GROUPS;
const Selection = z.object({
groups: z.array(z.enum(["billing", "invoices", "stock", "logistics"])),
confidence: z.number(),
});
export async function selectTools(task: string): Promise<Anthropic.Tool[]> {
const summary = Object.entries(GROUPS).map(([k, g]) => `${k}: ${g.desc}`).join("\n");
const { parsed_output: sel } = await client.messages.parse({
model: "claude-haiku-4-5",
max_tokens: 200,
system: `Escolha os grupos de ferramentas que a tarefa precisa.\n${summary}`,
messages: [{ role: "user", content: task }],
output_config: { format: zodOutputFormat(Selection) },
});
const groups: Group[] = sel && sel.confidence >= 0.6 ? sel.groups : ["billing"];
return groups.flatMap((g) => [...GROUPS[g].tools]);
}O que você ganhou: o compilador garante que só existem grupos conhecidos. O que você perdeu: o mesmo que em Python; o loop do agente continua por sua conta.
Python + LangGraph. Use quando a escolha dos grupos é um nó e o agente nasce no nó seguinte, só com essas ferramentas.
from typing import Literal
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.graph import END, START, StateGraph
from pydantic import BaseModel
from typing_extensions import TypedDict
Group = Literal["billing", "invoices", "stock", "logistics"]
class Selection(BaseModel):
groups: list[Group]
confidence: float
class State(TypedDict):
task: str
groups: list[str]
answer: str
router = init_chat_model("anthropic:claude-haiku-4-5", temperature=0)
def select(state: State) -> dict:
# 2. o mesmo GROUPS da aba Python: nome -> (descrição, ferramentas)
summary = "\n".join(f"{name}: {desc}" for name, (desc, _) in GROUPS.items())
sel = router.with_structured_output(Selection).invoke(
[("system", f"Escolha os grupos de ferramentas que a tarefa precisa.\n{summary}"),
("human", state["task"])]
)
groups = sel.groups if sel.confidence >= 0.6 else ["billing"] # 4
return {"groups": list(groups)}
def run(state: State) -> dict:
tools = [tool for g in state["groups"] for tool in GROUPS[g][1]] # 5
agent = create_agent("anthropic:claude-opus-5-5", tools=tools,
system_prompt="Você é o assistente de back-office.")
result = agent.invoke({"messages": [("human", state["task"])]}, {"recursion_limit": 16})
return {"answer": result["messages"][-1].text}
graph = StateGraph(State)
graph.add_node("select", select)
graph.add_node("run", run)
graph.add_edge(START, "select")
graph.add_edge("select", "run")
graph.add_edge("run", END)
app = graph.compile()O loop de create_agent tem as outras formas em Loop ReAct. request_group continua uma ferramenta sua, dentro de tools.
O que você ganhou: o agente é criado depois da escolha, então a ferramenta de estoque nem existe no segundo nó. O que você perdeu: recursion_limit conta passos do grafo, não reais.
Responda as 5 perguntas para concluir a aula. Só assistir não conclui: a checagem é o que conta para a trilha e para o certificado.
Roteador de Ferramentas (Tool Router) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Ferramentas e ação. Escolher um subconjunto pequeno de ferramentas antes de chamar o modelo principal.
Ferramentas Demais, System Prompt Roteador, Mega-Agente e Ferramentas Fantasma.
Nos cenários reais Automação de back-office com execução durável e Bot de suporte com escalonamento humano.