Ir para o conteúdo

29 · O caçador de bugs e o seletor juntos

O seletor decide o que o seu agente faz com um turno: qual ferramenta, com quais valores, ou nada. O caçador de bugs encontra cada caminho que as suas regras deixam até uma ação, ou não encontra nenhum. Juntos, eles respondem a uma pergunta que nenhum responde sozinho: toda chamada de ferramenta passa pelo seletor? Este capítulo é essa busca sobre o reembolso da loja, e o portão que a mantém verdadeira quando alguém acrescenta um caminho que a contorna.

Idiomas

O seletor lê turnos escritos em inglês e em português do Brasil (pt-BR). O idioma é escolhido, nunca adivinhado a partir do turno: na página Settings do console (Language the selector reads), em language numa requisição de plano, ou no settings.language do seu arquivo de vocabulário; sem nenhum deles, inglês. Um turno em português pode ser digitado com os acentos ou sem eles. O português europeu não é uma gramática que o seletor tenha, e um turno em qualquer outra língua não é lido.

Por que eles se combinam

Uma política de guardrail que chama uma ferramenta decide, nos seus próprios fluxos, quando a chamada roda. A verificação do seletor (capítulo 24) decide cada chamada pelo seu catálogo e pelo seu rulebook -- mas só as chamadas que lhe são enviadas. Um fluxo que chama a ferramenta direto nunca pergunta. Então o seletor só vale tanto quanto a promessa da política de perguntar a ele toda vez, e essa promessa é exatamente o que o caçador busca: um caminho até uma ação que não pode rodar sem um controle.

Os rails

O reembolso da loja atrás das duas ações NeMo do capítulo 27: stalwart_route lê a mensagem do usuário num plano e recusa o que o plano recusa, e stalwart_check_tool verifica o reembolso antes de o fluxo rodá-lo. Este é o próprio snippet em Colang 1.0 do SDK, com a intenção e a ferramenta da loja:

# O estorno da loja atrás do rail de entrada e da guarda de execução da Stalwart, do
# stalwart_agents.langchain.nemo.snippet("1.0"), com a intenção e a ferramenta da loja, em português.

define user request refund
  "estorne o meu pedido"
  "quero o meu dinheiro de volta"

define bot refuse stalwart declined
  "Não posso fazer isso: não é algo que este assistente possa fazer."

define bot refuse stalwart tool
  "Não posso fazer isso: a verificação não permitiu."

define bot confirm refund
  "O estorno foi feito."

define flow stalwart route input
  user request refund
  execute stalwart_route
  if $stalwart_declined
    bot refuse stalwart declined
    stop

define flow stalwart guarded refund
  user request refund
  execute stalwart_check_tool
  if $stalwart_allowed
    execute refund_card
    bot confirm refund
  else
    bot refuse stalwart tool

A obrigação

A equipe escreve a promessa como uma linha da sua tabela de propriedades (capítulo 11): o reembolso não pode rodar sem $stalwart_allowed, que só a verificação define.

{
  "policy": "rails.co",
  "properties": [
    {
      "id": "SEL-01",
      "description": "Um estorno roda sem que a verificação do seletor o permita.",
      "action": "refund_card",
      "context": "stalwart_allowed",
      "pins": {},
      "expect": "UNSAT",
      "gate": "hard"
    }
  ]
}

Enviada com a varredura (properties), a linha se junta às perguntas do próprio caçador como uma da equipe, e é feita a todo fluxo que chega ao reembolso:

result = client.hunt("rails.co", properties="properties.json", gate=True)
print("gate passed:", result.result["gate"]["passed"])
for finding in result.findings:
    print(f"{finding.rule} {finding.raw['title']} ({finding.raw['type']}) on {finding.raw['action']}")
gate passed: True
QG101 Unchecked action (hotspot) on stalwart_route
QG101 Unchecked action (hotspot) on stalwart_check_tool
gate passed: False | no_new_unshippable: QG005 42d4d03a70a3
QG002 Escapable guard at rails.hurried.co:28 (stalwart guarded refund) | row SEL-01
   The policy gates 'refund_card' on 'stalwart_allowed', and there is a way past it.
QG005 Unshippable policy at rails.hurried.co:40 (quick refund) 
   line 40: tool action 'refund_card' is registered from flow 'quick refund' as well as from 'stalwart guarded refund'

A primeira varredura passa. Os seus dois achados são hotspots, itens de revisão que nunca reprovam o portão: stalwart_route e stalwart_check_tool rodam sob nenhum controle, porque eles são o controle, rodando em todo turno correspondente por projeto. O snippet do SDK diz isso nos seus próprios comentários.

O portão que a mantém verdadeira

Um colega acrescenta um reembolso direto da intenção:

# Acrescentado às pressas: um estorno direto da intenção.
define user ask quick refund
  "só estorna"

define flow quick refund
  user ask quick refund
  execute refund_card
result = client.hunt("rails.hurried.co", properties="properties.json", gate=True)
print("gate passed:", result.result["gate"]["passed"], "|", "; ".join(result.result["gate"]["failed"]))
for finding in result.findings:
    if finding.raw["type"] != "hotspot":
        where = finding.location
        print(f"{finding.rule} {finding.raw['title']} at {where['file']}:{where['line']} ({where['flow']})",
              f"| row {finding.raw['obligation']}" if finding.raw["obligation"] else "")
        print("  ", finding.raw["message"].split(";")[0])

A segunda varredura, na saída acima, reprova o portão. QG002 nomeia a linha que ela quebra, SEL-01, na linha protegida que a equipe lê como segura: o reembolso está condicionado à verificação, e agora há um caminho que passa por ela. QG005 acrescenta que o reembolso está registrado por dois fluxos, então o mais fraco decide, e uma política que não pode ser entregue é o que reprova o portão; QG002 sozinho, numa escrita que não é crítica, seria um aviso. Rodada na CI pela GitHub Action num projeto (capítulo 28), esta é a verificação reprovada do pull request, antes que o fluxo chegue a alguém.

Um agente da Parte I não precisa de linha: o grafo que o agent.yaml monta põe toda ferramenta no passo de ferramentas protegido, e stalwart verify busca, em cada caminho, um que chegue a uma ferramenta contornando-o (capítulo 31). O capítulo 27 mostra a mesma busca reprovando quando um passo que contorna a verificação é acrescentado à mão.

O que uma varredura limpa significa, e o que não significa

  • Ela não encontrou caminho até o reembolso que contorne a verificação do seletor: nenhum fluxo, e nenhuma sequência de turnos que a varredura desdobra, chega a refund_card sem $stalwart_allowed.
  • Ela não diz nada sobre se o seletor escolheu bem. Se a verificação permitiu a chamada certa para o turno é decisão do roteador, lida pelas suas regras a partir do que as suas ferramentas declaram -- os limites do capítulo 20, que uma varredura não consegue levantar.
  • Ela cobre as obrigações que recebeu. Uma ferramenta sem linha é relatada como hotspot onde nada a protege, não provada; escreva uma linha para toda ferramenta que precisa passar pelo seletor.