agent_c 2.9979 → 2.71828
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/CLAUDE.md +1 -1
- data/README.md +1 -1
- data/TODO.md +5 -98
- data/docs/chat-methods.md +6 -5
- data/docs/pipeline-tips-and-tricks.md +0 -382
- data/docs/testing.md +19 -19
- data/lib/agent_c/agent/chat_response.rb +4 -10
- data/lib/agent_c/batch.rb +4 -40
- data/lib/agent_c/configs/repo.rb +1 -1
- data/lib/agent_c/pipeline.rb +83 -47
- data/lib/agent_c/processor.rb +1 -1
- data/lib/agent_c/schema.rb +22 -8
- data/lib/agent_c/session.rb +2 -1
- data/lib/agent_c/tools/edit_file.rb +1 -3
- data/lib/agent_c/tools.rb +0 -1
- data/lib/agent_c/utils/git.rb +14 -26
- data/lib/agent_c/version.rb +1 -1
- metadata +2 -5
- data/docs/batch.md +0 -503
- data/lib/agent_c/pipelines/agent.rb +0 -219
- data/lib/agent_c/tools/git_status.rb +0 -30
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8321a1602d20f566b59365d641fecb934340b043d6544e43b01b2951e947282b
|
|
4
|
+
data.tar.gz: bf3cd0d58944294f4e83e080ac7aebfff8677798c96426411e559c8f7e7f6a7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5dbe7a3d1ca921db961a5a3685c01f4e539b0358506cb45f9d6a04c462cb0657efa45eff16eea4f65c00bdb6f113b40551b38e71f5d61ceadf8463cec1ea9007
|
|
7
|
+
data.tar.gz: 4b407bc2cf7086536bce703b209552b8753c6b3eef37b096437728095945d247a5b6643113487ad739339e407b6e4c43c6b0cdd72aeca3f75551ab9e1ac763e0
|
data/CLAUDE.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- DO NOT add example scripts. Either add it to the readme or make a test.
|
|
9
9
|
- DO NOT add documentation outside of the README
|
|
10
10
|
- DO NOT program defensively. If something should respond_to?() a method then just invoke the method. An error is better than a false positive
|
|
11
|
-
-
|
|
11
|
+
- If you need to test a one-off script, write a test-case and run it instad of writing a temporary file or using a giant shell script
|
|
12
12
|
- DO NOT edit the singleton class of an object. If you think you need to do this, ideas for avoiding: inject an object, create a module and include it, make a base class.
|
|
13
13
|
|
|
14
14
|
# TESTING
|
data/README.md
CHANGED
|
@@ -336,7 +336,7 @@ I suggest following the structure of the [example template](./template).
|
|
|
336
336
|
|
|
337
337
|
Detailed guides for all features:
|
|
338
338
|
|
|
339
|
-
- **[
|
|
339
|
+
- **[Main README](../README.md)** - Batch and Pipeline usage (primary approach)
|
|
340
340
|
- **[Pipeline Tips and Tricks](docs/pipeline-tips-and-tricks.md)** - Useful patterns and techniques for pipelines
|
|
341
341
|
- **[Chat Methods](docs/chat-methods.md)** - Using session.prompt and session.chat for direct interactions
|
|
342
342
|
- **[Tools](docs/tools.md)** - Built-in tools for file operations and code interaction
|
data/TODO.md
CHANGED
|
@@ -5,101 +5,8 @@ Things I'd like to work on:
|
|
|
5
5
|
- Make injecting a Chat record simpler.
|
|
6
6
|
- Make injecting Git simpler (make injecting anything easier)
|
|
7
7
|
- Add a request queue to AgentC::Chat so that we can rate-limit and retry on error
|
|
8
|
-
- Use spring for run_rails_test, but add a timeout condition where it kills the
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Some scratch:
|
|
14
|
-
|
|
15
|
-
```ruby
|
|
16
|
-
agent_iterate_loop(
|
|
17
|
-
:implement_query_object,
|
|
18
|
-
max_tries: 17,
|
|
19
|
-
plan: [
|
|
20
|
-
:plan_step_1,
|
|
21
|
-
:plan_step_2,
|
|
22
|
-
],
|
|
23
|
-
implement: [
|
|
24
|
-
:implement_step_1,
|
|
25
|
-
:implement_step_2,
|
|
26
|
-
],
|
|
27
|
-
# optional: defaults to implement
|
|
28
|
-
iterate: [
|
|
29
|
-
:address_feedback_1
|
|
30
|
-
]
|
|
31
|
-
review: [
|
|
32
|
-
:review_step_1,
|
|
33
|
-
:review_step_2
|
|
34
|
-
],
|
|
35
|
-
)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Thoughts:
|
|
39
|
-
|
|
40
|
-
- This makes a demand on the `response_schema` of the prompts.
|
|
41
|
-
- Does the `task` track the looping here or the `record`?
|
|
42
|
-
- Is the progress tracked in memory? This can be one step...
|
|
43
|
-
- What if you have multiple `agent_iterate_loop` invocations? How do we store diffs/reviews for each of those?
|
|
44
|
-
- The `agent_step` applies the result to the record. If we track it on the task, we could get the attributes to the right place. Can we extend the response schema? The `plan` step definitely needs to update the record, so it needs to specify response_schema. The `implement/iterate` steps *might* need to accept response_schema (eg, path of file created? Or not necessary?, maybe not necessary.)
|
|
45
|
-
- Does a failed review trigger plan -> iterate -> review or just iterate -> review
|
|
46
|
-
- The implement/iterate arrays could include "plan" steps so that they know if they're starting from scratch or not.
|
|
47
|
-
- Do we run every review or stop at the first one?
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
```ruby
|
|
51
|
-
def agent_iterate_loop(
|
|
52
|
-
name,
|
|
53
|
-
max_tries: 3,
|
|
54
|
-
implement: [],
|
|
55
|
-
iterate: implement,
|
|
56
|
-
review: [],
|
|
57
|
-
)
|
|
58
|
-
step(name) do
|
|
59
|
-
tries = 0
|
|
60
|
-
|
|
61
|
-
while(tries < max_tries)
|
|
62
|
-
if tries == 0
|
|
63
|
-
implement.each do |name|
|
|
64
|
-
process_prompt(name)
|
|
65
|
-
end
|
|
66
|
-
else
|
|
67
|
-
iterate.each do |name|
|
|
68
|
-
process_prompt(
|
|
69
|
-
name,
|
|
70
|
-
additional_i18n_attrs: {
|
|
71
|
-
feedback: feedback.join("\n---\n")
|
|
72
|
-
}
|
|
73
|
-
)
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
feedbacks = []
|
|
78
|
-
|
|
79
|
-
diff = git.diff
|
|
80
|
-
review.each do |name|
|
|
81
|
-
result = process_prompt(
|
|
82
|
-
name,
|
|
83
|
-
schema: -> {
|
|
84
|
-
t.boolean(:pass)
|
|
85
|
-
t.string(:feedback)
|
|
86
|
-
},
|
|
87
|
-
additional_i18n_attrs: {
|
|
88
|
-
diff:
|
|
89
|
-
}
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
if !result.fetch("pass")
|
|
93
|
-
feedbacks << result.fetch("feedback")
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
if record.respond_to?(:add_review)
|
|
98
|
-
record.add_review(diff:, feedbacks:)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
break if feedbacks.empty?
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
```
|
|
8
|
+
- Use spring for run_rails_test, but add a timeout condition where it kills the
|
|
9
|
+
process if no stdout appears for a while and tries again without spring.
|
|
10
|
+
- tool calls should write the full results to file (except for readfile) and pass
|
|
11
|
+
back a reference for future queries. For example, if RunRailsTest gives way too
|
|
12
|
+
much output, we have to truncate but how to see the rest?
|
data/docs/chat-methods.md
CHANGED
|
@@ -43,14 +43,15 @@ answer = chat.get("What is 2 + 2?")
|
|
|
43
43
|
# give a reason why.
|
|
44
44
|
#
|
|
45
45
|
# The response will look like one of the following:
|
|
46
|
-
# Success response (just the data fields):
|
|
47
46
|
# {
|
|
47
|
+
# status: "success",
|
|
48
48
|
# name: "...",
|
|
49
49
|
# email: "...",
|
|
50
50
|
# }
|
|
51
|
-
# OR
|
|
51
|
+
# OR:
|
|
52
52
|
# {
|
|
53
|
-
#
|
|
53
|
+
# status: "failure",
|
|
54
|
+
# message: "some reason why it couldn't do it"
|
|
54
55
|
# }
|
|
55
56
|
|
|
56
57
|
schema = AgentC::Schema.result do
|
|
@@ -62,10 +63,10 @@ result = chat.get(
|
|
|
62
63
|
"Extract the name and email from this text: 'Contact John at john@example.com'",
|
|
63
64
|
schema: schema
|
|
64
65
|
)
|
|
65
|
-
# => { "name" => "John", "email" => "john@example.com" }
|
|
66
|
+
# => { "status" => "success", "name" => "John", "email" => "john@example.com" }
|
|
66
67
|
|
|
67
68
|
# If the LLM can't complete the task, it returns an error response:
|
|
68
|
-
# => { "
|
|
69
|
+
# => { "status" => "error", "message" => "No email found in the text" }
|
|
69
70
|
```
|
|
70
71
|
|
|
71
72
|
### Using confirm and out_of for consensus
|
|
@@ -2,12 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
This document contains useful patterns and techniques for working with AgentC pipelines.
|
|
4
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
5
|
## Custom I18n Attributes
|
|
12
6
|
|
|
13
7
|
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.
|
|
@@ -75,379 +69,3 @@ agent_step(:my_step)
|
|
|
75
69
|
### Return Value
|
|
76
70
|
|
|
77
71
|
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
|