Skip to Content
ExamplesAn agent that uses memory
Raw

An agent that uses memory

An Aetherfy agent is code you deploy from a directory containing an aetherfy.yaml. This page builds one that writes a memory and searches it, in Python and in Node, and deploys it with the CLI.

The important property: an agent does not need an API key in its configuration. Every Aetherfy agent machine receives AETHERFY_API_KEY in its environment automatically, and both SDKs read that variable when constructed with no arguments. So inside an agent, MemoryClient() just works.

What an Aetherfy agent is made of

FilePurpose
aetherfy.yamlThe agent manifest — name, runtime, type, resources
Your entrypointmain.py, index.js, or whatever your runtime expects
A dependency declarationrequirements.txt for Python, package.json for Node

Deploy the directory with the Aetherfy CLI. The binary is afy:

afy deploy

The Aetherfy agent manifest

A minimal aetherfy.yaml:

name: support-bot runtime: python3.11 type: service memory_mb: 256
KeyMeaning
nameThe agent’s name
runtimeWhich runtime image to build on. See the table below
typeservice for a long-lived HTTP process, job for one that runs once and exits
memory_mbMemory allocated to the agent machine

Valid runtime values:

Runtime
python3.11
python3.12
python3.13
node20
node22
node20-ts
node22-ts
bun
dockerfile

python on its own is not a valid runtime. Neither is node, or a version that is not in the table. Pick an exact value from the list.

The full manifest reference, including everything beyond these four keys, is at aetherfy.yaml reference.

Choosing between an Aetherfy service and a job

typeBehaviourUse for
serviceLong-lived HTTP process, stays upSomething that answers requests
jobRuns once and exitsBatch work, imports, scheduled work

The worked example below uses type: job, because a job has an obvious start and end and so demonstrates the whole memory round trip in one run. Everything it does works identically inside a service; only the process lifecycle differs. For the HTTP contract a service must satisfy, see Deploy an agent.

Environment variables every Aetherfy agent receives

These are injected into the agent machine. You do not set them, and you should not hard-code their values.

VariableContains
AETHERFY_AGENT_IDThe agent’s id
AETHERFY_AGENT_NAMEThe agent’s name, as declared in aetherfy.yaml
AETHERFY_REGIONThe region this machine is running in
AETHERFY_WORKSPACEThe workspace the agent belongs to
AETHERFY_API_KEYAn Aetherfy API key — this is why the SDKs need no configuration
AETHERFY_API_URLThe control API base URL
AETHERFY_VECTORS_URLThe vector database base URL
AETHERFY_SPAWN_URLThe spawn endpoint
AETHERFY_DEPLOYMENT_IDThe id of the deployment this machine came from
AETHERFY_SPAWN_IDPresent only on ephemeral runs

Do not test for AETHERFY_SPAWN_ID to decide whether you are running on Aetherfy — it is absent on ordinary deployments. Use AETHERFY_AGENT_ID for that.

Agents reach the vector database in their own region, so a memory read from inside an agent does not cross a region boundary. Note that region topology is plan-scoped: Free and Starter agents run in a single region, and placement across more than one region begins at the plan named Performance. See Regions & replication.

A Python Aetherfy agent that uses memory

Three files in one directory.

aetherfy.yaml

name: memory-demo-py runtime: python3.11 type: job memory_mb: 256

requirements.txt

aetherfy-vectors

The single aetherfy-vectors distribution provides both the aetherfy_vectors and aetherfy_memory import packages. There is nothing else to add.

main.py

"""An Aetherfy agent that writes a memory and searches it back. Runs as a `job`: it executes once and exits. No API key is configured anywhere — MemoryClient() reads AETHERFY_API_KEY, which the Aetherfy agent machine provides automatically. """ import hashlib import os from typing import List from aetherfy_memory import MemoryClient from aetherfy_memory.exceptions import NamespaceAlreadyExistsError def embed(text: str) -> List[float]: """REPLACE ME with a real embedding model. Aetherfy does not generate embeddings — every memory write must carry a vector you computed. This placeholder is deterministic (so the agent runs as written) but not semantic. See /examples/embeddings. """ h = hashlib.sha256(text.encode()).digest() return [h[i % len(h)] / 255.0 for i in range(384)] def main() -> None: print("agent", os.environ.get("AETHERFY_AGENT_NAME"), "region", os.environ.get("AETHERFY_REGION")) # No arguments: the API key comes from AETHERFY_API_KEY. memory = MemoryClient() namespace_name = "customer-42" try: memory.create_namespace(namespace_name) print("created namespace", namespace_name) except NamespaceAlreadyExistsError: print("namespace already exists", namespace_name) ns = memory.namespace(namespace_name) # Write a memory. add() is keyword-only and returns the point id. memory_id = ns.add( text="Lives in NYC and prefers email", vector=embed("Lives in NYC and prefers email"), metadata={"source": "support-transcript"}, ) print("wrote memory", memory_id) # Read it back. search() is keyword-only in Python. results = ns.search( vector=embed("where is the customer based?"), limit=3, with_payload=True, ) for r in results: print("hit", r.id, r.payload) # hit <uuid> {'text': 'Lives in NYC and prefers email', # 'metadata': {'source': 'support-transcript'}} print("total memories", ns.count()) if __name__ == "__main__": main()

Deploying the Python Aetherfy agent

From the directory containing those three files:

afy deploy

A Node Aetherfy agent that uses memory

Three files in one directory.

aetherfy.yaml

name: memory-demo-node runtime: node22 type: job memory_mb: 256

Use node20/node22 for plain JavaScript, or node20-ts/node22-ts if your entrypoint is TypeScript.

package.json

{ "name": "memory-demo-node", "private": true, "type": "module", "main": "index.js", "dependencies": { "aetherfy-vectors": "*" } }

The npm package is aetherfy-vectors, unscoped. MemoryClient is exported from the package root; there is no aetherfy-vectors/memory subpath.

index.js

/** * An Aetherfy agent that writes a memory and searches it back. * * Runs as a `job`: it executes once and exits. No API key is configured * anywhere — new MemoryClient() reads AETHERFY_API_KEY, which the Aetherfy * agent machine provides automatically. */ import { createHash } from 'node:crypto'; import { MemoryClient, NamespaceAlreadyExistsError } from 'aetherfy-vectors'; /** * REPLACE ME with a real embedding model. * * Aetherfy does not generate embeddings — every memory write must carry a * vector you computed. This placeholder is deterministic (so the agent runs * as written) but not semantic. See /examples/embeddings. */ function embed(text) { const h = createHash('sha256').update(text).digest(); return Array.from({ length: 384 }, (_, i) => h[i % h.length] / 255); } async function main() { console.log('agent', process.env.AETHERFY_AGENT_NAME, 'region', process.env.AETHERFY_REGION); // No arguments: the API key comes from AETHERFY_API_KEY. const memory = new MemoryClient(); const namespaceName = 'customer-42'; try { await memory.createNamespace(namespaceName); console.log('created namespace', namespaceName); } catch (e) { if (!(e instanceof NamespaceAlreadyExistsError)) throw e; console.log('namespace already exists', namespaceName); } const ns = await memory.namespace(namespaceName); // Write a memory. add() takes one options object and resolves to the id. const memoryId = await ns.add({ text: 'Lives in NYC and prefers email', vector: embed('Lives in NYC and prefers email'), metadata: { source: 'support-transcript' }, }); console.log('wrote memory', memoryId); // Read it back. search() takes the vector positionally in JavaScript. const results = await ns.search(embed('where is the customer based?'), { limit: 3, withPayload: true, }); for (const r of results) console.log('hit', r.id, r.payload); // hit <uuid> { text: 'Lives in NYC and prefers email', // metadata: { source: 'support-transcript' } } console.log('total memories', await ns.count()); } main();

Deploying the Node Aetherfy agent

From the directory containing those three files:

afy deploy

Keeping a conversation in an Aetherfy agent

A namespace holds standalone facts. When the agent is handling a conversation, use a thread instead — it stores ordered messages with a role and content, and reads back in order.

from aetherfy_memory import MemoryClient from aetherfy_memory.exceptions import ThreadAlreadyExistsError memory = MemoryClient() try: memory.create_thread("conv-99") except ThreadAlreadyExistsError: pass chat = memory.thread("conv-99") chat.add(role="user", content="Can you reset my password?", vector=embed("Can you reset my password?")) chat.add(role="assistant", content="I've sent a reset link.", vector=embed("I've sent a reset link.")) # Rebuild the recent context to hand to your model. context = [(m.role, m.content) for m in chat.history(limit=20)] print(context) # [('user', 'Can you reset my password?'), ('assistant', "I've sent a reset link.")]
import { MemoryClient, ThreadAlreadyExistsError } from 'aetherfy-vectors'; const memory = new MemoryClient(); try { await memory.createThread('conv-99'); } catch (e) { if (!(e instanceof ThreadAlreadyExistsError)) throw e; } const chat = await memory.thread('conv-99'); await chat.add({ role: 'user', content: 'Can you reset my password?', vector: embed('Can you reset my password?') }); await chat.add({ role: 'assistant', content: "I've sent a reset link.", vector: embed("I've sent a reset link.") }); const context = (await chat.history({ limit: 20 })).map((m) => [m.role, m.content]); console.log(context); // [ [ 'user', 'Can you reset my password?' ], // [ 'assistant', "I've sent a reset link." ] ]

Threads use append_many / appendMany for bulk writes — they have no add_many / addMany. And a Thread is not a Namespace subclass in either SDK, so code written against one is not reusable against the other for writes. Details in The memory API.

Things that will break an Aetherfy agent using memory

MistakeWhat happensFix
runtime: pythonThe manifest is invalidUse an exact value such as python3.11
Hard-coding an API key in the agentWorks until the key is rotatedRely on the injected AETHERFY_API_KEY
Writing a memory without a vectorEmbeddingNotSupportedError, raised before any requestCompute a vector; see Bring your own embeddings
A namespace name like -customer or has spaceInvalidNameErrorNames must match ^[a-zA-Z0-9][a-zA-Z0-9._-]{0,254}$
create_namespace on every run without catchingNamespaceAlreadyExistsError on the second runCatch it, or check namespace_exists first
Using metadata={"text": …} on a namespaceValueError in Python, TypeError in JavaScripttext is reserved on a namespace, as are role/content/ts on a thread
Calling add_many on a threadAttribute/type errorThreads use append_many / appendMany
Importing from aetherfy-vectors/memory in NodeModule not foundThe package exports only "." — import from the root

Next steps with Aetherfy agents

Last updated on