Getting Started with Procedures

Learn the basics and write your first agentic workflow.

Core Concepts

Procedures

A procedure is a reusable unit of agentic work defined in YAML. It has:

  • Parameters: Typed inputs validated before execution
  • Outputs: Typed results validated after execution
  • Agents: LLM workers that execute tasks
  • Workflow: Lua orchestration code
  • State: Mutable storage for tracking progress

Agents

Agents are configured LLM instances. Each agent has:

  • A system prompt (can use templates)
  • Available tools (including other procedures)
  • Response filtering and retry configuration

When you define an agent named worker, you get a Lua primitive Worker.turn() that executes one agentic turn.

Workflows

The workflow contains Lua code that orchestrates execution: calling agent turns, managing state, controlling flow, invoking other procedures, and returning results.

Your First Procedure

Step 1: Simple Research Task

Let's create a procedure that researches a topic:

name: simple_researcher version: 1.0.0 description: Research a topic and provide summary params: topic: type: string required: true outputs: summary: type: string required: true agents: researcher: system_prompt: | Research the topic: {params.topic} Use the search tool to find information. When done, call the done tool. tools: - search - done workflow: | -- Loop until done repeat Researcher.turn() until Tool.called("done") or Iterations.exceeded(10) return {summary = State.get("summary") or "No summary available"}

Step 2: Add State Tracking

Track progress across turns:

workflow: | -- Initialize state State.set("count", 0) -- Process each item for i, item in ipairs(params.items) do Worker.turn({inject = "Process: " .. item}) State.increment("count") end return {processed_count = State.get("count")}

Step 3: Add Stages

Use stages for status tracking:

stages: - loading - analyzing - reporting workflow: | Stage.set("loading") load_data() Stage.set("analyzing") repeat Worker.turn() until Tool.called("done") Stage.set("reporting") generate_report()

Working with Parameters

params: topic: type: string required: true description: "The research topic" depth: type: string enum: [shallow, deep] default: shallow max_results: type: number default: 10 include_sources: type: boolean default: true

Supported types: string, number, boolean, array, object

Error Handling

Use Lua's pcall for error handling:

workflow: | local ok, result = pcall(function() Worker.turn() return State.get("result") end) if not ok then Log.error("Operation failed: " .. tostring(result)) return {success = false, error = result} end return {success = true, result = result}

Invoking Other Procedures

Synchronous

workflow: | -- Call and wait for result local research = Procedure.run("researcher", { topic = params.topic }) Log.info("Research complete: " .. research.summary)

Asynchronous

workflow: | -- Spawn in background local handle = Procedure.spawn("researcher", { topic = params.topic }) -- Do other work do_something_else() -- Wait for result local research = Procedure.wait(handle)

Parallel Execution

workflow: | -- Spawn multiple procedures local handles = {} for _, topic in ipairs(params.topics) do local handle = Procedure.spawn("researcher", {topic = topic}) table.insert(handles, handle) end -- Wait for all Procedure.wait_all(handles) -- Collect results local results = {} for _, handle in ipairs(handles) do table.insert(results, Procedure.result(handle)) end

Complete Example

name: data_processor version: 1.0.0 params: items: type: array required: true outputs: processed: type: number required: true stages: - processing - complete agents: processor: system_prompt: | Process items one at a time. Progress: {state.processed}/{state.total} tools: - process_item - done workflow: | Stage.set("processing") -- Initialize State.set("processed", 0) State.set("total", #params.items) -- Process each item for i, item in ipairs(params.items) do Processor.turn({inject = "Process: " .. item}) State.increment("processed") end Stage.set("complete") return {processed = State.get("processed")}