agent_c 2.9
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 +7 -0
- data/.rubocop.yml +10 -0
- data/.ruby-version +1 -0
- data/CLAUDE.md +21 -0
- data/README.md +360 -0
- data/Rakefile +16 -0
- data/TODO.md +105 -0
- data/agent_c.gemspec +38 -0
- data/docs/batch.md +503 -0
- data/docs/chat-methods.md +156 -0
- data/docs/cost-reporting.md +86 -0
- data/docs/pipeline-tips-and-tricks.md +453 -0
- data/docs/session-configuration.md +274 -0
- data/docs/testing.md +747 -0
- data/docs/tools.md +103 -0
- data/docs/versioned-store.md +840 -0
- data/lib/agent_c/agent/chat.rb +211 -0
- data/lib/agent_c/agent/chat_response.rb +38 -0
- data/lib/agent_c/agent/chats/anthropic_bedrock.rb +48 -0
- data/lib/agent_c/batch.rb +102 -0
- data/lib/agent_c/configs/repo.rb +90 -0
- data/lib/agent_c/context.rb +56 -0
- data/lib/agent_c/costs/data.rb +39 -0
- data/lib/agent_c/costs/report.rb +219 -0
- data/lib/agent_c/db/store.rb +162 -0
- data/lib/agent_c/errors.rb +19 -0
- data/lib/agent_c/pipeline.rb +152 -0
- data/lib/agent_c/pipelines/agent.rb +219 -0
- data/lib/agent_c/processor.rb +98 -0
- data/lib/agent_c/prompts.yml +53 -0
- data/lib/agent_c/schema.rb +71 -0
- data/lib/agent_c/session.rb +206 -0
- data/lib/agent_c/store.rb +72 -0
- data/lib/agent_c/test_helpers.rb +173 -0
- data/lib/agent_c/tools/dir_glob.rb +46 -0
- data/lib/agent_c/tools/edit_file.rb +114 -0
- data/lib/agent_c/tools/file_metadata.rb +43 -0
- data/lib/agent_c/tools/git_status.rb +30 -0
- data/lib/agent_c/tools/grep.rb +119 -0
- data/lib/agent_c/tools/paths.rb +36 -0
- data/lib/agent_c/tools/read_file.rb +94 -0
- data/lib/agent_c/tools/run_rails_test.rb +87 -0
- data/lib/agent_c/tools.rb +61 -0
- data/lib/agent_c/utils/git.rb +87 -0
- data/lib/agent_c/utils/shell.rb +58 -0
- data/lib/agent_c/version.rb +5 -0
- data/lib/agent_c.rb +32 -0
- data/lib/versioned_store/base.rb +314 -0
- data/lib/versioned_store/config.rb +26 -0
- data/lib/versioned_store/stores/schema.rb +127 -0
- data/lib/versioned_store/version.rb +5 -0
- data/lib/versioned_store.rb +5 -0
- data/template/Gemfile +9 -0
- data/template/Gemfile.lock +152 -0
- data/template/README.md +61 -0
- data/template/Rakefile +50 -0
- data/template/bin/rake +27 -0
- data/template/lib/autoload.rb +10 -0
- data/template/lib/config.rb +59 -0
- data/template/lib/pipeline.rb +19 -0
- data/template/lib/prompts.yml +57 -0
- data/template/lib/store.rb +17 -0
- data/template/test/pipeline_test.rb +221 -0
- data/template/test/test_helper.rb +18 -0
- metadata +194 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Cost Reporting
|
|
2
|
+
|
|
3
|
+
Note: You probably want to make a `Batch`. See the [main README](../README.md)
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
AgentC automatically tracks all LLM interactions in a SQLite database, allowing you to generate detailed cost reports.
|
|
8
|
+
|
|
9
|
+
## Generating Reports
|
|
10
|
+
|
|
11
|
+
Track your LLM usage and costs:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# See the [configuration](./session-configuration.md) for session args
|
|
15
|
+
session = AgentC::Session.new(...)
|
|
16
|
+
|
|
17
|
+
# Generate a cost report for all projects
|
|
18
|
+
AgentC::Costs::Report.call(agent_store: session.agent_store)
|
|
19
|
+
|
|
20
|
+
# For a specific project
|
|
21
|
+
AgentC::Costs::Report.call(
|
|
22
|
+
agent_store: session.agent_store,
|
|
23
|
+
project: 'my_project'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# For a specific run
|
|
27
|
+
AgentC::Costs::Report.call(
|
|
28
|
+
agent_store: session.agent_store,
|
|
29
|
+
project: 'my_project',
|
|
30
|
+
run_id: 1234567890
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Or use the session's cost method for current project/run
|
|
34
|
+
puts "Project cost: $#{session.cost.project}"
|
|
35
|
+
puts "Run cost: $#{session.cost.run}"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## What's Included
|
|
39
|
+
|
|
40
|
+
The report includes:
|
|
41
|
+
- Input/output token counts
|
|
42
|
+
- Cache hit rates
|
|
43
|
+
- Per-interaction costs
|
|
44
|
+
- Total spending
|
|
45
|
+
- Pricing for both normal and long-context models
|
|
46
|
+
|
|
47
|
+
## Report Format
|
|
48
|
+
|
|
49
|
+
The cost report displays information organized by project and run:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Project: my_project
|
|
53
|
+
Run ID: 1234567890
|
|
54
|
+
|
|
55
|
+
Interaction 1 (2024-01-15 10:30:00)
|
|
56
|
+
Input tokens: 1,250
|
|
57
|
+
Output tokens: 450
|
|
58
|
+
Cached tokens: 800
|
|
59
|
+
Cost: $0.0234
|
|
60
|
+
|
|
61
|
+
Interaction 2 (2024-01-15 10:35:00)
|
|
62
|
+
Input tokens: 2,100
|
|
63
|
+
Output tokens: 680
|
|
64
|
+
Cached tokens: 1,500
|
|
65
|
+
Cost: $0.0356
|
|
66
|
+
|
|
67
|
+
Total Cost: $0.0590
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Cost Optimization Tips
|
|
71
|
+
|
|
72
|
+
1. **Use cached prompts** - System prompts that rarely change can be cached, significantly reducing costs
|
|
73
|
+
2. **Choose appropriate models** - Use lighter models (Haiku) for simple tasks, heavier models (Sonnet/Opus) for complex ones
|
|
74
|
+
3. **Batch operations** - Group similar tasks together to maximize cache hits
|
|
75
|
+
4. **Monitor costs regularly** - Run cost reports after each pipeline run to identify expensive operations
|
|
76
|
+
|
|
77
|
+
## Database Schema
|
|
78
|
+
|
|
79
|
+
All queries are persisted in SQLite with:
|
|
80
|
+
- Full conversation history
|
|
81
|
+
- Token usage metrics
|
|
82
|
+
- Timestamps and metadata
|
|
83
|
+
- Tool calls and responses
|
|
84
|
+
- Project and run ID associations
|
|
85
|
+
|
|
86
|
+
This allows for detailed analysis and debugging of AI interactions over time.
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# Pipeline Tips and Tricks
|
|
2
|
+
|
|
3
|
+
This document contains useful patterns and techniques for working with AgentC pipelines.
|
|
4
|
+
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
- [Custom I18n Attributes](#custom-i18n-attributes)
|
|
8
|
+
- [Rewinding to Previous Steps](#rewinding-to-previous-steps)
|
|
9
|
+
- [Agent Review Loop](#agent-review-loop)
|
|
10
|
+
|
|
11
|
+
## Custom I18n Attributes
|
|
12
|
+
|
|
13
|
+
By default, when using i18n interpolation in your prompts, AgentC will use `record.attributes` to provide values for interpolation. However, you can customize this behavior by implementing an `i18n_attributes` method on your record.
|
|
14
|
+
|
|
15
|
+
### Use Case
|
|
16
|
+
|
|
17
|
+
This is useful when:
|
|
18
|
+
- You want to interpolate values that aren't stored as attributes on the record
|
|
19
|
+
- You need to compute or format values specifically for prompts
|
|
20
|
+
- You want to limit which attributes are exposed to i18n interpolation
|
|
21
|
+
- You need to provide different data than what's in the database
|
|
22
|
+
|
|
23
|
+
### Example
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
class MyStore < VersionedStore::Base
|
|
27
|
+
include AgentC::Store
|
|
28
|
+
|
|
29
|
+
record(:my_record) do
|
|
30
|
+
schema do |t|
|
|
31
|
+
t.string(:file_path)
|
|
32
|
+
t.text(:file_contents)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Override the default i18n_attributes
|
|
36
|
+
def i18n_attributes
|
|
37
|
+
{
|
|
38
|
+
file_name: File.basename(file_path),
|
|
39
|
+
file_extension: File.extname(file_path),
|
|
40
|
+
lines_count: file_contents&.lines&.count || 0
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Now in your prompts, you can interpolate these computed values:
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
en:
|
|
51
|
+
analyze_file:
|
|
52
|
+
prompt: "Analyze %{file_name} which has %{lines_count} lines and is a %{file_extension} file"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### How It Works
|
|
56
|
+
|
|
57
|
+
When you use `agent_step` with i18n (either via `prompt_key` or the shorthand syntax), AgentC checks if your record responds to `i18n_attributes`. If it does, that method's return value is used for interpolation. Otherwise, it falls back to `record.attributes`.
|
|
58
|
+
|
|
59
|
+
This works with both explicit prompt keys:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
agent_step(
|
|
63
|
+
:my_step,
|
|
64
|
+
prompt_key: "my_step.prompt",
|
|
65
|
+
cached_prompt_keys: ["my_step.cached"]
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
And with the shorthand syntax:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
agent_step(:my_step)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Return Value
|
|
76
|
+
|
|
77
|
+
The `i18n_attributes` method should return a Hash with symbol or string keys. These keys will be used for interpolation in your i18n strings.
|
|
78
|
+
|
|
79
|
+
## Rewinding to Previous Steps
|
|
80
|
+
|
|
81
|
+
The `rewind_to!` method allows you to restart execution from a previously completed step. This is useful when you need to retry or re-execute steps based on runtime conditions.
|
|
82
|
+
|
|
83
|
+
### Use Case
|
|
84
|
+
|
|
85
|
+
This is useful when:
|
|
86
|
+
- An agent determines that a previous step needs to be re-executed
|
|
87
|
+
- You want to implement retry logic based on validation results
|
|
88
|
+
- You need to loop through steps until certain conditions are met
|
|
89
|
+
- A later step discovers that earlier work needs to be redone
|
|
90
|
+
|
|
91
|
+
### Basic Usage
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
class Store < AgentC::Store
|
|
95
|
+
record(:refactor) do
|
|
96
|
+
schema do
|
|
97
|
+
t.boolean(
|
|
98
|
+
:review_passed,
|
|
99
|
+
default: false
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
t.string(
|
|
103
|
+
:review_feedback,
|
|
104
|
+
default: "none"
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class MyPipeline < AgentC::Pipeline
|
|
111
|
+
# prompt:
|
|
112
|
+
# Perform the refactor.
|
|
113
|
+
# Here is feedback from the reviewer (if any):
|
|
114
|
+
# %{review_feedback}
|
|
115
|
+
agent_step(:perform_refactor)
|
|
116
|
+
|
|
117
|
+
# capture the diff
|
|
118
|
+
step(:capture_diff) do
|
|
119
|
+
record.update!(diff: git.diff)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# prompt:
|
|
123
|
+
# Review this diff: %{diff}
|
|
124
|
+
# schema:
|
|
125
|
+
# review_passed:
|
|
126
|
+
# type: boolean
|
|
127
|
+
# review_feedback:
|
|
128
|
+
# type: string
|
|
129
|
+
agent_step(:review_refactor)
|
|
130
|
+
|
|
131
|
+
step(:verify_output) do
|
|
132
|
+
# if the review hasn't passed,
|
|
133
|
+
# then review_feedback is now
|
|
134
|
+
# present and will be passed
|
|
135
|
+
# back in to refactor step above
|
|
136
|
+
unless record.review_passed
|
|
137
|
+
rewind_to!(:perform_refactor)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### How It Works
|
|
144
|
+
|
|
145
|
+
When you call `rewind_to!(step_name)`, the pipeline:
|
|
146
|
+
1. Validates that the specified step has already been completed
|
|
147
|
+
2. Validates that the step name appears only once in `completed_steps`
|
|
148
|
+
3. Removes the specified step and all subsequent steps from `completed_steps`
|
|
149
|
+
4. Continues execution from the rewound step
|
|
150
|
+
|
|
151
|
+
### Important Notes
|
|
152
|
+
|
|
153
|
+
**Infinite loops**: There's no automatic infinite loop detection. Use your record's state to count rewinds if you are concerned about a potential infinite loop.
|
|
154
|
+
|
|
155
|
+
**Must be called from within a step**: The `rewind_to!` method must be invoked from within a pipeline step during execution.
|
|
156
|
+
|
|
157
|
+
**Step must be completed**: You can only rewind to steps that have already been completed in the current pipeline run. Attempting to rewind to a step that hasn't been completed will raise an `ArgumentError`.
|
|
158
|
+
|
|
159
|
+
**Step must be unique**: If a step name appears multiple times in `completed_steps`, attempting to rewind to it will raise an `ArgumentError`. This prevents ambiguous rewind operations.
|
|
160
|
+
|
|
161
|
+
**State considerations**: When rewinding, be aware that any side effects from the original execution of the rewound steps will remain unless explicitly cleaned up. The pipeline doesn't automatically rollback database changes or other state modifications.
|
|
162
|
+
|
|
163
|
+
### Example: Retry Logic
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
class ProcessWithRetry < AgentC::Pipeline
|
|
167
|
+
step(:attempt_processing) do
|
|
168
|
+
result = process_with_agent
|
|
169
|
+
record.update!(
|
|
170
|
+
attempt_count: record.attempt_count + 1,
|
|
171
|
+
last_result: result
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
step(:check_result) do
|
|
176
|
+
if record.last_result.failed? && record.attempt_count < 3
|
|
177
|
+
# Retry by going back to the processing step
|
|
178
|
+
rewind_to!(:attempt_processing)
|
|
179
|
+
elsif record.last_result.failed?
|
|
180
|
+
task.fail!("Failed after 3 attempts")
|
|
181
|
+
else
|
|
182
|
+
record.update!(status: "completed")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Error Handling
|
|
189
|
+
|
|
190
|
+
If you try to rewind to a step that hasn't been completed yet:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
step(:early_step) do
|
|
194
|
+
rewind_to!(:later_step) # ArgumentError: Cannot rewind to a step that's not been completed yet
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
If a step name appears multiple times in `completed_steps`:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
# This will raise an ArgumentError about non-distinct step names
|
|
202
|
+
rewind_to!(:duplicate_step)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Agent Review Loop
|
|
206
|
+
|
|
207
|
+
The `agent_review_loop` method provides a declarative way to implement iterative review and refinement workflows. It automatically handles the loop logic where an agent implements a solution, reviewers provide feedback, and the agent iterates based on that feedback until the reviewers approve or a maximum number of tries is reached.
|
|
208
|
+
|
|
209
|
+
### Use Case
|
|
210
|
+
|
|
211
|
+
This is useful when:
|
|
212
|
+
- You need an agent to generate code, designs, or content that requires review
|
|
213
|
+
- Multiple reviewers need to evaluate the work from different perspectives
|
|
214
|
+
- The agent should iterate based on feedback until reviewers approve
|
|
215
|
+
- You want to capture review history for audit or debugging purposes
|
|
216
|
+
- You need to limit the number of iteration attempts
|
|
217
|
+
|
|
218
|
+
### Basic Example
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
class RefactorPipeline < AgentC::Pipeline
|
|
222
|
+
agent_review_loop(
|
|
223
|
+
:refactor_code,
|
|
224
|
+
max_tries: 5,
|
|
225
|
+
implement: :initial_refactor,
|
|
226
|
+
iterate: :improve_refactor,
|
|
227
|
+
review: :code_review
|
|
228
|
+
)
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
With i18n translations:
|
|
233
|
+
|
|
234
|
+
```yaml
|
|
235
|
+
en:
|
|
236
|
+
initial_refactor:
|
|
237
|
+
prompt: "Refactor the code to improve readability"
|
|
238
|
+
response_schema:
|
|
239
|
+
code:
|
|
240
|
+
description: "The refactored code"
|
|
241
|
+
|
|
242
|
+
improve_refactor:
|
|
243
|
+
prompt: |
|
|
244
|
+
The previous refactor received this feedback:
|
|
245
|
+
%{feedback}
|
|
246
|
+
|
|
247
|
+
Please improve the refactor based on this feedback.
|
|
248
|
+
response_schema:
|
|
249
|
+
code:
|
|
250
|
+
description: "The improved refactored code"
|
|
251
|
+
|
|
252
|
+
code_review:
|
|
253
|
+
prompt: |
|
|
254
|
+
Review this code change:
|
|
255
|
+
%{diff}
|
|
256
|
+
|
|
257
|
+
Is it ready to merge?
|
|
258
|
+
response_schema:
|
|
259
|
+
approved:
|
|
260
|
+
type: boolean
|
|
261
|
+
description: "Whether the code is approved"
|
|
262
|
+
feedback:
|
|
263
|
+
type: string
|
|
264
|
+
description: "Feedback if not approved (empty if approved)"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### How It Works
|
|
268
|
+
|
|
269
|
+
The `agent_review_loop` executes in iterations:
|
|
270
|
+
|
|
271
|
+
1. **First iteration (try 0)**:
|
|
272
|
+
- Runs all `implement` steps in order
|
|
273
|
+
- If any implement step fails, the loop stops and marks the task as failed
|
|
274
|
+
- Captures git diff of changes
|
|
275
|
+
- Runs all `review` steps with the diff
|
|
276
|
+
- Collects feedback from any reviewers who don't approve
|
|
277
|
+
|
|
278
|
+
2. **Subsequent iterations (try 1+)**:
|
|
279
|
+
- Runs all `iterate` steps with accumulated feedback
|
|
280
|
+
- If any iterate step fails, the loop stops and marks the task as failed
|
|
281
|
+
- Captures git diff of changes
|
|
282
|
+
- Runs all `review` steps with the new diff
|
|
283
|
+
- Collects feedback from any reviewers who don't approve
|
|
284
|
+
|
|
285
|
+
3. **Loop continues until**:
|
|
286
|
+
- All reviewers approve (feedback list is empty), OR
|
|
287
|
+
- `max_tries` is reached, OR
|
|
288
|
+
- Any step fails, OR
|
|
289
|
+
- The task is marked as failed by other means
|
|
290
|
+
|
|
291
|
+
### Multiple Steps
|
|
292
|
+
|
|
293
|
+
You can specify multiple steps for implement, iterate, and review:
|
|
294
|
+
|
|
295
|
+
```ruby
|
|
296
|
+
agent_review_loop(
|
|
297
|
+
:multi_file_refactor,
|
|
298
|
+
max_tries: 5,
|
|
299
|
+
implement: [
|
|
300
|
+
:refactor_controller,
|
|
301
|
+
:refactor_model,
|
|
302
|
+
:refactor_view
|
|
303
|
+
],
|
|
304
|
+
iterate: [
|
|
305
|
+
:improve_controller,
|
|
306
|
+
:improve_model,
|
|
307
|
+
:improve_view
|
|
308
|
+
],
|
|
309
|
+
review: [
|
|
310
|
+
:code_quality_review,
|
|
311
|
+
:security_review,
|
|
312
|
+
:performance_review
|
|
313
|
+
]
|
|
314
|
+
)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Steps are executed in order. If any step fails, the loop stops immediately.
|
|
318
|
+
|
|
319
|
+
### Feedback Interpolation
|
|
320
|
+
|
|
321
|
+
The `iterate` steps automatically receive a `%{feedback}` interpolation variable containing all feedback from reviewers, joined with `"\n---\n"` as a separator:
|
|
322
|
+
|
|
323
|
+
```yaml
|
|
324
|
+
improve_refactor:
|
|
325
|
+
prompt: |
|
|
326
|
+
Previous feedback from reviewers:
|
|
327
|
+
%{feedback}
|
|
328
|
+
|
|
329
|
+
Please address all concerns.
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Review Schema
|
|
333
|
+
|
|
334
|
+
Your "review" I18n should **not** include any response schema. AgentC will
|
|
335
|
+
configure the schema for you.
|
|
336
|
+
|
|
337
|
+
Review steps must return a response with these fields:
|
|
338
|
+
- `approved` (boolean): Whether the work is approved
|
|
339
|
+
- `feedback` (string): Feedback message if not approved (can be empty string if approved)
|
|
340
|
+
|
|
341
|
+
If a review step fails to return valid data, the task is marked as failed.
|
|
342
|
+
|
|
343
|
+
### Optional: Recording Reviews
|
|
344
|
+
|
|
345
|
+
If your record implements an `add_review` method, it will be called after each review iteration with the diff and collected feedback:
|
|
346
|
+
|
|
347
|
+
```ruby
|
|
348
|
+
record(:my_record) do
|
|
349
|
+
schema do |t|
|
|
350
|
+
t.json(:reviews, default: [])
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def add_review(diff:, feedbacks:)
|
|
354
|
+
self.reviews ||= []
|
|
355
|
+
self.reviews << {
|
|
356
|
+
timestamp: Time.now.iso8601,
|
|
357
|
+
diff: diff,
|
|
358
|
+
feedbacks: feedbacks
|
|
359
|
+
}
|
|
360
|
+
save!
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
This allows you to maintain a complete history of all review iterations.
|
|
366
|
+
|
|
367
|
+
### Default Iterate Behavior
|
|
368
|
+
|
|
369
|
+
If you don't specify `iterate`, it defaults to the same value as `implement`:
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
# These are equivalent:
|
|
373
|
+
agent_review_loop(:refactor, implement: :refactor_code, review: :review)
|
|
374
|
+
agent_review_loop(:refactor, implement: :refactor_code, iterate: :refactor_code, review: :review)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
This is useful when the same prompt can handle both initial implementation and iteration based on feedback.
|
|
378
|
+
|
|
379
|
+
### Important Notes
|
|
380
|
+
|
|
381
|
+
**Required parameters**: You must provide either `implement` or `iterate` (or both). Providing only `review` will raise an `ArgumentError`.
|
|
382
|
+
|
|
383
|
+
**Max tries behavior**: When `max_tries` is reached, the loop completes the step successfully even if reviews haven't all approved. The loop doesn't fail the task when max tries is exceeded.
|
|
384
|
+
|
|
385
|
+
**Git diff**: The git diff is captured after each iteration's implementation/iteration steps complete, and is passed to review steps via the `%{diff}` interpolation variable.
|
|
386
|
+
|
|
387
|
+
**Failure handling**: If any implement, iterate, or review step returns invalid data or raises an exception, the entire agent_review_loop step is marked as failed and the task stops.
|
|
388
|
+
|
|
389
|
+
**Step naming**: The `agent_review_loop` counts as a single pipeline step with the name you provide (e.g., `:refactor_code`), not separate steps for each iteration.
|
|
390
|
+
|
|
391
|
+
### Complete Example
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
class DocumentationPipeline < AgentC::Pipeline
|
|
395
|
+
agent_review_loop(
|
|
396
|
+
:write_documentation,
|
|
397
|
+
max_tries: 3,
|
|
398
|
+
implement: [:draft_readme, :draft_examples],
|
|
399
|
+
iterate: [:improve_readme, :improve_examples],
|
|
400
|
+
review: [:technical_review, :style_review]
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
step(:publish) do
|
|
404
|
+
# Only reached if reviews passed or max_tries exceeded
|
|
405
|
+
record.update!(published: true)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
With a record that tracks review history:
|
|
411
|
+
|
|
412
|
+
```ruby
|
|
413
|
+
record(:documentation) do
|
|
414
|
+
schema do |t|
|
|
415
|
+
t.text(:readme_content)
|
|
416
|
+
t.text(:examples_content)
|
|
417
|
+
t.json(:review_history, default: [])
|
|
418
|
+
t.boolean(:published, default: false)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def add_review(diff:, feedbacks:)
|
|
422
|
+
self.review_history << {
|
|
423
|
+
iteration: review_history.length + 1,
|
|
424
|
+
timestamp: Time.now.iso8601,
|
|
425
|
+
diff_size: diff.length,
|
|
426
|
+
feedback_count: feedbacks.length,
|
|
427
|
+
feedbacks: feedbacks
|
|
428
|
+
}
|
|
429
|
+
save!
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def i18n_attributes
|
|
433
|
+
attributes.merge(
|
|
434
|
+
total_reviews: review_history.length,
|
|
435
|
+
last_feedback: review_history.last&.dig("feedbacks")&.join("\n---\n") || "none"
|
|
436
|
+
)
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### When to Use agent_review_loop vs rewind_to!
|
|
442
|
+
|
|
443
|
+
Use `agent_review_loop` when:
|
|
444
|
+
- The review and iteration logic is straightforward and consistent
|
|
445
|
+
- You want a declarative approach with less boilerplate
|
|
446
|
+
- Multiple reviewers are involved
|
|
447
|
+
- You want automatic feedback collection and interpolation
|
|
448
|
+
|
|
449
|
+
Use `rewind_to!` when:
|
|
450
|
+
- You need custom logic to determine whether to retry
|
|
451
|
+
- The retry conditions are complex or context-dependent
|
|
452
|
+
- You need to rewind to steps other than the immediate previous one
|
|
453
|
+
- You want explicit control over the retry logic
|