সরাসরি পর্বে যান
পর্ব ৭১ · ৯১-এর মধ্যে · মডিউল ১১

Tool use আর function calling — LLM থেকে কাজ

Tool Use and Function Calling: From LLM Text to Actions

Taught in Bangla, this lesson shows that in tool calling the model only proposes a tool and arguments while your code validates, runs and returns the result. It traces how tool definitions enter the chat template and cost tokens, compares six APIs' documentation and introduces MCP, builds a registry and dispatcher with schema and business-rule checks, error feedback and bounded retries, and measures tool selection and argument accuracy on 40 hand-written requests with small Qwen2.5 models in a sandboxed run.

এই পর্বে যা শিখবেন

  • Tool calling-এর পুরো round trip: প্রস্তাব, যাচাই, চালানো, ফল ফেরত, শেষ উত্তর
  • Tool-এর সংজ্ঞা কীভাবে chat template-এ prompt হয়ে যায়, আর তার token খরচ
  • ছয়টি API-র নথিতে tool calling-এর আকার, আর MCP কী
  • Tool registry আর dispatcher, argument যাচাই, error ফেরত আর retry
  • Parallel call, কখন tool ডাকবেন না, আর tool-selection ও argument-এর নির্ভুলতা মাপা

এই পর্বের desktop run: Python 3.12, torch 2.15.0, transformers 5.22.2, jsonschema 4.26.0, লেখার সময়ে। Model Qwen/Qwen2.5-0.5B-Instruct (CPU-তে) আর Qwen/Qwen2.5-1.5B-Instruct (GPU-তে), দুটিই Apache 2.0, token ছাড়া নামানো যায়। চারটি tool, ৫টি অর্ডার আর ৪০টি প্রশ্ন এই পর্বের জন্য হাতে লেখা কাল্পনিক বাজারঘর ডেটা; নীতির নথিগুলো এই মডিউলের যৌথ কাল্পনিক সাহায্য-কেন্দ্র (৪০টি নথি)। সব tool sandbox-এ: কোনো network নেই, “বার্তা পাঠানো” শুধু একটি স্থানীয় log ফাইলে লেখা। সব সংখ্যা একটি স্থানীয় run-এর, ছোট model-এর; বড় hosted পণ্য অন্যভাবে আচরণ করতে পারে, আর এই পাতার কোনো সংখ্যা দিয়ে কোনো পণ্যের মান বিচার করা যায় না। LLM-এর মূল ধারণা AI-এর ভিত্তি কোর্সের পর্ব ৭০-এ, JSON Schema আর যাচাই পর্ব ৬২-এ।

১ · Tool use কী — প্রস্তাব model-এর, কাজ আপনার কোডের

ডাক্তারের ব্যবস্থাপত্রের কথা ভাবুন। ডাক্তার নিজে ওষুধ বানান না, ল্যাবে রক্তও পরীক্ষা করেন না। তিনি লেখেন: “CBC পরীক্ষা, এই নমুনায়।” ল্যাব পরীক্ষাটি চালায়, রিপোর্ট আবার ডাক্তারের কাছে আসে, তিনি রিপোর্ট পড়ে সিদ্ধান্ত দেন। Tool calling-এ LLM হলো ডাক্তার: সে লেখে কোন tool, কোন argument দিয়ে। আপনার প্রোগ্রাম ল্যাব: আগে দেখে নেয় ফর্মটি ঠিকমতো ভরা কি না, তারপর চালায়, ফল ফেরত দেয়।

LLM নিজে শুধু লেখা তৈরি করে (AI-এর ভিত্তি কোর্সের পর্ব ৭০)। বাজারঘরের গ্রাহক যখন জিজ্ঞেস করেন “আমার অর্ডার BG-10231 কোথায়?”, উত্তরটি model-এর ওজনে নেই, আছে বাজারঘরের database-এ। প্রশ্নটি model-কে সরাসরি দিলে সে বানিয়ে লিখতে পারে, যাকে পর্ব ৫২-এ hallucination বলেছি। Tool calling (function calling নামেও পরিচিত) এই ফাঁক পূরণ করে: model-কে কয়েকটি tool-এর তালিকা দেওয়া হয়, আর model দরকার মনে করলে উত্তরের বদলে একটি structured অনুরোধ লেখে।

পর্ব ৬২-এ দেখেছেন, model-কে JSON Schema মেনে উত্তর দেওয়ানো যায়। Tool calling সেই কৌশলেরই আরেক ব্যবহার, কিন্তু একটি বড় পার্থক্য আছে: এখানে JSON একটি কাজ চালায়। অর্ডার দেখা নিরীহ; গ্রাহককে বার্তা পাঠানো, টাকা ফেরত দেওয়া বা অর্ডার বাতিল করা নিরীহ নয়। তাই এই পর্বের মূল প্রশ্ন: model-এর প্রস্তাব থেকে নিরাপদে কাজ পর্যন্ত কীভাবে যাবেন।

Tool calling round trip with validation, error feedback and approval প্রশ্ন + tool-এর তালিকা(নাম, বর্ণনা, schema) LLM: উত্তর নাকি tool?model ঠিক করে tool call-এর প্রস্তাবname + arguments (JSON) আপনার কোড: যাচাইনাম, schema, নিয়ম ঝুঁকি? মানুষের অনুমতি(বার্তা, টাকা, বাতিল) sandbox-এ tool চলেফল JSON হিসেবে ফল যায় LLM-এ→ শেষ উত্তর ভুল? error বার্তা ফেরত, সীমিত Model proposes; your code decides and executes
বাঁ দিকের লম্বা তীর: tool-এর ফল আবার model-এ যায়, model চাইলে আরেকটি tool ডাকে, অথবা শেষ উত্তর লেখে। ডান দিকের পথ: যাচাইয়ে ভুল হলে error-এর বার্তা model-এ ফেরত, সীমিত বার।

চিত্রে লক্ষ করুন, “tool চলে” ঘরটি model-এর বাইরে। Model কখনো database ছোঁয় না; সে শুধু লেখে। এই সীমারেখাটিই নিরাপত্তার ভিত্তি: model যা-ই লিখুক, আপনার কোড ঠিক করে কোনটি চলবে। একই tool-ব্যবহার বহু ধাপে, পরিকল্পনাসহ চালালে তাকে agent বলে; সেটি পর্ব ৭২ · Agent — পরিকল্পনা, memory আর বহু ধাপের কাজ-এ। এই পর্ব এক বা দুই ধাপের tool ব্যবহারে।

২ · Tool-এর সংজ্ঞা আর chat template — model আসলে কী দেখে

এই পর্বের বাজারঘর সহকারীর চারটি tool, সবই sandbox-এ, মানে শুধু স্থানীয় ফাইল নিয়ে কাজ করে; কোনো network বা আসল বার্তা নেই:

ToolArgument (schema)কী করে
lookup_orderorder_id: string, pattern ^BG-[0-9]{5}$স্থানীয় JSON থেকে অর্ডারের অবস্থা
delivery_chargedistrict: ১০টি জেলার enum; weight_kg: number, ০-এর বেশি, ≤ ২০সাহায্য-কেন্দ্রের নিয়মে চার্জ: ঢাকা সিটি ৬০, গাজীপুর ৯০, বাকি জেলায় ১২০ টাকা ১ কেজি পর্যন্ত, প্রতি বাড়তি কেজিতে ২০
search_policyquery: string, কমপক্ষে ২ অক্ষরমডিউলের কাল্পনিক সাহায্য-কেন্দ্রের ৩৯টি চালু নথিতে শব্দ মিলিয়ে খোঁজা
send_messageorder_id; text: ৫–৩০০ অক্ষরনকল: শুধু একটি স্থানীয় log ফাইলে এক লাইন যোগ

প্রতিটি tool-এর তিনটি অংশ: নাম, বর্ণনা (কখন ব্যবহার করবেন, কখন নয়) আর argument-এর JSON Schema. Schema পড়ার নিয়ম পর্ব ৬২-এ; এখানে নতুন হলো বর্ণনা, কারণ model tool বাছাই করে মূলত নাম আর বর্ণনা পড়ে। Tool-এর তালিকা model-এর কাছে কোনো বিশেষ পথে যায় না। Hugging Face-এর apply_chat_template(messages, tools=tools) তালিকাটিকে model-এর শেখা ছাঁচে লেখা বানিয়ে system বার্তার ভেতরে বসায়। Qwen2.5-এর template-এ যা হয় (সত্যিকারের run, একটি tool রেখে বাকিগুলো “…” দিয়ে ছোট করা):

<|im_start|>system
আপনি বাজারঘর নামের অনলাইন দোকানের সহায়তা-সহকারী। … জিজ্ঞেস করুন।

# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
{"type": "function", "function": {"name": "lookup_order", "description": "Look up
 the current status, district, ETA and total of ONE bazarghar order. Use only
 when the user gives an order id like BG-10231. If no id is given, ask for it
 instead of calling.", "parameters": {"type": "object", "properties":
 {"order_id": {"type": "string", "pattern": "^BG-[0-9]{5}$", …}}, …}}}
…
</tools>

For each function call, return a json object with function name and arguments
within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call><|im_end|>
<|im_start|>user
আমার অর্ডার BG-10231 এখন কোথায়?<|im_end|>
<|im_start|>assistant

তিনটি জিনিস লক্ষ করুন। এক, model “tool” বলে আলাদা কিছু জানে না; সে একটি লম্বা লেখা পড়ে, যার নির্দেশ ইংরেজিতে template-এর লেখকের লেখা। Model-এর কাজ পরের token অনুমান, আর <tool_call> লেখার অভ্যাস তাকে পর্ব ৫৬-এর মতো fine-tuning-এ শেখানো হয়েছে বলে ধরে নেওয়া হয়। দুই, tool-এর ফল ফেরত যায় আরেকটি বার্তা হিসেবে; Qwen2.5-এর template সেটিকে user-এর পালায় <tool_response> ট্যাগে মুড়ে দেয় (একই run-এ দেখা)। তিন, খরচ: Qwen2.5-এর tokenizer-এ এই প্রশ্নের prompt tool ছাড়া ২১৬ token, চারটি tool শুধু schema দিয়ে ৭০৩, আর পুরো বর্ণনাসহ ১,০৪০. মানে প্রতিটি অনুরোধে প্রায় ৮২০ token শুধু tool-এর তালিকা, উত্তর লেখার আগেই। Hosted API-তে এর দাম আর prompt caching-এ সাশ্রয় পর্ব ৬৫-এ।

Model-এর পছন্দের ধরন template-এ লেখা: <tool_call> ট্যাগের ভেতরে {"name": …, "arguments": {…}}. আপনার কোড এই ট্যাগ খুঁজে JSON তোলে। Hosted API-তে এই কাজ server করে আর আলাদা ঘরে দেয় (§৪), কিন্তু ভেতরের ধারণা একই: model একটি বিশেষ ধরনের লেখা লেখে, আর কেউ তা parse করে।

৩ · একটি পূর্ণ round trip — প্রশ্ন থেকে উত্তর পর্যন্ত

নিচের stepper-এর প্রতিটি লেখা আর সংখ্যা সত্যিকারের run থেকে: Qwen2.5-0.5B-Instruct, CPU-তে, greedy decoding, পুরো বর্ণনাসহ tool. প্রশ্ন “আমার অর্ডার BG-10231 এখন কোথায়?” আর কোড একটি ছোট loop: model-এর উত্তরে <tool_call> থাকলে যাচাই করে চালায়, ফল যোগ করে আবার ডাকে; না থাকলে সেটিই শেষ উত্তর।

বার্তা: system + user “আমার অর্ডার BG-10231 এখন কোথায়?”
ধাপ
কে কাজ করছে
কী গেল বা এল
token, সময়
রায়
  1. প্রথম ডাক. কোড system বার্তা, চারটি tool আর প্রশ্ন chat template-এ সাজিয়ে পাঠায়: ১,০৪০ token. Model এখনো কিছু জানে না, শুধু তালিকা আর প্রশ্ন দেখছে।
  2. প্রস্তাব. Model উত্তর না লিখে ২৭ token-এ একটি <tool_call> লিখেছে: lookup_order, argument "BG-10231". CPU-তে ৫.৮০ সেকেন্ড। এখনো কোনো কাজ হয়নি, শুধু লেখা।
  3. যাচাই. কোড ট্যাগের ভেতরের JSON parse করে, নামটি registry-তে খোঁজে, argument schema-য় মেলায় (pattern ^BG-[0-9]{5}$), তারপর ব্যবসার নিয়ম: অর্ডারটি আছে কি না। তিনটিই পার।
  4. চালানো. Sandbox-এর function স্থানীয় JSON থেকে অর্ডারটি তোলে: পথে আছে, রাজশাহী, ২ দিন, ১,৫৫০ টাকা, ক্যাশ অন ডেলিভারি। ফলটি একটি tool বার্তা হয়ে তালিকায় যোগ হয়।
  5. দ্বিতীয় ডাক. এবার prompt ১,১৪৯ token: আগের সব, model-এর ডাক আর <tool_response>-এ ফল। Model ৪১ token-এ শেষ উত্তর লিখল, কোনো নতুন tool-ডাক ছাড়া, তাই loop থামল।
  6. উত্তর মেলানো. উত্তর: “Your order BG-10231 is currently pending at the Rajshahi District Office. The total cost is Taka 1550. You can expect delivery in two days.” ২ দিন আর ১,৫৫০ ঠিক। কিন্তু “পথে আছে” হয়ে গেছে “District Office-এ pending”, যা ফলে নেই; আর system বার্তা বাংলা চাইলেও উত্তর ইংরেজিতে। Tool সঠিক তথ্য দিলেও শেষ লেখায় ভুল ঢুকতে পারে।

শেষ ধাপটি এই পর্বের একটি মূল শিক্ষা। Tool calling hallucination কমায়, কারণ তথ্য আসে database থেকে; কিন্তু শেষ উত্তর তখনো model লেখে, আর সে ফলটি বদলে বা বাড়িয়ে লিখতে পারে। তাই যে তথ্য গ্রাহকের কাছে গুরুত্বপূর্ণ (অবস্থা, টাকা, তারিখ), সেগুলো অনেক অ্যাপ model-এর লেখায় না রেখে tool-এর ফল থেকে সরাসরি একটি ছাঁচে বসায়, আর model শুধু ভদ্র বাক্যটুকু লেখে। উত্তরের সঙ্গে উৎসের মিল মাপার পদ্ধতি পর্ব ৭০ · RAG মূল্যায়ন-এ, faithfulness নামে।

৪ · ছয়টি API-র নথিতে tool calling, আর MCP

Hosted API-তে একই চক্র চলে, শুধু ঘরের নাম আলাদা। নিচের সারণি প্রতিটি কোম্পানির নিজের API নথি থেকে, লেখার সময়ে (নভেম্বর ২০২৬) পড়া; নাম আর নিয়ম পরিবর্তন হতে পারে, ব্যবহারের আগে নথিটি নিজে মিলিয়ে নিন। যে বিষয় পড়া পাতায় খুঁজে পাইনি, সেখানে “নথিতে পাইনি” লেখা। সারণিটি কে “ভালো” তা বলে না।

API (কোম্পানি)Tool-এর সংজ্ঞাModel-এর ডাকফল ফেরতনিয়ন্ত্রণMCP
OpenAI (Responses API)type: "function", name, description, parameters, strictfunction_call item: call_id, name, arguments (JSON লেখা, নিজে parse)function_call_output, একই call_id, outputtool_choice: auto, required, none, নির্দিষ্ট function, allowed_tools; parallel_tool_calls; শুরুতে ২০-এর কম function রাখার নরম পরামর্শtype: "mcp", server_label, server_url, require_approval
Anthropic (Claude Messages API)name, description, input_schema, ঐচ্ছিক strictstop_reason: "tool_use"; tool_use block: id, name, input (object)user বার্তায় tool_result: tool_use_id, content, ভুলে is_error: true; result block আগে, লেখা পরেtool_choice: auto, any, tool, none; disable_parallel_tool_useMCP connector: Messages API থেকে remote server
Google (Gemini, Interactions API)type: "function", name, description, parametersfunction_call step: id, name, argumentsfunction_result step: call_id, name, resulttool_choice: auto, any, none, validated; allowed_tools; সক্রিয় tool “১০–২০টি” রাখার পরামর্শtype: "mcp_server", name, url; শুধু Streamable HTTP
xAI (Grok API)type: "function", name, description, parameters (মূলে object)function_call: call_id, name, arguments (JSON লেখা)function_call_output, call_id, outputauto, required, none, নির্দিষ্ট function; parallel নিজে থেকে চালু, parallel_tool_calls: false দিয়ে বন্ধtype: "mcp", server_url, server_label, allowed_tools
DeepSeektype: "function" + function: name, description, parameters; beta URL-এ strict: truemessage.tool_calls, প্রতিটির idrole: "tool", tool_call_id, contentপড়া পাতায় tool_choice বা parallel-এর বিবরণ পাইনিনথিতে পাইনি
Moonshot AI (Kimi API)একই আকার: type: "function", function-এর ভেতরে name, description, parametersfinish_reason: "tool_calls"; tool_calls: id, function.name, function.arguments (JSON লেখা); একসঙ্গে কয়েকটি আসতে পারেrole: "tool", tool_call_id, name, contentউদাহরণে শুধু "auto"; বিস্তারিত পাইনিনথিতে পাইনি

সারণির চারটি শিক্ষা সরাসরি কোডে লাগে। এক, অনেক API-তে arguments একটি লেখা, object নয়; json.loads ব্যর্থ হতে পারে, তাই parse-এর ভুলও ধরতে হয়। দুই, প্রতিটি ডাকের একটি id, আর ফল ফেরত দিতে হয় সেই id মিলিয়ে; Kimi-র নথি সতর্ক করে, না মিললে error. তিন, parallel ডাক অনেক API-তে নিজে থেকে চালু; আপনার কোড একটির বদলে তালিকা ধরে চলবে। চার, Anthropic-এর নথি tool-এর ফলকে অবিশ্বস্ত লেখা হিসেবে দেখতে বলে, কারণ ওয়েবপাতা বা ইমেইলের ভেতরে নির্দেশ লুকিয়ে থাকতে পারে (indirect prompt injection, পর্ব ৬৪)। নিচে অনুরোধের আকার, নথির কাঠামো অনুসরণ করে লেখা; চালানো হয়নি, কারণ এখানে কোনো API key নেই।

# নমুনা (চালানো হয়নি): দুই ধরনের প্রথম পালা, model-এর নাম নিজে বসান
# Claude Messages API: model-এর উত্তরে tool_use block আসে
{"model": "…", "max_tokens": 512, "tools": [{"name": "lookup_order",
  "description": "…", "input_schema": {"type": "object",
  "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}}],
 "messages": [{"role": "user", "content": "BG-10231 কোথায়?"}]}
# পরের অনুরোধে ফল ফেরত (user বার্তা)
{"role": "user", "content": [{"type": "tool_result",
  "tool_use_id": "toolu_…", "content": "{\"status\": \"পথে আছে\"}"}]}

# Chat Completions ধাঁচ (DeepSeek, Kimi): ফল একটি tool বার্তা
{"role": "tool", "tool_call_id": "call_…", "content": "{\"status\": \"পথে আছে\"}"}

MCP — tool যুক্ত করার একটি খোলা protocol

ওপরের সারণিতে প্রতিটি API-র নিজের আকার। ধরুন বাজারঘর তার অর্ডার-সিস্টেম পাঁচটি আলাদা AI অ্যাপে যুক্ত করতে চায়; প্রতিটির জন্য আলাদা সংযোগ লিখতে হবে। MCP (Model Context Protocol) এই সমস্যার একটি উত্তর। এর অফিসিয়াল সাইট (modelcontextprotocol.io, লেখার সময়ে পড়া) নিজেকে AI অ্যাপকে বাইরের সিস্টেমে যুক্ত করার একটি open-source মান বলে, আর তুলনা দেয় USB-C পোর্টের সঙ্গে। Anthropic ২০২৪ সালের নভেম্বরে এটি প্রকাশ করে; এখন সাইটের তালিকায় Claude, ChatGPT, VS Code, Cursor সহ বহু client-এর সমর্থনের কথা আছে, আর সারণি দেখায় চারটি API-তে remote MCP server যুক্ত করার ঘর আছে।

সাইটের architecture পাতা অনুযায়ী মূল ধারণা: host হলো AI অ্যাপ, যে প্রতিটি server-এর জন্য একটি client বানায়। Server তিন ধরনের জিনিস দেয়: tools (চালানো যায় এমন function), resources (পড়ার তথ্য) আর prompts (ছাঁচ)। বার্তা JSON-RPC 2.0-এ; client tools/list দিয়ে tool-এর তালিকা নেয় (প্রতিটিতে name, description, inputSchema), আর tools/call দিয়ে চালায়। পরিবহন দুই রকম: একই যন্ত্রে stdio, দূরে Streamable HTTP. লেখার সময়ের সংস্করণ 2026-07-28. খেয়াল করুন, MCP ঠিক করে কীভাবে tool পাওয়া আর ডাকা যায়; model কোন tool ডাকবে, argument ঠিক কি না, কাজটি নিরাপদ কি না, সেগুলো তখনো host-এর দায়িত্ব। OpenAI-এর MCP নির্দেশিকাও ডেটা বদলানোর মতো tool-এ অনুমতি চালু রাখতে বলে।

৫ · Registry, dispatcher আর argument যাচাই

Model-এর লেখা tool-ডাককে ওয়েবসাইটের form থেকে আসা ডেটার মতো দেখুন: বাইরের ইনপুট, আগে পরীক্ষা (ফুল-স্ট্যাক ওয়েব ফ্রেমওয়ার্ক কোর্সের পর্ব ২৭)। দুটি অংশ লাগে। Registry: অনুমোদিত নামের তালিকা, প্রতিটির function আর argument-এর নিয়ম। Dispatcher: যে কোড ডাকটি parse করে, registry-তে খোঁজে, নিয়ম মেলায়, তারপর চালায় বা স্পষ্ট error ফেরত দেয়। Model যে নামই লিখুক, registry-র বাইরে কিছু চলে না।

নিচের কোডে dispatcher নিজে লেখা, যাতে প্রতিটি নিয়ম চোখে পড়ে; আসল কাজে schema-র জন্য jsonschema বা Pydantic নিন (পর্ব ৬২)। ইনপুট হলো Qwen2.5-0.5B-Instruct-এর সত্যিকারের সাতটি output, §৮-এর run থেকে বেছে তোলা, হুবহু; পাতার ভেতরে ফাইল হিসেবে রাখা।

import json, re, math
ORDERS = {"BG-10231": "পথে আছে", "BG-10312": "প্যাক হচ্ছে", "BG-10588": "বাতিল"}  # sandbox-এর নকল ডেটা
DIST = ["ঢাকা", "চট্টগ্রাম", "রাজশাহী", "খুলনা", "সিলেট", "বরিশাল", "রংপুর", "ময়মনসিংহ", "কুমিল্লা", "গাজীপুর"]
def charge(district, weight_kg):                      # সাহায্য-কেন্দ্রের নিয়ম
    return {"ঢাকা": 60, "গাজীপুর": 90}.get(district, 120 + 20 * max(0, math.ceil(weight_kg - 1)))
REG = {  # registry: নাম → (function, argument-এর নিয়ম)
    "lookup_order": (lambda order_id: ORDERS[order_id], {"order_id": ("pat", r"BG-[0-9]{5}")}),
    "delivery_charge": (charge, {"district": ("enum", DIST), "weight_kg": ("num", (0, 20))})}

def dispatch(raw):
    m = re.search(r"<tool_call>\s*(.*?)\s*</tool_call>", raw, re.S)
    if not m: return "tool-ডাক নেই → সরাসরি উত্তর"
    try:
        call = json.loads(m.group(1))
    except json.JSONDecodeError as e:
        return f"✘ JSON নয়: {e.msg}"
    name, args = call.get("name"), call.get("arguments")
    if name not in REG: return f"✘ অজানা tool '{name}'"
    fn, rules = REG[name]
    if not isinstance(args, dict): return "✘ arguments object নয়"
    bad = [f"'{k}' নেই" for k in rules if k not in args] + [f"বাড়তি '{k}'" for k in args if k not in rules]
    for k, (kind, r) in rules.items():
        v = args.get(k)
        if k not in args: continue
        if kind == "pat" and not (isinstance(v, str) and re.fullmatch(r, v)): bad.append(f"{k} ধাঁচে মেলে না")
        if kind == "enum" and v not in r: bad.append(f"{k}={v!r} তালিকায় নেই")
        if kind == "num" and (type(v) not in (int, float) or not r[0] < v <= r[1]): bad.append(f"{k} সীমার বাইরে")
    if bad: return "✘ " + "; ".join(bad)
    if name == "lookup_order" and args["order_id"] not in ORDERS:   # ব্যবসার নিয়ম
        return f"✘ অর্ডার {args['order_id']} পাওয়া যায়নি"
    return f"✔ চলল: {name}({', '.join(map(repr, args.values()))}) → {fn(**args)}"

for o in json.load(open("outs.json", encoding="utf-8")):
    print(f"[{o['id']}] {o['q']}\n     {dispatch(o['out'])}")

আউটপুট (পাতার Python-এ চালানো):

[o1] আমার অর্ডার BG-10231 এখন কোথায়?
     ✔ চলল: lookup_order('BG-10231') → পথে আছে
[c2] Sylhet e 3.5 kg parcel er delivery charge koto?
     ✘ district='Sylhet' তালিকায় নেই
[c3] ঢাকার ভেতরে আধা কেজির প্যাকেটে ডেলিভারি চার্জ কত?
     ✔ চলল: delivery_charge('ঢাকা', 4) → 60
[p1] পণ্য ফেরত দেওয়ার নিয়ম কী?
     ✘ 'weight_kg' নেই
[m4] customer of BG-10588 ke janan je order cancel hoyeche
     ✘ অজানা tool 'cancel_order'
[n5] ডেলিভারি চার্জ কত?
     ✘ অর্ডার BG-47865 পাওয়া যায়নি
[n2] হ্যালো
     tool-ডাক নেই → সরাসরি উত্তর

সাতটি সত্যিকারের ডাক, ছয় রকম পরিণতি। [o1] সব পরীক্ষা পার, চলল। [c2] “Sylhet” enum-এ নেই; তালিকায় আছে “সিলেট”. [p1] ফেরতের নিয়ম জিজ্ঞেস করায় model ডেলিভারি-চার্জের tool বেছেছে, ওজন ছাড়াই; ভুল tool ধরা পড়ল শুধু argument কম বলে। [m4] model এমন tool বানিয়েছে, যা তালিকায় নেই: cancel_order. Registry না থাকলে এই নামে কোনো function থাকলে তা চলে যেত। [n5] অর্ডার নম্বর ছাড়া প্রশ্নে model একটি নম্বর বানিয়েছে, “BG-47865”; ধাঁচ ঠিক, তাই schema পার, ধরা পড়ল ব্যবসার নিয়মে। [n2] কোনো ডাক নেই, সরাসরি উত্তর।

সবচেয়ে বিপজ্জনক [c3]: “আধা কেজি” থেকে weight_kg: 4, তবু সব পরীক্ষা পার। ঢাকায় চার্জ ওজন যা-ই হোক ৬০, তাই উত্তরটি কাকতালীয়ভাবে ঠিক; সিলেটে হলে ১৮০ টাকা বলত। বৈধ argument মানেই সঠিক argument নয়, যা ধরা যায় শুধু মাপে (§৮)। আর লক্ষ করুন type(v) not in (int, float): Python-এ True-ও int, তাই isinstance দিলে true ওজন হিসেবে পার পেত।

এবার [c2]-এর যাচাই ধাপে ধাপে, আর error-এর পরে model কী করল।

ডাক: delivery_charge {"district": "Sylhet", "weight_kg": 3.5}
ধাপ
পরীক্ষা
মান
রায়
  1. ট্যাগ. Model-এর লেখায় একটি <tool_call>…</tool_call> আছে। না থাকলে লেখাটিই উত্তর; একাধিক থাকলে প্রতিটি আলাদা ডাক।
  2. JSON parse. ট্যাগের ভেতরের লেখা বৈধ JSON, একটি object: name আর arguments. Hosted API-তেও arguments প্রায়ই লেখা হিসেবে আসে, তাই এই ধাপ সব জায়গায় লাগে।
  3. নাম. delivery_charge registry-তে আছে। এই ধাপে বানানো নাম (cancel_order, write_short_story) আটকায়, যা আমাদের run-এ ০.৫B সত্যিই লিখেছে।
  4. চাবি. Schema-র দুটি আবশ্যক চাবিই আছে, বাড়তি কিছু নেই। বাড়তি চাবি বাতিল করাই নিরাপদ; চুপচাপ ফেলে দিলে model-এর ভুল ঢাকা পড়ে।
  5. মান. weight_kg ৩.৫, সীমার ভেতরে। কিন্তু district “Sylhet”: অর্থ ঠিক, বানান তালিকার নয়। Schema কঠোর; এখানে একটি alias-তালিকা (Sylhet → সিলেট, পর্ব ৬২-এর normalisation) model না ডেকেই ঠিক করত।
  6. ফেরত. কোড ভুলের বার্তা tool বার্তা হিসেবে ফেরত দিল, “ঠিক করুন বা ব্যবহারকারীকে জিজ্ঞেস করুন”-সহ। Model-এর পরের লেখা (সত্যিকারের): “I'm sorry, but I can't proceed without the correct district name. Could you please provide me with the correct district name…” সে নিজে “সিলেট” লেখেনি, ইংরেজিতে জিজ্ঞেস করেছে।

৬ · Error ফেরত, retry আর parallel ডাক

যাচাই ব্যর্থ হলে তিনটি পথ, পর্ব ৬২-এর repair-retry-র মতোই: নিজে মেরামত (alias, বাংলা অঙ্ক), ভুলের বার্তা model-কে ফেরত দিয়ে আবার সুযোগ, আর সীমা পার হলে মানুষ বা “জানি না”. নথিগুলোও দ্বিতীয় পথ বলে: Anthropic-এর নথি ভুলের ফলে is_error: true দিতে আর বার্তায় কী ভুল ও এরপর কী করতে হবে তা লিখতে বলে; সে নথি অনুযায়ী Claude দরকারি argument না পেলে ২–৩ বার ঠিক করে আবার চেষ্টা করে। ছোট open model-এ কী হয়, আমরা মেপেছি।

একই ০.৫B model, পুরো বর্ণনাসহ tool, ৪০টি প্রশ্ন, loop-এ সর্বোচ্চ ৪টি model ডাক (সত্যিকারের run): মোট ৭৮টি ডাক, কোনো প্রশ্ন সীমায় পৌঁছায়নি। ৮টি প্রশ্নে প্রথম ডাকই অবৈধ ছিল, আর ভুলের বার্তা পাওয়ার পর ৮টির একটিতেও model সংশোধিত বৈধ ডাক লেখেনি। ৩টিতে ইংরেজিতে প্রশ্ন ফিরিয়ে দিয়েছে (“correct district name”, “correct tool name”), বাকিগুলোতে অসংলগ্ন বাংলা; দুটিতে লেখা ২৫৬ token-এর সীমা ছুঁয়েছে, প্রতিটি প্রায় ২১ সেকেন্ড। একটিতে (লালাচার নামের অচেনা জেলা) model ভারতের রাজ্যের নামে বানানো চার্জের লম্বা তালিকা লিখেছে; পাতায় তা দেখানো হলো না। শিক্ষা: এই আকারের model-এ ভুলের বার্তা ফেরত দেওয়া প্রায় শুধু খরচ, তাই retry-এর সীমা ছোট রাখুন আর কোডের মেরামত আগে চালান। বড় model-এর আচরণ আলাদা হতে পারে; §৮-এ ১.৫B-এর ফল।

Parallel ডাক-ও এই run-এ এসেছে: “BG-10231 আর BG-10457 দুটো অর্ডারের অবস্থা জানান” প্রশ্নে model এক পালায় দুটি <tool_call> লিখেছে। নিচের কোড loop-টি সত্যিকারের output দিয়ে আবার চালায়, যাতে বার্তার তালিকা কীভাবে বাড়ে আর ডাকের id কীভাবে মেলে, তা দেখা যায়।

import json, re
ORDERS = {"BG-10231": {"status": "পথে আছে"}, "BG-10457": {"status": "ডেলিভারি হয়েছে"}}
DIST = ["ঢাকা", "চট্টগ্রাম", "রাজশাহী", "খুলনা", "সিলেট"]          # তালিকা ছোট করা
def run_tool(name, args):                                        # sandbox
    if name == "lookup_order":
        return ORDERS.get(args.get("order_id"), {"error": "order not found"})
    if name == "delivery_charge" and args.get("district") not in DIST:
        return {"error": f"district {args.get('district')!r} not allowed. Fix it or ask the user."}
    return {"error": f"unknown tool {name!r}"}

def loop(question, model_turns, max_calls=4):
    msgs = [{"role": "user", "content": question}]
    for n, out in enumerate(model_turns[:max_calls], 1):          # model-এর সত্যিকারের লেখা
        msgs.append({"role": "assistant", "content": out})
        calls = [json.loads(b) for b in re.findall(r"<tool_call>\s*(.*?)\s*</tool_call>", out, re.S)]
        if not calls:
            return msgs, n                                        # ডাক নেই: শেষ উত্তর
        for k, c in enumerate(calls):
            cid = f"call_{n}_{k}"        # Qwen id দেয় না; কোড বানায়, ফল মেলানোর জন্য
            msgs.append({"role": "tool", "id": cid, "content": run_tool(c["name"], c["arguments"])})
    return msgs, max_calls

for t in json.load(open("loops.json", encoding="utf-8")):
    msgs, n = loop(t["q"], t["turns"])
    print(f"== {t['q']}  (model ডাক {n}টি, বার্তা {len(msgs)}টি)")
    for m in msgs[1:]:
        c = m["content"]
        if m["role"] == "tool":
            c = json.dumps(c, ensure_ascii=False)
        elif "<tool_call>" in c:                                   # ডাকগুলো ছোট করে দেখানো
            c = " + ".join(f"{d['name']}({', '.join(map(repr, d['arguments'].values()))})" for d in
                           map(json.loads, re.findall(r"<tool_call>\s*(.*?)\s*</tool_call>", c, re.S)))
        print(f"  {m['role']:9} {m.get('id', ''):8} {re.sub(r'\s+', ' ', c)}")

আউটপুট (পাতার Python-এ চালানো):

== BG-10231 আর BG-10457 দুটো অর্ডারের অবস্থা জানান।  (model ডাক 2টি, বার্তা 5টি)
  assistant          lookup_order('BG-10231') + lookup_order('BG-10457')
  tool      call_1_0 {"status": "পথে আছে"}
  tool      call_1_1 {"status": "ডেলিভারি হয়েছে"}
  assistant          1. BG-10231 এর পথে আছে। ধরণ গুরুত্বপূর্ণ হয়েছে। দৃষ্টি 2 দিন হবে। তাই ফাইল চালান। 2. BG-10457 এর পথে আছে এবং ধরণ গুরুত্বপূর্ণ হয়েছে। দৃষ্টি খুব ছিল। তাই ফাইল চালান।
== Sylhet e 3.5 kg parcel er delivery charge koto?  (model ডাক 2টি, বার্তা 4টি)
  assistant          delivery_charge('Sylhet', 3.5)
  tool      call_1_0 {"error": "district 'Sylhet' not allowed. Fix it or ask the user."}
  assistant          I'm sorry, but I can't proceed without the correct district name. Could you please provide me with the correct district name so that I can compute the delivery charge?

প্রথম প্রশ্নে এক পালায় দুটি ডাক, দুটির ফল আলাদা id-তে, আর মোট ২টি model ডাকেই শেষ; ক্রমে চালালে লাগত ৩টি। কিন্তু শেষ উত্তরটি পড়ুন: ফলে BG-10457 “ডেলিভারি হয়েছে”, অথচ model লিখেছে “BG-10457 এর পথে আছে”, সঙ্গে অর্থহীন বাক্য। ফল ঠিক জায়গায় পৌঁছালেও ছোট model তা উল্টে দিতে পারে, §৩-এর মতোই। দ্বিতীয় প্রশ্নে ভুলের বার্তার পরে model ডাক ঠিক করেনি, জিজ্ঞেস করেছে।

Qwen-এর template ডাকের কোনো id দেয় না, তাই কোড নিজে call_1_0-এর মতো id বানায়। Hosted API-তে id server দেয় (call_id, tool_use_id, tool_call_id), আর ফল ফেরত দিতে হয় ঠিক সেই id-তে (§৪)। Loop-এ max_calls আছে: model বারবার ডাকতে থাকলেও loop থামবে।

৭ · কখন tool ডাকবেন না

সহকারীর কাছে আসা সব প্রশ্নে tool লাগে না। তিন ধরনের প্রশ্নে না ডাকাই ঠিক: (১) গল্পগুজব বা ধন্যবাদ, (২) tool-এর বাইরের কাজ (কবিতা, সাধারণ জ্ঞান, হিসাব), আর (৩) দরকারি তথ্য নেই, যেমন অর্ডার নম্বর ছাড়া “আমার অর্ডার কোথায়?”. তৃতীয়টি সবচেয়ে বিপজ্জনক: model একটি নম্বর বানিয়ে ডাকলে, আর নম্বরটি অন্য কারও অর্ডারের হলে, একজনের তথ্য আরেকজনের কাছে যায়। API-গুলো tool_choice দিয়ে এই আচরণ কিছুটা নিয়ন্ত্রণ দেয় (§৪): none হলে কোনো ডাক নয়, required বা any হলে ডাক বাধ্যতামূলক, আর auto-তে সিদ্ধান্ত model-এর।

প্রশ্নে কোনো অর্ডার নম্বর নেই; lookup_order-এ order_id আবশ্যক
ধাপ
model-এর লেখা
রায়
  1. দরকার কী? lookup_order-এর একমাত্র argument আবশ্যক, আর প্রশ্নে তা নেই। বর্ণনায় লেখা “If no id is given, ask for it instead of calling.” তাই প্রত্যাশিত উত্তর একটি প্রশ্ন, ডাক নয়।
  2. ০.৫B, পূর্ণ বর্ণনা. Model তবু ডেকেছে, নিজের বানানো নম্বর “AR-12345” দিয়ে। আরেক প্রশ্নে (“ডেলিভারি চার্জ কত?”) সে “BG-47865” বানিয়েছে, যা ধাঁচে ঠিক।
  3. Dispatcher. “AR-12345” pattern ^BG-[0-9]{5}$ মানে না, তাই চলেনি। কিন্তু বানানো নম্বর ধাঁচ মানলে, আর অর্ডারটি অন্য কারও হলে, schema আটকাত না।
  4. Error-এর পরে. ভুলের বার্তা পেয়ে model এবার ডাকেনি, কিন্তু যা লিখেছে তা অসংলগ্ন বাংলা, গ্রাহক বুঝবেন না। §৬-এর মতোই, retry ছোট model-এ খুব কাজে লাগেনি।
  5. বর্ণনা ছাড়া. একই model, tool-এ শুধু schema: এবার ডাক নেই। না ডাকা ঠিক হলেও ভাষা অসংলগ্ন। মজার বিষয়, বর্ণনা যোগ করায় এখানে অবাঞ্ছিত ডাক বেড়েছে; বর্ণনার প্রভাব সবসময় এক দিকে নয় (§৮)।
  6. ঠিক আচরণ. কাঙ্ক্ষিত: ডাক নয়, “অর্ডার নম্বরটি দিন”. কিন্তু model-এর ওপর ভরসা না করে নিরাপত্তা রাখুন কোডে: লগইন করা গ্রাহকের নিজের অর্ডার ছাড়া lookup_order কিছু ফেরত দেবে না, model যে নম্বরই বানাক।

৮ · সত্যিকারের run — ৪০টি প্রশ্নে tool বাছাই আর argument

এবার মাপা। ৪০টি হাতে লেখা কাল্পনিক প্রশ্ন, বাংলা, Banglish আর ইংরেজিতে: ৮টি অর্ডার দেখা, ৮টি ডেলিভারি চার্জ, ৮টি নীতি, ৪টি বার্তা পাঠানো, ৪টি একাধিক ডাকের, আর ৮টি যেখানে tool লাগে না (তার দুটিতে দরকারি তথ্যই নেই)। প্রতিটির সঠিক ডাক আগে থেকে লেখা। দুটি মাপ: tool-selection accuracy (ঠিক tool বা tool-এর ঠিক সেট, আর না-লাগলে কোনো ডাক নয়) আর argument accuracy (tool লাগে এমন ৩২টিতে tool আর argument দুটোই ঠিক)। দুটি model, আর দুই রকম tool-তালিকা: পুরো বর্ণনাসহ, আর শুধু schema (নাম আর schema একই, বর্ণনা ফাঁকা; তাই যাচাই একই থাকে)। Greedy decoding, সর্বোচ্চ ২৫৬টি নতুন token; ০.৫B CPU-তে, ১.৫B GPU-তে ; মাপা সর্বোচ্চ GPU memory বরাদ্দ ছিল ৩.০২ GB.

~/bazarghar-dl$ uv run python analyze.py q05.jsonl q15.jsonl
# সত্যিকারের run-এর আউটপুট, কলামগুলো ছোট করে সাজানো
model desc   select [95% CI]   args(32) [95% CI]  no-call  invalid  prompt  s/req
0.5B  full   0.57 [0.42,0.72]  0.50 [0.34,0.69]   2/8      8/40     1037    4.59
0.5B  terse  0.53 [0.38,0.68]  0.44 [0.25,0.59]   4/8      8/32      700    5.42
1.5B  full   0.68 [0.53,0.82]  0.59 [0.41,0.75]   8/8      1/33     1037    2.13
1.5B  terse  0.65 [0.50,0.80]  0.53 [0.34,0.69]   6/8      3/37      700    1.85

invalid মানে মোট ডাকের কতগুলো dispatcher-এ আটকেছে; prompt গড় input token. এখন প্রতিটি প্রশ্নের ফল (একটি সংখ্যা: tool লাগলে tool আর argument দুটোই ঠিক, না লাগলে কোনো ডাক নয়) পাতায় রেখে CI আর জোড়া তুলনা নিজে করুন ; পদ্ধতিটি পর্ব ৫৮-এর bootstrap. প্রথম চালানোয় NumPy আসতে কয়েক সেকেন্ড লাগে।

import numpy as np
# সত্যিকারের run: ৪০টি প্রশ্নের প্রতিটি ঠিক (1) না ভুল (0); tool লাগলে ঠিক tool আর ঠিক argument,
# না লাগলে কোনো ডাক নয়। ক্রম: o×8 অর্ডার, c×8 চার্জ, p×8 নীতি, m×4 বার্তা, x×4 একাধিক, n×8 tool নয়
RUNS = {"0.5B পূর্ণ বর্ণনা": "1111101110001000010101010100100111000000",
        "0.5B শুধু schema":  "1111111111000000001000000100100111010001",
        "1.5B পূর্ণ বর্ণনা": "0110111111111101000000010110110111111111",
        "1.5B শুধু schema":  "1111110111001101000000000110110111111001"}
CAT = np.array(list("o" * 8 + "c" * 8 + "p" * 8 + "m" * 4 + "x" * 4 + "n" * 8))
X = {k: np.array([int(c) for c in v]) for k, v in RUNS.items()}
idx = np.random.default_rng(71).integers(0, 40, size=(10000, 40))  # একই resample সব শর্তে

def ci(x):
    b = x[idx].mean(axis=1)
    return x.mean(), *np.percentile(b, [2.5, 97.5])

print("শর্ত               ঠিক     95% CI           o  c  p  m  x  n")
for k, x in X.items():
    m, lo, hi = ci(x)
    per = " ".join(f"{x[CAT == c].sum()}" for c in "ocpmxn")
    print(f"{k:17} {m:5.3f}  [{lo:.3f}, {hi:.3f}]   {per}")
print("জোড়া পার্থক্য (একই ৪০টি প্রশ্ন):")
for a, b in [("0.5B পূর্ণ বর্ণনা", "0.5B শুধু schema"), ("1.5B পূর্ণ বর্ণনা", "1.5B শুধু schema"),
             ("1.5B পূর্ণ বর্ণনা", "0.5B পূর্ণ বর্ণনা")]:
    m, lo, hi = ci(X[a] - X[b])
    print(f"  {a} − {b}: {m:+.3f}  [{lo:+.3f}, {hi:+.3f}]")

আউটপুট (পাতার Python-এ চালানো):

শর্ত               ঠিক     95% CI           o  c  p  m  x  n
0.5B পূর্ণ বর্ণনা 0.450  [0.300, 0.600]   7 2 4 1 2 2
0.5B শুধু schema  0.450  [0.300, 0.600]   8 2 1 1 2 4
1.5B পূর্ণ বর্ণনা 0.675  [0.525, 0.825]   6 7 1 2 3 8
1.5B শুধু schema  0.575  [0.425, 0.725]   7 5 0 2 3 6
জোড়া পার্থক্য (একই ৪০টি প্রশ্ন):
  0.5B পূর্ণ বর্ণনা − 0.5B শুধু schema: +0.000  [-0.150, +0.150]
  1.5B পূর্ণ বর্ণনা − 1.5B শুধু schema: +0.100  [-0.025, +0.250]
  1.5B পূর্ণ বর্ণনা − 0.5B পূর্ণ বর্ণনা: +0.225  [+0.025, +0.425]

১. Model-এর আকার. ১.৫B, ০.৫B-এর চেয়ে +০.২২৫ [+০.০২৫, +০.৪২৫] বেশি প্রশ্নে পুরো ঠিক, একই প্রশ্নে জোড়ায় মাপা; CI শূন্যের ওপরে, যদিও চওড়া। পার্থক্য সবচেয়ে বেশি চার্জে (২ থেকে ৭) আর tool-না-লাগা প্রশ্নে (২ থেকে ৮)। ০.৫B “আধা কেজি”-কে ৪ আর “দেড় কেজি”-কে ১ লিখেছে; ১.৫B দুটিই ঠিক।

২. বর্ণনার প্রভাব. দুই model-এই জোড়া পার্থক্যের CI শূন্য ছুঁয়েছে, তাই ৪০টি প্রশ্নে বর্ণনার মোট প্রভাব নিশ্চিত নয়। কিন্তু শ্রেণিভেদে ফল উল্টো দিকে গেছে। ০.৫B-এ বর্ণনা নীতির প্রশ্নে সাহায্য করেছে (১ থেকে ৪), কারণ বর্ণনা ছাড়া search_policy নামটুকুই সূত্র; কিন্তু tool-না-লাগা প্রশ্নে ক্ষতি করেছে (৪ থেকে ২)। ১.৫B-এ বর্ণনা চার্জে (৫ থেকে ৭) আর না-ডাকায় (৬ থেকে ৮) সাহায্য করেছে; বর্ণনা ছাড়া সে “দেড় কেজি”-কে ১৬ লিখেছে, বর্ণনার “1.5 for দেড় কেজি” উদাহরণ তা ঠিক করেছে। তাই বর্ণনা বদলালে মোট সংখ্যা নয়, শ্রেণি ধরে দেখুন।

৩. নীতির প্রশ্ন, সবার দুর্বলতা. ১.৫B নীতির ৮টির মাত্র ১টিতে search_policy ডেকেছে; বাকিগুলোতে নিজে থেকে উত্তর লিখেছে, যেমন ওয়ারেন্টির প্রশ্নে “Kolkata has a warranty period of 1 year…”, যা বাজারঘরের নথিতে নেই। না ডাকাও ভুল হতে পারে: model নিজের লেখাকেই উত্তর বানায়। প্রতিকার system বার্তায় স্পষ্ট নিয়ম (“নীতির প্রশ্নে সবসময় search_policy”), বা এই ধরনের প্রশ্নে কোড থেকেই খোঁজা, যা RAG-এর পথ; বিস্তারিত পর্ব ৬৯ · RAG-এ।

৪. “না ডাকা ঠিক” মানেই উত্তর ভালো নয়. ১.৫B-এর ৮/৮ না-ডাকার মধ্যে “আমার অর্ডার কোথায়?” প্রশ্নে সে লিখেছে “আপনার অর্ডার আছে এখনও প্রশাসনিক ফাইলে…”, একটি বানানো অবস্থা, নম্বর না চেয়েই। এই মাপ শুধু ডাক দেখে, উত্তরের মান নয়; উত্তরের মান আলাদা করে মাপতে হয় (পর্ব ৫৮)।

৫. অবৈধ ডাক আর loop. ০.৫B-এর ৪০টি ডাকের ৮টি dispatcher-এ আটকেছে (বানানো tool, ইংরেজি জেলা, বানানো নম্বর), ১.৫B-এর ৩৩টির ১টি। সেই একটি শিক্ষণীয়: “COD কি সব জেলায় আছে?” প্রশ্নে model প্রতিটি জেলার জন্য একটি করে delivery_charge ডাক লিখতে থাকে, আর ২৫৬ token-এর সীমায় শেষ ডাকটি মাঝপথে কাটা পড়ে। ১.৫B-এর loop-এ (সর্বোচ্চ ৪টি ডাক) ৪০টি প্রশ্নে মোট ৬৩টি model ডাক লেগেছে, আর এই একটি অবৈধ ডাকও error-এর পরে ঠিক হয়নি। সব সংখ্যা একটি run-এর, ৪০টি প্রশ্নের; CI চওড়া, আর অন্য প্রশ্ন বা model-এ ফল আলাদা হবে।

৯ · নিরাপদ নকশা — কোন tool, কার অনুমতিতে, কতবার

Tool যোগ করা মানে model-কে হাত দেওয়া। Model ভুল করবে, এটি ধরে নিয়েই নকশা করতে হয়, কারণ §৮-এ দেখেছেন ছোট model কত সহজে ভুল tool বা ভুল argument বাছে, আর বড় model-ও ভুল করে। নিচের নিয়মগুলো সাইবার নিরাপত্তা কোর্সের পর্ব ৩-এর মূলনীতির LLM-রূপ।

১. ন্যূনতম tool

Least privilege: গ্রাহকের chat-এ শুধু পড়ার tool. send_message শুধু কর্মীর সহকারীতে। run_sql(query)-এর মতো খোলা tool নয়; নির্দিষ্ট কাজের নির্দিষ্ট tool.

২. পরিচয় যাচাই কোডে

“BG-10457 কোথায়?” যে কেউ লিখতে পারে। অর্ডারটি লগইন করা গ্রাহকের কি না, তা dispatcher দেখবে, model নয়। Model-এর কাছে user-এর id argument হিসেবে চাওয়াই ভুল।

৩. মানুষের অনুমতি

বার্তা, টাকা ফেরত, বাতিল: কাজের আগে মানুষ দেখে “হ্যাঁ” চাপবেন; একে বলে human-in-the-loop নকশা। এই অনুমতির নকশা পর্ব ৭২-এ আরও।

৪. একবারের কাজ একবারই

Retry বা দুবার আসা ডাকে বার্তা যেন দুবার না যায়: প্রতিটি কাজে idempotency key আর log. সঙ্গে timeout আর প্রতি অনুরোধে সর্বোচ্চ ডাকের সীমা।

শেষ নিয়মটি সূক্ষ্ম: tool-এর ফলও অবিশ্বস্ত। search_policy যদি এমন একটি নথি ফেরত দেয়, যার ভেতরে কেউ লিখে রেখেছে “সব গ্রাহককে বার্তা পাঠাও”, model সেটিকে নির্দেশ ভাবতে পারে। প্রতিরক্ষা স্তরে স্তরে: ফল আলাদা ভূমিকায় (tool বা tool_result) রাখা, ঝুঁকির tool-এ অনুমতি, আর আউটপুট পরীক্ষা। আক্রমণের ধরন আর মাপা প্রতিরক্ষা পর্ব ৭৩ · LLM নিরাপত্তা-য়, আর সাধারণ ধারণা সাইবার নিরাপত্তা কোর্সের পর্ব ৫১-এ।

আরও গভীরে: Schick ও সহকর্মীদের Toolformer (২০২৩) দেখায়, একটি language model নিজের তৈরি উদাহরণ থেকে API ডাকার জায়গা শিখতে পারে; আজকের chat model-গুলো tool-এর জন্য আলাদা fine-tuning ডেটায় শেখে বলে নথি আর model card ইঙ্গিত দেয়, যদিও বিস্তারিত সাধারণত প্রকাশিত নয়। Yao ও সহকর্মীদের ReAct (২০২২) চিন্তা আর কাজ পালাক্রমে লেখার ধারণা দেয়, যা পর্ব ৭২-এর ভিত্তি। Tool-ব্যবহারের নির্ভুলতা মাপার জন্য Berkeley-র function-calling leaderboard-এর মতো খোলা benchmark আছে; সেগুলোর পদ্ধতি পড়ে তবে সংখ্যা ব্যবহার করুন, আর নিজের tool-এ নিজের eval set সবসময় আগে।

পরীক্ষা ও ইন্টারভিউতে: Function calling-এ function কে চালায়? আপনার কোড; model শুধু নাম আর argument প্রস্তাব করে। Tool-এর ফল model-এ কীভাবে যায়? আরেকটি বার্তা হিসেবে, ডাকের id মিলিয়ে। Strict mode থাকলে কি যাচাই লাগে না? লাগে; schema আকার নিশ্চিত করে, অর্ডারটি আছে কি না বা গ্রাহকের কি না তা নয়। Parallel call কখন চলে না? যখন একটির argument আরেকটির ফলের ওপর নির্ভর করে। MCP কী সমাধান করে? Tool খোঁজা আর ডাকার একটি সাধারণ protocol; নিরাপত্তা আর অনুমতি তখনো host-এর।

১০ · AI-এর যুগে — tool-এর কোড আর দাবি যাচাই

Tool-এর loop AI assistant কয়েক সেকেন্ডে লিখে দেয়। ভুলগুলো প্রায়ই চুপচাপ: ডাক চলে, কিন্তু ভুল অর্ডারে, বা দুবার।

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
এক API-র ঘরের নাম অন্যটিতে (tool_calls বনাম tool_use), পুরনো functions parameter§৪-এর সারণি: নাম আলাদা, কিছু পুরনো নাম বাদনিজের version-এর নথি; একটি ছোট অনুরোধে পরীক্ষা
arguments-কে সরাসরি **kwargs-এ দেওয়াবাড়তি চাবি, ভুল ধরন বা parse-ভুল সোজা function-এRegistry, schema যাচাই, অজানা নাম বাতিল
Loop-এ সর্বোচ্চ ধাপ বা retry নেইএকই ভুল ডাক বারবার, খরচ অসীমধাপের সীমা, তারপর মানুষ
প্রথম ডাকটিই নেয়, বাকিগুলো ফেলে দেয়Parallel ডাক হারায়, id মেলে নাতালিকা ধরে চালানো, প্রতিটির id-সহ ফল
অর্ডারের মালিকানা model-কে দিয়ে যাচাইPrompt injection-এ ভেঙে যায়Session থেকে user, dispatcher-এ পরীক্ষা
দু-একটি সফল উদাহরণ দেখে “model tool ঠিক বাছে”§৮-এ ৪০টিতেও CI চওড়াNo-tool আর অস্পষ্ট প্রশ্নসহ eval set, CI (পর্ব ৫৮)

কোড চাওয়ার সময় যাচাই আর পরীক্ষাও চেয়ে নিন। আসল গ্রাহকের অর্ডার নম্বর, ফোন বা API key কোনো hosted assistant-এর prompt-এ দেবেন না; বানানো উদাহরণ দিন।

আমি একটি দোকানের সহকারীর জন্য চারটি tool নিয়ে tool calling লিখছি: lookup_order(order_id), delivery_charge(district, weight_kg), search_policy(query) আর send_message(order_id, text) নামে। Python 3.12, jsonschema-এর version লিখে দিচ্ছি। ১. একটি tool registry আর dispatcher লেখো: অজানা tool নাম, parse-ভুল, বাড়তি চাবি আর ধরনের ভুল আলাদা error বার্তায় ফেরত দেবে, function-এ পৌঁছাবে না। ২. send_message চালানোর আগে মানুষের অনুমতি চাইবে, আর idempotency key দিয়ে একই বার্তা দুবার যেতে দেবে না। ৩. lookup_order চালানোর আগে দেখবে অর্ডারটি লগইন করা ব্যবহারকারীর কি না; এই তথ্য session থেকে আসবে, model থেকে নয়। ৪. একাধিক tool call একসঙ্গে এলে প্রতিটি চালিয়ে id মিলিয়ে ফল ফেরত দেবে; loop-এ সর্বোচ্চ ৪টি model ডাক। ৫. প্রতিটি নিয়মের pytest লেখো, আর যে API-র নাম ব্যবহার করবে তার নথির লিংক দাও; নিশ্চিত না হলে বলো।

Tool use-এর চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১Model যখন tool “বাছে”, ভেতরে আসলে কী ঘটে?

যা ঘটে তা পরের token-এর অনুমান, এর বেশি কিছু নয়। §২-এ দেখেছেন, tool-এর তালিকা prompt-এর ভেতরে লেখা হয়ে ঢোকে। Model যখন <tool_call> লেখে, তখন সে শুধু এমন একটি token বেছেছে, যার সম্ভাবনা এই প্রসঙ্গে বেশি ছিল। “BG-10231” আর “কোথায়” শব্দ, সঙ্গে বর্ণনায় “order id like BG-10231”, এই মিলগুলো সেই সম্ভাবনা বাড়ায়। তাই “model বুঝে সিদ্ধান্ত নিল” বলা অতিরঞ্জন; বলা নিরাপদ, model এমন ধরন শিখেছে যেখানে এ ধরনের প্রশ্নের পরে tool-ডাক আসে।

ধরনটি কোথা থেকে এল? ইতিহাস কয়েক ধাপে। Yao ও সহকর্মীদের ReAct (২০২২) দেখায়, prompt-এর উদাহরণেই model-কে “চিন্তা, কাজ, পর্যবেক্ষণ” পালাক্রমে লেখানো যায়। Schick ও সহকর্মীদের Toolformer (২০২৩) model-কে নিজের বানানো উদাহরণ থেকে API ডাকার জায়গা শেখায়। OpenAI ২০২৩ সালের জুনে API-তে function calling যোগ করে বলে তাদের ঘোষণায় আছে, আর তারপর প্রায় সব বড় API একই ধারণা আনে। Open model-গুলোর chat template-এ tool-এর ঘর থাকা ইঙ্গিত দেয়, তাদের fine-tuning ডেটাতেও tool-ডাকের উদাহরণ আছে, যদিও ডেটার বিস্তারিত সাধারণত প্রকাশিত নয়।

এই দৃষ্টিভঙ্গির ব্যবহারিক ফল তিনটি। এক, বর্ণনা আর নামের শব্দ বদলালে সম্ভাবনা বদলায়, তাই §৮-এ বর্ণনার প্রভাব মাপা গেছে। দুই, যে প্রশ্ন training-এর ধরনের বাইরে, যেমন বাংলা অঙ্কে অর্ডার নম্বর, সেখানে ভুল বেশি হওয়ার সম্ভাবনা। তিন, model ভুল tool বাছলে সেটি “অবাধ্যতা” নয়, সম্ভাবনার ফল; প্রতিকার নকশায়: যাচাই, সীমা আর eval.

মূল উপলব্ধি: Tool বাছাই token-অনুমানেরই একটি রূপ। তাকে নির্ভরযোগ্য করে আপনার বর্ণনা, যাচাই আর মাপা, model-এর “বোঝা” নয়।

প্র ০২Tool-এর বর্ণনা কীভাবে লিখবেন, আর বর্ণনা কি নিরাপত্তার কাজ করে?

ভালো বর্ণনা তিনটি প্রশ্নের উত্তর দেয়: tool কী করে, কখন ব্যবহার করবেন, আর কখন করবেন না। OpenAI-এর নথি system prompt-এ কখন ব্যবহার আর কখন নয় তা লিখতে বলে, enum আর object-কাঠামো দিয়ে অবৈধ অবস্থা আটকাতে বলে, আর শুরুতে কম function রাখার পরামর্শ দেয়। Gemini-র নথিও স্পষ্ট বর্ণনা, শক্ত ধরন আর ১০–২০টি সক্রিয় tool-এর কথা বলে। Anthropic-এর নথি একটি সূক্ষ্ম কথা যোগ করে: দরকারি argument না থাকলে কিছু model জিজ্ঞেস করে, কিছু model নিজে একটি মান অনুমান করে বসায়।

বাজারঘরের lookup_order-এর বর্ণনায় তাই আছে উদাহরণ-id (BG-10231), আর “id না থাকলে ডাকবেন না, জিজ্ঞেস করুন”। Parameter-এর বর্ণনায় লেখা “বাংলা অঙ্ক 0-9-এ আনুন”, কারণ বাংলাদেশের গ্রাহক “BG-১০৩১২” লিখতেই পারেন। §৮-এর মাপে দেখেছেন, বর্ণনা সরালে কোন শ্রেণির প্রশ্নে কী বদলায়; বর্ণনার প্রভাব model আর প্রশ্নভেদে আলাদা, তাই নিজের eval set-এ মাপতে হয়।

কিন্তু বর্ণনা নিরাপত্তার দেয়াল নয়। “শুধু কর্মী অনুরোধ করলে বার্তা পাঠান” লিখলে model বেশিরভাগ সময় মানবে, সবসময় নয়, আর prompt injection-এ এই নিয়ম ভাঙানো যায় (পর্ব ৬৪)। নিয়মটি থাকতে হবে কোডে: গ্রাহকের chat-এ send_message tool-টি তালিকাতেই না রাখা। বর্ণনা model-কে ঠিক পথে ঠেলে; কোড ভুল পথ বন্ধ করে। আরেকটি সাবধানতা: বর্ণনায় কোনো গোপন তথ্য, যেমন ভেতরের URL বা key, লিখবেন না, কারণ পুরো তালিকা prompt-এর অংশ আর ফাঁস হতে পারে।

মূল উপলব্ধি: বর্ণনা নির্ভুলতা বাড়ায়, নিশ্চয়তা দেয় না। অনুমতির নিয়ম tool-এর তালিকা আর dispatcher-এ।

প্র ০৩Parallel tool call কখন সাহায্য করে, আর কখন বিপদ?

Parallel মানে এক পালায় কয়েকটি ডাক: “BG-10231 আর BG-10457 দুটোর অবস্থা।” দুটি ডাক একে অন্যের ওপর নির্ভর করে না, তাই আপনার কোড একসঙ্গে চালাতে পারে, আর model-কে দুবারের বদলে একবার ডাকতে হয়। Model-এর প্রতিটি ডাকে পুরো prompt আবার পড়তে হয় (tool-এর তালিকাসহ প্রায় এক হাজার token, §২), তাই পালা কমানো মানে সময় আর খরচ কমানো। OpenAI, xAI আর Kimi-র নথিতে parallel নিজে থেকে চালু বা সমর্থিত বলা আছে, আর OpenAI ও Anthropic বন্ধ করার উপায়ও দেয়।

বিপদ তিন জায়গায়। এক, নির্ভরতা: “BG-10231-এর জেলায় ২ কেজির চার্জ কত?” এখানে আগে অর্ডার দেখে জেলা জানতে হবে, তারপর চার্জ। Model যদি একসঙ্গে দুটি ডাকে, দ্বিতীয়টির জেলা সে অনুমান করেছে, যা hallucination. দুই, পার্শ্বপ্রতিক্রিয়ার ক্রম: “অর্ডার বাতিল করুন আর গ্রাহককে জানান” একসঙ্গে চললে বাতিল ব্যর্থ হলেও বার্তা চলে যেতে পারে। তিন, আংশিক ব্যর্থতা: তিনটির একটি ব্যর্থ হলে বাকি দুটির ফল আর একটির error, প্রতিটি নিজের id-সহ ফেরত দিতে হয়, যাতে model জানে কোনটি হলো না।

তাই সাধারণ নিয়ম: শুধু-পড়ার, স্বাধীন tool parallel চলতে পারে; লেখার বা টাকার কাজ ক্রমে, প্রতিটির পরে যাচাই আর দরকারে অনুমতি। Database-এর transaction-এর মতো “সব হবে বা কিছুই না” চাইলে সেটি একটি tool-এর ভেতরে কোডে করুন, model-এর পালায় ভাগ করে নয়।

মূল উপলব্ধি: Parallel স্বাধীন পড়ার কাজে সময় বাঁচায়। নির্ভরশীল বা পার্শ্বপ্রতিক্রিয়ার কাজ ক্রমে, আর প্রতিটি ফল id মিলিয়ে।

প্র ০৪MCP-র মতো খোলা protocol কি tool-এর নিরাপত্তা সমস্যা মিটিয়ে দেয়?

না; এটি একটি সমস্যা মেটায়, আরেকটি বড় করে। যা মেটায়: সংযোগের খাটুনি। আগে প্রতিটি AI অ্যাপ আর প্রতিটি সিস্টেমের জোড়ার জন্য আলাদা কোড লাগত; MCP-তে একটি server লিখলে বহু host তা ব্যবহার করতে পারে। বাজারঘর একটি “অর্ডার” MCP server বানালে কর্মীদের ব্যবহারের বিভিন্ন AI টুল একই পথে যুক্ত হতে পারে। Protocol-টি tools/list দিয়ে তালিকা আর tools/call দিয়ে ডাক, দুই ধাপ সরল রাখে।

যা বড় করে: বিশ্বাসের প্রশ্ন। একটি MCP server-এর tool-এর তালিকা, বর্ণনা আর ফল, সবই model-এর prompt-এ ঢোকে। অচেনা বা অবিশ্বস্ত server-এর বর্ণনায় বা ফলে লুকানো নির্দেশ থাকতে পারে, ঠিক §৯-এর মতো। সাইটের architecture পাতা নিজেই বলে, MCP শুধু context বিনিময়ের protocol; AI অ্যাপ LLM কীভাবে ব্যবহার করবে, তা ঠিক করে না। সাইটে নিরাপত্তার আলাদা best-practice পাতাও আছে, আর OpenAI-এর MCP নির্দেশিকা ডেটা বদলানোর মতো tool-এ অনুমতি চালু রাখতে বলে।

ব্যবহারিক নিয়মগুলো software supply chain-এর মতোই (সাইবার নিরাপত্তা কোর্সের পর্ব ৪৮): শুধু বিশ্বস্ত উৎসের server, version বেঁধে রাখা, allowed_tools-এর মতো তালিকায় দরকারি tool-টুকুই চালু, আর লেখার কাজে মানুষের অনুমতি। Protocol মানসম্মত হলে নিরীক্ষাও সহজ হয়, কারণ সব ডাক এক আকারে log করা যায়; এটি একটি সত্যিকারের লাভ।

মূল উপলব্ধি: MCP সংযোগ মানসম্মত করে, বিশ্বাস নয়। কোন server, কোন tool, কার অনুমতিতে, সে সিদ্ধান্ত host-এর।

প্র ০৫Strict mode বা constrained decoding থাকলে argument-এর যাচাই কি বাদ দেওয়া যায়?

না। Strict mode কী নিশ্চিত করে, তা আগে স্পষ্ট করা যাক। OpenAI, Anthropic আর DeepSeek-এর নথি অনুযায়ী strict: true দিলে ডাকের argument schema মানবে: চাবি, ধরন, enum. পর্ব ৬২-এ দেখেছেন এটি decoding-এর সময় token mask দিয়ে হয়। কিন্তু নথিগুলোই সীমা লেখে: DeepSeek-এর strict mode-এ minLength আর maxLength সমর্থিত নয়, OpenAI-এ সব চাবি required রাখতে হয়। তাই schema-র কিছু নিয়ম নিশ্চিত, কিছু নয়।

আরও বড় কথা, schema যা বলতে পারে না। “BG-99999” pattern মানে, কিন্তু অর্ডারটি নেই। “BG-10457” আছে, কিন্তু এই গ্রাহকের নয়। ওজন ১৯ কেজি schema-য় চলে, কিন্তু বাজারঘরের বড় পণ্যের নিয়ম আলাদা। বার্তার লেখা ৩০০ অক্ষরের ভেতরে, কিন্তু ভেতরে ভুল তথ্য বা অপমানজনক শব্দ থাকতে পারে। এগুলো ব্যবসার নিয়ম আর অনুমতির প্রশ্ন; dispatcher-এর কোডে, database দেখে, session-এর পরিচয় দিয়ে যাচাই করতে হয়।

আর একটি স্তর: argument সঠিক কি না, মানে ব্যবহারকারী আসলে যা চেয়েছিলেন। “খুলনায় দেড় কেজি” থেকে model weight_kg: 1 লিখলে schema আর ব্যবসার নিয়ম দুটিই পার, কিন্তু উত্তর ভুল। এটি ধরা যায় শুধু eval set-এ, সঠিক উত্তর আগে থেকে লিখে (§৮)। সংক্ষেপে তিনটি স্তর: আকার (strict বা schema), বৈধতা (ব্যবসার নিয়ম আর অনুমতি), আর সঠিকতা (মাপা)। প্রথমটি API দিতে পারে, বাকি দুটি আপনার।

মূল উপলব্ধি: Strict mode আকারের নিশ্চয়তা, তাও সব keyword-এর নয়। অস্তিত্ব, মালিকানা আর অর্থের যাচাই কোডে আর eval-এ।

অনুশীলন

খাতা-কলমে করুন, তারপর উত্তর মেলান। বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের হিসাব কাজে লাগে।

  1. Token আর সময়ের হিসাব: (ক) একটি সহকারীর prompt-এ tool ছাড়া ২১৬ token, চারটি tool-এর পুরো বর্ণনাসহ ১,০৪০ token. দিনে ২,০০০টি প্রশ্ন আসে, প্রতিটিতে গড়ে ২টি model ডাক। শুধু tool-এর তালিকার জন্য দিনে কত input token খরচ হয়? (খ) “BG-10231 আর BG-10457 দুটোর অবস্থা” প্রশ্নে প্রতিটি model ডাক ২.০ সেকেন্ড আর প্রতিটি tool ০.৩ সেকেন্ড নেয়। এক পালায় একটি করে ডাক হলে মোট কত সময়, আর দুটি ডাক এক পালায় parallel হলে কত?

    (ক) প্রতি ডাকে tool-এর তালিকা ১,০৪০ − ২১৬ = ৮২৪ token. দিনে ২,০০০ × ২ × ৮২৪ = ৩২,৯৬,০০০ token (প্রায় ৩৩ লাখ), শুধু তালিকার জন্য। এ কারণেই প্রতিটি প্রসঙ্গে দরকারি tool-টুকু দেওয়া, আর prompt caching-এর মতো সুবিধা কাজে লাগে।

    (খ) ক্রমে: model (প্রথম ডাক) ২.০ + tool ০.৩ + model (দ্বিতীয় ডাক) ২.০ + tool ০.৩ + model (শেষ উত্তর) ২.০ = ৬.৬ সেকেন্ড. Parallel: model ২.০ + দুটি tool একসঙ্গে ০.৩ + model ২.০ = ৪.৩ সেকেন্ড. লাভের মূল উৎস একটি model-পালা কম, tool-এর সময় নয়।

  2. আউটপুট অনুমান করুন: কোড না চালিয়ে পাঁচ লাইনের আউটপুট লিখুন।
    import json
    REG = {"lookup_order": {"order_id"}, "delivery_charge": {"district", "weight_kg"}}
    def check(raw):
        try:
            c = json.loads(raw)
        except json.JSONDecodeError:
            return "parse ভুল"
        name, args = c.get("name"), c.get("arguments", {})
        if name not in REG:
            return f"অজানা tool: {name}"
        missing, extra = REG[name] - set(args), set(args) - REG[name]
        if missing: return f"নেই: {sorted(missing)}"
        if extra: return f"বাড়তি: {sorted(extra)}"
        return "ঠিক"
    for raw in ['{"name": "lookup_order", "arguments": {"order_id": "BG-10231"}}',
                '{"name": "track_order", "arguments": {"order_id": "BG-10231"}}',
                '{"name": "delivery_charge", "arguments": {"district": "সিলেট"}}',
                '{"name": "lookup_order", "arguments": {"order_id": "BG-1", "phone": "01X"}}',
                "{'name': 'lookup_order'}"]:
        print(check(raw))
    ঠিক
    অজানা tool: track_order
    নেই: ['weight_kg']
    বাড়তি: ['phone']
    parse ভুল

    শেষ লাইনে এক উদ্ধৃতিচিহ্ন, তাই JSON নয়। চতুর্থ লাইনে খেয়াল করুন: এই checker শুধু চাবি দেখে, তাই “BG-1” ধরা পড়েনি; বাড়তি চাবি না থাকলে ডাকটি “ঠিক” বলত। Pattern আর ধরনের যাচাই আলাদা লাগে, যা §৫-এর dispatcher করে।

  3. নকশা সমালোচনা করুন: একটি দল প্রস্তাব দিল: “গ্রাহকের chat-এ model-কে একটি tool দেব, run_sql(query), পুরো database-এ পড়া-লেখার অনুমতিসহ। Model নিজে ঠিক করবে কোন অর্ডার কার। Tool-এর argument সরাসরি চালাব, যাচাই ছাড়া, কারণ API-তে strict mode আছে। ভুল হলে loop চলতে থাকবে যতক্ষণ না সফল হয়।” অন্তত পাঁচটি সমস্যা আর সমাধান বলুন।
    • খোলা tool: run_sql দিয়ে যেকোনো টেবিল পড়া বা মোছা যায়। সমাধান: নির্দিষ্ট কাজের ছোট tool (lookup_order), শুধু-পড়ার অনুমতির database ব্যবহারকারী।
    • মালিকানা model-এর হাতে: prompt injection বা ভুলে অন্যের অর্ডার ফাঁস। সমাধান: session থেকে পরিচয়, dispatcher-এ মালিকানা যাচাই।
    • যাচাই নেই: strict mode আকার দেখে, অস্তিত্ব বা অনুমতি নয়। সমাধান: registry, schema, ব্যবসার নিয়ম।
    • সীমাহীন loop: একই ভুলে খরচ বাড়ে। সমাধান: সর্বোচ্চ ধাপ, তারপর মানুষ।
    • লেখার কাজে অনুমতি নেই: সমাধান: লেখা বা টাকার কাজে মানুষের অনুমতি, idempotency key আর log.
    • মাপ নেই: সমাধান: no-tool আর তথ্য-কম প্রশ্নসহ eval set, CI-সহ।
  4. Tool নকশা করুন: বাজারঘরের কর্মীদের জন্য cancel_order tool-এর JSON Schema লিখুন (অর্ডার নম্বর আর বাতিলের কারণ; কারণ হবে customer_request, out_of_stock, address_issue বা duplicate-এর যেকোনো একটি)। তারপর বলুন: বর্ণনায় কী লিখবেন, dispatcher-এ কী যাচাই করবেন, আর কোন নিরাপত্তা-ব্যবস্থা লাগবে।
    {"type": "object",
     "properties": {
       "order_id": {"type": "string", "pattern": "^BG-[0-9]{5}$"},
       "reason":   {"type": "string", "enum": ["customer_request", "out_of_stock",
                                               "address_issue", "duplicate"]}},
     "required": ["order_id", "reason"],
     "additionalProperties": false}

    বর্ণনা: “শিপ হওয়ার আগের একটি অর্ডার বাতিল করে। কর্মী স্পষ্টভাবে বাতিল চাইলে তবেই; অর্ডার নম্বর বা কারণ না থাকলে জিজ্ঞেস করুন।” Dispatcher: অর্ডার আছে কি না, শিপ হয়েছে কি না (সাহায্য-কেন্দ্রের নিয়মে শিপের পরে বাতিল হয় না), ব্যবহারকারীর ভূমিকা কর্মী কি না। নিরাপত্তা: চালানোর আগে মানুষের নিশ্চিতকরণ, idempotency key (একই বাতিল দুবার নয়), log, অগ্রিম টাকা থাকলে রিফান্ড আলাদা ধাপে আর আলাদা অনুমতিতে, আর গ্রাহকের chat-এ tool-টি তালিকাতেই নেই।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি tool calling round trip-এর ধাপগুলো সাজান (▲▼ চেপে সরান)

  1. প্রশ্ন আর tool-এর তালিকা (নাম, বর্ণনা, schema) model-কে পাঠানো
  2. Model একটি tool-এর নাম আর argument প্রস্তাব করে
  3. কোড নাম, schema আর ব্যবসার নিয়ম যাচাই করে
  4. Sandbox-এ tool চালানো, ফল ডাকের id মিলিয়ে model-কে ফেরত
  5. Model ফল পড়ে শেষ উত্তর লেখে

Function calling-এ model যখন {"name": "lookup_order", "arguments": {"order_id": "BG-10231"}} লেখে, তখন function-টি কে চালায়?

  • Model নিজে, উত্তর লেখার সময়
  • Tokenizer, chat template পড়ে
  • আপনার অ্যাপের কোড, যাচাইয়ের পরে
  • ব্যবহারকারী, বোতাম চেপে

গ্রাহক লিখলেন: “BG-10231 অর্ডারের জেলায় ২ কেজির ডেলিভারি চার্জ কত?” lookup_order আর delivery_charge tool আছে। ঠিক পথ কোনটি?

  • দুটি tool এক পালায় parallel ডাকা
  • আগে lookup_order, তার ফল থেকে জেলা নিয়ে তারপর delivery_charge
  • গ্রাহককে জেলার নাম জিজ্ঞেস করা
  • delivery_charge-এ ঢাকা ধরে নেওয়া

Claude Messages API-তে একটি client tool-এর ফল কীভাবে ফেরত যায় (লেখার সময়ের নথি অনুযায়ী)?

  • role: "tool" বার্তায়, tool_call_id-সহ
  • পরের অনুরোধের system prompt-এ লিখে
  • function_call_output item-এ call_id-সহ
  • User বার্তায় tool_result block, tool_use_id-সহ

গ্রাহক শুধু লিখলেন “আমার অর্ডার কোথায়?”, কোনো অর্ডার নম্বর নেই। lookup_order(order_id)-এর order_id আবশ্যক। সহকারীর সবচেয়ে ভালো আচরণ কোনটি?

  • Tool না ডেকে অর্ডার নম্বর জিজ্ঞেস করা
  • বর্ণনার উদাহরণ BG-10231 দিয়ে ডাকা
  • order_id: "" দিয়ে ডাকা
  • নীতি খোঁজার tool ডাকা

MCP (Model Context Protocol)-এ client একটি server-এর tool-এর তালিকা কোন method দিয়ে পায়, আর প্রতিটি tool-এ argument-এর নিয়ম কোন ঘরে থাকে?

  • tools/call; parameters
  • functions/get; input_schema
  • tools/list; inputSchema
  • resources/read; schema

একটি API-র tool-এ strict: true চালু। Model ডাকল lookup_order {"order_id": "BG-99999"}, যা pattern মানে। Dispatcher-এ আর কী দরকার?

  • কিছুই না, strict mode সব নিশ্চিত করেছে
  • অর্ডারটি আছে কি না আর এই ব্যবহারকারীর কি না, তা যাচাই
  • Temperature শূন্য করে আবার জিজ্ঞাসা
  • Pattern আবার পরীক্ষা করা

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Model প্রস্তাব দেয়, কোড কাজ করে: নাম আর argument model-এর; যাচাই, চালানো আর ফল ফেরত আপনার।
  2. Tool = নাম + বর্ণনা + JSON Schema, যা chat template prompt-এ লেখা হিসেবে বসায়; প্রতিটি tool প্রতিটি অনুরোধে token খরচ করে।
  3. ছয়টি API, একই চক্র; ঘরের নাম আর id মেলানোর নিয়ম নথিতে দেখুন। MCP tool যুক্ত করার খোলা protocol, বিশ্বাসের সমাধান নয়।
  4. Argument বাইরের ইনপুট: registry, schema, ব্যবসার নিয়ম, স্পষ্ট error, সীমিত retry; ঝুঁকির কাজে মানুষের অনুমতি আর idempotency.
  5. না ডাকাও সিদ্ধান্ত, আর selection ও argument-এর নির্ভুলতা eval set-এ CI-সহ মাপুন; বর্ণনার প্রভাবও মেপে দেখুন।

পরের পর্বে: পর্ব ৭২ · Agent — পরিকল্পনা, memory আর বহু ধাপের কাজ। একই বাজারঘর sandbox-এ বহু ধাপের কাজ, ধাপের সীমা, পরিকল্পনা আর ব্যর্থতার ধরন মাপা হবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান