Crear un agente de programación GPT-6 Astra capaz de mejorar por sí mismo con memoria de código fuente
Principales conclusiones
- La memoria externa permite a GPT-6 Astra conservar las correcciones de código confirmadas entre sesiones y cadenas de respuesta distintas.
- VectorAI DB almacena soluciones verificadas fuera de la conversación para que Astra pueda recuperar soluciones similares antes de editar el código.
- El agente revisa la memoria antes del diagnóstico y solo almacena una solución una vez que las pruebas de aceptación confirman que la reparación funciona.
- Las integraciones locales y los metadatos estructurados permiten buscar soluciones anteriores por similitud, tipo de error, archivos afectados y resultado.
- Las pruebas demostraron que Astra podía recuperar una solución de una sesión anterior en una nueva cadena de respuestas y, aun así, seguir superando las pruebas de aceptación originales.
Uno de los aspectos más frustrantes a la hora de programar agentes es la facilidad con la que pierden contexto útil entre sesiones. Astra puede dedicar una sesión entera a resolver un error y llegar a una solución probada. Cuando aparece un error similar en una sesión posterior, puede tener que repetir gran parte de la misma investigación, ya que la solución anterior sigue vinculada a la sesión anterior.
Comprobamos si la memoria externa podía conservar una corrección confirmada en sesiones posteriores, la misma prueba que realizamos en nuestra versión del complemento de memoria OpenClaw, donde las correcciones de un asistente de programación tenían que persistir de la misma manera. En la primera ejecución, Astra resolvió un error de configuración de origen y almacenó la solución en la base de datos Actian VectorAI. En una segunda ejecución, se utilizó un proyecto diferente y una nueva cadena de respuestas, pero se recuperó ese registro antes de modificar ningún código. A continuación, Astra corrigió el error de normalización relacionado y las cuatro pruebas de aceptación originales se superaron.
Este tutorial te muestra cómo crear esa capa de memoria utilizando incrustaciones locales, VectorAI DB y dos funciones a las que Astra tiene acceso a través de la API de respuestas: una para buscar soluciones confirmadas y otra para almacenarlas.
Por qué Astra necesita una capa de memoria independiente
GPT-6 Astra puede mantener el contexto entre llamadas a la API, pero la aplicación debe indicarle qué interacción anterior corresponde a la actual. En la API de respuestas, previous_response_id conecta un respuesta a la anterior, lo que permite a Astra continuar con el mismo hilo de conversación. Para las conversaciones que deben mantenerse a lo largo de diferentes sesiones, dispositivos o tareas, la misma API también admite objetos de conversación duraderos.
Ambas opciones permiten mantener una conversación que la aplicación ya sabe que quiere continuar. Además, evitan que Astra inicie cadenas de respuesta independientes cada vez que se encuentra con un error conocido.
El historial resulta especialmente útil cuando un agente necesita un dato que no se ha incluido en el resumen de trabajo. BlackwellBoy ofrece un ejemplo práctico:
«Si hace tres horas se produjo un error extraño en una prueba y no quedó reflejado en las notas resumidas, Astra puede revisar los registros para localizarlo».
En este ejemplo se trata de localizar un evento anterior en los registros de una tarea de larga duración. Además, queríamos que las sesiones posteriores pudieran encontrar soluciones relevantes procedentes de trabajos independientes, por lo que guardamos cada solución confirmada en la base de datos de VectorAI para que una nueva cadena de respuesta pudiera buscarla.
Cómo funciona la arquitectura de la memoria
Cuando falla una prueba, Astra envía los detalles del error a search_codebase_memory. La herramienta genera una representación local y busca en la base de datos de VectorAI soluciones confirmadas similares. Cada resultado incluye el tipo de error, los archivos afectados, la solución y el resultado de la prueba.
El bucle del agente devuelve el resultado de la búsqueda a Astra como un function_call_output vinculado a la solicitud original a través de su call_id. A continuación, Astra compara cualquier solución encontrada con el código actual antes de realizar cambios. Si la búsqueda no encuentra nada relevante, continúa la investigación utilizando los archivos del proyecto y los resultados de las pruebas.
Tras implementar una corrección, Astra ejecuta las pruebas de aceptación. Si el resultado es satisfactorio, le permite llamar a store_fix_memory, que guarda el error, la solución, los archivos afectados y el resultado verificado. El gestor rechaza los registros no confirmados, lo que evita que los intentos fallidos aparezcan en búsquedas posteriores.
Este orden hace que la recuperación de la memoria se realice antes de la edición y que el almacenamiento de la memoria se produzca tras las pruebas superadas. Dado que VectorAI DB almacena los registros fuera de la cadena de respuesta, las sesiones posteriores pueden recuperar las correcciones guardadas durante el trabajo anterior.
El proyecto requiere acceso a Astra, una instancia local de la base de datos VectorAI y un modelo de incrustación local.
Configuración de la pila
GPT-6 Astra no está disponible en el nivel gratuito de la API, y la facturación de la API es independiente de la suscripción a ChatGPT. Actualmente, el procesamiento estándar cuesta 10 dólares por cada millón de tokens de entrada y 50 dólares por cada millón de tokens de salida. En nuestro experimento, la comprobación de transporte y las dos sesiones en directo costaron unos 0,15 dólares. Una nueva ejecución puede costar más o menos, dependiendo del uso de tokens y del número de solicitudes a la API de respuestas que necesite el agente.
Esquema del proyecto
El proyecto separa la capa de memoria, el bucle del agente Astra, las herramientas de codificación locales y el flujo de trabajo de demostración en sus propios archivos:
astra-codebase-memory/
├── docker-compose.yml
├── requirements.txt
├── settings.py
├── vectoraidb_memory_tools.py
├── astra_agent.py
├── coding_tools.py
├── cost_guard.py
├──main.py
├── smoke_test.py
├── session_demo.py
├── demo_projects/
└── tests/
vectoraidb_memory_tools.py contiene el backend de la base de datos de VectorAI, el embeddador local y los gestores de memoria. El bucle de la API de respuestas se encuentra en astra_agent.py. El main.py El punto de entrada conecta estos componentes para una única sesión de codificación, mientras que session_demo.py ejecuta el experimento controlado entre sesiones. La implementación completa, las pruebas y los proyectos de demostración están disponibles en el Repositorio de GitHub.
Iniciar VectorAI DB
Recupera la imagen actual de la base de datos de VectorAI:
docker pull actian/vectorai:latest
El proyecto inicia la configuración probada mediante docker-compose.yml:
docker compose -p astra-codebase-memory up -d
docker compose -p astra-codebase-memory ps
El archivo «Compose» asigna el punto final REST a http://localhost:16573, que es la dirección que utiliza el cliente de Python.
Instala las dependencias de Python
Las versiones de los paquetes fijados se almacenan en requirements.txt. Desde la raíz del repositorio en WSL2, crea el entorno e instálalos:
python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m pip install --no-deps --no-build-isolation -e .
Copia el archivo de entorno de ejemplo:
cp .env.example .env
Abrir .env y añade tu clave de API de OpenAI:
OPENAI_API_KEY=your_api_key
Los valores restantes en .env.example Configurar la conexión a la base de datos de VectorAI, el procesamiento de la API estándar y el límite de gasto que utiliza el proyecto.
Comprueba la colección y las incrustaciones locales
vectoraidb_memory_tools.py crea el astra_codebase_memory conjunto con vectores de 384 dimensiones y distancia coseno:
created = await self._call(
"collections_create",
self.collection_name,
{"vectors": {"size": EMBEDDING_DIMENSION, "distance": "Cosine"}},
timeout=30.0,
)
Cada vector almacenado contiene una carga útil con la descripción de la corrección, el tipo de error, la ruta del archivo, el resultado confirmado, la marca de tiempo y el ID de sesión.
Se carga el mismo archivo sentence-transformers/all-MiniLM-L6-v2 en la CPU y normaliza los vectores generados:
self._model = factory(self.model_name, device="cpu")
encoded = self._load_model().encode(
clean_text,
normalize_embeddings=True,
show_progress_bar=False,
convert_to_numpy=True,
)
El modelo se descarga la primera vez que se carga. Dado que genera estas representaciones de forma local, no consume créditos de representaciones de OpenAI.
La comprobación final se lleva a cabo smoke_test.py:
.venv/bin/python -u smoke_test.py store
.venv/bin/python -u smoke_test.py verify --memory-id "PASTE_MEMORY_ID_FROM_STORE"
El primer comando crea o valida la colección, almacena una solución provisional y la busca. Puedes copiar el resultado devuelto memory_id en el segundo comando, que confirma que el registro se ha conservado y, a continuación, lo elimina.
En este momento, VectorAI DB puede almacenar y recuperar registros de correcciones integrados. Astra sigue necesitando una forma controlada de utilizar esa base de datos, y ahí es donde entran en juego las dos herramientas de memoria.
Creación de las herramientas de memoria
Ambas herramientas de memoria se encuentran en vectoraidb_memory_tools.py y utiliza el mismo MemoryService. El servicio recibe el backend de la base de datos de VectorAI, el embedder local y el ID de la sesión actual cuando se inicia el agente.
Cuando Astra detecta un fallo, llama a search_codebase_memory antes de modificar cualquier código. El método almacena la descripción del error localmente y busca soluciones relacionadas en la base de datos de VectorAI:
async def search_codebase_memory(
self,
query: str,
*,
error_type: str | None = None,
top_k: int = 3,
) -> str:
clean_query = _required_text(query, "query")
_validate_search_arguments(top_k, error_type)
embedding = await self.embedder.embed(clean_query)
results = await self.backend.search(
embedding,
top_k=top_k,
error_type=error_type,
)
return format_search_results(results)
query contiene el mensaje de error o los detalles de la prueba fallida que Astra quiere buscar. El parámetro opcional error_type permite limitar la búsqueda a un tipo concreto de fallo, mientras que top_k establece el número máximo de resultados que se devuelven. El método convierte la consulta en una representación local y espera a que VectorAI DB complete la búsqueda antes de que Astra continúe.
format_search_results convierte los registros coincidentes en texto que la API de respuestas puede devolver como un function_call_output:
def format_search_results(
results: Sequence[MemorySearchResult],
) -> str:
"""Return compact readable evidence for a function-call output."""
if not results:
return "No relevant confirmed fixes found."
blocks = []
for index, result in enumerate(results, start=1):
record = result.record
blocks.append(
"\n".join(
(
f"Match {index} (score={result.score:.4f})",
f"Fix: {record.fix_description}",
f"Error type: {record.error_type}",
f"Files: {', '.join(record.file_paths)}",
f"Outcome: {record.outcome}",
f"Confirmed: {record.timestamp}",
f"Session: {record.session_id}",
)
)
)
return "\n\n".join(blocks)
Cada coincidencia proporciona a Astra la solución anterior y los detalles que necesita para evaluar su relevancia respecto al problema actual. La puntuación de similitud procede de la base de datos de VectorAI y se redondea a cuatro decimales en el resultado formateado. Las rutas de los archivos y el resultado de la prueba aportan más contexto sobre la solución almacenada.
Una vez finalizadas las reparaciones de Astra, el bucle de agentes ejecuta las pruebas del proyecto. Un resultado satisfactorio se convierte en el resultado confirmado y aceptado por store_fix_memory:
def confirm_outcome(self, outcome: str) -> None:
self._confirmed_outcomes.add(
_required_text(outcome, "outcome")
)
async def store_fix_memory(
self,
*,
fix_description: str,
error_type: str,
file_paths: Sequence[str],
outcome: str,
timestamp: datetime | None = None,
) -> str:
clean_fix = _required_text(
fix_description,
"fix_description",
)
clean_error = _required_text(error_type, "error_type")
clean_outcome = _required_text(outcome, "outcome")
if clean_outcome not in self._confirmed_outcomes:
raise UnconfirmedOutcomeError(
"outcome was not confirmed by the current run"
)
if isinstance(file_paths, (str, bytes)):
raise MemoryValidationError(
"file_paths must be a sequence of paths"
)
paths = tuple(file_paths)
confirmed_at = timestamp or datetime.now(UTC)
if (
confirmed_at.tzinfo is None
or confirmed_at.utcoffset() is None
):
raise MemoryValidationError(
"timestamp must include a timezone"
)
memory_id = str(
uuid5(
NAMESPACE_URL,
"\n".join(
(
self.session_id,
clean_fix,
clean_error,
clean_outcome,
*paths,
)
),
)
)
record = MemoryRecord(
memory_id=memory_id,
fix_description=clean_fix,
error_type=clean_error,
outcome=clean_outcome,
file_paths=paths,
timestamp=confirmed_at.isoformat(),
session_id=self.session_id,
embedding=await self.embedder.embed(clean_fix),
)
await self.backend.upsert(record)
return memory_id
El bucle del agente llama a confirm_outcome con el último resultado de la prueba superada justo antes de guardar la corrección. store_fix_memory comprueba ese valor, crea un identificador a partir de la sesión y los detalles de la reparación, incorpora la descripción de la solución de forma local y guarda el registro completado en la base de datos de VectorAI. El UUID generado se convierte en el identificador del registro en la base de datos de VectorAI. Si la misma sesión vuelve a realizar una solicitud de almacenamiento idéntica, genera el mismo UUID, por lo que actualiza el registro existente en lugar de duplicarlo.
Astra puede invocar estos métodos una vez que se hayan declarado como herramientas de funciones de la API de Responses. Las definiciones incluidas en el mismo archivo describen cada función y los argumentos que debe proporcionar el modelo. La definición de búsqueda también utiliza la opción de invocación asíncrona de herramientas de Astra, lo que permite que el modelo continúe con su trabajo de forma independiente mientras la aplicación ejecuta la búsqueda en memoria:
MEMORY_TOOL_DEFINITIONS = (
{
"type": "function",
"name": "search_codebase_memory",
"description": (
"Search confirmed past debugging results "
"using observed failure symptoms."
),
"async": True,
"strict": True,
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"error_type": {
"type": ["string", "null"]
},
"top_k": {
"type": "integer",
"minimum": 1,
"maximum": 10,
},
},
"required": [
"query",
"error_type",
"top_k",
],
"additionalProperties": False,
},
},
{
"type": "function",
"name": "store_fix_memory",
"description": (
"Store a debugging result after its outcome "
"is confirmed by the current run."
),
"strict": True,
"parameters": {
"type": "object",
"properties": {
"fix_description": {
"type": "string"
},
"error_type": {"type": "string"},
"file_paths": {
"type": "array",
"items": {"type": "string"},
},
"outcome": {"type": "string"},
},
"required": [
"fix_description",
"error_type",
"file_paths",
"outcome",
],
"additionalProperties": False,
},
},
)
Escenario strict a True mantiene cada llamada a una función dentro de su esquema declarado. Astra debe proporcionar todos los argumentos necesarios, y additionalProperties: False Rechaza los campos que no se ajustan a la definición. La herramienta de búsqueda se considera asíncrona porque espera a que se realice la incrustación local y la consulta en la base de datos de VectorAI.
Una vez registradas las funciones de memoria, el bucle del agente puede pasar las llamadas de Astra a MemoryService y devolver cada resultado en la respuesta correcta.
El bucle del agente
El bucle en astra_agent.py determina cuándo Astra puede buscar en la colección de memoria, examinar el proyecto, editar un archivo y guardar una corrección confirmada. Las instrucciones del sistema establecen ese orden al inicio de cada sesión:
SYSTEM_INSTRUCTIONS = """You are fixing a bug in a confined demonstration workspace.
Call search_codebase_memory using only observed failure symptoms before stating a diagnosis or
editing code. Wait for its function output. An empty result still completes the required search.
Use only the supplied coding tools. Store a fix only after run_tests returns exit code 0, and use
the exact confirmed_outcome string returned by that test call. Do not assume memory will help."""
La aplicación refuerza esas instrucciones con comprobaciones dentro del bucle. Cada llamada a `run` comienza con previous_response_id configurar en None, por lo que la primera solicitud a la API inicia una nueva cadena de respuestas. Una vez que Astra responde, el bucle guarda el ID de la respuesta devuelta y lo adjunta a la siguiente solicitud de la misma sesión.
async def run(
self,
prompt: str,
*,
session_id: str | None = None,
) -> AgentRunResult:
if not isinstance(prompt, str) or not prompt.strip():
raise ValueError("prompt must be a non-empty string")
run_session_id = session_id or str(uuid4())
previous_response_id: str | None = None
next_input: object = [
{"role": "user", "content": prompt.strip()}
]
memory_result_delivered = False
pending_memory_delivery = False
last_confirmed_outcome: str | None = None
start_event_index = len(self.event_logger.events)
run_model_requests = 0
run_local_tools = 0
while True:
if run_model_requests >= self.limits.max_turns:
raise AgentLimitError(
"model turn limit reached"
)
payload: dict[str, Any] = {
"model": "gpt-6-astra",
"instructions": SYSTEM_INSTRUCTIONS,
"input": next_input,
"tools": self.tool_definitions,
"reasoning": {"effort": "low"},
"text": {"verbosity": "low"},
"max_output_tokens": (
self.limits.max_output_tokens
),
}
if previous_response_id is not None:
payload["previous_response_id"] = (
previous_response_id
)
if pending_memory_delivery:
memory_result_delivered = True
pending_memory_delivery = False
response = await self._request(
payload,
run_session_id,
)
run_model_requests += 1
response_id, output = self._validate_response(
response
)
function_calls = [
item
for item in output
if item.get("type") == "function_call"
]
if not function_calls:
if not memory_result_delivered:
raise MemoryGateError(
"agent produced a diagnosis before "
"receiving memory output"
)
final_text = self._extract_text(output)
return AgentRunResult(
session_id=run_session_id,
final_text=final_text,
final_response_id=response_id,
model_request_count=run_model_requests,
local_tool_call_count=run_local_tools,
events=tuple(
self.event_logger.events[
start_event_index:
]
),
)
outputs: list[dict[str, str]] = []
memory_ready_at_response_start = (
memory_result_delivered
)
search_completed = False
for item in function_calls:
if (
run_local_tools
>= self.limits.max_tool_calls
):
raise AgentLimitError(
"local tool-call limit reached"
)
(
tool_output,
confirmed_outcome,
was_search,
) = await self._dispatch(
item,
session_id=run_session_id,
response_id=response_id,
memory_ready=(
memory_ready_at_response_start
),
last_confirmed_outcome=(
last_confirmed_outcome
),
)
run_local_tools += 1
if item.get("name") == "run_tests":
last_confirmed_outcome = (
confirmed_outcome
)
if item.get("name") == "apply_edit":
last_confirmed_outcome = None
search_completed = (
search_completed or was_search
)
outputs.append(
{
"type": "function_call_output",
"call_id": str(item["call_id"]),
"output": tool_output,
}
)
if search_completed:
pending_memory_delivery = True
previous_response_id = response_id
next_input = outputs
Cada resultado de la herramienta incluye el call_id a partir de la llamada a la función original de Astra. Esto permite que la siguiente respuesta asocie el valor devuelto con la solicitud correcta. La puerta de memoria registra el momento en que el resultado de la búsqueda ha llegado a Astra, mientras que last_confirmed_outcome mantiene disponible el último resultado de la prueba superada para store_fix_memory.
El _dispatch El método realiza comprobaciones finales de almacenamiento. Compara el resultado proporcionado por Astra con el último resultado de prueba satisfactorio antes de confirmar y guardar el registro:
elif name == "store_fix_memory":
self._require_keys(
arguments,
{
"fix_description",
"error_type",
"file_paths",
"outcome",
},
)
if not memory_ready:
raise MemoryGateError(
"memory storage attempted before "
"memory result delivery"
)
if (
last_confirmed_outcome is None
or arguments["outcome"]
!= last_confirmed_outcome
):
raise ConfirmedFixGateError(
"store_fix_memory requires the latest "
"passing-test confirmed_outcome"
)
self.memory_service.confirm_outcome(
last_confirmed_outcome
)
result = {
"memory_id": (
await self.memory_service.store_fix_memory(
**arguments
)
)
}
was_search = False
confirmed_outcome = last_confirmed_outcome
El proyecto envía cada carga útil a la API de respuestas con requests.post. La llamada HTTP se encuentra dentro de RealHTTPResponsesTransport, que además comprueba que se hayan configurado el modo en producción, la clave de API y el presupuesto del proyecto antes de que se envíe una solicitud:
response = await asyncio.to_thread(
requests.post,
RESPONSES_URL,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=payload,
timeout=self.timeout_seconds,
)
response.raise_for_status()
result = response.json()
Organiza una sesión de programación
main.py ofrece el punto de partida para una sesión de programación. Tras cargar la configuración y comprobar el límite de gasto, se conecta al backend de la base de datos de VectorAI, al servicio de memoria, a las herramientas de programación y al transporte de la API de respuestas:
backend = ActianVectorAIBackend(
settings.vectorai_url,
collection_name=settings.vectorai_collection,
grpc_url=settings.vectorai_grpc_url,
)
await backend.ensure_collection()
memory_service = MemoryService(
backend,
SentenceTransformerEmbedder(
settings.embedding_model
),
session_id=run_id,
)
transport = RecordedLiveTransport(
RealHTTPResponsesTransport(
guard,
enabled=True,
api_key=settings.api_key,
processing_tier=settings.processing_tier,
),
ledger=ledger,
guard=guard,
session_id=run_id,
transcript_path=(
evidence_dir
/ "private"
/ f"{run_id}-transcript.jsonl"
),
)
agent = AstraAgent(
transport,
memory_service,
SafeCodingTools(workspace),
limits=AgentLimits(
max_turns=LIVE_MAX_TURNS,
max_output_tokens=LIVE_MAX_OUTPUT_TOKENS,
),
)
result = await agent.run(
prompt,
session_id=run_id,
)
El completo main.py El archivo del repositorio también comprueba el espacio de trabajo, la clave de API, el nivel de procesamiento, el ID de ejecución y la aprobación de los costes en tiempo real antes de iniciar la sesión.
El escenario «Alpha» contiene el error intencionado que el agente va a investigar. Cópialo en una carpeta de trabajo para que Astra pueda editar los archivos sin modificar la versión original:
mkdir -p runs/tutorial
cp -R demo_projects/templates/scenario_alpha runs/tutorial/scenario_alpha
Ejecuta el agente en el proyecto copiado:
.venv/bin/python main.py \
--workspace runs/tutorial/scenario_alpha \
--run-id tutorial-alpha \
--approve-live-cost
El --approve-live-cost El indicador confirma que la sesión puede enviar solicitudes de pago a la API de Respuestas. Cuando finaliza la ejecución, main.py muestra la respuesta de Astra, el número de solicitudes a la API y de llamadas a herramientas, así como el coste de la sesión.
Para evaluar la memoria en distintas cadenas de respuesta, session_demo.py ejecuta dos proyectos con la misma colección de la base de datos de VectorAI. Crea un nuevo servicio de memoria y un nuevo agente para cada proyecto, mientras que la función completa también se encarga de la validación y la limpieza:
for scenario, suffix in (
("scenario_alpha", "alpha"),
("scenario_beta", "beta"),
):
session_id = f"{run_id}-session-{suffix}"
workspace = reset_workspace(run_id, scenario)
coding_tools = SafeCodingTools(workspace)
transport = RecordedLiveTransport(
transport_factory(),
ledger=ledger,
guard=guard,
session_id=session_id,
transcript_path=(
evidence_dir
/ "private"
/ f"{run_id}-{suffix}-transcript.jsonl"
),
)
service = TrackedMemoryService(
backend,
embedder,
session_id=session_id,
tracker=tracker,
)
agent = AstraAgent(
transport,
service,
coding_tools,
limits=AgentLimits(
max_turns=LIVE_MAX_TURNS,
max_output_tokens=LIVE_MAX_OUTPUT_TOKENS,
),
event_logger=EventLogger(
evidence_dir
/ "private"
/ f"{run_id}-{suffix}-events.jsonl"
),
)
result = await agent.run(
DEMO_PROMPT,
session_id=session_id,
)
new_chains = all(
bool(transport.requests)
and "previous_response_id"
not in transport.requests[0]
for transport in transports
)
En la primera sesión no había soluciones almacenadas a las que recurrir. Astra identificó que el error en la comprobación del origen se debía a los espacios en blanco alrededor de los valores separados por comas, y actualizó app/service.py, y guardé el resultado una vez que se superaron las cuatro pruebas:
Fixed `app/service.py` to trim whitespace around comma-separated allowed origins. The leading space caused the second origin to be rejected.
All 4 tests pass. Stored the confirmed fix result.
En la segunda sesión, cambiamos a otro proyecto y comenzamos una nueva cadena de respuestas. Su búsqueda en la memoria encontró el registro de la primera ejecución:
Match 1 (score=0.1650)
Fix: Trim surrounding whitespace from each comma-separated APP_ALLOWED_ORIGINS value in configured_values so the second configured origin matches exactly and receives Access-Control-Allow-Origin.
Error type: AssertionError
Files: app/service.py
Outcome: pytest passed with exit code 0
Confirmed: 2026-09-15T12:11:51.934136+00:00
Session: live-session1-20260915-1207-session-alpha
Según la transcripción, la recuperación se produce antes de cualquier lectura de archivos relevante, diagnóstico o cambio de código. Posteriormente, Astra corrigió un error de normalización independiente en app/service.py, convirtiendo valores como Audit-Log a audit_log. Dado que la ejecución en producción también modificó una aserción en tests/test_service.py, volvimos a comprobar la corrección de la aplicación en un nuevo espacio de trabajo que contenía las pruebas originales, sin modificar:
.... [100%]
4 passed in 0.17s
El hecho de que se hayan superado las cuatro pruebas de aceptación originales en el nuevo espacio de trabajo confirma la corrección de la aplicación de la Sesión 2. La transcripción también verifica que una corrección confirmada de la Sesión 1 llegó a Astra en una cadena de respuestas independiente antes de que comenzaran el diagnóstico y la edición.
Puedes ejecutar el mismo flujo de trabajo de dos sesiones con VectorAI DB Community Edition y el código completo que se encuentra en el repositorio asociado.
También registramos cada llamada a una herramienta para comprobar si la memoria influía en la cantidad de trabajo realizado en sesiones posteriores.
En qué mejora el agente
Ejecutamos la misma tarea de Scenario Beta en cinco sesiones activas de Astra. Cada sesión utilizó un espacio de trabajo nuevo y una cadena de respuesta independiente, por lo que su primera solicitud no incluía previous_response_id.
La sesión 1 comenzó con una colección vacía de la base de datos de VectorAI y almacenó su corrección confirmada una vez superadas las pruebas. Las sesiones 2 a 5 comenzaron con ese registro como única memoria disponible. Los registros creados por esas sesiones posteriores se eliminaron antes de la siguiente ejecución.
| Sesión | Memoria recuperada de la sesión 1 | Respuestas a las solicitudes de la API | Llamadas a herramientas locales | Pruebas sin realizar | Coste |
| 1 | No | 7 | 9 | 4 aprobados | $0.0779135 |
| 2 | Sí | 7 | 9 | 4 aprobados | $0.0758070 |
| 3 | Sí | 7 | 9 | 4 aprobados | $0.0702995 |
| 4 | Sí | 7 | 9 | 4 aprobados | $0.0718085 |
| 5 | Sí | 7 | 9 | 4 aprobados | $0.0704115 |
En cada sesión se realizaron siete solicitudes a la API de Responses y nueve llamadas a herramientas locales. Astra editó app/service.py y tests/test_service.py, así que comprobamos cada corrección de la aplicación en un espacio de trabajo nuevo que contenía las pruebas originales. Las cuatro pruebas se superaron en todas las ocasiones.
Las sesiones 2 a 5 recuperaron la solución probada en la sesión 1, a pesar de que se iniciaron nuevas cadenas de respuesta. Esto resulta especialmente útil cuando un fallo se asemeja a uno que el agente ya ha resuelto anteriormente. Los nuevos errores y las decisiones de diseño más amplias siguen requiriendo un análisis del proyecto actual.
Conclusión
Nuestra serie de cinco sesiones demostró que una corrección confirmada podía pasar de una cadena de respuesta de Astra a otra a través de VectorAI DB. Las sesiones 2 a 5 recuperaron los registros guardados durante la sesión 1, y cada sesión superó las cuatro pruebas de aceptación originales.
VectorAI DB Community Edition te ofrece una base de datos local para que pruebes el mismo enfoque con tus propias tareas de programación. Descarga la imagen con:
docker pull actian/vectorai:latest
Desde allí, puedes conectar las herramientas de búsqueda y almacenamiento de este tutorial a Astra para que pueda guardar las soluciones que hayan funcionado y volver a encontrarlas en sesiones posteriores.
Preguntas frecuentes
¿GPT-6 Astra recuerda las sesiones anteriores?
No de forma automática. previous_response_id O bien, un objeto «Conversación» puede conservar el contexto anterior, pero una cadena de respuestas independiente necesita una herramienta de memoria externa para recuperar soluciones de otras sesiones.
¿Cómo puedo añadir memoria persistente a un agente Astra GPT-6?
Almacena las correcciones confirmadas fuera de la cadena de respuesta y ofrece herramientas para buscar y añadir registros. En este tutorial, Astra realiza una búsqueda antes de editar y almacena una corrección solo después de que las pruebas hayan dado resultado satisfactorio.
¿Puedo utilizar una base de datos vectorial externa con GPT-6 Astra?
Sí. Define las herramientas de la función de la API de respuestas que buscan y actualizan la base de datos y, a continuación, devuelven los resultados a Astra como salida de la herramienta.
¿Cuál es la diferencia entre el modelo de OpenAI y... file_search ¿una herramienta y una base de datos vectorial externa?
El de OpenAI file_search Es una herramienta alojada que permite buscar archivos subidos. Una base de datos vectorial externa permite a tu aplicación controlar directamente el almacenamiento, las representaciones vectoriales, los metadatos, los filtros, las actualizaciones y la eliminación.