Building Agents with ADK & TypeScript — Part 3: Hands-On: Transform Your Agent

Search for a command to run...

Great Part 3 — the hands-on flow clarifies what was abstract in earlier parts. One thing I've learned building agent systems with Claude plus MCP for clients: treat the tool interface like a product surface, not an implementation detail. Clear tool descriptions and tight input schemas cut hallucinated calls drastically. Curious how ADK handles tool-selection once you add 10-plus tools — does context bloat become an issue, or is there a retrieval layer in the SDK.
Bun, the hyper-fast JavaScript runtime known for its incredible speed and Zig-based architecture, is being completely rewritten in Rust. For a project that has over 22 million monthly downloads and ea

Next.js 16.3 is almost here, and it's packed with a ton of improvements and let's see them in this post 1.Instant Navigation For a while now, there’s been a valid, lingering debateServer Components vs

It’s official. React has merged its long-awaited Rust port of the React Compiler (formerly known as React Forget) into the main repository. What started as an experimental research project by Joseph S

If you follow modern technology trends, you have likely been led to believe that artificial intelligence is entirely built on Python. Every tutorial, machine learning framework documentation, and open

Just when the JavaScript ecosystem was catching its breath after last week’s TanStack Router compromise, the Mini Shai-Hulud supply chain worm has continued its relentless march. Developed by the thre

Where we are: In Part 1 you set up an environment and built your first agent. In Part 2 you learned what each parameter actually means. Now it's time to get your hands dirty — let's take that default agent and transform it into a specialized math tutor.
Here's the generic agent you created in Part 1:
import { LlmAgent } from '@google/adk';
export const rootAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'hello_time_agent',
description: 'A helpful assistant agent.',
instruction: 'You are a helpful assistant.',
});
It works — but it has no personality, no focus, and no real purpose beyond "being helpful." Let's fix that in three steps.
The name field is your agent's internal identity inside ADK. It's used for logging, debugging, and delegation in multi-agent systems. hello_time_agent doesn't tell us much — let's make it descriptive.
export const rootAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'math_tutor_agent', // More specific internal name
description: 'A helpful assistant agent.',
instruction: 'You are a helpful assistant.',
});
Small change, big impact — anyone reading this code (or any other agent in a multi-agent system) now immediately understands what this agent is for.
Remember from Part 2: description is not for this agent — it's for other agents that need to decide whether to delegate tasks here. A vague description like "A helpful assistant agent." gives other agents nothing useful to work with.
export const rootAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'math_tutor_agent',
description: 'Helps students learn algebra by guiding them through problem-solving steps.',
instruction: 'You are a helpful assistant.',
});
What changed: Other agents can now recognize that this agent specifically handles algebra tutoring — not general questions, not customer support, not data analysis. Specificity is the key.
Now for the most impactful change. The instruction is what this agent reads to know how to behave. Replace the generic placeholder with something meaningful
export const rootAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'math_tutor_agent',
description: 'Helps students learn algebra by guiding them through problem-solving steps.',
instruction: 'You are a patient math tutor. Help students with algebra problems.',
});
This single sentence instruction does three things:
Defines the role → math tutor
Sets the personality → patient
Specifies the task → help with algebra problems
Note: This simple instruction is perfect for learning the basics. In a future part of this series, you'll learn how to write production-ready instructions with detailed personas, boundaries, and few-shot examples.
Here's your final agent.ts — clean, focused, and ready to test:
import { LlmAgent } from '@google/adk';
export const rootAgent = new LlmAgent({
model: 'gemini-2.5-flash',
name: 'math_tutor_agent',
description: 'Helps students learn algebra by guiding them through problem-solving steps.',
instruction: 'You are a patient math tutor. Help students with algebra problems.',
});
Save this to your agent.ts file in the adk-agent directory from Part 1.
This simple agent now demonstrates:
Specific role definition (math tutor)
Clear personality (patient)
Defined task scope (algebra problems)
From your adk-agent directory, run:
npx adk web
You could see an output like this
Compare responses side by side:
| Generic Agent (Part 1) | Math Tutor (Part 3) | |
|---|---|---|
| Instruction | "You are a helpful assistant." |
"You are a patient math tutor. Help students with algebra problems." |
| Response style | Generic answers | Focused on teaching |
| Tone | Neutral | Patient and supportive |
| Domain | Anything | Algebra only |
Even this minimal change makes a significant difference in how the agent behaves. This is the power of a well-crafted instruction.
Required parameters:
Only model and name are technically required
But instruction is critical for useful, focused behavior
description is important the moment you start building multi-agent systems
Best practices:
Write simple, focused instructions that define the role and task
Be specific — "helps students learn algebra" beats "is helpful"
Include a personality trait to guide the tone
Use npx adk web to quickly test how instruction changes affect behavior
You've now built, understood, and customized your first ADK agent in TypeScript. The next big step is giving your agent real capabilities — Tools.
In Part 4, we'll explore how to write custom FunctionTool implementations, wire them into your agent, and understand how the model decides when and how to call them.