scout-ai 1.2.5 → 2.0.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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +111 -31
  3. data/README.md +171 -320
  4. data/Rakefile +17 -1
  5. data/VERSION +1 -1
  6. data/doc/Improvements.md +325 -0
  7. data/doc/StartHere.md +110 -0
  8. data/doc/developer/Architecture.md +126 -0
  9. data/doc/developer/Backends.md +199 -0
  10. data/doc/developer/ChatLifecycle.md +183 -0
  11. data/doc/developer/DelegationInternals.md +295 -0
  12. data/doc/developer/DesignPrinciples.md +245 -0
  13. data/doc/developer/PromptProcessing.md +292 -0
  14. data/doc/developer/Provenance.md +317 -0
  15. data/doc/user/BuildingAgents.md +345 -0
  16. data/doc/user/Cookbook.md +333 -0
  17. data/doc/user/CoreConcepts.md +181 -0
  18. data/doc/user/Delegation.md +191 -0
  19. data/doc/user/GettingStarted.md +159 -0
  20. data/doc/user/ManagingContext.md +163 -0
  21. data/doc/user/MultiAgentWorkflows.md +256 -0
  22. data/doc/user/Python.md +159 -0
  23. data/doc/user/RunningInference.md +200 -0
  24. data/doc/user/ToolCalling.md +193 -0
  25. data/doc/user/WritingChats.md +197 -0
  26. data/lib/scout/llm/agent/chat.rb +62 -12
  27. data/lib/scout/llm/agent/delegate.rb +274 -65
  28. data/lib/scout/llm/agent/iterate.rb +2 -2
  29. data/lib/scout/llm/agent/save.rb +273 -0
  30. data/lib/scout/llm/agent/workflow.rb +164 -0
  31. data/lib/scout/llm/agent.rb +86 -61
  32. data/lib/scout/llm/ask.rb +36 -6
  33. data/lib/scout/llm/backends/anthropic.rb +9 -1
  34. data/lib/scout/llm/backends/bedrock.rb +15 -3
  35. data/lib/scout/llm/backends/default.rb +129 -89
  36. data/lib/scout/llm/backends/glm.rb +58 -0
  37. data/lib/scout/llm/backends/huggingface.rb +13 -1
  38. data/lib/scout/llm/backends/ollama.rb +13 -0
  39. data/lib/scout/llm/backends/openwebui.rb +8 -3
  40. data/lib/scout/llm/chat/agent_meta.rb +264 -0
  41. data/lib/scout/llm/chat/annotation.rb +37 -14
  42. data/lib/scout/llm/chat/parse.rb +28 -6
  43. data/lib/scout/llm/chat/persist.rb +25 -0
  44. data/lib/scout/llm/chat/process/clear.rb +19 -1
  45. data/lib/scout/llm/chat/process/files.rb +16 -1
  46. data/lib/scout/llm/chat/process/meta.rb +422 -32
  47. data/lib/scout/llm/chat/process/options.rb +21 -1
  48. data/lib/scout/llm/chat/process/tools.rb +49 -12
  49. data/lib/scout/llm/chat/process.rb +4 -0
  50. data/lib/scout/llm/chat/prompt/shorten_tools.rb +125 -0
  51. data/lib/scout/llm/chat/prompt/shorten_tools_epoch.rb +365 -0
  52. data/lib/scout/llm/chat/prompt.rb +48 -0
  53. data/lib/scout/llm/chat/provenance.rb +775 -0
  54. data/lib/scout/llm/chat/tool_calls.rb +76 -0
  55. data/lib/scout/llm/chat.rb +18 -2
  56. data/lib/scout/llm/embed.rb +8 -3
  57. data/lib/scout/llm/image.rb +86 -0
  58. data/lib/scout/llm/rag.rb +3 -3
  59. data/lib/scout/llm/tools/call.rb +159 -10
  60. data/lib/scout/llm/tools/knowledge_base.rb +1 -1
  61. data/lib/scout/llm/tools/workflow.rb +13 -5
  62. data/lib/scout-ai.rb +1 -0
  63. data/research/ChatAnalyst-required-changes.md +167 -0
  64. data/research/agent-delegation-analysis.md +810 -0
  65. data/research/agent-meta-provenance-integration-plan.md +622 -0
  66. data/research/agent-workflow-analysis.md +1120 -0
  67. data/research/backends-analysis.md +836 -0
  68. data/research/chat-core-analysis.md +946 -0
  69. data/research/chatanalyst-provenance/00-baseline.md +30 -0
  70. data/research/chatanalyst-provenance/01-repo-map.md +60 -0
  71. data/research/chatanalyst-provenance/02-event-reconstruction.md +55 -0
  72. data/research/chatanalyst-provenance/03-duplication-evidence.md +45 -0
  73. data/research/chatanalyst-provenance/04-tooling-root-cause.md +57 -0
  74. data/research/chatanalyst-provenance/05-fix-plan.md +46 -0
  75. data/research/chatanalyst-provenance/07-critic-review.md +25 -0
  76. data/research/chatanalyst-provenance/final-report.md +45 -0
  77. data/research/chatanalyst-provenance/resumption.md +37 -0
  78. data/research/coding-philosophy-analysis.md +928 -0
  79. data/research/commands-analysis.md +947 -0
  80. data/research/multi-agent-patterns-analysis.md +853 -0
  81. data/research/prompt-strategies-analysis.md +630 -0
  82. data/research/prov-verbosity-fix-notes.md +77 -0
  83. data/research/provenance-analysis.md +469 -0
  84. data/research/provenance-navigation-design.md +640 -0
  85. data/research/synthesis-report.md +487 -0
  86. data/research/tools-system-analysis.md +779 -0
  87. data/scout-ai.gemspec +97 -13
  88. data/scout_commands/agent/ask +13 -3
  89. data/scout_commands/agent/kb +2 -0
  90. data/scout_commands/llm/ask +11 -4
  91. data/scout_commands/llm/md +76 -0
  92. data/scout_commands/llm/prov +602 -0
  93. data/scout_commands/llm/word +71 -0
  94. data/share/word/reference.docx +0 -0
  95. data/test/etc/AI/mock.yaml +11 -0
  96. data/test/fixtures/backends/anthropic.json +19 -0
  97. data/test/fixtures/backends/anthropic_tool_use.json +24 -0
  98. data/test/fixtures/backends/bedrock.json +8 -0
  99. data/test/fixtures/backends/bedrock_embedding.json +3 -0
  100. data/test/fixtures/backends/bedrock_tool_use.json +17 -0
  101. data/test/fixtures/backends/ollama.json +16 -0
  102. data/test/fixtures/backends/ollama_tool_call.json +27 -0
  103. data/test/fixtures/backends/openai_chat.json +21 -0
  104. data/test/fixtures/backends/openai_chat_tool_call.json +31 -0
  105. data/test/fixtures/backends/responses.json +33 -0
  106. data/test/fixtures/backends/responses_tool_call.json +28 -0
  107. data/test/integration/README.md +32 -0
  108. data/test/integration/scout/llm/backends/test_endpoints.rb +34 -0
  109. data/test/integration/scout/llm/backends/test_openwebui.rb +61 -0
  110. data/test/integration/scout/llm/backends/test_relay.rb +52 -0
  111. data/test/integration/scout/llm/test_infrastructure.rb +74 -0
  112. data/test/{scout → integration/scout}/llm/test_mcp.rb +1 -1
  113. data/test/integration/scout/llm/tools/test_mcp.rb +42 -0
  114. data/test/integration/scout/model/test_base.rb +91 -0
  115. data/test/scout/llm/agent/test_chat.rb +8 -2
  116. data/test/scout/llm/agent/test_save.rb +413 -0
  117. data/test/scout/llm/agent/test_workflow.rb +110 -0
  118. data/test/scout/llm/backends/test_anthropic.rb +93 -10
  119. data/test/scout/llm/backends/test_bedrock.rb +118 -2
  120. data/test/scout/llm/backends/test_ollama.rb +70 -20
  121. data/test/scout/llm/backends/test_openwebui.rb +42 -40
  122. data/test/scout/llm/backends/test_relay.rb +4 -2
  123. data/test/scout/llm/chat/agent_meta_fixtures.rb +131 -0
  124. data/test/scout/llm/chat/process/test_meta.rb +518 -0
  125. data/test/scout/llm/chat/process/test_normalize_usage.rb +183 -0
  126. data/test/scout/llm/chat/test_agent_meta.rb +357 -0
  127. data/test/scout/llm/chat/test_agent_meta_provenance.rb +467 -0
  128. data/test/scout/llm/chat/test_agent_meta_tokens.rb +594 -0
  129. data/test/scout/llm/chat/test_parse.rb +70 -15
  130. data/test/scout/llm/chat/test_prov_cli.rb +274 -0
  131. data/test/scout/llm/chat/test_provenance.rb +240 -0
  132. data/test/scout/llm/chat/test_tool_calls.rb +38 -0
  133. data/test/scout/llm/test_agent.rb +13 -36
  134. data/test/scout/llm/test_ask.rb +75 -52
  135. data/test/scout/llm/test_chat.rb +107 -13
  136. data/test/scout/llm/test_embed.rb +48 -0
  137. data/test/scout/llm/test_rag.rb +23 -16
  138. data/test/scout/llm/test_tools.rb +12 -1
  139. data/test/scout/llm/tools/test_knowledge_base.rb +0 -1
  140. data/test/scout/llm/tools/test_mcp.rb +5 -3
  141. data/test/scout/llm/tools/test_workflow.rb +23 -2
  142. data/test/scout/model/python/huggingface/causal/test_next_token.rb +11 -5
  143. data/test/scout/model/python/huggingface/test_causal.rb +9 -3
  144. data/test/scout/model/python/huggingface/test_classification.rb +11 -2
  145. data/test/scout/model/python/test_torch.rb +2 -0
  146. data/test/scout/model/python/torch/test_helpers.rb +4 -0
  147. data/test/scout/model/test_base.rb +4 -2
  148. data/test/support/availability.rb +231 -0
  149. data/test/support/fake_clients.rb +138 -0
  150. data/test/support/fixtures.rb +21 -0
  151. data/test/support/infrastructure_probes.rb +136 -0
  152. data/test/support/mock_backend.rb +215 -0
  153. data/test/test_helper.rb +32 -2
  154. metadata +96 -12
  155. data/doc/Agent.md +0 -354
  156. data/doc/Chat.md +0 -481
  157. data/doc/LLM.md +0 -356
  158. data/doc/PythonAgentTasks.md +0 -333
  159. data/doc/RAG.md +0 -129
  160. data/doc/USER_GUIDE.md +0 -572
  161. data/scout_commands/documenter +0 -148
  162. data/test/scout/llm/backends/test_openai.rb +0 -192
  163. data/test/scout/llm/backends/test_responses.rb +0 -238
  164. data/test/scout/llm/test_parse.rb +0 -98
data/doc/Agent.md DELETED
@@ -1,354 +0,0 @@
1
- # Agent
2
-
3
- `LLM::Agent` is the stateful wrapper around `LLM.ask` + `Chat`.
4
-
5
- Use an Agent when you want **one or more ongoing conversations** (state), plus:
6
-
7
- - a consistent place to store default LLM options (`endpoint`, `model`, `backend`, `format`, …)
8
- - automatic tool wiring from:
9
- - a **Workflow** (tasks as function tools)
10
- - a **KnowledgeBase** (databases as function tools)
11
- - a convenient Ruby API for structured outputs (`json_format`, `iterate`, `iterate_dictionary`)
12
- - optional delegation to other agents (multi-agent control loops)
13
-
14
- Related docs:
15
-
16
- - `USER_GUIDE.md` — step-by-step introduction to chats, agents, workflows, and multi-agent patterns
17
- - `../python/README.md` — Python SDK for Scout-AI chats and agents
18
- - `doc/LLM.md` — `LLM.ask`, endpoints/backends, tool calling
19
- - `doc/Chat.md` — chat file roles/options (including `tool`, `task`, `mcp`, `previous_response_id`)
20
-
21
- ---
22
-
23
- ## 1. Quick start
24
-
25
- ### 1.1 Minimal stateful conversation
26
-
27
- ```ruby
28
- require 'scout-ai'
29
-
30
- agent = LLM::Agent.new(endpoint: :nano)
31
- agent.start_chat.system "You are a helpful assistant"
32
-
33
- agent.start # create a new conversation branch
34
- agent.user "Say hi" # append to current_chat
35
- puts agent.chat # ask + append assistant reply
36
- ```
37
-
38
- ### 1.2 Factory shortcut
39
-
40
- ```ruby
41
- agent = LLM.agent(endpoint: :ollama, model: 'llama3.1')
42
- ```
43
-
44
- ---
45
-
46
- ## 2. Conversation lifecycle
47
-
48
- Agents maintain two chats:
49
-
50
- ### `start_chat`
51
-
52
- The “base” chat.
53
-
54
- - It is where you put messages that should always be present (system policy, examples, shared context).
55
- - It is **not automatically sent** unless you create a current chat from it via `start`.
56
-
57
- ### `start(chat=nil)`
58
-
59
- - `start()` with no argument:
60
- - branches `start_chat` (non-destructive copy)
61
- - stores it as `current_chat`
62
- - `start(chat)` with a `Chat` or `Array`:
63
- - adopts that as the `current_chat`
64
-
65
- ### `current_chat`
66
-
67
- The active conversation.
68
-
69
- ### Common pitfall
70
-
71
- If you call `agent.ask(...)` directly with your own messages array, the Agent will not automatically prepend `start_chat`. The simplest “normal” pattern is:
72
-
73
- ```ruby
74
- agent.start
75
- agent.user "..."
76
- agent.chat
77
- ```
78
-
79
- ---
80
-
81
- ## 3. Agent forwards the Chat DSL
82
-
83
- `LLM::Agent` forwards unknown methods to `current_chat` (via `method_missing`).
84
-
85
- So you can use the chat builder methods directly:
86
-
87
- ```ruby
88
- agent.system "You are a domain expert"
89
- agent.user "Summarize this file"
90
- agent.file "paper.md"
91
- agent.image "figure.png"
92
- agent.pdf "supplement.pdf"
93
- ```
94
-
95
- All the special chat-file roles described in `doc/Chat.md` work the same way from an Agent: `import`, `continue`, `tool`, `task`, `mcp`, etc. These use the `message` builder, which is a more general way to add messages to the chat. These are equivalent:
96
-
97
- ```ruby
98
- agent.pdf "supplement.pdf"
99
- agent.message :pdf, "supplement.pdf"
100
- ```
101
- ---
102
-
103
- ## 4. Tool wiring (Workflow + KnowledgeBase)
104
-
105
- ### 4.1 Workflow tools
106
-
107
- If an Agent has a `workflow`, all exported tasks are exposed as callable tools when the model supports function calling.
108
-
109
- ```ruby
110
- agent = LLM::Agent.new(workflow: 'Baking', endpoint: :nano)
111
- agent.start
112
- agent.user "Bake muffins using the tool"
113
- puts agent.chat
114
- ```
115
-
116
- Internally:
117
-
118
- - `LLM.workflow_tools(workflow)` produces one tool definition per task.
119
- - When the model calls a function, `LLM.process_calls` executes it via `LLM.call_workflow`.
120
-
121
- ### 4.2 KnowledgeBase tools
122
-
123
- If an Agent has a `knowledge_base`, each database is exposed as a callable tool.
124
-
125
- - For a database `brothers`, the model can call `brothers(entities: [...])`.
126
- - If the database has fields, an additional tool `brothers_association_details` is exposed.
127
-
128
- ### 4.3 Tool merging rules (important nuance)
129
-
130
- Tools come from multiple places:
131
-
132
- 1) tools passed to `ask(..., tools: ...)`
133
- 2) tools stored in `agent.other_options[:tools]`
134
- 3) tools auto-exported from `workflow` / `knowledge_base`
135
-
136
- The Agent merges them roughly as:
137
-
138
- - start with `options[:tools]` (or `{}`)
139
- - merge `other_options[:tools]`
140
- - merge workflow and knowledge base tools
141
-
142
- If the same tool name appears multiple times, later merges override earlier ones.
143
-
144
- ### 4.4 Workflows as toolkits (and how they relate to "skills")
145
-
146
- In Scout-AI, a `Workflow` is the main executable toolkit abstraction. When you attach a workflow to an Agent or expose it in a chat with `tool:`, its exported tasks become callable tools with typed input schemas.
147
-
148
- If you come from systems that talk about "skills", the closest Scout-AI equivalents are usually:
149
-
150
- - a `Workflow` for executable capabilities
151
- - a `start_chat` file for instructions, conventions, and examples
152
- - an optional `KnowledgeBase` for retrieval tools
153
- - an agent directory that bundles those pieces together
154
-
155
- So a small agent directory often plays the role of a skill package, but with stronger typing and provenance because the executable part is a real Scout Workflow.
156
-
157
- ---
158
-
159
- ## 5. Asking vs chatting
160
-
161
- ### `ask(messages, options={})`
162
-
163
- Low-level: calls `LLM.ask(...)` with Agent defaults merged.
164
-
165
- - returns a **string** by default
166
- - returns a **message trace** if `return_messages: true`
167
-
168
- ### `chat(options={})`
169
-
170
- High-level “stateful” method:
171
-
172
- - calls `ask(current_chat, return_messages: true)`
173
- - appends returned messages onto `current_chat`
174
- - returns the assistant content (the last assistant message)
175
-
176
- ---
177
-
178
- ## 6. Structured outputs
179
-
180
- ### `json`
181
-
182
- Sets chat format to JSON and parses the response:
183
-
184
- ```ruby
185
- agent.start
186
- agent.user "Return {\"content\": [\"a\",\"b\"]}"
187
- pp agent.json
188
- ```
189
-
190
- If the returned JSON is exactly `{"content": ...}`, the helper returns the inner `content`.
191
-
192
- ### `json_format(schema_hash)`
193
-
194
- Requests a JSON response constrained by a schema (supported best by the Responses backend).
195
-
196
- ```ruby
197
- schema = {
198
- name: 'answer',
199
- type: 'object',
200
- properties: {
201
- judgement: { type: :boolean },
202
- notes: { type: :string, default: "" }
203
- },
204
- required: [:judgement],
205
- additionalProperties: false
206
- }
207
-
208
- agent.start
209
- agent.user "Is this funny?"
210
- pp agent.json_format(schema)
211
- ```
212
-
213
- ---
214
-
215
- ## 7. Iteration helpers (programmatic control loops)
216
-
217
- These helpers are designed for “agentic scripts” where you want the model to produce a list/dictionary and then iterate in Ruby.
218
-
219
- ### `iterate(prompt=nil) { |item| ... }`
220
-
221
- - forces `endpoint :responses`
222
- - requests JSON schema `{content: [string, ...]}`
223
- - yields each item
224
- - resets `format` back to `:text`
225
-
226
- ```ruby
227
- agent = LLM.agent
228
- agent.iterate("List 3 next actions") do |action|
229
- puts "- #{action}"
230
- end
231
- ```
232
-
233
- ### `iterate_dictionary(prompt=nil) { |k,v| ... }`
234
-
235
- - requests a JSON object whose values are strings (`additionalProperties: {type: :string}`)
236
-
237
- ```ruby
238
- agent.iterate_dictionary("Return a dict of tool_name => what it does") do |name, desc|
239
- puts "#{name}: #{desc}"
240
- end
241
- ```
242
-
243
- ---
244
-
245
- ## 8. Delegation (multi-agent wiring)
246
-
247
- `Agent#delegate` registers another agent as a **tool**.
248
-
249
- The tool name becomes:
250
-
251
- ```text
252
- hand_off_to_<name>
253
- ```
254
-
255
- The default schema expects:
256
-
257
- - `message` (required)
258
- - `new_conversation` (optional, default false)
259
-
260
- Example:
261
-
262
- ```ruby
263
- joker = LLM.agent(endpoint: :nano)
264
- joker.start_chat.system "You only answer with knock knock jokes"
265
-
266
- judge = LLM.agent(endpoint: :nano, format: { judgement: :boolean })
267
- judge.start_chat.system "Judge if a joke is funny"
268
-
269
- supervisor = LLM.agent(endpoint: :nano)
270
- supervisor.start_chat.system "Use delegated agents to do the work"
271
-
272
- supervisor.delegate(joker, :joker, "Generate jokes")
273
- supervisor.delegate(judge, :judge, "Evaluate jokes")
274
-
275
- supervisor.start
276
- supervisor.user "Try up to 5 jokes until judged funny"
277
- puts supervisor.chat
278
- ```
279
-
280
- ---
281
-
282
- ## 9. Loading an Agent (agent directories)
283
-
284
- Agents can be loaded by name or from a directory.
285
-
286
- ### `LLM::Agent.load_agent(name)`
287
-
288
- Resolution logic (simplified):
289
-
290
- - if `name` is a path:
291
- - if it is a directory with `agent/*.rb` it loads that script
292
- - otherwise it loads the directory as an agent directory
293
- - otherwise it looks under the standard Scout paths:
294
- - workflows (`Scout.workflows[name]`)
295
- - agent dirs (`Scout.var.Agent[name]`)
296
- - chat dirs (`Scout.chats[name]`)
297
-
298
- ### Agent directory layout
299
-
300
- If you create an agent directory, these files are detected automatically:
301
-
302
- ```text
303
- <agent_dir>/workflow.rb # optional
304
- <agent_dir>/knowledge_base/ # optional
305
- <agent_dir>/start_chat # optional chat file
306
- <agent_dir>/python/*.py # optional Python-backed workflow tasks
307
- ```
308
-
309
- If `workflow.rb` is absent but the agent directory contains `python/*.py`, Scout-AI auto-loads those files through `PythonWorkflow.load_directory(..., 'ScoutAgent')` and treats them as workflow tasks for the agent.
310
-
311
- If `start_chat` is not present but a workflow exists, the Agent will create a base chat containing an `introduce: <workflow>` message (workflow documentation injected).
312
-
313
- See `doc/PythonAgentTasks.md` for the Python-backed agent pattern.
314
-
315
- ---
316
-
317
- ## 10. Advanced: workflow-provided `ask` task
318
-
319
- If the Agent’s workflow defines a task named `ask`, `Agent#ask` can delegate the entire LLM interaction to that workflow task:
320
-
321
- - the Agent passes `chat: Chat.print(messages)` as input
322
- - the workflow task can implement a custom control loop, custom tool execution, etc.
323
-
324
- This is the main Scout-AI escape hatch for building agent strategies as workflows. It is especially useful when you want Ruby to control a multi-step pattern such as:
325
-
326
- - intake -> plan -> execute -> review
327
- - search only when blocked
328
- - validation chains
329
- - artifact-first multi-agent collaboration
330
-
331
- In that style, the workflow owns the orchestration and writes artifacts with normal Scout `Step#file` helpers, while the individual agents stay role-specialized.
332
-
333
- ---
334
-
335
- ## 11. Error handling
336
-
337
- Set `agent.process_exception` to a Proc to intercept exceptions raised during ask/chat.
338
-
339
- If the Proc returns truthy, the call is retried.
340
-
341
- ---
342
-
343
- ## 12. CLI integration
344
-
345
- Agents are primarily used from:
346
-
347
- ```bash
348
- scout-ai agent ask <agent_name> your question
349
- scout-ai agent ask -c my.chat <agent_name> continue this conversation
350
- ```
351
-
352
- Note how the question does not need to be in quotes.
353
-
354
- See `doc/Chat.md` for chat file roles and `doc/LLM.md` for endpoint configuration.