Manage Learnings
Read and manage learning records through the AgentOS /learnings endpoints.
AgentOS exposes /learnings REST endpoints for CRUD over the agno_learnings table, the table that backs the user_profile, user_memory, session_context, entity_memory, and decision_log stores (learned_knowledge lives in the knowledge base instead). Register a supported database with AgentOS to make these endpoints available. An agent with learning enabled can then read and write compatible records through its learning stores.
Prerequisites
- A database adapter that implements the learning CRUD methods: PostgreSQL, SQLite, MongoDB, and Valkey support them. A backend with only learning-store writes may still return
501for unsupported REST operations. - Register the database directly with
AgentOS(db=db)or through a registered agent. Enabling learning on an agent is optional for REST CRUD.
Example
For the example below, install uv pip install -U "agno[os]" openai, set OPENAI_API_KEY in the server terminal, and save the code as learnings_with_agentos.py. Start it with python learnings_with_agentos.py.
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.learn import LearningMachine
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS
db = SqliteDb(id="learnings-os-demo", db_file="tmp/learnings_os_demo.db")
learning = LearningMachine(
db=db,
model=OpenAIResponses(id="gpt-5.4"),
user_profile=True,
user_memory=True,
namespace="global",
)
assistant = Agent(
name="Assistant",
model=OpenAIResponses(id="gpt-5.4"),
instructions=["You are a helpful assistant. Use what you know about the user."],
db=db,
learning=learning,
)
agent_os = AgentOS(agents=[assistant])
app = agent_os.get_app()
if __name__ == "__main__":
agent_os.serve(app="learnings_with_agentos:app", reload=True)Browse the interactive OpenAPI docs at http://localhost:7777/docs.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /learnings | Paginated list with filters and sorting |
POST | /learnings | Create a record |
GET | /learnings/users | List users that own learnings, with last-activity timestamps |
DELETE | /learnings/users/{user_id} | Delete all of a user's learnings (or one type) |
GET | /learnings/{learning_id} | Fetch a single record |
PATCH | /learnings/{learning_id} | Update content and/or metadata |
DELETE | /learnings/{learning_id} | Delete a record |
Every endpoint accepts db_id and table query parameters to target a specific database or table. table requires db_id.
Listing and filtering
GET /learnings returns a paginated envelope: data holds the records and meta holds the pagination info (page, limit, total_pages, total_count).
curl "http://localhost:7777/learnings?user_id=demo-user&limit=10&page=1"| Parameter | Description |
|---|---|
learning_type | Filter by store (user_profile, user_memory, etc.) |
user_id, agent_id, team_id, session_id | Filter by owner |
namespace, entity_id, entity_type | Filter by scope or entity |
limit | Page size (1–1000, default 100) |
page | 1-indexed page number |
sort_by | created_at or updated_at (default). Unknown fields are ignored |
sort_order | asc or desc (default) |
For a per-user view, list users with GET /learnings/users, then drill into one with GET /learnings?user_id=....
Creating records
curl -X POST http://localhost:7777/learnings \
-H "Content-Type: application/json" \
-d '{
"learning_type": "user_profile",
"namespace": "global",
"user_id": "demo-user",
"content": {"user_id": "demo-user", "name": "Yash", "preferred_name": "Yash"},
"metadata": {"source": "rest-api-demo"}
}'Identity-keyed learning types use deterministic IDs derived from their identity fields. POST computes the same ID, so a record created through the API reconciles with what the agent reads and writes without creating orphaned or duplicate rows.
learning_type | Derived ID | Required identity fields |
|---|---|---|
user_profile | user_profile_{user_id} | user_id |
user_memory | memories_{user_id} | user_id |
session_context | session_context_{session_id} | session_id |
entity_memory, non-user namespace | entity_{namespace}_{entity_type}_{entity_id} | entity_type, entity_id (namespace defaults to global) |
entity_memory, namespace="user" | entity_user_{sha256(user_id)[:16]}_{entity_type}_{entity_id} | user_id, entity_type, entity_id |
- Provide the required identity field(s), or the request returns
422. - Include the same identity fields inside
contentso the agent's store can deserialize the record. - An existing record for that identity returns
409. UsePATCHto update it. - Other types (for example,
decision_log) get a generated ID, so a user can have many.
Updating records
PATCH replaces content and/or metadata with caller-supplied JSON. Row identity columns are immutable; keep identity fields within replacement content consistent with them. Use the same namespace as the learning store you intend to update.
curl -X PATCH http://localhost:7777/learnings/user_profile_demo-user \
-H "Content-Type: application/json" \
-d '{"content": {"user_id": "demo-user", "name": "Yash", "preferred_name": "Yash P."}}'Deleting records
# Delete a single record
curl -X DELETE http://localhost:7777/learnings/user_profile_demo-user
# Delete all of a user's learnings (add ?learning_type= to restrict to one store)
curl -X DELETE http://localhost:7777/learnings/users/demo-userBoth return 204. The user-level delete never touches records with no owner.
Authorization and isolation
Scoping follows the framework's opt-in user isolation contract (AuthorizationConfig(user_isolation=True)). Admins and requests with isolation disabled are unscoped. Anonymous requests are unscoped only on an open instance. JWT-enabled instances reject missing tokens with 401, static security-key callers are unscoped, and service-account PATs always self-scope unless they carry the admin scope. For a scoped non-admin caller:
| Operation | Behavior |
|---|---|
| List / list users | Bound to the caller. List also includes records with no owner (user_id IS NULL). A different user_id returns 403 |
| Create | Body user_id must be omitted/null or match the caller, otherwise 403 |
| Delete user | Only the caller's own learnings; a different user_id returns 403 |
| Get single record | A cross-user record returns 404 (no existence leak) |
| Patch / delete single record | Cross-user returns 404. Shared records (user_id IS NULL) are readable but admin-only to mutate, so a regular user gets 403 |
When RBAC is enabled, the routes require the learnings:read, learnings:write, or learnings:delete scopes.