### Overview and What You Will Learn * What an AI agent is in plain language and how it differs from a regular chatbot * Why agents can do things that normal LLMs cannot * The three core things every agent needs to work * Where agents are used in real ops and AIOps pipelines * The mental model that makes everything else in this module click ### Why This Matters Every serious AIOps tool you will encounter is built on agents. Alert triage systems, automated RCA pipelines, self-healing infrastructure, runbook executors — all of them are agents under the hood. Understanding what an agent actually is, not just the buzzword version, means you can build them, debug them, and know when to use them versus when a simpler approach is better. ### What an AI Agent Is A regular LLM is like a very smart person locked in a room with no phone, no internet, and no tools. You pass a note under the door asking a question. They write an answer based on everything they memorised before being locked in, and pass it back. They cannot look anything up. They cannot take any action. They can only reason about what they already know. An AI agent is that same person but now with tools they can use — a phone to call APIs, a computer to run code, a database they can query, a messaging app to send alerts. They can now do things, not just think about things. More precisely, an AI agent is a system where an LLM is connected to tools and given a loop: observe the situation, decide what to do, use a tool to do it, observe the result, decide what to do next, and keep going until the task is done. The key difference from a regular LLM call: Regular LLM: Input → [LLM thinks] → Output Done. One shot. AI Agent: Input → [LLM thinks] → Tool call → Result → [LLM thinks again] → Tool call → Result → [LLM thinks again] → Final answer Done. Many steps, adaptive. ### Why Agents Can Do Things LLMs Cannot A plain LLM has two fundamental limitations. First, its knowledge is frozen at training time — it does not know what is happening on your servers right now. Second, it cannot take actions — it cannot restart a service, query Prometheus, or post to Slack. Agents solve both problems. By connecting the LLM to tools, you give it access to real-time data and the ability to take real actions. The LLM provides the reasoning. The tools provide the capability. Together they produce something genuinely useful. ### The Three Things Every Agent Needs Every AI agent, no matter how simple or complex, needs exactly three things: **1. A brain — the LLM** This is the reasoning engine. It reads the situation, decides what to do, interprets results, and generates the final answer. In practice this is Claude, GPT-4, Llama, or any capable model. **2. Tools — the capabilities** These are functions the LLM can call. A tool can be anything: a function that reads a file, an API call to Prometheus, a database query, a subprocess command, a Slack webhook. Tools are what let the agent interact with the real world. **3. A loop — the control flow** This is the code that runs the agent. It sends the LLM's output to the right tool, feeds the result back to the LLM, and keeps going until the task is done. Without the loop, you just have an LLM that knows about tools but cannot use them. ### Where Agents Appear in Ops Work Agents are not just an AI research concept. They are the architecture behind most serious ops automation: * **Alert triage** — an agent reads incoming alerts, queries monitoring APIs, classifies severity, and routes to the right team * **Incident investigation** — an agent checks CPU, memory, logs, and recent deployments to diagnose what is wrong * **Runbook execution** — an agent reads a runbook and executes steps one by one, checking results before proceeding * **Self-healing pipelines** — an agent detects an anomaly, identifies the cause, and triggers a remediation action automatically * **RCA reports** — an agent collects evidence from multiple systems and synthesises a human-readable root cause analysis Every one of these is a loop: observe, think, act, observe again. > 📌 **Remember:** An agent is not magic. It is an LLM plus tools plus a loop. Once you see it that way, building one stops being intimidating and starts being straightforward engineering. ---
### Overview and What You Will Learn * How an agent perceives its environment * The reasoning loop — observe, decide, act, observe again * What agent memory is and why it matters * How agents plan multi-step tasks * Why agents fail and what causes loops and wrong answers ### Why This Matters Understanding how an agent thinks is what separates someone who can copy-paste an agent framework from someone who can build one from scratch, debug it when it fails, and tune it when it produces wrong answers. The reasoning loop is simple once you see it — and once you see it, every agent framework you encounter becomes transparent. ### The Core Reasoning Loop Every agent runs the same fundamental loop regardless of how complex the framework looks on the surface. Step 1: OBSERVE The agent receives the current situation — the user's request, any tool results from previous steps, conversation history. Step 2: THINK The LLM reasons about what it knows and what it needs. "I need to check CPU usage. I have a check_cpu tool available." Step 3: ACT The agent calls a tool with specific arguments. check_cpu(server="prod-api-01") Step 4: OBSERVE RESULT The tool returns data. The agent reads it. "CPU is at 94%. That is very high." Step 5: THINK AGAIN The LLM updates its understanding and decides next step. "High CPU. Need to check memory and recent logs too." Step 6: ACT AGAIN Calls another tool. Loop continues. Step N: CONCLUDE The LLM has enough information to answer. Produces final output. Loop ends. This loop is what makes agents powerful. Unlike a single LLM call that must answer in one shot, an agent can gather information progressively, adapt based on what it finds, and build toward an answer step by step. ### Agent Memory An agent's memory is simply the accumulated context — everything it has seen so far in the current session. Every tool result gets added to the context. Every reasoning step gets added. The LLM always has access to the full history of the current task when deciding what to do next. ```python ## Agent memory is just a list of messages memory = [ {"role": "user", "content": "Payment service is slow. Investigate."}, {"role": "assistant", "content": "I'll check CPU usage first."}, {"role": "tool", "content": "CPU on prod-pay-01: 94%"}, {"role": "assistant", "content": "High CPU. Checking memory next."}, {"role": "tool", "content": "Memory on prod-pay-01: 67% - normal"}, {"role": "assistant", "content": "CPU high, memory normal. Checking logs."}, ## ... and so on ] ``` This is why context window size matters for agents. A complex investigation with many tool calls accumulates a lot of context. If the context fills up, the agent loses access to earlier observations. ### How Agents Plan For simple tasks, agents do not need explicit planning — they figure out the next step after each observation. For complex tasks, agents can plan upfront: break the goal into subtasks, sequence them logically, then execute. ``` Goal: "Investigate why checkout is failing for 30% of users" Implicit plan the agent forms: 1. Check error rate on checkout service 2. Check recent error logs for checkout 3. Check database connection count 4. Check if any recent deployments happened 5. Synthesise findings into a diagnosis ``` The agent does not write this plan explicitly — the LLM infers it from the goal and starts executing. But you can prompt the agent to make its plan explicit, which produces more structured and predictable behaviour. ### Why Agents Fail Understanding failure modes is as important as understanding how agents work. **Infinite loops** — the agent keeps calling tools without converging on an answer. Usually caused by unclear termination conditions or tools returning unhelpful results that the agent tries to resolve with more tool calls. **Wrong tool selection** — the agent calls the wrong tool because the tool descriptions are ambiguous. Precise tool definitions prevent this. **Hallucinated tool arguments** — the agent passes invalid arguments to a tool because the parameter schema is not strict enough. Strong type constraints prevent this. **Context overflow** — after many tool calls, the context gets too long and the LLM starts ignoring earlier observations. Summarising intermediate results helps. **Confident wrong answers** — the agent reaches a conclusion too early without enough evidence. Prompting it to gather all relevant data before concluding helps. > 🔴 **Common Mistake:** Building an agent with no maximum iteration limit. If anything goes wrong in the loop, the agent runs forever burning API tokens. Always set a maximum number of steps and exit cleanly if it is exceeded. ---
### Overview and What You Will Learn * What tool calling is and how it works mechanically * How an LLM produces a structured tool call instead of plain text * What a tool definition looks like — name, description, parameters * The execution boundary — why the LLM proposes but your code executes * How tool results get fed back into the agent's context ### Why This Matters Tool calling is the most important technical concept in this module. It is the mechanism that connects the LLM's reasoning to real-world action. Every agent framework — LangChain, LlamaIndex, AutoGen, CrewAI — is built on top of tool calling. Understanding it at the raw level means you can debug any framework and build without one when you need to. ### What Tool Calling Is Tool calling is the ability of an LLM to output a structured JSON object describing a function call instead of plain text. When the model decides it needs to use a tool, it stops generating prose and outputs something like: ```json { "tool": "check_cpu", "arguments": { "server": "prod-api-01", "time_window_minutes": 15 } } ``` Your application code receives this, validates it, executes the actual function, and returns the result to the model as new context. The model does not execute anything. This is the most important thing to understand. The LLM is a reasoning engine that proposes actions. Your code is the execution engine that carries them out. This boundary is what makes agents safe and controllable. LLM (reasoning layer): "I need CPU data. I'll call check_cpu with server=prod-api-01" → outputs structured JSON tool call Your code (execution layer): Receives JSON → validates arguments → calls the actual function → gets result → returns it to LLM LLM (reasoning continues): "CPU is 94%. Now I'll check the logs." → outputs next tool call ### Writing Tool Definitions A tool definition tells the LLM what a tool does, when to use it, and what arguments it takes. The quality of your tool definitions directly determines how accurately the agent uses them. A tool definition has three parts: ```python tools = [ { "name": "check_cpu", "description": """Get CPU usage percentage for a server over a time window. Use this when investigating performance issues, high latency, or service slowdowns. Do NOT use this for memory issues or disk problems — use check_memory or check_disk instead.""", "input_schema": { "type": "object", "properties": { "server": { "type": "string", "description": "Server hostname, e.g. prod-api-01" }, "time_window_minutes": { "type": "integer", "description": "How many minutes of history to check. Default 15.", "default": 15 } }, "required": ["server"] } } ] ``` **The description is the most important part.** It tells the LLM when to use this tool and — crucially — when NOT to use it. Overlapping tools without clear boundaries cause the model to pick the wrong one. Good description: `"Search for current or time-sensitive information. Use for events after your training cutoff. Do NOT use for questions answerable from your training data."` Bad description: `"Search the web"` ### The Execution Boundary in Practice Here is a complete minimal example of tool calling with the Claude API: ```python import anthropic import json client = anthropic.Anthropic() ## Step 1 - Define your tools tools = [ { "name": "check_cpu", "description": "Get current CPU usage for a server. Returns percentage 0-100.", "input_schema": { "type": "object", "properties": { "server": {"type": "string", "description": "Server hostname"} }, "required": ["server"] } } ] ## Step 2 - Define your actual tool functions (the execution layer) def check_cpu(server: str) -> dict: ## In a real system this would call Prometheus or your monitoring API ## For now we simulate it mock_data = { "prod-api-01": 94.2, "prod-db-01": 43.1, "prod-cache-01": 12.7 } cpu = mock_data.get(server, 50.0) return {"server": server, "cpu_usage": cpu, "status": "critical" if cpu > 85 else "normal"} ## Step 3 - Send initial message to Claude with tools available response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=[ {"role": "user", "content": "Check CPU on prod-api-01 and tell me if it needs attention."} ] ) ## Step 4 - Check if Claude wants to call a tool if response.stop_reason == "tool_use": tool_use_block = next(b for b in response.content if b.type == "tool_use") tool_name = tool_use_block.name tool_args = tool_use_block.input print(f"Claude wants to call: {tool_name}({tool_args})") ## Step 5 - Execute the tool (your code, not Claude) if tool_name == "check_cpu": result = check_cpu(**tool_args) ## Step 6 - Send the result back to Claude final_response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=[ {"role": "user", "content": "Check CPU on prod-api-01 and tell me if it needs attention."}, {"role": "assistant", "content": response.content}, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool_use_block.id, "content": json.dumps(result) } ] } ] ) print(final_response.content[0].text) ``` This is the complete raw tool calling loop. Every agent framework wraps exactly this pattern. > 💡 **Tip:** The `stop_reason == "tool_use"` check is how you know Claude wants to call a tool instead of giving a final answer. Always check this before assuming the response is complete. ---
### Overview and What You Will Learn * What ReAct is and the problem it solves * The Think-Act-Observe loop in detail * How to implement ReAct in plain Python * Why making the agent's reasoning explicit improves results * When to use ReAct and when a simpler approach is enough ### Why This Matters ReAct (Reasoning and Acting) is the most widely used agent pattern in production systems. It is the foundation of LangChain agents, AutoGen, and most serious agent frameworks. Understanding it means you understand what every agent framework is doing internally — which means you can debug them, tune them, and build without them when you want full control. ### What ReAct Is ReAct is a prompting pattern that asks the agent to alternate between reasoning steps and action steps explicitly. Instead of jumping straight to a tool call, the agent first writes out its reasoning — what it knows, what it needs, why it is calling this particular tool. Then it acts. Then it observes the result and reasons again. ``` Thought: The user says payment service is slow. I should check CPU first since high CPU is the most common cause of latency. Action: check_cpu(server="prod-pay-01") Observation: CPU is 94.2% - critically high Thought: CPU is very high. This explains the slowness. Memory is normal so it is not a memory leak. I should check what is consuming the CPU by looking at recent logs. Action: get_recent_logs(server="prod-pay-01", lines=20) Observation: Log shows "processing order batch: 50000 items" repeated every 2 seconds Thought: A batch job is running continuously processing 50k items every 2 seconds. This is likely a misconfigured batch processor consuming all CPU. I have enough information to give a diagnosis. Action: FINISH ``` The explicit reasoning (Thought) before each action produces dramatically better results on complex problems because: * The model commits to a reasoning path before acting * Each thought builds on the previous observations * The model is less likely to call irrelevant tools * The reasoning chain is visible for debugging ### Implementing ReAct in Plain Python ```python import anthropic import json client = anthropic.Anthropic() ## Tool definitions TOOLS = [ { "name": "check_cpu", "description": "Get CPU usage for a server. Returns percentage.", "input_schema": { "type": "object", "properties": { "server": {"type": "string"} }, "required": ["server"] } }, { "name": "check_memory", "description": "Get memory usage for a server. Returns percentage used.", "input_schema": { "type": "object", "properties": { "server": {"type": "string"} }, "required": ["server"] } }, { "name": "get_recent_logs", "description": "Get recent log lines from a server. Use to find errors and warnings.", "input_schema": { "type": "object", "properties": { "server": {"type": "string"}, "lines": {"type": "integer", "default": 10} }, "required": ["server"] } } ] ## Tool implementations (execution layer) def check_cpu(server: str) -> dict: data = {"prod-pay-01": 94.2, "prod-db-01": 43.1} return {"server": server, "cpu_percent": data.get(server, 45.0)} def check_memory(server: str) -> dict: data = {"prod-pay-01": 68.4, "prod-db-01": 71.2} return {"server": server, "memory_percent": data.get(server, 60.0)} def get_recent_logs(server: str, lines: int = 10) -> dict: logs = { "prod-pay-01": [ "2026-06-09 14:23:01 INFO processing order batch: 50000 items", "2026-06-09 14:23:03 INFO processing order batch: 50000 items", "2026-06-09 14:23:05 ERROR connection pool: 498/500 connections used", "2026-06-09 14:23:07 INFO processing order batch: 50000 items", ] } return {"server": server, "logs": logs.get(server, ["No logs available"])} def execute_tool(name: str, args: dict) -> str: if name == "check_cpu": return json.dumps(check_cpu(**args)) if name == "check_memory": return json.dumps(check_memory(**args)) if name == "get_recent_logs": return json.dumps(get_recent_logs(**args)) return json.dumps({"error": f"Unknown tool: {name}"}) ## ReAct Agent loop def run_react_agent(problem: str, max_steps: int = 10) -> str: print(f"\nProblem: {problem}") print("=" * 60) messages = [{"role": "user", "content": problem}] system = """You are an ops investigation agent. When given a problem: 1. Think through what you know and what you need to find out 2. Call tools to gather information 3. After each observation, think about what it tells you 4. When you have enough information, provide a clear diagnosis Be systematic. Check the most likely causes first.""" for step in range(max_steps): response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, system=system, tools=TOOLS, messages=messages ) ## Add assistant response to memory messages.append({"role": "assistant", "content": response.content}) ## Check if we are done if response.stop_reason == "end_turn": final_text = next( (b.text for b in response.content if hasattr(b, "text")), "" ) print(f"\nFINAL DIAGNOSIS:\n{final_text}") return final_text ## Process tool calls if response.stop_reason == "tool_use": tool_results = [] for block in response.content: if block.type == "tool_use": print(f"\nStep {step + 1}: Calling {block.name}({block.input})") result = execute_tool(block.name, block.input) print(f"Result: {result}") tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": result }) messages.append({"role": "user", "content": tool_results}) return "Maximum steps reached without conclusion." ## Run it run_react_agent("Payment service on prod-pay-01 is slow. Users reporting timeouts. Investigate.") ``` ### When to Use ReAct vs Simpler Approaches Use ReAct when: * The task requires multiple investigation steps * You do not know in advance which tools will be needed * The next action depends on the result of the previous one * You need the reasoning to be visible for debugging Use a simpler direct approach when: * The task is a single tool call * You always know exactly which tools to call * Speed matters more than adaptability ---
### Overview and What You Will Learn * The five types of agents from simplest to most capable * What each type can and cannot do * Which types appear most in ops and AIOps work * How to choose the right agent type for a given task ### Why This Matters Not every problem needs a full ReAct agent with chain-of-thought reasoning and multiple tool calls. Using an over-engineered agent for a simple task wastes tokens, adds latency, and introduces failure modes you did not need. Knowing the spectrum helps you choose the right tool for the job. ### The Five Agent Types **Type 1 — Simple Reflex Agent** Acts on fixed rules. No memory. No planning. Given condition X, always do Y. ```python ## Simple reflex agent for alert routing def route_alert(alert): if alert["cpu"] > 90: return "page-oncall" elif alert["cpu"] > 75: return "slack-warning" else: return "log-only" ``` Use when: The decision is fully determined by the current input and a fixed ruleset. Fast, predictable, zero AI required. **Type 2 — Model-Based Reflex Agent** Has memory of past state. Can make decisions based on history, not just current input. ```python ## Tracks state across multiple alerts class AlertTracker: def __init__(self): self.alert_history = [] def process(self, alert): self.alert_history.append(alert) ## Detect if same server has alerted 3 times in last 10 minutes recent = [a for a in self.alert_history[-20:] if a["server"] == alert["server"]] if len(recent) >= 3: return "escalate-incident" return "standard-alert" ``` Use when: The right action depends on history, not just the current moment. **Type 3 — Goal-Based Agent** Plans a sequence of actions to reach a defined goal. Searches for paths to the objective. Use when: You have a clear end state and need to figure out the sequence of steps to reach it. Runbook execution is a good example — the goal is a healthy service, and the agent plans which steps to execute. **Type 4 — Utility-Based Agent** Chooses actions that maximise a utility score. Can balance multiple competing objectives. Use when: Multiple approaches could achieve the goal and you need to pick the best one — for example, choosing between restarting a service (fast but disruptive) versus scaling up (slower but safer). **Type 5 — Learning Agent** Improves its behaviour over time based on feedback. Updates its knowledge from experience. Use when: You have enough historical data and feedback to train improvement. Most production AIOps systems eventually incorporate this — models that learn which alerts are real incidents vs noise. ### Which Types Appear in AIOps Most AIOps systems use a combination: | Task | Agent Type | | :--- | :--- | | Alert severity classification | Simple Reflex | | Anomaly detection with rolling baseline | Model-Based | | Incident investigation | Goal-Based + ReAct | | RCA with evidence synthesis | Goal-Based + ReAct | | Automated remediation | Utility-Based | | Alert noise reduction over time | Learning | The hands-on project in this module is a Goal-Based ReAct agent — the most practical and buildable type for ops work. ---
### Overview and What You Will Learn * Why tool definitions are the biggest lever on agent accuracy * The three parts of a strong tool definition * How to write descriptions that prevent wrong tool selection * Parameter design — types, constraints, and defaults * Handling overlapping tools — making boundaries explicit ### Why This Matters You can have a perfect agent loop, perfect prompting, and the best available model — and still get bad results if your tool definitions are vague. Tool definitions are how the LLM knows what tools exist, what they do, and when to use them. Every wrong tool call, every bad argument, every unnecessary API hit traces back to a weak tool definition. ### The Three Parts of a Strong Definition **Part 1 — Precise purpose with scope and boundaries** Vague: `"Search for information"` Strong: ``` "Search the web for current or time-sensitive information. Use for: news, recent events, live prices, anything after your training cutoff. Do NOT use for: general concepts, historical facts, or anything answerable from training data. Do NOT use for: internal system data — use get_metrics or get_logs for that." ``` The negative guidance is as important as the positive. Telling the model when NOT to use a tool prevents unnecessary calls that burn tokens and add latency. **Part 2 — Typed and constrained parameters** ```python ## Weak parameter definition "parameters": { "server": {"type": "string"}, "time": {"type": "string"} ## too vague — what format? what range? } ## Strong parameter definition "parameters": { "server": { "type": "string", "description": "Server hostname exactly as it appears in monitoring. Example: prod-api-01", "pattern": "^[a-z]+-[a-z]+-[0-9]+$" }, "time_window_minutes": { "type": "integer", "description": "How many minutes of history to retrieve. Use 15 for recent issues, 60 for trends.", "minimum": 1, "maximum": 1440, "default": 15 } } ``` Prefer enums over open strings when the options are known. Use natural identifiers the model can infer from context. Add format examples where there is any ambiguity. **Part 3 — Clear output contract** The description should mention what the tool returns so the model knows how to interpret the result: ``` "Returns: JSON object with fields: - cpu_percent (float): current CPU usage 0-100 - status (string): 'normal' if < 75%, 'warning' if 75-90%, 'critical' if > 90% - trend (string): 'stable', 'increasing', or 'decreasing' over the time window Returns empty object {} if server not found." ``` ### Handling Overlapping Tools When two tools do similar things, the model needs an explicit decision boundary. Bad — model cannot decide: ``` check_server_health: "Check if a server is healthy" get_server_status: "Get the current status of a server" ``` Good — boundary is explicit: ``` check_server_health: "Run a health check ping. Use to confirm if a server is reachable. Returns only UP or DOWN. Use get_server_metrics for detailed data." get_server_metrics: "Get detailed CPU, memory, and disk metrics. Use when investigating performance problems. Requires server to be reachable — run check_server_health first if unsure." ``` > 📌 **Remember:** If you cannot explain in one sentence why an agent would pick tool A over tool B, the boundary is not clear enough. Rewrite the descriptions until you can. ---
Overview and What You Will Learn What an AI agent is in plain language and how it differs from a regular chatbot Why age...
Overview and What You Will Learn How an agent perceives its environment The reasoning loop — observe, decide, act, obser...
Overview and What You Will Learn What tool calling is and how it works mechanically How an LLM produces a structured too...
Overview and What You Will Learn What ReAct is and the problem it solves The Think-Act-Observe loop in detail How to imp...
Overview and What You Will Learn The five types of agents from simplest to most capable What each type can and cannot do...
Overview and What You Will Learn Why tool definitions are the biggest lever on agent accuracy The three parts of a stron...
Overview and What You Will Learn When to run tool calls in parallel vs sequentially The dependency rule for parallelizat...
Overview and What You Will Learn What blast radius means for agents and why it matters The principle of least privilege ...
Overview and What You Will Learn How tool calling works across different LLM providers The universal pattern that works ...
Overview and What You Will Learn The Planner-Executor pattern — separating planning from action Supervisor agents — coor...
Overview and What You Will Learn Complete agent building cheat sheet The most common agent bugs and how to fix them Inte...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.