Configuração, personalização, ajustes de plataforma e referências de API para Pi.

Compactação e Resumo de Filiais

LLMs têm janelas de contexto limitadas. Quando as conversas ficam muito longas, Pi usa a compactação para resumir o conteúdo mais antigo, preservando o trabalho recente. Esta página cobre compactação automática e branch summarization.

Arquivos de origem (pi-mono):

Para definições de TypeScript em seu projeto, inspecione node_modules/@earendil-works/pi-coding-agent/dist/.

Visão geral

Pi possui dois mecanismos de resumo:

Mecanismo Acionar Propósito
Compactação O contexto excede o limite ou /compact Resuma mensagens antigas para liberar contexto
Resumo da filial /tree navegação Preservar o contexto ao mudar de ramificação

Ambos usam o mesmo formato de resumo estruturado e rastreiam operações de arquivo cumulativamente. Solicitações de compactação e resumo de ramificação usam novos IDs de sessão de roteamento e, quando suportados pelo provedor, desativam gravações de cache de prompt porque é improvável que esses prompts únicos sejam reutilizados.

Compactação

Quando isso desencadeia

A compactação automática é acionada quando:

contextTokens > contextWindow - reserveTokens

Por padrão, reserveTokens são 16384 tokens (configuráveis ​​em ~/.pi/agent/settings.json ou <project-dir>/.pi/settings.json). Isto deixa espaço para a resposta do LLM.

Você também pode acionar manualmente com /compact [instructions], onde instruções opcionais concentram o resumo.

Como funciona

  1. Encontrar ponto de corte: retrocede a partir da mensagem mais recente, acumulando estimativas de token até que keepRecentTokens (padrão 20k, configurável em ~/.pi/agent/settings.json ou <project-dir>/.pi/settings.json) seja alcançado
  2. Extrair mensagens: Colete mensagens do limite mantido anteriormente (ou início da sessão) até o ponto de corte
  3. Gerar resumo: Chame o LLM para resumir com formato estruturado, passando o resumo anterior como contexto iterativo quando presente
  4. Anexar entrada: Salve CompactionEntry com resumo e firstKeptEntryId
  5. Recarregar: A sessão é recarregada, usando resumo + mensagens de firstKeptEntryId em diante
Before compaction:

  entry:  0     1     2     3      4     5     6      7      8     9
        ┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┐
        │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│
        └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┘
                └────────┬───────┘ └──────────────┬──────────────┘
               messagesToSummarize            kept messages
                                   ↑
                          firstKeptEntryId (entry 4)

After compaction (new entry appended):

  entry:  0     1     2     3      4     5     6      7      8     9     10
        ┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┬─────┐
        │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│ cmp │
        └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┴─────┘
               └──────────┬──────┘ └──────────────────────┬───────────────────┘
                 not sent to LLM                    sent to LLM
                                                         ↑
                                              starts from firstKeptEntryId

What the LLM sees:

  ┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐
  │ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │
  └────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘
       ↑         ↑      └─────────────────┬────────────────┘
    prompt   from cmp          messages from firstKeptEntryId

Em compactações repetidas, o vão resumido começa no limite mantido da compactação anterior (firstKeptEntryId), e não na entrada de compactação em si, voltando para a entrada após a compactação anterior se essa entrada mantida não puder ser encontrada no caminho. Isso preserva as mensagens que sobreviveram à compactação anterior, incluindo-as também na próxima passagem de resumo. Pi também recalcula tokensBefore a partir do contexto da sessão reconstruída antes de escrever o novo CompactionEntry, portanto, a contagem de tokens reflete o contexto real de pré-compactação que está sendo substituído.

Turnos Divididos

Um "turno" começa com uma mensagem do usuário e inclui todas as respostas do assistente e chamadas de ferramentas até a próxima mensagem do usuário. Normalmente, a compactação corta nos limites das curvas.

Quando uma única curva excede keepRecentTokens, o ponto de corte chega no meio da curva em uma mensagem do assistente. Esta é uma "virada dividida":

Split turn (one huge turn exceeds budget):

  entry:  0     1     2      3     4      5      6     7      8
        ┌─────┬─────┬─────┬──────┬─────┬──────┬──────┬─────┬──────┐
        │ hdr │ usr │ ass │ tool │ ass │ tool │ tool │ ass │ tool │
        └─────┴─────┴─────┴──────┴─────┴──────┴──────┴─────┴──────┘
                ↑                                     ↑
         turnStartIndex = 1                  firstKeptEntryId = 7
                │                                     │
                └──── turnPrefixMessages (1-6) ───────┘
                                                      └── kept (7-8)

  isSplitTurn = true
  messagesToSummarize = []  (no complete turns before)
  turnPrefixMessages = [usr, ass, tool, ass, tool, tool]

Para turnos divididos, Pi gera dois resumos e os mescla:

  1. Resumo do histórico: contexto anterior (se houver)
  2. Resumo do prefixo de curva: a parte inicial da curva dividida

Regras de ponto de corte

Os pontos de corte válidos são:

  • Mensagens do usuário
  • Mensagens do assistente
  • Mensagens BashExecution
  • Mensagens personalizadas (custom_message, branch_summary)

Nunca corte nos resultados da ferramenta (eles devem permanecer com a chamada da ferramenta).

Estrutura de entrada de compactação

Definido em session-manager.ts:

interface CompactionEntry<T = unknown> {
  type: "compaction";
  id: string;
  parentId: string;
  timestamp: number;
  summary: string;
  firstKeptEntryId: string;
  tokensBefore: number;
  usage?: Usage;       // LLM usage that generated the summary
  fromHook?: boolean;  // true if provided by extension (legacy field name)
  details?: T;         // implementation-specific data
}

// Default compaction uses this for details (from compaction.ts):
interface CompactionDetails {
  readFiles: string[];
  modifiedFiles: string[];
}

Extensions pode armazenar qualquer dado serializável JSON em details. A compactação padrão rastreia operações de arquivo, mas implementações de extensões personalizadas podem usar sua própria estrutura. Os resumos gerados e fornecidos pela extensão armazenam seu LLM usage quando disponível, de modo que os totais das sessões incluam o trabalho de resumo.

Veja prepareCompaction() e compact() para a implementação. Para resumo programático direto, generateSummary() retorna o texto do resumo e generateSummaryWithUsage() retorna { text, usage }.

Resumo de Filiais

Quando isso desencadeia

Quando você usa /tree para navegar para um branch diferente, Pi se oferece para resumir o trabalho que você está deixando. Isso injeta o contexto do branch esquerdo no novo branch.

Como funciona

  1. Encontrar ancestral comum: nó mais profundo compartilhado por posições antigas e novas
  2. Coletar entradas: caminhar da folha antiga até o ancestral comum
  3. Prepare-se com orçamento: inclua mensagens até o orçamento simbólico (as mais recentes primeiro)
  4. Gerar resumo: Ligue para LLM com formato estruturado
  5. Anexar entrada: Salve BranchSummaryEntry no ponto de navegação
Tree before navigation:

         ┌─ B ─ C ─ D (old leaf, being abandoned)
    A ───┤
         └─ E ─ F (target)

Common ancestor: A
Entries to summarize: B, C, D

After navigation with summary:

         ┌─ B ─ C ─ D
    A ───┤
         └─ E ─ F ─ [summary of B,C,D] (new leaf)

Rastreamento cumulativo de arquivos

Tanto a compactação quanto o branch summarization rastreiam arquivos cumulativamente. Ao gerar um resumo, pi extrai operações de arquivo de:

  • Chamadas de ferramentas nas mensagens sendo resumidas
  • Compactação anterior ou resumo de ramificação details (se houver)

Isso significa que o rastreamento de arquivos se acumula em diversas compactações ou resumos de ramificações aninhadas, preservando o histórico completo de arquivos lidos e modificados.

Estrutura BranchSummaryEntry

Definido em session-manager.ts:

interface BranchSummaryEntry<T = unknown> {
  type: "branch_summary";
  id: string;
  parentId: string;
  timestamp: number;
  summary: string;
  fromId: string;      // Entry we navigated from
  usage?: Usage;       // LLM usage that generated the summary
  fromHook?: boolean;  // true if provided by extension (legacy field name)
  details?: T;         // implementation-specific data
}

// Default branch summarization uses this for details (from branch-summarization.ts):
interface BranchSummaryDetails {
  readFiles: string[];
  modifiedFiles: string[];
}

Assim como a compactação, as extensões podem armazenar dados personalizados em details.

Consulte collectEntriesForBranchSummary(), prepareBranchEntries() e generateBranchSummary() para a implementação.

Formato de resumo

Tanto a compactação quanto o branch summarization usam o mesmo formato estruturado:

## Goal
[What the user is trying to accomplish]

## Constraints & Preferences
- [Requirements mentioned by user]

## Progress
### Done
- [x] [Completed tasks]

### In Progress
- [ ] [Current work]

### Blocked
- [Issues, if any]

## Key Decisions
- **[Decision]**: [Rationale]

## Next Steps
1. [What should happen next]

## Critical Context
- [Data needed to continue]

<read-files>
path/to/file1.ts
path/to/file2.ts
</read-files>

<modified-files>
path/to/changed.ts
</modified-files>

Serialização de mensagens

Antes do resumo, as mensagens são serializadas em texto via serializeConversation():

[User]: What they said
[Assistant thinking]: Internal reasoning
[Assistant]: Response text
[Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
[Tool result]: Output from tool

Isso evita que o modelo trate isso como uma conversa para continuar.

Os resultados da ferramenta são truncados para 2.000 caracteres durante a serialização. O conteúdo além desse limite é substituído por um marcador que indica quantos caracteres foram truncados. Isso mantém as solicitações de resumo dentro de orçamentos de tokens razoáveis, uma vez que os resultados da ferramenta (especialmente de read e bash) são normalmente os que mais contribuem para o tamanho do contexto.

Resumo personalizado via Extensions

Extensions pode interceptar e personalizar compactação e branch summarization. Veja extensions/types.ts para definições de tipo de evento.

session_before_compact

Disparado antes da compactação automática ou /compact. Pode cancelar ou fornecer um resumo personalizado. Veja SessionBeforeCompactEvent e CompactionPreparation no arquivo de tipos.

pi.on("session_before_compact", async (event, ctx) => {
  const { preparation, branchEntries, customInstructions, reason, willRetry, signal } = event;

  // preparation.messagesToSummarize - messages to summarize
  // preparation.turnPrefixMessages - split turn prefix (if isSplitTurn)
  // preparation.previousSummary - previous compaction summary
  // preparation.fileOps - extracted file operations
  // preparation.tokensBefore - context tokens before compaction
  // preparation.firstKeptEntryId - where kept messages start
  // preparation.settings - compaction settings

  // branchEntries - all entries on current branch (for custom state)
  // reason - "manual" (/compact), "threshold", or "overflow"
  // willRetry - whether the aborted turn is retried after compaction (overflow recovery)
  // signal - AbortSignal (pass to LLM calls)

  // Cancel:
  return { cancel: true };

  // Custom summary:
  return {
    compaction: {
      summary: "Your summary...",
      firstKeptEntryId: preparation.firstKeptEntryId,
      tokensBefore: preparation.tokensBefore,
      // usage: summaryResponse.usage, // Optional; included in session totals
      details: { /* custom data */ },
    }
  };
});

Convertendo mensagens em texto

Para gerar um resumo com seu próprio modelo, converta mensagens em texto usando serializeConversation:

import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";

pi.on("session_before_compact", async (event, ctx) => {
  const { preparation } = event;
  
  // Convert AgentMessage[] to Message[], then serialize to text
  const conversationText = serializeConversation(
    convertToLlm(preparation.messagesToSummarize)
  );
  // Returns:
  // [User]: message text
  // [Assistant thinking]: thinking content
  // [Assistant]: response text
  // [Assistant tool calls]: read(path="..."); bash(command="...")
  // [Tool result]: output text

  // Now send to your model for summarization
  const { summary, usage } = await myModel.summarize(conversationText);
  
  return {
    compaction: {
      summary,
      firstKeptEntryId: preparation.firstKeptEntryId,
      tokensBefore: preparation.tokensBefore,
      usage,
    }
  };
});

Veja custom-compaction.ts para um exemplo completo usando um modelo diferente.

sessão_antes_árvore

Disparado antes da navegação /tree. Sempre é acionado independentemente de o usuário optar por resumir. Pode cancelar a navegação ou fornecer um resumo personalizado.

pi.on("session_before_tree", async (event, ctx) => {
  const { preparation, signal } = event;

  // preparation.targetId - where we're navigating to
  // preparation.oldLeafId - current position (being abandoned)
  // preparation.commonAncestorId - shared ancestor
  // preparation.entriesToSummarize - entries that would be summarized
  // preparation.userWantsSummary - whether user chose to summarize

  // Cancel navigation entirely:
  return { cancel: true };

  // Provide custom summary (only used if userWantsSummary is true):
  if (preparation.userWantsSummary) {
    return {
      summary: {
        summary: "Your summary...",
        // usage: summaryResponse.usage, // Optional; included in session totals
        details: { /* custom data */ },
      }
    };
  }
});

Veja SessionBeforeTreeEvent e TreePreparation no arquivo de tipos.

Configurações

Configure a compactação em ~/.pi/agent/settings.json ou <project-dir>/.pi/settings.json:

{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}
Contexto Padrão Descrição
enabled true Ativar compactação automática
reserveTokens 16384 Tokens para reservar para resposta LLM
keepRecentTokens 20000 Tokens recentes para manter (não resumidos)

Desative a compactação automática com "enabled": false. Você ainda pode compactar manualmente com /compact.