LangChain e LangGraph
stalwart_agents.langchain coloca o roteamento, a verificação de ferramentas e as sagas da
Stalwart dentro de agentes LangChain e LangGraph. Nenhum seletor, vocabulário ou verificação está
neste módulo. Toda decisão sobre a qual ele age é tomada pelo runtime que o wheel traz, na mesma
máquina, sob a sua licença, ou pelo qls-router enterprise que você executa no seu próprio
ambiente, onde o cliente o nomeia (router_url). A API hospedada da Stalwart não toma nenhuma
delas. Um turno que precisa de um cálculo que nenhuma ferramenta responde é executado pelo nó de
cálculo na imagem de contêiner qls-sandbox no seu próprio host, onde há um mecanismo de
contêineres e a imagem; nos demais casos, ele é recusado, com um motivo que começa com
compute off:.
$ pip install stalwart-agents # the middlewares, the graph parts, Guard
$ pip install "stalwart-agents[otel]" # OpenTelemetry spans
$ pip install "stalwart-agents[postgres]" # resume confirmations from Postgres (or [sqlite])
$ pip install "stalwart-agents[nemo]" # the NeMo Guardrails actions and rails
LangChain: dois middlewares, um importador, um callback
from langchain.agents import create_agent
from stalwart_agents.langchain import (StalwartRouterMiddleware, StalwartToolGuardMiddleware,
StalwartFeedbackCallback, register_catalogue)
register_catalogue(tools, agent="desk", client=client) # POST /v1/catalogues
agent = create_agent(model, tools, middleware=[
StalwartRouterMiddleware(client, agent="desk", on_unread="refuse"),
StalwartToolGuardMiddleware(client, agent="desk"),
])
- O roteador lê cada turno do usuário em um plano antes de o modelo ser chamado. Um turno
que se resolve em uma ferramenta, uma pergunta ou uma recusa custa nenhuma chamada de
modelo. Um cálculo é escrito pelo seu próprio modelo. Pede-se que um turno que nada lê seja
reformulado uma vez; depois disso, ele segue a sua configuração:
refuse,modelouask, que o entrega ao modelo só quando o usuário diz que sim (uma interrupçãodelegate:approveoudecline). - A guarda de ferramentas verifica toda chamada de ferramenta, quem quer que a tenha escolhido. Uma chamada recusada não executa nada e recebe um resultado que diz por quê. Uma escrita pausa na interrupção do LangGraph e só é executada quando os argumentos exatos mostrados são aprovados. As chamadas de uma mensagem rodam lado a lado e se revezam no único histórico da sessão, de modo que nenhuma se perde.
- A composição: o middleware do NeMo Guardrails por fora, depois o roteador, depois o modelo; a guarda de ferramentas em volta da execução das ferramentas. O nosso nunca remove o hook de outro middleware.
StalwartFeedbackCallbacklembra o turno e a ferramenta de cada execução.thumbs(run_id, up=False)envia um registro de feedback. Onde o roteador leu o turno, o registro é a leitura do próprio roteador, a partir do estado da execução: o seu veredito, a regra que decidiu e as ferramentas visíveis do agente; um turno que ele entregou ao modelo registraunreade a ferramenta do modelo. Um turno com várias instruções é avaliado passo a passo (step=); uma resposta que a conversa decidiu, ou a resposta depois de uma confirmação, é recusada comThumbRefused.
LangGraph: as partes, ou o grafo inteiro
from stalwart_agents.langchain.graph import create_stalwart_agent
agent = create_stalwart_agent(model, tools, catalogue=catalogue, agent="desk", client=client,
checkpointer=checkpointer) # verified once per graph digest
| Parte | O que faz |
|---|---|
StalwartState |
os nossos canais ao lado das suas mensagens: a sessão, o histórico (só de acréscimo), os rótulos de confiança, a saga, as confirmações em aberto, o plano |
stalwart_messages, trim_pairs |
add_messages, e uma chamada de ferramenta nunca deixada sem o seu resultado; resultados na ordem das chamadas; um corte que mantém os pares |
stalwart_route_node |
o turno lido em um plano, devolvido como Command com os seus destinos declarados |
StalwartToolNode |
toda chamada verificada (congelar, verificar, executar uma vez, registrar, empilhar a reversão), um passo por vez, de modo que uma falha no meio de uma chamada é retomada com honestidade |
stalwart_edge |
uma aresta condicional que só pode tomar os destinos que declara, e um rejected_route nos demais casos |
stalwart_rollback_node, stalwart_rewind_node |
uma execução que falhou desfeita da última para a primeira escrita, passando pela verificação, e depois a memória rebobinada, nunca com uma reversão pendente e nunca o histórico |
stalwart_hand_off_node |
uma transferência que leva o rótulo do agente que a faz |
stalwart_branches, stalwart_join_node |
as chamadas de uma mensagem distribuídas como ramos Send, verificadas um histórico por vez e desfeitas na ordem inversa de conclusão; as reversões de um subgrafo entregues ao pai quando ele conclui, e a sua falha passa a ser do pai quando não conclui |
with_stalwart_channels |
os nossos canais acrescentados a qualquer esquema de estado, para um supervisor, um swarm ou o seu próprio grafo que executa um agente stalwart ao longo dos turnos |
create_stalwart_agent, build_stalwart_graph |
o grafo gerado a partir do seu catálogo, verificado pelo modo graph; compile() verifica todos os nós que você acrescentar |
validate_state, validating |
cada quebra do estado nomeada, inclusive o token de encadeamento do histórico com history_verifier; o modo de depuração para no nó que a causou |
export_graph, replay_witness |
a exportação que o verificador lê, e a testemunha de um achado reproduzida com toda ferramenta substituída por um stub |
Python puro: Guard
from stalwart_agents.langchain.guard import Guard
guard = Guard(client, agent="desk", catalogue=catalogue, tools=[charge_card, refund_card])
with guard.run(guard.session()) as run:
run.call(charge_card, order_id=o, amount=a) # checked, keyed, its undo pushed
run.call(provision_account, order_id=o) # raises -> the charge refunded
stalwart_agents.langchain.guard não carrega nem LangChain nem LangGraph.
Em volta do agente
stalwart_agents.langchain.server.ResumeServer: um app ASGI sobre o seu checkpointer. Ele lista as interrupções em aberto (as aninhadas e as paralelas, cada uma pelo seu próprio id), retoma uma delas em qualquer worker, recusa uma retomada desatualizada com409 interrupt_stale, transmite os passos de uma conversa (retomáveis porLast-Event-ID) e permite que um operador reverta uma conversa:ResumeServer(operator_token=...), enviado comoAuthorization: BeareremPOST /threads/{id}/rollback; sem nenhum definido, a rota responde- Duas respostas a uma mesma interrupção são diferenciadas por uma reivindicação
(
SqlClaimssobre o seu banco de dados, para vários workers). Cada resposta leva orefusale orollbackdo turno como dados.Webhook(url, secret)envia cada interrupção com uma assinatura HMAC, um carimbo de tempo e uma janela de repetição de cinco minutos; um envio que falha nunca faz o turno falhar.
- Duas respostas a uma mesma interrupção são diferenciadas por uma reivindicação
(
- Recibos: uma chamada leva
current_receipt()-- o histórico da sessão terminando na verificação que a permitiu --, de modo que um ponto de controle na frente das suas ferramentas (a rota da receita do Kong) a admite pela verificação do próprio agente, em vez de decidi-la de novo. stalwart_agents.langchain.otel:OtelTracereNodeSpansdão um span para cada nó, plano, verificação, chamada, reversão, interrupção e retomada, com a decisão como evento do span. O conteúdo fica desligado por padrão.python -m stalwart_agents.langchain.trace show|html|diffrenderiza uma execução a partir do seu arquivo de spans, ou só a partir do checkpointer.stalwart_agents.langchain.nemo: as açõesstalwart_routeestalwart_check_tool, e um rail de entrada e uma guarda de execução em Colang 1.0 e Colang 2. Os dois trechos são verificados no nosso solver.stalwart_agents.langchain.redteam.RedTeam: um planejador, um explorador e um crítico apontados para os achados do caçador, ou em aberto, contra o seu grafo e o seu modelo, com toda ferramenta substituída por um stub. Not realised in budget não diz nada sobre segurança.graph_from_exportconstrói o alvo a partir de uma exportação, onde o seu próprio código não está à mão.