rcrewai 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -1
- data/README.md +108 -1
- data/ROADMAP.md +15 -10
- data/docs/superpowers/specs/2026-07-06-cognitive-memory-design.md +118 -0
- data/docs/upgrading-to-0.4.md +191 -0
- data/docs/upgrading-to-0.6.md +101 -0
- data/examples/cognitive_memory_example.rb +66 -0
- data/examples/flow_example.rb +89 -0
- data/examples/knowledge_rag_example.rb +72 -0
- data/examples/planning_and_training_example.rb +72 -0
- data/examples/structured_output_example.rb +92 -0
- data/lib/rcrewai/agent.rb +50 -7
- data/lib/rcrewai/agent_augmentations.rb +75 -0
- data/lib/rcrewai/context_window.rb +75 -0
- data/lib/rcrewai/crew.rb +45 -7
- data/lib/rcrewai/knowledge/store.rb +4 -14
- data/lib/rcrewai/legacy_react_runner.rb +7 -1
- data/lib/rcrewai/memory/base_memory.rb +107 -0
- data/lib/rcrewai/memory/entity_memory.rb +47 -0
- data/lib/rcrewai/memory/in_memory_store.rb +40 -0
- data/lib/rcrewai/memory/long_term_memory.rb +39 -0
- data/lib/rcrewai/memory/short_term_memory.rb +20 -0
- data/lib/rcrewai/memory/sqlite_store.rb +99 -0
- data/lib/rcrewai/memory/tool_memory.rb +38 -0
- data/lib/rcrewai/memory.rb +57 -160
- data/lib/rcrewai/multimodal.rb +67 -0
- data/lib/rcrewai/rate_limiter.rb +94 -0
- data/lib/rcrewai/similarity.rb +43 -0
- data/lib/rcrewai/task.rb +2 -1
- data/lib/rcrewai/tool_runner.rb +7 -1
- data/lib/rcrewai/version.rb +1 -1
- data/lib/rcrewai.rb +11 -0
- data/rcrewai.gemspec +1 -0
- metadata +35 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb90e26000ddf4a2907b06150fdc87b1707fc701c6c61ba35db33339ed6f38d4
|
|
4
|
+
data.tar.gz: ba3435eb1918be328ce65faf2c3000553833b09778eb3b06f2a6191cae31aab4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 992ac3d7368cb7690f11294b78fe876fcea29d48cffd001a355471a351c22c321563fbd390a0688951be6bcbd0f177322509e59cdd19cf8073e228784d96b503
|
|
7
|
+
data.tar.gz: 92e7db7609d4503693396f85735fd48633cb5204a34d2431e57ffcb7c758e693bff5b1282ed716899f4242bd9a26b0483f278c991dedb13b21d05894a59b7105
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2026-07-06
|
|
11
|
+
|
|
12
|
+
Replaces the placeholder agent memory with a **cognitive memory** system —
|
|
13
|
+
semantic recall (embeddings + cosine), optional SQLite persistence, and four
|
|
14
|
+
memory types. This is the one area where CrewAI advanced past what the gem
|
|
15
|
+
originally ported. Backward compatible: the `Memory` public API is unchanged and
|
|
16
|
+
the zero-config default behaves as before.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Cognitive memory: `RCrewAI::Memory` is now a semantic, optionally-persistent, multi-type memory system, replacing the previous word-overlap placeholder. It composes four memory types — `ShortTermMemory` (recent executions, capped), `LongTermMemory` (durable, deduped insights), `EntityMemory` (facts about entities seen in work), and `ToolMemory` (tool-call history) — behind the original `Memory` public API.
|
|
20
|
+
- Semantic recall: pass `Agent.new(memory: { embedder: RCrewAI::Knowledge::Embedder.new })` to recall conceptually related past work via embeddings + cosine similarity. Without an embedder, recall falls back to lexical (word-overlap) similarity — and embedding failures fall back gracefully, so memory never breaks execution.
|
|
21
|
+
- Persistent memory: `RCrewAI::Memory::SqliteStore` persists memories to SQLite (vectors packed as floats, metadata as JSON) so recall survives restarts. Default store is `InMemoryStore` (volatile). The store interface is pluggable.
|
|
22
|
+
- Memory scoping: each agent's memory is scoped to its name by default, so agents sharing a persistent store don't cross-read; override via `memory: { scope: ... }`.
|
|
23
|
+
- `RCrewAI::Similarity` — shared cosine + lexical similarity helpers (extracted from `Knowledge::Store`, now used by both Knowledge and Memory).
|
|
24
|
+
- `sqlite3` added as a runtime dependency (required lazily; only needed for `SqliteStore`).
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- `Agent.new(memory:)` accepts a pre-built `Memory`, an options hash (`{ embedder:, store:, scope:, short_term_limit: }`), or nothing (zero-config default, unchanged behavior).
|
|
28
|
+
|
|
29
|
+
## [0.5.0] - 2026-07-03
|
|
30
|
+
|
|
31
|
+
Polish release completing the roadmap backlog: crew lifecycle hooks, batch
|
|
32
|
+
execution, rate limiting, per-agent reasoning, context-window management, and
|
|
33
|
+
multimodal image input. All additive — existing code runs unchanged.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Crew lifecycle hooks: `Crew#before_kickoff` and `Crew#after_kickoff` register callbacks that run before/after execution. A `before_kickoff` hook receives the inputs hash (passed via `crew.execute(inputs:)`) and may transform it; an `after_kickoff` hook receives the result and may transform it. Multiple hooks run in registration order. The (possibly transformed) inputs are exposed on `Crew#last_inputs`. (#15)
|
|
37
|
+
- `Crew#kickoff_for_each(inputs:)` runs the crew once per input set and returns one result per input, in order. Runs are isolated — each execution starts from only its own inputs. (#16)
|
|
38
|
+
- Rate limiting: `Agent.new(max_rpm:)` throttles the agent's LLM calls to the given requests-per-minute using a thread-safe rolling-window `RCrewAI::RateLimiter`. The agent's client is transparently wrapped (`RateLimiter::ThrottledClient`) so every `chat` acquires a slot first; `max_rpm` nil/0 means unlimited. (#17)
|
|
39
|
+
- Reasoning: `Agent.new(reasoning: true)` runs a reasoning/planning pass before answering — the LLM drafts a short plan for the task, which is injected into the answer prompt and surfaced on the result hash as `:reasoning` (without polluting `task.result`). Bounded by `max_reasoning_attempts` (default 3), retrying if the model returns empty output; if every attempt is empty, execution proceeds without a plan. Off by default. (#18)
|
|
40
|
+
- Context-window management: `Agent.new(respect_context_window: true)` trims the message history to fit the model's context window before each LLM call, dropping the oldest non-system messages while always keeping system messages and the latest message. The new `RCrewAI::ContextWindow` module provides the token estimate (chars/4 heuristic), a per-model window-size table, and the `fit` trimmer. Off by default. (#19)
|
|
41
|
+
- Multimodal input: `Task.new(attachments:)` accepts image attachments (`{ type: :image, url: '...' }` or `{ type: :image, path: '...' }`). When a task has attachments, the agent builds an OpenAI-style multimodal user message (text + `image_url` parts); local files are base64-encoded into data URLs with a mime type inferred from the extension. Supported on OpenAI/Azure; other providers raise a clear `Multimodal::UnsupportedProviderError`. The new `RCrewAI::Multimodal` module builds the content parts. (#20)
|
|
42
|
+
|
|
10
43
|
## [0.4.0] - 2026-07-03
|
|
11
44
|
|
|
12
45
|
This release closes the feature-parity gap with the modern CrewAI framework,
|
|
@@ -165,7 +198,9 @@ output, guardrails, planning, and training/testing. See `ROADMAP.md`.
|
|
|
165
198
|
- CLI usage documentation
|
|
166
199
|
- Real-world use cases and examples
|
|
167
200
|
|
|
168
|
-
[Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.
|
|
201
|
+
[Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.6.0...HEAD
|
|
202
|
+
[0.6.0]: https://github.com/gkosmo/rcrewAI/compare/v0.5.0...v0.6.0
|
|
203
|
+
[0.5.0]: https://github.com/gkosmo/rcrewAI/compare/v0.4.0...v0.5.0
|
|
169
204
|
[0.4.0]: https://github.com/gkosmo/rcrewAI/compare/v0.3.0...v0.4.0
|
|
170
205
|
[0.3.0]: https://github.com/gkosmo/rcrewAI/compare/v0.1.0...v0.3.0
|
|
171
206
|
[0.1.0]: https://github.com/gkosmo/rcrewAI/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ RCrewAI is a Ruby implementation of the CrewAI framework, allowing you to create
|
|
|
13
13
|
- **🤖 Intelligent Agents**: AI agents with reasoning loops, memory, and tool usage capabilities
|
|
14
14
|
- **🔗 Multi-LLM Support**: OpenAI, Anthropic (Claude), Google (Gemini), Azure OpenAI, and Ollama
|
|
15
15
|
- **🛠️ Rich Tool Ecosystem**: Web search, file operations, SQL, email, code execution, PDF processing, and custom tools
|
|
16
|
-
- **🧠
|
|
16
|
+
- **🧠 Cognitive Memory**: Semantic recall (embeddings + cosine) with optional SQLite persistence and short-term/long-term/entity/tool memory types
|
|
17
17
|
- **🤝 Human-in-the-Loop**: Interactive approval workflows, human guidance, and collaborative decision making
|
|
18
18
|
- **⚡ Advanced Task System**: Dependencies, retries, async/concurrent execution, and context sharing
|
|
19
19
|
- **🏗️ Hierarchical Teams**: Manager agents that coordinate and delegate tasks to specialist agents
|
|
@@ -263,6 +263,88 @@ crew.test(n_iterations: 5)
|
|
|
263
263
|
# => { iterations: 5, scores: [...], average_score: 92.0 }
|
|
264
264
|
```
|
|
265
265
|
|
|
266
|
+
## 🪝 Kickoff Hooks & Batch Runs
|
|
267
|
+
|
|
268
|
+
Run setup/teardown around a crew, and batch it over many inputs:
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
crew.before_kickoff { |inputs| inputs.merge(started_at: Time.now) } # may transform inputs
|
|
272
|
+
crew.after_kickoff { |result| notify(result); result } # may transform result
|
|
273
|
+
|
|
274
|
+
crew.execute(inputs: { topic: 'ruby' })
|
|
275
|
+
crew.last_inputs # => the (possibly transformed) inputs the run used
|
|
276
|
+
|
|
277
|
+
# Batch: run the crew once per input set, results returned in order.
|
|
278
|
+
results = crew.kickoff_for_each(inputs: [
|
|
279
|
+
{ topic: 'ruby' },
|
|
280
|
+
{ topic: 'python' }
|
|
281
|
+
])
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## ⏱️ Rate Limiting
|
|
285
|
+
|
|
286
|
+
Cap an agent's LLM calls to stay under provider limits. Calls beyond the cap
|
|
287
|
+
block until the rolling 60-second window frees up:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
agent = RCrewAI::Agent.new(name: 'a', role: '...', goal: '...', max_rpm: 20)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The limiter (`RCrewAI::RateLimiter`) is thread-safe, so it holds under async
|
|
294
|
+
execution. `max_rpm: nil` (the default) or `0` means unlimited.
|
|
295
|
+
|
|
296
|
+
## 🧠 Reasoning
|
|
297
|
+
|
|
298
|
+
Have an agent think through a plan before answering. The reasoning trace is
|
|
299
|
+
surfaced on the result and does not pollute `task.result`:
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
agent = RCrewAI::Agent.new(name: 'a', role: '...', goal: '...',
|
|
303
|
+
reasoning: true, max_reasoning_attempts: 3)
|
|
304
|
+
|
|
305
|
+
result = agent.execute_task(task)
|
|
306
|
+
result[:reasoning] # => the plan the agent drafted before answering
|
|
307
|
+
result[:content] # => the final answer
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Off by default. If the reasoning pass keeps returning empty output past
|
|
311
|
+
`max_reasoning_attempts`, the agent proceeds without a plan.
|
|
312
|
+
|
|
313
|
+
## 🪟 Context Window Management
|
|
314
|
+
|
|
315
|
+
Keep long tool-use loops or large injected context from overflowing the model's
|
|
316
|
+
context window. When enabled, the oldest non-system messages are dropped to fit
|
|
317
|
+
before each LLM call (system messages and the latest message are always kept):
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
agent = RCrewAI::Agent.new(name: 'a', role: '...', goal: '...',
|
|
321
|
+
respect_context_window: true)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Window sizes come from `RCrewAI::ContextWindow` (with a conservative default for
|
|
325
|
+
unknown models); headroom for the response is reserved from `max_tokens`. Off by
|
|
326
|
+
default.
|
|
327
|
+
|
|
328
|
+
## 🖼️ Multimodal Input
|
|
329
|
+
|
|
330
|
+
Pass images to a vision-capable model via task attachments. Local files are
|
|
331
|
+
base64-encoded automatically; URLs pass through:
|
|
332
|
+
|
|
333
|
+
```ruby
|
|
334
|
+
RCrewAI.configure { |c| c.llm_provider = :openai; c.openai_model = 'gpt-4o' }
|
|
335
|
+
|
|
336
|
+
task = RCrewAI::Task.new(
|
|
337
|
+
name: 'describe', description: 'What is in this chart?', agent: agent,
|
|
338
|
+
attachments: [
|
|
339
|
+
{ type: :image, path: 'chart.png' },
|
|
340
|
+
{ type: :image, url: 'https://example.com/photo.jpg' }
|
|
341
|
+
]
|
|
342
|
+
)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Supported on OpenAI and Azure; other providers raise a clear error when
|
|
346
|
+
attachments are present.
|
|
347
|
+
|
|
266
348
|
## 📚 Knowledge (RAG)
|
|
267
349
|
|
|
268
350
|
Ground agents in your own documents. Sources are chunked, embedded, and stored
|
|
@@ -292,6 +374,31 @@ Embeddings default to OpenAI's `text-embedding-3-small`; pass a custom
|
|
|
292
374
|
`embedder:` (anything responding to `embed(texts)`) or vector store to swap the
|
|
293
375
|
backend.
|
|
294
376
|
|
|
377
|
+
## 🧠 Cognitive Memory
|
|
378
|
+
|
|
379
|
+
Agents remember what they've done and recall it semantically on future tasks.
|
|
380
|
+
Memory is zero-config by default (in-memory, lexical recall); add an embedder
|
|
381
|
+
for semantic recall and a SQLite store for persistence:
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
embedder = RCrewAI::Knowledge::Embedder.new
|
|
385
|
+
store = RCrewAI::Memory::SqliteStore.new(path: '~/.rcrewai/memory.db')
|
|
386
|
+
|
|
387
|
+
agent = RCrewAI::Agent.new(
|
|
388
|
+
name: 'engineer', role: '...', goal: '...',
|
|
389
|
+
memory: { embedder: embedder, store: store } # both optional
|
|
390
|
+
)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- **Semantic recall** — with an embedder, an agent recalls conceptually related
|
|
394
|
+
past work even when the wording differs (falls back to word-overlap without one).
|
|
395
|
+
- **Persistence** — a `SqliteStore` makes memory survive restarts.
|
|
396
|
+
- **Memory types** — `agent.memory.short_term` / `long_term` / `entity` / `tool`.
|
|
397
|
+
- **Scoping** — memory is scoped per agent so a shared store doesn't cross-read.
|
|
398
|
+
|
|
399
|
+
Memory is best-effort: embedding failures fall back to lexical similarity, so it
|
|
400
|
+
never breaks agent execution.
|
|
401
|
+
|
|
295
402
|
## 🌊 Flows
|
|
296
403
|
|
|
297
404
|
Beyond crews, RCrewAI has **Flows** — an event-driven workflow engine for
|
data/ROADMAP.md
CHANGED
|
@@ -16,12 +16,10 @@ pricing.
|
|
|
16
16
|
|
|
17
17
|
Since CrewAI's `1.0`, the framework grew a second pillar (**Flows**) plus
|
|
18
18
|
**Knowledge (RAG)**, **Guardrails**, **structured output**, **Planning**, and
|
|
19
|
-
**Training/Testing**.
|
|
20
|
-
all of these — see the matrix below. Only backlog polish items remain.
|
|
19
|
+
**Training/Testing**. RCrewAI now implements all of these — see the matrix below.
|
|
21
20
|
|
|
22
|
-
**Status:
|
|
23
|
-
|
|
24
|
-
hooks, multimodal).
|
|
21
|
+
**Status: complete.** All milestone issues (#5–#12) shipped in v0.4.0, and all
|
|
22
|
+
backlog items (#15–#20) shipped in v0.5.0. There is no outstanding roadmap work.
|
|
25
23
|
|
|
26
24
|
## Parity matrix
|
|
27
25
|
|
|
@@ -42,7 +40,7 @@ hooks, multimodal).
|
|
|
42
40
|
| Flows (`start`/`listen`/`router`) | ✅ | ✅ (#11) | ✅ done |
|
|
43
41
|
| Flow state + persistence | ✅ | ✅ (#11) | ✅ done |
|
|
44
42
|
| Training / Testing | ✅ | ✅ (#12) | ✅ done |
|
|
45
|
-
| Reasoning, rate-limiting, batch kickoff | ✅ |
|
|
43
|
+
| Reasoning, rate-limiting, batch kickoff, hooks, context window, multimodal | ✅ | ✅ (#15–#20) | ✅ done |
|
|
46
44
|
|
|
47
45
|
## Milestones (highest leverage first)
|
|
48
46
|
|
|
@@ -78,7 +76,14 @@ The flagship. A Ruby DSL mirroring CrewAI Flows:
|
|
|
78
76
|
- `crew.train(n_iterations:, filename:)` capturing human feedback.
|
|
79
77
|
- `crew.test(n_iterations:, model:)` scoring runs.
|
|
80
78
|
|
|
81
|
-
### Backlog
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
### Backlog — ✅ all complete
|
|
80
|
+
|
|
81
|
+
Formerly polish items with no set version; all shipped in the `[Unreleased]`
|
|
82
|
+
changes (see CHANGELOG):
|
|
83
|
+
|
|
84
|
+
- [#15](https://github.com/gkosmo/rcrewAI/issues/15) — `before_kickoff` / `after_kickoff` lifecycle hooks ✅
|
|
85
|
+
- [#16](https://github.com/gkosmo/rcrewAI/issues/16) — `kickoff_for_each` batch execution ✅
|
|
86
|
+
- [#17](https://github.com/gkosmo/rcrewAI/issues/17) — `max_rpm` rate limiting ✅
|
|
87
|
+
- [#18](https://github.com/gkosmo/rcrewAI/issues/18) — per-agent reasoning (`reasoning:`, `max_reasoning_attempts:`) ✅
|
|
88
|
+
- [#19](https://github.com/gkosmo/rcrewAI/issues/19) — `respect_context_window` history trimming ✅
|
|
89
|
+
- [#20](https://github.com/gkosmo/rcrewAI/issues/20) — multimodal agents (image/file inputs) ✅
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Cognitive Memory: Semantic, Persistent, Multi-Type Agent Memory
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-07-06
|
|
4
|
+
**Status:** Approved design (pending implementation)
|
|
5
|
+
**Target version:** `rcrewai` 0.6.0 (current is 0.5.0)
|
|
6
|
+
**Scope:** Replace the placeholder `RCrewAI::Memory` with a cognitive memory system modeled on CrewAI's memory taxonomy — short-term (semantic recent recall), long-term (persistent insights), entity memory, and tool-usage memory — backed by embeddings and a persistent SQLite vector store. No new external dependencies; reuses the in-repo `Knowledge::Embedder` and cosine-search infrastructure.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Motivation
|
|
11
|
+
|
|
12
|
+
The current `RCrewAI::Memory` is a placeholder. It matches "relevant" past executions with word-overlap similarity (`common_words / total_words`) plus a hardcoded keyword/stopword list and a `classify_task_type` that greps the description for words like "research" or "write". Nothing is embedded; nothing is persisted; everything is lost when the process exits. It is the weakest module in the codebase and it undercuts the framework's core promise — agents that learn from what they've done.
|
|
13
|
+
|
|
14
|
+
Meanwhile CrewAI has advanced *past* the version this project ported: its 2026 "cognitive memory" work adds semantic recall, distinct memory types (short-term, long-term, entity), and persistence. This is genuine parity work, not gold-plating — and the infrastructure to do it well already exists in-repo (`Knowledge::Embedder`, cosine `Knowledge::Store`), so we compose rather than build from scratch.
|
|
15
|
+
|
|
16
|
+
## Goals
|
|
17
|
+
|
|
18
|
+
1. **Semantic recall.** Replace word-overlap with embedding-based similarity so an agent recalls conceptually related past work, not just keyword-overlapping work.
|
|
19
|
+
2. **Persistence.** Memory survives process restarts via a SQLite-backed vector store (no external service).
|
|
20
|
+
3. **Memory types.** Model CrewAI's taxonomy: short-term, long-term, entity, and tool-usage memory, each with clear write/read semantics.
|
|
21
|
+
4. **Backward compatibility.** The existing `Memory` public API (`add_execution`, `add_tool_usage`, `relevant_executions`, `tool_usage_for`, `clear_*!`, `stats`) keeps working, so `Agent` and the runners need no changes to function.
|
|
22
|
+
5. **No new hard dependencies.** SQLite via Ruby's stdlib-adjacent `sqlite3` gem (already common in Ruby projects) — but the store is pluggable, and the default remains in-memory so the gem works with zero setup.
|
|
23
|
+
|
|
24
|
+
## Non-goals
|
|
25
|
+
|
|
26
|
+
- External vector databases (Chroma/Qdrant/pgvector). The pluggable store interface leaves room for them later; we ship SQLite + in-memory.
|
|
27
|
+
- Reranking, query rewriting, or summarization of memories (CrewAI has some of this; it's a follow-up).
|
|
28
|
+
- Multi-tenant / shared cross-agent memory servers.
|
|
29
|
+
|
|
30
|
+
## Decisions captured during brainstorming
|
|
31
|
+
|
|
32
|
+
| # | Decision | Choice |
|
|
33
|
+
|---|----------|--------|
|
|
34
|
+
| 1 | Similarity mechanism | Embeddings + cosine, reusing `Knowledge::Embedder` and the cosine math already in `Knowledge::Store`. |
|
|
35
|
+
| 2 | Persistence | Pluggable vector store; ship `InMemoryStore` (default) and `SqliteStore`. |
|
|
36
|
+
| 3 | Memory taxonomy | Four types: `ShortTermMemory`, `LongTermMemory`, `EntityMemory`, `ToolMemory`. |
|
|
37
|
+
| 4 | Backward compat | Keep the `Memory` facade and its current method signatures; delegate internally to the new types. |
|
|
38
|
+
| 5 | Default behavior | Zero-config: in-memory store, embeddings only if an embedder is available/keyed; graceful fallback to the current lexical similarity when no embedder. |
|
|
39
|
+
| 6 | Embedding failures | Never fatal. If embedding fails (no key, network), fall back to lexical similarity so agents still run. |
|
|
40
|
+
| 7 | Namespacing | Memories are scoped by agent (role/name) so one agent's memory doesn't leak into another, matching CrewAI's per-role collections. |
|
|
41
|
+
|
|
42
|
+
## Architecture
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
RCrewAI::Memory (facade — preserves today's public API)
|
|
46
|
+
├── ShortTermMemory recent executions, semantic recall, capped, volatile-by-default
|
|
47
|
+
├── LongTermMemory durable insights from successful executions, persistent
|
|
48
|
+
├── EntityMemory facts about entities (people, systems, concepts) extracted from work
|
|
49
|
+
└── ToolMemory tool-call history + outcomes (replaces @tool_usage)
|
|
50
|
+
|
|
51
|
+
Each memory type holds:
|
|
52
|
+
- a VectorStore (InMemoryStore | SqliteStore) ← persistence + search
|
|
53
|
+
- an Embedder (Knowledge::Embedder | nil) ← semantic vectors
|
|
54
|
+
- a lexical fallback (current word-overlap) ← when no embedder
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Storage: `Memory::VectorStore`
|
|
58
|
+
|
|
59
|
+
A small interface (mirrors `Knowledge::Store` so they can converge later):
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
store.add(id:, text:, vector:, metadata:) # upsert a record
|
|
63
|
+
store.search(vector, k:, scope:) # cosine top-k, filtered by scope
|
|
64
|
+
store.all(scope:) # enumerate (for stats / lexical fallback)
|
|
65
|
+
store.delete(scope:) # clear a scope
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **`InMemoryStore`** — array of records; cosine in Ruby (lift the existing private cosine out of `Knowledge::Store` into a shared `Similarity` helper). Default.
|
|
69
|
+
- **`SqliteStore`** — one row per memory: `id, scope, type, text, vector (blob), metadata (json), created_at`. Vectors stored as packed floats (`Array#pack('e*')`). Cosine computed in Ruby over candidate rows filtered by `scope`/`type` (fine for the thousands-of-memories scale agents produce; a real ANN index is a later optimization). Opened at a configurable path (default `~/.rcrewai/memory.db` or `:memory:`).
|
|
70
|
+
|
|
71
|
+
### Embeddings
|
|
72
|
+
|
|
73
|
+
Reuse `Knowledge::Embedder`. The memory system takes an optional `embedder:`; when present, `add_*` embeds the text and stores the vector, and recall embeds the query. When absent (no key, or explicitly disabled), the store keeps `vector: nil` and recall falls back to the current lexical similarity over `store.all`. This keeps the zero-config path working.
|
|
74
|
+
|
|
75
|
+
### The four memory types
|
|
76
|
+
|
|
77
|
+
- **ShortTermMemory** — every execution is written here; recall returns the top-k semantically similar recent items. Capped (default 100) and, by default, uses the in-memory store (volatile). This is the direct upgrade of today's `@short_term` + `relevant_executions`.
|
|
78
|
+
- **LongTermMemory** — successful executions promote a distilled "insight" record (task type, what worked, result summary) into a persistent store, deduped by similarity so we don't store near-identical insights repeatedly. Upgrade of today's `@long_term`.
|
|
79
|
+
- **EntityMemory** — optional lightweight entity extraction (heuristic first: capitalized noun phrases / quoted terms; pluggable LLM extractor later) so agents accumulate facts about recurring entities. New capability.
|
|
80
|
+
- **ToolMemory** — replaces `@tool_usage`; same API (`add_tool_usage`, `tool_usage_for`) but persistable and searchable.
|
|
81
|
+
|
|
82
|
+
### Facade: `RCrewAI::Memory`
|
|
83
|
+
|
|
84
|
+
Keeps today's signatures, delegating to the types:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
Memory.new(embedder: nil, store: nil, scope: nil, short_term_limit: 100)
|
|
88
|
+
|
|
89
|
+
add_execution(task, result, execution_time) # -> ShortTerm + (if success) LongTerm + Entity
|
|
90
|
+
add_tool_usage(tool_name, params, result) # -> ToolMemory
|
|
91
|
+
relevant_executions(task, limit = 3) # -> semantic recall across Short+Long, formatted string|nil
|
|
92
|
+
tool_usage_for(tool_name, limit = 5) # -> ToolMemory
|
|
93
|
+
clear_short_term! / clear_all! / stats # -> delegate
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`Agent#initialize` gains optional `memory:` config (embedder/store/persistence) but defaults to the zero-config in-memory, lexical-fallback behavior — so existing code is unchanged.
|
|
97
|
+
|
|
98
|
+
## Backward compatibility & migration
|
|
99
|
+
|
|
100
|
+
- The `Memory` public API is preserved exactly; `agent.rb` and the runners are untouched functionally.
|
|
101
|
+
- Default construction (`Memory.new` with no args) behaves like today from the caller's perspective — just with better recall when an embedder is configured, and identical lexical recall when not.
|
|
102
|
+
- `relevant_executions` still returns the same formatted-string-or-nil shape consumed at `agent.rb:241`.
|
|
103
|
+
|
|
104
|
+
## Testing strategy
|
|
105
|
+
|
|
106
|
+
- **Unit**: each memory type with a fake deterministic embedder (as `knowledge_spec.rb` already does) — assert semantic recall ranks the conceptually-closest item first, not the keyword-overlapping one.
|
|
107
|
+
- **Persistence**: `SqliteStore` round-trip — write memories, reopen the DB, recall survives.
|
|
108
|
+
- **Fallback**: with no embedder, recall still returns results via lexical similarity (proves the graceful path).
|
|
109
|
+
- **Facade compat**: the existing `memory_spec.rb` expectations continue to pass (or are updated only where the placeholder behavior was clearly a bug).
|
|
110
|
+
- **Agent integration**: an agent with `reasoning`/knowledge off recalls a semantically-related prior execution injected into its context.
|
|
111
|
+
|
|
112
|
+
## Dependencies
|
|
113
|
+
|
|
114
|
+
- `sqlite3` gem — added as a runtime dependency, but only required lazily inside `SqliteStore` so the gem loads and the in-memory path works even if it's absent. (If we'd rather avoid the dependency entirely, the fallback is a JSON-file-backed store; SQLite is preferred for query/scale.)
|
|
115
|
+
|
|
116
|
+
## Rollout
|
|
117
|
+
|
|
118
|
+
Ship in `0.6.0`. `docs/upgrading-to-0.6.md` documents opt-in persistence and embeddings; the default path is unchanged, so it's a no-action upgrade for existing users.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Upgrading to RCrewAI 0.4
|
|
2
|
+
|
|
3
|
+
RCrewAI 0.4 closes the feature-parity gap with the modern CrewAI framework. It
|
|
4
|
+
adds CrewAI's second pillar — **Flows** — alongside **Knowledge (RAG)**,
|
|
5
|
+
structured task output, guardrails, planning, and training/testing.
|
|
6
|
+
|
|
7
|
+
**0.4 is fully backward compatible.** There are no breaking changes: existing
|
|
8
|
+
0.3 code runs unchanged. Everything below is opt-in.
|
|
9
|
+
|
|
10
|
+
Each new capability has a runnable example under `examples/` — see the links
|
|
11
|
+
throughout.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. What you must do
|
|
16
|
+
|
|
17
|
+
Nothing. Upgrade the gem and your existing code keeps working:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'rcrewai', '~> 0.4'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 2. What you can do (new capabilities)
|
|
26
|
+
|
|
27
|
+
### 2a. Per-agent LLM
|
|
28
|
+
|
|
29
|
+
Give each agent its own provider/model instead of only the global default.
|
|
30
|
+
Pass a provider symbol, an options hash, or a pre-built client:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
worker = RCrewAI::Agent.new(name: 'worker', role: '...', goal: '...',
|
|
34
|
+
llm: { provider: :openai, model: 'gpt-4o-mini' })
|
|
35
|
+
manager = RCrewAI::Agent.new(name: 'manager', role: '...', goal: '...',
|
|
36
|
+
llm: { provider: :anthropic, model: 'claude-3-opus-20240229' })
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Omitting `llm:` keeps the global `RCrewAI.configure` behavior. Overrides never
|
|
40
|
+
mutate the global configuration.
|
|
41
|
+
|
|
42
|
+
### 2b. Structured output, guardrails, and file output
|
|
43
|
+
|
|
44
|
+
Post-process a task's result after the agent produces it:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
task = RCrewAI::Task.new(
|
|
48
|
+
name: 'extract', description: '...', agent: agent,
|
|
49
|
+
|
|
50
|
+
# Validate & coerce against a JSON schema. Non-conforming output re-runs the
|
|
51
|
+
# agent with the error fed back. Parsed object on task.structured_output.
|
|
52
|
+
output_schema: { type: 'object', properties: { title: { type: 'string' } },
|
|
53
|
+
required: ['title'] },
|
|
54
|
+
|
|
55
|
+
# Validate & transform. ->(output) { [ok, value_or_error] }. Retries up to
|
|
56
|
+
# guardrail_max_retries (default 3) with the reason fed back to the agent.
|
|
57
|
+
guardrail: ->(out) { [out.length < 5000, 'too long'] },
|
|
58
|
+
|
|
59
|
+
# Persist the result (parent dirs created unless create_directory: false).
|
|
60
|
+
output_file: 'out/report.md', markdown: true
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
task.execute
|
|
64
|
+
task.structured_output # => { "title" => "..." }
|
|
65
|
+
task.raw_result # => the unprocessed string the agent produced
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
See `examples/structured_output_example.rb`.
|
|
69
|
+
|
|
70
|
+
### 2c. Knowledge (RAG)
|
|
71
|
+
|
|
72
|
+
Ground agents in your own documents. Sources are chunked, embedded, and stored
|
|
73
|
+
in an in-memory cosine vector store; relevant chunks are injected into each
|
|
74
|
+
task's prompt automatically.
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
kb = RCrewAI::Knowledge::Base.new(sources: [
|
|
78
|
+
RCrewAI::Knowledge::StringSource.new('Refunds within 30 days.'),
|
|
79
|
+
RCrewAI::Knowledge::FileSource.new('docs/policy.txt'),
|
|
80
|
+
RCrewAI::Knowledge::PdfSource.new('handbook.pdf'),
|
|
81
|
+
RCrewAI::Knowledge::UrlSource.new('https://example.com/faq')
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
# Agent-level (role-specific):
|
|
85
|
+
agent = RCrewAI::Agent.new(name: 'support', role: '...', goal: '...', knowledge: kb)
|
|
86
|
+
|
|
87
|
+
# Or pass raw sources and let the agent build the base:
|
|
88
|
+
agent = RCrewAI::Agent.new(name: 'support', role: '...', goal: '...',
|
|
89
|
+
knowledge_sources: [RCrewAI::Knowledge::StringSource.new('...')])
|
|
90
|
+
|
|
91
|
+
# Crew-level knowledge is shared with every agent:
|
|
92
|
+
crew = RCrewAI::Crew.new('support', knowledge: kb)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Embeddings default to OpenAI's `text-embedding-3-small`; pass a custom
|
|
96
|
+
`embedder:` (anything responding to `embed(texts)`) to swap the backend.
|
|
97
|
+
See `examples/knowledge_rag_example.rb`.
|
|
98
|
+
|
|
99
|
+
### 2d. Planning
|
|
100
|
+
|
|
101
|
+
Have a planner pass draft a per-task plan before execution:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
crew = RCrewAI::Crew.new('research_crew', planning: true)
|
|
105
|
+
# Optionally use a dedicated (stronger) planner model:
|
|
106
|
+
crew = RCrewAI::Crew.new('research_crew', planning: true,
|
|
107
|
+
planning_llm: { provider: :anthropic, model: 'claude-3-opus-20240229' })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The plan is folded into each task's description. Planning is best-effort: a
|
|
111
|
+
planner error or unparseable output leaves tasks unchanged.
|
|
112
|
+
|
|
113
|
+
### 2e. Training & testing
|
|
114
|
+
|
|
115
|
+
Iterate on a crew with feedback, or score repeated runs:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# Run N times, collect feedback after each run, persist to JSON.
|
|
119
|
+
crew.train(n_iterations: 3, filename: 'training.json')
|
|
120
|
+
|
|
121
|
+
# Provide feedback programmatically instead of prompting a human:
|
|
122
|
+
crew.train(n_iterations: 3, filename: 'training.json',
|
|
123
|
+
feedback: ->(iteration, result) { "run #{iteration}: #{result[:success_rate]}%" })
|
|
124
|
+
|
|
125
|
+
# Run N times and score each run (defaults to success_rate).
|
|
126
|
+
crew.test(n_iterations: 5)
|
|
127
|
+
# => { iterations: 5, scores: [...], average_score: 92.0 }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
See `examples/planning_and_training_example.rb`.
|
|
131
|
+
|
|
132
|
+
### 2f. Flows
|
|
133
|
+
|
|
134
|
+
Flows are an event-driven workflow engine — the biggest addition in 0.4.
|
|
135
|
+
Subclass `RCrewAI::Flow` and wire methods with a class-level DSL:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
class ArticleFlow < RCrewAI::Flow
|
|
139
|
+
start :outline
|
|
140
|
+
def outline
|
|
141
|
+
state.sections = %w[intro body conclusion]
|
|
142
|
+
state.sections.length
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
listen :outline
|
|
146
|
+
def draft(section_count)
|
|
147
|
+
state.words = section_count * 100
|
|
148
|
+
state.words
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
router :draft
|
|
152
|
+
def review(words)
|
|
153
|
+
words >= 250 ? :publish : :expand
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
listen :publish
|
|
157
|
+
def publish = state.status = 'published'
|
|
158
|
+
|
|
159
|
+
listen :expand
|
|
160
|
+
def expand = state.status = 'needs more work'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
flow = ArticleFlow.new
|
|
164
|
+
flow.kickoff(inputs: { author: 'me' })
|
|
165
|
+
flow.state.status # => "published"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- `start` / `listen` / `router` wire methods into a graph; a listener receives
|
|
169
|
+
its trigger's return value, and a router's return becomes a label listeners
|
|
170
|
+
fire on.
|
|
171
|
+
- Combine triggers with `and_(:a, :b)` (all) and `or_(:a, :b)` (any).
|
|
172
|
+
- **State** is a schemaless object with a UUID, seedable via `kickoff(inputs:)`.
|
|
173
|
+
- **Persistence**: pass `state_store:`
|
|
174
|
+
(`RCrewAI::Flow::FileStateStore.new(dir)` or your own `#save`/`#load`) and
|
|
175
|
+
call `flow.restore(id)` to resume.
|
|
176
|
+
- Invoke a `Crew` inside any step, or pause with `human_feedback('Approve?')`.
|
|
177
|
+
|
|
178
|
+
See `examples/flow_example.rb`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## When to use Flows vs. Crews
|
|
183
|
+
|
|
184
|
+
- **Crew** — a team of agents collaborating on a set of tasks (sequential,
|
|
185
|
+
hierarchical, or async). Reach for a crew when the work is "have these agents
|
|
186
|
+
produce these outputs."
|
|
187
|
+
- **Flow** — explicit, branching orchestration with state. Reach for a flow
|
|
188
|
+
when you need conditional paths, joins, persistence/resumption, or you want to
|
|
189
|
+
coordinate multiple crews and plain Ruby steps.
|
|
190
|
+
|
|
191
|
+
They compose: a Flow step can kick off a Crew.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Upgrading to RCrewAI 0.6
|
|
2
|
+
|
|
3
|
+
RCrewAI 0.6 replaces the placeholder memory with a **cognitive memory** system:
|
|
4
|
+
semantic recall (embeddings + cosine), optional SQLite persistence, and four
|
|
5
|
+
memory types (short-term, long-term, entity, tool).
|
|
6
|
+
|
|
7
|
+
**0.6 is backward compatible.** The `RCrewAI::Memory` public API is unchanged,
|
|
8
|
+
and the default (`Memory.new` with no config) behaves like before from the
|
|
9
|
+
caller's perspective — just with better recall once an embedder is configured.
|
|
10
|
+
Everything below is opt-in.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. What you must do
|
|
15
|
+
|
|
16
|
+
Nothing. Upgrade the gem; existing code keeps working:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
gem 'rcrewai', '~> 0.6'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Note: `0.6` adds `sqlite3` as a runtime dependency (used only if you opt into
|
|
23
|
+
persistence). It is required lazily, so the in-memory default works even if the
|
|
24
|
+
native extension isn't present.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. What you can do (new capabilities)
|
|
29
|
+
|
|
30
|
+
### 2a. Semantic recall via embeddings
|
|
31
|
+
|
|
32
|
+
By default, memory recall uses lexical (word-overlap) similarity — same as
|
|
33
|
+
before. Pass an embedder to get semantic recall, so an agent remembers
|
|
34
|
+
conceptually related work even when the wording differs:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
embedder = RCrewAI::Knowledge::Embedder.new # OpenAI text-embedding-3-small
|
|
38
|
+
agent = RCrewAI::Agent.new(
|
|
39
|
+
name: 'engineer', role: '...', goal: '...',
|
|
40
|
+
memory: { embedder: embedder }
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If embedding fails (no API key, network error), recall automatically falls back
|
|
45
|
+
to lexical similarity — memory never breaks agent execution.
|
|
46
|
+
|
|
47
|
+
### 2b. Persistent memory (SQLite)
|
|
48
|
+
|
|
49
|
+
Give memory a SQLite store and it survives restarts:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
store = RCrewAI::Memory::SqliteStore.new(path: '~/.rcrewai/memory.db')
|
|
53
|
+
agent = RCrewAI::Agent.new(
|
|
54
|
+
name: 'engineer', role: '...', goal: '...',
|
|
55
|
+
memory: { embedder: embedder, store: store }
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The default store is in-memory (volatile). Any object implementing the store
|
|
60
|
+
interface (`add` / `search` / `all` / `delete` / `delete_record`) can be used —
|
|
61
|
+
e.g. a future Chroma/pgvector adapter.
|
|
62
|
+
|
|
63
|
+
### 2c. Memory scoping
|
|
64
|
+
|
|
65
|
+
Each agent's memory is scoped to its name by default, so agents sharing a
|
|
66
|
+
persistent store don't read each other's memories. Override with
|
|
67
|
+
`memory: { scope: 'custom' }` if you want deliberate sharing.
|
|
68
|
+
|
|
69
|
+
### 2d. Memory types (direct access)
|
|
70
|
+
|
|
71
|
+
The `Memory` facade exposes the underlying types for advanced use:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
agent.memory.short_term # recent executions, semantic recall, capped
|
|
75
|
+
agent.memory.long_term # durable insights from successful executions, deduped
|
|
76
|
+
agent.memory.entity # facts about entities (people, systems) seen in work
|
|
77
|
+
agent.memory.tool # tool-call history + outcomes
|
|
78
|
+
|
|
79
|
+
agent.memory.entity.entities # => ["Alice", "AWS", ...]
|
|
80
|
+
agent.memory.long_term.recall('...', limit: 3)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2e. Pre-built Memory
|
|
84
|
+
|
|
85
|
+
You can also construct and pass a `Memory` directly:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
memory = RCrewAI::Memory.new(scope: 'shared', embedder: embedder, store: store)
|
|
89
|
+
agent = RCrewAI::Agent.new(name: 'a', role: '...', goal: '...', memory: memory)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Behavior notes
|
|
95
|
+
|
|
96
|
+
- `add_execution` writes to short-term always, and (on success) promotes a
|
|
97
|
+
deduped insight to long-term and extracts entities.
|
|
98
|
+
- `relevant_executions(task, limit)` recalls across short- and long-term and
|
|
99
|
+
returns the same formatted-string-or-nil shape as before.
|
|
100
|
+
- `clear_short_term!` leaves long-term insights intact; `clear_all!` wipes
|
|
101
|
+
everything.
|