এআই এজেন্টের ভেতরে আসলে কী কী থাকে — Brain, Memory, Tools আর Action Loop
শেখার ডায়েরি

এআই এজেন্টের ভেতরে আসলে কী কী থাকে — Brain, Memory, Tools আর Action Loop

Z
জাহিদ হাসান তন্ময়
২৮ সেপ্টেম্বর, ২০২৬
10 মিনিটের পড়া
🇬🇧 Read in English
|
0 views
অডিও ওভারভিউ ও পডকাস্ট
~১ মিনিট কুইক ওভারভিউ
0%

আগের পোস্টে LLM আর এজেন্টের পার্থক্য বোঝাতে একটা ছোট্ট এজেন্ট বানিয়েছিলাম, যাতে ছিল একটা model, একটা tool আর একটা loop। এবার সেই এজেন্টের ভেতরটা একটু খুলে দেখব: কোন অংশ ঠিক কী কাজ করে।

ধরুন আপনি খরচের হিসাব রাখার একটা এজেন্ট বানিয়েছেন। সকালে বললেন, “দুপুরে ২৫০ টাকা খরচ হয়েছে।” এজেন্ট হিসাবটা লিখে রাখল। কয়েক মিনিট পর জিজ্ঞেস করলেন, “আজ মোট কত খরচ হলো?” সে পাল্টা প্রশ্ন করল, “কোন খরচের কথা বলছেন?” এই ভুলটা মডেলের বুদ্ধির অভাব নয়। এজেন্টের একটা অংশ কাজ করছিল না, আর সেটা কোনটা তা না জানলে ভুল ধরা যায় না।

এআই এজেন্টের ভেতরে কী কী থাকে?

একটা এআই এজেন্টের ভেতরে থাকে চারটা অংশ: Brain (যে language model পরের ধাপ ঠিক করে), Memory (কথোপকথনের ইতিহাস আর জমিয়ে রাখা তথ্য), Tools (বাইরের জগতে কাজ করার ফাংশন) এবং Action Loop (যে কোড মডেলকে ডাকে, তার চাওয়া tool চালায় আর ফলাফল ফেরত পাঠায়)। এর মধ্যে শুধু Brain-টাই মডেল; বাকি তিনটা আপনার লেখা কোড আর ডেটা।

মুদি দোকানের উদাহরণে চারটা অংশ

একটা মুদি দোকান ভাবুন। দোকানদারের বুদ্ধি আর বিচার হলো Brain: কোন জিনিসের দাম কত, কী বেচবেন, কী বেচবেন না, সেটা সে-ই ঠিক করে। বাকির খাতা হলো Memory: কে কত বাকি রেখেছে, দোকানদার তা মুখস্থ রাখে না, খাতায় লেখে। ক্যালকুলেটর, দাঁড়িপাল্লা আর ক্যাশবাক্স হলো Tools, যেগুলো দিয়ে সে হাতে-কলমে কাজ করে। আর “আর কিছু লাগবে?” বলে বারবার জিজ্ঞেস করার ছন্দটা হলো Action Loop: ক্রেতা বলল, দোকানদার দেখল, করল, আবার জিজ্ঞেস করল, যতক্ষণ না ক্রেতা বলে “হয়ে গেছে”।

দোকানদারের বুদ্ধি একা দোকান চালাতে পারে না। model-ও তেমনি একা এজেন্ট নয়।

📊 Architecture DiagramArchitecture Flow
Interactive diagram rendering...
flowchart LR
    U[ব্যবহারকারীর মেসেজ] --> M[(Memory)]
    M -->|পড়ে| B[Brain: model]
    B -->|tool চায়| T[Tools]
    T -->|ফলাফল জমা হয়| M
    B -->|tool লাগবে না| A[চূড়ান্ত উত্তর]
System architecture specification and node flow: flowchart LR U[ব্যবহারকারীর মেসেজ] --> M[(Memory)] M -->|পড়ে| B[Brain: model] B -->|tool চায়| T[Tools] T -->|ফলাফল জমা হয়| M B -->|tool লাগবে না| A[চূড়ান্ত উত্তর]

Flow-এর সারসংক্ষেপ: ব্যবহারকারীর মেসেজ Memory-তে জমা হয়, আর Brain সেটা পড়ে। Brain কোনো tool চাইলে আপনার কোড সেটা চালিয়ে ফলাফল আবার Memory-তে রাখে, তারপর Brain নতুন করে পড়ে। tool না লাগলে Brain চূড়ান্ত উত্তর দেয়। এই ঘুরে আসাটাই Action Loop।

চারটা অংশ আলাদা করে

Brain: সিদ্ধান্ত যেখানে হয়

Brain হলো language model। প্রতিবার সে পায় system prompt, এ পর্যন্ত হওয়া কথোপকথন, কোন কোন tool আছে তার তালিকা আর আগের tool-এর ফলাফল। বদলে সে দুটোর একটা দেয়: ব্যবহারকারীর জন্য লেখা উত্তর, অথবা একটা tool চালানোর অনুরোধ। নিজে সে কিছুই চালায় না, শুধু চায়।

দুটো কথা মাথায় রাখবেন। এক, model নিজে কিছু মনে রাখে না; শুধু ওই মুহূর্তের request-এর ভেতর যা আছে, তাই জানে। দুই, তার কাজের নির্দেশ থাকে system prompt-এ, যেটা অনেকটা চাকরির বিবরণের মতো। নিচের কোডে Brain মানে একটাই লাইন: client.messages.create। আগের পোস্টে OpenAI-এর SDK ছিল, এবার Anthropic-এর ব্যবহার করেছি। tool আর মেসেজের ফরম্যাট দুই জায়গায় আলাদা, কিন্তু চারটা অংশ একই জায়গায় বসে।

Memory: এজেন্ট এখন কী দেখতে পায়, আর কী জমিয়ে রাখে

Memory-র দুটো স্তর। প্রথমটা short-term: প্রতিবার model-কে যে message history পাঠান, অর্থাৎ ব্যবহারকারীর কথা, model-এর উত্তর আর tool-এর ফলাফল। দ্বিতীয়টা long-term: যা storage-এ লিখে রাখা হয় আর পরের কোনো run-এ ফিরিয়ে আনা হয়, যেমন notes ফাইল, database-এর সারি বা vector store। আগের পোস্টে বলেছিলাম শুধু chat history ফিরিয়ে দিলেই সেটা প্রকৃত memory নয়। কথাটা ঠিক, তবে ওটা ছবির অর্ধেক মাত্র।

model যেহেতু কিছুই মনে রাখে না, দুই স্তরের দায়িত্বই আপনার কোডের। নিচের উদাহরণে messages অ্যারে হলো short-term আর notes.json হলো long-term memory। প্রতিবার system prompt-এ জমানো notes ঢুকিয়ে দেওয়া হয়, তাই এক সেশনে বলা পছন্দ পরের সেশনেও কাজে লাগে। মুদি দোকানের ভাষায়, messages হলো কাউন্টারের ওপর খোলা পাতা, আর notes.json হলো আলমারিতে রাখা পুরোনো খাতা।

Tools: বাইরের জগতে হাত বাড়ানো

Tool মানে একটা সাধারণ ফাংশন আর তার একটা বিবরণ। model তিনটা জিনিস দেখে: নাম, কখন ব্যবহার করতে হবে তার সহজ ভাষার ব্যাখ্যা, আর input-এর JSON Schema। আপনার কোড সে কখনোই দেখে না। শুধু বিবরণ পড়েই সিদ্ধান্ত নেয়, তাই বিবরণটা আসলে এমন এক পাঠকের জন্য লেখা API ডকুমেন্টেশন, যে কিছু জিজ্ঞেস করার সুযোগ পায় না।

model কোনো tool চাইলে tool-এর নাম আর arguments-সহ একটা structured অনুরোধ ফেরত দেয়। আপনার কোড ফাংশনটা চালায় আর ফলাফল ফেরত পাঠায়। সীমা টানার জায়গাও এটাই, কারণ এজেন্ট শুধু ততটুকুই করতে পারে যতটুকু তার tool-এ আছে। পড়ার tool লেখার tool-এর চেয়ে নিরাপদ, আর নিশ্চিত করে নেওয়ার ধাপওয়ালা tool সরাসরি কাজ করে ফেলা tool-এর চেয়ে নিরাপদ।

Action Loop: সবকিছু জোড়া লাগায় যে

Loop হলো সেই কোড, যেটা বাকি তিন অংশকে চালায়। ধাপগুলো বারবার ঘোরে:

  1. Memory Brain-কে পাঠানো হয়।
  2. Brain সাধারণ লেখা দিলে কাজ শেষ।
  3. Brain tool চাইলে loop সেটা চালায়।
  4. ফলাফল Memory-তে যোগ হয়, আর আবার ১ নম্বর ধাপ থেকে শুরু।

এই ধরনটাকে অনেকে ReAct বলে, মানে reason আর act।

Loop-এর বেরোনোর পথ লাগে: চূড়ান্ত উত্তর, ধাপের সীমা, এমন কোনো tool-এর ব্যর্থতা যা সামলানো যায় না, বা মানুষের অনুমোদন। আগের পোস্টের while (true)-তে আমার শুধু প্রথমটা ছিল। নিচের সংস্করণে ছয় ধাপের একটা সীমা যোগ করেছি; এক লাইনের এই সীমাটা দিশেহারা model-কে আপনার বিল বাড়াতে দেয় না।

একটা ছোট্ট ফাইলে চারটা অংশ

আগের পোস্টের এজেন্টের মতোই ধারণা, তবে এবার খরচের হিসাব রাখার এজেন্ট বানিয়েছি, যাতে মনে রাখার মতো কিছু থাকে আর করার মতো কাজও থাকে। চালানোর ধাপগুলো:

  1. agent/four-parts.ts নামে ফাইলটা সেভ করুন।
  2. npm install @anthropic-ai/sdk চালিয়ে SDK ইনস্টল করুন।
  3. ANTHROPIC_API_KEY সেট করুন।
  4. npx tsx agent/four-parts.ts চালান।
ts
// agent/four-parts.ts
import Anthropic from '@anthropic-ai/sdk';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';

const client = new Anthropic();
const NOTES_FILE = 'notes.json';

const messages: Anthropic.MessageParam[] = [];
const expenses: { item: string; amount: number }[] = [];

function loadNotes(): string[] {
  return existsSync(NOTES_FILE) ? JSON.parse(readFileSync(NOTES_FILE, 'utf8')) : [];
}

const tools: Anthropic.Tool[] = [
  {
    name: 'add_expense',
    description: 'Record one expense. Use it whenever the user says they spent money.',
    input_schema: {
      type: 'object',
      properties: {
        item: { type: 'string' },
        amount: { type: 'number', description: 'Amount in BDT' },
      },
      required: ['item', 'amount'],
    },
  },
  {
    name: 'get_total',
    description: 'Return the exact total of all recorded expenses in BDT.',
    input_schema: { type: 'object', properties: {} },
  },
  {
    name: 'save_note',
    description: 'Save a lasting fact about the user, such as a preference, for future sessions.',
    input_schema: {
      type: 'object',
      properties: { note: { type: 'string' } },
      required: ['note'],
    },
  },
];

const handlers: Record<string, (input: Record<string, unknown>) => unknown> = {
  add_expense: (input) => {
    expenses.push({ item: String(input.item), amount: Number(input.amount) });
    return { ok: true, recorded: expenses.length };
  },
  get_total: () => ({ total: expenses.reduce((sum, e) => sum + e.amount, 0) }),
  save_note: (input) => {
    writeFileSync(NOTES_FILE, JSON.stringify([...loadNotes(), String(input.note)]));
    return { ok: true };
  },
};

function execute(name: string, input: Record<string, unknown>): unknown {
  try {
    const handler = handlers[name];
    return handler ? handler(input) : { error: 'unknown tool' };
  } catch (err) {
    return { error: String(err) };
  }
}

async function runAgent(userText: string): Promise<string> {
  messages.push({ role: 'user', content: userText });

  for (let step = 0; step < 6; step++) {
    const response = await client.messages.create({
      model: 'claude-sonnet-5',
      max_tokens: 1024,
      system: `You track expenses. Known about the user: ${loadNotes().join('; ') || 'nothing yet'}`,
      tools,
      messages,
    });

    messages.push({ role: 'assistant', content: response.content });

    if (response.stop_reason !== 'tool_use') {
      return response.content.flatMap((b) => (b.type === 'text' ? [b.text] : [])).join('');
    }

    const results: Anthropic.ToolResultBlockParam[] = [];
    for (const block of response.content) {
      if (block.type === 'tool_use') {
        const output = execute(block.name, block.input as Record<string, unknown>);
        results.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(output) });
      }
    }
    messages.push({ role: 'user', content: results });
  }

  return 'Stopped: step limit reached';
}

async function main() {
  console.log(await runAgent('I spent 250 on lunch and 80 on a rickshaw. I prefer short answers.'));
  console.log(await runAgent('What is my total so far?'));
}

main();

কোডে চারটা অংশ চিনে নিন: Brain হলো client.messages.create কল। Memory হলো messages আর notes.json। Tools হলো tools, handlers আর execute। Action Loop হলো runAgent-এর ভেতরের for ব্লক।

একটা মেসেজ কীভাবে ভেতর দিয়ে যায়, ধাপে ধাপে দেখুন:

  1. আপনার মেসেজ messages-এ ঢোকে।
  2. Brain সংরক্ষিত notes, history আর tool-এর তালিকা পড়ে প্রতিটা খরচের জন্য add_expense চায়।
  3. Loop প্রতিটা অনুরোধ execute দিয়ে চালায় আর ফলাফল নতুন মেসেজ হিসেবে রাখে।
  4. Brain ফলাফল পড়ে দেখে আর কিছু লাগবে না, তাই লেখা উত্তর দেয়।
  5. সে save_note-ও চাইলে আপনার পছন্দ notes.json-এ থেকে যায়, আর পরের run শুরুই হয় সেই তথ্য system prompt-এ নিয়ে।

যে ভুলে আমার একটা বিকেল গিয়েছিল

খরচের এই এজেন্টের প্রথম সংস্করণ প্রতিবার সবকিছু ভুলে যেত। আমি প্রতিটা মেসেজ সামলানোর ফাংশনের ভেতরেই const messages = [] লিখে রেখেছিলাম, ফলে প্রতিবার ডাকা হলেই history খালি থেকে শুরু হচ্ছিল। বললাম দুপুরে ২৫০ টাকা খরচ হয়েছে, সে লিখে রাখল; এক মিনিট পর মোট জিজ্ঞেস করতেই সে উল্টে জিজ্ঞেস করল কোন খরচের কথা বলছি। দোষ দিলাম system prompt-কে, বারবার সেটাই ঘষামাজা করলাম, শেষে চোখে পড়ল অ্যারেটা প্রতিবার নতুন করে তৈরি হচ্ছে। এক লাইন ফাংশনের বাইরে নিতেই সমস্যা মিটল। Brain ঠিকই ছিল; Memory-টা আসলে কখনো জোড়াই লাগানো হয়নি।

চারটা অংশ জোড়া লাগাতে গিয়ে সাধারণ ভুল

  • অস্পষ্ট tool বিবরণ। Brain বিবরণ পড়েই tool বাছে, তাই “খরচ-সংক্রান্ত কাজ করে” লিখলে সে আন্দাজে চলে। tool কী করে, কী input লাগে আর কখন ব্যবহার করতে হবে, স্পষ্ট লিখে দিন।
  • পুরো history চিরকাল পাঠানো। প্রতিটা ধাপে সবকিছু আবার পাঠাতে হয়, ফলে খরচ আর দেরি বাড়ে, আর পুরোনো খুঁটিনাটি এখনকার কাজকে ঢেকে দিতে পারে। পুরোনো মেসেজ ছেঁটে বা সারাংশ করে রাখুন, আর স্থায়ী তথ্য আলাদা জায়গায় জমান।
  • tool-এর error চেপে যাওয়া। tool ব্যর্থ হলে আপনার কোড যদি কিছুই ফেরত না দেয়, Brain ভুল শোধরাতে পারে না। execute-এর মতো করে error-এর লেখাটাই ফলাফল হিসেবে ফেরত দিন, model আবার চেষ্টা করবে।
  • যে কাজ কোড পারে সেটা Brain-কে দিয়ে করানো। prompt-এর ভেতরে অনেকগুলো অঙ্ক যোগ করালে হিসাবে ভুল ঢুকতে পারে। তাই get_total একটা tool। নিখুঁত হিসাবের জন্য কোড, বিচার-বিবেচনার জন্য model।

এরপর

এই চারটা অংশের প্রতিটাই একটা সেকশনের চেয়ে বেশি আলোচনার দাবি রাখে; tool calling আর memory নিয়ে পরের কোনো পোস্টে আরও বিস্তারিত আলোচনা করব। আপাতত একটা অভ্যাস তৈরি করে নিন: নতুন কোনো এজেন্ট framework-এর কোড খুললে এই চারটা জিনিস খুঁজুন। চারটাই আঙুল দিয়ে দেখাতে পারলে সাধারণত সেটার ভেতরে পথ চিনে নিতে পারবেন।

প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী (FAQ)

একটা এআই এজেন্ট বানাতে প্রথমে কোন অংশটা তৈরি করা উচিত?

শুরু করুন Loop আর একটামাত্র tool দিয়ে। model-কে দিয়ে একটা আসল ফাংশন চাইয়ে নিন, সেটা চালান আর ফলাফল ফেরত দিন। এটা কাজ করলে তারপর Memory, তারপর আরও tool। এই ক্রমে বানালে প্রতিটা নতুন অংশ জোড়া লাগানোর মতো একটা ভিত পায়।

এজেন্টের Memory-র জন্য কি database লাগে?

শুরুতে লাগে না। Short-term memory মানে শুধু একটা message অ্যারে, আর কয়েকটা নোট রাখার জন্য একটা JSON ফাইলই যথেষ্ট। নোট ফাইলের সীমা ছাড়িয়ে গেলে, বা অর্থের ভিত্তিতে নোট খুঁজতে চাইলে তখন database বা vector store-এ যান।

এই চারটা অংশ বানাতে কি LangChain-এর মতো framework লাগে?

না। ওপরের ফাইলটাই পুরো ধরন, প্রায় একশো লাইনের কোডে। Framework এই একই চারটা অংশ প্যাকেজ করে আর কিছু সুবিধা যোগ করে; নিজে একবার কাঁচা সংস্করণটা লিখে নিলে framework বিচার করাও সহজ হয়।

img2 chatgpt
img2 chatgpt

img1 chatgpt
img1 chatgpt

thumb chatgpt
thumb chatgpt

img2 chatgpt
img2 chatgpt

img1 chatgpt
img1 chatgpt

thumb chatgpt
thumb chatgpt

🟢 ফ্রিল্যান্স ও কনট্রাক্ট প্রজেক্টের জন্য উন্মুক্ত

কাস্টম ওয়েব অ্যাপ বা এআই অটোমেশন বানাতে চান?

আমি স্টার্টআপ ও আধুনিক ব্যবসার জন্য স্কেলেবল ফুল-স্ট্যাক ওয়েব অ্যাপ্লিকেশন (Next.js, React, Node, PostgreSQL) এবং ইন্টেলিজেন্ট এআই এজেন্ট সলিউশন তৈরি করি। আপনার প্রজেক্টের আইডিয়া নিয়ে কথা বলা যাক!

ফুল-স্ট্যাক এমভিপি (MVP) ডেভেলপমেন্ট
অটোনোমাস এআই এজেন্ট ও এলএলএম ইন্টিগ্রেশন
পারফেক্ট এসইও ও সর্বোচ্চ পারফরম্যান্স
ক্লিন কোড ও মডার্ন আর্কিটেকচার

আর্টিকেলটি কি আপনার ভালো লেগেছে?

তন্ময়ের কাজকে সাপোর্ট করতে তালি (Clap) দিয়ে উৎসাহিত করুন!

0 views
Z

জাহিদ হাসান তন্ময়

সফটওয়্যার ডেভেলপার

MERN ফুল-স্ট্যাক ডেভেলপার এবং AI এজেন্ট ডেভেলপার, ঢাকা, বাংলাদেশ। ওয়েব ডেভেলপমেন্ট, রিঅ্যাক্ট, লারাভেল এবং আমার লার্নিং জার্নি নিয়ে লিখছি।

Related Articles

Stay Updated

Subscribe to get insights on full-stack architecture, AI agent engineering, and web development.