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/LLM.md DELETED
@@ -1,356 +0,0 @@
1
- # LLM
2
-
3
- This package provides a compact layer to talk to multiple LLM backends while keeping:
4
-
5
- - **configuration reproducible** (named endpoints)
6
- - **prompts reproducible** (chat files)
7
- - **tool calling first-class** (Workflow tasks, KnowledgeBase databases, MCP tools)
8
-
9
- The core entry points are:
10
-
11
- - `LLM.ask(question, options={}, &block)` — ask a model (optionally with tools)
12
- - `LLM.chat(...)` — compile/expand a chat file or message array into final messages
13
- - `LLM.options(messages)` — extract per-chat options (`endpoint`, `model`, `format`, …)
14
- - `LLM.print(messages)` — serialize messages back to chat-file format
15
- - `LLM.embed(text, options={})` — embeddings (not the focus of this document)
16
-
17
- If you are new to Scout-AI, start with `USER_GUIDE.md`.
18
-
19
- For stateful multi-turn conversations, see `doc/Agent.md`.
20
-
21
- For the full chat-file role reference, see `doc/Chat.md`.
22
-
23
- ---
24
-
25
- ## 0. Getting started path
26
-
27
- A good first learning sequence is:
28
-
29
- 1. create one endpoint under `~/.scout/etc/AI/<name>`
30
- 2. run `scout-ai llm ask -e <name> "Say hi"`
31
- 3. create a chat file and continue it with `-c`
32
- 4. load a stateful `LLM::Agent`
33
- 5. expose one Scout `Workflow` as a tool
34
-
35
- Use `USER_GUIDE.md` for the step-by-step walkthrough, then come back here for backend and CLI details.
36
-
37
- ---
38
-
39
- ## 1. Endpoints: configuring inference points (`~/.scout/etc/AI/<endpoint>`)
40
-
41
- Scout-AI uses **named endpoints** so colleagues can agree on short names like `nano` or `deep`.
42
-
43
- At runtime, `LLM.ask(..., endpoint: :nano)` will load:
44
-
45
- ```text
46
- ~/.scout/etc/AI/nano
47
- ```
48
-
49
- This file is YAML.
50
-
51
- Minimum useful keys:
52
-
53
- - `backend`: which backend to use (`responses`, `openai`, `anthropic`, `ollama`, `vllm`, `openwebui`, `bedrock`, `relay`)
54
- - `url`: server URL (only for backends that need one, e.g. `ollama`, `openwebui`)
55
- - `model`: backend-specific model id
56
-
57
- Any **additional keys are passed to the backend** and may be interpreted there.
58
-
59
- Examples:
60
-
61
- `~/.scout/etc/AI/nano`
62
-
63
- ```yaml
64
- backend: responses
65
- model: gpt-5-nano
66
- ```
67
-
68
- `~/.scout/etc/AI/deep`
69
-
70
- ```yaml
71
- backend: responses
72
- model: gpt-5
73
- reasoning_effort: high
74
- text_verbosity: high
75
- ```
76
-
77
- `~/.scout/etc/AI/ollama`
78
-
79
- ```yaml
80
- backend: ollama
81
- url: http://localhost:11434
82
- model: llama3.1
83
- ```
84
-
85
- How they are used:
86
-
87
- ```ruby
88
- LLM.ask "Say hi", endpoint: :nano
89
- LLM.ask "Think harder", endpoint: :deep
90
- ```
91
-
92
- From the CLI:
93
-
94
- ```bash
95
- scout-ai llm ask -e nano "Say hi"
96
- ```
97
-
98
- ---
99
-
100
- ## 2. `LLM.ask`: the core call
101
-
102
- ### 2.1 Input types
103
-
104
- The `question` argument can be:
105
-
106
- - a String (free text)
107
- - a **path to a chat file** (if the string looks like a filename and exists)
108
- - an Array of `{role:, content:}` hashes
109
- - a `Chat` (which is an annotated array)
110
-
111
- Internally `LLM.ask` always compiles input via `LLM.chat` first.
112
-
113
- ### 2.2 Option resolution order
114
-
115
- Options come from multiple places:
116
-
117
- 1) options extracted from the compiled messages (`LLM.options(messages)`)
118
- 2) options passed explicitly to `LLM.ask(question, options)`
119
- 3) endpoint YAML (`~/.scout/etc/AI/<endpoint>`) is merged in (as defaults)
120
-
121
- ### 2.3 Caching (`persist`)
122
-
123
- `LLM.ask` caches responses by default via `Persist.persist`.
124
-
125
- - default: `persist: true`
126
- - set `persist: false` to disable caching
127
-
128
- The cache key includes the endpoint name, messages, and most options.
129
-
130
- ### 2.4 Return value
131
-
132
- - default: returns the assistant content string
133
- - if `return_messages: true`: returns the full message trace (including tool calls)
134
-
135
- ---
136
-
137
- ## 3. Chat compilation (`LLM.chat`) and why it matters
138
-
139
- `LLM.chat` is what makes chat files powerful.
140
-
141
- It expands special roles **before** calling the backend:
142
-
143
- - imports: `import`, `continue`, `last`
144
- - pruning: `clear`, `skip`
145
- - workflow execution: `task`, `inline_task`, `exec_task`, and then `job`, `inline_job`
146
- - file inclusion: `file`, `directory`, `image`, `pdf`
147
-
148
- See `doc/Chat.md` for exact behavior and role syntax.
149
-
150
- ---
151
-
152
- ## 4. Backends (and what is “passed through”)
153
-
154
- Backends are selected by `backend:` (from endpoint YAML, env/config, or explicit options).
155
-
156
- Currently supported backends (see `lib/scout/llm/backends/`):
157
-
158
- - `responses` — OpenAI Responses API style (default). Supports:
159
- - JSON Schema responses (`format:` as schema)
160
- - multimodal (`image:`, `pdf:` roles)
161
- - session continuation (`previous_response_id`)
162
- - optional web search tool (`websearch`)
163
- - `openai` — OpenAI Chat Completions style
164
- - `anthropic` — Anthropic Messages API style
165
- - `ollama` — local Ollama server
166
- - `vllm` — vLLM OpenAI-compatible server
167
- - `openwebui` — OpenWebUI-compatible chat/completions
168
- - `bedrock` — AWS Bedrock runtime
169
- - `relay` — simple SCP/poll relay
170
-
171
- ### 4.1 Common backend options
172
-
173
- Many backends accept the same high-level keys:
174
-
175
- - `model`, `url`, `key`
176
- - `request_timeout`
177
- - `log_errors`
178
- - `tools`
179
-
180
- And then backend-specific keys are forwarded (for example: reasoning/text options for `responses`).
181
-
182
- ### 4.2 Responses session continuation (`previous_response_id`)
183
-
184
- If you use the Responses backend and provide a `previous_response_id`, Scout-AI will:
185
-
186
- - keep only the messages after the most recent `previous_response_id` marker (so you do not resend the entire conversation)
187
- - pass `previous_response_id` to the backend
188
-
189
- If you set `previous_response: false`, session continuation is disabled.
190
-
191
- ---
192
-
193
- ## 5. Tools and function calling
194
-
195
- Scout-AI represents tools as a **hash**:
196
-
197
- ```ruby
198
- tools = {
199
- "tool_name" => [handler, tool_definition]
200
- }
201
- ```
202
-
203
- Where:
204
-
205
- - `handler` can be:
206
- - a `Workflow`
207
- - a `KnowledgeBase`
208
- - a `Proc` (custom tool implementation)
209
- - `tool_definition` is an OpenAI-style function schema (adapted per backend)
210
-
211
- ### 5.1 Providing tools
212
-
213
- You can provide tools in three main ways:
214
-
215
- 1) programmatically: `LLM.ask(..., tools: {...})`
216
- 2) via chat roles: `tool:`, `kb:`, `association:`, `mcp:`
217
- 3) via an Agent: `LLM::Agent` automatically exports a workflow/KB as tools
218
-
219
- ### 5.2 Workflow tools
220
-
221
- ```ruby
222
- workflow_tools = LLM.workflow_tools(Baking)
223
- LLM.ask "Bake muffins", tools: workflow_tools
224
- ```
225
-
226
- Or in a chat file:
227
-
228
- ```text
229
- tool: Baking bake_muffin_tray
230
- ```
231
-
232
- ### 5.3 KnowledgeBase tools
233
-
234
- ```ruby
235
- kb_tools = LLM.knowledge_base_tool_definition(kb)
236
- LLM.ask "Query relationships", tools: kb_tools
237
- ```
238
-
239
- Or in a chat file:
240
-
241
- ```text
242
- association: brothers test/data/person/brothers undirected=true
243
- ```
244
-
245
- ### 5.4 Tool execution and output limits
246
-
247
- Tool calls are executed by `LLM.process_calls`.
248
-
249
- To protect the context window, tool outputs are capped (config key `:max_content_length` under `:llm_tools`).
250
-
251
- If a tool returns more than the max length, the content is replaced by an exception-like JSON string.
252
-
253
- ---
254
-
255
- ## 6. Convenience helpers
256
-
257
- ### 6.1 `LLM.workflow_ask`
258
-
259
- ```ruby
260
- LLM.workflow_ask(Baking, "Bake muffins", endpoint: :nano)
261
- ```
262
-
263
- ### 6.2 `LLM.knowledge_base_ask`
264
-
265
- ```ruby
266
- LLM.knowledge_base_ask(kb, "Who is X's brother in law?", endpoint: :nano)
267
- ```
268
-
269
- ---
270
-
271
- ## 7. CLI usage
272
-
273
- ### 7.1 `scout-ai llm ask`
274
-
275
- The main “stateless” CLI.
276
-
277
- ```bash
278
- scout-ai llm ask -e nano "Say hi"
279
- ```
280
-
281
- For convenience prompt does not need quotes.
282
-
283
- ```bash
284
- scout-ai llm ask -e nano Say hi
285
- ```
286
-
287
- Useful flags:
288
-
289
- - `-e/--endpoint <name>`
290
- - `-m/--model <model_id>`
291
- - `-b/--backend <backend>`
292
- - `-c/--chat <file>` — read/append to a chat file
293
- - `-i/--imports <a,b,c>` — import other chat files
294
- - `-t/--template <name_or_path>` — templates with optional `???` placeholder
295
- - `-f/--file <path>` — inline a file into the prompt
296
- - `-d/--dry_run` — print the compiled conversation (no model call)
297
-
298
- The configuration files under etc/config can be used to setup default
299
- endpoints or models.
300
-
301
- ```config
302
- # ~/.scout/etc/config
303
- endpoint nano ask
304
- ```
305
-
306
- ### 7.2 `scout-ai agent ask`
307
-
308
- The “agent-aware” CLI.
309
-
310
- ```bash
311
- scout-ai agent ask Baking Bake a tray of muffins
312
- ```
313
-
314
- It loads the agent (workflow + knowledge base + start_chat) and runs a stateful conversation.
315
-
316
- See `doc/Agent.md`.
317
-
318
- ---
319
-
320
- ## 8. Programmatic use (control loops)
321
-
322
- For loops, branching, delegation, and structured iteration, use `LLM::Agent`.
323
-
324
- See `doc/Agent.md` for the full API and examples.
325
-
326
- ---
327
-
328
- ## 9. Minimal end-to-end example
329
-
330
- 1) Create an endpoint file:
331
-
332
- ```yaml
333
- # ~/.scout/etc/AI/nano
334
- backend: responses
335
- model: gpt-5-nano
336
- ```
337
-
338
- 2) Ask from the CLI:
339
-
340
- ```bash
341
- scout-ai llm ask -e nano Say hi
342
- ```
343
-
344
- 3) Use a chat file with a workflow tool:
345
-
346
- ```text
347
- user:
348
-
349
- tool: Baking bake_muffin_tray
350
-
351
- Bake muffins using the tool.
352
- ```
353
-
354
- ```bash
355
- scout-ai llm ask -c baking.chat -e nano
356
- ```
@@ -1,333 +0,0 @@
1
- # Python-backed Agent Tasks
2
-
3
- This guide explains how to write Scout-AI agent tasks in Python.
4
-
5
- This document is about writing Python-backed workflow tasks for Ruby-side agents. If you want to use Scout-AI chats and agents from Python, see `../python/README.md`.
6
-
7
- The key idea is simple:
8
-
9
- - a normal Scout agent can be packaged as a directory
10
- - if that directory contains a `python/` subdirectory with `*.py` files, `LLM::Agent.load_agent` can auto-load those files as workflow tasks
11
- - the resulting tasks are exposed to the agent exactly like regular workflow tasks
12
-
13
- This gives you a convenient way to keep:
14
-
15
- - chat and agent orchestration in Scout-AI and Ruby
16
- - task logic in Python
17
-
18
- For the underlying generic mechanism, see `~/git/scout-rig/doc/PythonWorkflow.md`.
19
-
20
- ## 1. When to use Python-backed tasks
21
-
22
- Use Python-backed agent tasks when:
23
-
24
- - the logic is easier to write in Python
25
- - you want access to Python libraries directly
26
- - the task is naturally a standalone function with typed inputs and a structured return value
27
- - you still want Scout workflow behavior such as persistence, job directories, provenance, and CLI integration
28
-
29
- Do not use this mechanism just to avoid Ruby entirely. The most effective pattern is usually:
30
-
31
- - use Ruby and Scout workflows to orchestrate the overall control loop
32
- - use Python for the tasks that benefit from Python libraries or Python-first implementations
33
-
34
- ## 2. How agent auto-loading works
35
-
36
- `LLM::Agent.load_agent` resolves an agent in this order:
37
-
38
- - named workflow
39
- - agent directory with `workflow.rb`
40
- - agent directory with `knowledge_base/`
41
- - agent directory with `start_chat`
42
- - agent directory with `python/*.py`
43
-
44
- The important part for Python-backed agents is in `lib/scout/llm/agent.rb`:
45
-
46
- - if the agent directory has a `python/` subdirectory
47
- - and that directory contains `*.py` files
48
- - Scout requires `scout/workflow/python`
49
- - then calls `PythonWorkflow.load_directory(agent_path.python, 'ScoutAgent')`
50
-
51
- This means that for agent directories the convention is slightly different from the generic `PythonWorkflow` case:
52
-
53
- - generic workflow usage often uses `python/task/<name>.py`
54
- - agent auto-loading uses all `*.py` files directly under `python/`
55
-
56
- So for an agent directory, place task files directly in `python/`.
57
-
58
- Two practical notes:
59
-
60
- - every `*.py` file directly under `python/` is considered during auto-loading
61
- - each file may register one or more functions with `scout.task(...)`
62
-
63
- ## 3. Minimal agent directory layout
64
-
65
- A minimal Python-backed agent directory can look like this:
66
-
67
- ```text
68
- MyAgent/
69
- ├── start_chat
70
- └── python/
71
- ├── hello.py
72
- └── summarize.py
73
- ```
74
-
75
- Optional additions:
76
-
77
- ```text
78
- MyAgent/
79
- ├── start_chat
80
- ├── knowledge_base/
81
- └── python/
82
- ├── hello.py
83
- └── summarize.py
84
- ```
85
-
86
- You do not need a `workflow.rb` file if the Python tasks are enough.
87
-
88
- ## 4. Minimal Python task example
89
-
90
- Create `python/hello.py`:
91
-
92
- ```python
93
- import scout
94
-
95
- def hello(name: str, excited: bool = False) -> str:
96
- """
97
- Generate a greeting.
98
-
99
- Args:
100
- name: Name of the person to greet.
101
- excited: Whether to add an exclamation mark.
102
-
103
- Returns:
104
- Greeting text.
105
- """
106
- return f"Hello, {name}{'!' if excited else ''}"
107
-
108
- scout.task(hello)
109
- ```
110
-
111
- This file does three things:
112
-
113
- - defines a normal Python function
114
- - uses type hints and defaults
115
- - registers the function with `scout.task(...)`
116
-
117
- That registration is what makes the function visible to `PythonWorkflow`.
118
-
119
- A single file can register multiple functions if you want a small family of related tasks in one place.
120
-
121
- ## 5. Minimal `start_chat`
122
-
123
- A matching `start_chat` can be very small:
124
-
125
- ```text
126
- system:
127
-
128
- You are a helpful assistant with a few Python-backed tools.
129
- Use them when helpful.
130
- ```
131
-
132
- If the agent is loaded with `LLM::Agent.load_agent("MyAgent")`, the Python tasks are loaded as workflow tasks, and the agent can expose them automatically just as it would for any other workflow-backed agent.
133
-
134
- ## 6. Loading the agent
135
-
136
- Ruby:
137
-
138
- ```ruby
139
- require 'scout-ai'
140
-
141
- agent = LLM::Agent.load_agent('MyAgent', endpoint: :nano)
142
- agent.start
143
- agent.user 'Greet Alice using your tool'
144
- puts agent.chat
145
- ```
146
-
147
- If the backend supports function calling, the Python-backed tasks are available as tools through the workflow auto-export mechanism.
148
-
149
- ## 7. Writing good Python tasks
150
-
151
- A Python-backed task should behave like a clean, standalone function.
152
-
153
- Recommended rules:
154
-
155
- - use explicit type hints
156
- - use defaults where possible
157
- - write a short docstring preamble
158
- - document arguments in Google-style `Args:` sections
159
- - return plain strings, arrays, or JSON-serializable objects
160
- - keep side effects explicit and minimal
161
-
162
- A better example:
163
-
164
- ```python
165
- import scout
166
-
167
- def top_words(text: str, limit: int = 10) -> list[str]:
168
- """
169
- Return the most frequent words in a text.
170
-
171
- Args:
172
- text: Input text to analyze.
173
- limit: Maximum number of words to return.
174
-
175
- Returns:
176
- A list of the most frequent words.
177
- """
178
- counts = {}
179
- for word in text.lower().split():
180
- counts[word] = counts.get(word, 0) + 1
181
- return [w for w, _ in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))[:limit]]
182
-
183
- scout.task(top_words)
184
- ```
185
-
186
- ## 8. Type mapping
187
-
188
- The Python function signature is converted into Scout workflow inputs and return types through the metadata produced by `scout.task(...)`.
189
-
190
- Important mappings include:
191
-
192
- - `str` -> `:string`
193
- - `int` -> `:integer`
194
- - `float` -> `:float`
195
- - `bool` -> `:boolean`
196
- - `list[str]` -> `:array`
197
- - `path`-like metadata -> `:file` or `:file_array`
198
-
199
- For the full mapping rules, see `~/git/scout-rig/doc/PythonWorkflow.md`.
200
-
201
- ## 9. Return values
202
-
203
- At execution time the Python task prints its result to stdout and Scout interprets it as follows:
204
-
205
- - valid JSON -> parsed as JSON
206
- - array/file-array outputs -> split on newlines if not JSON
207
- - anything else -> stripped string
208
-
209
- In practice, this means:
210
-
211
- - simple strings are easy
212
- - lists are easy
213
- - dictionaries and richer results should usually be JSON-serializable
214
-
215
- If you want structured outputs, return JSON-friendly Python objects.
216
-
217
- ## 10. Standalone CLI behavior
218
-
219
- A Python task file can also be run directly.
220
-
221
- Metadata mode:
222
-
223
- ```bash
224
- python python/hello.py --scout-metadata
225
- ```
226
-
227
- Execution mode:
228
-
229
- ```bash
230
- python python/hello.py --name Alice --excited
231
- ```
232
-
233
- If a file registers multiple functions, metadata returns multiple task descriptions, and one function can be selected on the CLI by name.
234
-
235
- This is useful because it means your task file is:
236
-
237
- - inspectable on its own
238
- - testable on its own
239
- - usable both from Scout and from the command line
240
-
241
- ## 11. Agent-facing pattern
242
-
243
- A very effective Scout-AI pattern is:
244
-
245
- - package Python tasks in `python/`
246
- - keep the role instructions in `start_chat`
247
- - let a Ruby workflow orchestrate the larger multi-agent pattern when needed
248
-
249
- For example:
250
-
251
- - a Python task does extraction or scoring
252
- - a Worker agent calls it as a tool
253
- - a Critic agent checks the result
254
- - a Session-like workflow coordinates the whole run and writes artifacts with `Step#file`
255
-
256
- This keeps the system clean:
257
-
258
- - Python does task logic
259
- - Scout workflows do orchestration
260
- - chat files do policy and agent behavior
261
-
262
- ## 12. Relationship to workflows and skills
263
-
264
- A Python-backed agent task is still a workflow task.
265
-
266
- That matters because it means:
267
-
268
- - it participates in the normal Scout workflow/tool machinery
269
- - it can be exposed as a tool through `LLM::Agent`
270
- - it fits naturally into workflow-backed `ask` patterns
271
-
272
- If you come from systems that talk about “skills”, the Scout-AI equivalent here is:
273
-
274
- - `python/*.py` gives you executable task logic
275
- - `start_chat` gives you the instructional layer
276
- - the agent directory bundles them together as a reusable toolkit
277
-
278
- ## 13. A slightly larger example
279
-
280
- Directory:
281
-
282
- ```text
283
- TextAgent/
284
- ├── start_chat
285
- └── python/
286
- ├── top_words.py
287
- └── count_chars.py
288
- ```
289
-
290
- `python/count_chars.py`:
291
-
292
- ```python
293
- import scout
294
-
295
- def count_chars(text: str) -> int:
296
- """
297
- Count characters in a string.
298
-
299
- Args:
300
- text: Input text.
301
-
302
- Returns:
303
- Character count.
304
- """
305
- return len(text)
306
-
307
- scout.task(count_chars)
308
- ```
309
-
310
- Then in Ruby:
311
-
312
- ```ruby
313
- agent = LLM::Agent.load_agent('TextAgent', endpoint: :nano)
314
- agent.start
315
- agent.user 'Use your tools to count characters in hello world and list the top words'
316
- puts agent.chat
317
- ```
318
-
319
- ## 14. What to read next
320
-
321
- For a practical introduction to chats and agents, read:
322
-
323
- - `doc/USER_GUIDE.md`
324
- - `doc/Agent.md`
325
- - `doc/Chat.md`
326
-
327
- For the generic Python workflow mechanism, read:
328
-
329
- - `~/git/scout-rig/doc/PythonWorkflow.md`
330
-
331
- For the underlying Scout workflow model, read:
332
-
333
- - `~/git/scout-gear/doc/Workflow.md`