Skip to main content

Python SDK

The Python SDK (amfs on PyPI) connects an agent you control to your SenseLab account. It provides AgentMemory — the interface for reading, writing and learning — and Run, the smallest way to wire the learning loop into the agent.

Installation

Requires Python 3.11 or later. The project started life as AMFS (Agent Memory File System), so the package names and environment variables still carry that name; pip install amfs gets you SenseLab.
Check that it reaches your account with an API key from Settings → API Keys:

Quick start: the learning loop

Every run follows the same four steps. Get briefed, do the work while recording what you relied on and what you did, write what a future agent would need, and commit the outcome. The outcome is what makes the next briefing better.
Every one of these calls goes through your account: the briefing is compiled there, retrieve() ranks by meaning on the server, and the committed outcome becomes a decision trace you can open on the dashboard.
Pass outcome_type as an OutcomeType member (OutcomeType.SUCCESS, OutcomeType.FAILURE, …). The decision trace is built from the enum, so a bare string such as "success" raises here.

Connecting to SenseLab

AgentMemory reaches your account through an HttpAdapter, built with the SenseLab URL and an API key from Settings → API Keys:
Every method on AgentMemory then goes through the API: retrieve() uses the server’s embedder and index, briefing() returns compiled digests, and commit_outcome() seals a decision trace you can browse on the dashboard or with list_traces().
The SDK does not read AMFS_HTTP_URL or AMFS_API_KEY by itself. Those variables are honoured by the MCP server, but AgentMemory(agent_id=...) with no adapter= never contacts SenseLab — it falls back to storage on the local machine, silently, with no error. If your dashboard stays empty after “connecting”, this is why. Always construct the HttpAdapter as above.
The adapter identifies your agent and session on every request, so the server can attribute reads and keep a whole session together. HttpAdapter(base_url, api_key, timeout=30.0) also accepts a request timeout in seconds. See the Connect to SenseLab guide for getting a key.

Creating an instance

agent_id is the agent’s identity. Everything it writes is tagged with it, recall() and my_entries() are scoped to it, and the dashboard groups work by it. Use a stable, kebab-case role name (deploy-agent, dashboard-agent) that persists across sessions, not a per-task name.

Constructor options

With an importance evaluator

Every write() is scored and gets importance_score and importance_dimensions. If the evaluator raises, the write proceeds without scoring.

Context manager

Acting as another agent

A server that holds one process-wide handle can act for one of its callers without mutating shared state:

Core operations

Write

Writes never overwrite. Every write is a new immutable version; history() returns them all. Write with a TTL (time-to-live):
Private entries (shared=False) are invisible to other agents through read(), search(), list() and read_from(). Only the owning agent sees them, via recall() and my_entries().

Read

read() returns the latest shared version by any agent, or None. Every read is logged in the session’s causal chain — this is what commit_outcome() credits by default.

Recall, my entries, and reading from another agent

Unlike read(), which returns the latest version by any agent, recall() falls back through history to find the most recent version authored by this agent. read_from() creates a learned_from edge in the knowledge graph and logs the read on both agents’ timelines.

List

Search (filters)

Progressive retrieval with depth searches only the high-priority tiers for fast, high-signal results:
Composite recall scoring blends semantic similarity, recency and confidence:
recall_config scoring runs in the SDK over the entries search() returned, and its semantic component needs an embedder on your side. For ranking by meaning use retrieve() below — SenseLab does it on the server.

Retrieve (by meaning)

retrieve() is the default way to recall: a natural-language query, no key, no exact path. It runs on the server — embeddings, full text, confidence and evidence together, with discredited entries held back.
Ask for action priors. When the task ends in a choice among a fixed set of actions, pass the candidates as tool:action keys and read the recommendation from last_priors:

History

Stats

Timeline

Every write, outcome and cross-agent read is an event on the agent’s timeline:
The same history, with branches, tags and rollback, is the Git Repository section of the agent’s page — see Learning as Git.

Memory types

Classify entries to control how they decay and how the briefing presents them:

Outcomes and decision traces

Everything an agent reads, records and does in a session is collected into a decision trace. commit_outcome() seals it, persists it, and moves confidence on every entry the run relied on.

Record what you learned and what you did

record_action() is for what you did; record_context() is for what you learned. Reads and searches are tracked automatically and need neither. Use the real tool name consistently, and when the action is one of a fixed set pass action_key="<tool>:<action>" — it is what the outcome is credited to and what the next agent’s action priors are counted over.

Mark a failed attempt before trying another

Everything read and done since the previous boundary is attributed to that attempt. At commit_outcome() those entries receive the attempt’s failure and only what was read afterwards is credited with the final success — a stale entry that misled the agent loses confidence instead of being reinforced by the recovery. When a success follows failed attempts, a lesson-contrast-<ref> entry is written naming what failed and what worked. record_attempt() sends nothing by itself; commit once, at the end.

Commit the outcome

Without commit_outcome() the trace is lost when the session ends.

How confidence moves

Confidence is trust a memory has earned. Each committed outcome adds evidence to every entry it cited: a success confirms, a failure erodes, and failures weigh more than successes — a critical failure most of all. The effect is split across the entries the run relied on and scaled by surprise, so a failure on an entry everyone trusted costs more than one on an entry nobody was sure about. A long-validated entry survives one failure as contested and is discredited on the second. Every entry carries the result as evidence_status: untested, validated, contested or discredited. The briefing and retrieve() read it, so a discredited fix stops being recommended without anyone deleting it. See Confidence.

Explain the current session

Browse past traces

list_traces() filters by entity_path, agent_id, outcome_type, since and until, and pages with limit, offset or cursor. On the dashboard these are the agent’s runs.

Session attributes and LLM calls

Attributes are the dimensions a run is later filtered and grouped by; LLM calls are what give a run real token and cost figures on the dashboard. Both are collected during the session and sealed into the trace at commit_outcome().
Attributes are scalars only — at most 20 keys of up to 64 characters, string values up to 256 characters; keys are lowercased. Cost is not estimated from the model name: pass cost_usd if you want spend on the run. For nested steps with durations, see Instrument your agent.

Briefing

Agents retrieve pre-compiled knowledge digests ranked by relevance — summaries of entities, other agents and external sources — instead of searching through raw entries.

Parameters

Read the evidence sections first

The lead digest carries the state of knowledge from the outcome record:
  • validated — entries every outcome confirmed. Act on these.
  • discredited — entries a failure gated, with replaced_by where a later success is known. Avoid these.
  • regime_shift — long-validated entries that recently started failing. Something changed; verify before reusing anything in the scope.
Each hot_context row has an evidence_status. The briefing also carries tried_here — which actions were taken on this entity and how each fared — and, under a regime shift, an explore line naming the untried action assigned to you.

Digest types

Environment scoping

briefing() and retrieve() send the run’s environment — model, agent_version, runtime, platform — read from the session (mem.session_metadata = SessionMetadata(model=...), or set_session_attributes() with those keys). A procedure whose preconditions name a different environment is set apart as procedures_not_applicable in the briefing and dropped from retrieve() hits, with the failing precondition spelled out. Pass environment={} to send nothing.

Guidance with Run

You control your agent’s code; SenseLab does not. amfs.Run is the smallest way to wire it in — three calls at three points of a task — and it closes the loop that makes memory improve: guidance in, actions out, outcome in.
What to pass, and why:
  • Name what the agent acted on. begin() serves several entries and the agent follows one. causal_entry_keys on attempt_failed() and complete() sends the outcome to that one; left unnamed, every entry read since the last boundary is charged, and a correct lesson that merely shared the context with a wrong one loses confidence alongside it. Ask your model to return the keys it used, intersect them with guidance.entry_keys, and fall back to guidance.top_key. When the agent took an action a lesson recommended, guidance.lessons_claiming(action) gives you those keys without asking the model.
  • Follow the plan, not just the first action. guidance.plan is the order to try actions in for the whole run: the recommendation’s action first, then what has a winning record on tasks like this, then the untried candidates — never an action that already failed here. next_action is its first entry.
  • Declare the situation. With begin(..., situation="jest-snapshot-on-ui-pr") the outcome is stored under that label, and the next run that declares the same one gets priors counted over that situation’s own record rather than the pooled neighbourhood. Use a stable class label (the triage category, the failing check plus the kind of change), never the task text.
  • Write lessons as claims. run.learn(situation, action, worked, text) writes a structured lesson whose claim is (situation, action, worked). Restating a lesson inherits its outcome record instead of opening a fresh, untested entry, so a discredited lesson does not come back clean.
  • Pass the environment every time. model, agent_version and runtime scope procedures to this run and are stamped on the sealed trace — which is how SenseLab tells a change in your agent from a change in its memory.
  • Memory being down is not your agent being down. If the retrieve behind begin() fails, it returns empty guidance with error set instead of raising; the run continues and complete() still seals the outcome. on_tool_result() returns None in the same case.
Guidance carries:

Branches

Every AgentMemory has an active branch — main unless branch= or AMFS_BRANCH says otherwise — and every read and write goes there unless the call names another. checkout() moves a live memory without rebuilding the session:
Outcomes committed off main carry a memory_branch attribute, so a trace says which memory it read. See Learning Branching.

Scopes

A MemoryScope provides a focused view bound to a single entity path:

Scope utilities


Transactions

Group multiple writes into one atomic commit:

Commit log

Diff and patch


Integrity verification

Verify content hashes and integrity chains for stored entries:

Knowledge graph

The knowledge graph builds automatically as agents write, commit outcomes and learn from each other. Pattern refs and causal chains create edges. You can traverse it directly:

Agent binding

Profile

Capabilities

Declare capabilities so other agents can discover you:

Contracts

Set memory contracts that define expectations for entries:

Discovery


Conflict handling

Handle concurrent writes to the same key:

Rooms and collaboration

AgentMemory also exposes rooms — shared workspaces where several users’ agents collaborate. Entries written on a room’s entity path are shared with every member.

Negotiation

Rooms support structured negotiation sessions between agents:
See Rooms for how rooms, members and invitations work.

Troubleshooting

My writes do not show up on the dashboard. The SDK does not read AMFS_HTTP_URL or AMFS_API_KEY, and without an adapter it never contacts SenseLab. Pass adapter=HttpAdapter(base_url=..., api_key=...) explicitly — see Connecting to SenseLab. type(mem.adapter).__name__ should print HttpAdapter. briefing() returns [], list_traces() returns [], last_priors is None. Same cause: the AgentMemory was built without the HttpAdapter, so nothing reached your account. Compiled briefings, decision traces and action priors only exist there. commit_outcome raises AttributeError: 'str' object has no attribute 'value'. Pass OutcomeType.SUCCESS (or another member), not the string "success". read() returns None for an entry I wrote. If it was written with shared=False by another agent it is invisible to you. For your own private entries use recall().

Exports

Properties


See also