Agents and tools
Four workflow patterns later, time to talk about agents on their own terms. An agent isn't a workflow pattern — it's an architectural stance: give Claude a goal and a set of tools, then get out of the way and let the model decide what to do. The quality of an agent is almost entirely determined by the tools you give it, which is why this lesson is not about "how to build an agent" but rather "how to design tools that an agent can combine creatively."
The agent premise
Workflows require you to know the exact steps for a task in advance. Agents assume you don't. You can't enumerate every programming task a developer might ask Claude Code to do. You can't list every possible report a data analyst might ask for. The agent approach is to provide general-purpose tools and trust Claude to combine them based on whatever request walks in the door.
This is more flexible than a workflow — you build the agent once and it handles a long tail of cases you never planned for. It's also less predictable: the trade-offs we'll catalogue in the last lesson of this module are why agents aren't always the right choice. But when they are the right choice, the quality of your tool design is the whole game.
A small worked example
Recall the reminder project from Module 6. The three tools you built:
get_current_datetime— returns the current date and time.add_duration_to_datetime— adds an interval to a datetime.set_reminder— records a reminder at a specific time.
Individually, these are trivially simple. Each function is a few lines of Python. But look at what Claude can do by combining them:
- "What's the time?" → call
get_current_datetime. One tool. - "What day of the week is it in 11 days?" → call
get_current_datetime, thenadd_duration_to_datetimewith 11 days. Two tools, chained. - "Set a reminder for my gym appointment next Wednesday." → call
get_current_datetime, compute days until next Wednesday, calladd_duration_to_datetime, callset_reminder. All three tools. - "When does my 90-day warranty expire?" → recognise that the agent doesn't know when the warranty started, ask the user for the purchase date, then call
add_duration_to_datetime. The agent can ask follow-up questions when it's missing information.
None of those combinations were hard-coded. You didn't write a check_warranty_expiration tool. Claude figured out that the tools you provided were sufficient and strung them together. That's the agent flexibility you're buying.
The key design principle: abstract tools
The reminder project is a tiny example of a broader principle that Claude Code illustrates perfectly.
Claude Code does not have a refactor_code tool. It doesn't have an install_dependencies tool. It doesn't have a fix_failing_tests tool. What it has is:
bash— run any shell commandread— read any filewrite— create any fileedit— modify filesglob— find files matching a patterngrep— search file contents
These are abstract, general-purpose tools. A refactor is "read the file, edit it, run the tests." Installing dependencies is "run pip install or npm install via bash." Fixing tests is "run the tests with bash, read the failures, edit the code, repeat." Claude Code has never heard of any of those high-level tasks, but it can do all of them by combining the primitives.
This is the design principle in one sentence: give agents combinable tools that compose well rather than tools tailored to anticipated tasks.
Why abstract tools win
A hyper-specialised tool like refactor_code might work great for the exact refactor pattern you built it for. It will fail silently (or loudly) for every variation you didn't anticipate:
- The refactor involves a language you didn't code for.
- The refactor spans multiple files.
- The refactor requires moving things between modules.
- The refactor triggers a cascade of test updates.
- The refactor needs to leave a comment explaining why.
The abstract-tool approach handles all of these. Claude reads the files, decides which edits to make, applies them with edit, runs the tests with bash, reads the failures, iterates. Nothing new has to be built; the combination falls out of the primitives Claude already has.
The corollary: specialised tools are lost effort. Any specialised tool you build is one task the agent handles well and zero tasks you haven't imagined yet. An abstract tool is infinite tasks that compose, most of which you'll never anticipate.
Combinable tools in other domains
The principle generalises:
A social media video agent might have:
bash— run any command (for FFmpeg, ImageMagick, anything)generate_image— create images from promptstext_to_speech— convert text to audiopost_media— upload to social platforms
That's four abstract tools, and they compose into video creation, thumbnail generation, subtitle overlay, audio replacement, trimming, reposting, cross-posting, and any other workflow the user dreams up. None of those workflows is a dedicated tool. All of them emerge from the primitives.
A data analysis agent might have:
sql_query— run any SQL against the warehousepython_repl— run Python code (in a sandbox)fetch_url— grab any URLrender_chart— turn a DataFrame into a plot
That's the entire toolkit you need for "answer any question a business analyst might ask." The specific analyses emerge from Claude combining the tools, not from the tools being pre-built for specific analyses.
A customer support agent might have:
search_knowledge_base— find relevant docslookup_customer— fetch customer recordcreate_ticket— escalate to humanssend_email— respond to the customer
Four tools, one agent, covers everything from "where's my order" to "I need a refund" to "I found a bug in your product" — because the agent decides which combinations fit which situation.
Design questions to ask yourself
When you're designing tools for an agent, three questions pressure-test your design:
- "What do I need if I want Claude to do anything in this domain?" Build those tools first. The general primitives.
- "Which two of these tools compose into a task I haven't thought about yet?" If the answer is "lots of them," your tools are the right level of abstract. If the answer is "hardly any," your tools are too specific.
- "If I remove this tool, what tasks become impossible vs what tasks become less convenient?" Impossible means the tool is foundational. Less convenient means the tool is specialised — consider whether Claude can accomplish the same thing by combining others.
You want a small set of powerful, flexible primitives, not a large bag of specialised shortcuts. This is the same instinct that made the Unix philosophy work: small composable tools, each doing one thing well, combined into pipelines that solve any problem.
The next piece: observation
Abstract tools give agents the capability to act. The next lesson covers the other thing agents need to do reliably, which is observe the results of their actions — environment inspection. Without that, even the best tool set produces an agent that can do things blindly but can't tell whether it succeeded. Both halves are load-bearing.
Key Takeaways
- 1Agents derive their power from combining abstract, general-purpose tools in unexpected ways — the quality of an agent is mostly the quality of its tool design.
- 2Claude Code is the canonical example: its toolkit is bash, read, write, edit, glob, grep — no hyper-specialised tools like refactor_code or install_dependencies.
- 3The design principle is combinable tools that compose well, not tools tailored to anticipated tasks — the latter handles the tasks you imagined and none of the ones you did not.
- 4A small set of abstract primitives covers a much larger task space than a big bag of specialised shortcuts, because the agent composes the primitives into combinations you never explicitly built for.
- 5The Unix philosophy applies directly: small composable tools, each doing one thing well, combined by the agent into solutions to novel problems.
- 6When designing a tool set, ask what general primitives cover your whole domain, check which two-tool combinations solve tasks you did not anticipate, and drop tools whose removal would only be inconvenient rather than blocking.