robot_lab 0.1.0 → 0.2.1
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/.architecture/AGENTS.md +32 -0
- data/.architecture/config.yml +8 -0
- data/.architecture/members.yml +60 -0
- data/.architecture/reviews/feature-free-will.md +490 -0
- data/.architecture/reviews/overall-codebase.md +427 -0
- data/.claude/settings.local.json +57 -0
- data/.codex/config.toml +2 -0
- data/.irbrc +2 -2
- data/.rubocop.yml +172 -0
- data/CHANGELOG.md +72 -0
- data/CLAUDE.md +139 -0
- data/README.md +91 -95
- data/Rakefile +109 -3
- data/agent2agent_review.md +192 -0
- data/agentf_improvements.md +253 -0
- data/agents.md +14 -0
- data/docs/examples/index.md +37 -2
- data/docs/getting-started/configuration.md +20 -7
- data/docs/guides/index.md +16 -16
- data/docs/guides/knowledge.md +7 -1
- data/docs/guides/observability.md +132 -0
- data/docs/index.md +30 -3
- data/docs/superpowers/plans/2026-05-06-agentskills.md +1303 -0
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +247 -0
- data/examples/.envrc +1 -0
- data/examples/01_simple_robot.rb +5 -9
- data/examples/02_tools.rb +5 -9
- data/examples/03_network.rb +8 -9
- data/examples/04_mcp.rb +21 -29
- data/examples/05_streaming.rb +12 -18
- data/examples/06_prompt_templates.rb +11 -19
- data/examples/07_network_memory.rb +16 -31
- data/examples/08_llm_config.rb +10 -22
- data/examples/09_chaining.rb +16 -27
- data/examples/10_memory.rb +12 -28
- data/examples/11_network_introspection.rb +15 -29
- data/examples/12_message_bus.rb +5 -12
- data/examples/13_spawn.rb +5 -10
- data/examples/14_rusty_circuit/.envrc +1 -0
- data/examples/14_rusty_circuit/comic.rb +2 -0
- data/examples/14_rusty_circuit/heckler.rb +1 -1
- data/examples/14_rusty_circuit/open_mic.rb +1 -3
- data/examples/14_rusty_circuit/scout.rb +2 -0
- data/examples/15_memory_network_and_bus/.envrc +1 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +6 -3
- data/examples/15_memory_network_and_bus/linux_writer.rb +1 -1
- data/examples/15_memory_network_and_bus/output/combined_article.md +6 -6
- data/examples/15_memory_network_and_bus/output/final_article.md +6 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +4 -2
- data/examples/15_memory_network_and_bus/output/mac_draft.md +3 -3
- data/examples/15_memory_network_and_bus/output/memory.json +6 -6
- data/examples/15_memory_network_and_bus/output/revision_1.md +10 -11
- data/examples/15_memory_network_and_bus/output/revision_2.md +6 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +3 -3
- data/examples/16_writers_room/.envrc +1 -0
- data/examples/16_writers_room/writers_room.rb +2 -4
- data/examples/17_skills.rb +8 -17
- data/examples/18_rails/Gemfile +1 -0
- data/examples/19_token_tracking.rb +9 -15
- data/examples/20_circuit_breaker.rb +10 -19
- data/examples/21_learning_loop.rb +11 -20
- data/examples/22_context_compression.rb +6 -13
- data/examples/23_convergence.rb +6 -17
- data/examples/24_structured_delegation.rb +11 -15
- data/examples/25_history_search.rb +5 -12
- data/examples/26_document_store.rb +6 -13
- data/examples/27_incident_response/incident_response.rb +4 -5
- data/examples/28_mcp_discovery.rb +8 -11
- data/examples/29_ractor_tools.rb +4 -9
- data/examples/30_ractor_network.rb +10 -19
- data/examples/31_launch_assessment.rb +10 -23
- data/examples/32_newsletter_reader.rb +188 -0
- data/examples/33_stock_generator.rb +80 -0
- data/examples/33_stock_predictor.rb +306 -0
- data/examples/34_agentskills.rb +72 -0
- data/examples/README.md +1 -1
- data/examples/common.rb +76 -0
- data/examples/ruboruby.md +423 -0
- data/examples/temp.md +51 -0
- data/lib/robot_lab/agent_skill.rb +63 -0
- data/lib/robot_lab/agent_skill_catalog.rb +74 -0
- data/lib/robot_lab/ask_user.rb +2 -2
- data/lib/robot_lab/bus_poller.rb +12 -5
- data/lib/robot_lab/config.rb +1 -12
- data/lib/robot_lab/delegation_future.rb +1 -1
- data/lib/robot_lab/doom_loop_detector.rb +98 -0
- data/lib/robot_lab/history_compressor.rb +4 -10
- data/lib/robot_lab/mcp/client.rb +1 -2
- data/lib/robot_lab/mcp/connection_poller.rb +3 -3
- data/lib/robot_lab/mcp/server.rb +1 -1
- data/lib/robot_lab/mcp/server_discovery.rb +0 -2
- data/lib/robot_lab/memory.rb +32 -27
- data/lib/robot_lab/memory_change.rb +2 -2
- data/lib/robot_lab/message.rb +4 -4
- data/lib/robot_lab/network.rb +11 -6
- data/lib/robot_lab/robot/agent_skill_matching.rb +99 -0
- data/lib/robot_lab/robot/bus_messaging.rb +9 -27
- data/lib/robot_lab/robot/history_search.rb +4 -1
- data/lib/robot_lab/robot/mcp_management.rb +5 -11
- data/lib/robot_lab/robot/template_rendering.rb +60 -40
- data/lib/robot_lab/robot.rb +323 -206
- data/lib/robot_lab/robot_result.rb +6 -5
- data/lib/robot_lab/run_config.rb +5 -11
- data/lib/robot_lab/script_tool.rb +76 -0
- data/lib/robot_lab/state_proxy.rb +7 -5
- data/lib/robot_lab/tool.rb +3 -3
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +5 -7
- data/lib/robot_lab/user_message.rb +2 -2
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab/waiter.rb +1 -1
- data/lib/robot_lab.rb +41 -52
- data/logfile +8 -0
- data/mkdocs.yml +2 -3
- data/robot_concurrency.md +38 -0
- data/simple_acp_review.md +298 -0
- data/site/404.html +2300 -0
- data/site/api/core/index.html +2706 -0
- data/site/api/core/memory/index.html +3793 -0
- data/site/api/core/network/index.html +3500 -0
- data/site/api/core/robot/index.html +4566 -0
- data/site/api/core/state/index.html +3390 -0
- data/site/api/core/tool/index.html +3843 -0
- data/site/api/index.html +2635 -0
- data/site/api/mcp/client/index.html +3435 -0
- data/site/api/mcp/index.html +2783 -0
- data/site/api/mcp/server/index.html +3252 -0
- data/site/api/mcp/transports/index.html +3352 -0
- data/site/api/messages/index.html +2641 -0
- data/site/api/messages/text-message/index.html +3087 -0
- data/site/api/messages/tool-call-message/index.html +3159 -0
- data/site/api/messages/tool-result-message/index.html +3252 -0
- data/site/api/messages/user-message/index.html +3212 -0
- data/site/api/streaming/context/index.html +3282 -0
- data/site/api/streaming/events/index.html +3347 -0
- data/site/api/streaming/index.html +2738 -0
- data/site/architecture/core-concepts/index.html +3757 -0
- data/site/architecture/index.html +2797 -0
- data/site/architecture/message-flow/index.html +3238 -0
- data/site/architecture/network-orchestration/index.html +3433 -0
- data/site/architecture/robot-execution/index.html +3140 -0
- data/site/architecture/state-management/index.html +3498 -0
- data/site/assets/css/custom.css +56 -0
- data/site/assets/images/favicon.png +0 -0
- data/site/assets/images/robot_lab.jpg +0 -0
- data/site/assets/javascripts/bundle.79ae519e.min.js +16 -0
- data/site/assets/javascripts/bundle.79ae519e.min.js.map +7 -0
- data/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- data/site/assets/javascripts/lunr/tinyseg.js +206 -0
- data/site/assets/javascripts/lunr/wordcut.js +6708 -0
- data/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
- data/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
- data/site/assets/stylesheets/main.484c7ddc.min.css +1 -0
- data/site/assets/stylesheets/main.484c7ddc.min.css.map +1 -0
- data/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
- data/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
- data/site/concepts/index.html +3455 -0
- data/site/examples/basic-chat/index.html +2880 -0
- data/site/examples/index.html +2907 -0
- data/site/examples/mcp-server/index.html +3018 -0
- data/site/examples/multi-robot-network/index.html +3131 -0
- data/site/examples/rails-application/index.html +3329 -0
- data/site/examples/tool-usage/index.html +3085 -0
- data/site/getting-started/configuration/index.html +3745 -0
- data/site/getting-started/index.html +2572 -0
- data/site/getting-started/installation/index.html +2981 -0
- data/site/getting-started/quick-start/index.html +2942 -0
- data/site/guides/building-robots/index.html +4290 -0
- data/site/guides/creating-networks/index.html +3858 -0
- data/site/guides/index.html +2586 -0
- data/site/guides/mcp-integration/index.html +3581 -0
- data/site/guides/memory/index.html +3586 -0
- data/site/guides/rails-integration/index.html +4019 -0
- data/site/guides/streaming/index.html +3157 -0
- data/site/guides/using-tools/index.html +3802 -0
- data/site/index.html +2671 -0
- data/site/search/search_index.json +1 -0
- data/site/sitemap.xml +183 -0
- data/site/sitemap.xml.gz +0 -0
- data/site/tags.json +1 -0
- data/temp.md +6 -0
- data/tool_manifest_plan.md +155 -0
- metadata +154 -92
- data/docs/examples/rails-application.md +0 -419
- data/docs/guides/ractor-parallelism.md +0 -364
- data/docs/guides/rails-integration.md +0 -681
- data/docs/superpowers/plans/2026-04-14-ractor-integration.md +0 -1538
- data/docs/superpowers/specs/2026-04-14-ractor-integration-design.md +0 -258
- data/lib/generators/robot_lab/install_generator.rb +0 -90
- data/lib/generators/robot_lab/job_generator.rb +0 -40
- data/lib/generators/robot_lab/robot_generator.rb +0 -55
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -42
- data/lib/generators/robot_lab/templates/job.rb.tt +0 -21
- data/lib/generators/robot_lab/templates/migration.rb.tt +0 -32
- data/lib/generators/robot_lab/templates/result_model.rb.tt +0 -52
- data/lib/generators/robot_lab/templates/robot.rb.tt +0 -31
- data/lib/generators/robot_lab/templates/robot_job.rb.tt +0 -18
- data/lib/generators/robot_lab/templates/robot_test.rb.tt +0 -34
- data/lib/generators/robot_lab/templates/routing_robot.rb.tt +0 -59
- data/lib/generators/robot_lab/templates/thread_model.rb.tt +0 -40
- data/lib/robot_lab/document_store.rb +0 -155
- data/lib/robot_lab/ractor_boundary.rb +0 -42
- data/lib/robot_lab/ractor_job.rb +0 -37
- data/lib/robot_lab/ractor_memory_proxy.rb +0 -85
- data/lib/robot_lab/ractor_network_scheduler.rb +0 -154
- data/lib/robot_lab/ractor_worker_pool.rb +0 -117
- data/lib/robot_lab/rails_integration/engine.rb +0 -29
- data/lib/robot_lab/rails_integration/job.rb +0 -158
- data/lib/robot_lab/rails_integration/railtie.rb +0 -51
- data/lib/robot_lab/rails_integration/turbo_stream_callbacks.rb +0 -72
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Review: `agent2agent` Ruby Gem
|
|
2
|
+
|
|
3
|
+
**Source:** https://github.com/general-intelligence-systems/agent2agent
|
|
4
|
+
**Docs:** https://general-intelligence-systems.github.io/agent2agent/
|
|
5
|
+
**License:** Apache 2.0
|
|
6
|
+
**Ruby:** >= 3.2
|
|
7
|
+
**Reviewed:** 2026-05-05
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What It Is
|
|
12
|
+
|
|
13
|
+
A complete Ruby implementation of Google's A2A (Agent-to-Agent) protocol — an open standard for interoperable, cross-vendor AI agent communication over HTTP. The gem provides both a server (Rack app) and a client, plus SSE streaming, task persistence, and push notifications.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Wire Protocol
|
|
18
|
+
|
|
19
|
+
Two parallel transport bindings, both implemented:
|
|
20
|
+
|
|
21
|
+
| Transport | Path | Format |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| JSON-RPC 2.0 | `POST /a2a` | `{"jsonrpc":"2.0","method":"SendMessage","params":{...}}` |
|
|
24
|
+
| HTTP+JSON/REST | `POST /message:send`, `GET /tasks/{id}`, etc. | Plain JSON |
|
|
25
|
+
| Agent Discovery | `GET /.well-known/agent-card.json` | Capabilities manifest |
|
|
26
|
+
|
|
27
|
+
### The 11 Protocol Operations
|
|
28
|
+
|
|
29
|
+
1. `SendMessage` — POST `/message:send` (synchronous)
|
|
30
|
+
2. `SendStreamingMessage` — POST `/message:stream` (SSE, server-streaming)
|
|
31
|
+
3. `GetTask` — GET `/tasks/{id}`
|
|
32
|
+
4. `ListTasks` — GET `/tasks`
|
|
33
|
+
5. `CancelTask` — POST `/tasks/{id}:cancel`
|
|
34
|
+
6. `SubscribeToTask` — SSE stream of task updates
|
|
35
|
+
7. `CreateTaskPushNotificationConfig` — POST `/tasks/{id}/push`
|
|
36
|
+
8. `GetTaskPushNotificationConfig` — GET `/tasks/{id}/push/{config_id}`
|
|
37
|
+
9. `ListTaskPushNotificationConfigs` — GET `/tasks/{id}/push`
|
|
38
|
+
10. `DeleteTaskPushNotificationConfig` — DELETE `/tasks/{id}/push/{config_id}`
|
|
39
|
+
11. `GetExtendedAgentCard` — GET extended agent card
|
|
40
|
+
|
|
41
|
+
### Task State Machine
|
|
42
|
+
|
|
43
|
+
`SUBMITTED → WORKING → INPUT_REQUIRED → COMPLETED / FAILED / CANCELED / REJECTED`
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Key Classes and Their Roles
|
|
48
|
+
|
|
49
|
+
| Class | Role |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `A2A::Agent` | DSL: `on("SendMessage") { \|req\| respond(...) }` — registers operation handlers |
|
|
52
|
+
| `A2A::Server` | Rack app — mountable in any Rack stack or Rails router |
|
|
53
|
+
| `A2A::Client` | Async-HTTP client; all 11 ops auto-generated as snake_case methods |
|
|
54
|
+
| `A2A::Proto` | Parses the real `data/a2a.proto` file — single source of truth for operations |
|
|
55
|
+
| `A2A::Schema` | Loads 47-type `data/a2a.json`; validates with `json_schemer`; camelCase/snake_case |
|
|
56
|
+
| `A2A::TaskStore` | In-memory task CRUD with `Thread::Queue` pub/sub and webhook delivery |
|
|
57
|
+
| `A2A::Store::SQLite` | Production drop-in: WAL mode, indexed, fiber-safe `Async::Queue` pub/sub |
|
|
58
|
+
| `A2A::SSE::Stream` | Subclasses `Protocol::HTTP::Body::Writable`; Falcon passes it untouched |
|
|
59
|
+
| `A2A::Bindings::JsonRpc` | Rack middleware that parses JSON-RPC envelopes and wraps responses |
|
|
60
|
+
|
|
61
|
+
### Agent DSL Example
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
agent = A2A::Agent.new do
|
|
65
|
+
on "SendMessage", "SendStreamingMessage" do |context|
|
|
66
|
+
task = context.store.create(context.request)
|
|
67
|
+
stream = context.stream
|
|
68
|
+
|
|
69
|
+
Async do
|
|
70
|
+
result = robot.run(context.request.params[:message])
|
|
71
|
+
context.store.complete(task.id, result)
|
|
72
|
+
stream.event(result, type: "result")
|
|
73
|
+
stream.finish
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context.respond(task)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
server = A2A::Server.new
|
|
81
|
+
server.register(agent)
|
|
82
|
+
run server
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Client Example
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
Async do
|
|
89
|
+
client = A2A::Client.new("http://localhost:9292")
|
|
90
|
+
card = client.agent_card
|
|
91
|
+
result = client.send_message(message: { role: "user", parts: [{ text: "Hello" }] })
|
|
92
|
+
puts result
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Notable Patterns
|
|
99
|
+
|
|
100
|
+
- **Duck-typed stores:** Any object implementing the task store interface can be swapped in — `TaskStore` (in-memory) or `Store::SQLite` (production), or a custom implementation.
|
|
101
|
+
- **Proto as source of truth:** `Proto` parses `data/a2a.proto` directly to extract operation metadata — stays in sync with the Google A2A spec automatically.
|
|
102
|
+
- **Fiber-native:** The SQLite store's pub/sub uses `Async::Queue`; SSE bodies use `Protocol::HTTP::Body::Writable`. Fully fiber-safe when run under Falcon.
|
|
103
|
+
- **`returnImmediately` flag:** Background jobs return a task ID immediately; updates stream via SSE as work proceeds.
|
|
104
|
+
- **`STATE_INPUT_REQUIRED`:** Multi-turn conversations — agent transitions to this state when it needs more user input before continuing. Client sends another `SendMessage` referencing the same task context.
|
|
105
|
+
- **Inline tests via `scampi`:** Tests live inside source files (`test do ... end`), not a separate test directory.
|
|
106
|
+
- **Tenant-prefixed paths:** Every REST route has a variant: `/{tenant}/message:send` for multi-tenant deployments.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Authentication
|
|
111
|
+
|
|
112
|
+
- **AgentCard** declares supported auth schemes in the capabilities manifest
|
|
113
|
+
- **Push notification configs** carry per-webhook auth: `scheme` + `credentials` sent as `Authorization: Bearer <credentials>` on webhook delivery; optionally also `X-A2A-Notification-Token`
|
|
114
|
+
- Incoming request authentication is left to the application layer (standard Rack middleware pattern)
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Dependencies
|
|
119
|
+
|
|
120
|
+
**Runtime:**
|
|
121
|
+
- `async (~> 2.0)`, `async-http (~> 0.95)` — fiber concurrency and HTTP
|
|
122
|
+
- `rack (~> 3.0)` — server composition (pure Rack, Rails-mountable)
|
|
123
|
+
- `json_schemer (~> 2.5)` — schema validation against 47 A2A types
|
|
124
|
+
- `google-protobuf (~> 4.34)` — proto file parsing
|
|
125
|
+
- `sqlite3` — persistent task store
|
|
126
|
+
- `protocol-http (~> 0.62)` — `Body::Writable` for SSE
|
|
127
|
+
- `scampi` — inline test framework
|
|
128
|
+
- `console` — structured logging
|
|
129
|
+
|
|
130
|
+
**Development:**
|
|
131
|
+
- `falcon (~> 0.55)` — async HTTP server for running agents
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Applicability to Your Projects
|
|
136
|
+
|
|
137
|
+
### RobotLab — High Value
|
|
138
|
+
|
|
139
|
+
The most compelling integration: expose each `Robot` or `Network` as a standard A2A service. This enables cross-language orchestration (Python LangChain, JS Genkit, Go agents, Google Cloud agents) without any shared code.
|
|
140
|
+
|
|
141
|
+
**Robot-as-A2A-Service:**
|
|
142
|
+
- `A2A::Agent` adapter delegates `SendMessage` → `robot.run(request.message)`, result becomes the A2A task result
|
|
143
|
+
- Publish an AgentCard at `/.well-known/agent-card.json` advertising each robot's capabilities and tools
|
|
144
|
+
- Use `A2A::Store::SQLite` to persist tasks across requests (stateless HTTP tier in front of stateful robots)
|
|
145
|
+
|
|
146
|
+
**Streaming:**
|
|
147
|
+
- `SendStreamingMessage` + `SSE::Stream` maps directly onto RubyLLM's streaming callbacks
|
|
148
|
+
- Streaming events become real-time SSE — no additional infrastructure needed
|
|
149
|
+
|
|
150
|
+
**Cross-process robot networks:**
|
|
151
|
+
- Instead of `TypedBus` (in-process pub/sub only), robots in separate processes or machines call each other via `A2A::Client`
|
|
152
|
+
- A Network router can delegate to remote robots via `client.send_message(...)` — standard HTTP replaces shared-memory message passing
|
|
153
|
+
|
|
154
|
+
**Background jobs:**
|
|
155
|
+
- A2A's push notification config CRUD gives external systems a standard webhook protocol for task completion
|
|
156
|
+
- Maps cleanly onto RobotLab's existing async robot semantics
|
|
157
|
+
|
|
158
|
+
**Multi-turn conversations:**
|
|
159
|
+
- `STATE_INPUT_REQUIRED` maps directly to RobotLab's `AskUser` tool pattern — robot needs user input before continuing
|
|
160
|
+
- Web clients get a proper protocol for handling confirmation flows rather than a terminal prompt
|
|
161
|
+
|
|
162
|
+
**Compatibility note:** Both RobotLab and `agent2agent` use the `async` gem — they compose cleanly. The main requirement is running under Falcon rather than a plain Ruby process. `TypedBus` (`Async::Queue`) and `A2A::Store::PubSub` (`Async::Queue`) are both fiber-based and compatible.
|
|
163
|
+
|
|
164
|
+
### AIA — Moderate Value
|
|
165
|
+
|
|
166
|
+
- `A2A::Client` could delegate tasks to remote A2A-compliant agents (specialized coding agents, search agents, etc.) instead of calling LLM APIs directly
|
|
167
|
+
- AIA could optionally expose itself as a local A2A server on `localhost:PORT` so IDE plugins or other tools can send it tasks via standard protocol
|
|
168
|
+
- AgentCard discovery would let AIA auto-configure available capabilities when connecting to remote agents
|
|
169
|
+
|
|
170
|
+
### Rails Apps Generally
|
|
171
|
+
|
|
172
|
+
`A2A::Server` is pure Rack:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# config/routes.rb
|
|
176
|
+
mount A2A::Server.new(agent: my_agent), at: "/agents/myagent"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
This works as-is. The 47-type schema validation via `json_schemer` is also useful standalone for validating A2A protocol payloads.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Bottom Line
|
|
184
|
+
|
|
185
|
+
Production-quality gem for its scope. Clean architecture: Rack middleware chain, duck-typed stores, fiber-safe pub/sub, SSE via `protocol-http`. Inline test coverage is extensive.
|
|
186
|
+
|
|
187
|
+
**Highest-value opportunity for RobotLab:** Robot-as-A2A-Service — exposing robots as standards-compliant HTTP endpoints enables cross-language agent orchestration that the current `TypedBus` approach (in-process only) cannot support. This would position RobotLab robots as first-class participants in the emerging A2A ecosystem alongside Python, JavaScript, and Go agent frameworks.
|
|
188
|
+
|
|
189
|
+
**Specific algorithms/patterns worth porting:**
|
|
190
|
+
1. `A2A::Store::SQLite` pub/sub pattern (Async::Queue-based) — applicable to RobotLab's Memory system
|
|
191
|
+
2. AgentCard capability manifest — useful for RobotLab's planned tool/capability discovery
|
|
192
|
+
3. `STATE_INPUT_REQUIRED` state machine entry — formalizes the `AskUser` pattern with a standard protocol state
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# `agentf` Gem Analysis
|
|
2
|
+
|
|
3
|
+
**Repository:** https://github.com/nealdeters/agentf
|
|
4
|
+
**Author:** Neal Deters
|
|
5
|
+
**Ruby:** 3.3.0+ | **Runtime deps:** `redis ~> 4.8`, `dotenv ~> 2.8`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What It Is
|
|
10
|
+
|
|
11
|
+
A Ruby multi-agent workflow engine for software development. Orchestrates
|
|
12
|
+
role-specialized agents (Planner, Engineer, QA, Reviewer, Security) that share
|
|
13
|
+
Redis memory — but **never calls an LLM itself**. It runs as an MCP server over
|
|
14
|
+
stdio and lets the IDE's AI (Copilot, OpenCode) do the actual inference.
|
|
15
|
+
It is scaffolding, not an API wrapper.
|
|
16
|
+
|
|
17
|
+
Three surfaces:
|
|
18
|
+
- **CLI** (`agentf <subcommand>`) — memory management, code exploration, metrics, evals
|
|
19
|
+
- **MCP server** (`agentf mcp-server`) — 19 tools over stdio for IDE integrations
|
|
20
|
+
- **Ruby API** — `WorkflowEngine`, `Agents::*`, `RedisMemory` for programmatic use
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
| Layer | Files | Role |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Entry point | `agentf.rb`, `bin/agentf` | Config singleton + CLI boot |
|
|
29
|
+
| CLI | `cli/router.rb`, `cli/memory.rb`, `cli/code.rb` | Subcommand dispatch |
|
|
30
|
+
| Agents | `agents/base.rb`, role subclasses | Role-based agent classes |
|
|
31
|
+
| Workflow | `workflow_engine.rb` | Orchestrator / sequencer |
|
|
32
|
+
| Memory | `memory.rb`, `memory/confirmation_handler.rb` | Redis-backed storage |
|
|
33
|
+
| Contracts | `workflow_contract.rb`, `agent_execution_contract.rb` | Constraint enforcement |
|
|
34
|
+
| Tools | `tools/`, `tools.rb` | Primitive capabilities |
|
|
35
|
+
| Commands | `commands/registry.rb`, `commands/*.rb` | Named command registry |
|
|
36
|
+
| MCP | `mcp/server.rb`, `mcp/stub.rb` | stdio MCP protocol server |
|
|
37
|
+
| Service | `service/providers.rb` | Provider adapters |
|
|
38
|
+
| Installer | `installer.rb` | Manifest generation and provider setup |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Patterns Worth Stealing
|
|
43
|
+
|
|
44
|
+
### 1. Agents That Describe Themselves at the Class Level
|
|
45
|
+
|
|
46
|
+
All agent metadata — `description`, `deliverables`, `policy_boundaries`,
|
|
47
|
+
`when_to_use`, `commands` — are class methods, not instance state or external
|
|
48
|
+
YAML. Agents are self-documenting, introspectable at install time, and
|
|
49
|
+
verifiable without instantiating anything. The `Installer` reads these at
|
|
50
|
+
install time to generate markdown manifests.
|
|
51
|
+
|
|
52
|
+
### 2. Three-Mode Contract Enforcement (advisory / enforcing / off)
|
|
53
|
+
|
|
54
|
+
A contract object wraps agent execution with `before!`/`after!` validation.
|
|
55
|
+
Run workflows in `advisory` mode during development (log violations, don't
|
|
56
|
+
stop), flip to `enforcing` for production. TDD phase discipline (`"red"` vs
|
|
57
|
+
`"green"`) is enforced at the contract layer, not by convention.
|
|
58
|
+
|
|
59
|
+
### 3. Human-in-the-Loop Memory Writes
|
|
60
|
+
|
|
61
|
+
`ConfirmationHandler` wraps Redis writes so that when confirmation is needed,
|
|
62
|
+
instead of raising it returns `{ confirmation: true, payload: ..., instructions: ... }`.
|
|
63
|
+
The caller re-invokes with `confirmedWrite: "confirmed"`. Works identically
|
|
64
|
+
across CLI, MCP, and programmatic callers.
|
|
65
|
+
|
|
66
|
+
### 4. Deterministic Local Embeddings With No ML Dependency
|
|
67
|
+
|
|
68
|
+
`EmbeddingProvider` SHA256-hashes tokens, uses the hash to pick a dimension in
|
|
69
|
+
a 64-element float vector, then normalizes it. Zero API calls, zero latency,
|
|
70
|
+
fully reproducible. Crude but sufficient for semantic memory search in a
|
|
71
|
+
dev-tool context.
|
|
72
|
+
|
|
73
|
+
### 5. Black-Box Shell Script Evals
|
|
74
|
+
|
|
75
|
+
Each eval scenario is three files: `prompt.txt`, `scenario.yml`, and
|
|
76
|
+
`verify.sh`. The shell script asserts postconditions against real Redis state.
|
|
77
|
+
Simple, portable, no mocking — `agentf eval run all` just executes them.
|
|
78
|
+
|
|
79
|
+
### 6. Graceful Redis Capability Degradation
|
|
80
|
+
|
|
81
|
+
The memory layer detects at runtime whether Redis Stack's JSON/Search/Vector
|
|
82
|
+
modules are present and degrades silently. Full semantic search if available;
|
|
83
|
+
plain key-value otherwise. No config flag needed — baked into `RedisMemory`.
|
|
84
|
+
|
|
85
|
+
### 7. Ruby Generating Its Own TypeScript Integration
|
|
86
|
+
|
|
87
|
+
The installer generates TypeScript plugin files (`agentf-plugin.ts`,
|
|
88
|
+
`tsconfig.json`, `package.json`) from within Ruby for OpenCode integration.
|
|
89
|
+
A Ruby gem producing its own typed IDE plugin layer is unusual and practical.
|
|
90
|
+
|
|
91
|
+
### 8. Policies Stored in Code, Not Config
|
|
92
|
+
|
|
93
|
+
`policy_boundaries` returns `{ "always" => [...], "ask_first" => [...], "never" => [...] }`
|
|
94
|
+
directly from Ruby class methods. Changing a policy means changing Ruby, not
|
|
95
|
+
YAML — no config drift.
|
|
96
|
+
|
|
97
|
+
### 9. Workflow Profiles as Constants
|
|
98
|
+
|
|
99
|
+
Workflow compositions are defined as constants in `WorkflowEngine::PROFILES`
|
|
100
|
+
(e.g., `rails_standard`, `rails_37signals`), mapping task types to ordered
|
|
101
|
+
agent sequences. One canonical source of truth for workflow shapes.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## RobotLab Applicability Analysis
|
|
106
|
+
|
|
107
|
+
### Patterns That Don't Apply
|
|
108
|
+
|
|
109
|
+
**Pattern 7 — Ruby Generating Its Own TypeScript Integration**
|
|
110
|
+
agentf is an IDE tool targeting Copilot/OpenCode. RobotLab is a Ruby library with no IDE integration surface. Not relevant.
|
|
111
|
+
|
|
112
|
+
**Pattern 6 — Graceful Redis Capability Degradation**
|
|
113
|
+
Already handled. `Memory` already degrades `redis → hash` fallback at initialization. `DocumentStore` uses fastembed, not Redis Stack — separate concern covered below.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### Patterns Worth Implementing
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
#### Pattern 1 — Self-Describing Robots/Tools (HIGH VALUE)
|
|
122
|
+
|
|
123
|
+
This is the missing piece for `tool_manifest_plan.md`. Nothing auto-registers today because there is nowhere to register *to* and no class-level metadata to register. agentf's insight: put descriptors at the class level, not in instances.
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
class MyTool < RobotLab::Tool
|
|
127
|
+
self.description = "Fetch current weather for a location"
|
|
128
|
+
self.tags = [:network, :read_only]
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
At class-load time, Zeitwerk triggers auto-registration into `RobotLab.tool_registry`. The selector-robot pattern becomes viable without any explicit registration ceremony. Live callable instances stay per-robot; the global registry holds lightweight descriptors only (name + description). This is the v1 the `tool_manifest_plan.md` actually needs — and avoids the MCP auto-registration problem identified in that plan's review notes.
|
|
133
|
+
|
|
134
|
+
Robot subclasses get the same treatment:
|
|
135
|
+
```ruby
|
|
136
|
+
class SupportBot < RobotLab::Robot
|
|
137
|
+
self.description = "Handles tier-1 customer support"
|
|
138
|
+
self.capabilities = [:search, :ticket_creation]
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This also enables the `Installer`-style manifest generation: `RobotLab.tool_registry.summary` produces the compact name+description list the selector robot reasons over.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
#### Patterns 2 + 8 — Contract Enforcement + Policy Boundaries (HIGH VALUE, medium effort)
|
|
147
|
+
|
|
148
|
+
RobotLab has a circuit breaker (`max_tool_rounds`) and an error hierarchy — but no *pre/post validation layer* on execution. As AIA drives RobotLab into production use, this gap is significant.
|
|
149
|
+
|
|
150
|
+
The three modes map cleanly to the dev→prod lifecycle:
|
|
151
|
+
- `advisory` — log violations, don't block (development default)
|
|
152
|
+
- `enforcing` — raise on violation (production default)
|
|
153
|
+
- `off` — no overhead (test default)
|
|
154
|
+
|
|
155
|
+
Policies declared at the Tool class level:
|
|
156
|
+
```ruby
|
|
157
|
+
class DeleteFileTool < RobotLab::Tool
|
|
158
|
+
policy :ask_first # always prompt user before executing
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class FormatDriveTool < RobotLab::Tool
|
|
162
|
+
policy :never # contract blocks execution entirely in enforcing mode
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The contract wraps `robot.run()` with `before!`/`after!` hooks — checks token budget, validates tool policies, enforces max cost. A `RunConfig` field (`contract: :advisory`) controls the mode and flows through the standard hierarchy. This pairs naturally with the existing `RunConfig` merge semantics and would be the first true safety/governance layer in RobotLab.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
#### Pattern 9 — Workflow Profiles (LOW EFFORT, good discoverability)
|
|
171
|
+
|
|
172
|
+
Networks are currently built by hand each time. A `RobotLab::Profiles` module with named constants would reduce boilerplate and document canonical topologies:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
RobotLab::Profiles::CONSENSUS # fan-out to N robots → reconciler
|
|
176
|
+
RobotLab::Profiles::PIPELINE # sequential chain
|
|
177
|
+
RobotLab::Profiles::PARALLEL # concurrent, no synthesis
|
|
178
|
+
RobotLab::Profiles::MCP_FAN_OUT # one robot per MCP server
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Each profile is a lambda/factory that takes robots + optional router and returns a configured network. This also gives AIA's `RobotFactory` topologies a canonical home in the library itself rather than application-level code.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
#### Pattern 5 — Eval Framework (MEDIUM EFFORT, high long-term value)
|
|
186
|
+
|
|
187
|
+
RobotLab has 22 example scripts but **zero assertions**. They are demos, not evals. agentf's `prompt.txt + scenario.yml + verify.sh` per scenario is the right shape.
|
|
188
|
+
|
|
189
|
+
For RobotLab the natural form:
|
|
190
|
+
- `evals/scenarios/convergence_basic/` — prompt, expected behavior description, `verify.rb` script
|
|
191
|
+
- `rake eval:run[convergence_basic]` replays against a VCR cassette and asserts postconditions
|
|
192
|
+
- `rake eval:run:all` becomes a regression suite for agent behavior
|
|
193
|
+
|
|
194
|
+
This connects directly to AIA's EDD (Eval-Driven Development) vision and would give confidence before a 1.0 release. The existing VCR/WebMock infrastructure is the right foundation.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
#### Pattern 4 — Deterministic Embedding Fallback (LOW EFFORT)
|
|
199
|
+
|
|
200
|
+
`DocumentStore` hard-requires fastembed — no fallback. On first use it downloads an ONNX model, which is slow and requires a working network. `word_hash` (stemmed TF vectors) already exists in the codebase and is used by `compress_history`, `search_history`, and `Convergence`.
|
|
201
|
+
|
|
202
|
+
A simple degradation path: when fastembed raises `LoadError` or the model is unavailable, `DocumentStore` falls back to `word_hash` cosine similarity with a logged warning. Zero new infrastructure — just wiring what already exists into `DocumentStore#embed`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
#### Pattern 3 — HITL Confirmation Protocol (LOW VALUE given AskUser)
|
|
207
|
+
|
|
208
|
+
The agentf pattern — return `{ confirmation: true, payload: }` instead of raising, re-invoke with `confirmedWrite: "confirmed"` — is elegant for memory writes that need approval. However, RobotLab already has `AskUser` for the primary HITL use case. This pattern only becomes interesting if RobotLab needs programmatic HITL that doesn't require a terminal (e.g., web apps pausing for user approval via HTTP callback). Not urgent.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### Priority Ranking
|
|
213
|
+
|
|
214
|
+
| Priority | Pattern | Effort | Connects To |
|
|
215
|
+
|---|---|---|---|
|
|
216
|
+
| 1 | Self-describing tools/robots (class-level metadata) | Medium | `tool_manifest_plan.md` — makes ToolManifest v1 actionable |
|
|
217
|
+
| 2 | Contract enforcement + Policy boundaries | Medium | Production safety gap; RunConfig integration |
|
|
218
|
+
| 3 | Eval framework | Medium | Long-term regression confidence; AIA EDD |
|
|
219
|
+
| 4 | Workflow profiles | Low | AIA's RobotFactory; discoverability |
|
|
220
|
+
| 5 | DocumentStore word_hash fallback | Low | Dev ergonomics; no ONNX download needed |
|
|
221
|
+
|
|
222
|
+
**Natural weekly pairing:** Pattern 1 (self-describing classes → ToolManifest v1) + Pattern 9 (workflow profiles) form a coherent "discoverability and composability" theme. Patterns 2+8 are the right *next* architectural investment but scope to a separate week.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Comparison to Other Agent Frameworks
|
|
227
|
+
|
|
228
|
+
| Aspect | `agentf` | LangChain (Python) | CrewAI (Python) |
|
|
229
|
+
|---|---|---|---|
|
|
230
|
+
| Language | Ruby | Python | Python |
|
|
231
|
+
| LLM calls | None (delegates to IDE) | Direct | Direct |
|
|
232
|
+
| Agent communication | Shared Redis memory | In-process state | Sequential/hierarchical |
|
|
233
|
+
| Memory | Episodic + semantic (Redis Stack) | Various vector stores | Basic |
|
|
234
|
+
| IDE integration | MCP server (Copilot, OpenCode) | None native | None native |
|
|
235
|
+
| Contract enforcement | 3-mode advisory/enforcing/off | None | None |
|
|
236
|
+
| Self-describing agents | Class-method metadata | No | Role strings in YAML |
|
|
237
|
+
| Eval framework | Black-box shell scripts | Unit tests | Unit tests |
|
|
238
|
+
| TDD enforcement | Built-in (red/green contracts) | None | None |
|
|
239
|
+
|
|
240
|
+
Within Ruby there is essentially no direct equivalent. `ruby-openai` and
|
|
241
|
+
`omniai` handle LLM API calls but have no orchestration. `agentf` is novel in
|
|
242
|
+
the Ruby ecosystem for combining Redis-backed episodic memory, role-specialized
|
|
243
|
+
agent classes, contract enforcement, and an MCP stdio server in one gem.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## The Big Takeaway
|
|
248
|
+
|
|
249
|
+
The most instructive thing about `agentf` is its **division of responsibility**:
|
|
250
|
+
the gem owns memory, sequencing, policies, and tool-exposure via MCP — the
|
|
251
|
+
IDE's AI owns inference. That separation means no API keys, no HTTP calls to
|
|
252
|
+
LLM providers, and no model coupling. If building AI-assisted tooling in Ruby,
|
|
253
|
+
that architecture is worth emulating.
|
data/agents.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
### Local Agent Context: Repository Root
|
|
2
|
+
|
|
3
|
+
- Start here, then navigate to the closest directory AGENTS.md before coding.
|
|
4
|
+
- Do not operate from root unless necessary.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [.architecture/reviews](./.architecture/reviews/AGENTS.md): Access `config.yml` for architecture-related settings.
|
|
9
|
+
- [.github/workflows](./.github/workflows/AGENTS.md): Review `deploy-github-pages.yml` for CI/CD pipeline specifics.
|
|
10
|
+
- [.ruby-lsp](./.ruby-lsp/AGENTS.md): Initialize your environment with `Gemfile` dependencies.
|
|
11
|
+
- [docs](./docs/AGENTS.md): Consult `/docs/api/core/robot.md` for robot-related API documentation.
|
|
12
|
+
- [examples](./examples/AGENTS.md): Explore `14_rusty_circuit/open_mic.rb` for demo scripts in action.
|
|
13
|
+
- [lib/generators/robot_lab](./lib/generators/robot_lab/AGENTS.md): Use `templates/initializer.rb.tt` for custom project setup.
|
|
14
|
+
- [test/robot_lab](./test/robot_lab/AGENTS.md): Implement new tests based on `test_helper.rb` configurations.
|
data/docs/examples/index.md
CHANGED
|
@@ -14,10 +14,11 @@ These examples show how to use RobotLab for common scenarios, from simple chatbo
|
|
|
14
14
|
| [Multi-Robot Network](multi-robot-network.md) | Customer service with routing |
|
|
15
15
|
| [Tool Usage](tool-usage.md) | External API integration |
|
|
16
16
|
| [MCP Server](mcp-server.md) | Creating an MCP tool server |
|
|
17
|
-
| [Rails Application](rails-application.md) | Full Rails integration |
|
|
18
17
|
| [Message Bus](#message-bus) | Bidirectional robot communication with convergence |
|
|
19
18
|
| [Spawning Robots](#spawning-robots) | Dynamic specialist creation at runtime |
|
|
20
19
|
|
|
20
|
+
> **Rails example** — see [robot_lab-rails](https://github.com/MadBomber/robot_lab-rails/blob/main/docs/examples/rails-application.md) for a full Rails integration example.
|
|
21
|
+
|
|
21
22
|
## Quick Links
|
|
22
23
|
|
|
23
24
|
### Simple Examples
|
|
@@ -158,7 +159,7 @@ puts result.last_text_content
|
|
|
158
159
|
|
|
159
160
|
3. Run example:
|
|
160
161
|
```bash
|
|
161
|
-
ruby examples/
|
|
162
|
+
ruby examples/01_simple_robot.rb
|
|
162
163
|
```
|
|
163
164
|
|
|
164
165
|
Or use the provided rake tasks:
|
|
@@ -168,6 +169,40 @@ bundle exec rake examples:all # Run all examples
|
|
|
168
169
|
bundle exec rake examples:run[1] # Run specific example by number
|
|
169
170
|
```
|
|
170
171
|
|
|
172
|
+
## Shared Example Setup (`examples/common.rb`)
|
|
173
|
+
|
|
174
|
+
All numbered examples (`01_*.rb` through `34_*.rb`) begin with:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
require_relative "common"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`common.rb` handles the shared boilerplate so individual examples stay focused:
|
|
181
|
+
|
|
182
|
+
- **`LLM` hash** — frozen lookup of provider/model pairs accessible as `LLM[:default]`, `LLM[:local]`, `LLM[:anthropic]`. Each entry is a `LlmConfig = Data.define(:provider, :model)` value, so you access the model string as `LLM[:default].model`.
|
|
183
|
+
- **`RubyLLM.configure`** — sets a null logger and `LLM[:default].model` as the `default_model`.
|
|
184
|
+
- **`RobotLab.configure`** — sets a null logger.
|
|
185
|
+
- **Output helpers** — `banner(title)`, `section(title)`, `hr`, and `show_code(ruby_string, label:)` (Rouge-highlighted) for consistent terminal formatting.
|
|
186
|
+
|
|
187
|
+
## Template Path via direnv
|
|
188
|
+
|
|
189
|
+
Examples that bundle their own `prompts/` directory ship with a `.envrc` file:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
examples/.envrc
|
|
193
|
+
examples/14_rusty_circuit/.envrc
|
|
194
|
+
examples/15_memory_network_and_bus/.envrc
|
|
195
|
+
examples/16_writers_room/.envrc
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each sets `ROBOT_LAB_TEMPLATE_PATH` to the local `prompts/` directory when [direnv](https://direnv.net/) is active. `common.rb` also sets this variable as a fallback if `direnv` has not loaded the `.envrc`:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
ENV["ROBOT_LAB_TEMPLATE_PATH"] ||= File.join(__dir__, "prompts")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
This means examples work correctly whether you run them from the project root with rake tasks or directly from inside the example's own directory.
|
|
205
|
+
|
|
171
206
|
## Message Bus
|
|
172
207
|
|
|
173
208
|
Robots can communicate bidirectionally via a message bus, enabling convergence loops and negotiation patterns. This example demonstrates a comedy critic tasking a comedian to generate jokes until one passes:
|
|
@@ -31,8 +31,14 @@ RobotLab.config.streaming_enabled #=> true
|
|
|
31
31
|
RobotLab.config.development? #=> true/false
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
!!!
|
|
35
|
-
RobotLab
|
|
34
|
+
!!! tip "configure block"
|
|
35
|
+
`RobotLab.configure` yields the config object for block-style setup — useful for setting runtime-only attributes like the logger. For static settings (API keys, timeouts, model defaults) prefer config files or environment variables.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
RobotLab.configure do |c|
|
|
39
|
+
c.logger = Logger.new(File::NULL) # silence all RobotLab logging
|
|
40
|
+
end
|
|
41
|
+
```
|
|
36
42
|
|
|
37
43
|
## Environment Variables
|
|
38
44
|
|
|
@@ -195,14 +201,21 @@ Default chat parameters applied to all robots unless overridden:
|
|
|
195
201
|
|
|
196
202
|
## Runtime-Only Attributes
|
|
197
203
|
|
|
198
|
-
Some attributes can only be set at runtime, not through config files:
|
|
204
|
+
Some attributes can only be set at runtime, not through config files. Use direct assignment on `RobotLab.config` or the `RobotLab.configure` block:
|
|
199
205
|
|
|
200
206
|
```ruby
|
|
201
|
-
#
|
|
202
|
-
RobotLab.config.logger = Logger.new(nil)
|
|
203
|
-
RobotLab.config.logger = Logger.new("robot.log")
|
|
207
|
+
# Direct assignment
|
|
208
|
+
RobotLab.config.logger = Logger.new(nil) # silence logging
|
|
209
|
+
RobotLab.config.logger = Logger.new("robot.log") # log to file
|
|
210
|
+
|
|
211
|
+
# Block-style configure (equivalent, useful when setting multiple values)
|
|
212
|
+
RobotLab.configure do |c|
|
|
213
|
+
c.logger = Logger.new(File::NULL)
|
|
214
|
+
end
|
|
204
215
|
```
|
|
205
216
|
|
|
217
|
+
`RobotLab.configure` yields the same `Config` object returned by `RobotLab.config`.
|
|
218
|
+
|
|
206
219
|
## Reloading Configuration
|
|
207
220
|
|
|
208
221
|
To reload configuration from all sources:
|
|
@@ -361,7 +374,7 @@ effective.temperature #=> 0.9 (overridden)
|
|
|
361
374
|
| **LLM** | `model`, `temperature`, `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, `stop` |
|
|
362
375
|
| **Tools** | `mcp`, `tools` |
|
|
363
376
|
| **Callbacks** | `on_tool_call`, `on_tool_result` |
|
|
364
|
-
| **Infrastructure** | `bus`, `enable_cache`, `max_tool_rounds`, `token_budget`, `ractor_pool_size`, `max_concurrent_robots` |
|
|
377
|
+
| **Infrastructure** | `bus`, `enable_cache`, `max_tool_rounds`, `token_budget`, `ractor_pool_size`, `max_concurrent_robots`, `doom_loop_threshold`, `auto_compact`, `compact_threshold` |
|
|
365
378
|
|
|
366
379
|
### RunConfig vs RobotLab.config
|
|
367
380
|
|
data/docs/guides/index.md
CHANGED
|
@@ -40,21 +40,11 @@ If you're new to RobotLab, start here:
|
|
|
40
40
|
|
|
41
41
|
- [:octicons-pulse-24: **Observability & Safety**](observability.md)
|
|
42
42
|
|
|
43
|
-
Token tracking, circuit breakers, and learning accumulation
|
|
43
|
+
Token tracking, circuit breakers, doom loop detection, auto compaction, and learning accumulation
|
|
44
44
|
|
|
45
|
-
- [:
|
|
45
|
+
- [:octicons-search-24: **Knowledge & Retrieval**](knowledge.md)
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
## Framework Integration
|
|
52
|
-
|
|
53
|
-
<div class="grid cards" markdown>
|
|
54
|
-
|
|
55
|
-
- [:material-language-ruby:{ .lg } **Rails Integration**](rails-integration.md)
|
|
56
|
-
|
|
57
|
-
Use RobotLab in Ruby on Rails applications
|
|
47
|
+
Chat history search and embedding-based document store for RAG workflows
|
|
58
48
|
|
|
59
49
|
</div>
|
|
60
50
|
|
|
@@ -68,6 +58,16 @@ If you're new to RobotLab, start here:
|
|
|
68
58
|
| [MCP Integration](mcp-integration.md) | External tool servers | 10 min |
|
|
69
59
|
| [Streaming](streaming.md) | Real-time responses | 5 min |
|
|
70
60
|
| [Memory](memory.md) | Shared data store | 5 min |
|
|
71
|
-
| [
|
|
72
|
-
| [
|
|
73
|
-
|
|
61
|
+
| [Observability & Safety](observability.md) | Token tracking, circuit breaker, doom loop detection, auto compaction, learning loop | 10 min |
|
|
62
|
+
| [Knowledge & Retrieval](knowledge.md) | Chat history search and embedding-based document store (RAG) | 10 min |
|
|
63
|
+
|
|
64
|
+
## Extension Gems
|
|
65
|
+
|
|
66
|
+
Additional capabilities are available as separate gems:
|
|
67
|
+
|
|
68
|
+
| Gem | Description | Docs |
|
|
69
|
+
|-----|-------------|------|
|
|
70
|
+
| [robot_lab-rails](https://github.com/MadBomber/robot_lab-rails) | Rails generators, background jobs, Turbo Stream broadcasting | [Rails Integration guide](https://github.com/MadBomber/robot_lab-rails/blob/main/docs/guides/rails-integration.md) |
|
|
71
|
+
| [robot_lab-ractor](https://github.com/MadBomber/robot_lab-ractor) | True CPU parallelism for tools and robot pipelines via Ruby Ractors | [Ractor Parallelism guide](https://github.com/MadBomber/robot_lab-ractor/blob/main/docs/guides/ractor-parallelism.md) |
|
|
72
|
+
| [robot_lab-durable](https://github.com/MadBomber/robot_lab-durable) | Cross-session knowledge persistence with YAML-backed storage | [robot_lab-durable README](https://github.com/MadBomber/robot_lab-durable) |
|
|
73
|
+
| [robot_lab-document_store](https://github.com/MadBomber/robot_lab-document_store) | Embedding-based semantic document search via fastembed | [robot_lab-document_store README](https://github.com/MadBomber/robot_lab-document_store) |
|
data/docs/guides/knowledge.md
CHANGED
|
@@ -171,7 +171,13 @@ result = robot.run("Use the following context:\n#{context}\n\nQuestion: #{user_q
|
|
|
171
171
|
|
|
172
172
|
### Dependency
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
The embedding-based document store requires the [`robot_lab-document_store`](https://github.com/MadBomber/robot_lab-document_store) extension gem. Add it to your Gemfile:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
gem "robot_lab-document_store"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This gem bundles `fastembed` for ONNX-based embeddings. The `BAAI/bge-small-en-v1.5` model (~23 MB) is downloaded on first use and cached in `~/.cache/fastembed/`. Without `robot_lab-document_store` loaded, calling `memory.store_document` or `memory.search_documents` raises `RobotLab::DependencyError`.
|
|
175
181
|
|
|
176
182
|
---
|
|
177
183
|
|