little_ghost 0.9.0 → 0.10.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/README.md +15 -9
- data/docs/guides/assemblies.md +64 -12
- data/docs/guides/core_concepts.md +65 -61
- data/docs/guides/getting_started.md +38 -68
- data/docs/guides/integrations.md +20 -6
- data/docs/guides/production.md +17 -4
- data/lib/little_ghost/ag_ui/adapter.rb +64 -1
- data/lib/little_ghost/agent.rb +1 -1
- data/lib/little_ghost/agent_stream_source.rb +41 -2
- data/lib/little_ghost/assembly.rb +22 -18
- data/lib/little_ghost/assembly_execution.rb +3 -1
- data/lib/little_ghost/code_mode/broker.rb +5 -3
- data/lib/little_ghost/code_mode/javascript/session.rb +22 -2
- data/lib/little_ghost/code_mode/ruby/session.rb +15 -1
- data/lib/little_ghost/execution.rb +3 -1
- data/lib/little_ghost/graph.rb +3 -1
- data/lib/little_ghost/run.rb +23 -8
- data/lib/little_ghost/run_context.rb +21 -0
- data/lib/little_ghost/runtime.rb +3 -3
- data/lib/little_ghost/stream_event.rb +5 -2
- data/lib/little_ghost/swarm.rb +14 -48
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost/workflow.rb +74 -96
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50e597abbf025721200897c852c8ea72e244c0b5bb2f1b50aa58084e55d5d0df
|
|
4
|
+
data.tar.gz: be4d63e81003723c9a916a61359ae508a8d35a16a02b8c7e11f14dc5a3bcd12b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f546cd196e43eb53f6e3ab812c362a9bfb5d81e5408b0608c14ba20ca865a77196c6a801ed3b8980f38e098b6e468555dc2520ee32d9ee72373b4307686b9768
|
|
7
|
+
data.tar.gz: 18294764bb5ada766b993993172cccef4e077bf7d2cb886e7f76f1a3bda7ce60a8c448547b056f55eadd2775761457f8a725d3ab895d268f9785cdc2eebac22b
|
data/README.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# Build AI features that feel at home in Ruby
|
|
2
2
|
|
|
3
|
+
LittleGhost is a Ruby library for adding AI features to an existing application
|
|
4
|
+
or building a dedicated AI service. An **agent** combines a model with
|
|
5
|
+
instructions and Ruby operations it can call. Agents can work together in an
|
|
6
|
+
**assembly**, which your application calls like a single agent.
|
|
7
|
+
|
|
3
8
|
> **Using a coding agent?** Start with
|
|
4
9
|
> [`llms.txt`](https://littleghostai.org/llms.txt) for a concise map
|
|
5
10
|
> of the guides and API. [`llms-full.txt`](https://littleghostai.org/llms-full.txt)
|
|
6
11
|
> contains the complete documentation in one file.
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
With the gem installed and `OPENROUTER_API_KEY` set, start with one class:
|
|
9
14
|
|
|
10
15
|
```ruby
|
|
11
16
|
require "little_ghost"
|
|
@@ -100,14 +105,13 @@ end
|
|
|
100
105
|
The schema checks the shape of the input. Your Ruby code still decides whether
|
|
101
106
|
the operation is allowed. The result goes back to the model as context.
|
|
102
107
|
|
|
103
|
-
An ordinary Tool runs in your Ruby process.
|
|
104
|
-
processes,
|
|
105
|
-
further: a sandboxed interpreter can compose several Tools, while every Tool
|
|
106
|
-
call still returns to your Ruby Tool for validation and permission checks.
|
|
108
|
+
An ordinary Tool runs in your Ruby process. For operations that need files or
|
|
109
|
+
child processes, see [Workspaces and Sandboxes](docs/guides/sandboxing.md).
|
|
107
110
|
|
|
108
111
|
## Grow without changing the caller
|
|
109
112
|
|
|
110
|
-
|
|
113
|
+
When a task needs several agents, choose how they work together. These example
|
|
114
|
+
classes use different coordination styles, but their callers all use `.ask`:
|
|
111
115
|
|
|
112
116
|
```ruby
|
|
113
117
|
CustomerSupportAgent.ask(question)
|
|
@@ -118,12 +122,12 @@ SupportFlowGraph.ask(question)
|
|
|
118
122
|
|
|
119
123
|
Choose the coordination style that matches who should control the next step:
|
|
120
124
|
|
|
121
|
-
- A **subagent**
|
|
125
|
+
- A **subagent** is a specialist an agent can ask for help.
|
|
122
126
|
- A **workflow** uses ordinary Ruby for ordering and branching.
|
|
123
127
|
- A **swarm** lets configured agents choose permitted handoffs.
|
|
124
128
|
- A **graph** makes allowed routes explicit as nodes and edges.
|
|
125
129
|
|
|
126
|
-
A Workflow or Graph can contain agents, other assemblies, or both.
|
|
130
|
+
A Workflow or Graph can contain agents, other assemblies, or both.
|
|
127
131
|
|
|
128
132
|
```text
|
|
129
133
|
request ──> CustomerSupportAgent
|
|
@@ -137,7 +141,9 @@ request ──> SupportFlowGraph ──> TriageAgent ──edge──> ResponseA
|
|
|
137
141
|
|
|
138
142
|
The result stays familiar too. Every call returns a `Run` with the response,
|
|
139
143
|
outcome, usage, and any final error. A coordinated assembly also records which
|
|
140
|
-
participants ran. Use `.stream_ask` to watch the work as it happens
|
|
144
|
+
participants ran. Use `.stream_ask` to watch the work as it happens and choose
|
|
145
|
+
which participants to display. [Getting Started](docs/guides/getting_started.md)
|
|
146
|
+
shows how to stream an Agent's answer and read the completed result.
|
|
141
147
|
|
|
142
148
|
LittleGhost is pre-1.0. Pin the gem version and review release notes before
|
|
143
149
|
upgrading, because interfaces may change between releases.
|
data/docs/guides/assemblies.md
CHANGED
|
@@ -15,7 +15,12 @@ run = entrypoint.ask(question)
|
|
|
15
15
|
|
|
16
16
|
## Use a Workflow for explicit application logic
|
|
17
17
|
|
|
18
|
-
A Workflow's `perform` method is ordinary Ruby. Inside it, `invoke` prepares a
|
|
18
|
+
A Workflow's `perform` method is ordinary Ruby. Inside it, `invoke` prepares a
|
|
19
|
+
child call. Read `.output` for its text or structured answer, or `.result` for
|
|
20
|
+
the complete `RunResult`. Return an invocation to select its answer as the
|
|
21
|
+
Workflow result. You can inspect that answer first, or return a value that Ruby
|
|
22
|
+
computes from intermediate answers. Every participating Agent publishes live
|
|
23
|
+
progress regardless of how you access or select results.
|
|
19
24
|
|
|
20
25
|
```ruby
|
|
21
26
|
class ResponseWorkflow < LittleGhost::Workflow
|
|
@@ -41,7 +46,7 @@ Every participant passed to `invoke` can be an Agent or another Assembly. By def
|
|
|
41
46
|
|
|
42
47
|
Each child Agent keeps its own [prompt view](prompt_views.md). The Workflow supplies request-specific input; it does not replace that Agent's reusable system instructions.
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
When one child should supply the final answer, return its invocation:
|
|
45
50
|
|
|
46
51
|
```ruby
|
|
47
52
|
def perform
|
|
@@ -49,6 +54,38 @@ def perform
|
|
|
49
54
|
end
|
|
50
55
|
```
|
|
51
56
|
|
|
57
|
+
To inspect an answer before selecting it, keep the invocation, read its output,
|
|
58
|
+
and return the same invocation after your checks. For example, given a
|
|
59
|
+
`ReviewAgent` whose structured result includes an `approved` boolean:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
class ReviewedResponseWorkflow < LittleGhost::Workflow
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def perform
|
|
66
|
+
candidate = invoke(CustomerSupportAgent)
|
|
67
|
+
review = invoke(ReviewAgent, input: candidate.output, history: [], context: {}).output
|
|
68
|
+
|
|
69
|
+
review.fetch("approved") ? candidate : invoke(EscalationAgent)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Returning the candidate does not repeat the work or replay its text. The final
|
|
75
|
+
result preserves its conversation, state, and structured value. Usage and steps
|
|
76
|
+
include both the candidate and the review. Repeated `.result` or `.output` reads
|
|
77
|
+
reuse the completed result. An invocation must belong to the workflow returning
|
|
78
|
+
it and must complete successfully.
|
|
79
|
+
|
|
80
|
+
You can watch each Agent while the Workflow runs, including while an answer is
|
|
81
|
+
being reviewed. Reviewing an answer does not hide its progress. Choose which
|
|
82
|
+
participants your audience may see, as described in
|
|
83
|
+
[Watch every agent](#watch-every-agent-in-an-assembly).
|
|
84
|
+
|
|
85
|
+
Cancellation and deadlines apply before each child runs and before the Workflow
|
|
86
|
+
returns its answer. They also apply if a checkpoint callback cancels the run or
|
|
87
|
+
finishes after the deadline.
|
|
88
|
+
|
|
52
89
|
When Ruby should compute the caller-visible result, consume every child and return the computed value:
|
|
53
90
|
|
|
54
91
|
```ruby
|
|
@@ -67,11 +104,11 @@ class EvidenceWorkflow < LittleGhost::Workflow
|
|
|
67
104
|
end
|
|
68
105
|
```
|
|
69
106
|
|
|
70
|
-
A returned String becomes the Workflow's textual response. Arrays, mappings, numbers, and booleans become a structured result available through `RunResult#output`, including when the Workflow is exposed with `assembly_as_tool`. Direct structured results must be JSON-compatible and stay within LittleGhost's structured-result size, depth, and complexity limits. Return an explicit value: `nil` remains a `ProtocolError`, which catches forgotten returns.
|
|
107
|
+
A returned String becomes the Workflow's textual response. Arrays, mappings, numbers, and booleans become a structured result available through `RunResult#output`, including when the Workflow is exposed with `assembly_as_tool`. Computed values appear in the final result, not as synthetic text-delta events. Direct structured results must be JSON-compatible and stay within LittleGhost's structured-result size, depth, and complexity limits. Return an explicit value: `nil` remains a `ProtocolError`, which catches forgotten returns.
|
|
71
108
|
|
|
72
109
|
### Choose a branch in Ruby
|
|
73
110
|
|
|
74
|
-
Each branch should end with its final
|
|
111
|
+
Each branch should end with its final invocation or a directly computed value:
|
|
75
112
|
|
|
76
113
|
```ruby
|
|
77
114
|
class RoutedResponseWorkflow < LittleGhost::Workflow
|
|
@@ -305,7 +342,9 @@ Retries start at zero. When `retries` is greater than zero, `retry_on` must list
|
|
|
305
342
|
|
|
306
343
|
## Watch every agent in an assembly
|
|
307
344
|
|
|
308
|
-
Follow each participant
|
|
345
|
+
Follow each participant by handling source-tagged `:agent_stream` events. Every
|
|
346
|
+
Run uses this same progress channel, from one root Agent to nested assemblies
|
|
347
|
+
and subagents. Lifecycle events and the final result remain separate:
|
|
309
348
|
|
|
310
349
|
```ruby
|
|
311
350
|
stream = SupportFlowGraph.stream_ask("Why was I charged twice?")
|
|
@@ -333,19 +372,31 @@ run.completed? # => true
|
|
|
333
372
|
|
|
334
373
|
`source.agent_id` identifies the Agent class, `source.agent_path` distinguishes managed subagents, and `source.operation_id` groups one invocation. `source.assembly_path` lists the enclosing Workflow, Swarm, or Graph steps from the outside inward.
|
|
335
374
|
|
|
375
|
+
An Agent or Assembly invoked as a Tool within the Run adds a `:tool` step to that path. The
|
|
376
|
+
step identifies the invoking Agent and Tool name, and its descendants retain
|
|
377
|
+
the boundary. Tool-invoked Agents therefore remain distinct from the root
|
|
378
|
+
Agent even when their managed-subagent path is `/root`.
|
|
379
|
+
|
|
336
380
|
The routed input and inner event are copied and frozen before they reach the
|
|
337
381
|
observer, so changing an event can't affect the running assembly. Parallel
|
|
338
382
|
participants can interleave. Events from each Agent retain their order, and
|
|
339
383
|
LittleGhost never calls the stream block concurrently.
|
|
340
384
|
|
|
341
|
-
|
|
385
|
+
Agent progress appears only through `:agent_stream`, without duplicate raw
|
|
386
|
+
events. Returning an invocation selects its result; it does not replay that
|
|
387
|
+
Agent's progress. Handle the terminal Run event or read `run.result` for the
|
|
388
|
+
complete answer and aggregate usage. Pass `include_agent_events: false` to omit
|
|
389
|
+
all Agent progress without changing execution, lifecycle events, or results.
|
|
342
390
|
|
|
343
|
-
The AG-UI adapter
|
|
391
|
+
The [AG-UI adapter](integrations.md) displays root Agent progress by default.
|
|
392
|
+
Supply its `source_filter:` callable when a client should see selected assembly
|
|
393
|
+
participants.
|
|
344
394
|
|
|
345
|
-
> **Safety note:**
|
|
346
|
-
>
|
|
347
|
-
>
|
|
348
|
-
>
|
|
395
|
+
> **Safety note:** Every Run stream can include inputs, reasoning, Tool arguments
|
|
396
|
+
> and results, errors, and private output from nested participants. The example
|
|
397
|
+
> above observes the complete Run. Before sending or storing events for a
|
|
398
|
+
> narrower audience, select allowed sources and filter their fields. Result
|
|
399
|
+
> selection does not authorize disclosure of a participant's progress.
|
|
349
400
|
|
|
350
401
|
## Inspect what the assembly did
|
|
351
402
|
|
|
@@ -362,7 +413,8 @@ policy = trajectory.find { |step| step.participant == "policy" }
|
|
|
362
413
|
trajectory.concurrent?(ledger.id, policy.id)
|
|
363
414
|
```
|
|
364
415
|
|
|
365
|
-
|
|
416
|
+
The trajectory retains step outputs, not a transcript of streaming events.
|
|
417
|
+
Step outputs and final results have size limits. Use your application's
|
|
366
418
|
instrumentation when you need deeper diagnostics.
|
|
367
419
|
|
|
368
420
|
## Compose assemblies inside assemblies
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Core Concepts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Start with an Agent: a Ruby class that gives a model instructions and operations
|
|
4
|
+
it can call. This guide builds on the help center example in
|
|
5
|
+
[Getting Started](getting_started.md), including its `HelpCenterLookupTool` and
|
|
6
|
+
provider setup.
|
|
4
7
|
|
|
5
8
|
```ruby
|
|
6
9
|
class CustomerSupportAgent < LittleGhost::Agent
|
|
@@ -9,16 +12,20 @@ class CustomerSupportAgent < LittleGhost::Agent
|
|
|
9
12
|
tools HelpCenterLookupTool
|
|
10
13
|
end
|
|
11
14
|
|
|
12
|
-
run = CustomerSupportAgent.ask("
|
|
15
|
+
run = CustomerSupportAgent.ask("What is the refund policy?")
|
|
13
16
|
run.response
|
|
14
17
|
```
|
|
15
18
|
|
|
16
19
|
From there, add only what the work needs. Give the agent a tool. Let it ask a specialist for help. Or coordinate several agents while the rest of your application keeps making the same call.
|
|
17
20
|
|
|
18
|
-
## An Agent
|
|
21
|
+
## An Agent carries a request through to an answer
|
|
19
22
|
|
|
20
23
|
An **Agent** defines one model-driven behavior. It chooses the model, supplies the instructions and tools, and carries one request through to an answer.
|
|
21
24
|
|
|
25
|
+
The model can answer immediately or ask to call a Tool. LittleGhost runs the
|
|
26
|
+
Tool and sends its result back to the model, which can continue working. That
|
|
27
|
+
back-and-forth is the **model loop**.
|
|
28
|
+
|
|
22
29
|
The class holds the behavior you want to reuse. Each call brings its own input, history, context, settings, and attachments. Request data never needs to live on the class.
|
|
23
30
|
|
|
24
31
|
```text
|
|
@@ -60,71 +67,32 @@ inside the Tool using identity and account information from your application.
|
|
|
60
67
|
[Tools](tools.md) follows that path from model input to application code,
|
|
61
68
|
including run-scoped bindings, concurrency, retries, and sandbox delegation.
|
|
62
69
|
|
|
63
|
-
## A Run
|
|
70
|
+
## A Run records one request
|
|
64
71
|
|
|
65
72
|
Every `.ask` or `.stream_ask` creates a **Run**. Think of it as the record of one trip through LittleGhost. It opens what the request needs, records how the work ended, and closes the resources it owns.
|
|
66
73
|
|
|
67
74
|
```ruby
|
|
68
|
-
run = CustomerSupportAgent.ask("
|
|
75
|
+
run = CustomerSupportAgent.ask("What is the refund policy?")
|
|
69
76
|
|
|
70
77
|
run.completed? # => true
|
|
71
78
|
run.response
|
|
72
|
-
# One possible response:
|
|
73
|
-
run.usage #
|
|
79
|
+
# One possible response: Refunds are available within 30 days.
|
|
80
|
+
run.usage # Token counts reported by the model provider.
|
|
74
81
|
run.result # => the complete LittleGhost::RunResult
|
|
75
82
|
```
|
|
76
83
|
|
|
77
84
|
The Agent defines reusable behavior; the Run records what happened this time.
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Run
|
|
85
|
-
├── Invocation: caller input, history, and application context
|
|
86
|
-
├── RunContext: mutable working state for this execution
|
|
87
|
-
└── Agent and Tools ──> RunResult
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
An **Invocation** is the request in LittleGhost's standard shape. Its `context`
|
|
91
|
-
contains current request values supplied by your application. A Tool can read
|
|
92
|
-
those values through `run.invocation.context` when it checks permission.
|
|
93
|
-
|
|
94
|
-
A **Session** stores conversation state between Runs when persistence is
|
|
95
|
-
configured. The **RunContext** carries mutable working state in `context.state`
|
|
96
|
-
during one Run. LittleGhost loads saved Session state before adding the current
|
|
97
|
-
Invocation context. Recheck saved values before using them for permission
|
|
98
|
-
decisions.
|
|
99
|
-
|
|
100
|
-
A Tool's **Binding** gives the Tool access to objects created for this run,
|
|
101
|
-
including the Agent, Run, Workspace, and Sandbox. These objects are separate
|
|
102
|
-
from arguments chosen by the model. [Tools](tools.md) explains the binding;
|
|
103
|
-
[Workspaces and Sandboxes](sandboxing.md) explains delegated files and child
|
|
104
|
-
processes.
|
|
105
|
-
|
|
106
|
-
The final **RunResult** keeps the complete assembly result. Its `text` is the final text answer. Its `output` returns structured data when the Agent declared a result schema, and text otherwise. The top-level `Run#response` is always the caller-facing text.
|
|
107
|
-
|
|
108
|
-
### See how a call ended
|
|
109
|
-
|
|
110
|
-
Top-level calls normally return a Run, even when execution fails. The terminal event carries the same outcome when you stream:
|
|
86
|
+
The final **RunResult**, available through `run.result`, holds the answer and
|
|
87
|
+
details such as token usage. Its `output` returns text unless you configured
|
|
88
|
+
the Agent to return checked data, such as a hash of named fields. See
|
|
89
|
+
[Structured Results and Content](structured_outputs_and_content.md) for that
|
|
90
|
+
alternative. Use `run.response` when you want the text answer.
|
|
111
91
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
| The deadline stopped work | `partial` | `:run_partial` | Returns the Run with any response produced so far |
|
|
117
|
-
| Cancellation stopped work | `cancelled` | `:run_cancel` | Returns the Run without a response |
|
|
118
|
-
| Tool input or a `ToolError` failed | The model may recover | No terminal event by itself | Gives a safe error result back to the model |
|
|
119
|
-
| Input, configuration, or resources failed before a Run could start | No Run exists | None | Raises the exception |
|
|
120
|
-
|
|
121
|
-
Unexpected Tool exception messages are hidden from the model. The original
|
|
122
|
-
exception remains available to application callbacks and diagnostics.
|
|
123
|
-
|
|
124
|
-
Failures while closing resources, delivering events, or reporting
|
|
125
|
-
instrumentation sit outside the normal result path. They raise a Ruby exception
|
|
126
|
-
because LittleGhost can no longer promise that it delivered a clean ending.
|
|
127
|
-
[Running in Production](production.md) covers supervision and shutdown.
|
|
92
|
+
When a Tool needs to know who is asking, pass information from your application
|
|
93
|
+
with the request. [Tools](tools.md) explains how it reaches the Tool. When a
|
|
94
|
+
conversation should continue across requests, a **Session** saves its history
|
|
95
|
+
and working state; [Running in Production](production.md) covers that setup.
|
|
128
96
|
|
|
129
97
|
## An Assembly can look like one Agent
|
|
130
98
|
|
|
@@ -157,6 +125,10 @@ The coordination types differ mainly in who decides what happens next:
|
|
|
157
125
|
|
|
158
126
|
A **subagent** is a specialist that a parent Agent can call for help. The parent model chooses when to delegate, reads the result, and then continues its own answer.
|
|
159
127
|
|
|
128
|
+
The examples below omit the specialist Agent definitions. Each is an Agent
|
|
129
|
+
class like `CustomerSupportAgent`, with instructions and tools suited to its
|
|
130
|
+
task. [Compose Agents](assemblies.md) expands on these coordination patterns.
|
|
131
|
+
|
|
160
132
|
```ruby
|
|
161
133
|
class CustomerSupportAgent < LittleGhost::Agent
|
|
162
134
|
model "openrouter:openai/gpt-5.6-luna"
|
|
@@ -166,15 +138,13 @@ end
|
|
|
166
138
|
|
|
167
139
|
Use a subagent when delegation is part of one model's decision-making. Use a Workflow when application code must guarantee that a step happens.
|
|
168
140
|
|
|
169
|
-
When an Agent also uses code mode, subagent controls stay in the Agent's
|
|
170
|
-
conversation. Code-mode programs can compose ordinary Tools, while spawning,
|
|
171
|
-
messaging, and checking on subagents remain decisions for the parent model.
|
|
172
|
-
|
|
173
141
|
### Workflows make Ruby the coordinator
|
|
174
142
|
|
|
175
143
|
A **Workflow** coordinates work with ordinary Ruby. Its `perform` method can call an Agent or another Assembly, read a result, choose a branch, or run independent steps together.
|
|
176
144
|
|
|
177
|
-
`invoke` prepares a
|
|
145
|
+
`invoke` prepares a participant's call without running it yet. Read `.output`
|
|
146
|
+
to run it and use its answer in Ruby. Return the final `invoke` call to use
|
|
147
|
+
that participant's answer as the Workflow's result.
|
|
178
148
|
|
|
179
149
|
```ruby
|
|
180
150
|
class ResponseWorkflow < LittleGhost::Workflow
|
|
@@ -192,6 +162,13 @@ class ResponseWorkflow < LittleGhost::Workflow
|
|
|
192
162
|
end
|
|
193
163
|
```
|
|
194
164
|
|
|
165
|
+
In this example, research finishes before the support Agent begins. `input.text`
|
|
166
|
+
is the original question; the Workflow adds the research to it.
|
|
167
|
+
|
|
168
|
+
You can also inspect an answer before choosing it as the final result, without
|
|
169
|
+
running the participant again. [Compose Agents](assemblies.md) shows how, along
|
|
170
|
+
with ways to display each participant's progress.
|
|
171
|
+
|
|
195
172
|
Workflow children receive the caller's history and application context by default. Pass `history: []`, `context: {}`, or redacted values when a participant should receive less.
|
|
196
173
|
|
|
197
174
|
### Swarms let agents hand work to one another
|
|
@@ -216,7 +193,8 @@ Agent should use it as context rather than proof that an action is permitted.
|
|
|
216
193
|
|
|
217
194
|
### Graphs make routes visible
|
|
218
195
|
|
|
219
|
-
A **Graph**
|
|
196
|
+
A **Graph** lays out the steps and routes through a task. Each named **node**
|
|
197
|
+
runs an Agent or another Assembly. An **edge** says which node can run next.
|
|
220
198
|
|
|
221
199
|
```ruby
|
|
222
200
|
class SupportFlowGraph < LittleGhost::Graph
|
|
@@ -236,6 +214,10 @@ class SupportFlowGraph < LittleGhost::Graph
|
|
|
236
214
|
end
|
|
237
215
|
```
|
|
238
216
|
|
|
217
|
+
Here, `TriageAgent` is expected to answer `billing` for billing questions.
|
|
218
|
+
That answer selects the conditional billing route. Otherwise, the unconditional
|
|
219
|
+
general route is the fallback; it does not run alongside the billing route.
|
|
220
|
+
|
|
239
221
|
Graph nodes receive the original task and results from the nodes immediately
|
|
240
222
|
before them. They do not receive caller history or application context unless
|
|
241
223
|
their declarations opt in. [Compose Agents](assemblies.md) explains parallel
|
|
@@ -256,6 +238,28 @@ run.result.trajectory.transitions
|
|
|
256
238
|
This record shows which participants ran. [Compose Agents](assemblies.md)
|
|
257
239
|
explains builders, detailed routing records, and live events from nested Agents.
|
|
258
240
|
|
|
241
|
+
## Handle the outcome
|
|
242
|
+
|
|
243
|
+
An Agent or coordinated Assembly normally returns a Run even when the work
|
|
244
|
+
fails. Check its outcome before using the answer:
|
|
245
|
+
|
|
246
|
+
| What happened | Run outcome | What to inspect |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| Work completed | `completed` | `run.response` or `run.result` |
|
|
249
|
+
| Model, provider, or assembly execution failed | `failed` | `run.error` |
|
|
250
|
+
| The deadline stopped work | `partial` | Any response produced so far |
|
|
251
|
+
| Cancellation stopped work | `cancelled` | No response is returned |
|
|
252
|
+
|
|
253
|
+
A Tool error need not end the Run: LittleGhost can give the model a safe error
|
|
254
|
+
result so it can try again. Unexpected exception messages stay in application
|
|
255
|
+
diagnostics rather than going to the model.
|
|
256
|
+
|
|
257
|
+
Some failures raise Ruby exceptions instead of returning a Run, including
|
|
258
|
+
invalid setup before work starts and failures while closing resources or
|
|
259
|
+
delivering events. [Running in Production](production.md) covers error handling
|
|
260
|
+
and shutdown; [Run](rdoc-ref:LittleGhost::Run) lists the streaming events for
|
|
261
|
+
each outcome.
|
|
262
|
+
|
|
259
263
|
The pieces now fit together: Agents define behavior. Tools connect them to Ruby. Runs record one execution. Assemblies let the system grow without changing the caller.
|
|
260
264
|
|
|
261
265
|
Continue with [Models and Providers](models_and_providers.md) to choose model
|
|
@@ -17,7 +17,8 @@ $ export OPENROUTER_API_KEY="..."
|
|
|
17
17
|
|
|
18
18
|
Use your application's secret manager outside a local shell, and never commit provider credentials.
|
|
19
19
|
|
|
20
|
-
This guide uses OpenRouter
|
|
20
|
+
This guide uses OpenRouter, a service that sends requests to your chosen AI
|
|
21
|
+
model. Set `OPENROUTER_API_KEY` to a key from your OpenRouter account. Prefer another hosted
|
|
21
22
|
provider or a local model server? Start with [Provider Support](providers.md).
|
|
22
23
|
|
|
23
24
|
## See your first answer
|
|
@@ -41,10 +42,11 @@ else
|
|
|
41
42
|
end
|
|
42
43
|
```
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
`model` selects the service and model to call. `system_prompt` supplies the
|
|
46
|
+
instructions the model follows on each request. Run the file with your bundle:
|
|
45
47
|
|
|
46
48
|
```sh
|
|
47
|
-
$ ruby customer_support_agent.rb
|
|
49
|
+
$ bundle exec ruby customer_support_agent.rb
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
`CustomerSupportAgent.ask` creates a `LittleGhost::Run` for this request. When the work finishes, the Run holds the outcome and response.
|
|
@@ -55,7 +57,9 @@ The selected external provider may receive system instructions, caller input, co
|
|
|
55
57
|
|
|
56
58
|
## Connect the agent to your application
|
|
57
59
|
|
|
58
|
-
The first agent can answer general questions. A **tool** gives it a focused
|
|
60
|
+
The first agent can answer general questions. A **tool** gives it a focused
|
|
61
|
+
operation backed by your Ruby code. Add this class after the `require` line,
|
|
62
|
+
before `CustomerSupportAgent`:
|
|
59
63
|
|
|
60
64
|
```ruby
|
|
61
65
|
class HelpCenterLookupTool < LittleGhost::Tool
|
|
@@ -80,7 +84,9 @@ class HelpCenterLookupTool < LittleGhost::Tool
|
|
|
80
84
|
end
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
`input_schema` describes the arguments the model may supply. Here, `topic` must
|
|
88
|
+
be one of the help center's keys. Replace the Agent definition and the call at
|
|
89
|
+
the end of the file with these:
|
|
84
90
|
|
|
85
91
|
```ruby
|
|
86
92
|
class CustomerSupportAgent < LittleGhost::Agent
|
|
@@ -106,84 +112,48 @@ LittleGhost checks the model's arguments before it calls
|
|
|
106
112
|
`HelpCenterLookupTool#call`. The Tool's result then becomes context for the
|
|
107
113
|
model.
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
While an Agent is working, LittleGhost binds each Tool instance to the current
|
|
116
|
-
Run. The Tool can read request values through its `run` accessor:
|
|
117
|
-
|
|
118
|
-
```ruby
|
|
119
|
-
class OrderStatusTool < LittleGhost::Tool
|
|
120
|
-
ORDER_STATUSES = {
|
|
121
|
-
["user-7", "account-2", "481"] => "out for delivery"
|
|
122
|
-
}.freeze
|
|
123
|
-
|
|
124
|
-
description "Look up an order that belongs to the current customer."
|
|
125
|
-
input_schema(
|
|
126
|
-
type: "object",
|
|
127
|
-
properties: {order_number: {type: "string"}},
|
|
128
|
-
required: ["order_number"],
|
|
129
|
-
additionalProperties: false
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
def call(input)
|
|
133
|
-
lookup = [
|
|
134
|
-
run.invocation.actor_id,
|
|
135
|
-
run.invocation.context.fetch("account_id"),
|
|
136
|
-
input.fetch("order_number")
|
|
137
|
-
]
|
|
138
|
-
|
|
139
|
-
ORDER_STATUSES.fetch(lookup) do
|
|
140
|
-
raise LittleGhost::ToolError, "Order not found"
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
class CustomerSupportAgent < LittleGhost::Agent
|
|
146
|
-
tools HelpCenterLookupTool, OrderStatusTool
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
run = CustomerSupportAgent.ask(
|
|
150
|
-
"Where is order 481?",
|
|
151
|
-
actor_id: "user-7",
|
|
152
|
-
context: {account_id: "account-2"}
|
|
153
|
-
)
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Here, `order_number` came from the model. The application supplied `actor_id`
|
|
157
|
-
and `account_id` after authenticating the caller. LittleGhost places those
|
|
158
|
-
request values on `run.invocation`; context keys become strings.
|
|
159
|
-
|
|
160
|
-
> **Safety note:** Treat model-selected Tool arguments like any other external
|
|
161
|
-
> input. Check permission using the current user and account before returning
|
|
162
|
-
> private data or performing a write.
|
|
163
|
-
|
|
164
|
-
That is enough to authorize the first Tool safely. [Core Concepts](core_concepts.md) names the request and working-state objects behind `run`, and [Running in Production](production.md) explains what changes when you add saved conversations.
|
|
115
|
+
> **Safety note:** The schema checks arguments, not permission. This example
|
|
116
|
+
> reads a public help center. Before a Tool returns private data or changes
|
|
117
|
+
> anything, check permission using the user and account identified by your
|
|
118
|
+
> application—not values supplied by the model. [Tools](tools.md) shows how to
|
|
119
|
+
> pass that information to a Tool.
|
|
165
120
|
|
|
166
121
|
## Stream the same agent
|
|
167
122
|
|
|
168
|
-
Use `.stream_ask` when a console, HTTP response, or user interface should receive
|
|
123
|
+
Use `.stream_ask` when a console, HTTP response, or user interface should receive
|
|
124
|
+
the answer as it is written. Replace the `.ask` call with the following code.
|
|
125
|
+
|
|
126
|
+
Each `:agent_stream` event contains the Agent's progress and a `source` that
|
|
127
|
+
identifies which Agent produced it. The source check below selects the Agent
|
|
128
|
+
you called directly: `/root` with no enclosing assembly steps. `:text_delta`
|
|
129
|
+
contains the next piece of its answer.
|
|
169
130
|
|
|
170
131
|
```ruby
|
|
171
132
|
stream = CustomerSupportAgent.stream_ask("Can I get a refund?")
|
|
172
133
|
|
|
173
134
|
run = stream.each do |event|
|
|
174
135
|
case event.type
|
|
175
|
-
when :
|
|
176
|
-
|
|
136
|
+
when :agent_stream
|
|
137
|
+
source = event.data.fetch(:source)
|
|
138
|
+
next unless source.agent_path == "/root" && source.assembly_path.empty?
|
|
139
|
+
|
|
140
|
+
progress = event.data.fetch(:event)
|
|
141
|
+
print progress.data.fetch(:text) if progress.type == :text_delta
|
|
177
142
|
when :run_error
|
|
178
143
|
warn event.data.fetch(:message)
|
|
179
144
|
end
|
|
180
145
|
end
|
|
181
146
|
|
|
182
|
-
|
|
147
|
+
run.response # The completed answer, separate from live progress.
|
|
183
148
|
warn run.error.class.name if run.failed?
|
|
184
149
|
```
|
|
185
150
|
|
|
186
|
-
|
|
151
|
+
When enumeration finishes, `.each` returns the Run with the final outcome and
|
|
152
|
+
complete response. You can display progress and still read the finished answer.
|
|
153
|
+
|
|
154
|
+
If you later add other Agents, their progress arrives in the same stream. Keep
|
|
155
|
+
the source check when your audience should see only this Agent's text. See
|
|
156
|
+
[Compose Agents](assemblies.md) to display several participants.
|
|
187
157
|
|
|
188
158
|
## Give the code a home
|
|
189
159
|
|
|
@@ -223,8 +193,8 @@ The source repository also contains a complete
|
|
|
223
193
|
[single-file Agent](https://github.com/littleghostai/little_ghost/tree/main/examples/basic_agent)
|
|
224
194
|
and a
|
|
225
195
|
[coding harness](https://github.com/littleghostai/little_ghost/tree/main/examples/coding_harness)
|
|
226
|
-
that
|
|
227
|
-
|
|
196
|
+
that shows a larger application with several agents and tools for working
|
|
197
|
+
with files.
|
|
228
198
|
|
|
229
199
|
You now have the smallest useful LittleGhost application: one Agent, one Tool, and one familiar Ruby call.
|
|
230
200
|
|
data/docs/guides/integrations.md
CHANGED
|
@@ -27,10 +27,22 @@ events = LittleGhost::AGUI::Adapter.new.stream(
|
|
|
27
27
|
events.each { |event| websocket.write(JSON.generate(event)) }
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
The adapter translates
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
The adapter translates Run lifecycle and final outcomes, plus selected Agent
|
|
31
|
+
progress such as model output, Tool activity, and retries. By default, it selects
|
|
32
|
+
only the root Agent: `agent_path == "/root"` with an empty `assembly_path`.
|
|
33
|
+
Nested Agent progress is not forwarded. The adapter reads Agent progress from
|
|
34
|
+
`:agent_stream` and reports aggregate usage separately.
|
|
35
|
+
|
|
36
|
+
Pass `source_filter:` to `LittleGhost::AGUI::Adapter.new` to select other
|
|
37
|
+
participants. This application-owned callable receives an `AgentStreamSource`
|
|
38
|
+
and returns a truthy value only for sources the client may see. Match the
|
|
39
|
+
enclosing assembly path as well as the Agent identity when selecting a nested
|
|
40
|
+
participant. Computed assembly answers arrive in `RUN_FINISHED`'s result, not
|
|
41
|
+
as synthetic text deltas.
|
|
42
|
+
|
|
43
|
+
The adapter does not keep state between calls. Your application owns the
|
|
44
|
+
connection, backpressure, disconnect behavior, and any request state its
|
|
45
|
+
callbacks need.
|
|
34
46
|
|
|
35
47
|
LittleGhost may emit event types beyond the core AG-UI set. Decide whether the
|
|
36
48
|
client preserves or ignores types it does not recognize. See the [AG-UI event
|
|
@@ -38,8 +50,10 @@ documentation](https://docs.ag-ui.com/concepts/events) when implementing the
|
|
|
38
50
|
client.
|
|
39
51
|
|
|
40
52
|
> **Safety note:** A Run stream can include model output, Tool arguments and
|
|
41
|
-
> results, errors, and participant activity.
|
|
42
|
-
>
|
|
53
|
+
> results, errors, and participant activity. Source selection controls which
|
|
54
|
+
> Agent progress is forwarded; it does not redact that progress or filter the
|
|
55
|
+
> final Run result. Check that the connected user may see those values, and
|
|
56
|
+
> filter sensitive fields before sending or storing events.
|
|
43
57
|
|
|
44
58
|
Calling `each` drives the source stream on the caller's fiber or thread. When a
|
|
45
59
|
client disconnects, stop enumerating and decide whether the application should
|