Migrate from CrewAI to OSYRA Memory (OME)
This guide maps CrewAI's memory subsystem onto OSYRA Memory (OME) claims. CrewAI is an agent-framework memory-adapter migration, the same family as the LangGraph guide; read that guide first for the full reference, then read this one for the CrewAI-specific differences.
| Aspect | Value |
|---|---|
| Migration pattern | Agent-framework memory-adapter |
| Source scope | CrewAI Crew runs; per-crew or per-agent memory |
| Source-side write access required | Yes — the adapter wraps CrewAI's Storage interface |
| Fidelity guarantees | Crew-run reconstruction: task order preserved; agent role preserved; tool calls preserved as separate claims |
| Embedding policy | OME re-embeds |
| Rollback | Adapter-disable plus claim-revoke (see §8) |
§1 — Prerequisites
Source side (CrewAI)
- A CrewAI 0.60+ application using one of:
Crewwithmemory=True(default memory enabled; CrewAI's bundled storage)Crewwith a custommemory_config(for example, ChromaDB or Postgres backing)
- CrewAI's memory persistence path (defaults to
~/.crewai/memory/for local; varies for custom storage). - The crew name or ID, plus the list of agent roles (for example
researcher,analyst,writer). - For task-level migration: access to crew run outputs (typically captured in your application's logging or via the
Crew.kickoff()return value). - CrewAI 0.60.x or later (the memory subsystem API was stabilized in 0.60; earlier versions are out of scope).
OSYRA side (OME)
Identical to the LangGraph prerequisites. CrewAI-specific naming:
- Suggested runtime signer DID:
did:osyra:ws:<uuid>:signer:agent-crewai-<crew-name>. - Suggested runtime tag:
crewai:<crew-name>:run:<run-id>:agent:<role>, orcrewai:<crew-name>:agent:<role>for a cross-run aggregate.
Credential handling
Supply credentials through environment variables sourced from a secret store, never as command-line flags.
§2 — Source-system schema (CrewAI)
CrewAI's memory subsystem has four sub-memories, each with a different shape, plus task outputs.
Short-term memory (STM)
Per-task, ephemeral within a crew run; backed by a vector store (default ChromaDB). Items are the agent's intermediate reasoning and scratchpad content.
| Source field | Source type | Treatment |
|---|---|---|
| text | string | Becomes claim.body |
| agent_role | string | Becomes claim.metadata.agent_role and a tag suffix |
| task_id | string | Becomes claim.metadata.task_id |
| (derived) sub-memory type | string ("short-term") | Becomes claim.metadata.memory_layer |
Long-term memory (LTM)
Cross-run, persistent; backed by SQLite (default) or custom. Items are distilled learnings from past runs.
| Source field | Source type | Treatment |
|---|---|---|
| text | string | Becomes claim.body |
| agent_role | string | Becomes claim.metadata.agent_role |
| task | string (the task the learning came from) | Becomes claim.metadata.source_task |
| quality_score | float (LTM ranking) | Becomes claim.metadata.quality_score |
| (derived) sub-memory type | string ("long-term") | metadata.memory_layer |
Entity memory
Cross-run, persistent; backed by a vector store. Items are structured records about entities the crew has encountered (people, places, concepts).
| Source field | Source type | Treatment |
|---|---|---|
| entity_name | string | Becomes claim.external_id (with namespace prefix entity:) |
| description | string | Becomes claim.body |
| attributes | dict | Becomes claim.metadata.source_attributes.* |
| (derived) | string ("entity") | metadata.memory_layer; claim.type = memory.fact |
Contextual memory
Per-run, derived. Items are contextual snippets (for example, "the user mentioned they are in EU jurisdiction").
| Source field | Source type | Treatment |
|---|---|---|
| text | string | Becomes claim.body |
| context_key | string | Becomes claim.metadata.context_key |
| (derived) | string ("contextual") | metadata.memory_layer |
Task outputs
Separate from the memory subsystem, but valuable to migrate.
| Source field | Source type | Treatment |
|---|---|---|
| TaskOutput.raw | string | Becomes claim.body |
| TaskOutput.pydantic | Pydantic model or None | If structured: serialize to JSON, store in claim.metadata.structured_output |
| TaskOutput.json_dict | dict or None | Same as pydantic, pre-serialized |
| TaskOutput.agent | string (role) | claim.metadata.agent_role |
| TaskOutput.description | string | claim.metadata.task_description |
| (derived) | string ("task-output") | metadata.memory_layer; claim.type = memory.observation |
Concepts that are not preserved
| Source concept | Why not preserved | Implication | |---|---|---| | Agent role definitions | Configuration, not memory | None | | Task definitions | Configuration, not memory | None | | Process type (sequential, hierarchical) | Control flow, not memory | None | | Streaming step events | Only complete task outputs are captured | If you stream, the adapter waits |
§3 — OSYRA target mapping
The mapping follows the same general shape as the LangGraph mapping. The key CrewAI-specific decision: each of the four sub-memories becomes a distinct memory_layer tag prefix, so you can query OME with sub-memory granularity.
Per-sub-memory mapping
| Sub-memory | Claim type | metadata.memory_layer | Tag format |
|---|---|---|---|
| Short-term | memory.observation | short-term | crewai:<crew>:run:<run-id>:layer:short-term |
| Long-term | memory.fact | long-term | crewai:<crew>:layer:long-term (cross-run) |
| Entity | memory.fact | entity | crewai:<crew>:layer:entity (cross-run) |
| Contextual | memory.observation | contextual | crewai:<crew>:run:<run-id>:layer:contextual |
| Task outputs | memory.observation | task-output | crewai:<crew>:run:<run-id>:layer:task-output:agent:<role> |
Entity external_id collision avoidance
Because entity memory uses entity_name as external_id, and entity names can collide across crews (two crews might both have an entity named "Salesforce"), the migration prefixes the external ID:
external_id = "entity:<crew-name>:<entity_name>"This ensures cross-crew migrations into the same workspace do not collide. The runtime adapter applies the same prefix, so writes and reads are symmetric.
Adapter interface (Python)
CrewAI's Storage interface is the integration point. The osyra-crewai package ships a single adapter:
from crewai import Crew, Agent, Task
from osyra_crewai import OsyraStorage
osyra_storage = OsyraStorage(
workspace_id="ws_abc123",
signer_did="did:osyra:ws:abc123:signer:agent-crewai-prod",
crew_name="research-crew",
# Optional: forward writes to a "real" storage as well (dual-write)
inner_storage=...,
)
crew = Crew(
agents=[researcher, analyst],
tasks=[task1, task2],
memory=True,
memory_config={
"provider": "custom",
"storage": osyra_storage,
},
)
result = crew.kickoff()The adapter intercepts all four sub-memory writes and task outputs.
Override flags (CLI back-fill)
| Flag | Default | Effect |
|---|---|---|
| --source-memory-dir <path> | ~/.crewai/memory/ | CrewAI memory persistence directory |
| --crew-name <name> | (required) | Goes into tags and the signer DID |
| --sub-memories <list> | all | Comma-separated: short-term,long-term,entity,contextual,task-output |
| --run-id <id> | (all) | Migrate only a specific run-id; default migrates all runs found |
§4 — CLI walkthrough
Dry-run
export OSYRA_WORKSPACE_ID="ws_abc123"
export OSYRA_SIGNER_DID="did:osyra:ws:abc123:signer:migration"
osyra migrate crewai \
--source-memory-dir ~/.crewai/memory/ \
--crew-name research-crew \
--dry-runThe dry-run reports per-sub-memory item counts:
Source memory directory: ~/.crewai/memory/
Crew: research-crew
Short-term memory: 1,247 items
Long-term memory: 183 items
Entity memory: 47 entities
Contextual memory: 412 items
Task outputs: 892 outputs
Total claims (est): 2,781Migrate only LTM + entity (cross-run history)
osyra migrate crewai \
--source-memory-dir ~/.crewai/memory/ \
--crew-name research-crew \
--sub-memories long-term,entity \
--output-dir ./crewai-ltm-entity-migrate/Migrate the cross-run sub-memories (LTM and entity) first, since they have customer-facing value; defer the per-run sub-memories (STM, contextual, task-output) to a separate pass.
Full back-fill
osyra migrate crewai \
--source-memory-dir ~/.crewai/memory/ \
--crew-name research-crew \
--batch-size 1000 \
--output-dir ./crewai-full/Deploy the adapter
See §3 for adapter usage. The deployment order is identical to the LangGraph walkthrough.
§5 — Expected output
osyra-migrate crewai
Workspace: ws_abc123
Signer DID: did:osyra:ws:abc123:signer:migration
Source memory dir: ~/.crewai/memory/
Crew: research-crew
Sub-memories: all (short-term, long-term, entity, contextual, task-output)
Batch size: 1000
Output dir: ./crewai-full/
[layer 1/5: short-term]
Items found: 1,247
Bundles: 2
Bundle CIDs: bafy2bzaceabc...001, bafy2bzaceabc...002
[layer 2/5: long-term]
Items found: 183
Bundles: 1
Bundle CIDs: bafy2bzaceabc...003
[layer 3/5: entity]
Items found: 47
Bundles: 1
Bundle CIDs: bafy2bzaceabc...004
[layer 4/5: contextual]
Items found: 412
Bundles: 1
Bundle CIDs: bafy2bzaceabc...005
[layer 5/5: task-output]
Items found: 892
Bundles: 1
Bundle CIDs: bafy2bzaceabc...006
Summary:
Total observation claims: 2,551 (STM + contextual + task-output)
Total fact claims: 230 (LTM + entity)
Total claims: 2,781
Total bundles: 6§6 — Caveats
1. Four sub-memories mean four mental models
The split between STM, LTM, entity, and contextual is preserved as metadata.memory_layer and separate tag prefixes. When querying OME after migration, you need to know which sub-memory your query targets. Train ops teams who will be querying.
2. LTM quality scores have no effect on OME retrieval
CrewAI's LTM has a quality_score (used by CrewAI to rank which long-term memories to surface). It is preserved in metadata.quality_score, but OME does not use a quality-score signal. If your retrieval needs LTM-quality-weighted results, filter on metadata.quality_score in your query.
3. Entity external_id namespacing
Entity external_id is prefixed with entity:<crew-name>: (see §3). OME queries against the original entity name will not match directly — query external_id = "entity:<crew>:<name>" instead. The runtime adapter applies the same prefix, so writes and reads are symmetric.
4. Contextual memory volatility
Contextual memory in CrewAI is generally short-lived and high-volume. Migrating it produces many per-step context claims that may not be useful long-term. Consider --sub-memories long-term,entity,task-output for most migrations and skip contextual unless you have a specific reason.
5. Task-output dual representation
CrewAI's TaskOutput has both raw (string) and pydantic/json_dict (structured). The migration stores raw as claim.body and pydantic (if present) as claim.metadata.structured_output. Querying OME by structured-output fields is not supported — only claim.body is indexed. If you rely on structured output, plan to re-derive it at retrieval time.
6. Sequencing, idempotency, GDPR, adapter signer custody
These behave identically to LangGraph — see the LangGraph caveats.
§7 — Round-trip verification
The protocol is the same as the LangGraph verification protocol, with these substitutions:
- LangGraph thread ID → CrewAI run ID (or cross-run for LTM and entity).
state.messages→ CrewAI per-sub-memory item lists.- Per-message claim count: one per sub-memory item; task outputs are one per output (no expansion).
CrewAI-specific checks
- For each sub-memory type, verify the expected
metadata.memory_layervalue. - For entity memory, verify
external_idhas theentity:<crew>:prefix. - For task outputs, verify
metadata.structured_outputround-trips for items that hadpydanticsource content.
Acceptance criterion
At least 99.5% of sampled sub-memory items round-trip with an exact body-text match.
§8 — Rollback
The structure is identical to the LangGraph rollback: adapter-disable, then selective claim-revoke, then back-fill rollback.
The CrewAI adapter is disabled by removing OsyraStorage from the crew's memory_config and re-deploying. CrewAI's default in-memory or ChromaDB-backed storage takes back over; the inner_storage (if you passed one for dual-write) remains active.
Per-sub-memory rollback
To roll back only one sub-memory (for example, STM was migrated incorrectly but LTM was fine), tombstone by tag prefix:
osyra ome tombstone-by-query \
--workspace ws_abc123 \
--tag-prefix "crewai:research-crew:run:<run-id>:layer:short-term" \
--reason rollback_stm_only \
--reason-detail "STM mapping had a bug; re-running migration with a fixed mapping"This leaves LTM, entity, contextual, and task-output claims intact.
§9 — Troubleshooting
1. Which sub-memory is this claim from?
- Symptom: a post-migration query returns a claim and you are not sure which CrewAI sub-memory it came from.
- Likely cause: the query did not filter on
metadata.memory_layer. - Fix: always filter on
metadata.memory_layerwhen querying CrewAI-migrated content. The five values areshort-term,long-term,entity,contextual, andtask-output.
2. Entity claim not found by entity name
- Symptom: you query OME by
external_id = "Salesforce"(the original entity name) and get no results. - Likely cause: the external ID is namespaced (
entity:<crew>:Salesforce), per §3. - Diagnostic:
osyra ome list-by-tag --tag "crewai:research-crew:layer:entity"to see the actual external_id format. - Fix: update the query to use the namespaced external ID.
3. Long-term memory claim count higher than expected
- Symptom: the dry-run reports more LTM items than CrewAI's
LongTermMemoryItemcount. - Likely cause: CrewAI may have multiple LTM databases per crew (one per agent role); the extractor traverses all of them.
- Diagnostic:
ls ~/.crewai/memory/<crew-name>/and count.dbfiles. - Fix: this is expected behavior — the total is the union of all per-agent LTM databases.
4. Structured output not queryable after migration
- Symptom: you want to retrieve task outputs by a structured field (for example
output.confidence > 0.8) and cannot. - Likely cause: structured output is stored in
metadata.structured_outputbut its fields are not indexed for query (see §6 caveat 5). - Fix: retrieve by
claim.body(the raw output), parse client-side, and filter.
5. Signer DID not authorized, or adapter buffer growing
Both surface through the adapter via the same OSY-AUTH-1071 path. See the LangGraph adapter-buffer troubleshooting and the LangGraph signer-DID troubleshooting. Differentiate them with osyra workspace metrics ws_<id> | grep adapter_buffer_depth.