Lesson 2: Module 02: Your First Reasoning Step
Build a machine that uses an LLM to analyze text and return structured data.
Learning Objectives
- Write an
askstep with model, prompt, and structured output - Choose the right model tier for a task
- Use
returnsto get predictable, typed results
Complexity Ladder: Level 1 (Linear), single-step reasoning with no branching or looping.
The Concept: Structured AI Reasoning
If you have used ChatGPT or Claude, you already understand the core idea behind an ask step. When you type a message in ChatGPT, you are sending a prompt to an LLM (large language model, an AI like Claude or GPT) and getting text back. An ask step does the same thing, but with structure and guardrails.
Think of it like ordering at a restaurant. In a normal ChatGPT conversation, you ask for “something good” and the chef improvises. With an ask step, you hand the chef a specific order form: “I want a sentiment field that must be one of these four values, a category from this list, and a confidence score between 0 and 1.” The chef (the LLM) still uses creativity and intelligence, but the shape of what comes back is guaranteed.
The ask step is mashin’s primitive for LLM inference (sending a prompt to an AI model and getting a response back). Every agent and AI workflow you build starts here.
Here is how an ask step maps to concepts you may already know:
ChatGPT / Claude Mashin ask Step┌──────────────────┐ ┌──────────────────────────────────┐│ System prompt │ ──────────► │ with role "..." ││ User message │ ──────────► │ with task "..." ││ JSON mode │ ──────────► │ returns ││ Model selection │ ──────────► │ ask name, using: "model" │└──────────────────┘ └──────────────────────────────────┘If you have used ChatGPT’s JSON mode or function calling, returns is the same idea: you tell the AI the exact shape of the response you want, and the runtime enforces it.
An ask step has three key parts:
using:which LLM to use (e.g.,"anthropic:claude-haiku-4")with rolethe system prompt (instructions that set the LLM’s role and personality, like “You are a customer feedback analyst”)with taskthe user prompt (the specific task or question for this call)returnsthe shape of the response (types and constraints), also called structured output (a response forced into a specific format rather than free-form text)
The returns block is what makes mashin ask steps reliable. Instead of hoping the LLM returns JSON (a text format for structured data, like {"key": "value"}) in the right format, you declare the schema and the runtime enforces it.
Start With Koda
Koda requires a free account. Sign in or create an account to use Koda exercises throughout this course. If you’re not signed in yet, read on; the exercises will be here when you’re ready.
Before writing code by hand, try building this with Koda (Mashin’s intelligent development environment):
Ask Koda:
“Build a machine that classifies customer feedback into categories (product, service, pricing, shipping) with sentiment (positive, negative, neutral) and a confidence score. Use a fast model.”
Examine the machine Koda generates. Check that it uses claude-haiku-4 (fast tier), has a low temperature (how creative vs consistent the AI is; low means consistent, high means creative), and uses returns with typed fields. If Koda uses Sonnet, ask: “Can we use Haiku since this is a classification task?”
This gives you a working starting point. The rest of this module explains every part of what Koda generated, so you understand it fully.
Building It: Sentiment Analyzer
Let’s build a machine that classifies customer feedback into categories with a confidence score.
machine sentiment_analyzer "Sentiment Analyzer"
// What the machine receives when invokedaccepts feedback as string, is required
// What the machine returns when it finishesresponds with sentiment as string category as string confidence as decimal summary as string
implements // An ask step sends a prompt to an LLM and gets structured output back ask classify, using: "anthropic:claude-haiku-4" // with role = the system prompt (sets the LLM's role, stays the same across calls) with role "You are a customer feedback analyst. Classify feedback accurately and concisely. Base your confidence score on how clear the sentiment is." // with task = the user prompt (the specific task, changes with each input) with task "Classify this customer feedback:\n\n${input.feedback}" // returns = the exact shape of the response the LLM must return returns sentiment as string, is required category as string, is required confidence as decimal, is required summary as string, is requiredBreaking It Down
Model selection: We use anthropic:claude-haiku-4 because classification is a fast-tier task. It doesn’t need the reasoning power of Sonnet or Opus. Haiku is cheaper and faster.
Temperature: 0.1 keeps output consistent. For classification, you want the same input to produce the same output. Higher temperatures add randomness, useful for creative tasks but harmful for analysis.
with role vs with task: The with role block sets the LLM’s role (system prompt). The with task block provides the specific task (user prompt). Keep with role stable across calls; vary with task with each input.
returns: Each field has a name, type, and optional constraints. The is required modifier ensures the field is always present.
Variable Access
Mashin provides helper functions to access data from inputs and previous steps:
| Expression | Purpose | Example |
|---|---|---|
input.<field> | Read machine input | input.feedback |
steps.<name>.<field> | Read a step’s output field | steps.classify.sentiment |
steps.<name> | Read a step’s entire output | steps.classify |
state.<field> | Read machine state | state.counter |
These expressions work in with task strings via ${expr} interpolation and in compute expressions.
Multi-Step Reasoning
One powerful pattern: chain multiple ask steps together, each focused on a single task.
implements // Step 1: Extract key entities using a fast (cheap) model ask extract, using: "anthropic:claude-haiku-4" with task "Extract named entities from: ${input.text}" returns entities as list
// Step 2: Analyze relationships using a balanced (smarter) model ask analyze, using: "anthropic:claude-sonnet-4" with task "Given these entities: ${steps.extract.entities}\nFrom this text: ${input.text}\n\nDescribe the relationships between them." returns relationships as list summary as stringEach step does one thing well. The fast model handles extraction; the balanced model handles analysis. This is cheaper and more reliable than asking one model to do everything at once.
Key Syntax
ask name, using: "provider:model-name" with role "system prompt" // Optional: LLM role/personality with task "user prompt with ${input.field} interpolation" // Required: the task returns // Optional but recommended: enforces response shape field_name as type // string, number, decimal, boolean, map, listModel Tiers
| Tier | Models | Use For |
|---|---|---|
| Fast | anthropic:claude-haiku-4, openai:gpt-4o-mini | Classification, extraction, validation |
| Balanced | anthropic:claude-sonnet-4, openai:gpt-4o | Summarization, generation, reasoning |
| Powerful | anthropic:claude-opus-4, openai:o1 | Complex research, multi-document analysis |
Temperature Guide
| Task | Temperature | Why |
|---|---|---|
| Classification | 0.0-0.2 | Consistency matters |
| Extraction | 0.1-0.3 | Accuracy over creativity |
| Content writing | 0.5-0.8 | Creative variety |
| Brainstorming | 0.8-1.0 | Maximum exploration |
Common Mistakes
-
Using one model for everything. Match the model to the task. A 10-step machine using Sonnet everywhere costs 30x more than using Haiku where appropriate.
-
Putting too much in one
askstep. “Analyze, extract entities, classify sentiment, summarize, and rate importance”: split this into 5 focused steps. Each step should do one thing. -
Skipping
returns. Without it, you get unstructured text. With it, you get typed, validated data you can pass to the next step reliably.
What’s Next
In Module 03, you’ll learn the key insight that turns an ask step into an agent: giving it tools.