Ir para o conteúdo

32 · Contra o que toda chamada de ferramenta é verificada

Idiomas

O seletor entende turnos escritos em inglês. O português é a sua próxima gramática e ainda não foi lançado; até lá, um turno escrito em português não é lido como português.

Toda chamada que o seu agente faz a uma ferramenta é verificada antes de rodar, não importa quem a pediu: o turno da pessoa, o modelo, ou um desfazer depois de uma falha. A verificação responde allow, confirm (uma pessoa precisa aprová-la antes) ou refuse, com os motivos. Este capítulo é a ordem da verificação, e o que cada passo significa.

Onde a verificação roda

A verificação roda no runtime licenciado na sua máquina, com o catálogo e o rulebook que você registrou (capítulo 31). O seu agente a chama no seu passo de ferramentas protegido, uma vez por chamada, e age conforme a resposta: roda uma vez uma chamada permitida, espera por uma pessoa em confirm, e não roda nada em refuse. Nada da chamada -- a ferramenta, os argumentos, o turno -- é enviado para nós decidirmos.

A ordem

Os passos são feitos nesta ordem, e o primeiro que responde encerra a verificação. Gerada a partir da própria verificação do runtime:

Ordem O que é verificado Motivos com que responde Exemplo
1 a ferramenta está no catálogo e este agente pode alcançá-la; com vários agentes, a chamada nomeia o seu agente; com a licença vencida, uma escrita é recusada, ou toda chamada quando a configuração do tenant diz refuse unknown_tool, agent_required, licence_lapsed delete_order, que o catálogo não tem: refuse, unknown_tool
2 o histórico da sessão se verifica, entrada por entrada, pela assinatura que cada entrada carrega sobre as anteriores history_invalid um histórico cuja primeira entrada foi editada à mão: refuse, history_invalid
3 os argumentos contra o schema da ferramenta; um argumento que o schema não declara é recusado argument_schema refund_card com um note que ela não declara: refuse, argument_schema em /note
4 um destino -- um endereço, uma URL, um host -- num argumento de uma ferramenta não declarada sends_out undeclared_destination refund_card com https://refunds.invalid/4411 como pedido: refuse, undeclared_destination
5 as regras de ordem da ferramenta sobre o histórico verificado: after (e um argumento ligado ao resultado daquela chamada), not_after, at_most order_rule uma ferramenta declarada after: [get_order], chamada antes de qualquer get_order rodar na sessão: refuse, order_rule, nomeando after get_order
6 um desfazer nomeia o passo que desfaz, esse passo rodou, o desfazer é o declarado para ele, cada argumento ligado vem desse passo, e o passo é desfeito uma única vez compensation_spent, compensation_mismatch refund_card enviado uma segunda vez como desfazer de uma cobrança já desfeita: refuse, compensation_spent
7 o portão do rulebook, como o rulebook compilado do tenant decide a chamada a regra do rulebook que a recusou um reembolso que o fluxo do rulebook recusa: refuse, com aquela regra como motivo e o seu fluxo no detalhe
8 marcas menos confiáveis do que a ferramenta exige, numa escrita; recusada aqui quando a taint_policy do tenant é refuse tainted_call com taint_policy: refuse, um reembolso proposto depois de ler a caixa de entrada: refuse, tainted_call
9 um token de confirmação que cobre exatamente esta chamada, com estes argumentos, neste ponto do histórico confirmed o mesmo reembolso, apresentado com o token que a pessoa aprovou: allow, confirmed
10 todo motivo pelo qual uma escrita precisa de uma pessoa, juntos; uma escrita sem nenhum deles ainda é confirmada, por write gate_clarify, argument_origin, tainted_call, irreversible_pivot, schema_partial, write o reembolso com um valor que o modelo escreveu: confirm, argument_origin: amount; o mesmo valor digitado pela pessoa: confirm, write
11–12 um desfazer que passou por todos os passos acima; senão, uma chamada que nada acima reteve compensation, allow get_order, uma leitura: allow; refund_card como o desfazer que uma execução que falhou envia: allow, compensation

Assim, uma recusa sempre nomeia a primeira coisa errada: uma chamada a uma ferramenta que o catálogo não tem é recusada por isso, sejam quais forem os seus argumentos; uma chamada que passa pelos passos 1 a 8 com uma aprovação válida é permitida no 9, sem perguntar de novo; e uma escrita que chega ao passo 10 é confirmada, com todos os motivos de uma vez.

Uma chamada, todas as verificações

O reembolso da loja, perguntado de cada uma das formas como uma execução pode perguntar. A sessão do balcão de cobrança lê o turno da pessoa, refund order 4411 40.00, e cada chamada é verificada com o histórico dessa sessão (os turnos são lidos em inglês):

def show(what, check):
    detail = {k: v for k, v in check.detail.items() if k in ("pointer", "rule", "destinations", "wording")}
    print(f"{what}: {check.decision} {', '.join(check.reasons)} {detail if detail else ''}".rstrip())


with Client() as client:
    billing = client.session("billing")       # one conversation with the billing desk
    billing.read("refund order 4411 40.00")   # what the person typed

    refund = {"order_id": "4411", "amount": 40.00}
    show("the model wrote the amount",
         billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "model"}))
    asked = billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "typed"})
    show("the person typed the amount", asked)
    show("the person approved it",
         billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "typed"},
                                 confirmation=asked.confirmation))

    asked = billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "typed"})
    show("approved, then the amount changed",
         billing.check_tool_call("refund_card", {"order_id": "4411", "amount": 45.00},
                                 origins={"order_id": "typed", "amount": "model"}, confirmation=asked.confirmation))

    show("an argument the tool does not declare",
         billing.check_tool_call("refund_card", {**refund, "note": "goodwill"},
                                 origins={"order_id": "typed", "amount": "typed", "note": "model"}))
    show("a tool the catalogue does not have", billing.check_tool_call("delete_order", {"order_id": "4411"}))
    show("a destination in a tool that sends nothing out",
         billing.check_tool_call("refund_card", {"order_id": "https://refunds.invalid/4411", "amount": 40.00},
                                 origins={"order_id": "model", "amount": "typed"}))
    show("proposed after reading the inbox",
         billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "typed"},
                                 labels={"call": ["untrusted:read_inbox"]}))

    billing.history.entries[0]["tool"] = "get_order"     # the history, edited by hand
    show("the history edited by hand",
         billing.check_tool_call("refund_card", refund, origins={"order_id": "typed", "amount": "typed"}))
the model wrote the amount: refuse refuse
the person typed the amount: refuse refuse
the person approved it: refuse refuse
approved, then the amount changed: refuse refuse
an argument the tool does not declare: refuse argument_schema {'pointer': '/note', 'rule': 'additionalProperties'}
a tool the catalogue does not have: refuse unknown_tool
a destination in a tool that sends nothing out: refuse undeclared_destination {'destinations': ['https://refunds.invalid/4411']}
proposed after reading the inbox: refuse refuse
the history edited by hand: refuse history_invalid

Linha a linha:

  • o valor que o modelo escreveu pergunta à pessoa, nomeando o argumento: argument_origin: amount;
  • o valor que a pessoa digitou -- ele está no turno dela -- ainda pergunta, porque toda escrita é confirmada: o motivo é apenas write;
  • apresentada com o token daquela confirmação, a mesma chamada é allow, confirmed;
  • uma aprovação apresentada depois de o valor mudar não cobre nada: a verificação pergunta de novo;
  • note, um argumento que refund_card não declara, é recusado em /note;
  • delete_order não está no catálogo;
  • um endereço da web no número do pedido é um destino, e refund_card não envia nada para fora;
  • um reembolso proposto depois de ler a caixa de entrada pergunta, e a sua pergunta diz isso;
  • um histórico editado à mão é recusado antes de qualquer outra coisa ser lida.

De onde veio cada argumento

Todo argumento de uma escrita carrega a sua origem, e a verificação confia em algumas origens e não em outras:

  • typed: a pessoa o digitou. Confiável apenas onde o valor está nos turnos dela, como ela o digitou: um valor declarado como digitado que nenhum turno contém é perguntado;
  • reply, context e computed: a resposta da pessoa a uma pergunta, um valor que a sua aplicação pôs no contexto da execução (como o cliente autenticado), e um cálculo que a execução fez. Confiáveis;
  • result:<passo> ou result:<passo>.<campo>: um campo do resultado de uma chamada anterior. Confiável onde essa chamada rodou nesta sessão e o schema de resultado da sua ferramenta declara o campo;
  • model, e qualquer outra: o modelo o escreveu. Perguntado, pelo nome.

Um argumento não declarado é recusado. O schema de uma ferramenta diz quais são os seus argumentos, e um argumento que ele não declara é um para o qual ninguém escreveu regra: um note num reembolso, um cc num recibo. Recusá-lo no passo 3 significa que as regras seguintes nunca são consultadas sobre um valor que não conseguem ver.

O histórico da sessão

A verificação lê o histórico da sessão: toda chamada verificada até agora, com o seu passo, a sua ferramenta, o seu agente, os digests dos seus argumentos e das suas origens, as marcas que carregava, a resposta da verificação e o que aconteceu com ela. Quem chama carrega o histórico e nós o assinamos: cada resposta acrescenta a entrada da chamada e um token que a assina junto com todas as entradas anteriores, com as chaves da sua licença. A verificação seguinte confere essas assinaturas.

history_invalid significa que as assinaturas não se verificaram: uma entrada foi editada, removida ou reordenada, ou o histórico é de outra sessão. Nada mais é lido, e a resposta começa um novo histórico no passo 0. O que aconteceu com uma chamada -- ran, failed, outcome_unknown ou compensated -- é o relato de quem chama, e o elo da entrada seguinte o carrega.

Regras de ordem

As palavras de ordem de uma ferramenta são verificadas sobre o histórico verificado (capítulo 30):

  • after: [get_order]: a ferramenta só roda depois que um get_order rodou nesta sessão. Uma chamada permitida que falhou não conta. Um after pode ligar um argumento ao resultado daquela chamada anterior, para que um reembolso nomeie uma cobrança que esta sessão fez, e não uma que o modelo escreveu: a origem do argumento precisa ser o result daquela chamada, ou a verificação o recusa nomeando a ligação;
  • not_after: [read_inbox], ou not_after: [untrusted]: a ferramenta nunca roda depois que a ferramenta nomeada foi permitida, ou depois que qualquer coisa de fora foi lida. Uma leitura conta, aconteça o que acontecer com ela: o que ela devolveu foi lido;
  • at_most: 1: a ferramenta roda no máximo uma vez na sessão.

Cada uma é recusada como order_rule, com a regra que quebrou no detalhe, nas palavras que o QG014 da caça usa.

Desfazeres ligados ao que desfazem

Um desfazer que uma execução que falhou envia é verificado como qualquer chamada, e também contra o passo que desfaz: esse passo rodou, o desfazer é o que a sua ferramenta declara, cada argumento que o desfazer liga vem dos argumentos ou do resultado desse passo, e o passo ainda não foi desfeito. Um desfazer que passa é permitido sem confirmação, a menos que o seu rulebook faça uma pergunta sobre ele: a pessoa aprovou a escrita que ele desfaz. Um enviado duas vezes é compensation_spent; um que não corresponde ao seu passo é compensation_mismatch (capítulo 34).

O portão do rulebook

O seu rulebook decide a chamada no passo 7, como foi compilado no registro. Um fluxo que recusa a chamada a recusa, nomeando a sua regra. Um fluxo que pede uma pergunta transforma a chamada numa confirmação, como gate_clarify.

Valores não confiáveis

Um valor vindo de uma ferramenta que reads_outside, ou de um agente com menos privilégio do que a ferramenta exige, carrega uma marca (capítulo 33). Uma escrita que carrega uma marca menos confiável do que a ferramenta é retida: por padrão ela é confirmada, com a sua pergunta dizendo de onde veio a marca; onde a taint_policy da sua organização é refuse, ela é recusada. Leituras nunca são retidas por uma marca.

As confirmações e os seus tokens

Um confirm vem com um token que cobre exatamente aquela chamada: a ferramenta, o digest dos seus argumentos, a sessão e o ponto do histórico em que foi pedido, e uma validade (por padrão, um dia). A aprovação da pessoa envia a chamada de volta com o token, e a verificação a permite no passo 9. Um argumento mudado pergunta de novo: o seu digest já não é o que o token cobre, então o token não cobre nada, e a chamada é verificada desde o início. Depois que a chamada aprovada roda, o histórico segue em frente e o token também não cobre mais nada.

Uma escrita irreversível numa execução cujas escritas anteriores seriam desfeitas se ela falhasse acrescenta irreversible_pivot, e a sua pergunta termina com: Nothing undoes this step, and the steps after it cannot undo it.

Os limites

  • A verificação confia no relato de quem chama sobre se uma chamada rodou. Ela decide se uma chamada pode rodar; o que aconteceu com ela é o relato do host, carregado no histórico. O gateway o vê, porque o próprio gateway faz a chamada.
  • O que fica na sua máquina: as conversas dos seus agentes, as suas chamadas de ferramenta e os seus resultados, os dados, a função e o resultado de um cálculo, e os documentos de que os seus agentes respondem e o seu índice. Não guardamos nenhuma sessão, nenhum argumento de chamada de ferramenta e nenhum resultado de ferramenta, nem as palavras de um turno, exceto as que um registro de feedback carrega. (capítulo 16)
  • Uma confirmação prova que uma pessoa aprovou a chamada mostrada, com aqueles argumentos. Ela não prova que a pessoa a leu.
  • A verificação governa as chamadas que o seu agente faz. Uma ferramenta alcançável por outro caminho pode ser chamada contornando-a (capítulo 37).