teachyou.ai academy
← All posts
AI AgentsBootcamp

30 Days to a Production AI Agent: The Hermes Agent Curriculum Breakdown

Pramod Dutta · Jul 3, 2026 · 15 min read

Most people who say they "know how to build AI agents" have built one thing: a chatbot with a system prompt and a few tools bolted on with LangChain. It works in a demo. It falls over the moment a tool call fails, the context window fills up, or a user asks something the happy path didn't anticipate. The gap between "agent demo" and "agent in production" is not a gap in prompting skill — it's a gap in engineering discipline: state management, planning under uncertainty, memory design, and the unglamorous evaluation and guardrail work that nobody puts in a tweet.

That gap is exactly what a 30 day AI agent bootcamp needs to close, and it's why we built 30 Days of Hermes Agent the way we did: four weeks, each ending in a checkpoint you can't fake your way through, building toward one artifact — a production agent you deploy and demo publicly on day 30. This isn't a tutorial series where you copy-paste a notebook. It's a curriculum where each day's code either survives into the next week or gets refactored because you've outgrown it, exactly like real agent systems evolve. Below is the actual breakdown: what you build each week, why the sequencing is deliberate, and what "done" looks like at every checkpoint.

Week 1: The Loop — building the walking skeleton

Everything in agent engineering sits on top of one primitive: a loop that reads state, decides an action, executes it, and updates state. Skip understanding this loop deeply and every abstraction you bolt on later — planners, memory, guardrails — becomes cargo cult code you can't debug. So Week 1 is deliberately slow and low-level. No frameworks. You write the loop yourself in raw Python against the model API, because the fastest way to understand what LangChain or an agent framework is hiding from you is to build the thing it's hiding first.

Days 1-3 are the bare agent loop: a while loop that calls the model, inspects the response, and either terminates or continues. At this stage there are no tools yet — the point is to internalize that an "agent" is fundamentally just a controlled loop around a stateless model call, and that everything interesting happens in how you manage what goes into the next call. You implement termination conditions (the model signals it's done, a max-iteration cap trips, an error boundary fires) because an infinite loop against a paid API is a very fast way to learn why cost caps exist — which becomes directly relevant in Week 4.

def run_agent(user_input: str, max_steps: int = 10) -> str:
    messages = [{"role": "user", "content": user_input}]
    for step in range(max_steps):
        response = call_model(messages)
        if response.stop_reason == "end_turn":
            return response.text
        messages.append({"role": "assistant", "content": response.text})
        messages.append({"role": "user", "content": "continue"})
    raise AgentTimeoutError(f"No termination after {max_steps} steps")

Days 4-5 introduce tools and function calling — the mechanism by which the model requests real-world side effects instead of just generating text. This is where most tutorials wave their hands, so we don't. You implement the full round-trip: the model emits a structured tool call, your code validates the arguments against a schema, executes the tool, and serializes the result back into the conversation in a format the model can reason about on the next turn. The subtlety students consistently underestimate is error handling on the tool side — what happens when the tool call has malformed arguments, when the tool itself throws, when the tool returns something too large to fit back into context. You build all three failure paths explicitly rather than assuming the "happy path" tool call is the only path.

Day 6 is structured outputs and state. Free-text agent responses are fine for a demo and unusable for a system. You move the agent's internal state — what it has learned, what it still needs to do, what its confidence is — into a strictly validated schema (Pydantic or equivalent) instead of hoping the model's prose is parseable. This is the day the agent stops being "a chat with extra steps" and starts being a state machine with an LLM as the transition function.

Day 7 checkpoint: the walking skeleton. In agile engineering, a walking skeleton is the thinnest possible slice of a system that exercises every architectural layer end to end — not a feature, a proof that the wiring works. For an agent, that means: can it receive a task, call a real tool, handle that tool failing, update its state correctly, and terminate cleanly — all without a single hardcoded shortcut? The checkpoint doesn't test whether the agent is smart. It tests whether the scaffolding is honest. A shocking number of "agent projects" never pass this bar; they work only because the tool never fails in the demo. Here's the kind of test that validates it:

def test_walking_skeleton_survives_tool_failure():
    agent = HermesAgent(tools=[flaky_search_tool])
    flaky_search_tool.set_failure_mode(fail_on_call=1)

    result = agent.run("Find the current status of order #4471")

    assert result.terminated_cleanly
    assert result.state.tool_error_count == 1
    assert result.state.recovered_from_error is True
    assert "order #4471" in result.final_answer
    assert result.step_count <= agent.max_steps

If that test doesn't pass, nothing you build in weeks 2 through 4 will be trustworthy — you'd just be adding intelligence on top of a broken foundation.

Week 2: Reasoning & Planning — from reflexes to strategy

A Week 1 agent is reactive: see input, call tool, respond. That's enough for single-step tasks but collapses on anything requiring multiple dependent actions — book a flight, then check the hotel's cancellation policy, then only confirm if both are compatible with the budget. Week 2 is about giving the agent an internal architecture for multi-step reasoning instead of hoping a bigger prompt makes it "think harder."

Days 8-10 cover the planner/executor/critic pattern, which separates concerns that a single monolithic prompt conflates. The planner decomposes a goal into an ordered (or partially ordered) set of subtasks. The executor is the Week 1 loop, now scoped to executing one subtask at a time against the tools available. The critic is a separate model call — sometimes even a separate, smaller model — whose only job is to look at the executor's output and judge whether the subtask was actually satisfied, not just whether the executor claimed it was. Separating these roles matters because a single agent evaluating its own single-pass answer has no adversarial pressure on itself; it's grading its own homework with the same blind spots it had while writing it. A dedicated critic step, even a crude one, catches a surprising fraction of failures where the executor technically "did something" but not the right thing.

class Plan(BaseModel):
    goal: str
    steps: list[PlanStep]
    current_step_index: int = 0

class PlanStep(BaseModel):
    description: str
    status: Literal["pending", "in_progress", "done", "failed"]
    result: str | None = None

Days 11-13 are the heart of the week: reflection and self-correction. This is the concept most people conflate with "just re-prompting," and the distinction matters enormously in practice. Re-prompting is asking the model to try again with the same information — it's a coin flip whether the second attempt is any better, because nothing changed except the random seed. Reflection is structurally different: it requires the agent to generate an explicit critique of its own prior output against stated criteria, and then use that critique — not the original prompt — as new input to the next attempt. The critique has to name what specifically failed, or it doesn't count as reflection.

Concretely, a re-prompt loop looks like "that wasn't right, try again." A reflection loop looks like this:

def reflect_and_retry(task: str, attempt: str, criteria: list[str]) -> str:
    critique = call_model(
        f"Task: {task}\nAttempt: {attempt}\n"
        f"Criteria: {criteria}\n"
        "List every criterion this attempt fails and why, specifically."
    )
    if critique.all_criteria_met:
        return attempt
    revised = call_model(
        f"Task: {task}\nPrevious attempt: {attempt}\n"
        f"Specific failures: {critique.failures}\n"
        "Produce a new attempt that fixes each named failure."
    )
    return revised

The reason this outperforms naive retries is information: the model is no longer guessing what was wrong, it's working from a diagnosis. You'll also implement a hard cap on reflection cycles here, because self-correction without a bound is just a slower infinite loop — another guardrail concept that pays off in Week 4.

Day 14 checkpoint: multi-step tasks. The bar is a task that cannot be solved in one tool call and has at least one point where a naive agent would plausibly go wrong — a step whose output invalidates an earlier assumption. You evaluate not just whether the agent reaches the right final answer, but whether the plan adapted when a step's result contradicted the plan's premise. That adaptability is the entire point of Week 2; an agent that can't revise its plan mid-execution isn't planning, it's just running a script with extra tokens.

Week 3: Memory & Context — the discipline of forgetting on purpose

By Week 3 your agent can act and can reason across steps within a single task. What it still can't do is persist anything useful across tasks, or manage its own context as that context grows past what's actually useful. This week is arguably where the biggest gap exists between hobbyist agents and production ones, because it's the least visually impressive work and the most consequential for reliability at scale.

Days 15-17 establish the split between short-term and long-term memory. Short-term memory is the working context of the current task — the conversation so far, the plan, intermediate tool results — and it's inherently transient and bounded by the context window. Long-term memory is durable knowledge that should survive across sessions: user preferences, facts learned from past interactions, corrections the user has made before. Conflating these two is one of the most common agent bugs: either everything gets crammed into the prompt every turn (expensive, and it degrades reasoning quality once the context is bloated with irrelevant history), or nothing persists and the agent re-learns the same user preference every session. You implement a retrieval layer — typically a vector store plus a simpler structured key-value store for facts that don't benefit from semantic search — and, critically, a policy for what gets written to long-term memory at all. Not every exchange deserves to be remembered forever.

Days 18-20 are context engineering at scale, and this is the section that reframes how students think about context windows entirely. The instinct when you hit a context problem is "use a model with a bigger window." That's treating the symptom. A bigger window doesn't fix the actual failure mode, which is that relevance density drops as context grows, and models measurably lose precision on needle-in-haystack style retrieval as the haystack grows even when the needle is technically "in context." A 200K token window stuffed with 190K tokens of marginally-relevant tool history doesn't help the model — it dilutes the signal the model needs at the exact moment it needs to make a decision. Context engineering is the practice of actively curating what's in the window on every single turn: summarizing or evicting stale tool outputs, ranking memory retrievals by actual relevance to the current step rather than dumping the top-k blindly, and structuring the prompt so the highest-priority information is positioned where models attend to it most reliably. This is why context engineering matters more than raw window size: window size is a budget, context engineering is how you spend it, and a bigger budget spent badly still produces a worse agent than a smaller budget spent well.

def build_context(task: str, memory: MemoryStore, history: list[Message], budget_tokens: int) -> str:
    retrieved = memory.search(query=task, top_k=8)
    retrieved = rerank_by_relevance(retrieved, task)[:3]

    recent_history = summarize_if_needed(history, keep_last_n=5, budget_tokens=budget_tokens // 2)

    return assemble_prompt(
        priority_facts=retrieved,
        recent_turns=recent_history,
        current_task=task,
    )

Day 21 checkpoint: the remembering agent. This validates something specific: that the agent recalls a fact from a session that has fully ended — not from the current conversation buffer, from persisted long-term storage — and, just as importantly, that it doesn't drown a fresh task in irrelevant memory it happens to have. A good test session gives the agent a preference on day one, starts a brand-new session (empty short-term context) on day two, and checks that the preference surfaces only when it's actually relevant to the new task, not injected wholesale into every response.

Week 4: Production — evals, guardrails, and shipping the thing

Weeks 1 through 3 build an agent that works when you're watching it. Week 4 builds the parts that make it trustworthy when you're not — which is the actual definition of "production," and the part almost every AI agent bootcamp skips or treats as an afterthought slide at the end. We treat it as a third of the curriculum on purpose.

Days 22-24 cover evals and LLM-as-a-judge. Once your agent has enough moving parts — planner, critic, memory, tools — manual spot-checking stops being a viable way to know if a change made things better or worse. You build an eval harness: a fixed set of test tasks with known-good criteria, run automatically against every change to the agent's prompts or logic. For tasks with a verifiable answer (did the tool call use the right parameters, is the output valid JSON matching the schema), you use deterministic checks — they're cheap and unambiguous, always prefer them when available. For tasks where correctness is more about quality than a single right answer (is this summary faithful to the source, is this response appropriately cautious), you use a separate model call as a judge, scoring the output against explicit rubric criteria rather than a vague "is this good" prompt. The discipline here is treating the judge model itself as a component that needs validation — you sanity-check its scores against a small set of human-labeled examples before you trust it to gate anything.

Days 25-27 are guardrails, cost, and latency — the days that separate an agent you'd demo from one you'd bill customers against. Guardrails aren't a vague safety concept, they're specific, testable code paths:

  • Cost caps: a hard ceiling on tokens or dollars spent per task, enforced in code, not requested politely in the system prompt. When the loop from Week 1 doesn't terminate cleanly, this is what actually stops the bleeding.
  • Rate limits: bounding how many tool calls, model calls, or retries an agent can make per user per time window, so a single runaway task or a hostile user can't monopolize capacity or run up an unbounded bill.
  • Output validation: every tool call's arguments and every final response gets validated against a schema before it's allowed to execute or ship — the same discipline from Day 6, now enforced as a non-negotiable gate rather than a best-effort parse.
  • Latency budgets: a per-step and per-task timeout, because a "correct" answer that arrives after the user has given up is a production failure indistinguishable from a wrong one.
class AgentGuardrails:
    def __init__(self, max_cost_usd: float, max_calls_per_minute: int, step_timeout_s: int):
        self.max_cost_usd = max_cost_usd
        self.rate_limiter = RateLimiter(max_calls_per_minute)
        self.step_timeout_s = step_timeout_s
        self.spent_usd = 0.0

    def check_before_call(self, estimated_cost: float):
        if self.spent_usd + estimated_cost > self.max_cost_usd:
            raise CostCapExceeded(self.spent_usd, self.max_cost_usd)
        self.rate_limiter.acquire()

    def record_spend(self, actual_cost: float):
        self.spent_usd += actual_cost

Notice the guardrail check happens before the call, not after — you cap cost prospectively based on an estimate, then reconcile with the actual spend. Checking only after the fact means you've already paid for the call that broke the budget.

Days 28-29 are deployment to cloud: packaging the agent as a service, wiring up logging and tracing so a failure in production is debuggable instead of a mystery, and setting up the monitoring that tells you when your Day 22 evals would have caught something your guardrails didn't. This is also where you confront the operational reality that an agent is a long-running, stateful, occasionally-failing distributed system, not a script — deployment choices around timeouts, retries, and observability aren't optional polish, they're the difference between an incident you catch in a dashboard and one you catch from a user complaint.

Day 30: the public capstone demo. You deploy your Hermes Agent build, live, and demonstrate it against tasks it hasn't seen — with its guardrails, evals, and memory all engaged simultaneously, not each showcased in isolation. This is intentionally the least forgiving checkpoint in the entire curriculum: there's no "checkpoint test suite" to satisfy, there's a working system in front of an audience. That's the actual bar for production AI engineering, and it's the bar this bootcamp is built to get you over.

Why the sequencing is the whole point

None of these four weeks is optional or reorderable, and that's a deliberate curriculum decision, not padding. You can't engineer good guardrails (Week 4) without understanding the loop's failure modes (Week 1). You can't do meaningful context engineering (Week 3) without a planner that generates the multi-step traces that make context management a real problem instead of a toy one (Week 2). Every checkpoint is a gate, not a milestone you can skip past with a green checkmark you didn't earn — the Day 7 walking skeleton test, the Day 14 plan-adaptation test, the Day 21 cross-session memory test, and the Day 30 live demo all have to genuinely pass, because each week's code becomes next week's foundation.

If you've been building agent demos that work until the first tool call fails, or you've read every framework's documentation but never built the loop underneath it yourself, this curriculum is designed to close exactly that gap in 30 days of deliberate, checkpointed practice. Come build the walking skeleton, the planner, the remembering agent, and the guarded production system — one week at a time, with Pramod Dutta and Ira Menon — in 30 Days of Hermes Agent.

30 Days to a Production AI Agent: The Hermes Agent Curriculum Breakdown · TeachYou Academy