Agentes de Fluxo de Trabalho
Agentes de fluxo de trabalho orquestram múltiplos agentes em padrões previsíveis—pipelines sequenciais, execução paralela ou loops iterativos. Diferente do LlmAgent, que usa raciocínio de IA, agentes de fluxo de trabalho seguem caminhos de execução determinísticos.
Início Rápido
Crie um novo projeto:
cargo new workflow_demo
cd workflow_demo
Adicione dependências a Cargo.toml:
[dependencies]
adk-rust = "0.2.0"
tokio = { version = "1.40", features = ["full"] }
dotenvy = "0.15"
Crie .env:
echo 'GOOGLE_API_KEY=your-api-key' > .env
SequentialAgent
SequentialAgent executa sub-agentes um após o outro. Cada agente vê o histórico de conversa acumulado dos agentes anteriores.
Quando Usar
- Pipelines multi-etapas onde a saída alimenta o próximo passo
- Fluxos de trabalho de Pesquisa → Análise → Resumo
- Cadeias de transformação de dados
Exemplo Completo
Substitua src/main.rs:
use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
// Step 1: Research agent gathers information
let researcher = LlmAgentBuilder::new("researcher")
.instruction("Research the given topic. List 3-5 key facts or points. \
Be factual and concise.")
.model(model.clone())
.output_key("research") // Saves output to state
.build()?;
// Step 2: Analyzer agent identifies patterns
let analyzer = LlmAgentBuilder::new("analyzer")
.instruction("Based on the research above, identify 2-3 key insights \
or patterns. What's the bigger picture?")
.model(model.clone())
.output_key("analysis")
.build()?;
// Step 3: Summarizer creates final output
let summarizer = LlmAgentBuilder::new("summarizer")
.instruction("Create a brief executive summary combining the research \
and analysis. Keep it under 100 words.")
.model(model.clone())
.build()?;
// Create the sequential pipeline
let pipeline = SequentialAgent::new(
"research_pipeline",
vec![Arc::new(researcher), Arc::new(analyzer), Arc::new(summarizer)],
).with_description("Research → Analyze → Summarize");
println!("📋 Sequential Pipeline: Research → Analyze → Summarize");
println!();
Launcher::new(Arc::new(pipeline)).run().await?;
Ok(())
}
Execute:
cargo run
Exemplo de Interação
You: Tell me about Rust programming language
🔄 [researcher] Researching...
Here are key facts about Rust:
1. Systems programming language created at Mozilla in 2010
2. Memory safety without garbage collection via ownership system
3. Zero-cost abstractions and minimal runtime
4. Voted "most loved language" on Stack Overflow for 7 years
5. Used by Firefox, Discord, Dropbox, and Linux kernel
🔄 [analyzer] Analyzing...
Key insights:
1. Rust solves the memory safety vs performance tradeoff
2. Strong developer satisfaction drives rapid adoption
3. Trust from major tech companies validates production-readiness
🔄 [summarizer] Summarizing...
Rust is a systems language that achieves memory safety without garbage
collection through its ownership model. Created at Mozilla in 2010, it's
been rated the most loved language for 7 consecutive years. Major companies
like Discord and Linux kernel adopt it for its zero-cost abstractions
and performance guarantees.
Como Funciona
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Researcher │ → │ Analyzer │ → │ Summarizer │
│ (step 1) │ │ (step 2) │ │ (step 3) │
└─────────────┘ └─────────────┘ └─────────────┘
↓ ↓ ↓
"Key facts..." "Insights..." "Executive summary"
- A mensagem do usuário vai para o primeiro agente (Researcher)
- A resposta do Researcher é adicionada ao histórico
- O Analyzer vê: mensagem do usuário + resposta do researcher
- O Summarizer vê: mensagem do usuário + respostas do researcher + do analyzer
- O pipeline é concluído quando o último agente termina
ParallelAgent
ParallelAgent executa todos os sub-agents concorrentemente. Cada agent recebe a mesma entrada e trabalha de forma independente.
Quando Usar
- Múltiplas perspectivas sobre o mesmo tópico
- Processamento em fan-out (mesma entrada, análises diferentes)
- Cenários de multitarefa críticos para a velocidade
Exemplo Completo
use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
// Three analysts with DISTINCT personas (important for parallel execution)
let technical = LlmAgentBuilder::new("technical_analyst")
.instruction("You are a senior software architect. \
FOCUS ONLY ON: code quality, system architecture, scalability, \
security vulnerabilities, and tech stack choices. \
Start your response with '🔧 TECHNICAL:' and give 2-3 bullet points.")
.model(model.clone())
.build()?;
let business = LlmAgentBuilder::new("business_analyst")
.instruction("You are a business strategist and MBA graduate. \
FOCUS ONLY ON: market opportunity, revenue model, competition, \
cost structure, and go-to-market strategy. \
Start your response with '💼 BUSINESS:' and give 2-3 bullet points.")
.model(model.clone())
.build()?;
let user_exp = LlmAgentBuilder::new("ux_analyst")
.instruction("You are a UX researcher and designer. \
FOCUS ONLY ON: user journey, accessibility, pain points, \
visual design, and user satisfaction metrics. \
Start your response with '🎨 UX:' and give 2-3 bullet points.")
.model(model.clone())
.build()?;
// Create parallel agent
let multi_analyst = ParallelAgent::new(
"multi_perspective",
vec![Arc::new(technical), Arc::new(business), Arc::new(user_exp)],
).with_description("Technical + Business + UX analysis in parallel");
println!("⚡ Parallel Analysis: Technical | Business | UX");
println!(" (All three run simultaneously!)");
println!();
Launcher::new(Arc::new(multi_analyst)).run().await?;
Ok(())
}
💡 Dica: Torne as instruções do
ParallelAgentaltamente distintas com personas únicas, áreas de foco e prefixos de resposta. Isso garante que cadaAgentproduza uma saída única.
Exemplo de Interação
You: Evaluate a mobile banking app
🔧 TECHNICAL:
• Requires robust API security: OAuth 2.0, certificate pinning, encrypted storage
• Offline mode with sync requires complex state management and conflict resolution
• Biometric auth integration varies significantly across iOS/Android platforms
💼 BUSINESS:
• Highly competitive market - need unique differentiator (neobanks, traditional banks)
• Revenue model: interchange fees, premium tiers, or lending products cross-sell
• Regulatory compliance costs significant: PCI-DSS, regional banking laws, KYC/AML
🎨 UX:
• Critical: fast task completion - check balance must be < 3 seconds
• Accessibility essential: screen reader support, high contrast mode, large touch targets
• Trust indicators important: security badges, familiar banking patterns
Como Funciona
┌─────────────────┐
│ User Message │
└────────┬────────┘
┌───────────────────┼───────────────────┐
↓ ↓ ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Technical │ │ Business │ │ UX │
│ Analyst │ │ Analyst │ │ Analyst │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
↓ ↓ ↓
(response 1) (response 2) (response 3)
Todos os agents iniciam simultaneamente e os resultados são transmitidos conforme são concluídos.
LoopAgent
LoopAgent executa sub-agentes repetidamente até que uma condição de saída seja atendida ou o número máximo de iterações seja atingido.
Quando Usar
- Refinamento iterativo (rascunho → crítica → melhoria → repetir)
- Lógica de repetição com melhoria
- Portões de qualidade que exigem múltiplas passagens
ExitLoopTool
Para sair de um loop prematuramente, atribua a um agente o ExitLoopTool. Quando acionada, ela sinaliza para o loop parar.
Exemplo Completo
use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
// Critic agent evaluates content
let critic = LlmAgentBuilder::new("critic")
.instruction("Review the content for quality. Score it 1-10 and list \
specific improvements needed. Be constructive but critical.")
.model(model.clone())
.build()?;
// Refiner agent improves based on critique
let refiner = LlmAgentBuilder::new("refiner")
.instruction("Apply the critique to improve the content. \
If the score is 8 or higher, call exit_loop to finish. \
Otherwise, provide an improved version.")
.model(model.clone())
.tool(Arc::new(ExitLoopTool::new())) // Can exit the loop
.build()?;
// Create inner sequential: critic → refiner
let critique_refine = SequentialAgent::new(
"critique_refine_step",
vec![Arc::new(critic), Arc::new(refiner)],
);
// Wrap in loop with max 3 iterations
let iterative_improver = LoopAgent::new(
"iterative_improver",
vec![Arc::new(critique_refine)],
).with_max_iterations(3)
.with_description("Critique-refine loop (max 3 passes)");
println!("🔄 Iterative Improvement Loop");
println!(" critic → refiner → repeat (max 3x or until quality >= 8)");
println!();
Launcher::new(Arc::new(iterative_improver)).run().await?;
Ok(())
}
Exemplo de Interação
Você: Escreva um slogan para uma cafeteria
🔄 Iteração 1
[critic] Pontuação: 5/10. "Bom café aqui" é muito genérico. Necessita de:
- Proposta de valor única
- Conexão emocional
- Fraseado memorável
[refiner] Melhorado: "Onde cada xícara conta uma história"
🔄 Iteração 2
[critic] Pontuação: 7/10. Melhor! Mas poderia ser mais forte:
- Mais orientada à ação
- Sugere a experiência
[refiner] Melhorado: "Crie seu momento perfeito"
🔄 Iteração 3
[critic] Pontuação: 8/10. Forte, orientada à ação, experiencial.
Menor: poderia ser mais distintiva.
[refiner] A pontuação é 8+, limite de qualidade atingido!
[exit_loop chamado]
Final: "Crie seu momento perfeito"
Como Funciona
┌──────────────────────────────────────────┐
│ LoopAgent │
│ ┌────────────────────────────────────┐ │
│ │ SequentialAgent │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
→ │ │ │ Crítico │ → │ Refinador │ │ │ →
│ │ │ (revisar)│ │ (melhorar ou │ │ │
│ │ └──────────┘ │ exit_loop) │ │ │
│ │ └──────────────┘ │ │
│ └────────────────────────────────────┘ │
│ ↑_____________↓ │
│ repetir até a saída │
└──────────────────────────────────────────┘
ConditionalAgent (Baseado em Regras)
ConditionalAgent ramifica a execução com base em uma condição síncrona, baseada em regras. Use isso para roteamento determinístico, como testes A/B ou roteamento baseado em ambiente.
ConditionalAgent::new("router", |ctx| ctx.session().state().get("premium")..., premium_agent)
.with_else(basic_agent)
Nota: Para roteamento inteligente baseado em LLM, use
LlmConditionalAgentem vez disso.
LlmConditionalAgent (Baseado em LLM)
LlmConditionalAgent usa um LLM para classificar a entrada do usuário e rotear para o sub-agente apropriado. Isso é ideal para roteamento inteligente onde a decisão de roteamento requer a compreensão do conteúdo.
Quando Usar
- Classificação de intenção - Roteia com base no que o usuário está perguntando
- Roteamento multi-direcional - Mais de 2 destinos
- Roteamento sensível ao contexto - Requer compreensão, não apenas palavras-chave
Exemplo Completo
use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
// Create specialist agents
let tech_agent: Arc<dyn Agent> = Arc::new(
LlmAgentBuilder::new("tech_expert")
.instruction("You are a senior software engineer. Be precise and technical.")
.model(model.clone())
.build()?
);
let general_agent: Arc<dyn Agent> = Arc::new(
LlmAgentBuilder::new("general_helper")
.instruction("You are a friendly assistant. Explain simply, use analogies.")
.model(model.clone())
.build()?
);
let creative_agent: Arc<dyn Agent> = Arc::new(
LlmAgentBuilder::new("creative_writer")
.instruction("You are a creative writer. Be imaginative and expressive.")
.model(model.clone())
.build()?
);
// LLM classifies the query and routes accordingly
let router = LlmConditionalAgent::new("smart_router", model.clone())
.instruction("Classify the user's question as exactly ONE of: \
'technical' (coding, debugging, architecture), \
'general' (facts, knowledge, how-to), \
'creative' (writing, stories, brainstorming). \
Respond with ONLY the category name.")
.route("technical", tech_agent)
.route("general", general_agent.clone())
.route("creative", creative_agent)
.default_route(general_agent)
.build()?;
println!("🧠 LLM-Powered Intelligent Router");
Launcher::new(Arc::new(router)).run().await?;
Ok(())
}
Exemplo de Interação
You: How do I fix a borrow error in Rust?
[Routing to: technical]
[Agent: tech_expert]
A borrow error occurs when Rust's ownership rules are violated...
You: What's the capital of France?
[Routing to: general]
[Agent: general_helper]
The capital of France is Paris! It's a beautiful city...
You: Write me a haiku about the moon
[Routing to: creative]
[Agent: creative_writer]
Silver orb above,
Shadows dance on silent waves—
Night whispers secrets.
Como Funciona
┌─────────────────┐
│ User Message │
└────────┬────────┘
↓
┌─────────────────┐
│ LLM Classifies│ "technical" / "general" / "creative"
│ (smart_router)│
└────────┬────────┘
↓
┌────┴────┬──────────┐
↓ ↓ ↓
┌───────┐ ┌───────┐ ┌─────────┐
│ tech │ │general│ │creative │
│expert │ │helper │ │ writer │
└───────┘ └───────┘ └─────────┘
Combinando Agentes de Fluxo de Trabalho
Agentes de fluxo de trabalho podem ser aninhados para padrões complexos.
Sequencial + Paralelo + Loop
use adk_rust::prelude::*;
use std::sync::Arc;
// 1. Análise paralela de múltiplas perspectivas
let parallel_analysis = ParallelAgent::new(
"multi_analysis",
vec![Arc::new(tech_analyst), Arc::new(biz_analyst)],
);
// 2. Sintetizar os resultados paralelos
let synthesizer = LlmAgentBuilder::new("synthesizer")
.instruction("Combine all analyses into a unified recommendation.")
.model(model.clone())
.build()?;
// 3. Loop de qualidade: criticar e refinar
let quality_loop = LoopAgent::new(
"quality_check",
vec![Arc::new(critic), Arc::new(refiner)],
).with_max_iterations(2);
// Pipeline final: paralelo → sintetizar → loop de qualidade
let full_pipeline = SequentialAgent::new(
"full_analysis_pipeline",
vec![
Arc::new(parallel_analysis),
Arc::new(synthesizer),
Arc::new(quality_loop),
],
);
Rastreando a Execução do Fluxo de Trabalho
Para ver o que está acontecendo dentro de um fluxo de trabalho, habilite o rastreamento:
use adk_rust::prelude::*;
use adk_rust::runner::{Runner, RunnerConfig};
use adk_rust::futures::StreamExt;
use std::sync::Arc;
// Crie o pipeline como antes...
// Use Runner em vez de Launcher para controle detalhado
let session_service = Arc::new(InMemorySessionService::new());
let runner = Runner::new(RunnerConfig {
app_name: "workflow_trace".to_string(),
agent: Arc::new(pipeline),
session_service: session_service.clone(),
artifact_service: None,
memory_service: None,
run_config: None,
})?;
let session = session_service.create(CreateRequest {
app_name: "workflow_trace".to_string(),
user_id: "user".to_string(),
session_id: None,
state: Default::default(),
}).await?;
let mut stream = runner.run(
"user".to_string(),
session.id().to_string(),
Content::new("user").with_text("Analyze Rust"),
).await?;
// Processe cada evento para ver a execução do fluxo de trabalho
while let Some(event) = stream.next().await {
let event = event?;
// Mostra qual agente está respondendo
println!("📍 Agent: {}", event.author);
// Mostra o conteúdo da resposta
if let Some(content) = event.content() {
for part in &content.parts {
if let Part::Text { text } = part {
println!(" {}", text);
}
}
}
println!();
}
Referência da API
SequentialAgent
SequentialAgent::new("name", vec![agent1, agent2, agent3])
.with_description("Optional description")
.before_callback(callback) // Chamado antes da execução
.after_callback(callback) // Chamado após a execução
ParallelAgent
ParallelAgent::new("name", vec![agent1, agent2, agent3])
.with_description("Optional description")
.before_callback(callback)
.after_callback(callback)
LoopAgent
LoopAgent::new("name", vec![agent1, agent2])
.with_max_iterations(5) // Limite de segurança (recomendado)
.with_description("Optional description")
.before_callback(callback)
.after_callback(callback)
ConditionalAgent
ConditionalAgent::new("name", |ctx| condition_fn, if_agent)
.with_else(else_agent) // Ramificação 'else' opcional
.with_description("Optional description")
ExitLoopTool
// Adicione a um agente para permitir que ele saia de um LoopAgent
.tool(Arc::new(ExitLoopTool::new()))
Anterior: LlmAgent | Próximo: Sistemas Multiagentes →