Importação
Este guia cobre o processo de leitura de uma exportação de provedor e produção de um arquivo válido de memory store ou conversação PAM. Inclui heurísticas de detecção de formato, normalização de roles, normalização de timestamps e orientações sobre versionamento de importadores.
Processo geral
Seção intitulada “Processo geral”- Obtenha a exportação do seu provedor (veja Visão Geral de Provedores)
- Detecte o formato do provedor usando as heurísticas abaixo
- Mapeie os campos para o schema PAM (veja as páginas individuais de cada provedor para os mapeamentos de campos)
- Normalize os roles usando a tabela de referência abaixo
- Normalize os timestamps para ISO 8601 usando a referência abaixo
- Compute os content hashes conforme a normalização da spec §6
- Valide a saída contra o schema PAM
Heurísticas de detecção de formato
Seção intitulada “Heurísticas de detecção de formato”A função a seguir detecta o formato do provedor a partir de uma estrutura JSON carregada. Note que exportações do Copilot são CSV, não JSON, e devem ser detectadas pela extensão do arquivo ou pela linha de cabeçalho antes de chamar esta função.
def detect_provider(data): if isinstance(data, list): sample = data[0] if data else {} if "mapping" in sample: return "chatgpt" if "chat_messages" in sample: return "claude" if "header" in sample and ("details" in sample or "userInteractions" in sample): return "gemini" if isinstance(data, dict): if "conversations" in data and isinstance(data["conversations"], list): sample = data["conversations"][0] if data["conversations"] else {} if "conversation" in sample and "responses" in sample: return "grok" return "unknown"function detectProvider(data) { if (Array.isArray(data)) { const sample = data[0] || {}; if ("mapping" in sample) return "chatgpt"; if ("chat_messages" in sample) return "claude"; if ("header" in sample && ("details" in sample || "userInteractions" in sample)) return "gemini"; } if (typeof data === "object" && !Array.isArray(data)) { const convs = data.conversations; if (Array.isArray(convs)) { const sample = convs[0] || {}; if ("conversation" in sample && "responses" in sample) return "grok"; } } return "unknown";}Sinais de detecção por provedor:
| Sinal | Provedor |
|---|---|
Array JSON; primeiro elemento possui chave mapping | OpenAI / ChatGPT |
Array JSON; primeiro elemento possui chave chat_messages | Anthropic / Claude |
Array JSON; primeiro elemento possui chave header e details ou userInteractions | Google / Gemini |
Objeto JSON; conversations[] onde o primeiro item possui chaves conversation e responses | xAI / Grok |
| Arquivo CSV (detectar pela extensão do arquivo ou linha de cabeçalho) | Microsoft / Copilot |
Normalização de roles
Seção intitulada “Normalização de roles”Todos os valores de role do provedor devem ser normalizados para os quatro roles canônicos do PAM: user, assistant, system, tool.
| Provedor | Valor do provedor | Valor normalizado PAM |
|---|---|---|
| OpenAI | user | user |
| OpenAI | assistant | assistant |
| OpenAI | system | system |
| OpenAI | tool | tool |
| Anthropic | human | user |
| Anthropic | assistant | assistant |
Request (Takeout) | user | |
Response (Takeout) | assistant | |
| Microsoft | user (CSV) | user |
| Microsoft | AI (CSV) | assistant |
| xAI | human | user |
| xAI | assistant | assistant |
| xAI | ASSISTANT | assistant |
| xAI | grok-3 (nome do modelo como remetente) | assistant |
| xAI | qualquer valor que não seja human | assistant |
Para xAI / Grok, a normalização de role DEVE ser case-insensitive. Quatro valores distintos de remetente foram observados: "human", "assistant", "ASSISTANT" (maiúsculo) e nomes de modelo como "grok-3". Trate qualquer valor que não seja "human" (case-insensitive) como "assistant".
Normalização de timestamps
Seção intitulada “Normalização de timestamps”Todos os timestamps no PAM DEVEM ser strings ISO 8601. Exportações de provedores usam diversos formatos:
| Provedor | Formato de origem | Transformação |
|---|---|---|
| OpenAI | Unix epoch (float, segundos) | datetime.fromtimestamp(v, tz=UTC).isoformat() |
| Anthropic | ISO 8601 | direto (nenhuma transformação necessária) |
| Google (Takeout) | ISO 8601 | direto (nenhuma transformação necessária) |
| Microsoft (CSV) | String de data local (varia por arquivo) | parse com dateutil.parser.parse() |
| xAI (nível de conversação) | ISO 8601 | direto (nenhuma transformação necessária) |
| xAI (nível de mensagem) | BSON {"$date":{"$numberLong":"<ms>"}} | datetime.fromtimestamp(int(v["$date"]["$numberLong"])/1000, tz=UTC).isoformat() |
xAI / Grok usa dois formatos de timestamp diferentes dentro do mesmo arquivo: ISO 8601 no nível de conversação e BSON no nível de mensagem. Dois parsers separados são necessários.
Para OpenAI, algumas mensagens possuem create_time: 0 ou null. Use o create_time no nível de conversação como fallback.
Computação do content hash
Seção intitulada “Computação do content hash”Toda memória importada deve incluir um content_hash. Compute-o conforme a spec §6:
- Pegue a string
content - Remova espaços em branco no início e no final
- Converta para minúsculas
- Aplique a normalização Unicode NFC
- Reduza espaços em branco consecutivos para um único espaço
- Compute o SHA-256 do resultado codificado em UTF-8
- Formate como
sha256:<hex_digest>
import hashlibimport unicodedata
def compute_content_hash(content: str) -> str: text = content.strip().lower() text = unicodedata.normalize("NFC", text) text = " ".join(text.split()) return f"sha256:{hashlib.sha256(text.encode('utf-8')).hexdigest()}"import {createHash} from "node:crypto";
function computeContentHash(content) { let text = content.trim().toLowerCase(); text = text.normalize("NFC"); text = text.replace(/\s+/g, " "); const hash = createHash("sha256").update(text, "utf8").digest("hex"); return `sha256:${hash}`;}Versionamento de importadores
Seção intitulada “Versionamento de importadores”Formatos de exportação dos provedores mudam sem aviso prévio. Todo importador DEVE registrar sua versão e o arquivo de origem na saída PAM. Use o campo import_metadata no schema de conversação:
{ "import_metadata": { "importer": "pam-converter/1.0.0", "importer_version": "openai-importer/2025.01", "imported_at": "2026-02-15T22:00:00Z", "source_file": "conversations.json", "source_checksum": "sha256:abc123..." }}Quando um provedor altera seu formato de exportação:
- Crie uma nova versão do importador (ex.:
openai-importer/2026.01) - Mantenha a versão antiga do importador disponível para reprocessamento de exportações anteriores
- Detecte automaticamente a versão do formato quando possível, verificando diferenças na presença de campos ou na estrutura do schema
Validação
Seção intitulada “Validação”Após a importação, valide sua saída contra o schema PAM:
from jsonschema import Draft202012Validatorimport json
with open("portable-ai-memory.schema.json") as f: schema = json.load(f)with open("memory-store.json") as f: data = json.load(f)
errors = list(Draft202012Validator(schema).iter_errors(data))print(f"{len(errors)} validation errors")import Ajv2020 from "ajv/dist/2020.js";import {readFileSync} from "node:fs";
const ajv = new Ajv2020({allErrors: true});const schema = JSON.parse(readFileSync("portable-ai-memory.schema.json", "utf8"));const data = JSON.parse(readFileSync("memory-store.json", "utf8"));
const validate = ajv.compile(schema);if (validate(data)) { console.log("memory-store.json valid");} else { console.log(`${validate.errors.length} validation errors`); process.exit(1);}Veja o Guia de Validação para instruções detalhadas.