HackIndia Logo
HackathonsNewsSkillsGrowth
HackIndia LogoContact: +91 9910125804

General

  • Companies
  • Colleges
  • Board of Advisors
  • Brand Ambassadors
  • FAQ
  • Contact
  • Report a Bug

Hackathons

  • Upcoming Hackathons
  • Judges
  • Mentors
  • Partners
  • Participating Colleges
  • Testimonials

Resources

  • About Us
  • Branding
  • Latest News
  • Growth
  • Skills
  • Builders
  • Ecosystem
  • Forum
  • Ideas
  • Prompt Optimizer
  • Games

© 2026 HackIndia. All rights reserved.

Privacy PolicyTerms of ServiceCode of ConductSitemap

Build 2.139 · 05 Nov 2026, 10:27 UTC

    HackIndia
    HackathonsNewsInternshipsSkillsGrowthPro Perks
    ← Build a safe tool-calling agent

    All the reading

    AI Agents

    7 lessons, about 35 minutes. The check's questions come from these.

    Contents
    Tool purpose and schemas
    1. 1. Tools make models act
    2. 2. Define reliable tool schemas
    Call-result loop
    1. 3. Build the call-result loop
    Approval gates and limits
    1. 4. Add human approval gates
    2. 5. Stop runaway agents
    3. 6. Choose agent or script
    Repo notes and project ship
    1. 7. Ship the repo project

    Tool purpose and schemas

    1. Tools make models act

    5 min

    In 30 seconds
    • Explain what a tool-calling agent can do that a chat-only model cannot.
    • Describe the basic loop: model asks, app runs, model answers.
    • Create a tiny repo note that lists tools your agent may need.
    Key point

    The core idea

    A model does not directly send UPI, edit your database, or call an API. It emits a structured tool request, then your app or the provider executes that tool and sends the result back.

    Tools are how an agent acts. Without tools, the model can only answer from text it already has.

    What counts as a tool

    A tool can fetch data, run code, call an external API, use a computer, or call another agent. Current SDKs group tools in different ways, such as hosted tools, built-in execution tools, function tools, and agents-as-tools.

    For this track, start simple. A function tool is enough: you wrap a local function with a JSON schema so the model knows its name, description, inputs, and expected output.

    Example

    Tiny mental model

    Imagine a campus event helper. A student asks, “What events are open today?” The model should not guess. It should call a tool that reads your events data.

    The tool call is not the final answer. It is a request for your program to do work.

    User: What events are open today?
    
    Model returns a tool request:
    {
      "tool": "list_open_events",
      "arguments": {
        "date": "2026-09-21"
      }
    }
    
    Your app runs list_open_events(date).
    
    Tool result:
    {
      "events": ["AI project clinic", "Web dev sprint"]
    }
    
    Model returns final answer:
    Today you can join AI project clinic or Web dev sprint.
    Your to-do

    The basic loop

    2. Define reliable tool schemas

    5 min

    In 30 seconds
    • Define tool schemas the model can call without guessing.
    • Write names, descriptions, inputs, outputs, and risk notes clearly.
    • Add schemas to your repo so reviewers can inspect your agent design.

    Why schemas matter

    A tool schema is the contract between the model and your code. It tells the model what the tool does, what inputs are allowed, and what shape to expect back.

    Current agent systems use tools for actions like fetching data, running code, calling APIs, or using a computer. The model decides when to request a tool call, but your app or provider executes it.

    Key point

    One idea

    A reliable tool schema removes guessing. If the name, description, and input fields are vague, the model will call the wrong tool or pass bad arguments.

    Your to-do

    Schema checklist

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Call-result loop

    3. Build the call-result loop

    5 min

    In 30 seconds
    • Run a loop where the model asks for a tool, your app executes it, then sends back the result.
    • Keep tool execution outside the model, so you can log, test, and control every action.
    • Return a final answer only when the model stops asking for tool calls.
    Key point

    The core loop

    A tool-calling agent is not one model call. It is a loop: ask the model, inspect the response, run any requested tool, send the tool result back, then ask again.

    The model decides what tool to request from the tools you gave it. Your code executes the tool and controls what result goes back.

    Who does what

    The model emits a structured tool call, such as search_notes with arguments. Your app checks the name, parses the arguments, runs the matching function, and records the result.

    Do not let the model pretend it ran the tool. If there is a tool call, your code must produce the tool result. If there is no tool call, the model’s message can be the final answer.

    Your to-do

    Loop shape

    Approval gates and limits

    4. Add human approval gates

    5 min

    In 30 seconds
    • Put a human check before risky tool calls execute.
    • Approve, reject, or edit a tool request without breaking the loop.
    • Show approval rules clearly in your project repo.
    Key point

    One rule

    Do not let the model directly execute risky actions. Let it request the action, then pause and ask a human before the tool runs.

    Risky means money, messages, database writes, deletes, external posts, or anything hard to undo.

    What to gate

    Your agent can call safe read-only tools without approval. For example, searching your own notes or checking a timetable file is usually fine.

    Add approval when the tool changes the world. Think UPI payment, sending an email to a professor, creating a GitHub issue, deleting a record, or updating a team database.

    Your to-do

    Approval flow

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    Repo notes and project ship

    7. Ship the repo project

    5 min

    In 30 seconds
    • Package your agent so a reviewer can run it from a fresh clone.
    • Show proof of schemas, loop, approvals, limits, and agent-vs-script thinking.
    • Submit a repo that looks like an internship-ready project, not a class demo.
    Key point

    One review rule

    Your repo must make the agent easy to inspect, easy to run, and hard to misuse. A reviewer should not search through random files to find the tool schemas, approval gates, call-result loop, limits, or script note.

    What to ship

    You are not shipping a perfect AI product. You are shipping evidence that you can build a safe tool-calling agent.

    Current agent systems still follow the same core pattern: the model asks for a tool, the app or provider runs it, the result goes back, and the loop continues until a final answer. Your repo should make that pattern visible.

    Your to-do

    Repo checklist

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    10 questions, 7 to pass.

    Sign in to take the test

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Common mistake

    Common wrong idea

    Beginners often think the model “uses the API” by itself. Usually it does not. The model selects a tool and provides arguments. Your code is responsible for execution, errors, permissions, and logging.

    Older tutorials may show tools only as function calling. That is incomplete now. Function tools are still useful, but modern agent platforms also include hosted tools, built-in execution tools, MCP tools, and agents-as-tools.

    Tip

    Name actions clearly

    Tool names should sound like safe functions, not vague wishes. Use get_fee_status instead of handle_student. Use create_invoice_draft instead of do_payment.

    A clear name and description help the model choose the right tool. They also help your reviewer understand your public repo quickly.

    Example

    First repo note

    Before writing SDK code, add a small design note. This makes your project easier to review and keeps your agent scoped.

    Use this as a starting file in your repo.

    File: docs/agent-tools.md
    
    Project: Campus Event Helper
    
    User goal:
    Find open college events and draft a registration message.
    
    Tools the agent may call:
    1. list_open_events
       Purpose: Read events available on a given date.
       Inputs: date
       Output: event names, timings, venue
    
    2. get_event_details
       Purpose: Get rules and registration deadline for one event.
       Inputs: event_id
       Output: rules, fee, deadline
    
    3. draft_registration_message
       Purpose: Draft a message the student can review before sending.
       Inputs: student_name, event_id
       Output: message text
    
    Tools the agent must not call yet:
    - send_message
    - make_payment
    - update_database
    
    Why:
    This first version can read and draft. It cannot take high-risk actions.
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Tool schema file

    Put schemas in a visible file, not hidden inside a long agent script. This helps hackathon reviewers see that your agent has a real tool boundary.

    This example is for a campus event helper. The tool only searches events. It does not book seats or send messages.

    {
      "name": "search_campus_events",
      "description": "Search public campus events by city, date, and topic. Use this when the user asks for events to attend, not when they want to register.",
      "risk": "low",
      "input_schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "Indian city name, for example Bengaluru, Pune, Delhi, or Jaipur."
          },
          "date": {
            "type": "string",
            "description": "Date in YYYY-MM-DD format."
          },
          "topic": {
            "type": "string",
            "description": "Event topic, for example AI, web3, design, finance, or open source."
          },
          "max_results": {
            "type": "integer",
            "description": "Maximum number of events to return. Use 1 to 10."
          }
        },
        "required": ["city", "date", "topic", "max_results"]
      },
      "output_schema": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "description": "List of matching public events with title, date, venue, and source_url."
          },
          "error": {
            "type": "string",
            "description": "Empty string when successful, otherwise a short error message."
          }
        }
      }
    }
    Common mistake

    Avoid broad tools

    Do not create one tool called do_task, run_command, or manage_user. Broad tools make the model invent hidden behaviour.

    Split risky actions from safe reads. For example, search_campus_events is safe. register_for_event should be a separate tool, because it changes something and may need approval.

    Tip

    Use AI carefully

    You can ask ChatGPT, Gemini, or Claude to draft schemas from your app idea. Then check every field yourself.

    Check three things: can a developer implement this function, can the model know when to use it, and can bad inputs be rejected before execution.

    Prompt:
    I am building a tool-calling agent for a hackathon project. Draft JSON tool schemas for these tools: search campus events, summarize one event page, and save a shortlist. For each tool include name, description, risk, input_schema, output_schema, and one example call. Keep write actions separate from read actions.

    Keep outputs boring

    Tool outputs should be structured and predictable. Return JSON-like data your agent can read, not a paragraph full of styling.

    Include errors as data. A failed API call, missing event, or invalid date should return a clear error field so the next lesson’s call-result loop can handle it.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 6 done

    Example

    Minimal loop

    This is the shape you want in your repo. The callModel function stands for whichever model API or SDK you use. Keep the loop code separate from the tool functions.

    This example uses one fake campus tool so you can test the flow without paid APIs.

    const tools = {
      get_canteen_menu: async ({ day }) => {
        const menus = {
          monday: 'Idli, sambar, chai',
          tuesday: 'Poha, bread omelette, chai',
          wednesday: 'Paratha, curd, chai'
        };
    
        return menus[day.toLowerCase()] || 'Menu not available';
      }
    };
    
    async function runAgent(userMessage) {
      const messages = [{ role: 'user', content: userMessage }];
    
      while (true) {
        const response = await callModel({
          messages,
          tools: [
            {
              name: 'get_canteen_menu',
              description: 'Get the college canteen breakfast menu for a day',
              parameters: {
                type: 'object',
                properties: {
                  day: { type: 'string', description: 'Day name, like Monday' }
                },
                required: ['day']
              }
            }
          ]
        });
    
        messages.push(response.message);
    
        if (!response.toolCalls || response.toolCalls.length === 0) {
          return response.message.content;
        }
    
        for (const call of response.toolCalls) {
          const tool = tools[call.name];
          if (!tool) throw new Error(`Unknown tool: ${call.name}`);
    
          const result = await tool(call.arguments);
    
          messages.push({
            role: 'tool',
            toolCallId: call.id,
            name: call.name,
            content: JSON.stringify(result)
          });
        }
      }
    }
    Common mistake

    Common wrong version

    Beginners often call the model once, see a tool call, run the function, and print the raw result. That is not an agent loop.

    The model needs the tool result back so it can explain, combine, retry, or answer the user in normal language.

    Tip

    Log every turn

    Print the model message, tool call name, arguments, and tool result while developing. It makes bugs obvious.

    If the agent gives a strange final answer, your logs show whether the schema was unclear, the arguments were wrong, or the tool returned poor data.

    console.log('MODEL:', response.message);
    console.log('TOOL CALLS:', response.toolCalls);
    console.log('TOOL RESULT:', result);
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Simple approval wrapper

    This is a plain Python pattern you can use around the call-result loop you built earlier. The important part is that approval happens before the real tool function runs.

    For a hackathon demo, terminal approval is acceptable. In a deployed app, use a stored state so the run can resume after approval.

    APPROVAL_REQUIRED = {
        "send_email",
        "create_github_issue",
        "update_database",
        "make_payment"
    }
    
    TOOLS = {
        "search_notes": search_notes,
        "send_email": send_email,
        "create_github_issue": create_github_issue,
    }
    
    def needs_approval(tool_name):
        return tool_name in APPROVAL_REQUIRED
    
    def ask_for_approval(tool_name, args):
        print("Approval needed")
        print("Tool:", tool_name)
        print("Args:", args)
        choice = input("Type approve or reject: ").strip().lower()
        return choice == "approve"
    
    def execute_tool_call(tool_call):
        tool_name = tool_call["name"]
        args = tool_call["arguments"]
    
        if needs_approval(tool_name):
            approved = ask_for_approval(tool_name, args)
            if not approved:
                return {
                    "ok": False,
                    "error": "Human rejected this tool call",
                    "tool": tool_name
                }
    
        result = TOOLS[tool_name](**args)
        return {"ok": True, "result": result}
    Tip

    Show enough detail

    Approval is not useful if the human sees only “send_email”. Show the final recipient, subject, body preview, and attachments if any.

    For database writes, show the table or collection, record id, changed fields, and old versus new value when you can.

    Common mistake

    Do not fake safety

    A common mistake is asking for approval after the tool has already run. That is only a notification, not an approval gate.

    Another mistake is blocking forever with no saved state. Current human-in-the-loop systems pause runs for pending approvals, then resume after approve or reject. Production systems should preserve state across that pause.

    SDK idea

    Some agent SDKs include this pattern. OpenAI’s Python Agents SDK can pause runs for pending approvals; pending items appear in interruptions, and you approve or reject before resuming.

    LangChain also documents human-in-the-loop middleware for approval, editing, or rejection before tool execution. It notes that high-stakes actions like database writes and financial transactions are good cases for this.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    5. Stop runaway agents

    5 min

    In 30 seconds
    • Add hard limits so your agent cannot loop forever.
    • Stop by turns, tool calls, time, and repeated failures.
    • Return a clear final state when the agent hits a limit.
    Key point

    One safety rule

    Every agent loop needs a budget before it starts. If the budget is used up, stop and return a safe status instead of asking the model one more time.

    Do not copy older tutorials with unbounded while loops. Current agent frameworks expose max-turn, model-call, and tool-call limits because runaway loops are a normal failure mode.

    What runaway means

    A runaway agent keeps calling tools without getting closer to a final answer. It may repeat the same search, retry a broken API call, or ask the model again after every weak result.

    In a hackathon project, this can burn credits, spam APIs, hit rate limits, or make your demo hang in front of judges.

    Your to-do

    Set four budgets

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Guard the loop

    This is a plain JavaScript pattern you can use even if your SDK has its own max-turn option. The idea is the same: check the budget before each model call and before each tool call.

    const limits = {
      maxTurns: 8,
      maxToolCalls: 12,
      maxMs: 30000,
      maxFailures: 2
    };
    
    async function runAgent(userInput) {
      const startedAt = Date.now();
      const state = {
        turns: 0,
        toolCalls: 0,
        failures: 0,
        messages: [{ role: "user", content: userInput }]
      };
    
      while (true) {
        if (state.turns >= limits.maxTurns) {
          return { status: "stopped", reason: "max_turns", state };
        }
        if (state.toolCalls >= limits.maxToolCalls) {
          return { status: "stopped", reason: "max_tool_calls", state };
        }
        if (Date.now() - startedAt > limits.maxMs) {
          return { status: "stopped", reason: "timeout", state };
        }
        if (state.failures >= limits.maxFailures) {
          return { status: "stopped", reason: "too_many_failures", state };
        }
    
        state.turns += 1;
        const response = await callModel(state.messages);
    
        if (response.final) {
          return { status: "completed", answer: response.final, state };
        }
    
        for (const call of response.toolCalls) {
          if (state.toolCalls >= limits.maxToolCalls) {
            return { status: "stopped", reason: "max_tool_calls", state };
          }
    
          state.toolCalls += 1;
          try {
            const result = await executeTool(call);
            state.messages.push({ role: "tool", name: call.name, content: JSON.stringify(result) });
          } catch (err) {
            state.failures += 1;
            state.messages.push({ role: "tool", name: call.name, content: JSON.stringify({ error: err.message }) });
          }
        }
      }
    }
    Tip

    Use SDK limits too

    If your SDK has built-in turn limits, use them. OpenAI’s JS runner throws MaxTurnsExceededError when maxTurns is reached, unless maxTurns is null. OpenAI’s Python runner raises MaxTurnsExceeded when max_turns is exceeded, and max_turns=None disables the limit.

    Keep your own app-level limits as well. They make your repo easier to review because judges can see exactly how your agent stops.

    Common mistake

    Null is not safe

    A common mistake is leaving limits empty while testing and forgetting to restore them. A demo prompt like “keep trying until it works” can then run for a long time.

    Avoid unlimited settings unless you are running a controlled local experiment. For a public hackathon repo, choose small defaults and document them.

    Watch concurrency

    Parallel tool calls can be useful, but they also multiply damage. If five tool calls run at once and each retries, your simple agent becomes noisy fast.

    Use concurrency controls when available. The OpenAI JS SDK documents toolExecution.maxFunctionToolConcurrency, and the Python SDK separates provider-side parallel_tool_calls from SDK-side max_function_tool_concurrency.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    6. Choose agent or script

    5 min

    In 30 seconds
    • Decide when a task needs an agent and when a script is enough.
    • Write a short repo note that justifies your choice clearly.
    • Use agents only when model judgment is needed between steps.
    Key point

    The one test

    Use an agent when the model must choose the next step after seeing tool results. Use a script when the steps are known in advance.

    A script is not a downgrade. For many hackathon features, it is safer, cheaper, and easier to debug.

    When agents fit

    An agent fits when the path changes based on messy input or tool results. Example: a support assistant that reads a complaint, checks order status, decides whether to ask a follow-up question, then drafts a reply.

    The model is useful here because it makes judgments between steps. It decides whether to call a tool based on the user request and the tool description.

    When scripts win

    Use a script when the workflow is deterministic. That means the inputs, steps, and outputs are fully known before the run starts.

    Example: read a CSV of expenses, group by category, create a chart, and save a PDF. No model needs to decide the next action after each result.

    Your to-do

    Ask these questions

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Common mistake

    Common mistake

    Do not use an agent just because the workflow has many steps. Multi-step work can still be a plain script if the order is fixed.

    Older advice often says agents are for all multi-step tasks. That is too broad. Bounded workflows with loops, branches, parallel calls, and calculations can still be handled without an open-ended agent loop.

    Example

    Repo note example

    Add a short note like this to your public repo. Keep it honest. Judges and recruiters want to see that you know the trade-off.

    Agent vs script note
    
    This project uses a tool-calling agent for the complaint triage flow.
    
    Why an agent:
    - The user message can be vague or incomplete.
    - The next step depends on tool results.
    - The agent may need to ask a follow-up question before acting.
    - The agent chooses between order_lookup, refund_policy_lookup, and draft_reply.
    
    Where I would replace it with a script:
    - Nightly export of all closed complaints to CSV.
    - Fixed refund report generation.
    - Sending the same reminder email to users after 48 hours.
    
    Those jobs have known steps and do not need model judgment between tool calls.
    Tip

    Good repo signal

    Name the boring parts you would not make agentic. This makes your project look more production-minded.

    For example, payment retries, database migrations, scheduled reports, and fixed UPI reconciliation flows are usually scripts or jobs, not agents.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 6 done

    Example

    README skeleton

    Use this as your minimum README. Replace the placeholders with your project details. Keep it honest if some features are basic.

    Project: Campus Reimbursement Helper
    
    What it does
    Helps a student prepare a reimbursement request from bill details. It can read structured input, calculate totals, draft a message, and ask before sending anything.
    
    How to run
    1. Install dependencies.
    2. Add required environment variables.
    3. Run the agent with a sample request.
    
    Safety features
    - Tool schemas are in: src/tools/
    - Call-result loop is in: src/agent_loop.py
    - Approval gate is used before: sending the final email
    - Hard limits used: max turns and tool call budget
    - Risky actions are not run without human approval
    
    When I would replace this with a script
    I would use a script if the steps are fixed: read a CSV, calculate totals, fill a template, and save a PDF. I use an agent only when the request needs judgement, such as asking follow-up questions or choosing which tool to call.
    Common mistake

    Common submission mistake

    Do not submit only a chat UI. A chat screen does not prove that you built a tool-calling agent.

    Also avoid a repo where all safety logic is described in the README but not present in code. The README should point to real files.

    Tip

    Use AI carefully

    Ask an AI assistant to review your repo before submission. Give it the checklist and ask what is missing.

    Then verify the answer yourself. AI may claim that a limit or approval gate exists when it only appears in comments.

    Prompt to use:
    Review this repo for a hackathon submission. Check whether it clearly shows:
    1. tool schemas
    2. a call-result loop
    3. a human approval gate
    4. hard limits
    5. a note on when a script is better
    Return missing items with file names. Do not guess.
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done