Structured Outputs in Laravel AI Applications

Structured output হলো model-কে শুধু কিছু লিখতে বলা নয়; বরং Laravel safely use করতে পারে এমন data return করতে বলা। AI feature যদি routing, storage, automation বা product decision-এ প্রভাব ফেলে, তাহলে response-এর জন্য contract দরকার: field, type, allowed value, validation rule এবং fallback path।
Explanation, draft, summary অথবা human review-এর জন্য plain text useful। কিন্তু Laravel যদি ticket classify করে, invoice field extract করে, workflow choose করে অথবা result store করে, তাহলে prose fragile হয়ে যায়। ছোট wording change parser ভেঙে দিতে পারে, uncertainty hide করতে পারে অথবা invalid value application-এ ঢুকিয়ে দিতে পারে।
Plain text কেন product workflow ভাঙে
একটি paragraph user-এর কাছে ঠিক মনে হতে পারে, কিন্তু application-এর জন্য useless হতে পারে। Model যদি বলে “this is probably urgent billing”, Laravel-কে guess করতে হয় queue-তে কোন value যাবে: `urgent`, `high`, `billing`, `payment_issue`, না অন্য কিছু। এই guess hidden business logic হয়ে যায়।
Structured output সেই guess parser থেকে সরিয়ে explicit contract-এ নিয়ে আসে। Model known field return করবে, Laravel field validate করবে, আর validation fail করলে workflow retry, fallback অথবা human review-তে যাবে।
- Human read এবং approve করলে plain text ব্যবহার করুন।
- Laravel route, store, calculate, compare অথবা workflow trigger করলে structured output ব্যবহার করুন।
- UI explanation চাইলে এবং backend validated field চাইলে দুইটি একসঙ্গে ব্যবহার করুন।
Application decision দিয়ে শুরু করুন
“Model কী JSON return করবে?”—এটা দিয়ে শুরু করবেন না। আগে ঠিক করুন Laravel কী product decision নিতে চায়। Support triage feature-এর priority, category, confidence এবং reason দরকার হতে পারে। Invoice extraction feature-এর invoice number, vendor name, due date, currency এবং line item দরকার হতে পারে।
Classification
Priority, category, sentiment, moderation state অথবা next workflow-এর মতো controlled label choose করা।
Extraction
Unstructured text, email, document অথবা support message থেকে field বের করা, তারপর external input-এর মতো validate করা।
Decision support
Recommendation, confidence এবং reason return করা; final business decision Laravel-এর কাছে রাখা।
UI structure
Frontend predictable rendering চাইলে section, bullet, table row অথবা card structure return করা।
Structured response decision-এর জন্য যতটুকু দরকার ততটুকুই হওয়া উচিত। Extra field cost, latency, validation work এবং invented detail-এর risk বাড়ায়।
Laravel validate করতে পারে এমন schema design করুন
Schema shape, type, allowed value, nullable behavior এবং uncertainty rule define করবে। ভালো schema সাধারণত boring হয়। Product-এর known choice থাকলে enum ব্যবহার করুন এবং stored/displayed free text short রাখুন।
<?php namespace App\Ai\Contracts; final class TicketTriageSchema{ public const PRIORITIES = ['low', 'normal', 'high', 'urgent']; public const CATEGORIES = ['billing', 'bug', 'account', 'feature_request', 'other']; public static function rules(): array { return [ 'priority' => ['required', 'string', 'in:low,normal,high,urgent'], 'category' => ['required', 'string', 'in:billing,bug,account,feature_request,other'], 'confidence' => ['required', 'numeric', 'between:0,1'], 'reason' => ['required', 'string', 'max:180'], ]; }}এ ধরনের schema Laravel-কে enforce করার মতো concrete rule দেয়। Model-এর জন্যও target clear হয়: এই value-গুলোর মধ্যে একটি return করো, briefly explain করো, এবং uncertainty এমন number হিসেবে দাও যেটা application reason করতে পারে।
Decoration নয়, data-এর জন্য prompt করুন
Structured output prompt-এ field, allowed value, missing information এবং unclear input হলে কী হবে—এসব explicit থাকতে হবে। শুধু “valid JSON দাও” বললে formatting define হয়, business meaning define হয় না।
$prompt = <<<PROMPTClassify this support ticket for a Laravel support queue. Allowed priorities: low, normal, high, urgent.Allowed categories: billing, bug, account, feature_request, other. Return JSON with exactly these fields:- priority- category- confidence- reason Rules:- confidence must be between 0 and 1.- If the ticket is unclear, use category "other" and confidence below 0.6.- Do not invent account state, payment status, or engineering cause.- reason must be one short sentence. Ticket:{$ticket->message}PROMPT;Prompt response contract define করে, কিন্তু Laravel-কে result parse এবং validate করতেই হবে। Model instruction ignore করতে পারে, malformed JSON return করতে পারে, unsupported enum choose করতে পারে অথবা confident কিন্তু wrong reason দিতে পারে।
Parse, validate, তারপর DTO-তে map করুন
Model output-কে external API response-এর মতো treat করুন। Carefully parse করুন, validate করুন, তারপর typed object-এ map করুন। Raw model array domain code-এর গভীরে pass করবেন না।
final class RunTicketTriage{ public function parse(string $content): ?TicketTriageResult { try { $data = json_decode($content, true, flags: JSON_THROW_ON_ERROR); } catch (JsonException) { return null; } $validator = Validator::make($data, TicketTriageSchema::rules()); if ($validator->fails()) { return null; } return new TicketTriageResult( priority: $data['priority'], category: $data['category'], confidence: (float) $data['confidence'], reason: $data['reason'], ); }}DTO শুধু clean code-এর জন্য নয়। এটি application-এর বাকি অংশকে model-specific response shape থেকে আলাদা রাখে। Provider, schema বা parser change হলেও domain workflow stable থাকতে পারে।
Confidence routing signal হিসেবে ব্যবহার করুন
Confidence truth নয়; এটি routing signal। Low-confidence classification-ও useful হতে পারে, যদি Laravel সেটিকে automation slow down, manual review অথবা safer fallback choose করার signal হিসেবে ব্যবহার করে।
if ($result === null) { return TicketTriageDecision::manualReview('Invalid AI output');} if ($result->confidence < 0.65) { return TicketTriageDecision::manualReview($result->reason);} if ($result->priority === 'urgent') { return TicketTriageDecision::escalate($result->category, $result->reason);} return TicketTriageDecision::route($result->category, $result->priority);এটাই মূল boundary: model structured suggestion return করে, আর Laravel সিদ্ধান্ত নেয় suggestion কী করতে পারবে। Confidence low হলে অথবা category sensitive business rule ছুঁলে workflow conservative হতে পারে।
Retry শুধু তখনই করুন যখন help করতে পারে
Output malformed হলে repair prompt দিয়ে retry help করতে পারে। কিন্তু ticket ambiguous হওয়ার কারণে wrong business category choose করলে একই prompt retry করা শুধু cost বাড়াতে পারে। Formatting failure এবং uncertainty failure আলাদা করুন।
Retry policy ছোট এবং observable হওয়া উচিত। First response কেন fail করল, কয়বার retry হলো এবং final output validation pass করল কিনা log করুন। না হলে structured output failure invisible cost leak হয়ে যায়।
Structured output দিয়ে authorization skip করবেন না
Structured response official মনে হতে পারে—এটাই বিপজ্জনক। Model যদি `{ "action": "refund_customer" }` return করে, তবুও Laravel-কে current user, tenant, policy, record state এবং workflow action allow করে কিনা check করতে হবে।
- Record ID use করার আগে database-এ existence এবং access validate করুন।
- কোনো record show বা change করার আগে policy check করুন।
- Allowed action-এর জন্য enum ব্যবহার করুন এবং unknown value deny করুন।
- Refund, account change অথবা destructive operation-এর জন্য human approval রাখুন।
- Database write prompt-এর বাইরে Laravel service বা job-এর ভেতরে রাখুন।
শুধু text নয়, contract log করুন
Structured output workflow চালালে logs-এ কোন contract run হলো এবং validation result কী হলো সেটি থাকা দরকার। Prompt version change, provider formatting change অথবা schema too strict হলে debugging অনেক সহজ হয়।
logger()->info('ai.ticket_triage.structured_output', [ 'ticket_id' => $ticket->id, 'schema' => 'ticket-triage:v1', 'provider' => $response->provider, 'model' => $response->model, 'input_tokens' => $response->usage?->inputTokens, 'output_tokens' => $response->usage?->outputTokens, 'validation_passed' => $result !== null, 'fallback' => $result === null ? 'manual_review' : null,]);Sensitive prompt content defaultভাবে log করবেন না। অনেক application-এ schema version, validation status, token usage, latency এবং fallback path operational visibility-এর জন্য যথেষ্ট।
Common structured output mistake
Structured output bug বেশিরভাগ সময় হয় schema-কে অতিরিক্ত শক্তিশালী ধরে নেওয়ার কারণে। Schema model ও parser-এর communication সহজ করে, কিন্তু application-এর defensive engineering এখনো দরকার।
- Allowed value define না করে শুধু JSON চাওয়া।
- Model-returned record ID existence/access verify না করে ব্যবহার করা।
- Enum-এর বদলে free-text category ব্যবহার করা।
- Parsing বা validation fail করলে fail-open করা।
- Malformed output এবং ambiguous input আলাদা না করে সব failure retry করা।
- Typed application data দরকার হলেও raw model output store করা।
- Schema field change করে log ও consumer versioning না করা।
Series-এ structured output কোথায় fit করে
Structured output prompt engineering-কে application contract-এ convert করে। এটি Laravel-কে predictable shape দেয়, যেটা parse, validate, log এবং route করা যায়। Probabilistic generation এবং deterministic application behavior-এর boundary এখানে অনেক clear হয়।
Next episode-এ আমরা AI response reliability pattern নিয়ে কাজ করব: validation, retry, fallback, repair prompt, manual review এবং Laravel কীভাবে model behavior production workflow-এর জন্য dependable করে।
এই article-এর লেখা পরিমার্জন, formatting এবং Bangla translation-এ ChatGPT সহায়তা করেছে।
