Normalização de Esquemas
ADK-Rust normaliza automaticamente os esquemas de ferramentas de MCP para cada provedor LLM no momento da solicitação. Isso significa que as ferramentas MCP funcionam perfeitamente entre Gemini, OpenAI, Anthropic e outros provedores, sem ajustes manuais no esquema.
Como Funciona
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset descobre as ferramentas e armazena seu
inputSchemabruto sem modificações - Quando o modelo cria uma solicitação, ele chama
schema_adapter().normalize_schema()no esquema de cada ferramenta - Cada provedor tem seu próprio adaptador, que aplica apenas as transformações exigidas por seu API
- Os resultados são armazenados em cache por hash de conteúdo para evitar normalizações redundantes
Comportamento por Provedor
| Recurso | Gemini | OpenAI Strict | OpenAI | Anthropic | Genérico |
|---|---|---|---|---|---|
Resolução de $ref | ✅ Incorpora | ❌ Preserva | ❌ Preserva | ❌ Preserva | ❌ Preserva |
anyOf/oneOf | Recolhe | Preserva | Preserva | Preserva | Preserva |
allOf | Mescla | Preserva | Preserva | Preserva | Preserva |
additionalProperties | Remove | Define false | Preserva | Preserva | Preserva |
| Matrizes de tipo | Recolhe | Preserva | Preserva | Preserva | Preserva |
$schema | Remove | Remove | Remove | Remove | Remove |
if/then/else | Remove | Remove | Remove | Remove | Remove |
const | → enum | → enum | → enum | Preserva | → enum |
format não compatível | Remove | Remove | Remove | Preserva | Remove |
| Limite de profundidade de aninhamento | 5 níveis | Nenhum | Nenhum | Nenhum | Nenhum |
exclusiveMin/Max | Remove | Preserva | Preserva | Preserva | Preserva |
O trait SchemaAdapter
Todos os adaptadores implementam este trait de adk-core:
use serde_json::Value;
use std::borrow::Cow;
pub trait SchemaAdapter: Send + Sync + std::fmt::Debug {
/// Normalize a raw JSON Schema for this provider.
fn normalize_schema(&self, schema: Value) -> Value;
/// Normalize a tool name (default: truncate to 64 bytes at UTF-8 boundary).
fn normalize_tool_name<'a>(&self, name: &'a str) -> Cow<'a, str>;
/// Fallback schema when no parameters_schema is provided.
fn empty_schema(&self) -> Value;
}
Cada implementação de Llm retorna seu adaptador por meio de schema_adapter():
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?;
let adapter = model.schema_adapter(); // Returns &GeminiSchemaAdapter
Adaptadores disponíveis
GeminiSchemaAdapter
O adaptador mais agressivo. Aplica todas as transformações destrutivas exigidas pelo API de chamada de funções do Gemini:
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
use adk_core::SchemaAdapter;
// Standard Gemini API
let adapter = GeminiSchemaAdapter::new();
// Vertex AI (sets additionalProperties: false instead of removing)
let adapter = GeminiSchemaAdapter::vertex_ai();
Pipeline de transformação:
- Resolve
$ref(incorporando definições, interrompendo ciclos na profundidade 10) - Remove
$schema - Recolhe
anyOf/oneOf→ primeiro subesquema não nulo - Mescla subesquemas
allOf - Recolhe arrays de tipos (
["string", "null"]→"string") - Remove
if/then/else - Converte
const→enumde elemento único - Remove null de arrays
enum - Adiciona
type: "object"implícito - Remove palavras-chave não compatíveis
- Remove valores
formatnão compatíveis - Impõe profundidade de aninhamento (5 níveis)
- Remove
definitions/$defs
OpenAiStrictSchemaAdapter
Preserva a estrutura do esquema enquanto adiciona additionalProperties: false para saídas estruturadas:
use adk_model::openai::OpenAiStrictSchemaAdapter;
use adk_core::SchemaAdapter;
let adapter = OpenAiStrictSchemaAdapter;
OpenAiSchemaAdapter
Correções mínimas e seguras para o modo não estrito:
use adk_model::openai::OpenAiSchemaAdapter;
let adapter = OpenAiSchemaAdapter;
AnthropicSchemaAdapter
Quase um repasse direto — o Anthropic é compatível com a maioria dos recursos de esquema JSON:
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Padrão para provedores desconhecidos (Ollama, DeepSeek etc.):
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Cache de esquemas
Os esquemas normalizados são armazenados em cache por hash de conteúdo para evitar cálculos redundantes:
use adk_core::{GenericSchemaAdapter, SchemaCache};
use serde_json::json;
use std::sync::Arc;
let cache = SchemaCache::for_adapter(Arc::new(GenericSchemaAdapter));
let schema = json!({"type": "object", "properties": {"name": {"type": "string"}}});
// First call normalizes and caches
let result = cache.normalize(&schema);
// Subsequent calls return cached result (no re-normalization)
let cached = cache.normalize(&schema);
// Invalidate when tools change
cache.clear();
Cada cache possui uma instância de adaptador, portanto as entradas normalizadas para diferentes
provedores ou configurações de adaptador não podem colidir. Os clientes dos provedores usam esses
caches automaticamente durante generate_content().
Truncamento de nomes de ferramentas
Todos os adaptadores truncam nomes de ferramentas que excedem 64 bytes em limites válidos de caracteres UTF-8:
use adk_core::SchemaAdapter;
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
let adapter = GeminiSchemaAdapter::new();
// Short names pass through unchanged
let name = adapter.normalize_tool_name("get_weather");
assert_eq!(name, "get_weather");
// Long names are truncated to 64 bytes
let long = "mcp_server_github_com_organization_repository_pull_request_review_comments";
let truncated = adapter.normalize_tool_name(long);
assert!(truncated.len() <= 64);
// Multi-byte characters are never split
let emoji_name = "🔧_tool_名前が長い";
let result = adapter.normalize_tool_name(emoji_name);
assert!(std::str::from_utf8(result.as_bytes()).is_ok());
Utilitários compartilhados
O módulo adk_core::schema_utils fornece funções de transformação componíveis que os adaptadores usam internamente:
use adk_core::schema_utils;
use serde_json::json;
let mut schema = json!({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"status": { "const": "active" },
"email": { "type": "string", "format": "hostname" }
},
"if": { "properties": { "x": { "type": "number" } } },
"then": { "required": ["x"] }
});
// Apply individual transforms
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::convert_const_to_enum(&mut schema);
schema_utils::strip_unsupported_formats(&mut schema, &["date-time", "email", "uri"]);
Utilitários disponíveis:
strip_schema_keyword— remove$schemastrip_conditional_keywords— removeif/then/elseadd_implicit_object_type— adicionatype: "object"quandopropertiesexisteconvert_const_to_enum— converteconstemenumde elemento únicostrip_unsupported_formats— remove valores de formato que não estão na lista de permitidosstrip_null_from_enum— remove nulo de matrizes de enumeraçãotruncate_tool_name— trunca no limite UTF-8resolve_refs— incorpora referências$refde definiçõescollapse_combiners— reduzanyOf/oneOfao primeiro valor não nulomerge_all_of— mescla subesquemasallOfcollapse_type_arrays— reduz["string", "null"]a"string"enforce_nesting_depth— substitui esquemas profundos por{"type": "object"}
Adaptadores personalizados
Implemente SchemaAdapter para provedores personalizados:
use adk_core::{SchemaAdapter, schema_utils};
use serde_json::Value;
use std::borrow::Cow;
#[derive(Debug)]
struct MyProviderAdapter;
impl SchemaAdapter for MyProviderAdapter {
fn normalize_schema(&self, mut schema: Value) -> Value {
// Apply only the transforms your provider needs
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::add_implicit_object_type(&mut schema);
// Keep everything else as-is
schema
}
}
Exemplo
Execute a demonstração de normalização de esquema para ver todos os adaptadores em ação:
cd examples/schema_normalization
cargo run
Nenhuma chave API é necessária — isso demonstra a lógica de normalização localmente.
Migração de sanitize_schema
Se anteriormente você dependia de McpToolset retornar esquemas pré-sanitizados:
- Nenhuma alteração de código é necessária para o uso padrão. O trait
ToolsetAPI permanece inalterado. - Os esquemas agora são normalizados no momento da solicitação pelo adaptador do modelo, e não no registro da ferramenta.
- Se você chamava
parameters_schema()esperando uma saída formatada pelo Gemini, o esquema bruto agora é retornado. Use oSchemaAdapterapropriado para normalizá-lo por conta própria, se necessário.
Anterior: ← MCP Ferramentas | Próximo: Sessões →