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 querefund_cardnão declara, é recusado em/note;delete_ordernão está no catálogo;- um endereço da web no número do pedido é um destino, e
refund_cardnã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,contextecomputed: 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>ouresult:<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 umget_orderrodou nesta sessão. Uma chamada permitida que falhou não conta. Umafterpode 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 oresultdaquela chamada, ou a verificação o recusa nomeando a ligação;not_after: [read_inbox], ounot_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).