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.
- checksums.yaml +4 -4
- data/.vimproject +111 -31
- data/README.md +171 -320
- 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 +62 -12
- 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 +36 -6
- data/lib/scout/llm/backends/anthropic.rb +9 -1
- data/lib/scout/llm/backends/bedrock.rb +15 -3
- data/lib/scout/llm/backends/default.rb +129 -89
- data/lib/scout/llm/backends/glm.rb +58 -0
- data/lib/scout/llm/backends/huggingface.rb +13 -1
- data/lib/scout/llm/backends/ollama.rb +13 -0
- data/lib/scout/llm/backends/openwebui.rb +8 -3
- data/lib/scout/llm/chat/agent_meta.rb +264 -0
- data/lib/scout/llm/chat/annotation.rb +37 -14
- 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 +19 -1
- data/lib/scout/llm/chat/process/files.rb +16 -1
- data/lib/scout/llm/chat/process/meta.rb +422 -32
- data/lib/scout/llm/chat/process/options.rb +21 -1
- data/lib/scout/llm/chat/process/tools.rb +49 -12
- 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 +8 -3
- data/lib/scout/llm/image.rb +86 -0
- data/lib/scout/llm/rag.rb +3 -3
- data/lib/scout/llm/tools/call.rb +159 -10
- data/lib/scout/llm/tools/knowledge_base.rb +1 -1
- data/lib/scout/llm/tools/workflow.rb +13 -5
- data/lib/scout-ai.rb +1 -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 +97 -13
- 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/prov +602 -0
- data/scout_commands/llm/word +71 -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_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 +96 -12
- data/doc/Agent.md +0 -354
- data/doc/Chat.md +0 -481
- data/doc/LLM.md +0 -356
- data/doc/PythonAgentTasks.md +0 -333
- data/doc/RAG.md +0 -129
- data/doc/USER_GUIDE.md +0 -572
- 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
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
|
-
```
|
data/doc/PythonAgentTasks.md
DELETED
|
@@ -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`
|