Funktionelle Agent-loops: Hvorfor Nushells generate slår det imperative loop
Opbygning af AI-agenter involverer ofte et forudsigeligt, men rodet loop:
- Send historik til LLM'en.
- Modtag et svar.
- Hvis LLM'en vil kalde et værktøj, udfør det og gentag.
- Hvis det er et endeligt svar, stop.
I de fleste sprog implementeres dette med et while True-loop og et muterbart history-array.
Selvom dette for det meste fungerer, er det ofte "støjende" og håndterer tilstandsopdateringer og afslutningsbetingelser på en måde, der blander logik og kontrolflow.
Brug i stedet Nushells generate-kommando.
generate-mønsteret
Nushell er en shell med strukturerede data og en stærk funktionel indflydelse.
Nushells generate-kommando er en "generator" eller "unfold"-operation. Den tager en indledende tilstand og en lukning (en closure). Lukningen returnerer en post med to valgfrie nøgler:
out: Hvad der skal udsendes til strømmen.next: Tilstanden for den næste iteration.
Hvis next mangler, stopper loopet.
Hvorfor det er bedre for agenter
I vores implementering af agent.nu udnytter vi generate til at håndtere multi-turn samtalen med Gemini 2.0 Flash:
generate {|history|
let response = call_gemini $history
let message = $response.candidates.0.content
# Bruger den nye 'get -o' (optional) syntaks i stedet for forældede '-i'
let function_calls = $message.parts | where { |p| ($p | get -o functionCall) != null }
if ($function_calls | is-not-empty) {
# ... logik til udførelse af værktøjer ...
let next_history = ($history | append $message | append $tool_results)
{ out: $message, next: $next_history }
} else {
{ out: $message } # Ingen 'next' betyder, at vi er færdige!
}
} $initial_history
1. Tilstand er en Uforanderlig Strøm
I et primitivt loop tilføjer du konstant history.push(). I generate sendes historikken fra den ene iteration til den næste som en værdi. Dette gør logikken lettere at ræsonnere over - hvert "trin" af agenten er en ren transformation af history til (output, new_history).
2. Agenten er en stream
Fordi generate returnerer en stream, bliver din agent en første klasses Nushell-borger. Du kan sende agentens fremskridt direkte:
agent "Planlæg en tur til Tokyo" | each { |msg| print $msg.role }
Du kan endda bruge take, first eller where på agentens "tanker", mens de sker!
Hvis agenten tager 10 trin for at løse et problem, skriver generate hvert trin ud, efterhånden som de opstår. Med mindre du fravælger og kun vil have svaret med en simpel last tilføjelse til din pipeline.
3. Klare Afslutningsbetingelser
Imperative loops har ofte komplekse if/else-kæder med break eller return udsagn begravet dybt nede. Med generate er tilstedeværelsen eller fraværet af next-nøglen det eneste, der bestemmer, om loopet fortsætter. Dette tvinger dig til at være eksplicit omkring dine tilstandsovergange.
4. Komponerbarhed
Da outputtet er en standard Nushell-liste/strøm, kan du nemt gemme hele sporingen af en agents ræsonnement til en JSON-fil eller vise det som en tabel uden at tilføje nogen "logningskode" inde i selve loopet.
agent "Hvad er klokken?" | save trace.json
Konklusion
Nushells generate forvandler agent-loopet fra et stykke infrastruktur til en datastrøm. Den fjerner den overflødige kode fra manuel iteration og lader dig fokusere på kernen i logikken: hvordan agentens tilstand udvikler sig over tid.
Ved at omfavne funktionelle mønstre som generate, kan vi bygge agenter, der er renere, mere forudsigelige og naturligt integreret i shell-miljøet.