Skip to content

Providers and Agents pt

Hermes Agent edited this page Oct 1, 2026 · 1 revision

Provedores e agentes

English | 中文 | 日本語 | 한국어 | Español | Português | Русский

Quatro protocolos de rede (apiStyle)

lib/llm-client.js é a camada de protocolo. Os quatro streams compartilham um único esqueleto openSseStream() (fetch / classificação de erros / negociação de orçamento / abort / enquadramento SSE / finalização); cada protocolo mantém apenas buildRequest + parsers de eventos puros. A extração do payload data: do SSE tem fonte única (sseDataPayload, junção de múltiplas linhas de data conforme a spec):

apiStyle Endpoint Observações
chat /v1/chat/completions também interpreta reasoning_content no estilo DeepSeek
responses /v1/responses grafia multimodal nativa input_*
anthropic /v1/messages cache_control explícito em dois breakpoints (sistema + última mensagem) — a Anthropic não tem cache de prefixo implícito
runs Hermes /v1/runs protocolo de agente: aprovações / esclarecimentos / ferramentas / eventos de thinking

thinking: 'inline' | 'omit' (padrão omit) controla se o texto de raciocínio viaja em UM bloco <thinking> inline no delta stream; apenas o chat principal e a thread de detalhe usam inline. O campo reasoning.available do runs é um replay da resposta, não thinking — o echo guard deve descartá-lo (ADR-0004).

A escada de turn-request

A forma da requisição por provedor é reconstruída quatro vezes no chat principal (inicial / rebuild por overflow / continuação / reescrita de timestamps), unificada como createTurnRequest (prepare / rebuildFrom / continueWith / rewriteWith) — o chat-handler fica com o QUANDO, a escada é dona do COMO. A thread de detalhe deliberadamente não a usa (semântica de sessão isolada, ADR-0007).

Orçamento de saída e truncamento

  • max_tokens padrão 32768; servidores que REJEITAM um orçamento acima do teto com um 400 ganham uma única retentativa renegotiateOutputCap com o teto extraído do texto do erro.
  • finish_reason === 'length' → UMA passada silenciosa de continuação (instrução antirrepetição, deltas engolidos); ainda truncado → DONE carrega outputTruncated, o painel mostra um toast + um botão de "continuar" de um clique. Deliberadamente NÃO existe campo manual de max_tokens — "um botão que exige conhecimento por modelo é um bug de design".

Os quatro agentes

Agente Canal Sessões
Hermes /v1/runs do lado do servidor (sessionId no storage); as parts do turno atual usam text/image_url canônicos, conversation_history é apenas strings (422 da strict layer, verificado 2026-09-24)
OpenCode opencode serve HTTP do lado do servidor; porta aleatória por padrão → recomende --port fixa
OpenSquilla gateway local ws://…/ws uma sessão de gateway por conversa; anexos de >60K caracteres sobem como um documento page-context.md; allowlist de origem — veja o Modelo de segurança
Agent Bridge daemon local @xiaohuzai/agent-bridge adapta codex/claude/pi/gemini atrás de um único protocolo HTTP; logins de assinatura funcionam como fontes de modelo

A camada compartilhada lib/agent-turn.js: um turno de agente envia APENAS o turno atual do usuário + a run de page-context final (a transcrição vive do lado do servidor; o histórico nunca é reenviado). Imagens passam por pickTurnImages (≤8 imagens / orçamento de URL de ≤3MiB; os gates no anexo e no envio se espelham). Trocar para um provedor de agente no meio da conversa oferece 「带上当前对话继续」= um backfill de uso único (transcrição em texto puro, teto de cauda de 200K caracteres).

Handoff entre pontos de entrada: nomeação e ID de sessão no lado do agente (2026-10-01)

Para provedores de agente, a transcrição já vive do lado do servidor (turnos de agente enviam apenas o turno atual) — "deixar a própria UI do agente assumir" precisa de descobribilidade, não de movimento de dados. Duas peças:

  • Nomeação automática: após um turno Hermes bem-sucedido, um PATCH /api/sessions/{id} de uso único (API original do upstream; o servidor sanitiza títulos e rejeita conflitos exatos) define o título como browsa: + a primeira linha do texto do usuário do turno atual (teto de 48 caracteres). Semântica de carimbo único (hermesSessionTitled_<provider>, mesmo ciclo de vida do id de sessão): sucesso E 4xx carimbam (um conflito de título não pode virar um loop de retentativa por turno); apenas falhas de transporte (status 0) ficam sem carimbo para o próximo turno bem-sucedido. O PATCH expira em 10s e seu await corre contra um teto de 2,5s para que o DONE nunca fique pendurado. Canais hoje: Hermes (PATCH /api/sessions/{id}) e bridge/codex (o POST /threads/{id}/title do daemon → thread/name/set do app-server, verificado ao vivo no codex 0.149.1 — o nome persiste no state DB do codex e o codex resume resolve por nome ou id; veredictos de visibilidade no Picker para os quatro agentes da bridge (verificados no código-fonte em 2026-10-01): codex ✓ (predicado do picker has_user_event = 1 AND title <> '' — turnos reais o satisfazem; state DB global, sem escopo por cwd; nomeado via o POST /threads/{id}/title do daemon → thread/name/set do app-server). claude ✓ desde que o transcriptFix: 'claude' da bridge reescreve o entrypoint: sdk-* auto-carimbado para cli no disco após cada turno consolidado (agent-bridge #56, confirmado no Mac) — sem canal de renomeação no cliente (claude-agent-acp titula automaticamente), o resume funciona pelo picker ou por ID. pi ✓ (sessões ACP caem no store próprio do pi com escopo por cwd que o picker lê; toggle de escopo + filtro por nome; o RPC nativo set_session_name do pi grava uma entrada de nome session_info — o pi-acp só a expõe como o comando /name dentro do turno, ainda não ligado à bridge). gemini ✓ (a lista exclui apenas kind:"subagent"; sessões da bridge são kind:"main"; store ~/.gemini/tmp/<cwd>/chats/, com escopo por cwd; sem canal de nomeação — o título vem da primeira mensagem do usuário). opencode/squilla não verificados. As sessões dedicadas da thread de detalhe deliberadamente NÃO são tituladas.)
  • Exibição do ID de sessão: storage.getAgentSessionInfo é o único dono das formas das chaves de sessão por tipo (bridge indexada por endpoint via activeModel || baseUrl); a gaveta de sessões renderiza uma linha "Sessão de agente" (id curto + copiar) acima da lista, oculta para provedores LLM / quando não existe sessão.

Relay de aprovação / esclarecimento

Aprovações de ferramentas de agente e pedidos de esclarecimento passam por relay via lib/handlers/approval-relay.js (chat principal indexado por tabId, thread de detalhe por subId). A forma da pending-entry É a interface de despacho. No lado da UI, turn-chrome.js é o chrome de turno compartilhado pelas duas superfícies (indicador de espera, progresso de ferramentas, cartões de aprovação/esclarecimento, chip de uso).

UI de seleção de provedor (regras consolidadas)

  • O dropdown lista apenas provedores CONFIGURADOS, alcançáveis primeiro (ordenação estável); zero configurados → um placeholder desabilitado.
  • Se o activeProvider armazenado não está configurado, o primeiro configurado é auto-selecionado E persistido (reparo de estado, não preferência); o primeiro ping alcançável também auto-troca (uma vez).
  • Provedores multi-modelo: IDs de modelo separados por vírgula; uma entrada no dropdown por modelo; resolveChatModel honra o activeModel apenas enquanto ele ainda pertence àquele provedor.
  • Ambos os grupos (LLM / agente) renderizam como cartões em abas (grupo de agentes colapsado por padrão; a ordem das abas [bridge, opencode, hermes] é fixada por teste).

Economia do CAPABILITY_HINTS (ADR-0010 — não reapresente a proposta de encolhê-lo)

O prompt de sistema de todo turno CHAT = systemPrompt do usuário + linha de idioma da resposta + CAPABILITY_HINTS + CHOICE_REQUEST_HINT; ele deve permanecer um prefixo estável byte a byte (gating de palavras-chave por turno quebra o cache de prompt KV a partir da posição 0). Cada entrada restante se pagou com um bug real de renderização — não "aperte" uma cláusula de porquê sem ler o histórico dela no AGENTS.md. A correção para um formato perdido é um hint melhor, não detecção em runtime.


Versões autoritativas: Providers-and-Agents (inglês) / Providers-and-Agents-zh (chinês) — instantâneo de primeira tradução por IA, sincronizado em 2026-10-01.

Clone this wiki locally