For a simple editorial workflow, I would almost certainly choose reduce, but the "cool" choice (and the one that feels more like an autonomous agent) is generate.
Here is how to decide based on what your "workflow" actually looks like:
Option 1: Use reduce (The "Linear Polisher")
Use reduce if you have a fixed set of operations you want to run on a piece of text (e.g., "Fix Grammar" "Shorten" "Change Tone").
In this scenario, you are "folding" a list of instructions over your text.
let tasks = ["fix grammar", "make it sound more professional", "convert to markdown"]
let original_text = "i is writing a nushell script for agents"
let final_draft = ($tasks | reduce -f $original_text {|task, current_text|
# Each step takes the output of the previous LLM call
call-brain $"Apply this edit: ($task)" $current_text
})
- Best for: Consistency. You know exactly how many steps are involved.
- Logic: .
Option 2: Use generate (The "Iterative Refiner")
Use generate if you want the agent to keep editing until it’s happy. generate creates a stream, which is perfect for an "Editorial Loop" where the agent looks at its own work and decides if it needs another pass.
# generate {|state| {out: <value_to_emit>, next: <next_state>} } <initial_state>
let initial_state = { text: "The draft...", count: 0, done: false }
let editorial_history = (generate {|state|
if $state.done or $state.count > 3 { return null } # Exit condition
let feedback = (call-brain "Review this text. Is it perfect? Respond with 'YES' or feedback." $state.text)
if ($feedback | str contains "YES") {
{out: $state.text, next: {done: true}}
} else {
let new_text = (call-brain $"Apply this feedback: ($feedback)" $state.text)
{out: $new_text, next: {text: $new_text, count: ($state.count + 1), done: false}}
}
} $initial_state)
let final_result = ($editorial_history | last)
- Best for: Quality. It mimics a human editor who sends back a draft multiple times.
- Logic: Start Is it good? No? Edit and Repeat.
Comparison Table
| Feature | reduce |
generate |
|---|---|---|
| Flow | Deterministic (1 2 3) | Dynamic (Loop until done) |
| Termination | Ends when the list of tasks is empty | Ends when a condition is met (or limit reached) |
| Visibility | Harder to see intermediate steps mid-stream | Emits every version as a stream member (great for logging) |
| Use Case | Batch processing | Self-correcting agents |
My Recommendation
If you are building an Agent System with xs, use generate.
Why? Because generate allows the LLM to decide the "next state." It can output the current draft to an xs stream at every iteration, giving you a perfect audit trail of how the "editorial brain" evolved the text.
Would you like me to show you how to wrap that generate loop so it writes every "revision" to an xs stream in real-time?
Be sure to stay tuned if you want to learn more about xs (cross.stream), event sourcing and nushell with AI and LLMs.