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 501 for 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.

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

MethodPathDescription
GET/learningsPaginated list with filters and sorting
POST/learningsCreate a record
GET/learnings/usersList 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"
ParameterDescription
learning_typeFilter by store (user_profile, user_memory, etc.)
user_id, agent_id, team_id, session_idFilter by owner
namespace, entity_id, entity_typeFilter by scope or entity
limitPage size (1–1000, default 100)
page1-indexed page number
sort_bycreated_at or updated_at (default). Unknown fields are ignored
sort_orderasc 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_typeDerived IDRequired identity fields
user_profileuser_profile_{user_id}user_id
user_memorymemories_{user_id}user_id
session_contextsession_context_{session_id}session_id
entity_memory, non-user namespaceentity_{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 content so the agent's store can deserialize the record.
  • An existing record for that identity returns 409. Use PATCH to 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-user

Both 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:

OperationBehavior
List / list usersBound to the caller. List also includes records with no owner (user_id IS NULL). A different user_id returns 403
CreateBody user_id must be omitted/null or match the caller, otherwise 403
Delete userOnly the caller's own learnings; a different user_id returns 403
Get single recordA cross-user record returns 404 (no existence leak)
Patch / delete single recordCross-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.

Developer Resources