
আমাদের আগের পর্বে আমরা আমাদের প্রথম হাতে-কলমে প্রজেক্ট DevPulse-এর জন্য ৪ ধাপের রোডম্যাপ এবং TypeScript ও Zod কন্ট্রাক্ট (blueprint.ts) তৈরি করেছিলাম। কিন্তু শুধু ডেটা টাইপ আর স্কিমা লিখে রাখলেই তো এজেন্ট কাজ শুরু করবে না—এখন আমাদের দরকার এমন একটি নিরাপদ ব্যাকএন্ড রানটাইম, যা ইউজারের প্রশ্ন গ্রহণ করবে, সর্বোচ্চ ৫ ধাপের (maxSteps: 5) রিজনিং লুপ চালাবে, প্রয়োজনীয় টুল কল করবে এবং পুরো কাজের ট্রেস লগ আবার React ফ্রন্টএন্ডে ফেরত পাঠাবে।
Next.js 14 বা তার পরবর্তী ভার্সনে আলাদা কোনো Express বা Fastify সার্ভার না বানিয়েও সরাসরি 'use server' Server Action ব্যবহার করে একটি পূর্ণাঙ্গ ও টাইপ-সেফ AI Agent ব্যাকএন্ড তৈরি করা যায়। Server Action ব্যবহারের সবচেয়ে বড় সুবিধা হলো—এতে আপনার API Key ও ডেটাবেস লজিক শতভাগ সার্ভার সাইডে সুরক্ষিত থাকে, আলাদা করে ম্যানুয়াল fetch() বা REST API রাউট লেখার ঝামেলা থাকে না এবং ব্যাকএন্ড থেকে যে AgentStepTrace রিটার্ন করা হয় তার TypeScript টাইপ সরাসরি React কম্পোনেন্টে পাওয়া যায়।
অনেক ডেভেলপারই Next.js-এ কাজ করার সময় অভ্যাসবশত সবকিছুর জন্য /api/chat বা সাধারণ API Route খুলে বসেন। ওয়েবহুক রিসিভ করা বা কাস্টম Server-Sent Events (SSE) বানানোর জন্য API Route অবশ্যই কাজের (যা আমরা এই সিরিজেরই পরের দিকে দেখব), কিন্তু React UI থেকে সরাসরি এজেন্টের টুল লুপ ট্রিগার করা এবং স্টেপ-বাই-স্টেপ আউটপুট টাইপ-সেফভাবে পাওয়ার জন্য Server Action অনেক বেশি পরিচ্ছন্ন ও আধুনিক পদ্ধতি।
AI Agent ব্যাকএন্ডের জন্য কেন Next.js Server Action বেছে নেব?
একটি সাধারণ ওয়েব ফর্ম সাবমিট করা আর একটি AI Agent ব্যাকএন্ড চালানোর মধ্যে বেশ বড় পার্থক্য আছে। ইউজার যখন এজেন্টকে একটি প্রশ্ন করে, তখন ব্যাকএন্ডকে একসাথে বেশ কয়েকটি কাজ সামলাতে হয়:
১. ইউজারের পাঠানো প্রম্পট এবং সেশন আইডি (Session ID) ভ্যালিডেট করা, যাতে কেউ খালি বা অতিরিক্ত বড় টেক্সট পাঠিয়ে সার্ভারের রিসোর্স নষ্ট না করতে পারে।
২. আমাদের blueprint.ts থেকে এজেন্টের কনফিগারেশন (maxSteps: 5, temperature: 0.1, systemPrompt) লোড করা।
৩. একটি নিয়ন্ত্রিত while লুপ চালানো—যেখানে প্রতি ধাপে চেক করা হবে এজেন্ট কি এখন কোনো টুল (searchKnowledgeBase, checkIssueTracker, বা draftIssueTicket) কল করতে চায়, নাকি চূড়ান্ত উত্তর দেওয়ার মতো যথেষ্ট তথ্য পেয়ে গেছে।
৪. টুল কল করার প্রয়োজন হলে সার্ভার সাইডে সেই ফাংশনটি রান করা, Zod দিয়ে তার আর্গুমেন্ট চেক করা এবং কত মিলিসেকেন্ড সময় লাগল (durationMs) তা হিসাব রাখা।
৫. সবশেষে মূল উত্তরের পাশাপাশি প্রতিটি ধাপের বিস্তারিত লগ (AgentStepTrace[]) ফ্রন্টএন্ডে পাঠানো।
আপনি যদি এটি সাধারণ REST API দিয়ে করেন, তবে ব্যাকএন্ডের রেসপন্স ফরম্যাট একটু বদলালেই ফ্রন্টএন্ডে গিয়ে আবার ম্যানুয়ালি ইন্টারফেস আপডেট করতে হয়। কিন্তু Server Action মূলত একটি টাইপ-সেফ Remote Procedure Call (RPC)-এর মতো কাজ করে। ফলে ব্যাকএন্ডে runDevPulseAgent ফাংশনটি যা রিটার্ন করবে, ফ্রন্টএন্ড কম্পোনেন্ট কোনো অতিরিক্ত কনফিগারেশন ছাড়াই সাথে সাথে তার সঠিক টাইপ জেনে যাবে।

প্রজেক্টের ফোল্ডার স্ট্রাকচার (Phase 1)
কোড লেখার আগে চলুন আমাদের Next.js App Router প্রজেক্টের ফোল্ডার স্ট্রাকচারটি গুছিয়ে নিই, যাতে সার্ভার সাইডের টুল এবং ক্লায়েন্ট কম্পোনেন্ট কখনোই একে অপরের সাথে মিশে না যায়:
text src/ ├── app/ │ ├── actions/ │ │ └── agent.ts # 'use server' এন্ট্রিপয়েন্ট এবং এজেন্ট লুপ │ └── page.tsx # মেইন পেজ ├── components/ │ └── AgentTestConsole.tsx # 'use client' ইউআই কম্পোনেন্ট └── lib/ └── agent/ ├── blueprint.ts # পর্ব ১২-তে তৈরি করা Zod স্কিমা ও টাইপ └── tools.ts # সার্ভার-সাইড টুল রেজিস্ট্রি ও এক্সিকিউটর
এখানে tools.ts ফাইলটি শুধুমাত্র src/app/actions/agent.ts থেকে ইমপোর্ট করা হবে। এর ফলে ভবিষ্যতে আমরা যখন এখানে ডেটাবেস কুয়েরি বা প্রাইভেট টোকেন যুক্ত করব, তা কখনোই ক্লায়েন্ট ব্রাউজারের বান্ডেলে ফাঁস হবে না।
Server Action-এর ভেতরে এজেন্ট লুপ কীভাবে কাজ করে?
পরের পর্বে (পর্ব ১৪) আমরা সরাসরি OpenAI এবং Claude API-এর লাইভ কী (API Key) যুক্ত করব। কিন্তু একজন অভিজ্ঞ সফটওয়্যার ইঞ্জিনিয়ার কখনোই লাইভ LLM যুক্ত করার আগে তার ব্যাকএন্ড লুপ, Zod ভ্যালিডেশন এবং এরর হ্যান্ডলিং আনটেস্টেড রাখেন না। আগে যদি আপনি নিশ্চিত না হন যে আপনার while লুপ ঠিক ৫ ধাপে থামছে কি না এবং টুল রাউটার ঠিকমতো কাজ করছে কি না, তবে লাইভ API যুক্ত করার পর কোনো বাগ এলে আপনি বুঝতেই পারবেন না সমস্যাটা কি আপনার Next.js কোডে নাকি মডেলের প্রম্পটে!
নিচের সিকোয়েন্স ডায়াগ্রামে দেখুন আমাদের Server Action কীভাবে ক্লায়েন্ট থেকে রিকোয়েস্ট নিয়ে টুল লুপ সম্পন্ন করে:
`mermaid sequenceDiagram participant UI as React Client (AgentTestConsole) participant SA as Next.js Server Action (agent.ts) participant Z as Zod Validator (blueprint.ts) participant Loop as Bounded Agent Loop (maxSteps = 5) participant T as Tool Registry (tools.ts)
UI->>SA: কল করা হলো runDevPulseAgent(payload)
SA->>Z: ইউজার প্রম্পট ও সেশন আইডি ভ্যালিডেশন
Z-->>SA: নিরাপদ ও ভ্যালিডেটেড ইনপুট
SA->>Loop: লুপ শুরু (stepIndex = 1)
Loop->>T: টুল কল রাউটিং + Zod স্কিমা চেক
T-->>Loop: টুলের JSON আউটপুট + durationMs রিটার্ন
Loop->>Loop: AgentStepTrace সেভ ও পরবর্তী ধাপ যাচাই
Loop-->>SA: finalAnswer + steps[] + status রিটার্ন
SA-->>UI: টাইপ-সেফ সিরিয়ালাইজড JSON রেসপন্স
`
আর্কিটেকচার ফ্লো সামারি (Architecture Flow Summary): React ক্লায়েন্ট কম্পোনেন্ট সরাসরি runDevPulseAgent Server Action কল করে, যা প্রথমে Zod দিয়ে ইউজারের ইনপুট যাচাই করে এবং সার্ভারে সর্বোচ্চ ৫ ধাপের (maxSteps = 5) একটি এজেন্ট লুপ শুরু করে। প্রতিটি ধাপে এটি নির্দিষ্ট টুল কল করে, আর্গুমেন্ট ভ্যালিডেট করে, কাজের সময় ও আউটপুট AgentStepTrace-এ জমা রাখে এবং সবশেষে সম্পূর্ণ টাইপ-সেফ রেসপন্সটি UI-তে ফিরিয়ে দেয়।
ধাপ ১: সার্ভার-সাইড টুল রেজিস্ট্রি তৈরি (tools.ts)
চলুন প্রথমে src/lib/agent/tools.ts ফাইলটি তৈরি করি। এখানে আমরা আগের পর্বে বানানো searchDocsSchema, checkIssuesSchema, এবং draftTicketSchema ইমপোর্ট করে প্রতিটি টুলের জন্য একটি নিরাপদ এক্সিকিউটর ফাংশন লিখব:
`typescript // src/lib/agent/tools.ts import { searchDocsSchema, checkIssuesSchema, draftTicketSchema, type ToolName, } from './blueprint';
export interface ToolExecutionResult { ok: boolean; data: unknown; error?: string; }
export async function executeAgentTool( toolName: ToolName, rawArgs: unknown ): Promise { try { switch (toolName) { case 'searchKnowledgeBase': { const args = searchDocsSchema.parse(rawArgs); return { ok: true, data: { matchedChunks: [ { docPath: 'docs/api/rate-limits.md', score: 0.91, excerpt: 'API gateway enforces 60 requests per minute per IP. Exceeding this returns HTTP 429 with a Retry-After header.', }, ], queryUsed: args.query, topK: args.topK, }, }; }
case 'checkIssueTracker': {
const args = checkIssuesSchema.parse(rawArgs);
return {
ok: true,
data: {
keyword: args.keyword,
statusFilter: args.status,
issues: [
{
id: 'ISS-402',
title: 'Rate limiter 429 burst on concurrent token refresh',
status: 'open',
component: 'api-gateway',
},
],
},
};
}
case 'draftIssueTicket': {
const args = draftTicketSchema.parse(rawArgs);
return {
ok: true,
data: {
draftId: 'DRAFT-901',
status: 'awaiting_user_approval',
ticketPreview: args,
},
};
}
default: {
return {
ok: false,
data: null,
error: 'Unsupported tool requested by orchestrator.',
};
}
}
} catch (err) { const message = err instanceof Error ? err.message : 'Unknown tool validation error'; return { ok: false, data: null, error: message, }; } } `
এখানে একটি গুরুত্বপূর্ণ কৌশল খেয়াল করুন: executeAgentTool ফাংশনটি কিন্তু কোনো এরর হলে পুরো সার্ভার ক্র্যাশ করাচ্ছে না (throw করছে না)। বরং try/catch ব্লকের ভেতরে এররটি ধরে { ok: false, error: message } হিসেবে রিটার্ন করছে। প্রোডাকশন এজেন্টে এটি অপরিহার্য, কারণ কোনো টুলে ভুল প্যারামিটার গেলে এজেন্ট যেন সেই এরর মেসেজটি পড়ে পরের ধাপে নিজেকে সংশোধন করে নিতে পারে।
ধাপ ২: মূল Next.js Server Action তৈরি (agent.ts)
এবার আমরা লিখব আমাদের মূল ব্যাকএন্ড ফাইল src/app/actions/agent.ts। ফাইলের একদম প্রথম লাইনে 'use server' ডিরেক্টিভ থাকবে। এটি ইউজারের প্রম্পট ভ্যালিডেট করবে, maxSteps লুপ চালাবে এবং প্রতিটি ধাপের বিস্তারিত তথ্য রেকর্ড করবে:
`typescript // src/app/actions/agent.ts 'use server';
import { z } from 'zod'; import { defaultDevPulseConfig, type AgentStepTrace, type ToolName, } from '@/lib/agent/blueprint'; import { executeAgentTool } from '@/lib/agent/tools';
const userRequestSchema = z.object({ prompt: z .string() .trim() .min(4, 'Prompt must be at least 4 characters long.') .max(2000, 'Prompt exceeds maximum allowed length.'), sessionId: z.string().min(8), });
export interface AgentActionResponse { success: boolean; sessionId: string; finalAnswer: string; steps: AgentStepTrace[]; totalDurationMs: number; haltedReason: 'completed' | 'max_steps_reached' | 'validation_error'; errorMessage?: string; }
interface PlannedAction { done: boolean; thought: string; tool?: ToolName; toolInput?: unknown; finalResponse?: string; }
function decideNextStep( prompt: string, completedSteps: AgentStepTrace[] ): PlannedAction { const lower = prompt.toLowerCase();
if (completedSteps.length === 0) { if (lower.includes('429') || lower.includes('rate limit')) { return { done: false, thought: 'User asked about rate limits. Checking official documentation first.', tool: 'searchKnowledgeBase', toolInput: { query: 'API gateway 429 rate limit configuration', topK: 2 }, }; } return { done: false, thought: 'Searching documentation for relevant technical context.', tool: 'searchKnowledgeBase', toolInput: { query: prompt.slice(0, 80), topK: 3 }, }; }
if (completedSteps.length === 1 && (lower.includes('bug') || lower.includes('issue') || lower.includes('429'))) { return { done: false, thought: 'Documentation retrieved. Checking open issues in tracker for known 429 bugs.', tool: 'checkIssueTracker', toolInput: { keyword: '429', status: 'open' }, }; }
return { done: true, thought: 'Sufficient context gathered from documentation and issue tracker.', finalResponse: 'Based on the documentation (docs/api/rate-limits.md), the gateway limits traffic to 60 requests/min per IP. There is also an active open issue (ISS-402) regarding 429 bursts during concurrent token refreshes.', }; }
export async function runDevPulseAgent( rawInput: unknown ): Promise { const startTime = Date.now(); const parsed = userRequestSchema.safeParse(rawInput);
if (!parsed.success) { return { success: false, sessionId: 'invalid-session', finalAnswer: '', steps: [], totalDurationMs: Date.now() - startTime, haltedReason: 'validation_error', errorMessage: parsed.error.issues.map((i) => i.message).join('; '), }; }
const { prompt, sessionId } = parsed.data; const config = defaultDevPulseConfig; const steps: AgentStepTrace[] = []; let currentStep = 0;
while (currentStep < config.maxSteps) { currentStep += 1; const stepStart = Date.now(); const decision = decideNextStep(prompt, steps);
if (decision.done || !decision.tool) {
steps.push({
stepIndex: currentStep,
thoughtSummary: decision.thought,
durationMs: Date.now() - stepStart,
});
return {
success: true,
sessionId,
finalAnswer: decision.finalResponse ?? 'Task completed.',
steps,
totalDurationMs: Date.now() - startTime,
haltedReason: 'completed',
};
}
const toolResult = await executeAgentTool(decision.tool, decision.toolInput);
steps.push({
stepIndex: currentStep,
thoughtSummary: decision.thought,
selectedTool: decision.tool,
toolInput: decision.toolInput,
toolOutput: toolResult,
durationMs: Date.now() - stepStart,
});
}
return { success: false, sessionId, finalAnswer: 'The agent reached the maximum reasoning step limit (5) before finalizing an answer.', steps, totalDurationMs: Date.now() - startTime, haltedReason: 'max_steps_reached', }; } `
দেখুন, আমরা কীভাবে মূল while (currentStep < config.maxSteps) লুপ থেকে সিদ্ধান্ত নেওয়ার ফাংশনটিকে (decideNextStep) আলাদা রেখেছি। এর ফলে এক টাকাও API ক্রেডিট খরচ না করে আপনি লোকাল পিসিতে পুরো Server Action, মাল্টি-স্টেপ ট্রেস এবং UI রেন্ডারিং টেস্ট করে নিতে পারছেন। পরের পর্বে আমরা শুধু decideNextStep-এর ভেতরে OpenAI/Claude-এর কল বসিয়ে দেব, আর বাকি পুরো ব্যাকএন্ড আর্কিটেকচার হুবহু একই থাকবে!
ধাপ ৩: React Client Component থেকে ব্যাকএন্ড কল করা
ব্যাকএন্ড তো রেডি, এবার চলুন src/components/AgentTestConsole.tsx নামের একটি ছোট Client Component বানাই যা React-এর useTransition ব্যবহার করে আমাদের Server Action কল করবে এবং এজেন্ট ভেতরে ভেতরে কোন ধাপে কী কাজ করল তা স্ক্রিনে দেখাবে:
`tsx // src/components/AgentTestConsole.tsx 'use client';
import { useState, useTransition } from 'react'; import { runDevPulseAgent, type AgentActionResponse, } from '@/app/actions/agent';
export default function AgentTestConsole() { const [prompt, setPrompt] = useState( 'Why am I getting a 429 rate limit error, and is there an open bug for it?' ); const [result, setResult] = useState<AgentActionResponse | null>(null); const [isPending, startTransition] = useTransition();
function handleRunAgent(e: React.FormEvent) { e.preventDefault(); startTransition(async () => { const response = await runDevPulseAgent({ prompt, sessionId: 'session-local-dev-01', }); setResult(response); }); }
return ( Test Query for DevPulse Backend <textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} rows={3} className='w-full p-3 rounded-lg bg-slate-950 border border-slate-700 text-sm' /> {isPending ? 'Running Agent Loop...' : 'Execute Server Action'}
{result && (
<div className='space-y-4 pt-4 border-t border-slate-800'>
<div className='text-xs text-slate-400'>
Status: {result.haltedReason} | Total Time: {result.totalDurationMs}ms
</div>
<div className='p-4 rounded-lg bg-slate-950 border border-cyan-900/60 text-sm'>
{result.finalAnswer}
</div>
<div className='space-y-2'>
<h3 className='text-xs font-semibold uppercase tracking-wider text-slate-400'>
Execution Trace ({result.steps.length} steps)
</h3>
{result.steps.map((step) => (
<div
key={step.stepIndex}
className='p-3 rounded bg-slate-950/70 border border-slate-800 text-xs space-y-1'
>
<div className='font-medium text-cyan-400'>
Step {step.stepIndex}: {step.selectedTool ?? 'Final Synthesis'} ({step.durationMs}ms)
</div>
<div className='text-slate-300'>{step.thoughtSummary}</div>
</div>
))}
</div>
</div>
)}
</div>
); } `

Next.js Server Action নিয়ে কাজ করতে গিয়ে আমার একটি বাস্তব অভিজ্ঞতা
আমি যখন আমার বিভিন্ন Next.js প্রজেক্টে ব্যাকএন্ড মিউটেশন এবং প্রথম দিকের এজেন্ট প্রোটোটাইপগুলো সাধারণ /api রাউট থেকে সরিয়ে Server Action-এ নিয়ে আসি, তখন খুব অদ্ভুত একটা সিরিয়ালাইজেশন (Serialization) এররের মুখে পড়েছিলাম, যা ডিবাগ করতে আমার প্রায় দুই ঘণ্টা সময় চলে গিয়েছিল।
ঘটনাটা হয়েছিল এমন: আমার একটি টুল ফাংশন ডেটাবেস ও এক্সটার্নাল হেল্পার থেকে ডেটা নিয়ে এমন একটি অবজেক্ট রিটার্ন করছিল যার ভেতরে কাস্টম ক্লাস ইনস্ট্যান্স (Class Instance) এবং মেথড রয়ে গিয়েছিল। সাধারণ Express বা API Route-এ res.json() কল করলে তা অটোমেটিক্যালি মেথডগুলো বাদ দিয়ে সাধারণ JSON বানিয়ে ফেলে। কিন্তু Next.js Server Action ভেতরে ভেতরে React Flight প্রোটোকল ব্যবহার করে, যা শুধুমাত্র Plain Object, Array, String, Number ইত্যাদি সিরিয়ালাইজ করতে পারে। তার ওপর যখন আমার Zod ভ্যালিডেশন ফেইল করে সরাসরি ZodError ইনস্ট্যান্স ক্লায়েন্টে থ্রো (throw) করছিল, প্রোডাকশন বিল্ডে Next.js নিরাপত্তার কারণে আসল এরর মেসেজটি মুছে দিয়ে শুধু একটি জেনেরিক এরর ডাইজেস্ট দেখাতে শুরু করল!
সেদিন থেকেই আমি Next.js-এ এজেন্ট ব্যাকএন্ড লেখার ক্ষেত্রে দুটি নিয়ম কঠোরভাবে মেনে চলি: এক, Server Action থেকে কখনোই সরাসরি কোনো SDK ক্লাস বা Raw Error ইনস্ট্যান্স থ্রো করা যাবে না (safeParse ব্যবহার করে ফ্ল্যাট স্ট্রিং পাঠাতে হবে); এবং দুই, টুলের আউটপুট সবসময় সাধারণ JSON-compatible Plain Object হতে হবে। উপরের runDevPulseAgent কোডে দেখবেন আমরা ঠিক এই প্যাটার্নটাই ব্যবহার করেছি।
Server Action দিয়ে এজেন্ট বানানোর সময় যে ৪টি ভুল এড়িয়ে চলবেন
পরবর্তী পর্বে লাইভ LLM যুক্ত করার আগে নিশ্চিত হয়ে নিন আপনার কোডে যেন নিচের ভুলগুলো না থাকে:
১. ভুল ফাইলে 'use server' ডিরেক্টিভ বসানো
অনেকেই ভুল করে blueprint.ts বা স্কিমা ফাইলের উপরে 'use server' লিখে ফেলেন। মনে রাখবেন, কোনো ফাইলের শুরুতে 'use server' লেখা মানেই Next.js ধরে নেবে ওই ফাইলের প্রতিটি এক্সপোর্ট করা জিনিস একেকটি অ্যাসিঙ্ক্রোনাস Server Action! ফলে সাধারণ Zod স্কিমা (searchDocsSchema) বা কনফিগ অবজেক্ট (defaultDevPulseConfig) ইমপোর্ট করতে গেলেই বিল্ড ফেইল করবে। 'use server' শুধুমাত্র src/app/actions/agent.ts ফাইলেই রাখবেন।
২. ক্লায়েন্ট থেকে systemPrompt বা maxSteps ইনপুট হিসেবে নেওয়া
কখনোই ক্লায়েন্ট কম্পোনেন্টকে runDevPulseAgent({ prompt, maxSteps: 50, systemPrompt: '...' }) এভাবে কনফিগারেশন পাঠানোর সুযোগ দেবেন না। যেকোনো ইউজার ব্রাউজারের DevTools খুলে Server Action-এর পেলোড এডিট করে আপনার এজেন্টের আচরণ বদলে দিতে পারে বা maxSteps বাড়িয়ে আপনার API ক্রেডিট শেষ করে দিতে পারে। কনফিগারেশন সবসময় সার্ভারের ভেতরে লোড করবেন।
৩. টুল লুপের ভেতরে Unhandled Exception রেখে দেওয়া
আপনার এজেন্ট যদি ১ নম্বর ধাপে ডকুমেন্টেশন সার্চ করে সফল হয়, আর ২ নম্বর ধাপে ইস্যু ট্র্যাকার কল করতে গিয়ে আনহ্যান্ডেলড এরর খায়, তবে পুরো Server Action ক্র্যাশ করবে এবং ইউজার ১ নম্বর ধাপের রেজাল্টও হারাবে। তাই সবসময় executeAgentTool-এর ভেতরে এরর ক্যাচ করে স্ট্রাকচার্ড অবজেক্ট রিটার্ন করুন।
৪. Serverless Function-এর Timeout লিমিট ভুলে যাওয়া
একটি মাল্টি-স্টেপ এজেন্ট যখন ৩-৪ বার LLM এবং ডেটাবেস কল করবে, তখন পুরো কাজ শেষ হতে ৮ থেকে ১৫ সেকেন্ড সময় লাগতে পারে। Vercel বা অন্যান্য সার্ভারলেস প্ল্যাটফর্মে ডিফল্ট টাইমআউট অনেক সময় ১০ সেকেন্ড থাকে। তাই আপনার পেজ বা অ্যাকশন রাউটে export const maxDuration = 60; যোগ করে রাখলে মাঝপথে কানেকশন কেটে যাওয়ার ভয় থাকবে না।
প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী (FAQ)
AI Agent-এর জন্য Next.js Server Action নাকি সাধারণ API Route (/api/*)—কোনটা ব্যবহার করা উচিত?
যখন আপনার React অ্যাপ্লিকেশনের ভেতর থেকেই এজেন্টকে কল করা হচ্ছে এবং আপনি কোনো ম্যানুয়াল fetch কোড না লিখে সরাসরি অ্যান্ড-টু-অ্যান্ড TypeScript টাইপ সেফটি চান, তখন Next.js Server Action সেরা। তবে যখন বাইরের কোনো সার্ভিস (যেমন Telegram Bot বা GitHub Webhook) আপনার এজেন্টকে কল করবে কিংবা কাস্টম Server-Sent Events (SSE) স্ট্রিম বানানোর প্রয়োজন হবে, তখন আমরা /api/* Route Handler ব্যবহার করব।
Next.js Server Action ব্যবহার করলে আমার OpenAI বা Anthropic API Key কি পুরোপুরি নিরাপদ থাকবে?
হ্যাঁ, শতভাগ নিরাপদ থাকবে। 'use server' দেওয়া ফাইলের কোড শুধুমাত্র সার্ভার রানটাইমে এক্সিকিউট হয় এবং কখনোই ব্রাউজারের ক্লায়েন্ট বান্ডেলে যায় না। শুধু খেয়াল রাখবেন আপনার .env ফাইলে API Key-এর নামের শুরুতে যেন কখনোই NEXT_PUBLIC_ লেখা না থাকে।
লাইভ OpenAI বা Claude API যুক্ত করার আগে আমরা কেন এই ডিটারমিনিস্টিক লুপটি তৈরি করলাম?
প্রোডাকশন সফটওয়্যার ইঞ্জিনিয়ারিংয়ে LLM যুক্ত করার আগে অর্কেস্ট্রেশন লুপ (Orchestration Plumbing) আলাদাভাবে টেস্ট করে নেওয়া অত্যন্ত জরুরি। এতে এক টাকাও API টোকেন খরচ না করে আমরা নিশ্চিত হতে পারছি যে আমাদের Zod ভ্যালিডেশন, maxSteps: 5 লিমিট, এরর হ্যান্ডলিং এবং React UI রেন্ডারিং নিখুঁতভাবে কাজ করছে। পরের পর্বে আমরা এই একই স্ট্রাকচারে সরাসরি লাইভ মডেল API বসিয়ে দেব।
কাস্টম ওয়েব অ্যাপ বা এআই অটোমেশন বানাতে চান?
আমি স্টার্টআপ ও আধুনিক ব্যবসার জন্য স্কেলেবল ফুল-স্ট্যাক ওয়েব অ্যাপ্লিকেশন (Next.js, React, Node, PostgreSQL) এবং ইন্টেলিজেন্ট এআই এজেন্ট সলিউশন তৈরি করি। আপনার প্রজেক্টের আইডিয়া নিয়ে কথা বলা যাক!
আর্টিকেলটি কি আপনার ভালো লেগেছে?
তন্ময়ের কাজকে সাপোর্ট করতে তালি (Clap) দিয়ে উৎসাহিত করুন!
জাহিদ হাসান তন্ময়
সফটওয়্যার ডেভেলপার
MERN ফুল-স্ট্যাক ডেভেলপার এবং AI এজেন্ট ডেভেলপার, ঢাকা, বাংলাদেশ। ওয়েব ডেভেলপমেন্ট, রিঅ্যাক্ট, লারাভেল এবং আমার লার্নিং জার্নি নিয়ে লিখছি।