Provedores e failover
O que o AutoDev faz quando um provedor informa um limite de uso ou não pode ser alcançado. Ele passa o turno adiante em vez de dormir, e só dorme quando não sobra para onde ir.
Como um limite é detectado #
O AutoDev roda três CLIs de provedor por enquanto: claude, codex e antigravity, cujo binário é agy. Ele não prevê um limite. Ele fica sabendo de um quando uma chamada o informa. Três coisas contam:
- para o
claude, umrate_limit_eventestruturado cujo status érejectedoublocked, ou um aviso curto na saída que bate com uma frase conhecida, comoUsage limit reached. Try again in 2 hours.ouYou've hit your limit · resets 2:30pm (<time zone>); - para o
codex, os avisos dele, que trazem um tempo relativo, um horário ou uma data completa; - para o
agy, o próprio erro dele no stderr, um status429ouRESOURCE_EXHAUSTEDou uma linha dizendo que a cota foi atingida, com o reinício escrito como duração, comoResets in 2h31m33s.
Um aviso é curto, e texto escrito por um modelo, quando passa de 300 caracteres, nunca é examinado, então um relatório que apenas cita um aviso de limite não é confundido com um. O horário de reinício é lido do aviso. Quando nenhum pode ser lido, o claude espera uma hora, e o codex e o agy sondam de novo a cada 30 minutos.
Um limite pertence a um engine #
Um limite pertence ao engine que o informou, não ao participante que o atingiu. Todo participante que roda no mesmo engine o compartilha, então um turno limitado nunca é entregue a um colega na mesma conta esgotada, que atingiria o mesmo limite segundos depois. Um engine é um provedor e, quando uma entrada de failover nomeia um, um modelo dele: claude e claude:<model> são dois engines.
Primeiro, a cadeia do participante #
Um participante pode levar uma cadeia failover no arquivo dele: entradas provider[:model], em ordem.
--- provider: claude failover: [codex, claude] ---
Quando o turno de um participante informa um limite e nada foi postado, o AutoDev percorre primeiro a cadeia desse participante. Ele passa para a próxima entrada que não se sabe limitada e roda o turno ali, no mesmo passo. Se essa entrada também estiver limitada, ele avança de novo. Ele escreve <id>: <from> limited → <to>. Uma entrada que nomeia uma CLI que esta máquina não tem é ignorada. Num salto, a fixação do participante não se aplica, já que nomeia um modelo do provedor principal, a menos que a própria entrada nomeie um modelo.
A cadeia vem primeiro porque um colega muitas vezes não tem nada seu a fazer: quando a thread espera uma revisão ou uma resposta deste participante, os outros só poderiam dizer que estão esperando, e cada um desses é um turno pago.
Não existe cadeia padrão: um participante sem failover só tem os colegas. O campo mora no arquivo do participante. Edit prompt o abre no painel, e autodev team edit <id> imprime o caminho dele. Participantes e lanes trata do arquivo.
Depois, um colega #
Quando a cadeia se esgota ou está vazia, o turno vai para outro participante cujo engine esteja livre. O AutoDev escreve <id> is rate limited — @<other> takes the turn no log. O colega é escolhido pelas mesmas regras de qualquer turno.
Dois casos não passam para um colega. Quem tem um gate pendente nunca entrega o turno, e uma equipe de um só não tem a quem entregar. Os dois usam só a própria cadeia. Se o limite chega depois de o participante já ter postado, o post fica: o limite é registrado, e o turno seguinte o contorna.
Quando ninguém pode assumir o turno #
Quando todo participante e toda cadeia estão limitados, a execução não para. O status vira rate_limited, e o daemon dorme até o reinício mais próximo entre todos eles, não o de quem falhou por último. O AutoDev escreve every participant limited — waiting for <provider>, back first at <time>.
O sono acontece em fatias. Cada fatia atualiza o heartbeat, e um stop ou um pause o interrompe. Uma mensagem sua não, já que nada pode rodar antes do reinício. Uma máquina que ficou suspensa durante a espera acorda no reinício, não depois do sono que lhe restava. O tempo dormindo não é cobrado de maxRuntimeMin.
Uma espera por limite não tem teto próprio. maxWaitMin e a reverificação em intervalos que dobram pertencem a uma jogada wait, que A thread compartilhada descreve.
A volta #
No início de cada turno, o AutoDev levanta os limites cujo reinício já passou. Um participante que tinha avançado na cadeia volta para a entrada livre mais antiga, que nem sempre é a principal. O AutoDev escreve <id>: limit lifted — back on its primary provider.
A escolha do provedor é feita quando um turno começa e nunca durante um. O registro de quem está limitado fica na memória durante a vida do daemon. Um reinício o esquece e descobre de novo, sondando.
Quando uma chamada falha em vez disso #
Um limite de uso é algo que um provedor informa, com um horário para esperar. Uma chamada também pode falhar sem informar um, e o AutoDev distingue dois casos.
A rede caiu. Quando uma chamada falha com um erro de rede, como um endereço que não é encontrado ou uma conexão recusada, reiniciada ou expirada, e nada foi postado, o provedor fica fora para todo participante que roda nele, como um limite tira um engine. O AutoDev tenta de novo depois de 1, 2, 4 e 8 minutos, e então a cada 15 enquanto durar. Ele escreve <provider> could not be reached; trying it again at <time>, e <provider> is reachable again quando volta. Os participantes em outro provedor seguem. Com todos os provedores fora, ele escreve No provider can be reached; trying again at <time>. Isso não tem prazo: uma noite em que a internet fica fora por três horas continua quando ela volta.
A chamada falha de outro jeito. A CLI quebra, expira, ou termina o turno sem um post. O AutoDev espera mais depois de cada falha seguida: um segundo depois da primeira, depois 1, 2, 4, 8, 15 e 30 minutos, e então uma hora a cada vez. Depois de 12 falhas seguidas, cerca de cinco horas de espera, a execução pausa e escreve Gave up after 12 failed attempts in a row, over five hours (<what failed>); run paused — resume to try again. Uma mensagem sua encurta uma espera, e uma chamada que funciona zera a contagem. Se um engine limitado volta antes, a próxima tentativa é feita então. Ao contrário de uma espera por limite, cada tentativa conta contra maxIterations.
As cinco horas existem para um limite cujo aviso o AutoDev ainda não consegue ler. Ele se levanta sozinho, e uma agenda que passa de uma janela de uso leva a execução até lá. Um participante que termina três turnos seguidos sem um post é outro caso: tentar de novo gastaria cota para não aprender nada, então a execução pausa de uma vez e o nomeia.
O que isto não faz #
- Um limite só é conhecido quando uma chamada o informa. Os heartbeats de cota que o AutoDev registra não são o que a decisão lê.
- Um limite escrito de um jeito que o AutoDev nunca viu é lido como uma chamada que falhou, e a agenda acima vale.
- O
codexe oagynão têm verificação de login. Uma CLI sem sessão mostra o próprio erro na lane dela, e CLI não autenticada explica por quê. - Um turno iniciado pela aprovação de um gate é isento. Ele espera pelo próprio provedor e nunca roda em outro.
- Com um só engine na equipe e nenhuma cadeia, um limite é uma espera.
Participantes e lanes trata do arquivo onde a cadeia mora, e Todas as chaves de configuração lista maxIterations, maxRuntimeMin e maxWaitMin.