DeepSeek Harness: Logs de Sessão e Trajectory
Última atualização: 2026-08-31
Cada conversa de Agent é uma jornada irreproduzível — modelos têm aleatoriedade, ferramentas têm efeitos colaterais, contexto se acumula. O sistema Trajectory do DSH usa "logs append-only" para registrar completamente cada passo, permitindo que você rastreie, audite, faça fork e restaure o estado da sessão em qualquer ponto no tempo.
📋 Pré-requisitos: Ter completado 07-python-sdk.md, familiarizado com noções básicas do SDK
1. O Que Você Vai Aprender
- Princípios de design de logs append-only
- Tipos e estrutura do fluxo de eventos SessionEvent
- Uso da visualização Trajectory
- Mecanismos de fork e restauração de sessão
- Persistência e exportação de logs
2. Design de Log Append-Only
(1) Por Que Append-Only?
Sistemas de log tradicionais permitem modificação e exclusão, mas logs de sessão de Agent devem ser imutáveis — como um gravador de dados de voo (caixa preta), uma vez que um registro é escrito, não pode ser alterado:
graph LR
E1[Evento 1] --> E2[Evento 2] --> E3[Evento 3] --> E4[Evento 4] --> E5[Evento 5]
E5 -.->|Apenas adição| NEW[Evento 6]
style E1 fill:#e8f5e9
style E2 fill:#e8f5e9
style E3 fill:#e8f5e9
style E4 fill:#e8f5e9
style E5 fill:#e8f5e9
style NEW fill:#fff3e0
Três princípios do design append-only:
| Princípio | Descrição | Benefício |
|---|---|---|
| Imutável | Uma vez escritos, logs não podem ser modificados ou excluídos | Trilha de auditoria completa |
| Ordenado | Eventos são estritamente ordenados por timestamp | Reprodução reversível |
| Apenas adição | Apenas novos eventos podem ser adicionados, sem exclusão | Sem conflitos de concorrência |
(2) Comparação com Logs Tradicionais
| Dimensão | Logs Tradicionais | Logs Append-Only do DSH |
|---|---|---|
| Modificável | ✅ Pode modificar/excluir | ❌ Não pode modificar |
| Seguro para concorrência | Requer bloqueio | Naturalmente seguro (apenas adição) |
| Capacidade de rollback | Depende de backups | Restaurar a partir de qualquer ponto |
| Capacidade de auditoria | Pode ser adulterado | À prova de adulteração |
| Eficiência de armazenamento | Comprimível | Cresce continuamente (requer arquivamento periódico) |
(3) Estrutura de Armazenamento de Logs
.dsh/
└── sessions/
└── sess_abc123/
├── events.log # Log de eventos (append-only)
├── snapshots/ # Snapshots de estado
│ ├── snap_001.json
│ ├── snap_002.json
│ └── snap_003.json
└── metadata.json # Metadados da sessão
3. Fluxo de Eventos SessionEvent
(1) Tipos de Eventos
Cada operação em uma sessão DSH é registrada como um SessionEvent:
type SessionEventType =
| 'session.created'
| 'session.config_changed'
| 'user.message'
| 'agent.message'
| 'agent.thinking'
| 'tool.call'
| 'tool.result'
| 'tool.approval.requested'
| 'tool.approval.resolved'
| 'session.forked'
| 'session.restored'
| 'error.occurred';
(2) Estrutura do Evento
Cada SessionEvent contém campos padrão:
interface SessionEvent {
id: string; // ID único do evento
type: SessionEventType; // Tipo do evento
timestamp: number; // Timestamp Unix (milissegundos)
sessionId: string; // ID da sessão pai
data: Record<string, unknown>; // Dados de payload do evento
parentId?: string; // ID do evento pai (usado para forks)
}
(3) Descrições Detalhadas dos Eventos
Evento de Mensagem do Usuário:
▶ Exemplo 1:Evento user.message
{
"id": "evt_001",
"type": "user.message",
"timestamp": 1724486400000,
"sessionId": "sess_abc123",
"data": {
"content": "Me ajude a refatorar o diretório utils",
"attachments": []
}
}
Evento de Chamada de Ferramenta:
▶ Exemplo 2:Evento tool.call
{
"id": "evt_002",
"type": "tool.call",
"timestamp": 1724486401500,
"sessionId": "sess_abc123",
"data": {
"tool": "search",
"params": {
"pattern": "utils/*",
"type": "file"
},
"mode": "standard"
}
}
Evento de Resultado de Ferramenta:
▶ Exemplo 3:Evento tool.result
{
"id": "evt_003",
"type": "tool.result",
"timestamp": 1724486402300,
"sessionId": "sess_abc123",
"data": {
"toolCallId": "evt_002",
"status": "success",
"result": {
"files": ["utils/format.ts", "utils/validate.ts", "utils/helpers.ts"]
},
"duration_ms": 800
}
}
Evento de Aprovação:
▶ Exemplo 4:Evento tool.approval
{
"id": "evt_004",
"type": "tool.approval.requested",
"timestamp": 1724486403000,
"sessionId": "sess_abc123",
"data": {
"tool": "file_edit",
"params": {
"action": "edit",
"path": "utils/format.ts"
},
"riskLevel": "high"
}
}
{
"id": "evt_005",
"type": "tool.approval.resolved",
"timestamp": 1724486405000,
"sessionId": "sess_abc123",
"data": {
"approvalId": "evt_004",
"decision": "allowed",
"decidedBy": "user"
}
}
(4) Exemplo Completo do Fluxo de Eventos
Linha do Tempo Tipo de Evento
─────────────────────────────────────────
10:00:00.000 session.created
10:00:05.120 user.message "Me ajude a refatorar o diretório utils"
10:00:06.300 tool.call search → utils/*
10:00:07.100 tool.result Encontrados 3 arquivos
10:00:08.200 tool.call file_edit → read utils/format.ts
10:00:08.500 tool.result Conteúdo do arquivo retornado
10:00:10.800 agent.thinking Analisando plano de refatoração...
10:00:12.000 tool.approval.requested file_edit → edit
10:00:15.000 tool.approval.resolved → permitido
10:00:15.200 tool.call file_edit → edit utils/format.ts
10:00:15.600 tool.result Edição concluída
10:00:17.000 agent.message "Refatoração concluída!"
4. Visualização Trajectory
(1) O Que É Trajectory?
Trajectory é a interface visual dos logs de sessão, mostrando a "trajetória" completa do Agent:
┌─ Visualização Trajectory ──────────────────────────────────────┐
│ │
│ 10:00 👤 Me ajude a refatorar o diretório utils │
│ 10:00 🔍 search(utils/*) → 3 arquivos 0.8s │
│ 10:00 📄 file_edit(read) → utils/format.ts 0.3s │
│ 10:00 📄 file_edit(read) → utils/validate.ts 0.2s │
│ 10:00 🤔 Pensando... Analisando plano de refatoração │
│ 10:00 ⚠️ Aprovação: edit utils/format.ts │
│ 10:00 → ✅ Permitido │
│ 10:00 📝 file_edit(edit) → utils/format.ts 0.4s │
│ 10:00 ⚠️ Aprovação: edit utils/validate.ts │
│ 10:00 → ✅ Permitido │
│ 10:00 📝 file_edit(edit) → utils/validate.ts 0.3s │
│ 10:00 🤖 Refatoração concluída! Definições de tipo compartilhado extraídas... │
│ │
│ [Fork a partir daqui] [Restaurar até aqui] [Exportar] │
└─────────────────────────────────────────────────────────┘
(2) Acessando Trajectory na Web UI
Na Web UI, clique no ícone de log na barra de controle superior para abrir a visualização Trajectory:
Barra de controle superior → 📋 → Trajectory
(3) Filtragem e Busca no Trajectory
▶ Exemplo 5:Filtrar por Tipo de Evento
Filtros da Visualização Trajectory:
┌──────────────────────────────────────────┐
│ Filtros: │
│ ☑ user.message ☑ agent.message │
│ ☑ tool.call ☑ tool.result │
│ ☐ agent.thinking ☐ eventos de aprovação │
│ │
│ Busca: [Digite palavras-chave...] │
└──────────────────────────────────────────┘
(4) Acessando Trajectory via SDK
▶ Exemplo 6:Obter Fluxo de Eventos via SDK
from dsh import DSHClient
client = DSHClient(base_url="http://127.0.0.1:3080")
session = client.get_session("sess_abc123")
# Obter todos os eventos
events = session.get_trajectory()
for event in events:
print(f"[{event.timestamp}] {event.type}: {event.data}")
# Filtrar por tipo
tool_events = session.get_trajectory(event_type="tool.call")
for event in tool_events:
print(f"Ferramenta: {event.data['tool']}")
print(f"Parâmetros: {event.data['params']}")
5. Fork e Restauração de Sessão
(1) Conceito de Fork
Fork cria um branch de sessão a partir de um ponto específico no tempo — a linha principal continua avançando enquanto o branch se desenvolve independentemente:
graph LR
E1[Evento 1] --> E2[Evento 2] --> E3[Evento 3] --> E4[Evento 4]
E3 -->|fork| F1[Evento Fork 1] --> F2[Evento Fork 2]
E4 --> E5[Evento 5]
style E1 fill:#e8f5e9
style E2 fill:#e8f5e9
style E3 fill:#e8f5e9
style E4 fill:#e8f5e9
style E5 fill:#e8f5e9
style F1 fill:#e3f2fd
style F2 fill:#e3f2fd
(2) Casos de Uso de Fork
| Cenário | Descrição |
|---|---|
| Exploração de soluções | Testar abordagens diferentes a partir do mesmo nó, comparar resultados |
| Fallback seguro | Fazer fork antes de operações destrutivas; voltar à linha principal se falhar |
| Teste A/B | Comparar a mesma tarefa usando modelos/modos diferentes |
| Alterações experimentais | Quando incerto dos resultados, testar em um branch primeiro |
(3) Fazendo Fork na Web UI
Na visualização Trajectory, clique no botão "Fork a partir daqui" ao lado de qualquer evento:
10:00 📝 file_edit(edit) → utils/format.ts [Fork a partir daqui]
10:00 ⚠️ Aprovação: edit utils/validate.ts [Fork a partir daqui]
10:00 🤖 Refatoração concluída! [Fork a partir daqui]
Fork cria uma nova sessão começando a partir do ponto do evento selecionado, copiando todo o contexto anterior àquele ponto.
(4) Fazendo Fork via SDK
▶ Exemplo 7:Operação de Fork via SDK
client = DSHClient(base_url="http://127.0.0.1:3080")
session = client.get_session("sess_abc123")
# Fazer fork a partir do 5º evento
forked = session.fork(after_event="evt_005")
print(f"Sessão com fork: {forked.id}")
print(f"Pai: {forked.parent_id}")
print(f"Ponto de fork: evt_005")
# Continuar conversa no branch do fork
response = forked.send("Tente uma abordagem de refatoração diferente, divida por função")
(5) Restauração
Restauração é diferente de Fork — ela reverte a sessão atual para um ponto de evento especificado, descartando eventos subsequentes:
▶ Exemplo 8:Operação de Restauração via SDK
# Restaurar ao 3º ponto de evento
session.restore(to_event="evt_003")
# Eventos após evt_004, evt_005, etc. são marcados como "restaurados"
# Novos eventos continuam sendo adicionados após evt_003
Nota: Restauração não exclui eventos (princípio append-only), mas marca eventos subsequentes como inválidos e adiciona um evento
session.restored.
6. Persistência de Logs
(1) Armazenamento Padrão
Logs de sessão do DSH são armazenados por padrão no diretório .dsh/sessions/ do projeto:
.dsh/
├── sessions/
│ ├── sess_abc123/
│ │ ├── events.log # Log de eventos
│ │ ├── snapshots/ # Snapshots de estado
│ │ └── metadata.json # Metadados
│ └── sess_def456/
│ ├── events.log
│ └── ...
├── config.yaml # Configuração DSH
└── plugins/ # Diretório de plugins
(2) Configuração de Persistência
# dsh.config.yaml
storage:
# Caminho de armazenamento
base_path: ".dsh/sessions"
# Estratégia de snapshot
snapshots:
enabled: true
interval: 10 # Salvar snapshot a cada 10 eventos
max_snapshots: 5 # Manter no máximo 5 snapshots
# Rotação de logs
rotation:
max_size_mb: 100 # Máx. 100MB por arquivo de log
max_files: 50 # Manter no máximo 50 sessões
# Arquivamento
archive:
enabled: true
path: ".dsh/archive/"
after_days: 30 # Auto-arquivar após 30 dias
(3) Exportando Logs de Sessão
▶ Exemplo 9:Exportar como JSON
# Exportar log de sessão completo
session = client.get_session("sess_abc123")
events = session.get_trajectory()
import json
with open("session_export.json", "w") as f:
json.dump([e.to_dict() for e in events], f, indent=2)
▶ Exemplo 10:Exportar como Markdown
# Exportação via CLI
dsh session export sess_abc123 --format markdown --output session.md
# Formato de saída
# # Session: sess_abc123
# ## 10:00 - Usuário
# Me ajude a refatorar o diretório utils
# ## 10:00 - Ferramenta: search
# Padrão: utils/* → 3 arquivos encontrados
# ...
(4) Limpeza de Logs
# Listar todas as sessões (ordenadas por tamanho)
dsh session list --sort size
# Arquivar sessões antigas
dsh session archive --older-than 30d
# Excluir sessões arquivadas (irreversível)
dsh session clean --archived-only
7. Trajectory e Auditoria
(1) Auditoria de Operações
Trajectory registra cada operação do Agent, sendo naturalmente adequado para auditoria:
graph TB
AUDIT[Necessidade de Auditoria] --> T1[Quem executou?]
AUDIT --> T2[Quando?]
AUDIT --> T3[O que foi feito?]
AUDIT --> T4[Qual foi o resultado?]
T1 --> TRAJ[Fluxo de Eventos Trajectory]
T2 --> TRAJ
T3 --> TRAJ
T4 --> TRAJ
(2) Cenários de Conformidade
| Requisito de Conformidade | Como Trajectory Atende |
|---|---|
| Rastreabilidade de operações | Cada evento tem ID, timestamp, operador |
| Alterações à prova de adulteração | Log append-only, não pode modificar histórico |
| Registros de aprovação | approval.requested + approval.resolved registro completo |
| Capacidade de rollback | Restaurar a partir de qualquer checkpoint |
(3) Gerando Relatórios de Auditoria
▶ Exemplo 11:Gerando um Relatório de Auditoria
session = client.get_session("sess_abc123")
events = session.get_trajectory()
report = {
"session_id": session.id,
"duration": events[-1].timestamp - events[0].timestamp,
"user_messages": len([e for e in events if e.type == "user.message"]),
"tool_calls": len([e for e in events if e.type == "tool.call"]),
"approvals_requested": len([e for e in events if e.type == "tool.approval.requested"]),
"approvals_denied": len([e for e in events if e.type == "tool.approval.resolved" and e.data.get("decision") == "denied"]),
"files_modified": list(set([
e.data.get("path") for e in events
if e.type == "tool.call" and e.data.get("tool") == "file_edit"
])),
"errors": len([e for e in events if e.type == "error.occurred"])
}
import json
print(json.dumps(report, indent=2))
❓ Perguntas Frequentes
rotation.max_size_mb e archive.after_days para gerenciamento automático de armazenamento.dsh session export; SDK usa o método get_trajectory().📖 Resumo
- Logs de sessão do DSH usam design append-only: imutáveis, ordenados, apenas crescendo
- SessionEvent inclui 13 tipos de eventos cobrindo conversas, ferramentas, aprovações, erros, etc.
- Visualização Trajectory visualiza a trajetória completa de execução do Agent
- Fork cria branches a partir de qualquer nó sem afetar a linha principal; Restauração reverte para um nó especificado
- Persistência de logs suporta snapshots, rotação, arquivamento e exportação
- Trajectory naturalmente suporta auditoria de operações e requisitos de conformidade
- Tanto o SDK quanto o CLI podem acessar e operar dados Trajectory
📝 Exercícios
1. ⭐ Básico: Complete uma conversa de Agent (com pelo menos 2 chamadas de ferramentas), abra a visualização Trajectory e liste todos os eventos com seus tipos e timestamps.
2. ⭐⭐ Intermediário: Faça fork de um branch a partir do 3º evento de uma sessão, tente uma abordagem diferente no branch. Compare os resultados finais da linha principal e do branch.
3. ⭐⭐⭐ Desafio: Use o Python SDK para escrever uma ferramenta de análise Trajectory — insira um ID de sessão, gere automaticamente um relatório de auditoria (incluindo estatísticas de operações, registros de aprovação, lista de arquivos modificados, resumo de erros), em formato JSON.