Respawn Tech Dev

Memória de aprendizados

Memória de longo prazo para agentes de IA: salve o que aprendeu em cada projeto e recupere antes da próxima tarefa.

Os agentes gravam armadilhas, decisões, convenções e receitas aprendidas em cada projeto e, na próxima tarefa, recuperam só o que é relevante. Funciona sobre uma base de conhecimento criada automaticamente, com deduplicação e busca semântica.

Base path
/v1/tools/learnings
Escopos
tools:learnings:readtools:learnings:writetools:manage

Como funciona

Um aprendizado é um texto curto com um kind, ligado a um project:

kindPara guardar
pitfallArmadilhas e erros que não devem se repetir.
decisionDecisões tomadas e o motivo.
conventionConvenções do projeto.
howtoComo fazer algo (rodar, publicar, depurar).
otherO que não cabe nos outros.

Os aprendizados ficam numa base de conhecimento chamada agent-learnings, criada sozinha na primeira gravação. O recall faz uma busca semântica pela tarefa e devolve só os itens do project pedido, com a score de cada um.

Também está disponível no MCP como learning_save, learning_recall e learning_forget (toolset learnings).

OperaçãoRota
Salvar aprendizadoPOST/
Recuperar aprendizadosPOST/recall
Esquecer aprendizadoDELETE/{id}

Duplicados e ingestão assíncrona

  • Deduplicação: o save compara por hash e por similaridade semântica. Se já existe um aprendizado igual ou parecido, nada novo é gravado e a resposta traz duplicate_of com o id do existente.
  • Ingestão assíncrona: o texto é indexado em segundo plano. Um recall logo depois de um save pode ainda não trazer o item; tente de novo em alguns segundos.

Custos

A cobrança é a da base de conhecimento: o save como a ingestão (por trecho), o recall como a busca (franquia diária + blocos) e o armazenamento por dia. Esquecer é grátis. A memória segue as mesmas regras de liberação da base de conhecimento (rag.disabled e demais erros rag.*).

Endpoints

Salvar aprendizado

POST/v1/tools/learnings
Custo
Cobrado como a ingestão da base de conhecimento (por trecho)
Dry-run
Não
Autenticação
qualquer um detools:learnings:writetools:manage

project, kind e text são obrigatórios; source é opcional e diz de onde veio. A resposta traz o id do aprendizado ou, se for repetido, duplicate_of.

curl -sS -X POST 'https://api.respawntech.dev/v1/tools/learnings' \
  -H "X-API-Token: $RESPAWN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "project": "respawn-tech-docs", "kind": "pitfall", "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.", "source": "conversa de 06/10" }'
const response = await fetch('https://api.respawntech.dev/v1/tools/learnings', {
  method: 'POST',
  headers: {
    'X-API-Token': process.env.RESPAWN_API_TOKEN,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ "project": "respawn-tech-docs", "kind": "pitfall", "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.", "source": "conversa de 06/10" }),
});

if (!response.ok) throw new Error((await response.json()).error.code);
console.log(await response.json());
import os
import requests

response = requests.post(
    "https://api.respawntech.dev/v1/tools/learnings",
    headers={
        "X-API-Token": os.environ["RESPAWN_API_TOKEN"],
    },
    json={ "project": "respawn-tech-docs", "kind": "pitfall", "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.", "source": "conversa de 06/10" },
    timeout=30,
)
if not response.ok:
    raise RuntimeError(response.json()["error"]["code"])
print(response.json())
<?php
$ch = curl_init('https://api.respawntech.dev/v1/tools/learnings');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('RESPAWN_API_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => <<<'JSON'
{ "project": "respawn-tech-docs", "kind": "pitfall", "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.", "source": "conversa de 06/10" }
JSON,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($status >= 400) {
    throw new RuntimeException(json_decode($body, true)['error']['code']);
}
print_r(json_decode($body, true));
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	req, err := http.NewRequest("POST", "https://api.respawntech.dev/v1/tools/learnings", strings.NewReader(`{ "project": "respawn-tech-docs", "kind": "pitfall", "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.", "source": "conversa de 06/10" }`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Token", os.Getenv("RESPAWN_API_TOKEN"))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	if res.StatusCode >= 400 {
		panic(string(data))
	}
	fmt.Println(string(data))
}

Resposta 201

{
  "success": true,
  "trace_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Recuperar aprendizados

POST/v1/tools/learnings/recall
Custo
Cobrado como a busca da base de conhecimento (franquia diária + blocos)
Dry-run
Não
Autenticação
qualquer um detools:learnings:readtools:manage

project e task são obrigatórios; top_k limita quantos itens voltam. Cada item traz id, kind, text, source e score. Lembre que a ingestão é assíncrona: um item recém-salvo pode ainda não aparecer.

curl -sS -X POST 'https://api.respawntech.dev/v1/tools/learnings/recall' \
  -H "X-API-Token: $RESPAWN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "project": "respawn-tech-docs", "task": "rodar os checks antes de abrir o PR", "top_k": 5 }'
const response = await fetch('https://api.respawntech.dev/v1/tools/learnings/recall', {
  method: 'POST',
  headers: {
    'X-API-Token': process.env.RESPAWN_API_TOKEN,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ "project": "respawn-tech-docs", "task": "rodar os checks antes de abrir o PR", "top_k": 5 }),
});

if (!response.ok) throw new Error((await response.json()).error.code);
console.log(await response.json());
import os
import requests

response = requests.post(
    "https://api.respawntech.dev/v1/tools/learnings/recall",
    headers={
        "X-API-Token": os.environ["RESPAWN_API_TOKEN"],
    },
    json={ "project": "respawn-tech-docs", "task": "rodar os checks antes de abrir o PR", "top_k": 5 },
    timeout=30,
)
if not response.ok:
    raise RuntimeError(response.json()["error"]["code"])
print(response.json())
<?php
$ch = curl_init('https://api.respawntech.dev/v1/tools/learnings/recall');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('RESPAWN_API_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => <<<'JSON'
{ "project": "respawn-tech-docs", "task": "rodar os checks antes de abrir o PR", "top_k": 5 }
JSON,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($status >= 400) {
    throw new RuntimeException(json_decode($body, true)['error']['code']);
}
print_r(json_decode($body, true));
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	req, err := http.NewRequest("POST", "https://api.respawntech.dev/v1/tools/learnings/recall", strings.NewReader(`{ "project": "respawn-tech-docs", "task": "rodar os checks antes de abrir o PR", "top_k": 5 }`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Token", os.Getenv("RESPAWN_API_TOKEN"))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	if res.StatusCode >= 400 {
		panic(string(data))
	}
	fmt.Println(string(data))
}

Resposta 200

{
  "success": true,
  "trace_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "items": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "kind": "pitfall",
        "text": "O check:backend lê ../respawn-tech-backend; rode só com o backend já junto.",
        "source": "conversa de 06/10",
        "score": 0.82
      }
    ]
  }
}

Esquecer aprendizado

DELETE/v1/tools/learnings/{id}
Custo
Grátis
Dry-run
Não
Autenticação
qualquer um detools:learnings:writetools:manage
curl -sS -X DELETE 'https://api.respawntech.dev/v1/tools/learnings/550e8400-e29b-41d4-a716-446655440000' \
  -H "X-API-Token: $RESPAWN_API_TOKEN"
const response = await fetch('https://api.respawntech.dev/v1/tools/learnings/550e8400-e29b-41d4-a716-446655440000', {
  method: 'DELETE',
  headers: {
    'X-API-Token': process.env.RESPAWN_API_TOKEN,
  },
});

if (!response.ok) throw new Error((await response.json()).error.code);
console.log(await response.json());
import os
import requests

response = requests.delete(
    "https://api.respawntech.dev/v1/tools/learnings/550e8400-e29b-41d4-a716-446655440000",
    headers={
        "X-API-Token": os.environ["RESPAWN_API_TOKEN"],
    },
    timeout=30,
)
if not response.ok:
    raise RuntimeError(response.json()["error"]["code"])
print(response.json())
<?php
$ch = curl_init('https://api.respawntech.dev/v1/tools/learnings/550e8400-e29b-41d4-a716-446655440000');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('RESPAWN_API_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($status >= 400) {
    throw new RuntimeException(json_decode($body, true)['error']['code']);
}
print_r(json_decode($body, true));
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

func main() {
	req, err := http.NewRequest("DELETE", "https://api.respawntech.dev/v1/tools/learnings/550e8400-e29b-41d4-a716-446655440000", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Token", os.Getenv("RESPAWN_API_TOKEN"))

	client := &http.Client{Timeout: 30 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	if res.StatusCode >= 400 {
		panic(string(data))
	}
	fmt.Println(string(data))
}

Resposta 200

{
  "success": true,
  "trace_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "deleted": true
  }
}

Atualizado em