← Back to Agentic Workflow Guide

Chapter 7 Putting It All Together

From blank canvas to running workflow

You now have all the pieces: agents, topology, control flow, information flow, and validation. This final chapter walks through an end-to-end example that combines everything — and covers the output and aggregation patterns that tie the workflow together.

7.1 — End-to-End: CTF Binary Challenge Workflow

Let’s build a complete workflow for a CTF (Capture The Flag) binary exploitation challenge. This is a real-world use case from the iCTF competition, and it touches every concept from Chapters 1–6.

The Task

Given a compiled binary, the system should: analyze its protections, identify vulnerabilities, develop an exploit, extract the flag, and submit it. If the first exploit attempt fails, it should iterate.

Step 1: Choose the Topology

Applying the decision guide from Chapter 3:

  1. Can one agent handle it? No — too many distinct phases (recon, exploit, submit).
  2. Sequential phases? Yes — recon before exploit, exploit before submit. → Pipeline.
  3. Trial-and-error? Yes — exploit may need multiple attempts. → Add cycles.

Result: Pipeline with a cyclic exploit loop.

Step 2: Design the Agents

Applying the principles from Chapter 2:

AgentModelToolsMax IterationsRole
Recon Agent GPT-4o checksec, list_functions, check_strings 10 Analyze binary protections and structure
Exploit Agent GPT-4o decompile, run_payload, read_memory 30 Develop and test exploit payloads
Evaluator GPT-4o-mini None 0 Check if exploit succeeded (Router)
Submit Agent GPT-4o-mini submit_flag 3 Extract and submit the flag

Step 3: Draw the Graph

CTF Binary Challenge workflow — Entry → Recon → Exploit → Evaluator (Router), with retry loop to Exploit or success to Submit
Edges: Entry → Recon: Direct edge Recon → Exploit: Direct edge Exploit → Evaluator: Direct edge Evaluator → Exploit: Conditional edge ("retry") Evaluator → Submit: Conditional edge ("success")

Step 4: Define the State

# Shared state schema: { "count": "int", "messages": "Annotated[List[BaseMessage], lambda x, y: x + y]", "binary_path": "str", "protections": "str", # Recon output "vulnerability": "str", # Identified vuln "exploit_output": "str", # Last exploit attempt result "exploit_attempts": "int", # How many tries "flag": "str" # Extracted flag }

Step 5: Write the Prompts

# Recon Agent — System Prompt: "You are a binary analysis specialist. Analyze the target binary at {binary_path}. Your job is to: 1. Check security protections (ASLR, NX, canaries, PIE) 2. List all functions and identify entry points 3. Look for suspicious strings (format strings, shell commands) Report your findings in a structured format." # Exploit Agent — System Prompt: "You are an exploit developer. Based on the reconnaissance: Protections: {protections} Vulnerability: {vulnerability} Develop a working exploit. You have tools to decompile functions, run test payloads, and read memory. If a previous attempt failed: {exploit_output} Adjust your approach based on what went wrong." # Evaluator (Router) — System Prompt: "You are evaluating whether an exploit attempt succeeded. Look at the exploit output: {exploit_output} If the output contains a flag pattern (FLAG{...}), route to Submit. If the output shows a crash, segfault, or error, route to Exploit for another attempt." # Submit Agent — System Prompt: "Extract the flag from: {exploit_output} Submit it using the submit_flag tool."

7.2 — Output Patterns

How does a workflow produce its final result? There are three common patterns:

Pattern 1: Last Node Output

The simplest pattern. The final node in the pipeline produces the workflow’s output. Whatever it writes to shared state is the result.

# The last node's output is the workflow result: def submit_agent(global_state): # ... submit flag ... return Command( update={ "flag": "FLAG{s3cur1ty_ftw}", # ← This is the final output "messages": [response] }, goto="END" )

Pattern 2: Aggregation Node

A dedicated final node that reads from multiple fields and synthesizes a combined result. Useful for fan-out/fan-in topologies.

# Aggregation node reads multiple results and combines them: def aggregator(global_state): static_analysis = global_state.get("static_result", "") dynamic_analysis = global_state.get("dynamic_result", "") signature_analysis = global_state.get("signature_result", "") prompt = f"""Combine these three analyses into a unified report: Static: {static_analysis} Dynamic: {dynamic_analysis} Signatures: {signature_analysis}""" # LLM synthesizes a coherent summary response = model.invoke([HumanMessage(content=prompt)]) return Command( update={"final_report": response.content}, goto="END" )

Pattern 3: Structured Final Output

Use Pydantic structured output on the final node to guarantee the result matches a specific schema. This is the most robust approach for downstream consumption.

# Define the final output schema: class WorkflowResult(BaseModel): success: bool flag: Optional[str] attempts: int summary: str recommendations: List[str] # The final node uses structured output: model_with_output = model.with_structured_output(WorkflowResult) result = model_with_output.invoke(messages) # result.success → True # result.flag → "FLAG{s3cur1ty_ftw}" # result.attempts → 3 # result.summary → "Buffer overflow in check_password..."

7.3 — Design Principles Recap

Across all seven chapters, a few principles have appeared repeatedly. Here they are in one place:

1. Start Simple, Add Complexity

Begin with a single agent. If it works, you’re done. If it struggles, identify why (too many tools? too many responsibilities? needs iteration?) and add the minimum structure to solve that problem.

2. One Agent, One Job

Each agent should have a focused role with a clear system prompt and a small set of relevant tools. If an agent’s prompt is longer than a paragraph, consider splitting it.

3. Explicit Over Implicit

Use named state fields over message parsing. Use structured output over free-text parsing. Use template variables over hoping the LLM remembers context.

4. Defense in Depth

Every tool should handle errors. Every loop should have a limit. Every router should validate its decision. Layer your safeguards.

5. Model Selection is an Optimization

Use powerful models (GPT-4o, Claude Sonnet) for complex reasoning. Use cheap models (GPT-4o-mini) for classification, routing, and formatting. Don’t pay for intelligence you don’t need.

7.4 — What’s Next

Chapter Summary

Key Takeaways:
🎉 You’ve completed the Agentic Workflow Guide. You now understand the foundations of multi-agent systems, how to configure agents, connect them into workflows, control execution, pass data between them, handle errors, and produce structured output. Go build something.
← Chapter 6: Validation & Error Handling Back to Guide Overview →