KnowledgeWala Workshop · Agentic AI

Build an AI agent that searches the web for you

A hands-on workshop with LangChain, Google Gemini and DuckDuckGo search. Non-technical readers learn what an AI agent is and why it matters; developers build one, step by step, in about an hour.

Level: beginner to intermediate Time: 15 min read · 60 min hands-on Stack: Python, LangChain 1.x, Gemini, DuckDuckGo Cost: free tier is enough

In this workshop

  1. 1Why a chatbot alone is not enough Everyone
  2. 2How the agent loop works Everyone
  3. 3Words you will hear Everyone
  4. 4Where businesses use agents Everyone
  5. 5What you need Developers
  6. 6Build it: 9 steps Developers
  7. 7Check the answer, not just the code Everyone
  8. 8Errors we hit and how we fixed them Developers
  9. 9Under the hood: ReAct vs tool calling Developers
  10. 10Limits, cost and safety Everyone
  11. 11Practice tasks and quiz Everyone
Everyone

1. Why a chatbot alone is not enough

A large language model (LLM) such as Gemini learned from text up to a certain date. Ask it "What is the latest iPhone and what does it cost?" and it answers from memory, which may be months or years out of date. It cannot look anything up by itself.

An AI agent fixes this. It is the same LLM, plus a set of tools it is allowed to use, plus a loop that lets it use those tools as many times as it needs before answering. In this workshop the tool is a web search engine.

Think of it like this

The LLM is a very well-read assistant sitting at a desk. Without tools, they answer from what they remember. An agent is the same assistant with a phone and a browser. When you ask about today's prices, they decide to search, read the results, maybe search again, and only then reply. You still ask one question and get one answer; the extra work happens in between.

EveryoneDevelopers

2. How the agent loop works

This is the exact flow of the agent we build. Each step shows what happens in plain words and, in green, the matching piece of code.

  1. 1
    YouYour question"What is the latest iPhone model and its price in USD? Give me the price in UK currency."agent.invoke({"messages": [...]})
  2. 2
    LangChainLangChain agentPackages your question together with the list of tools Gemini may use, and sends it to Gemini.create_agent(model=llm, tools=[search])
  3. 3
    GeminiGemini decides: "I need web information"Gemini sees the question is about something current, so instead of answering it asks to use the search tool and writes a search query.AIMessage.tool_calls = [{"name": "duckduckgo_search", "args": {"query": "..."}}]
  4. 4
    ToolDuckDuckGoSearchRunLangChain, not Gemini, actually runs the search on DuckDuckGo. Gemini never touches the internet directly.DuckDuckGoSearchRun().run(query)
  5. 5
    ToolSearch resultsA few snippets of text from web pages come back.ToolMessage(content="...snippets...")
  6. 6
    LangChainLangChain sends results back to GeminiThe results are added to the conversation, so Gemini now "knows" what the web said.messages = [question, tool call, tool result]
  7. 7
    GeminiGemini decides whether another tool call is neededNot enough information? It searches again with a better query (back to step 3). Enough? It writes the answer.tool_calls present → loop again · no tool_calls → stop
  8. ✓
    YouFinal answerOne clear reply, built from fresh search results.response["messages"][-1].content
Gemini thinkingTool doing workLangChain / you

The key idea: Gemini only decides and writes. LangChain executes. This split is what makes agents controllable: you choose which tools exist, and the model can only ask for those.

Everyone

3. Words you will hear

TermPlain meaningIn this workshop
LLMAn AI model that reads and writes text.Google Gemini (gemini-3.5-flash-lite)
AgentAn LLM that can use tools in a loop until a task is done.create_agent(...)
ToolAny function the agent is allowed to call: search, calculator, database, email.DuckDuckGoSearchRun
Tool callThe model's request: "please run this tool with these inputs".msg.tool_calls
LangChainAn open-source Python library that connects LLMs, tools and data.langchain 1.x
API keyA secret password that lets your code use a paid or metered service.GOOGLE_API_KEY in a .env file
PromptThe instructions and question sent to the model.Your question, plus an optional system prompt
ReActA pattern where the model alternates Reason → Act → Observe.The idea behind the loop in section 2
Everyone

4. Where businesses use agents like this

Market and price research

"Compare the three cheapest flights to Dubai next Friday." The agent searches, compares and summarises.

Customer support

Swap web search for your help-centre search and order database. The agent looks up the order before replying.

Sales preparation

"Brief me on this company before my 3 pm call." Search for news, funding and leadership changes.

Internal knowledge

Point the tool at company documents instead of the web, and staff can ask policy questions in plain language.

The pattern stays the same in every case. Only the tools change. That is why learning this one small agent is a big step.

Developers

5. What you need

  • Python 3.10 or newer. The original notebook ran on Python 3.14 on Windows.
  • A Gemini API key from Google AI Studio. The free tier is enough for this workshop.
  • VS Code with the Jupyter extension, or plain Jupyter Notebook.
  • No search API key: DuckDuckGo search is free.

Versions used when this was tested: langchain 1.4.3, langchain-core 1.6.7, langgraph 1.2.14, langchain-google-genai 4.4.0, langchain-community 0.4.2, ddgs 9.16.0. AI libraries change quickly; if something breaks, compare your versions with these first.

Developers

6. Build it: 9 steps

  1. Create a project and install packages

    PowerShell
    # Windows (PowerShell) - macOS/Linux: use "python3" and "source .venv/bin/activate"
    mkdir ai_workshop; cd ai_workshop
    python -m venv .venv
    .venv\Scripts\activate
    
    pip install -U langchain langgraph langchain-core langchain-community langchain-google-genai ddgs python-dotenv jupyter

    A virtual environment keeps this project's packages separate from everything else on your computer. ddgs is the current name of the DuckDuckGo search package; the older duckduckgo-search name is being replaced.

  2. Store your API key safely

    .env
    # File name: .env   (same folder as your notebook - never commit this file to GitHub)
    GOOGLE_API_KEY=paste-your-key-from-google-ai-studio-here

    Keeping the key in a .env file means it never appears in your code, screenshots or GitHub. Add .env to .gitignore.

  3. Check that Python can see the key

    Python
    import os
    from dotenv import load_dotenv
    
    load_dotenv()                                   # reads .env into environment variables
    api_key = os.getenv("GOOGLE_API_KEY")
    
    print("API key loaded:", bool(api_key))         # True = good. Never print the key itself.

    If this prints False, every later step fails with "API key required". Fix it here first.

  4. Ask Google which models you can use

    Python
    from google import genai
    import os
    
    client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))
    
    # Model names change often. Ask Google which ones YOUR key can use today.
    for model in client.models.list():
        print(model.name)

    Model names change every few months and old ones get switched off. In our run, gemini-2.5-flash returned "no longer available to new users", so we listed the models and picked one from the list.

  5. Talk to Gemini once, without tools

    Python
    from langchain_google_genai import ChatGoogleGenerativeAI
    
    llm = ChatGoogleGenerativeAI(
        model="gemini-3.5-flash-lite",   # pick a name from the list in step 4
        timeout=60,                      # give up after 60 s instead of hanging
        max_retries=3,                   # retry temporary errors such as 503 "high demand"
    )
    
    response = llm.invoke("Say hello in one short sentence.")
    
    # Newer Gemini models return a LIST of content blocks, not a plain string.
    def text_of(content):
        if isinstance(content, list):
            return "".join(part.get("text", "") for part in content if isinstance(part, dict))
        return content
    
    print(text_of(response.content))

    This proves the key, network and model all work before you add more moving parts. The text_of helper handles a real surprise from our run: the newer model returned a list of content blocks instead of a plain string.

  6. Give the agent a tool: web search

    Python
    from langchain_community.tools import DuckDuckGoSearchRun
    
    search = DuckDuckGoSearchRun()        # free web search, no API key needed
    tools = [search]
    
    print("Tool ready:", search.name)     # -> duckduckgo_search
    print(search.description)             # this text is what Gemini reads to decide WHEN to use it

    The tool's name and description are sent to Gemini. That is all Gemini knows about the tool, so a clear description directly improves when and how the agent uses it.

  7. Create the agent and ask your question

    Python
    from langchain.agents import create_agent
    
    agent = create_agent(
        model=llm,
        tools=tools,
    )
    
    question = ("What is the latest iPhone model and its price in USD? "
                "Give me the price in UK currency.")
    
    response = agent.invoke({
        "messages": [{"role": "user", "content": question}]
    })
    
    print(text_of(response["messages"][-1].content))

    Output from our run:

    Output
    The latest flagship smartphone lineup from Apple is the iPhone 16 series
    (including the iPhone 16, iPhone 16 Plus, iPhone 16 Pro, and iPhone 16 Pro Max).
    
    Taking the base model (iPhone 16 with 128GB of storage) as the standard reference:
    
    * Price in USD: $799 (US prices do not include state/local sales tax)
    * Price in UK Currency (GBP): £799 (UK prices automatically include 20% VAT)
    
    (If you are looking at the entry-level Pro model, the iPhone 16 Pro starts at $999 USD / £999 GBP).
  8. Look inside the loop

    Python
    # Look inside the loop: every step the agent took is in response["messages"]
    for msg in response["messages"]:
        kind = type(msg).__name__                     # HumanMessage / AIMessage / ToolMessage
        if kind == "AIMessage" and msg.tool_calls:
            for call in msg.tool_calls:
                print(f"🧠 Gemini decided to call {call['name']} with {call['args']}")
        elif kind == "ToolMessage":
            print(f"🔎 Tool returned: {str(msg.content)[:200]}...")
        elif kind == "AIMessage":
            print(f"✅ Final answer: {text_of(msg.content)[:200]}...")
        else:
            print(f"🙋 You asked: {msg.content}")

    The response holds every message in the loop: your question, each tool call Gemini asked for, each search result, and the final answer. Printing them shows you whether the agent really searched or answered from memory. This matters, as section 7 explains.

  9. Make the agent more reliable

    Python
    from datetime import date
    from langchain.agents import create_agent
    from langchain.tools import tool
    
    @tool
    def usd_to_gbp(amount_usd: float, rate: float) -> str:
        """Convert US dollars to British pounds. Always find today's USD->GBP rate with web search first."""
        return f"{amount_usd} USD = {amount_usd * rate:.2f} GBP (rate {rate})"
    
    agent = create_agent(
        model=llm,
        tools=[search, usd_to_gbp],
        system_prompt=(
            f"Today is {date.today():%d %B %Y}. "
            "For anything that changes over time (products, prices, exchange rates, news) "
            "you MUST use web search instead of your own memory. "
            "Name the sources you used. If the search fails, say so."
        ),
    )

    Two upgrades. A system prompt tells the agent today's date and forces it to search for anything time-sensitive. A second custom tool, built with @tool, does the currency maths exactly, so the model does not guess a rate. The function's docstring becomes the tool description Gemini reads.

Everyone

7. Check the answer, not just the code

Our agent ran without errors and still gave a doubtful answer. It was run in October 2026 and said the latest iPhone is the iPhone 16. Apple released the iPhone 17 range in September 2025, so that answer was out of date.

A second run converted $799 using an "approximate" rate of 0.75 GBP per USD. The model chose that rate itself; it did not look it up. It also correctly noted that Apple's UK price is set separately (£799 including VAT) and is not a straight currency conversion.

Lessons for everyone, technical or not:

  • Working code is not the same as a correct answer. Always check facts that matter.
  • Check whether a tool was actually used. Step 8 shows the tool calls. If there are none, the answer came from the model's memory.
  • Tell the agent today's date. Models don't know it unless you say so, which is why step 9 adds it.
  • Use real tools for numbers. Exchange rates, totals and dates should come from a tool or a calculation, not from the model's guess.
  • Ask for sources. An answer that names its sources is easy to verify.
Developers

8. Errors we hit and how we fixed them

Every one of these happened while building this workshop. Expect to meet some of them.

Error messageWhat it meansFix
404 NOT_FOUND · "model is no longer available to new users"That model name has been retired for new keys.Run step 4 and choose a model from the list.
404 · "not found for API version v1beta"The model name is misspelled or doesn't exist (we tried gemini-3.8-flash-lite).Copy the name exactly from the step 4 list, without the models/ prefix.
"API key required for Gemini Developer API"The key wasn't loaded into the environment.Check .env location and spelling, call load_dotenv(), re-run step 3.
503 UNAVAILABLE · "high demand"Google's servers are busy. It's temporary and not your fault.Set max_retries=3 and timeout=60, wait, or switch to a lighter model.
UserWarning: "temperature will be ignored"Some newer models use fixed sampling settings.Harmless. Remove temperature to silence it.
Output looks like [{'type': 'text', 'text': ...}]The model returned content blocks, not a string.Use the text_of() helper from step 5.
DDGSException: DNSError … wt.wikipedia.orgThe search library's automatic backend tried a Wikipedia address that does not exist. A temporary search-side failure.Re-run; update with pip install -U ddgs. In production, catch search errors and let the agent say the search failed.
NameError: name 'uuid' is not defined (in create_agent)Appeared once in our notebook; most likely a stale import after upgrading packages without restarting the kernel.Restart the Jupyter kernel after any pip install, then run cells top to bottom.
"Direct use of automatic function calling (AFC) … not recommended"An informational notice from Google's SDK.Safe to ignore for this workshop.
Developers

9. Under the hood: ReAct prompts vs native tool calling

Older LangChain tutorials, and an early cell of our notebook, build agents with a ReAct prompt like this one:

ReAct prompt template
Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

The model writes "Action: …" as plain text and the framework parses it. That works, but a small formatting slip breaks the parser.

create_agent in LangChain 1.x uses native tool calling instead. Gemini returns a structured tool_calls field with the tool name and JSON arguments, so there is nothing to parse and far fewer format errors. The idea is still ReAct: reason, act, observe, repeat. Only the mechanics have improved. Under the hood, create_agent runs on LangGraph, which is why we installed langgraph.

ReAct text promptNative tool calling (create_agent)
How the model asks for a toolWrites "Action: search" in textReturns a structured tool_calls object
Parsing errorsCommonRare
Works withAlmost any LLMModels that support tool calling (Gemini, GPT, Claude, …)
Best forLearning the conceptReal projects
Everyone

10. Limits, cost and safety

TopicWhat to know
AccuracyAgents can still be wrong or out of date (section 7). Keep a human check on anything important.
CostEach loop step is another model call. A question that needs three searches costs about four calls. Set a limit on steps in production.
SpeedEvery search and model call adds seconds. Agents are slower than a plain chatbot.
Rate limitsFree tiers limit requests per minute, and search providers can block heavy use.
SecurityNever put API keys in code. Give agents only the tools they need: read-only first, and actions like "send email" only with human approval.
Untrusted contentWeb pages can contain text that tries to give the model instructions (prompt injection). Treat search results as information, never as commands.
EveryoneDevelopers

11. Practice tasks and quick quiz

Try these next

  1. Easy: ask "What is the weather in Mumbai today?" and use step 8 to confirm a search happened.
  2. Easy: change the system prompt so the agent always answers in Hindi.
  3. Medium: add a @tool that returns today's date and time, and remove the date from the system prompt.
  4. Medium: wrap the search tool so it catches errors and returns "search unavailable" instead of crashing.
  5. Stretch: add memory with a LangGraph checkpointer and a thread_id, so follow-up questions like "and in euros?" work.
  6. Stretch: turn it into a small web app with Streamlit or Gradio.

Quick quiz

1. In this agent, who actually runs the web search: Gemini or LangChain?

LangChain. Gemini only asks for the search through a tool call; LangChain runs the tool and sends the results back.

2. How does the agent know when to stop looping?

When Gemini replies without any tool calls, that reply is treated as the final answer.

3. Our agent said "iPhone 16" in October 2026. What are two ways to catch or prevent this?

Inspect the tool calls to see whether it really searched (step 8), and give it today's date plus a rule to always search for current facts (step 9).

4. Why keep the API key in a .env file?

So the secret never appears in your code, notebooks, screenshots or Git history.

5. What decides whether Gemini picks a tool?

The tool's name and description, which LangChain sends to the model with every request, plus the question and system prompt.

6. What does a 503 "high demand" error mean, and what should you do?

Google's servers are temporarily overloaded. Retry with max_retries, wait, or switch to a lighter model.

Summary

An AI agent is an LLM with tools and a loop. You ask a question, LangChain hands it to Gemini with a list of tools, and Gemini decides whether to search. LangChain runs the search and returns the results, and Gemini repeats this until it can answer. With about 30 lines of Python you now have a working research assistant.

The harder skill is judging the output: check that tools were used, give the agent today's date, use real tools for numbers, and keep a human in the loop for anything that matters.