Campos Condicionais¶
Com use_search=True e search_per_field=True, cada campo do modelo é preenchido por uma chamada própria do agente. Nesse modo, um campo pode depender do valor de outro:
- Execução condicional: o campo só é pedido ao LLM quando a condição vale; caso contrário, fica
Nonee não gasta busca. - Campos aninhados podem ser lidos na condição, com notação de ponto.
- Ordem de execução derivada da condição: o campo lido vem antes do campo que depende dele.
Configuração Básica¶
Use a chave condition no json_schema_extra do campo:
condition(dict): a dependência, e portanto a ordem de execução, é derivada do campo emcondition['field']. O dict trazfield, o nome do campo em texto, e um operador:equals,not_equals,in,not_inouexists.inenot_inrecebem uma lista, tupla, set ou frozenset dos valores aceitos.condition(callable): recebe os campos já preenchidos e devolve bool. Monte ojson_schema_extracomfield_condition(condicao, depends_on=...), que pede junto os campos lidos pela função, para que venham antes: o nome de um campo em texto, ou uma lista ou tupla de nomes. O helper devolve{'condition': condicao, 'depends_on': [...]}, o mesmo dict que se escreveria à mão; escrito à mão, o checker de tipos recusa oField, porque o Pydantic tipajson_schema_extracomo JSON e uma função não é JSON.
Outra forma de condition ou de depends_on levanta ValueError antes de processar.
Como o campo pulado fica None, declare-o como opcional (str | None = None).
Exemplo 1: Pessoa Física vs Jurídica¶
from pydantic import BaseModel, Field
from dataframeit import dataframeit
class PessoaInfo(BaseModel):
tipo: str = Field(
description="Tipo de pessoa: 'pf' para pessoa física ou 'pj' para pessoa jurídica"
)
cpf: str | None = Field(
default=None,
description="CPF da pessoa física",
json_schema_extra={'condition': {'field': 'tipo', 'equals': 'pf'}},
)
cnpj: str | None = Field(
default=None,
description="CNPJ da pessoa jurídica",
json_schema_extra={'condition': {'field': 'tipo', 'equals': 'pj'}},
)
razao_social: str | None = Field(
default=None,
description="Razão social da empresa",
json_schema_extra={'condition': {'field': 'tipo', 'equals': 'pj'}},
)
textos = [
"João Silva, CPF 123.456.789-00",
"Empresa XYZ LTDA, CNPJ 12.345.678/0001-90",
]
resultado = dataframeit(
textos,
PessoaInfo,
"Extraia as informações da pessoa ou empresa: {texto}",
use_search=True,
search_per_field=True, # condition exige busca por campo
)
# Linha 1: tipo='pf', cpf='123.456.789-00', cnpj=None, razao_social=None
# Linha 2: tipo='pj', cpf=None, cnpj='12.345.678/0001-90', razao_social='Empresa XYZ LTDA'
Exemplo 2: Campos Encadeados¶
class EnderecoInfo(BaseModel):
pais: str = Field(description="País")
estado: str | None = Field(
default=None,
description="Estado (apenas para Brasil)",
json_schema_extra={'condition': {'field': 'pais', 'equals': 'Brasil'}},
)
cep: str | None = Field(
default=None,
description="CEP (apenas para Brasil)",
json_schema_extra={'condition': {'field': 'estado', 'exists': True}},
)
zip_code: str | None = Field(
default=None,
description="Código postal (apenas para outros países)",
json_schema_extra={'condition': {'field': 'pais', 'not_equals': 'Brasil'}},
)
A ordem de execução é pais → estado → cep e pais → zip_code.
Exemplo 3: Condição Callable com Múltiplas Dependências¶
Quando a condição combina vários campos, use uma função e declare os campos lidos em depends_on:
from dataframeit import field_condition
class PedidoInfo(BaseModel):
tipo_cliente: str = Field(description="Tipo do cliente: 'novo' ou 'vip'")
valor_pedido: float = Field(description="Valor total do pedido")
desconto: float | None = Field(
default=None,
description="Desconto aplicado",
json_schema_extra=field_condition(
lambda dados: (
dados.get('tipo_cliente') == 'vip'
and (dados.get('valor_pedido') or 0) > 1000
),
depends_on=['tipo_cliente', 'valor_pedido'],
),
)
Para somar outras chaves por campo, desempacote o dict: json_schema_extra={**field_condition(...), 'search_depth': 'advanced'}.
Operadores de Condição¶
# Igualdade
{'field': 'tipo', 'equals': 'pf'}
# Diferença
{'field': 'tipo', 'not_equals': 'pj'}
# Está na lista
{'field': 'status', 'in': ['ativo', 'pendente']}
# Não está na lista
{'field': 'status', 'not_in': ['inativo', 'cancelado']}
# Campo preenchido (não é None)
{'field': 'campo_opcional', 'exists': True}
Sem depends_on, a ordem de um callable não é garantida.
Campos Aninhados em Condições¶
A condição pode ler um campo de modelo aninhado com notação de ponto. A dependência derivada é o campo raiz:
class Endereco(BaseModel):
cidade: str = Field(description="Cidade")
uf: str = Field(description="UF")
class Entrega(BaseModel):
endereco: Endereco = Field(description="Endereço de entrega")
taxa_entrega: float | None = Field(
default=None,
description="Taxa de entrega",
json_schema_extra={
'condition': {'field': 'endereco.cidade', 'in': ['São Paulo', 'Rio de Janeiro']}
},
)
A condição fica no campo de nível superior. condition ou depends_on num campo de modelo aninhado, ou de item de lista, levanta ValueError antes de processar.
Dependências Circulares¶
Um ciclo é detectado antes da primeira linha:
class ModeloInvalido(BaseModel):
a: str | None = Field(default=None, json_schema_extra={'condition': {'field': 'b', 'equals': 'x'}})
b: str | None = Field(default=None, json_schema_extra={'condition': {'field': 'a', 'equals': 'x'}})
# ValueError: Dependências circulares detectadas: a -> b -> a
Logging e Debug¶
A ordem de execução, as dependências e a avaliação de cada condição saem em nível DEBUG, e os campos pulados, em INFO, nos loggers dataframeit.conditional e dataframeit.agent:
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('dataframeit').setLevel(logging.DEBUG)
Combinando com Outras Configurações¶
A condição convive com as outras chaves de busca por campo:
class InfoCompleta(BaseModel):
tipo: str
detalhes_pf: str | None = Field(
default=None,
json_schema_extra={
'condition': {'field': 'tipo', 'equals': 'pf'},
'search_depth': 'advanced',
'max_results': 10,
'max_search_calls': 3,
'prompt_append': 'Inclua informações detalhadas sobre histórico.',
},
)
Limitações¶
- Condicionais só funcionam com
use_search=Trueesearch_per_field=True; sem isso,conditionoudepends_onno modelo levantaValueError. - Com
search_groups, a condição de um campo agrupado é avaliada antes da chamada do grupo, e o campo com condição falsa fica fora dela; se a condição depende de outro campo do mesmo grupo, ela é avaliada com a resposta do grupo, e o campo com condição falsa ficaNone. - A condição só pode ler campos que existem no modelo.
- Dependências circulares não são permitidas, inclusive entre um grupo e campos de fora dele.
- Cada condição é avaliada uma vez: antes da chamada do campo, ou depois da resposta do grupo quando depende de outro campo do mesmo grupo.
- Os campos com condição são processados em sequência, na ordem das dependências; um campo pulado não consome busca.