<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Tarun Singh - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Tarun Singh - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:23:53 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/tarunsinghofficial/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Get Type Safety Without Code Generation Using tRPC and Hono ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever updated your backend API property name but neglected to also update the frontend? I'm sure you have. When this occurs, it leads to production crashes and unhappy customers, plus you've wasted your entire week fixing the problem. To reso... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/type-safety-without-code-generation-using-trpc-and-hono/</link>
                <guid isPermaLink="false">696535106ca3442fa7874d4b</guid>
                
                    <category>
                        <![CDATA[ trpc ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hono ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tarun Singh ]]>
                </dc:creator>
                <pubDate>Mon, 12 Jan 2026 17:53:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768240380001/79d5aa1f-438e-4a1e-a072-3166b9a36333.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever updated your backend API property name but neglected to also update the frontend? I'm sure you have. When this occurs, it leads to production crashes and unhappy customers, plus you've wasted your entire week fixing the problem.</p>
<p>To resolve this issue in the past, you typically had to create a multitude of TypeScript interfaces by hand, using GraphQL Code Generator to generate the interface files, or hope that it all worked out. Well, there’s a better way to accomplish this now, without the need for code generation.</p>
<p><a target="_blank" href="https://trpc.io/">tRPC</a> and <a target="_blank" href="https://hono.dev/">Hono</a> are two applications that are changing how we develop TypeScript-based applications throughout the entirety of the full-stack.</p>
<p>By the end of this tutorial, you’ll understand:</p>
<ul>
<li><p>Why traditional REST APIs fail at type safety</p>
</li>
<li><p>How tRPC provides full end-to-end type inference between backend and frontend</p>
</li>
<li><p>How Hono delivers type-safe APIs while staying REST-friendly</p>
</li>
<li><p>When to choose tRPC vs Hono for your projects</p>
</li>
<li><p>How these tools improve developer experience, team velocity, and reliability</p>
</li>
</ul>
<p>If you’re building full-stack TypeScript applications and want fewer runtime bugs and faster iteration, this guide is for you.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-problem-with-traditional-apis">The Problem with Traditional APIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-makes-trpc-different">What Makes tRPC Different?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hono-the-lightweight-challenger">Hono: The Lightweight Challenger</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-this-matters-these-days">Why This Matters These Days</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-future-is-type-safe">The Future is Type-Safe</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along comfortably, you should have:</p>
<ul>
<li><p>Basic knowledge of TypeScript</p>
</li>
<li><p>Familiarity with REST APIs and how frontend-backend communication works</p>
</li>
<li><p>Some experience with Node.js and modern JavaScript frameworks</p>
</li>
<li><p>A general understanding of frontend frameworks like React or Next.js (helpful, but not required)</p>
</li>
</ul>
<p>You don’t need prior experience with tRPC, Hono, or GraphQL.</p>
<h2 id="heading-the-problem-with-traditional-apis">The Problem with Traditional APIs</h2>
<p>You’ve probably written something like this a hundred times:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Backend (Express)</span>
app.post(<span class="hljs-string">'/api/users'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { name, email } = req.body;
  <span class="hljs-comment">// Do stuff with user</span>
  res.json({ id: <span class="hljs-number">1</span>, name, email });
});

<span class="hljs-comment">// Frontend</span>
<span class="hljs-keyword">const</span> createUser = <span class="hljs-keyword">async</span> (name: <span class="hljs-built_in">string</span>, email: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/users'</span>, {
    method: <span class="hljs-string">'POST'</span>,
    headers: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
    body: <span class="hljs-built_in">JSON</span>.stringify({ name, email })
  });
  <span class="hljs-keyword">return</span> response.json(); 
};
</code></pre>
<p>The backend knows the shape of the data. But the frontend...it hopes it gets it right. You end up writing interfaces manually, like:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>If you change the backend tomorrow to return <code>userId</code> instead of <code>id</code>, TypeScript won't catch it. Your types and reality have diverged, and you won't know until runtime.</p>
<p><a target="_blank" href="https://graphql.org/">GraphQL</a> tried to solve this with schemas and codegen, but honestly? Setting up GraphQL feels like assembling IKEA furniture without instructions. You need a schema, resolvers, code generation tools, and suddenly your "simple" API has a 30-minute setup process.</p>
<h2 id="heading-what-makes-trpc-different">What Makes tRPC Different?</h2>
<p>tRPC flips the script entirely. Instead of defining your API in a separate schema language, your TypeScript code is the schema. Here's the same API in tRPC:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Backend (tRPC router)</span>
<span class="hljs-keyword">import</span> { initTRPC } <span class="hljs-keyword">from</span> <span class="hljs-string">'@trpc/server'</span>;
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>;

<span class="hljs-keyword">const</span> t = initTRPC.create();

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> appRouter = t.router({
  createUser: t.procedure
    .input(z.object({
      name: z.string(),
      email: z.string().email(),
    }))
    .mutation(<span class="hljs-function">(<span class="hljs-params">{ input }</span>) =&gt;</span> {
      <span class="hljs-comment">// Do stuff with user</span>
      <span class="hljs-keyword">return</span> { id: <span class="hljs-number">1</span>, name: input.name, email: input.email };
    }),
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AppRouter = <span class="hljs-keyword">typeof</span> appRouter;
</code></pre>
<p>This is where it gets cool. On your frontend:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Frontend - fully type-safe!</span>
<span class="hljs-keyword">import</span> { createTRPCClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@trpc/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AppRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'./server'</span>;

<span class="hljs-keyword">const</span> client = createTRPCClient&lt;AppRouter&gt;({
  url: <span class="hljs-string">'http://localhost:3000/trpc'</span>,
});

<span class="hljs-comment">// TypeScript knows EVERYTHING about this call</span>
<span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> client.createUser.mutate({
  name: <span class="hljs-string">'Alice'</span>,
  email: <span class="hljs-string">'alice@example.com'</span>
});

<span class="hljs-comment">// user is automatically typed as { id: number; name: string; email: string; }</span>
</code></pre>
<p>No code generation or build step, or GraphQL schema. Just pure TypeScript inference doing its thing. If you rename <code>id</code> to <code>userId</code> in your backend, your frontend will immediately show a TypeScript error. You'll catch it before you even save the file.</p>
<p>This is what we call end-to-end type safety, and it's honestly a great transition.</p>
<h2 id="heading-hono-the-lightweight-challenger">Hono: The Lightweight Challenger</h2>
<p>While tRPC is amazing for full-stack TypeScript apps where you control both ends, <a target="_blank" href="https://hono.dev/">Hono</a> takes a slightly different approach. It's a lightweight web framework that gives you type safety while still being a traditional HTTP framework.</p>
<p>Here's the same example in Hono:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>;
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>;
<span class="hljs-keyword">import</span> { zValidator } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/zod-validator'</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono();

<span class="hljs-keyword">const</span> userSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

app.post(<span class="hljs-string">'/api/users'</span>, zValidator(<span class="hljs-string">'json'</span>, userSchema), <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { name, email } = c.req.valid(<span class="hljs-string">'json'</span>);
  <span class="hljs-keyword">return</span> c.json({ id: <span class="hljs-number">1</span>, name, email });
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AppType = <span class="hljs-keyword">typeof</span> app;
</code></pre>
<p>On the frontend, you can use Hono’s RPC client:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { hc } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AppType } <span class="hljs-keyword">from</span> <span class="hljs-string">'./server'</span>;

<span class="hljs-keyword">const</span> client = hc&lt;AppType&gt;(<span class="hljs-string">'http://localhost:3000'</span>);

<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> client.api.users.$post({
  json: { name: <span class="hljs-string">'Bob'</span>, email: <span class="hljs-string">'bob@example.com'</span> }
});

<span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> response.json();
<span class="hljs-comment">// user is fully typed!</span>
</code></pre>
<p>Hono is incredibly fast (it runs on Cloudflare Workers, Deno, Bun, and Node.js), and it gives you that sweet type safety while still being a "regular" HTTP framework. You get RESTful routes, middleware, and all the familiar patterns – just with TypeScript powers.</p>
<h2 id="heading-why-this-matters-these-days">Why This Matters These Days</h2>
<p>You might think to yourself, “Okay, I know what you mean, but why should I care about it?” There’s a reason why these tools are being utilized more now than ever before.</p>
<h3 id="heading-developer-experience-is-essential">Developer experience is essential</h3>
<p>In 2026 and beyond, we’ll no longer accept long feedback loops. The ability to modify your backend code and see what might break on your frontend application without having to run the application will be fantastic for productivity. We’ll spend less time fixing bugs and more time creating new functionalities.</p>
<h3 id="heading-smaller-teams-better-apps">Smaller teams, better apps</h3>
<p>With tRPC or Hono, one developer can create an entire full-stack application with type safety at a very fast pace because they don’t have to switch back and forth between REST documentation and TypeScript interfaces – all the data is flowing to and from their backend code directly to their frontend.</p>
<h3 id="heading-the-end-of-works-on-my-machine">The end of “Works on my machine“</h3>
<p>With type safety, errors are caught at compile time instead of at the time your end-user clicks on a button. This is especially impactful when working in larger teams, when the backend developers and front-end developers may not be in constant communication with one another.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Want to try this out? Here's the fastest way:</p>
<p>For tRPC:</p>
<pre><code class="lang-bash">npm create @trpc/next-app@latest
</code></pre>
<p>This scaffolds a Next.js app with tRPC already configured. Check out the <a target="_blank" href="https://trpc.io/docs/client/nextjs">official tRPC docs</a> for more.</p>
<p>For Hono:</p>
<pre><code class="lang-bash">npm create hono@latest
</code></pre>
<p>Pick your runtime (Node.js, Cloudflare Workers, etc.), and you're off to the races. The <a target="_blank" href="https://hono.dev/">Hono documentation</a> is excellent and super approachable.</p>
<h2 id="heading-the-future-is-type-safe">The Future is Type-Safe</h2>
<p>Look, REST isn't going anywhere, and GraphQL has its place. But for full-stack TypeScript developers, tRPC and Hono represent something special: type safety without the ceremony. No code generation or no schema duplication, just TypeScript doing what it does best.</p>
<p>In the future, when you start a new project, give one of these a shot. Your future self – the one who's refactoring code at 2 AM – will thank you.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Nano Banana for Image Generation - Explained with Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ AI is changing the image generation and editing process into a smooth workflow. Now, with just a single prompt, you can tell your computer to generate or edit an existing image. Google just launched its new model for image generation or editing, "Nan... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/nano-banana-for-image-generation/</link>
                <guid isPermaLink="false">68cd5897ffbf18457f7bb85a</guid>
                
                    <category>
                        <![CDATA[ image processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tarun Singh ]]>
                </dc:creator>
                <pubDate>Fri, 19 Sep 2025 13:20:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758287738949/b33b68f4-0e84-46df-a85f-9ff6aacfd72c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>AI is changing the image generation and editing process into a smooth workflow. Now, with just a single prompt, you can tell your computer to generate or edit an existing image. Google just launched its new model for image generation or editing, <a target="_blank" href="https://gemini.google/overview/image-generation/">"Nano Banana" – Gemini 2.5 Flash</a>. It's a powerful, nimble tool that's changing how we think about image generation and manipulation, and it's something you'll definitely want in your developer toolkit.</p>
<p>In this article, you will learn how to use “Nano Banana” for Image Generation using Gemini’s 2.5 Flash Image. So, let’s get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-nano-banana">What is "Nano Banana"?</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-why-nano-banana">Why "Nano Banana"?</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-your-project">Setting Up Your Project</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-get-an-api-key-from-google-gemini">Step 1: Get an API key from Google Gemini</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-install-the-sdk-and-other-dependencies">Step 2: Install the SDK and Other Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-set-up-your-environment">Step 3: Set Up Your Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-image-generation-amp-editing">Step 4: Image Generation &amp; Editing</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-beyond-the-basics-what-else-can-you-do">Beyond the Basics: What Else Can You Do?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-what-is-nano-banana">What is "Nano Banana"?</h2>
<p>Nano Banana is the latest image-editing cum generation tool from Google DeepMind. Forget the formal jargon for a second. Imagine you have an incredibly talented, lightning-fast artist at your beck and call. You can describe <em>anything</em> to them – "an astronaut riding a horse on the Moon" – and <em>poof</em>, it appears. Or, you hand them a picture of your dog and say, "Make the dog wear a cap on his head," and they do it instantly, keeping your cat looking like <em>your</em> dog.</p>
<p>That's essentially Nano Banana. It's an advanced AI model from the Gemini family, specifically engineered for rapid, intelligent image generation and nuanced editing. It understands your natural language commands, enabling you to bring complex visual ideas to life or make surgical changes to existing images with surprising ease.</p>
<h3 id="heading-why-nano-banana">Why "Nano Banana"?</h3>
<p>Because it's small (flash!), packed with goodness, and leaves you feeling like you just peeled back a new layer of creative possibility. It's fast, efficient, and incredibly versatile.</p>
<p><strong>The Superpowers You Get:</strong></p>
<ul>
<li><p><strong>Prompt-Perfect Editing:</strong> Want to change a background, alter a pose, or add a specific object? Just ask. Nano Banana understands and executes.</p>
</li>
<li><p><strong>Character Consistency:</strong> This is a big one. If you're creating a story or a series of images, maintaining the look of a specific character or object is crucial. Nano Banana excels at this, ensuring your protagonist looks the same whether they're in a forest or on the moon.</p>
</li>
<li><p><strong>Visual Mashups (Multi-Image Fusion):</strong> Got a few different visual elements you want to combine seamlessly? It can blend them into a cohesive new image.</p>
</li>
</ul>
<p>and much more!</p>
<p>Interested? Let's get our hands dirty. But wait! To use “Nano Banana, “ you have two ways to do this:</p>
<ol>
<li><p><a target="_blank" href="https://aistudio.google.com/prompts/new_chat?model=gemini-2.5-flash-image-preview">Using Google AI Studio</a>: The simplest and easiest way to generate or edit images in Google Studio. This is a web-based tool that gives you direct access to the Gemini models without writing a single line of code. It's the absolute best place to test and start, and is useful for developers and non-developers, also. Also, there's no need to install libraries, manage API keys, or write any code</p>
</li>
<li><p><strong>Building with the Gemini API:</strong> This is beneficial if you want more custom solutions for your application. For any serious application—whether it's a web app, a mobile app, or a backend service—you'll need to integrate directly with the Gemini API. This is where the real power lies, as it allows you to automate tasks and create interactive experiences.</p>
</li>
</ol>
<p>In this tutorial, you will see how we can use this tool in our own applications, using nothing but Python. So, let’s get started.</p>
<h2 id="heading-how-to-set-up-your-project">How to Set Up Your Project</h2>
<h3 id="heading-step-1-get-an-api-key-from-google-gemini">Step 1: Get an API key from Google Gemini</h3>
<p>The very first step for using “Nano Banana” is to get an API key. Head over to <a target="_blank" href="https://aistudio.google.com/apikey">Google AI Studio</a>, click on “Create API key“, and generate a new one by specifying a project from your existing Google Cloud projects.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429573699/1c5d1a52-2e63-476b-a957-604542044fc7.png" alt="API key generated from Google Gemini " class="image--center mx-auto" width="1896" height="903" loading="lazy"></p>
<p>Once you have generated an API key, save it securely somewhere.</p>
<h3 id="heading-step-2-install-the-sdk-and-other-dependencies">Step 2: Install the SDK and Other Dependencies</h3>
<p>Open your terminal and run:</p>
<pre><code class="lang-bash">pip install google-generativeai pillow python-dotenv
</code></pre>
<p>We’ll use <code>Pillow</code> for easy image handling and <code>python-dotenv</code> to safely manage our API key.</p>
<h3 id="heading-step-3-set-up-your-environment">Step 3: Set Up Your Environment</h3>
<p>It’s crucial to keep your API key out of your code for security. For this, we usually use environment variables. So, create a file named <code>.env</code> in your project root and add your API key:</p>
<pre><code class="lang-bash">GEMINI_API_KEY=<span class="hljs-string">"YOUR_API_KEY_HERE"</span>
</code></pre>
<h3 id="heading-step-4-image-generation-amp-editing">Step 4: Image Generation &amp; Editing</h3>
<p><strong>Example 1: Text-to-Image Generation</strong></p>
<p>Text-to-Image is like an artist who can draw anything you describe. In this, you simply write the prompt (a sentence or a description), even a very detailed one, and the AI will generate a unique, high-quality image that matches your description. It’s perfect for bringing your most imaginative ideas to life with just a few words.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">from</span> io <span class="hljs-keyword">import</span> BytesIO
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

<span class="hljs-comment"># Configuration</span>
load_dotenv()
genai.configure(api_key=os.getenv(<span class="hljs-string">"GEMINI_API_KEY"</span>))
model = genai.GenerativeModel(<span class="hljs-string">'gemini-2.5-flash-image-preview'</span>)

<span class="hljs-comment"># Prompt, Image, and Response Setup</span>
prompt = <span class="hljs-string">"A golden retriever puppy sitting in a field of daisies, bright and cheerful"</span>
output_filename = <span class="hljs-string">"text_to_image_result.png"</span>

<span class="hljs-comment"># saving image helper function from text prompt response</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_image_from_response</span>(<span class="hljs-params">response, filename</span>):</span>
    <span class="hljs-string">"""Helper function to save the image from the API response."""</span>
    <span class="hljs-keyword">if</span> response.candidates <span class="hljs-keyword">and</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
        <span class="hljs-keyword">for</span> part <span class="hljs-keyword">in</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
            <span class="hljs-keyword">if</span> part.inline_data:
                image_data = BytesIO(part.inline_data.data)
                img = Image.open(image_data)
                img.save(filename)
                print(<span class="hljs-string">f"Image successfully saved as <span class="hljs-subst">{filename}</span>"</span>)
                <span class="hljs-keyword">return</span> filename
    print(<span class="hljs-string">"No image data found in the response."</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    print(<span class="hljs-string">f"Generating image for prompt: '<span class="hljs-subst">{prompt}</span>'..."</span>)
    response = model.generate_content(prompt)
    save_image_from_response(response, output_filename)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<p><strong>Output:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757485705896/50484418-c53c-4d61-8846-2c8875dc2cbd.png" alt="A golden retriever puppy sitting happily in a sunny meadow filled with white daisies, surrounded by bright green grass and a cheerful, vibrant atmosphere." class="image--center mx-auto" width="1024" height="1024" loading="lazy"></p>
<p>The code used in the example handles everything needed to communicate with the Gemini API and save the image.</p>
<ul>
<li><p>First, we import the required libraries and load the API key from <code>.env</code> using <code>load_dotenv()</code>. This makes the key available so we can connect to Google’s service with <code>genai.configure()</code>.</p>
</li>
<li><p>The model we’re using is <code>gemini-2.5-flash-image-preview</code>, which is designed for fast image generation.</p>
</li>
<li><p>We define a <code>prompt</code> <code>(“A golden retriever puppy...”)</code> and a filename for saving the image.</p>
</li>
<li><p>The helper function <code>save_image_from_response(...)</code> looks at the API’s response, extracts the raw image data, and saves it as a PNG file.</p>
</li>
<li><p>In <code>main()</code>, we call the model with the prompt, then pass the response to the helper function to save the result.</p>
</li>
<li><p>The <code>if __name__ == "__main__":</code> block ensures the script runs only when executed directly, not when imported.</p>
</li>
</ul>
<p><strong>Example 2: Image-to-Image Editing</strong></p>
<p>Image-to-Image is like a photo editor. Instead of starting from scratch, you can upload an existing picture and describe how to change it. For instance, you can request background removal, addition of new objects, or even a complete artistic style change.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">from</span> io <span class="hljs-keyword">import</span> BytesIO
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

<span class="hljs-comment"># Configuration</span>
load_dotenv()
genai.configure(api_key=os.getenv(<span class="hljs-string">"GEMINI_API_KEY"</span>))
model = genai.GenerativeModel(<span class="hljs-string">'gemini-2.5-flash-image-preview'</span>)

<span class="hljs-comment"># Prompt, Image, and Response Setup</span>
input_image_path = <span class="hljs-string">"input_dog.png"</span>
prommpt = <span class="hljs-string">"Make the dog wear a small wizard hat and spectacles."</span>
output_filename = <span class="hljs-string">"edited_image_result.png"</span>

<span class="hljs-comment"># saving image helper function from text prompt response</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_image_from_response</span>(<span class="hljs-params">response, filename</span>):</span>
    <span class="hljs-string">"""Helper function to save the image from the API response."""</span>
    <span class="hljs-keyword">if</span> response.candidates <span class="hljs-keyword">and</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
        <span class="hljs-keyword">for</span> part <span class="hljs-keyword">in</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
            <span class="hljs-keyword">if</span> part.inline_data:
                image_data = BytesIO(part.inline_data.data)
                img = Image.open(image_data)
                img.save(filename)
                print(<span class="hljs-string">f"Image successfully saved as <span class="hljs-subst">{filename}</span>"</span>)
                <span class="hljs-keyword">return</span> filename
    print(<span class="hljs-string">"No image data found in the response."</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    print(<span class="hljs-string">f"Editing image '<span class="hljs-subst">{input_image_path}</span>' with prompt: '<span class="hljs-subst">{prommpt}</span>'..."</span>)
    <span class="hljs-keyword">try</span>:
        img_to_edit = Image.open(input_image_path)
        response = model.generate_content([prommpt, img_to_edit])
        save_image_from_response(response, output_filename)
    <span class="hljs-keyword">except</span> FileNotFoundError:
        print(<span class="hljs-string">f"Error: The file '<span class="hljs-subst">{input_image_path}</span>' was not found."</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<p><strong>Output:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757486336530/84cba4bf-91bd-49b7-8fd3-e94b20eabbfb.png" alt="A before and after image of a playful dog wearing a small pointed wizard hat and round spectacles, sitting upright with a charming and magical look, giving a whimsical, storybook-like feel." class="image--center mx-auto" width="1250" height="627" loading="lazy"></p>
<p>This code is very similar to the first example, but the key difference is in the core logic.</p>
<ul>
<li><p><code>input_image_path</code>: This variable now holds the file path to the image you want to edit.</p>
</li>
<li><p><a target="_blank" href="http://Image.open"><code>Image.open</code></a><code>(input_image_path)</code>: This line uses the Pillow library to open your local image file to be used.</p>
</li>
<li><p><code>model.generate_content([prommpt, img_to_edit])</code>: This is the most important part. Unlike before, we now pass a list to the <code>generate_content</code> function that contains both the text prompt and the image object. This tells the API to use the provided image as a starting point for its generation.</p>
</li>
<li><p><code>try...except</code> block: Here, we are handling the errors. It tries to open the image file, and if it fails (because the file isn't there), it will <code>except</code> the <code>FileNotFoundError</code> and print a friendly message to the user instead of crashing.</p>
</li>
</ul>
<p><strong>Example 3: Multi-Image Fusion</strong></p>
<p>Multi-image fusion is like merging two or more images or objects. Upload several images and instruct the AI to blend them into one composite picture seamlessly. This is a tool for creating new scenes, combining people and backgrounds, or creating detailed product mockups.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">from</span> io <span class="hljs-keyword">import</span> BytesIO
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

<span class="hljs-comment"># Configuration</span>
load_dotenv()
genai.configure(api_key=os.getenv(<span class="hljs-string">"GEMINI_API_KEY"</span>))
model = genai.GenerativeModel(<span class="hljs-string">'gemini-2.5-flash-image-preview'</span>)

<span class="hljs-comment"># Prompt, Images, and Response Setup</span>
image1_path = <span class="hljs-string">"dog_image.png"</span>
image2_path = <span class="hljs-string">"cap_image.png"</span>
prompt = <span class="hljs-string">"Make the dog from the first image wear the cap from the second image. The cap should fit realistically on the dog's head."</span>
output_filename = <span class="hljs-string">"dog_with_cap_result.png"</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_image_from_response</span>(<span class="hljs-params">response, filename</span>):</span>
    <span class="hljs-string">"""Helper function to save the image from the API response."""</span>
    <span class="hljs-keyword">if</span> response.candidates <span class="hljs-keyword">and</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
        <span class="hljs-keyword">for</span> part <span class="hljs-keyword">in</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
            <span class="hljs-keyword">if</span> part.inline_data:
                image_data = BytesIO(part.inline_data.data)
                img = Image.open(image_data)
                img.save(filename)
                print(<span class="hljs-string">f"Image successfully saved as <span class="hljs-subst">{filename}</span>"</span>)
                <span class="hljs-keyword">return</span> filename
    print(<span class="hljs-string">"No image data found in the response."</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    print(<span class="hljs-string">f"Fusing images '<span class="hljs-subst">{image1_path}</span>' and '<span class="hljs-subst">{image2_path}</span>'..."</span>)
    <span class="hljs-keyword">try</span>:
        img1 = Image.open(image1_path)
        img2 = Image.open(image2_path)
        response = model.generate_content([prompt, img1, img2])
        save_image_from_response(response, output_filename)
    <span class="hljs-keyword">except</span> FileNotFoundError:
        print(<span class="hljs-string">"Error: One or both image files were not found."</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<p><strong>Output:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757486318798/2fe3ca32-3053-44cc-9350-b1c47abacdd9.png" alt="Three-part image showing a golden retriever puppy in a daisy field, a red baseball cap with the letter “A,” and the final edited version where the puppy is wearing the red cap while sitting happily among the daisies" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<p>The logic of the code above is an extension of the Image-to-Image example.</p>
<ul>
<li><p><code>image1_path</code> and <code>image2_path</code>: These variables hold the paths to the two images you want to fuse or merge.</p>
</li>
<li><p><code>model.generate_content([prompt, img1, img2])</code>: Here, the list passed to the <code>generate_content</code> function contains three items: the text prompt and both image objects. This tells the AI to use the prompt to combine the elements from both images into a single output.</p>
</li>
</ul>
<p><strong>Example 4: Image Restoration</strong></p>
<p>This feature can restore old, faded, or damaged photos. Upload a picture and request Gemini to restore it. This includes sharpening low-quality images, colorizing old black-and-white photos, and enhancing textures, which can make your memories look new again.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">from</span> io <span class="hljs-keyword">import</span> BytesIO
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

<span class="hljs-comment"># Configuration</span>
load_dotenv()
genai.configure(api_key=os.getenv(<span class="hljs-string">"GEMINI_API_KEY"</span>))
model = genai.GenerativeModel(<span class="hljs-string">'gemini-2.5-flash-image-preview'</span>)

<span class="hljs-comment"># Prompt, Image, and Response Setup</span>
input_image_path = <span class="hljs-string">"old_photo.png"</span>
prompt = <span class="hljs-string">"Restore this old, faded photograph. Sharpen the details, remove any scratches or damage, and enhance the colors to make it look like a new, high-quality photo."</span>
output_filename = <span class="hljs-string">"restored_image_result.png"</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_image_from_response</span>(<span class="hljs-params">response, filename</span>):</span>
    <span class="hljs-string">"""Helper function to save the image from the API response."""</span>
    <span class="hljs-keyword">if</span> response.candidates <span class="hljs-keyword">and</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
        <span class="hljs-keyword">for</span> part <span class="hljs-keyword">in</span> response.candidates[<span class="hljs-number">0</span>].content.parts:
            <span class="hljs-keyword">if</span> part.inline_data:
                image_data = BytesIO(part.inline_data.data)
                img = Image.open(image_data)
                img.save(filename)
                print(<span class="hljs-string">f"Image successfully saved as <span class="hljs-subst">{filename}</span>"</span>)
                <span class="hljs-keyword">return</span> filename
    print(<span class="hljs-string">"No image data found in the response."</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    print(<span class="hljs-string">f"Attempting to restore image: '<span class="hljs-subst">{input_image_path}</span>'..."</span>)
    <span class="hljs-keyword">try</span>:
        old_photo = Image.open(input_image_path)
        response = model.generate_content([prompt, old_photo])
        save_image_from_response(response, output_filename)
    <span class="hljs-keyword">except</span> FileNotFoundError:
        print(<span class="hljs-string">f"Error: The file '<span class="hljs-subst">{input_image_path}</span>' was not found."</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<p><strong>Output:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757486506412/201bf046-4c63-46eb-a026-f2a432ca8c3d.png" alt="Side-by-side comparison of an old photograph and a restored version. The left shows a scratched, sepia-toned photo of a vintage car on a rural road, while the right shows the same scene digitally restored in color, with a blue classic car under a bright sky in a golden countryside" class="image--center mx-auto" width="1167" height="572" loading="lazy"></p>
<p>The structure here is identical to the Image-to-Image Editing example because, from a technical perspective, image restoration is a form of image-to-image editing.</p>
<ul>
<li>Now the <code>prompt</code> is where the magic happens. The text prompt explicitly tells the model what to do with the image, outlining the restoration steps like "sharpen the details," "remove scratches," and "enhance the colors." The model's intelligence allows it to understand these abstract instructions and apply them to the visual data to give you a better and a realistic update to your old image.</li>
</ul>
<h2 id="heading-beyond-the-basics-what-else-can-you-do">Beyond the Basics: What Else Can You Do?</h2>
<p>This is just the tip of the iceberg! Nano Banana is incredibly versatile. Here are some ideas for where you can take your projects:</p>
<ul>
<li><p><strong>Batch Processing:</strong> Automate the generation of multiple images from a list of prompts.</p>
</li>
<li><p><strong>Creative Assets:</strong> Design icons, backgrounds, or character sprites for games or apps directly from your Python script.</p>
</li>
<li><p><strong>Data Processing:</strong> Integrate Nano Banana into a data pipeline to programmatically edit or generate images based on data inputs.</p>
</li>
<li><p><strong>AI Art Galleries:</strong> Build a backend service that allows users to submit prompts and receive images.</p>
</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>"Nano Banana" (Gemini 2.5 Flash Image) isn't just a cool tech tool; it's a practical, powerful tool for developers and creatives alike. With just a few lines of code, you can tap into its capabilities and bring your visual ideas to real life. This streamlined approach makes it easy to get started, experiment, and integrate this visual magic into your projects.</p>
<p>If you found this article helpful and want to discuss AI development, LLMs, or software development, feel free to connect with me on <a target="_blank" href="https://x.com/itsTarun24">X/Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/tarunsingh24">LinkedIn</a>, or check out my portfolio on my <a target="_blank" href="http://tarunportfolio.vercel.app/blog">Blog</a>. I regularly share insights about AI, development, technical writing, and much more.</p>
<p>Happy coding, and may your creations be as vibrant as a field of fresh bananas!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Prompt Engineering Cheat Sheet for GPT-5: Learn These Patterns for Solid Code Generation ]]>
                </title>
                <description>
                    <![CDATA[ When large language models like ChatGPT first became widely available, a lot of us developers felt like we’d been handed a new superpower. We could use LLMs to help us develop new coding projects, build websites, and much more – just using a few prom... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/prompt-engineering-cheat-sheet-for-gpt-5/</link>
                <guid isPermaLink="false">68c3f6454edd0f63eaaf098e</guid>
                
                    <category>
                        <![CDATA[ Prompt Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ openai ]]>
                    </category>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tarun Singh ]]>
                </dc:creator>
                <pubDate>Fri, 12 Sep 2025 10:30:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757672756195/67ddcb24-b8dd-4bf6-a83b-103940f4ae85.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When large language models like ChatGPT first became widely available, a lot of us developers felt like we’d been handed a new superpower. We could use LLMs to help us develop new coding projects, build websites, and much more – just using a few prompts.</p>
<p>LLMs were like a tireless, super knowledgeable pair programmer that could conjure code out of thin air. We’d type a quick, messy request, and out would pop something that...kind of worked. It was amazing, but also a little frustrating. The code might be buggy, inefficient, or completely miss the subtle context of our project.</p>
<p>But with <a target="_blank" href="https://platform.openai.com/docs/models/gpt-5"><strong>GPT-5</strong></a>, the game has changed quite a bit. This model doesn’t just spit out code – it reasons, adapts, and understands context like never before. Still, here’s the catch: you need to speak its language to be able to generate the best output. But how? That’s where <strong>prompt engineering</strong> comes in.</p>
<p>In this article, I’ll share 10 proven patterns that will help you transform GPT-5 from a helpful tool into a rock-solid coding partner you can trust for accuracy and speed. Let’s get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-gpt-5-why-you-should-use-it-as-a-developer">What is GPT-5? Why You Should Use It as a Developer?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-prompt-engineering">Why Prompt Engineering?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-gpt-5-for-free">How to Use GPT-5 for Free?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-patterns-every-developer-should-know">Patterns Every Developer Should Know</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-persona-pattern">Persona Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-few-shot-pattern">Few-Shot Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chain-of-thought-pattern">Chain-of-Thought Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-delimiter-pattern">Delimiter Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-structured-output-pattern">Structured Output Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-flipped-interaction-pattern">Flipped Interaction Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-negative-constraint-pattern">Negative Constraint Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tool-use-pattern">Tool Use Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-verbosity-pattern">Verbosity Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-code-as-context-pattern">Code-as-Context Pattern</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-common-pitfalls-to-avoid">Common Pitfalls to Avoid</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<h2 id="heading-what-is-gpt-5-why-you-should-use-it-as-a-developer">What is GPT-5? Why You Should Use It as a Developer?</h2>
<p>OpenAI recently launched one of its best models, GPT-5. It’s capable of performing coding and agentic tasks across various domains. Think of it as a full-stack, super-intelligent intern who’s been given a master key to the internet's knowledge. It's not just better at writing code, it can under <em>why</em> you need the code, how it should fit into a larger system, and how to debug it.</p>
<p>It excels at:</p>
<ul>
<li><p><strong>Long-context reasoning:</strong> It can handle an entire codebase or a lengthy API documentation, a game-changer for refactoring or fixing bugs across multiple files.</p>
</li>
<li><p><strong>Instruction following:</strong> It’s far less likely to get confused by a long list of constraints or a detailed set of steps.</p>
</li>
<li><p><strong>Tool use and agentic tasks:</strong> It can intelligently decide to call an external API, execute a shell command, or search a repository to complete a task.</p>
</li>
</ul>
<h2 id="heading-why-prompt-engineering">Why Prompt Engineering?</h2>
<p>Think of LLMs as junior developers: super smart, but literal. The way you phrase your request drastically changes the output. Prompt engineering is the art and science of crafting effective instructions for an LLM to achieve a specific goal. It’s the method you use to communicate your intent, provide necessary context, and structure your request in a way that the model can most accurately understand and respond to. When you master it, you can:</p>
<ul>
<li><p>Make GPT-5 generate working, testable code.</p>
</li>
<li><p>Avoid vague or irrelevant answers.</p>
</li>
<li><p>Save tokens (and money).</p>
</li>
<li><p>Reduce the time spent editing or debugging outputs.</p>
</li>
</ul>
<h2 id="heading-how-to-use-gpt-5-for-free">How to Use GPT-5 for Free</h2>
<p>While the API for GPT-5 is a <strong>paid</strong> service, many developers can access its power for free or at a low cost. Now, for example, the default public version of ChatGPT often uses the version of GPT-5 with certain usage caps. Many tools like <strong>Cursor, GitHub Copilot, Microsoft Copilot</strong> integrate GPT-5 or lighter variants.</p>
<p>See the screenshot below of the Cursor IDE with integration of various models, including <code>gpt-5-fast</code>, <code>gpt-5-low</code>, and so on. If you’re experimenting, this is the easiest way to explore GPT-5 without paying for direct API calls.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757253133347/525d9160-fce7-4310-a85e-7e7dcd9d929d.png" alt="Screenshot of the Cursor IDE's settings, showing various GPT-5 model options" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>For this article, we'll use a standard API call structure, but these same principles apply whether you're using a web interface or an integrated tool. Let’s dive into the patterns.</p>
<h2 id="heading-patterns-every-developer-should-know">Patterns Every Developer Should Know</h2>
<h3 id="heading-persona-pattern">Persona Pattern</h3>
<p>You know how, when you're interviewing a candidate, you might ask them to act as if they're a "Engineering Lead or Manager" or a "Frontend engineer"? This pattern is the same idea. By assigning the model a role, you give it an immediate set of assumptions and a knowledge filter.</p>
<p>To effectively craft a persona, be specific. For example, instead of saying "You are a developer," try "You are a senior JavaScript developer specializing in backend APIs and scalability." This provides context on their skill level, their domain, and their preferred programming language, guiding the LLM toward a more tailored and expert-level response.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># Python Example</span>
<span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> OpenAI
client = OpenAI()

response = client.responses.create(
    model=<span class="hljs-string">"gpt-5"</span>,
    input=<span class="hljs-string">"""You are a senior JavaScript developer. 
    Refactor this code for readability:
    numbers = [8, 9, 10, 11, 12]; total=0
    for i in numbers: total+=i
    print(total)"""</span>
)

print(response.output_text)
</code></pre>
<p>This code ensures answers match the tone and expertise you expect, as specified in the prompt.</p>
<h3 id="heading-few-shot-pattern">Few-Shot Pattern</h3>
<p>Sometimes, the best way to get a specific style or format of code is to provide an example. This is called "few-shot" prompting. Instead of just describing what you want, you show the model a few completed examples.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> OpenAI

client = OpenAI()

prompt = <span class="hljs-string">"""
Convert functions to arrow syntax:

Example:
function sum(x, y) { return x + y; }
=&gt; const sum = (x, y) =&gt; x + y;

Then convert:
function greet(name) { return "Hey, " + name; }
"""</span>

response = client.responses.create(
    model=<span class="hljs-string">"gpt-5"</span>,
    input=prompt
)

print(response.output_text)
</code></pre>
<p>This code example provides a concrete, undeniable pattern for the model to follow, which is much more effective than a verbose description.</p>
<h3 id="heading-chain-of-thought-pattern">Chain-of-Thought Pattern</h3>
<p>When faced with a complex problem, humans don't just jump to a solution instead, we think through the steps. The Chain-of-Thought pattern asks the LLM to do the same. By telling the model to “think step by step,” you're not just requesting a final answer but you're instructing it to perform internal reasoning and break down the problem into smaller, logical parts. This process is what gives you room to debug.</p>
<p>If the final output is incorrect, you can review its thought process to identify where the logic went wrong. This is particularly effective with GPT-5's enhanced reasoning capabilities. The LLM's reasoning might look like an intermediate, internal monologue you don't always see, but asking it to print its thought process can make it explicit.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python">prompt = <span class="hljs-string">"""
Debug the below step by step:
My Python function loop skips the last element of the list. Check why?
"""</span>
</code></pre>
<p>By encouraging reasoning, you reduce errors in the code.</p>
<h3 id="heading-delimiter-pattern">Delimiter Pattern</h3>
<p>When you’re giving the LLM instructions, it’s important to give it a clear way to differentiate your instructions from the data you want it to process. To do this, you can use delimiters like <code>###</code>, <code>"""</code>, or <code>&lt;&gt;</code> wrapped around your input text to create a clean boundary. This is a general best practice for all LLMs, as they all can struggle with this distinction without a clear signal.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python">prompt = <span class="hljs-string">"""
Explain this code in simple and easy English:

###
for i in range(10):
    print(i**3)
###
"""</span>
</code></pre>
<p>This helps prevent the model from misinterpreting your data as part of the instructions, particularly when the data contains instruction-like strings.</p>
<h3 id="heading-structured-output-pattern">Structured Output Pattern</h3>
<p>If you need the model's response to be easily parseable by a program, you must specify the format clearly. This is particularly important when you want to use the output as an input for a different part of your software, such as generating JSON configuration files, XML for web services, or even markdown (MD) files for documentation. By telling the model to adhere to a rigid structure, you ensure the output is consistent and reliable.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> OpenAI

client = OpenAI()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_product_list</span>(<span class="hljs-params">product_info</span>):</span>
    prompt = <span class="hljs-string">f"""
    Generate a JSON object for the following product information.
    The JSON should have a 'products' key, which is an array of objects.
    Each object should have keys for 'name', 'category', 'price', and 'in_stock' (a boolean).

    Product Information:
    <span class="hljs-subst">{product_info}</span>

    Provide only the JSON output, and nothing else.
    """</span>

    response = client.responses.create(
        model=<span class="hljs-string">"gpt-5"</span>,
        input=prompt
    )

    <span class="hljs-comment"># Try to parse the response as JSON</span>
    <span class="hljs-keyword">try</span>:
        json_output = json.loads(response.output_text)
        <span class="hljs-keyword">return</span> json_output
    <span class="hljs-keyword">except</span> json.JSONDecodeError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error parsing JSON: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-comment"># Let's try it out</span>
product_data = <span class="hljs-string">"""
Laptop Pro, Electronics, 1500, True
Ergo Mouse, Accessories, 50, True
Wireless Keyboard, Accessories, 90, False
"""</span>

product_list = generate_product_list(product_data)
<span class="hljs-keyword">if</span> product_list:
    print(json.dumps(product_list, indent=<span class="hljs-number">2</span>))
</code></pre>
<p>In this example, the <code>prompt</code> is the instruction you give to the LLM. It's a text string that outlines a clear task and specifies the output format (a JSON object with specific keys). The <code>response</code> from the model is the raw text it generates, which should be the JSON object you requested. The Python code then attempts to parse this raw text response into a structured JSON object using <code>json.loads()</code>.</p>
<h3 id="heading-flipped-interaction-pattern">Flipped Interaction Pattern</h3>
<p>Sometimes, the best way to get GPT-5 to help you is to have it ask you some questions before it writes any code.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python">prompt = <span class="hljs-string">"""
I want a python script to scrape travel websites for travelling data.
Ask me 5 clarifying questions before writing the code.
"""</span>
</code></pre>
<p>This type of prompt helps prevent assumptions and will provide more accurate code.</p>
<h3 id="heading-negative-constraint-pattern">Negative Constraint Pattern</h3>
<p>While it’s important to tell the model what it <strong>should do</strong>, it’s also sometimes as important to tell it what it <strong>should not do</strong> or what it shouldn’t include in its response. This helps the model avoid certain words, tones, or topics.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> OpenAI

client = OpenAI()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_func</span>(<span class="hljs-params">technical_report</span>):</span>
    prompt = <span class="hljs-string">f"""
    Summarize the following technical report for a non-technical audience. 
    Do not use any specialized jargon, acronyms, or complex terms. 
    Use simple, everyday language.

    Technical Report:
    "<span class="hljs-subst">{technical_report}</span>"
    """</span>
    response = client.responses.create(
        model=<span class="hljs-string">"gpt-5"</span>,
        input=prompt
    )
    <span class="hljs-keyword">return</span> response.output_text

<span class="hljs-comment"># Let's try it out</span>
report = (
    <span class="hljs-string">"The quantum entanglement protocol (QEP) showed significant improvements "</span>
    <span class="hljs-string">"in qubit coherence by utilizing a novel multi-photon emission cascade. "</span>
    <span class="hljs-string">"The data indicates a 12% reduction in decoherence rates, validating the "</span>
    <span class="hljs-string">"hypothesis that non-linear optical feedback could mitigate environmental noise."</span>
)

summary = my_func(report)
print(summary)
</code></pre>
<p>This pattern is a great way to fine-tune the output and steer it away from common pitfalls, overly technical language, and so on, ensuring it meets your specific requirements.</p>
<h3 id="heading-tool-use-pattern">Tool Use Pattern</h3>
<p>GPT-5 is an incredible reasoning engine, but its real power comes when it can interact with external tools, like a web search, a code interpreter, or a file retrieval system. This pattern involves providing the model with a clear description of the tools it can or should use.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python">prompt = <span class="hljs-string">"""
You have access to a 'code_interpreter' tool.
Its purpose is to execute JavaScript code in a secure sandbox.
The tool takes a single argument: the JavaScript code as a string.

Your task is to use this tool to calculate the area of a rectangle 
with a length and breadth as 15.
After you get the result, respond with only the final answer number.
"""</span>
</code></pre>
<p>This is what unlocks GPT-5's potential for true agentic behavior. It can autonomously solve a problem by deciding which tools to use and in what order, moving beyond simple text generation.</p>
<h3 id="heading-verbosity-pattern">Verbosity Pattern</h3>
<p>Depending on your needs, you might want more or less concise output from the LLM. With the GPT-5 API, you can adjust the level of detail and length of the output with the use of the new <code>text.verbosity</code> parameter. Just select the level of <code>text.verbosity</code> as <code>low</code>, <code>medium</code>, or <code>high</code>.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> OpenAI

client = OpenAI()

<span class="hljs-comment"># Low Verbosity for a concise function</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_concise_code</span>(<span class="hljs-params">description</span>):</span>
    prompt = <span class="hljs-string">f"Write a Python function for <span class="hljs-subst">{description}</span>."</span>
    response = client.responses.create(
        model=<span class="hljs-string">"gpt-5"</span>,
        input=prompt,
        metadata={<span class="hljs-string">"verbosity"</span>: <span class="hljs-string">"low"</span>} 
    )
    <span class="hljs-keyword">return</span> response.output_text

user_input = <span class="hljs-string">"a quicksort algorithm"</span>

concise_code = get_concise_code(user_input)

print(<span class="hljs-string">"Concise Code-\n"</span>, concise_code)
</code></pre>
<p>This saves you time by preventing the model from "over-explaining" when you just need a quick snippet, and it gives you more context when you're learning something new or working with a complex piece of code.</p>
<h3 id="heading-code-as-context-pattern">Code-as-Context Pattern</h3>
<p>GPT-5’s massive context window is a game-changer for working with a full file or even a small project. Instead of just giving it a snippet, you can feed it an entire script and ask it to analyze, refactor, or optimize it.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_optimize_codebase</span>(<span class="hljs-params">code_file: str</span>) -&gt; str:</span>
    prompt = <span class="hljs-string">f"""
    You are a performance optimization expert. Analyze the following JavaScript 
    code file for potential performance bottlenecks, redundant code, or memory leaks. 
    Provide a detailed report and then a refactored version of the code.

    Code to analyze:
    \"\"\"
    <span class="hljs-subst">{code_file}</span>
    \"\"\"
    """</span>
    <span class="hljs-comment"># For this demonstration, we'll just return the prompt</span>
    <span class="hljs-keyword">return</span> prompt


<span class="hljs-comment"># User input: "your text input here"</span>
my_code = <span class="hljs-string">"""
// A large, unoptimized JavaScript file
const fetchData = async () =&gt; {
  const data = await fetch('https://api.example.com/data');
  const jsonData = await data.json();
  const filteredData = jsonData.filter(item =&gt; item.isActive);
  const mappedData = filteredData.map(item =&gt; {
    return {
      id: item.id,
      name: item.name.toUpperCase(),
      status: 'active'
    };
  });

  // This is a loop that could be more efficient
  const res= [];
  for (let i = 0; i &lt; mappedData.length; i++) {
    for (let j = 0; j &lt; 10000; j++) {
      res.append(mappedData[i])
    }
  }
  return res;
};
"""</span>

<span class="hljs-keyword">import</span> asyncio

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    prompt = <span class="hljs-keyword">await</span> my_optimize_codebase(my_code)
    print(prompt)

asyncio.run(main())
</code></pre>
<p>This prompt allows GPT-5 to see the full picture. It can understand variable scope, function dependencies, and the overall logic of a file in a way that’s impossible with a single, isolated snippet.</p>
<h2 id="heading-common-pitfalls-to-avoid">Common Pitfalls to Avoid</h2>
<ul>
<li><p><strong>Being Vague or Ambiguous:</strong> A prompt such as “Write some code” will result in a response that lacks focus and is generic. Make sure to clarify which programming language, the specific function, output format, and any limitations that may be required.</p>
</li>
<li><p><strong>Overloading a Single Prompt:</strong> An example “Write a Python script, summarize it in three bullet points, and then translate it into French” has multiple unrelated tasks and will commonly generate disorganized or incomplete reports. Focus on complex requests and break them down into a series of prompts.</p>
</li>
<li><p><strong>Failing to Iterate:</strong> Usually, your first prompt is hardly the most accurate or relevant to the topic of discussion. A general approach is to focus on the prompts generated and go over the concerns of the first sentence as a response. Take into consideration to elaborate, incorporate more facts, and refine, hence have a conversation back and forth to achieve the desired result.</p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>With GPT-5, prompt engineering is much more complex than locating a “magic” phrase. You need to shift your thinking to software engineering and articulate it for the AI. You are not merely instructing the AI – you are defining the parameters within which it should work to arrive at an efficient solution.</p>
<p>You can put these 10 patterns, along with the new features of reasoning effort and verbosity control, to make GPT-5 a dependable coding assistant: generating boilerplate code, debugging, code refactoring, or app scaffolding. Start improving your prompt engineering technique with lower models like GPT-4o, Gemini, and others. Once you are ready, upgrade to GPT-5 to power real-world dev workflows.</p>
<p>If you found this article helpful and want to discuss AI development, LLMs, or software development, feel free to connect with me on <a target="_blank" href="https://x.com/itsTarun24">X/Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/tarunsingh24">LinkedIn</a>, or check out my portfolio on my <a target="_blank" href="http://tarunportfolio.vercel.app/blog">Blog</a>. I regularly share insights about AI, development, technical writing, and so on, and would love to see what you build with this foundation.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an AI Study Planner Agent using Gemini in Python ]]>
                </title>
                <description>
                    <![CDATA[ The world is shifting from simple AI chatbots answering our queries to full-fledged systems that are capable of so much more. AI Agents can not only answer our queries but can also perform tasks we give them independently, making them much more power... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-ai-study-planner-agent-using-gemini-in-python/</link>
                <guid isPermaLink="false">68baff7226d95038487cbb1f</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agentic AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tarun Singh ]]>
                </dc:creator>
                <pubDate>Fri, 05 Sep 2025 15:19:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757085526077/66391609-bf27-4206-aa29-382508d15ee8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The world is shifting from simple AI chatbots answering our queries to full-fledged systems that are capable of so much more. AI Agents can not only answer our queries but can also perform tasks we give them independently, making them much more powerful and useful.</p>
<p>In this tutorial, you’ll build an advanced, web-based agent that serves as your Virtual Study Planner. This AI agent will be able to understand your goals, make decisions, and act to achieve them.</p>
<p>This project goes beyond basic conversation. You’ll learn to build a goal-based agent with two key capabilities:</p>
<ol>
<li><p><strong>Memory:</strong> The agent will remember your entire conversation history, allowing it to provide follow-up advice and adapt its plans based on your feedback.</p>
</li>
<li><p><strong>Tool Use:</strong> The agent will be capable of using a search tool to find relevant online resources, making it a more powerful assistant than one that relies solely on its internal knowledge.</p>
</li>
</ol>
<p>You’ll learn to create a complete system with a simple web UI built with Flask and Tailwind CSS, providing a solid foundation for building even more complex agents in the future. So, let’s get started.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-youll-be-using-to-build-this-agent">Tools You'll Be Using to Build this Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-ai-agents">Understanding AI Agents</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-ai-agents-how-many-types-are-there">What are AI Agents? How many types are there?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-ai-agents-is-unique-compared-to-other-ai-tools">How AI Agents is unique compared to other AI tools?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-environment">How to Set Up Your Environment</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-create-a-project-directory">1. Create a Project Directory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-create-a-virtual-environment">2. Create a Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-install-dependencies">3. Install Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-get-your-gemini-api-key">4. Get Your Gemini API Key</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-add-your-key-to-the-env-file">5. Add Your Key to the .env File</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-real-time-agent-logic">How to Build the Real-Time Agent Logic</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-create-the-gemini-client-with-web-search">Create the Gemini Client (with web search)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-the-flask-backend-and-frontend">Create the Flask Backend and Frontend</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-ai-agent">How to Test the AI Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before following this tutorial, you should have:</p>
<ul>
<li><p>Basic Python knowledge</p>
</li>
<li><p>Basics of web development</p>
</li>
<li><p>Python 3+ is installed on your machine</p>
</li>
<li><p>Installed VS Code or another IDE of your choice</p>
</li>
</ul>
<h2 id="heading-tools-youll-be-using-to-build-this-agent">Tools You'll Be Using to Build this Agent</h2>
<p>To build this study planner agent, you'll need a few components:</p>
<ul>
<li><p><strong>Google Gemini API:</strong> This is the core AI service that provides the generative model. It allows our agent to understand natural language, reason, and generate human-like responses.</p>
</li>
<li><p><strong>Flask:</strong> This is a lightweight web framework for Python. We’ll use it to create our web server (that is, the backend). Its primary purpose here is to handle web requests from the user's browser, process them, and send back a response.</p>
</li>
<li><p><strong>Tailwind CSS:</strong> This is a CSS framework for building the user interface (that is, the frontend). Instead of writing custom CSS, you use pre-defined classes like <code>bg-blue-300</code>, <code>m-4</code>, and so on, to style the page directly in your HTML.</p>
</li>
<li><p><strong>Python-dotenv:</strong> This library helps us manage environment variables.</p>
</li>
<li><p><strong>DuckDuckGo Search:</strong> This library provides a simple way to perform real-time web searches. It acts as the "tool" for our AI agent. When a user asks a question that requires external information, our agent can use this tool to find relevant resources on the web and use that information to formulate a response.</p>
</li>
</ul>
<h2 id="heading-understanding-ai-agents">Understanding AI Agents</h2>
<p>Before jumping into the code, let’s cover the basics so you understand what an AI agent is and what it’s capable of.</p>
<h3 id="heading-what-are-ai-agents-how-many-types-are-there">What Are AI Agents? How Many Types Are There?</h3>
<p>An AI agent is software that can autonomously perform tasks on a user’s behalf. AI agents perceive their surroundings, process information, and act to achieve the user’s goals. Unlike fixed programs, an agent can reason and adapt.</p>
<p>There are a few different types of agents, including:</p>
<ul>
<li><p><strong>Simple Reflex</strong> (acts on current input, like a thermostat)</p>
</li>
<li><p><strong>Model-Based</strong> (uses an internal map, like robot vacuums)</p>
</li>
<li><p><strong>Goal-Based</strong> (plans to reach goals, like a study planner)</p>
</li>
<li><p><strong>Utility-Based</strong> (chooses best outcomes, like trading bots)</p>
</li>
<li><p><strong>Learning Agents</strong> (improve over time, like recommendation systems).</p>
</li>
</ul>
<h3 id="heading-how-are-ai-agents-unique-compared-to-other-ai-tools">How Are AI Agents Unique Compared to Other AI Tools?</h3>
<p>AI agents use technologies like LLMs, but they’re distinct because of their autonomy and ability to act. Let’s understand these different types of AI tools in more detail:</p>
<ol>
<li><p><strong>Large Language Models (LLMs):</strong> LLMs are the brain of the operation. They’re trained on a very large dataset to understand and process user queries in natural language to generate human-like output. OpenAI’s GPT, Google’s Gemini, and Anthropic’s Claude are all examples of LLMs.</p>
</li>
<li><p><strong>Retrieval-Augmented Generation (RAG):</strong> RAG is a process or a technique that allows LLMs to not only get their information from training data but also from external sources, like a database or document library, to answer user queries. While RAG retrieves information, it doesn't independently decide to perform an action or plan a sequence of steps to achieve a goal.</p>
</li>
<li><p><strong>AI Agents:</strong> As explained above, agents are the systems that can perform user tasks using LLMs as their core reasoning engine. An agent’s full architecture allows it to perceive its environment, plan, act, and learn (memory, based on past interactions).</p>
</li>
</ol>
<p>In this tutorial, you are going to use an LLM (Gemini) to reason, as well as a web search engine, DuckDuckGo search, for building the agent. So, now let’s move on to the next step.</p>
<h2 id="heading-how-to-set-up-your-environment">How to Set Up Your Environment</h2>
<p>Before you can build your Virtual Study Planner AI agent, you’ll need to set up your development environment. Here are the steps you’ll need to follow:</p>
<h3 id="heading-1-create-a-project-directory">1. Create a Project Directory</h3>
<p>First, create a new folder with any name and move to that directory:</p>
<pre><code class="lang-bash">mkdir study-planner
<span class="hljs-built_in">cd</span> study-planner
</code></pre>
<h3 id="heading-2-create-a-virtual-environment">2. Create a Virtual Environment</h3>
<p>In Python, it’s always recommended to work in a virtual environment. So, create one and activate it like this:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>Now activate the virtual environment:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># macOS/Linux</span>
<span class="hljs-built_in">source</span> venv/bin/activate

<span class="hljs-comment"># Windows</span>
venv\Scripts\activate
</code></pre>
<h3 id="heading-3-install-dependencies">3. Install Dependencies</h3>
<p>We’ll need a couple of packages or dependencies to build the AI study planner agent, and they include:</p>
<ul>
<li><p><code>flask</code>: web server</p>
</li>
<li><p><code>google-generativeai</code>: Gemini client</p>
</li>
<li><p><code>python-dotenv</code>: load GEMINI_API_KEY from .env</p>
</li>
<li><p><code>requests</code>: useful HTTP helper (nice to have)</p>
</li>
<li><p><code>duckduckgo-search</code>: real web search</p>
</li>
</ul>
<p>You can install them with a single command:</p>
<pre><code class="lang-bash">pip install flask google-generativeai python-dotenv requests duckduckgo-search
</code></pre>
<h3 id="heading-4-get-your-gemini-api-key">4. Get Your Gemini API Key</h3>
<p>Go to <a target="_blank" href="https://aistudio.google.com/">Google AI Studio</a> and create a new account (if you don’t have one already).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756829119333/fd2f68f8-2d15-491e-9b9d-a5563f3d926b.png" alt="Google AI Studio Landing Page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Next, get yourself a new API key by clicking the <strong>Create API Key</strong> from the <a target="_blank" href="https://platform.openai.com/api-keys">API Keys</a> section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756829166748/a79ec404-2267-42f3-8185-a88903f5bcaf.png" alt="Google AI Studio API Keys dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>NOTE:</strong> Once the API Key is generated, SAVE it somewhere else. You may not get the same API key again.</p>
<h3 id="heading-5-add-your-key-to-the-env-file">5. Add Your Key to the <code>.env</code> File</h3>
<p>Create a <code>.env</code> file inside <code>backend/</code> and add your API key.</p>
<pre><code class="lang-bash">GEMINI_API_KEY=your_api_key_here
</code></pre>
<p>Now you should have set up your development environment successfully. You’re ready to build the Virtual Study Planner AI agent. Let’s start!</p>
<h2 id="heading-how-to-build-the-real-time-agent-logic">How to Build the Real-Time Agent Logic</h2>
<p>The core of this project is a continuous loop that accepts user input, maintains a conversation history, and sends that history to the Gemini API to generate a response. This is how we give the agent memory.</p>
<h3 id="heading-create-the-gemini-client-with-web-search">Create the Gemini Client (with web search)</h3>
<p>Create a new file at <code>backend/gemini_client.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># backend/gemini_client.py</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Dict
<span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">from</span> duckduckgo_search <span class="hljs-keyword">import</span> DDGS

<span class="hljs-comment"># Load environment variables</span>
load_dotenv()

<span class="hljs-comment"># function uses a query string and duckduckgo_search library to perform a web search</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">perform_web_search</span>(<span class="hljs-params">query: str, max_results: int = <span class="hljs-number">6</span></span>) -&gt; List[Dict[str, str]]:</span>
    <span class="hljs-string">"""Perform a DuckDuckGo search and return a list of results.

    Each result contains: title, href, body.
    """</span>
    results: List[Dict[str, str]] = []
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> DDGS() <span class="hljs-keyword">as</span> ddgs:
            <span class="hljs-keyword">for</span> result <span class="hljs-keyword">in</span> ddgs.text(query, max_results=max_results):
                <span class="hljs-comment"># result keys typically include: title, href, body</span>
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(result, dict):
                    <span class="hljs-keyword">continue</span>
                title = result.get(<span class="hljs-string">'title'</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
                href = result.get(<span class="hljs-string">'href'</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
                body = result.get(<span class="hljs-string">'body'</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
                <span class="hljs-keyword">if</span> title <span class="hljs-keyword">and</span> href:
                    results.append({
                        <span class="hljs-string">'title'</span>: title,
                        <span class="hljs-string">'href'</span>: href,
                        <span class="hljs-string">'body'</span>: body,
                    })
        <span class="hljs-keyword">return</span> results
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"DuckDuckGo search error: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> []

<span class="hljs-comment"># A class that manages the interaction with the Gemini API and core agent logic </span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GeminiClient</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">try</span>:
            genai.configure(api_key=os.getenv(<span class="hljs-string">'GEMINI_API_KEY'</span>))
            self.model = genai.GenerativeModel(<span class="hljs-string">'gemini-1.5-flash'</span>)
            self.chat = self.model.start_chat(history=[])
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"Error configuring Gemini API: <span class="hljs-subst">{e}</span>"</span>)
            self.chat = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_response</span>(<span class="hljs-params">self, user_input: str</span>) -&gt; str:</span>
        <span class="hljs-string">"""Generate an AI response with optional web search when prefixed.

        To trigger web search, start your message with one of:
        - "search: &lt;query&gt;"
        - "/search &lt;query&gt;"
        Otherwise, the model responds directly using chat history.
        """</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.chat:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"AI service is not configured correctly."</span>

        <span class="hljs-keyword">try</span>:
            text = user_input <span class="hljs-keyword">or</span> <span class="hljs-string">""</span>
            lower = text.strip().lower()

            <span class="hljs-comment"># Search trigger</span>
            search_query = <span class="hljs-literal">None</span>
            <span class="hljs-keyword">if</span> lower.startswith(<span class="hljs-string">"search:"</span>):
                search_query = text.split(<span class="hljs-string">":"</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">1</span>].strip()
            <span class="hljs-keyword">elif</span> lower.startswith(<span class="hljs-string">"/search "</span>):
                search_query = text.split(<span class="hljs-string">" "</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">1</span>].strip()

            <span class="hljs-keyword">if</span> search_query:
                web_results = perform_web_search(search_query, max_results=<span class="hljs-number">6</span>)
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> web_results:
                    <span class="hljs-keyword">return</span> <span class="hljs-string">"I could not retrieve web results right now. Please try again."</span>

                <span class="hljs-comment"># Build context with numbered references</span>
                refs_lines = []
                <span class="hljs-keyword">for</span> idx, item <span class="hljs-keyword">in</span> enumerate(web_results, start=<span class="hljs-number">1</span>):
                    refs_lines.append(<span class="hljs-string">f"[<span class="hljs-subst">{idx}</span>] <span class="hljs-subst">{item[<span class="hljs-string">'title'</span>]}</span> — <span class="hljs-subst">{item[<span class="hljs-string">'href'</span>]}</span>\n<span class="hljs-subst">{item[<span class="hljs-string">'body'</span>]}</span>"</span>)
                refs_block = <span class="hljs-string">"\n\n"</span>.join(refs_lines)

                system_prompt = (
                    <span class="hljs-string">"You are an AI research assistant. Use the provided web search results to answer the user query. "</span>
                    <span class="hljs-string">"Synthesize concisely, cite sources inline like [1], [2] where relevant, and include a brief summary."</span>
                )
                composed = (
                    <span class="hljs-string">f"&lt;system&gt;\n<span class="hljs-subst">{system_prompt}</span>\n&lt;/system&gt;\n"</span>
                    <span class="hljs-string">f"&lt;user_query&gt;\n<span class="hljs-subst">{search_query}</span>\n&lt;/user_query&gt;\n"</span>
                    <span class="hljs-string">f"&lt;web_results&gt;\n<span class="hljs-subst">{refs_block}</span>\n&lt;/web_results&gt;"</span>
                )
                response = self.chat.send_message(composed)
                <span class="hljs-keyword">return</span> response.text

            <span class="hljs-comment"># Default: normal chat</span>
            response = self.chat.send_message(text)
            <span class="hljs-keyword">return</span> response.text
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"Error generating response: <span class="hljs-subst">{e}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-string">"I'm sorry, I encountered an error processing your request."</span>
</code></pre>
<p>Let’s understand what’s going on in the above code:</p>
<ul>
<li><p>The <code>perform_web_search()</code> function:</p>
<ul>
<li><p>We keep a chat session open so the model remembers the conversation.</p>
</li>
<li><p>If a message starts with <code>search:</code> or <code>/search</code>, the DuckDuckGo service is called, gathers a few results, and passes them to Gemini with a short instruction to cite sources.</p>
</li>
<li><p>Otherwise, we just send the message as normal.</p>
</li>
</ul>
</li>
<li><p>The <code>GeminiClient</code> class:</p>
<ul>
<li><p>The <code>GeminiClient</code> class is designed to connect and talk with Google’s Gemini AI. Inside the <code>__init__</code> method, it first calls <code>genai.configure()</code> with the API key from the environment variables, which basically unlocks access to Gemini’s services.</p>
</li>
<li><p>Then, <code>self.model = genai.GenerativeModel('gemini-1.5-flash')</code> loads the specific Gemini model, and <code>self.chat = self.model.start_chat(history=[])</code> starts a new conversation with no previous history. This way, the class is ready to send and receive AI responses.</p>
</li>
<li><p>The real action happens in <code>generate_response()</code>. If a user’s message begins with <code>search:</code> or <code>/search</code>, it triggers a DuckDuckGo search using <code>perform_web_search()</code>.</p>
</li>
<li><p>The results are formatted with titles, links, and snippets, and then passed to Gemini to create a clear, cited answer (you can sanitize the incoming data later by using any package in Python to make it more user-friendly in the frontend).</p>
</li>
<li><p>If no search command is used, it simply chats with Gemini using the given input. Error handling is built in, so instead of breaking, it returns a general safe message.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-create-the-flask-backend-and-frontend">Create the Flask Backend and Frontend</h3>
<p>Next, we'll set up the Flask web server to connect our agent logic to a simple web interface.</p>
<h4 id="heading-the-flask-backend">The Flask Backend</h4>
<p>Create a new <code>backend</code> folder inside the study-planner directory, and add a new file <code>app.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># backend/app.py</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request, jsonify
<span class="hljs-keyword">from</span> gemini_client <span class="hljs-keyword">import</span> GeminiClient

app = Flask(__name__, template_folder=<span class="hljs-string">'../templates'</span>)
client = GeminiClient()

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)

<span class="hljs-meta">@app.route('/api/chat', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>():</span>
    payload = request.get_json(silent=<span class="hljs-literal">True</span>) <span class="hljs-keyword">or</span> {}
    user_message = payload.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>).strip()
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user_message:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'No message provided'</span>}), <span class="hljs-number">400</span>

    <span class="hljs-keyword">try</span>:
        response_text = client.generate_response(user_message)
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response_text})
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Error generating response'</span>}), <span class="hljs-number">500</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>What it does:</p>
<ul>
<li><p><code>@app.route('/')</code>: This is the homepage. When a user navigates to the main URL, like, <code>http://localhost:5000</code>), Flask runs the <code>index()</code> function, which simply renders the <code>index.html</code> file. This serves the entire user interface to the browser useful when you don’t want to use the command line interface.</p>
</li>
<li><p>Next, we have created <code>@app.route('/api/chat', methods=['POST'])</code>, the API endpoint. When the user clicks "Send" on the frontend, the JavaScript sends a <code>POST</code> request to this URL. The <code>chat()</code> function then receives the user's message, passes it to the <code>GeminiClient</code> to get a response, and then sends that response back to the frontend as a JSON object.</p>
</li>
</ul>
<h4 id="heading-the-flask-frontend">The Flask Frontend</h4>
<p>Create a new folder named <code>templates</code> in your project's root directory. Inside it, create a file <code>index.html</code>.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>AI Study Planner<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
      <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f3f4f6</span>;
      }
      <span class="hljs-selector-class">.chat-container</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">768px</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
      }
      <span class="hljs-selector-class">.typing-indicator</span> {
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#6b7280</span>;
      }
      <span class="hljs-selector-class">.typing-dot</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">8px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">8px</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#6b7280</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">animation</span>: typing <span class="hljs-number">1s</span> infinite ease-in-out;
      }
      <span class="hljs-selector-class">.message-bubble</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">1.5rem</span>;
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
      }
      <span class="hljs-selector-class">.user-message</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#3b82f6</span>;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">align-self</span>: flex-end;
      }
      <span class="hljs-selector-class">.agent-message</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e5e7eb</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#374151</span>;
        <span class="hljs-attribute">align-self</span>: flex-start;
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white shadow-sm p-4 text-center font-bold text-xl text-gray-800"</span>
      &gt;</span>
        AI Study Planner
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chat-history"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 overflow-y-auto p-4 space-y-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"message-bubble agent-message"</span>&gt;</span>
          Hello! I'm your AI Study Planner. What topic would you like to study
          today?
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white p-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"user-input"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 p-3 border-2 border-gray-300 rounded-full focus:outline-none focus:border-blue-500"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type your message..."</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"send-btn"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"ml-4 px-6 py-3 bg-blue-500 text-white rounded-full font-semibold hover:bg-blue-600 transition-colors"</span>
          &gt;</span>
            Send
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-keyword">const</span> chatHistory = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"chat-history"</span>);
      <span class="hljs-keyword">const</span> userInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"user-input"</span>);
      <span class="hljs-keyword">const</span> sendBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"send-btn"</span>);

      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addMessage</span>(<span class="hljs-params">sender, text</span>) </span>{
        <span class="hljs-keyword">const</span> messageElement = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        messageElement.classList.add(
          <span class="hljs-string">"message-bubble"</span>,
          sender === <span class="hljs-string">"user"</span> ? <span class="hljs-string">"user-message"</span> : <span class="hljs-string">"agent-message"</span>
        );
        messageElement.textContent = text;
        chatHistory.appendChild(messageElement);
        chatHistory.scrollTop = chatHistory.scrollHeight;
      }

      <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> message = userInput.value.trim();
        <span class="hljs-keyword">if</span> (message === <span class="hljs-string">""</span>) <span class="hljs-keyword">return</span>;

        addMessage(<span class="hljs-string">"user"</span>, message);
        userInput.value = <span class="hljs-string">""</span>;

        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/chat"</span>, {
            <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
            <span class="hljs-attr">headers</span>: {
              <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
            },
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">message</span>: message }),
          });

          <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
          <span class="hljs-keyword">if</span> (data.response) {
            addMessage(<span class="hljs-string">"agent"</span>, data.response);
          } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.error) {
            addMessage(<span class="hljs-string">"agent"</span>, <span class="hljs-string">`Error: <span class="hljs-subst">${data.error}</span>`</span>);
          } <span class="hljs-keyword">else</span> {
            addMessage(<span class="hljs-string">"agent"</span>, <span class="hljs-string">"Unexpected response from server."</span>);
          }
        } <span class="hljs-keyword">catch</span> (error) {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error:"</span>, error);
          addMessage(<span class="hljs-string">"agent"</span>, <span class="hljs-string">"Sorry, something went wrong. Please try again."</span>);
        }
      }

      sendBtn.addEventListener(<span class="hljs-string">"click"</span>, sendMessage);
      userInput.addEventListener(<span class="hljs-string">"keypress"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">"Enter"</span>) {
          sendMessage();
        }
      });
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>That’s the entire UI. It’s just one page with a text box and a send button. It contains a simple JavaScript function to handle the chat interaction. Here’s how it works:</p>
<ul>
<li><p>When the user types a message and hits "Send," it:</p>
<ul>
<li><p>Takes the message from the input field.</p>
</li>
<li><p>Creates a new <code>user-message</code> bubble and displays it.</p>
</li>
<li><p>Uses the <code>fetch()</code> API to send the message to the backend's <code>/api/chat</code> endpoint.</p>
</li>
<li><p>Waits for the backend's response.</p>
</li>
<li><p>Once the response is received, it creates a new <code>agent-message</code> bubble and displays the AI’s reply.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-how-to-test-the-ai-agent">How to Test the AI Agent</h2>
<p>At this point, your project structure should look like this:</p>
<pre><code class="lang-bash">study-planner/
├── backend/
│   ├── .env
│   ├── app.py
│   └── gemini_client.py
└── templates/
    └── index.html
</code></pre>
<p>Now, navigate to the <code>backend</code> directory, and run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> backend
python app.py
</code></pre>
<p>If everything is set up, you’ll see the Flask app start on <a target="_blank" href="http://127.0.0.1:5000"><code>http://127.0.0.1:5000</code></a> or <a target="_blank" href="http://localhost:5000"><code>http://localhost:5000</code></a>.</p>
<p>Open that URL in your browser. That’s it, you have finally created an AI agent for yourself!</p>
<p>Try out asking normal questions like:</p>
<ul>
<li><p>“Make me a 3-week plan to learn Java programming for beginners.”</p>
</li>
<li><p>“Provide me a quiz on AI agents development?”</p>
</li>
</ul>
<p>Or you can also trigger a web search like:</p>
<ul>
<li><p><code>search: resources for java</code></p>
</li>
<li><p><code>/search how to prepare frontend coding interviews</code></p>
</li>
</ul>
<p>When you use the search prefix like above, the agent fetches a handful of links and asks <strong>Gemini</strong> to synthesize them with short inline citations like [1], [2]. It’s great for quick research summaries.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Congratulations! You now have a working study planner agent that remembers your chats and can even look things up online.</p>
<p>From here, you can further enhance this agent by:</p>
<ul>
<li><p>Saving user histories in a database.</p>
</li>
<li><p>Adding authentication, handling multiple users.</p>
</li>
<li><p>Connecting calendars or task managers, and much more.</p>
</li>
</ul>
<p>This foundation provides a solid starting point for building even more sophisticated AI agents tailored to your specific needs.</p>
<p>If you found this tutorial helpful and want to discuss AI development or software development, feel free to connect with me on <a target="_blank" href="https://x.com/itsTarun24">X/Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/tarunsingh24">LinkedIn</a>, or check out my portfolio at <a target="_blank" href="http://tarunportfolio.vercel.app/blog">Blog</a>. I regularly share insights about AI, development, technical writing, and so on, and would love to see what you build with this foundation.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
