scout-ai 1.2.3 → 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.
- checksums.yaml +4 -4
- data/.vimproject +138 -50
- data/README.md +171 -290
- data/Rakefile +17 -1
- data/VERSION +1 -1
- data/doc/Improvements.md +325 -0
- data/doc/StartHere.md +110 -0
- data/doc/developer/Architecture.md +126 -0
- data/doc/developer/Backends.md +199 -0
- data/doc/developer/ChatLifecycle.md +183 -0
- data/doc/developer/DelegationInternals.md +295 -0
- data/doc/developer/DesignPrinciples.md +245 -0
- data/doc/developer/PromptProcessing.md +292 -0
- data/doc/developer/Provenance.md +317 -0
- data/doc/user/BuildingAgents.md +345 -0
- data/doc/user/Cookbook.md +333 -0
- data/doc/user/CoreConcepts.md +181 -0
- data/doc/user/Delegation.md +191 -0
- data/doc/user/GettingStarted.md +159 -0
- data/doc/user/ManagingContext.md +163 -0
- data/doc/user/MultiAgentWorkflows.md +256 -0
- data/doc/user/Python.md +159 -0
- data/doc/user/RunningInference.md +200 -0
- data/doc/user/ToolCalling.md +193 -0
- data/doc/user/WritingChats.md +197 -0
- data/lib/scout/llm/agent/chat.rb +61 -11
- data/lib/scout/llm/agent/delegate.rb +274 -65
- data/lib/scout/llm/agent/iterate.rb +2 -2
- data/lib/scout/llm/agent/save.rb +273 -0
- data/lib/scout/llm/agent/workflow.rb +164 -0
- data/lib/scout/llm/agent.rb +86 -61
- data/lib/scout/llm/ask.rb +62 -17
- data/lib/scout/llm/backends/anthropic.rb +9 -2
- data/lib/scout/llm/backends/bedrock.rb +15 -3
- data/lib/scout/llm/backends/default.rb +183 -99
- data/lib/scout/llm/backends/glm.rb +58 -0
- data/lib/scout/llm/backends/huggingface.rb +196 -26
- data/lib/scout/llm/backends/ollama.rb +13 -1
- data/lib/scout/llm/backends/openai.rb +0 -2
- data/lib/scout/llm/backends/openwebui.rb +20 -13
- data/lib/scout/llm/backends/relay.rb +22 -22
- data/lib/scout/llm/backends/responses.rb +1 -1
- data/lib/scout/llm/chat/agent_meta.rb +264 -0
- data/lib/scout/llm/chat/annotation.rb +39 -10
- data/lib/scout/llm/chat/parse.rb +28 -6
- data/lib/scout/llm/chat/persist.rb +25 -0
- data/lib/scout/llm/chat/process/clear.rb +41 -6
- data/lib/scout/llm/chat/process/files.rb +21 -6
- data/lib/scout/llm/chat/process/meta.rb +421 -34
- data/lib/scout/llm/chat/process/options.rb +21 -1
- data/lib/scout/llm/chat/process/tools.rb +56 -15
- data/lib/scout/llm/chat/process.rb +4 -0
- data/lib/scout/llm/chat/prompt/shorten_tools.rb +125 -0
- data/lib/scout/llm/chat/prompt/shorten_tools_epoch.rb +365 -0
- data/lib/scout/llm/chat/prompt.rb +48 -0
- data/lib/scout/llm/chat/provenance.rb +775 -0
- data/lib/scout/llm/chat/tool_calls.rb +76 -0
- data/lib/scout/llm/chat.rb +18 -2
- data/lib/scout/llm/embed.rb +11 -3
- data/lib/scout/llm/image.rb +86 -0
- data/lib/scout/llm/mcp.rb +10 -2
- data/lib/scout/llm/rag.rb +3 -3
- data/lib/scout/llm/tools/call.rb +160 -11
- data/lib/scout/llm/tools/knowledge_base.rb +1 -1
- data/lib/scout/llm/tools/workflow.rb +32 -16
- data/lib/scout/model/python/huggingface/causal.rb +23 -5
- data/lib/scout/model/python/huggingface.rb +2 -1
- data/lib/scout-ai.rb +1 -0
- data/python/README.md +197 -14
- data/python/scout_ai/huggingface/eval.py +245 -34
- data/python/tests/test_huggingface_eval.py +58 -0
- data/research/ChatAnalyst-required-changes.md +167 -0
- data/research/agent-delegation-analysis.md +810 -0
- data/research/agent-meta-provenance-integration-plan.md +622 -0
- data/research/agent-workflow-analysis.md +1120 -0
- data/research/backends-analysis.md +836 -0
- data/research/chat-core-analysis.md +946 -0
- data/research/chatanalyst-provenance/00-baseline.md +30 -0
- data/research/chatanalyst-provenance/01-repo-map.md +60 -0
- data/research/chatanalyst-provenance/02-event-reconstruction.md +55 -0
- data/research/chatanalyst-provenance/03-duplication-evidence.md +45 -0
- data/research/chatanalyst-provenance/04-tooling-root-cause.md +57 -0
- data/research/chatanalyst-provenance/05-fix-plan.md +46 -0
- data/research/chatanalyst-provenance/07-critic-review.md +25 -0
- data/research/chatanalyst-provenance/final-report.md +45 -0
- data/research/chatanalyst-provenance/resumption.md +37 -0
- data/research/coding-philosophy-analysis.md +928 -0
- data/research/commands-analysis.md +947 -0
- data/research/multi-agent-patterns-analysis.md +853 -0
- data/research/prompt-strategies-analysis.md +630 -0
- data/research/prov-verbosity-fix-notes.md +77 -0
- data/research/provenance-analysis.md +469 -0
- data/research/provenance-navigation-design.md +640 -0
- data/research/synthesis-report.md +487 -0
- data/research/tools-system-analysis.md +779 -0
- data/scout-ai.gemspec +100 -11
- data/scout_commands/agent/ask +13 -3
- data/scout_commands/agent/kb +2 -0
- data/scout_commands/llm/ask +11 -4
- data/scout_commands/llm/md +76 -0
- data/scout_commands/llm/process_queries +48 -0
- data/scout_commands/llm/prov +602 -0
- data/scout_commands/llm/word +71 -0
- data/scout_commands/workflow/mcp +43 -0
- data/share/word/reference.docx +0 -0
- data/test/etc/AI/mock.yaml +11 -0
- data/test/fixtures/backends/anthropic.json +19 -0
- data/test/fixtures/backends/anthropic_tool_use.json +24 -0
- data/test/fixtures/backends/bedrock.json +8 -0
- data/test/fixtures/backends/bedrock_embedding.json +3 -0
- data/test/fixtures/backends/bedrock_tool_use.json +17 -0
- data/test/fixtures/backends/ollama.json +16 -0
- data/test/fixtures/backends/ollama_tool_call.json +27 -0
- data/test/fixtures/backends/openai_chat.json +21 -0
- data/test/fixtures/backends/openai_chat_tool_call.json +31 -0
- data/test/fixtures/backends/responses.json +33 -0
- data/test/fixtures/backends/responses_tool_call.json +28 -0
- data/test/integration/README.md +32 -0
- data/test/integration/scout/llm/backends/test_endpoints.rb +34 -0
- data/test/integration/scout/llm/backends/test_openwebui.rb +61 -0
- data/test/integration/scout/llm/backends/test_relay.rb +52 -0
- data/test/integration/scout/llm/test_infrastructure.rb +74 -0
- data/test/{scout → integration/scout}/llm/test_mcp.rb +1 -1
- data/test/integration/scout/llm/tools/test_mcp.rb +42 -0
- data/test/integration/scout/model/test_base.rb +91 -0
- data/test/scout/llm/agent/test_chat.rb +8 -2
- data/test/scout/llm/agent/test_save.rb +413 -0
- data/test/scout/llm/agent/test_workflow.rb +110 -0
- data/test/scout/llm/backends/test_anthropic.rb +93 -10
- data/test/scout/llm/backends/test_bedrock.rb +118 -2
- data/test/scout/llm/backends/test_huggingface.rb +137 -42
- data/test/scout/llm/backends/test_ollama.rb +70 -20
- data/test/scout/llm/backends/test_openwebui.rb +42 -40
- data/test/scout/llm/backends/test_relay.rb +4 -2
- data/test/scout/llm/chat/agent_meta_fixtures.rb +131 -0
- data/test/scout/llm/chat/process/test_meta.rb +518 -0
- data/test/scout/llm/chat/process/test_normalize_usage.rb +183 -0
- data/test/scout/llm/chat/test_agent_meta.rb +357 -0
- data/test/scout/llm/chat/test_agent_meta_provenance.rb +467 -0
- data/test/scout/llm/chat/test_agent_meta_tokens.rb +594 -0
- data/test/scout/llm/chat/test_parse.rb +70 -15
- data/test/scout/llm/chat/test_prov_cli.rb +274 -0
- data/test/scout/llm/chat/test_provenance.rb +240 -0
- data/test/scout/llm/chat/test_tool_calls.rb +38 -0
- data/test/scout/llm/test_agent.rb +13 -36
- data/test/scout/llm/test_ask.rb +75 -52
- data/test/scout/llm/test_chat.rb +107 -13
- data/test/scout/llm/test_embed.rb +48 -0
- data/test/scout/llm/test_rag.rb +23 -16
- data/test/scout/llm/test_tools.rb +12 -1
- data/test/scout/llm/tools/test_knowledge_base.rb +0 -1
- data/test/scout/llm/tools/test_mcp.rb +5 -3
- data/test/scout/llm/tools/test_workflow.rb +23 -2
- data/test/scout/model/python/huggingface/causal/test_next_token.rb +11 -5
- data/test/scout/model/python/huggingface/test_causal.rb +9 -3
- data/test/scout/model/python/huggingface/test_classification.rb +11 -2
- data/test/scout/model/python/test_torch.rb +2 -0
- data/test/scout/model/python/torch/test_helpers.rb +4 -0
- data/test/scout/model/test_base.rb +4 -2
- data/test/support/availability.rb +231 -0
- data/test/support/fake_clients.rb +138 -0
- data/test/support/fixtures.rb +21 -0
- data/test/support/infrastructure_probes.rb +136 -0
- data/test/support/mock_backend.rb +215 -0
- data/test/test_helper.rb +32 -2
- metadata +99 -10
- data/doc/Agent.md +0 -327
- data/doc/Chat.md +0 -458
- data/doc/LLM.md +0 -340
- data/doc/RAG.md +0 -129
- data/scout_commands/documenter +0 -148
- data/test/scout/llm/backends/test_openai.rb +0 -192
- data/test/scout/llm/backends/test_responses.rb +0 -238
- data/test/scout/llm/test_parse.rb +0 -98
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Cookbook
|
|
2
|
+
|
|
3
|
+
This page collects small, ready-to-use examples for common Scout-AI tasks. It
|
|
4
|
+
is intended for workflow authors who want quick, practical patterns.
|
|
5
|
+
|
|
6
|
+
**You should read this if:** you want copy-paste examples for specific tasks.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Quick one-shot question
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
scout-ai llm ask "What is the capital of France?"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
agent = LLM.agent(endpoint: :openai)
|
|
18
|
+
agent.start
|
|
19
|
+
agent.user "What is the capital of France?"
|
|
20
|
+
puts agent.chat
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Simple assistant with a system prompt
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
agent = LLM.agent(endpoint: :openai)
|
|
29
|
+
agent.start_chat.system "You are a helpful assistant. Answer concisely."
|
|
30
|
+
agent.start
|
|
31
|
+
agent.user "Explain recursion in one sentence."
|
|
32
|
+
puts agent.chat
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Chat file with endpoint and model
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
endpoint: anthropic
|
|
41
|
+
model: claude-sonnet-4-20250514
|
|
42
|
+
|
|
43
|
+
system:
|
|
44
|
+
|
|
45
|
+
You are a code reviewer.
|
|
46
|
+
|
|
47
|
+
user:
|
|
48
|
+
|
|
49
|
+
Review this function for bugs:
|
|
50
|
+
|
|
51
|
+
def add(a, b)
|
|
52
|
+
a + b
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Save as `review.chat` and run:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
scout-ai llm ask -c review.chat
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Agent with a workflow tool
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
agent = LLM.agent(endpoint: :openai)
|
|
68
|
+
agent.workflow do
|
|
69
|
+
task :search => :string do |query|
|
|
70
|
+
"Results for '#{query}': ..."
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
agent.start
|
|
74
|
+
agent.user "Search for Ruby tutorials."
|
|
75
|
+
puts agent.chat
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Agent from a directory
|
|
81
|
+
|
|
82
|
+
Directory layout:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Agent/
|
|
86
|
+
Greeter/
|
|
87
|
+
start_chat
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`start_chat`:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
system:
|
|
94
|
+
|
|
95
|
+
You are a friendly greeter. Always greet by name.
|
|
96
|
+
|
|
97
|
+
user:
|
|
98
|
+
|
|
99
|
+
Hello!
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Use it:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
agent = LLM.load_agent('Greeter')
|
|
106
|
+
agent.start
|
|
107
|
+
agent.user "Hi, I'm Alice."
|
|
108
|
+
puts agent.chat
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
From CLI:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
scout-ai agent ask Greeter "Hi, I'm Alice."
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Structured JSON output
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
agent = LLM.agent(endpoint: :openai)
|
|
123
|
+
agent.start
|
|
124
|
+
agent.user 'Return a JSON object: {"name": "Ruby", "type": "language", "year": 1995}'
|
|
125
|
+
result = agent.json
|
|
126
|
+
puts result["name"] # => "Ruby"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
With a schema:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
schema = {
|
|
133
|
+
name: 'evaluation',
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
score: { type: :integer },
|
|
137
|
+
feedback: { type: :string }
|
|
138
|
+
},
|
|
139
|
+
required: [:score, :feedback]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
agent.json_format(schema)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Importing a file into the conversation
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
system:
|
|
151
|
+
|
|
152
|
+
You are a document analyzer.
|
|
153
|
+
|
|
154
|
+
file: report.pdf
|
|
155
|
+
|
|
156
|
+
user:
|
|
157
|
+
|
|
158
|
+
Summarize the key findings.
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Multi-turn conversation
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
agent = LLM.agent(endpoint: :openai)
|
|
167
|
+
agent.start_chat.system "You are a patient tutor."
|
|
168
|
+
agent.start
|
|
169
|
+
|
|
170
|
+
agent.user "What is a variable?"
|
|
171
|
+
puts agent.chat
|
|
172
|
+
|
|
173
|
+
agent.user "How is it different from a constant?"
|
|
174
|
+
puts agent.chat # remembers the previous turn
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Delegation: orchestrator with specialist
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
# Load a specialist
|
|
183
|
+
searcher = LLM.load_agent('Searcher')
|
|
184
|
+
|
|
185
|
+
# Set up the orchestrator
|
|
186
|
+
orchestrator = LLM.load_agent('Manager')
|
|
187
|
+
orchestrator.socialize # model can call ask(agent: 'Searcher', prompt: ...)
|
|
188
|
+
|
|
189
|
+
orchestrator.start
|
|
190
|
+
orchestrator.user "Research the latest advances in protein folding."
|
|
191
|
+
orchestrator.chat
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Multi-agent pipeline in a workflow
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
module AnalysisPipeline
|
|
200
|
+
extend Workflow
|
|
201
|
+
include AgentWorkflow
|
|
202
|
+
|
|
203
|
+
chat_task :plan do |objective|
|
|
204
|
+
agent = self.agent('Planner', chat: chat)
|
|
205
|
+
agent.start
|
|
206
|
+
agent.user objective
|
|
207
|
+
agent.chat
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
chat_task :execute do |objective, plan|
|
|
211
|
+
agent = self.agent('Executor', chat: chat)
|
|
212
|
+
agent.start
|
|
213
|
+
agent.user "Objective: #{objective}\nPlan: #{plan}"
|
|
214
|
+
agent.chat
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
chat_task :review do |result|
|
|
218
|
+
agent = self.agent('Critic', chat: chat)
|
|
219
|
+
agent.start
|
|
220
|
+
agent.user "Review: #{result}"
|
|
221
|
+
agent.chat
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Python task in an agent
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
MyAgent/
|
|
232
|
+
├── start_chat
|
|
233
|
+
└── python/
|
|
234
|
+
└── analyze.py
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
# python/analyze.py
|
|
239
|
+
import scout
|
|
240
|
+
|
|
241
|
+
def word_count(text: str) -> dict:
|
|
242
|
+
"""
|
|
243
|
+
Count words in text.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
text: Input text.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Dictionary with word counts.
|
|
250
|
+
"""
|
|
251
|
+
words = text.lower().split()
|
|
252
|
+
counts = {}
|
|
253
|
+
for w in words:
|
|
254
|
+
counts[w] = counts.get(w, 0) + 1
|
|
255
|
+
return counts
|
|
256
|
+
|
|
257
|
+
scout.task(word_count)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
agent = LLM::Agent.load_agent('MyAgent', endpoint: :openai)
|
|
262
|
+
agent.start
|
|
263
|
+
agent.user "Count the words in: the quick brown fox"
|
|
264
|
+
puts agent.chat
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Error handling with retry
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
agent = LLM.agent(endpoint: :openai)
|
|
273
|
+
agent.process_exception = Proc.new do |e|
|
|
274
|
+
if e.message =~ /rate limit/i
|
|
275
|
+
sleep 10
|
|
276
|
+
true # retry
|
|
277
|
+
else
|
|
278
|
+
false # re-raise
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Clearing context between phases
|
|
286
|
+
|
|
287
|
+
```text
|
|
288
|
+
system:
|
|
289
|
+
|
|
290
|
+
You are a project manager.
|
|
291
|
+
|
|
292
|
+
user:
|
|
293
|
+
|
|
294
|
+
Phase 1: Analyze requirements.
|
|
295
|
+
|
|
296
|
+
clear:
|
|
297
|
+
|
|
298
|
+
user:
|
|
299
|
+
|
|
300
|
+
Phase 2: Design the system. (Phase 1 context is cleared)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Knowledge base tool
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
system:
|
|
309
|
+
|
|
310
|
+
You are a bioinformatics assistant.
|
|
311
|
+
|
|
312
|
+
kb: gene_db [genes proteins diseases]
|
|
313
|
+
|
|
314
|
+
user:
|
|
315
|
+
|
|
316
|
+
What proteins are associated with BRCA1?
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## MCP tool
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
system:
|
|
325
|
+
|
|
326
|
+
You have access to an external search service.
|
|
327
|
+
|
|
328
|
+
mcp: https://api.example.com/mcp/ [search]
|
|
329
|
+
|
|
330
|
+
user:
|
|
331
|
+
|
|
332
|
+
Search for recent papers on climate change.
|
|
333
|
+
```
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Core Concepts
|
|
2
|
+
|
|
3
|
+
This page gives you a conceptual map of Scout-AI's main abstractions. It is
|
|
4
|
+
intended for workflow authors — both human developers and coding agents — who
|
|
5
|
+
want to understand what Scout-AI offers before diving into specifics.
|
|
6
|
+
|
|
7
|
+
**You should read this if:** you have installed Scout-AI and want a high-level
|
|
8
|
+
orientation before building anything.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The big picture
|
|
13
|
+
|
|
14
|
+
Scout-AI sits between your application code and an LLM provider. It gives you
|
|
15
|
+
four building blocks:
|
|
16
|
+
|
|
17
|
+
| Concept | What it is | What problem it solves |
|
|
18
|
+
|---------|-----------|----------------------|
|
|
19
|
+
| **Chat** | A conversation format (plain text on disk, Array of hashes in memory) | Reproducibility: every conversation is inspectable, editable, and versionable |
|
|
20
|
+
| **Agent** | A stateful wrapper around a Chat with persistent defaults and tools | Persistence: your agent keeps its system prompt, tools, and options across conversations |
|
|
21
|
+
| **Tools** | Callable functions the LLM can invoke during inference | Grounding: the model can query real data and run real code instead of hallucinating |
|
|
22
|
+
| **Inference Endpoint** | A named configuration for a provider + model + credentials | Portability: switch between OpenAI, Anthropic, Ollama, etc. without changing application code |
|
|
23
|
+
|
|
24
|
+
These compose. An **Agent** has a **Chat** and a set of **Tools**, and it sends
|
|
25
|
+
the chat to an **Inference Endpoint** to get a response.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Chat: the conversation format
|
|
30
|
+
|
|
31
|
+
A Chat is simultaneously two things:
|
|
32
|
+
|
|
33
|
+
1. **On disk:** a plain-text file where each line block starts with a role
|
|
34
|
+
name and colon (`system:`, `user:`, `assistant:`, etc.).
|
|
35
|
+
2. **In memory:** an Array of message hashes, each with `role:` and `content:`
|
|
36
|
+
keys.
|
|
37
|
+
|
|
38
|
+
This dual nature is the foundation of Scout-AI's reproducibility. You can
|
|
39
|
+
write a conversation by hand in a text editor, run it through the CLI, inspect
|
|
40
|
+
the output, and feed it back as input.
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
system:
|
|
44
|
+
|
|
45
|
+
You are a helpful assistant.
|
|
46
|
+
|
|
47
|
+
user:
|
|
48
|
+
|
|
49
|
+
Hello!
|
|
50
|
+
|
|
51
|
+
assistant:
|
|
52
|
+
|
|
53
|
+
Hi there! How can I help?
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Key idea:** Every conversation — whether a one-shot CLI question, an agent
|
|
57
|
+
session, or the output of a workflow job — is serialized to the same
|
|
58
|
+
plain-text format. This means you can always inspect, edit, and reproduce
|
|
59
|
+
what happened.
|
|
60
|
+
|
|
61
|
+
→ See [WritingChats.md](WritingChats.md) for the full format.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Agent: the stateful wrapper
|
|
66
|
+
|
|
67
|
+
An Agent bundles a Chat with persistent configuration:
|
|
68
|
+
|
|
69
|
+
- **A start chat** — the system prompt, tool declarations, file imports, and
|
|
70
|
+
other seed messages that prefix every new conversation.
|
|
71
|
+
- **Tools** — workflows, knowledge bases, or MCP tools the agent can call.
|
|
72
|
+
- **Options** — endpoint, model, format, and other defaults.
|
|
73
|
+
|
|
74
|
+
Agents are **named directories**. You create an agent by making a directory
|
|
75
|
+
under your Scout Agent path and adding a `start_chat` file:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Agent/
|
|
79
|
+
Researcher/
|
|
80
|
+
start_chat # system prompt + tool declarations
|
|
81
|
+
workflow.rb # optional: Scout workflow providing tools
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Once defined, an agent is invoked by name:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
scout-ai agent ask Researcher "Find papers about protein folding."
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Agents can also **delegate** to other agents — an orchestrator agent can hand
|
|
91
|
+
off sub-tasks to specialists, each with its own tools and persona.
|
|
92
|
+
|
|
93
|
+
→ See [BuildingAgents.md](BuildingAgents.md) for agent lifecycle and DSL.
|
|
94
|
+
→ See [Delegation.md](Delegation.md) for multi-agent delegation.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Tools: grounding the model
|
|
99
|
+
|
|
100
|
+
Tools let the LLM call functions during inference. Scout-AI supports several
|
|
101
|
+
kinds:
|
|
102
|
+
|
|
103
|
+
| Tool source | How you declare it | What it gives the model |
|
|
104
|
+
|-------------|-------------------|----------------------|
|
|
105
|
+
| **Scout Workflow** | `tool:` or `introduce:` in chat, or auto-wired from agent workflow | Tasks with typed inputs/outputs become callable functions |
|
|
106
|
+
| **Knowledge Base** | `kb:` in chat | Database lookups (gene→protein, drug→disease, etc.) |
|
|
107
|
+
| **MCP Server** | `mcp:` in chat | Any MCP-compatible external tool |
|
|
108
|
+
|
|
109
|
+
The model sees a tool definition (name, description, JSON Schema parameters),
|
|
110
|
+
decides when to call it, and receives the result as a tool-return message that
|
|
111
|
+
is appended to the conversation.
|
|
112
|
+
|
|
113
|
+
→ See [ToolCalling.md](ToolCalling.md) for the full tool system.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Inference endpoint: provider abstraction
|
|
118
|
+
|
|
119
|
+
An **endpoint** is a named bundle of provider + model + credentials. You
|
|
120
|
+
configure endpoints once and reference them by name everywhere:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Use the 'anthropic' endpoint for this conversation
|
|
124
|
+
scout-ai llm ask -e anthropic "Hello"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Scout-AI supports OpenAI, Anthropic, Ollama, vLLM, and other OpenAI-compatible
|
|
128
|
+
providers. Endpoints are provider-agnostic from the application's perspective —
|
|
129
|
+
you write your agent once and switch models by changing the endpoint name.
|
|
130
|
+
|
|
131
|
+
→ See [RunningInference.md](RunningInference.md) for endpoint configuration
|
|
132
|
+
and CLI usage.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## How they compose
|
|
137
|
+
|
|
138
|
+
Here is the typical flow when an agent answers a question:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
User question
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
Agent receives question
|
|
145
|
+
│
|
|
146
|
+
├── Appends to current_chat (a Chat object)
|
|
147
|
+
├── Sends chat to inference endpoint
|
|
148
|
+
│ │
|
|
149
|
+
│ ├── Model responds with text → done
|
|
150
|
+
│ └── Model calls a tool → execute tool, append result, re-send
|
|
151
|
+
│
|
|
152
|
+
└── Returns final answer
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The tool-calling loop is automatic: if the model calls a tool, Scout-AI
|
|
156
|
+
executes it, append the result, and re-sends the conversation until the model
|
|
157
|
+
responds with plain text.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## When to use what
|
|
162
|
+
|
|
163
|
+
| If you want to... | Use... |
|
|
164
|
+
|-------------------|--------|
|
|
165
|
+
| Ask a one-off question | `scout-ai llm ask` |
|
|
166
|
+
| Run a saved conversation | `scout-ai llm ask -c file.chat` |
|
|
167
|
+
| Create a reusable persona with tools | An Agent (directory with `start_chat`) |
|
|
168
|
+
| Give the model data to query | Knowledge Base tools (`kb:`) |
|
|
169
|
+
| Give the model code to run | Workflow tools (`tool:` / `introduce:`) |
|
|
170
|
+
| Use external tools | MCP (`mcp:`) |
|
|
171
|
+
| Build multi-agent systems | Delegation (`socialize` / `delegate`) |
|
|
172
|
+
| Build reproducible pipelines | AgentWorkflow (Scout Workflow + Agent) |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Next steps
|
|
177
|
+
|
|
178
|
+
- [WritingChats.md](WritingChats.md) — master the chat-file format.
|
|
179
|
+
- [BuildingAgents.md](BuildingAgents.md) — create your first agent.
|
|
180
|
+
- [ToolCalling.md](ToolCalling.md) — wire up tools.
|
|
181
|
+
- [Delegation.md](Delegation.md) — build multi-agent systems.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Delegation
|
|
2
|
+
|
|
3
|
+
This page explains how one Scout-AI agent can delegate work to other agents.
|
|
4
|
+
It is intended for workflow authors building multi-agent systems.
|
|
5
|
+
|
|
6
|
+
**You should read this if:** you want an orchestrator agent to hand off
|
|
7
|
+
sub-tasks to specialist agents.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What delegation is
|
|
12
|
+
|
|
13
|
+
Delegation lets one agent (the **caller**) invoke another agent (the
|
|
14
|
+
**specialist**) during inference. Each specialist has its own persona, tools,
|
|
15
|
+
and conversation history.
|
|
16
|
+
|
|
17
|
+
Scout-AI provides two delegation mechanisms:
|
|
18
|
+
|
|
19
|
+
| Mechanism | How it works | When to use |
|
|
20
|
+
|-----------|-------------|-------------|
|
|
21
|
+
| **`socialize` (the `ask` tool)** | Exposes a single generic `ask` tool. The model chooses which agent to call and what to ask. | When the model should decide when and whom to delegate to |
|
|
22
|
+
| **`delegate` (named hand-off tools)** | Pre-registers a `hand_off_to_<name>` tool for a specific agent. | When you want explicit, named delegation to specific agents |
|
|
23
|
+
|
|
24
|
+
Both mechanisms share a common concept: **inheritance modes** that control how
|
|
25
|
+
much context flows from caller to specialist.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Inheritance modes
|
|
30
|
+
|
|
31
|
+
When a specialist is invoked, you control how much of the caller's context it
|
|
32
|
+
receives:
|
|
33
|
+
|
|
34
|
+
| Mode | What the specialist gets | Use case |
|
|
35
|
+
|------|------------------------|----------|
|
|
36
|
+
| **`none`** | Only its own system prompt | Fully isolated sub-agent |
|
|
37
|
+
| **`tools`** *(default)* | Its own prompt + the caller's tools (but not conversation history) | Same capabilities, private history |
|
|
38
|
+
| **`conversation`** | Its own prompt + the caller's entire current conversation | Deep collaboration with shared context |
|
|
39
|
+
|
|
40
|
+
The specialist always gets **its own** system prompt first. Inherited context
|
|
41
|
+
is appended after.
|
|
42
|
+
|
|
43
|
+
> **Important:** The inheritance mode only applies when a conversation is first
|
|
44
|
+
> created. Subsequent turns in the same conversation reuse the accumulated
|
|
45
|
+
> history regardless of the mode.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## The `ask` tool (socialize)
|
|
50
|
+
|
|
51
|
+
`socialize` registers a single tool called `ask`. When the LLM calls it, it
|
|
52
|
+
provides an agent name and a prompt. Scout-AI loads the specialist, sends the
|
|
53
|
+
prompt, and returns the text answer.
|
|
54
|
+
|
|
55
|
+
### Enabling delegation
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
agent = LLM.load_agent('Orchestrator')
|
|
59
|
+
agent.socialize
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Now the model can call the `ask` tool during inference.
|
|
63
|
+
|
|
64
|
+
### What the model sees
|
|
65
|
+
|
|
66
|
+
The model sees a tool with these parameters:
|
|
67
|
+
|
|
68
|
+
| Parameter | Required | Description |
|
|
69
|
+
|-----------|----------|-------------|
|
|
70
|
+
| `agent` | Yes | Name of the specialist agent |
|
|
71
|
+
| `prompt` | Yes | Plain-text prompt for the specialist |
|
|
72
|
+
| `conversation` | No | Named conversation. Omit for one-shot; reuse to continue |
|
|
73
|
+
| `inherit` | No (default `tools`) | Context policy for new conversations |
|
|
74
|
+
|
|
75
|
+
### Conversation persistence
|
|
76
|
+
|
|
77
|
+
If the model provides a `conversation` name, the specialist keeps that
|
|
78
|
+
conversation across multiple calls:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Call 1: ask(agent="Worker", prompt="Do X", conversation="task_1")
|
|
82
|
+
Call 2: ask(agent="Worker", prompt="Now do Y", conversation="task_1")
|
|
83
|
+
# Worker remembers the full conversation from task_1
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Without a `conversation` name, each call is one-shot (the specialist answers
|
|
87
|
+
and the conversation is not reused).
|
|
88
|
+
|
|
89
|
+
Each delegated call leaves provenance evidence in the parent chat: the
|
|
90
|
+
specialist's token usage is recorded next to the tool answer, so token costs
|
|
91
|
+
can be traced afterwards with `scout-ai llm prov --evidence`. See the
|
|
92
|
+
[developer provenance guide](../developer/Provenance.md) for details.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Named hand-off tools (delegate)
|
|
97
|
+
|
|
98
|
+
`delegate` creates a specific tool for a pre-loaded agent:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
worker = LLM.load_agent('Worker')
|
|
102
|
+
agent.delegate(worker, :worker, "Delegate work to the Worker agent")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This creates a tool called `hand_off_to_worker`. The model calls it with a
|
|
106
|
+
`message` parameter.
|
|
107
|
+
|
|
108
|
+
### Custom delegation blocks
|
|
109
|
+
|
|
110
|
+
You can customize what happens when the tool is called:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
agent.delegate(worker, :worker, "Delegate to Worker") do |_name, params|
|
|
114
|
+
worker.start if params[:new_conversation]
|
|
115
|
+
worker.user params[:message]
|
|
116
|
+
worker.chat
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## `socialize` vs `delegate`
|
|
123
|
+
|
|
124
|
+
| Aspect | `socialize` | `delegate` |
|
|
125
|
+
|--------|------------|------------|
|
|
126
|
+
| Agent selection | Model chooses at call time | Hard-coded at registration |
|
|
127
|
+
| Tool count | One `ask` tool for all agents | One tool per agent |
|
|
128
|
+
| Conversation management | Named conversations via `conversation` param | Single conversation, resettable |
|
|
129
|
+
| Flexibility | High (model decides) | Controlled (you decide) |
|
|
130
|
+
|
|
131
|
+
**Rule of thumb:** Use `socialize` when the model should decide delegation
|
|
132
|
+
dynamically. Use `delegate` when you want explicit control over which agents
|
|
133
|
+
are available.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Security: safe delegation
|
|
138
|
+
|
|
139
|
+
When the model delegates, it provides a prompt string. Scout-AI uses a safe
|
|
140
|
+
method to send this prompt to the specialist — it adds it as a plain user
|
|
141
|
+
message, not as chat-file syntax. This prevents the model from injecting
|
|
142
|
+
control directives (like `tool:` or `system:`) into the specialist's
|
|
143
|
+
conversation.
|
|
144
|
+
|
|
145
|
+
This is important: it means delegation is safe even if the model produces
|
|
146
|
+
untrusted output.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Building a multi-agent system
|
|
151
|
+
|
|
152
|
+
A typical pattern:
|
|
153
|
+
|
|
154
|
+
1. **Create specialist agents** — each in its own directory with a `start_chat`
|
|
155
|
+
and optional workflow.
|
|
156
|
+
|
|
157
|
+
2. **Create an orchestrator agent** — its `start_chat` describes the task and
|
|
158
|
+
the available specialists.
|
|
159
|
+
|
|
160
|
+
3. **Enable delegation** — call `socialize` or `delegate` on the orchestrator.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# Orchestrator
|
|
164
|
+
orchestrator = LLM.load_agent('Manager')
|
|
165
|
+
orchestrator.socialize
|
|
166
|
+
orchestrator.start
|
|
167
|
+
orchestrator.user "Plan and execute a data analysis pipeline."
|
|
168
|
+
orchestrator.chat
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The model can now delegate sub-tasks to any specialist by name.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Common mistakes
|
|
176
|
+
|
|
177
|
+
- **Forgetting to call `socialize` or `delegate`**: Without one of these, the
|
|
178
|
+
model has no tool to delegate with.
|
|
179
|
+
- **Expecting specialists to share the orchestrator's tools by default**: Only
|
|
180
|
+
if `inherit` is `tools` or `conversation`. With `none`, specialists are
|
|
181
|
+
isolated.
|
|
182
|
+
- **Using `conversation: 'current'`**: This is a legacy value. Use explicit
|
|
183
|
+
conversation names or omit the parameter.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Next steps
|
|
188
|
+
|
|
189
|
+
- [MultiAgentWorkflows.md](MultiAgentWorkflows.md) — orchestration patterns.
|
|
190
|
+
- [BuildingAgents.md](BuildingAgents.md) — creating agents.
|
|
191
|
+
- [ManagingContext.md](ManagingContext.md) — how delegation affects context.
|