ছোট VPS-এ Moli headless browser কীভাবে সেটআপ করবেন
ছোট VPS-এ headless Chrome চালানো কঠিন। Moli ব্যবহার করে CDP-এর মাধ্যমে এজেন্ট কানেক্ট করার উপায় জানুন। এর সীমাবদ্ধতা এবং কোন ক্ষেত্রে এটি কাজ করবে না তা বিস্তারিত দেখুন।
একটি ছোট VPS-এর জন্য উপযোগী headless browser
Moli হলো AI agent-এর জন্য একটি headless browser। এটি এতটাই ছোট যে headless Chrome যেখানে চালানো সম্ভব নয়, এমন VPS-এও এটি self-host করা যায়। এটি Rust-এ লেখা একটি browser engine, Chromium-এর wrapper নয়। এটি Chrome DevTools Protocol (CDP)-এর অনুরোধ গ্রহণ করে। আপনার automation library ইতিমধ্যে এই protocol ব্যবহার করে। একটি binary install করুন, moli serve চালান, তারপর Playwright বা আপনার নিজস্ব agent code-কে http://127.0.0.1:9222-এ নির্দেশ করুন।
কিছু install করার আগে সীমাবদ্ধতাগুলো পড়ুন। প্রকল্পটি নিজের scope স্পষ্টভাবে উল্লেখ করেছে: কোনো GUI browser নেই, কোনো GPU compositor নেই, Chrome-এর সঙ্গে pixel-for-pixel parity নেই, এবং Canvas বা media playback-এর high fidelity নেই। যেসব page-এর জন্য এগুলো প্রয়োজন, সেগুলো ব্যর্থ হবে। Playwright-এর মাধ্যমে real Chrome fallback হিসেবে থাকবে। কোন page-এর জন্য এটি প্রয়োজন, তা কীভাবে নির্ধারণ করবেন তা শেষ section-এ দেখানো হয়েছে।
নিচের প্রতিটি command প্রকল্পটির README এবং প্রকাশিত skill file থেকে নেওয়া হয়েছে এবং 20 October 2026-এ আবার যাচাই করা হয়েছে। chart-এর প্রতিটি সংখ্যা প্রকল্পটি তার নিজস্ব engine সম্পর্কে প্রকাশ করেছে; এই site থেকে নেওয়া কোনো measurement নয়। প্রতিটি chart caption-এ সেটি উল্লেখ করা হয়েছে। আপনি যদি এখনও engine নির্বাচন করে থাকেন, তাহলে VPS-এ agent-এর জন্য headless browser বিষয়ক বিস্তৃত survey-তে বিকল্পগুলো দেখুন।
হেডলেস Chrome এত বেশি memory ব্যবহার করে কেন?
Chrome একটি multi-process browser। এটি document-গুলোকে site অনুযায়ী group করে। Desktop Linux-এ full Site Isolation চালু থাকলে cross-site iframe-সহ প্রতিটি site নিজের renderer process-এ চলে। Chrome একই site-এর page-গুলোকে একটি renderer ভাগ করে চালাতে পারে। তবে কয়েকটি site-এ access করা একটি session-এ একাধিক renderer চলবেই। প্রতিটি renderer-এর নিজস্ব V8 heap এবং নিজস্ব graphics buffer থাকে। Desktop-এর জন্য এই নকশা উপযুক্ত, কারণ একটি page crash করলেও পুরো window বন্ধ হয়ে যায় না। 2 GB VPS-এ এর অর্থ হলো, আপনি যে application চালাচ্ছেন সেটির চেয়েও একটি browse step বেশি memory ব্যবহার করতে পারে।
এই project চারটি engine ব্যবহার করে 192টি mixed public URL crawl করেছে এবং ফলাফল প্রকাশ করেছে।
এই চার্টের পেছনের ডেটা
[
{
"engine": "Moli",
"useful_pages": 103,
"median_rss_mib": 73
},
{
"engine": "Chrome Headless",
"useful_pages": 101,
"median_rss_mib": 773
},
{
"engine": "Lightpanda",
"useful_pages": 85,
"median_rss_mib": 40
},
{
"engine": "Obscura",
"useful_pages": 57,
"median_rss_mib": 39
}
]Chrome Headless 101টি useful page ফেরত দিয়েছে এবং Moli 103টি ফেরত দিয়েছে। তাই এই sample-এ দুই engine web-এর প্রায় একই অংশ পড়েছে। পার্থক্যটি memory ব্যবহারে: Chrome-এর median RSS (resident set size, অর্থাৎ একটি process বাস্তবে RAM-এ ধরে রাখা memory) 773 MiB, আর Moli-এর 73 MiB। এই প্রবণতা বিশ্বাসযোগ্য, কারণ এটি process architecture-এর ফল। তবে আপনার page-গুলোর ক্ষেত্রে ঠিক একই ratio হবে, এমন ধরে নেওয়া উচিত নয়।
Median আপনাকে সমস্যায় ফেলে না। Peak memory ফেলে। 2 GB box-এর memory শেষ হয়ে গেলে kernel একটি process বেছে নিয়ে সেটি kill করে। সেই record dmesg -T অথবা journalctl -k-এ লেখা হয়:
Out of memory: Killed process 4211 (chrome) total-vm:2318936kB, anon-rss:1418324kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:3540kB oom_score_adj:0আপনার agent ওই line দেখতে পায় না। এটি এমন একটি browser দেখে, যা আর response দিচ্ছে না। সাধারণত Playwright error হিসেবে page.goto: Page crashed অথবা closed target দেখা যায়। ওই error-এ memory সম্পর্কে কিছু উল্লেখ থাকে না। তাই ছোট box-এ agent অনিয়মিতভাবে fail করলে প্রথমে OOM (out of memory) killer পরীক্ষা করুন। Peak অনুযায়ী sizing করার কাজটি agent VPS-এর জন্য RAM এবং CPU বেছে নেওয়ার কাজেরই অংশ।
একটি নির্দিষ্ট version-এ Moli binary install করুন
প্রকল্পটি GitHub releases-এ একটি shell installer এবং আগে থেকে তৈরি tarball প্রকাশ করে। 20 October 2026 অনুযায়ী সর্বশেষ release হলো 1.1.18, যা 17 October 2026-এ প্রকাশিত হয়েছে। Release দ্রুত আসে: 4 থেকে 17 October-এর মধ্যে 6টি release প্রকাশিত হয়েছে। এই guide-এ পরে উদ্ধৃত Lexbench score প্রকল্পটি 0.1.1-এ মেপেছিল, এবং README-তে অন্যান্য পরিসংখ্যানের পেছনে থাকা version-এর নাম উল্লেখ করা নেই। এগুলোকে install করা build সম্পর্কে নিশ্চয়তা হিসেবে নয়, engine-এর আনুমানিক বৈশিষ্ট্য হিসেবে বিবেচনা করুন।
Version নির্দিষ্ট করে দিন। এমন installer, যা সবসময় latest resolve করে, পরবর্তী rebuild-এ আপনার agent-কে অন্য browser engine-এ নিয়ে যাবে। Browser-এর আচরণে পরিবর্তন এমন পরিবর্তন, যা আপনি আগে থেকে schedule করতে চাইবেন, হঠাৎ আবিষ্কার করতে নয়।
Shell installer দ্রুত শুরু করার উপায়। তবে চালানোর আগে এটি পড়ে নেওয়া উচিত।
curl --proto '=https' --tlsv1.2 -fsSL \
-o /tmp/moli-installer.sh \
https://github.com/lexmount/moli/releases/download/v1.1.18/moli-installer.sh
less /tmp/moli-installer.sh
sh /tmp/moli-installer.shচালানোর আগে script পড়ুন। এটি ছোট। এটি আপনার uname -m থেকে একটি archive বেছে নেয়, তারপর ~/.local/bin-এ একটি single binary unpack করে। x86_64-এ এটি moli-x86_64-unknown-linux-gnu.tar.gz নেয়, আর Arm server-এ aarch64 archive নেয়। তাই Arm এবং x86 উভয় VPS plan-ই এতে সমর্থিত। অন্য কোথাও install করতে MOLI_INSTALL_DIR সেট করুন। Version-এর জন্য এটি কী resolve করে, তা লক্ষ্য করুন: আপনি যে script tag fetch করেছেন সেটি নয়, সর্বশেষ release। প্রথমবার দেখার জন্য এটি ঠিক আছে। তবে repeatable rebuild-এর জন্য এটি সঠিক পদ্ধতি নয়।
তাই স্থায়ী ব্যবহারের ক্ষেত্রে installer যা করে, তা নিজে হাতে করুন এবং exact archive-এর নাম উল্লেখ করুন। এভাবে binary এমন জায়গায় রাখা যায়, যেখানে system service এটি ব্যবহার করতে পারে। Download করা script shell-এ pipe করাও এড়ানো যায়।
cd /tmp
curl --proto '=https' --tlsv1.2 -fsSLO \
https://github.com/lexmount/moli/releases/download/v1.1.18/moli-x86_64-unknown-linux-gnu.tar.gz
mkdir -p moli-pkg
tar -xzf moli-x86_64-unknown-linux-gnu.tar.gz -C moli-pkg --strip-components=1
sudo install -m 0755 moli-pkg/moli /usr/local/bin/moli
moli --versionmoli --version দিয়ে নির্দিষ্ট version print করাই সম্পূর্ণ যাচাই। Installer-এর ঠিক পরে moli: command not found চালালে বোঝা যায় যে install directory আপনার PATH-এ নেই। Installer যে directory যোগ করতে হবে, তার নামসহ একটি line print করে।
moli fetch দিয়ে ওয়ান-শট এক্সট্রাকশন
একটি এজেন্ট ব্রাউজারকে যেসব কাজ দেয়, তার বেশিরভাগই হলো "এই URL-টি লোড করো এবং কী আছে তা জানাও"। এর জন্য কোনো সার্ভারের প্রয়োজন নেই। moli fetch ইঞ্জিন চালু করে, একটি পেজ লোড করে, একটি আর্টিফ্যাক্ট স্ট্যান্ডার্ড আউটপুটে লিখে প্রস্থান করে, তাই কলগুলোর মাঝে কোনো কিছুই মেমোরি দখল করে রাখে না।
moli fetch --dump markdown --wait-until networkidle https://example.com
moli fetch --dump semantic_tree_text --wait-selector "main" https://example.com
moli fetch --dump json --wait-until networkidle https://example.com > page.jsonপ্রথমটি পেজটিকে Markdown হিসেবে প্রিন্ট করে, যা # Example Domain দিয়ে শুরু হয়। মডেলকে দেওয়ার জন্য Markdown সবচেয়ে সাশ্রয়ী, কারণ এটি মার্কআপ বাদ দিয়ে শুধু টেক্সট রাখে। semantic_tree_text রোল এবং কাঠামো বজায় রাখে, যা নেভিগেশন-নির্ভর পেজের ক্ষেত্রে প্রয়োজন, যেখানে লিঙ্কের গুরুত্ব গদ্যের মতোই সমান। --dump json HTTP স্ট্যাটাস এবং রিকোয়েস্ট ট্রেস বহন করে, তাই যখন কোনো ফেচ খালি আসে এবং কারণ জানার প্রয়োজন হয়, তখন এটি ব্যবহার করুন।
ওয়েট স্ট্র্যাটেজি নির্ধারণ করে আপনি কন্টেন্ট পাবেন নাকি একটি খালি শেল। --wait-until networkidle নেটওয়ার্ক শান্ত হলে রিটার্ন করে। --wait-until domstable DOM পরিবর্তন হওয়া বন্ধ হলে রিটার্ন করে, যা এমন পেজের জন্য ভালো পছন্দ যেখানে ব্যাকগ্রাউন্ডে পোলিং চলতে থাকে এবং নেটওয়ার্ক কখনোই পুরোপুরি শান্ত হয় না। --wait-selector আপনার দেওয়া একটি সিলেক্টরের জন্য অপেক্ষা করে। এটিই একমাত্র স্ট্র্যাটেজি যা আপনি যে পেজটি ফেচ করছেন সে সম্পর্কে জানে, ফলে যখন আপনি টার্গেট সম্পর্কে নিশ্চিত থাকেন, তখন এটিই সবচেয়ে নির্ভরযোগ্য।
স্ক্রিনশট এবং PDF-এর জন্য রিয়েল লেআউট প্রয়োজন, এবং লেআউট ডিফল্টভাবে বন্ধ থাকে:
moli fetch --layout --dump screenshot https://example.com > page.png
moli fetch --layout --dump screenshot_full https://example.com > full-page.png
moli fetch --layout --dump pdf https://example.com > page.pdfREADME-তে ডিফল্ট লেআউট পলিসিকে LayoutPolicy::Mock বলা হয়েছে: জ্যামিতি নকল করা হয় এবং কিছুই পেইন্ট করা হয় না, কারণ লেআউট এবং পেইন্ট ব্রাউজারের সবচেয়ে ব্যয়বহুল অংশ। এই ডিফল্ট সেটিংসের কারণেই উপরের মেমোরি ফিগারগুলো এমন দেখায়। এর মানে হলো, একটি খালি PNG সাধারণত একটি ভাঙা পেজের চেয়ে বরং একটি অনুপস্থিত --layout ফ্ল্যাগের কারণে হয়।
আপনার বেছে নেওয়া URL-এর পরিবর্তে এজেন্ট যে URL খুঁজে পেয়েছে তার জন্য --block-private-networks যোগ করুন। একটি এজেন্ট যা পেজে পড়া লিঙ্ক অনুসরণ করে, তাকে ভুল পথে চালিত করে ক্লাউড ইনস্ট্যান্স ক্রেডেনশিয়ালের জন্য http://169.254.169.254/ ফেচ করানো হতে পারে, অথবা localhost-এর এমন কোনো ডাটাবেস পোর্ট ফেচ করানো হতে পারে যা ইন্টারনেটের জন্য উন্মুক্ত করার কথা ছিল না। এই ফ্ল্যাগটি প্রাইভেট অ্যাড্রেস স্পেসে নেভিগেশন প্রত্যাখ্যান করে, এবং --block-cidrs এটিকে আরও সংকুচিত করে। যখন কাজটি একটি পেজ পড়ার পরিবর্তে ক্রলিং হয়, তখন সেই পাইপলাইনের গঠন self-hosted Firecrawl alternatives-এ আলোচনা করা হয়েছে, এবং এর আগের ধাপ, অর্থাৎ URL খুঁজে বের করার বিষয়টি a SearXNG-backed search skill for agents-এ কভার করা হয়েছে।
CDP-এর মাধ্যমে Moli-তে একটি এজেন্ট নির্দেশ করা
যে এজেন্ট অনেকগুলো ধাপ জুড়ে নেভিগেট করে এবং ক্লিক করে, তার জন্য পরিবর্তে সার্ভারটি চালান।
moli serve --host 127.0.0.1 --port 9222127.0.0.1 এবং port 9222 হলো ডিফল্ট, তাই একটি সাধারণ moli serve কমান্ড দিলেই তা শুধুমাত্র loopback-এ bind হয়। যেকোনো স্থায়ী কনফিগারেশনের ক্ষেত্রে উভয়ই স্পষ্টভাবে লিখে রাখুন, কারণ আপনার সার্ভিস ফাইলটি পরবর্তীতে যিনি পড়বেন, তাকে যেন ডিফল্ট মানগুলো মনে রাখতে না হয়।
ক্লায়েন্ট সংযুক্ত করার আগে সার্ভারটি পরীক্ষা করুন:
curl -s http://127.0.0.1:9222/json/versionএকটি সচল সার্ভার একটি JSON অবজেক্টের মাধ্যমে উত্তর দেয় যাতে একটি webSocketDebuggerUrl ফিল্ড থাকে, এবং সেই URL-টিই CDP ক্লায়েন্ট সংযুক্ত করার জন্য ব্যবহার করে। curl: (7) Failed to connect to 127.0.0.1 port 9222: Connection refused মানে হলো কোনো কিছু listen করছে না, তাই আপনি যে টার্মিনালে এটি শুরু করেছিলেন সেটি দেখুন, অথবা সার্ভিস হিসেবে চলার সময় journalctl -u moli -n 50 ব্যবহার করুন। /json/list ওপেন টার্গেটগুলোর তালিকা দেখায় এবং /json/protocol এই বিল্ডে বাস্তবায়িত ডোমেইনগুলোর তালিকা দেখায়, যার মাধ্যমে আপনি জানতে পারবেন আপনার প্রয়োজনীয় CDP মেথডটি এখানে আছে কি না।
Playwright নিজস্ব ব্রাউজার চালু করার পরিবর্তে সেই এন্ডপয়েন্টে সংযুক্ত হয়:
import { chromium } from "playwright";
const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();
await page.goto("https://example.com");
console.log(await page.locator("body").innerText());
await browser.close();এখানে গুরুত্বপূর্ণ লাইনটি হলো connectOverCDP, chromium.launch() নয়। এখানে কোনো চাইল্ড Chromium প্রসেস নেই, তাই executablePath এবং --no-sandbox-এর মতো সাধারণ কন্টেইনার ফ্ল্যাগগুলোর কাজ করার মতো কিছু নেই। একই কারণে, প্রক্সি, কুকি এবং ইউজার-এজেন্ট সেটিংস Moli সার্ভারের নিজস্ব ফ্ল্যাগ হিসেবে পাঠাতে হয়। সম্পূর্ণ Chrome প্রোটোকলের পরিবর্তে নির্বাচিত CDP কাভারেজ আশা করুন: একটি স্পষ্ট unsupported-method এরর হলো ইঞ্জিনের সীমাবদ্ধতা, আপনার কোডের কোনো বাগ নয়।
দুটি সার্ভার ফ্ল্যাগ নির্ধারণ করে এজেন্ট কী করতে পারবে। --layout প্রকৃত জ্যামিতি (geometry) চালু করে, যা কোঅর্ডিনেট ক্লিক এবং স্ক্রিনশটের জন্য প্রয়োজন। --resource ঐচ্ছিক ইমেজ, ফন্ট এবং মিডিয়া নিয়ে আসে; এটি প্রতিবার পেজ লোড হওয়ার সময় ব্যান্ডউইথ এবং মেমোরি খরচ করে, তাই কোনো পেজের প্রয়োজন প্রমাণিত না হওয়া পর্যন্ত এটি বন্ধ রাখুন। --profile-dir রানগুলোর মধ্যে কুকি এবং স্টোরেজ ধরে রাখে, এটি ছাড়া প্রতিটি রানই অস্থায়ী (disposable) হয়ে যায়।
এই চার্টের পেছনের ডেটা
[
{
"engine": "Moli",
"cdp_ready_ms": 34.85,
"peak_pss_mib": 102.46,
"processes": 1
},
{
"engine": "Chromium",
"cdp_ready_ms": 169.37,
"peak_pss_mib": 348.82,
"processes": 11
}
]প্রজেক্টের নমুনা এজেন্ট ওয়ার্কলোডে, Moli একটি CDP কানেকশন গ্রহণ করেছে 34.85 ms সময়ে, যেখানে Chromium-এর ক্ষেত্রে সময় লেগেছে 169.37 ms। সর্বোচ্চ PSS (proportional set size, শেয়ারড পেজগুলো প্রসেসগুলোর মধ্যে ভাগ করে মেমোরি গণনা করা) ছিল 102.46 MiB, যেখানে Chromium-এর ক্ষেত্রে তা ছিল 348.82 MiB। কাঠামোগত পার্থক্যটি শেষ কলামে দেখা যাচ্ছে: 1 টি প্রসেস, যেখানে Chromium-এর ক্ষেত্রে ছিল 11 টি। একটি প্রসেস মানে systemd-এর তত্ত্বাবধানের জন্য একটি একক বিষয় এবং cgroup-এর সীমাবদ্ধতার জন্য একটি একক ইউনিট, যা পরবর্তী সেকশনটিকে সংক্ষিপ্ত করে তুলেছে।
moli serve-কে loopback-এ systemd service হিসেবে চালানো
যখন কোনো agent-এর জন্য একটি browser প্রস্তুত থাকা প্রয়োজন, তখন server-টিকে service হিসেবে চালান। যখন প্রয়োজন নেই, তখন প্রতি URL-এর জন্য moli fetch ব্যবহার করা চালিয়ে যান, কারণ একটি idle server-ও তার memory দখল করে রাখে।
port 9222-কে public interface-এ রাখবেন না। CDP-তে কোনো ধরনের authentication ধাপ নেই। যে কেউ এই port-এ পৌঁছাতে পারলে সে browser-টিকে নিয়ন্ত্রণ করতে পারবে এবং browser যা যা দেখতে পায় তার সবকিছুই পড়তে পারবে, যার মধ্যে আপনার profile directory-র cookies-ও অন্তর্ভুক্ত। এটিকে 127.0.0.1-এ সীমাবদ্ধ রাখুন। SSH tunnel (ssh -L 9222:127.0.0.1:9222 user@your-vps) বা private VPN interface-এর মাধ্যমে অন্য কোনো machine থেকে এতে প্রবেশ করুন এবং agent-কে তার নিজের tunnel-এর দিক থেকে http://127.0.0.1:9222-এ connect করতে দিন।
একটি service user তৈরি করুন, তারপর unit file-টি তৈরি করুন:
sudo useradd --system --home-dir /var/lib/moli --shell /usr/sbin/nologin moli/etc/systemd/system/moli.service লিখুন:
[Unit]
Description=Moli headless browser CDP server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=moli
Group=moli
ExecStart=/usr/local/bin/moli serve --host 127.0.0.1 --port 9222 --profile-dir /var/lib/moli/profile --block-private-networks
Restart=on-failure
RestartSec=2
StateDirectory=moli
MemoryAccounting=yes
MemoryMax=768M
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now moli.service
systemctl status moli.service
curl -s http://127.0.0.1:9222/json/versionsystemctl status-এ active (running) দেখানো উচিত এবং curl-এর discovery JSON ফেরত দেওয়া উচিত। ProtectSystem=strict এই unit-এর জন্য পুরো filesystem-কে read-only হিসেবে mount করে, যে কারণে StateDirectory=moli এখানে ঐচ্ছিক নয়: এটি service user-এর মালিকানাধীন /var/lib/moli তৈরি করে এবং শুধুমাত্র সেই path-টিকে writable করে তোলে। যে unit শুরু হওয়ার পর journalctl -u moli-এ permission error নিয়ে বন্ধ হয়ে যায়, সেটি প্রায় সবসময়ই এমন কোথাও লেখার চেষ্টা করে যা ProtectSystem কেবল read-only করেছে, তাই সেই path-টিকে state directory-র অধীনে সরিয়ে নিন।
আপনার application-এর পাশে এটি নিরাপদে চালানোর জন্য MemoryMax=768M প্রয়োজন। unit-টি তার নিজস্ব cgroup পায় এবং যখন সেই cgroup তার সীমা অতিক্রম করে, তখন kernel তার ভেতরের কোনো কিছুকে বন্ধ করে দেয় এবং box-এর বাকি অংশকে অক্ষত রাখে। journal-এ এটি রেকর্ড হয়:
moli.service: A process of this unit has been killed by the OOM killer.এই লাইনটিকে sizing signal হিসেবে পড়ুন। হয় page-গুলো আপনার পরিকল্পনার চেয়ে বেশি ভারী, অথবা সীমাটি খুব কম। আপনার নিজের page-গুলোর পরিমাপ থেকে সংখ্যাটি নির্ধারণ করুন, যা পরবর্তী অংশে রয়েছে। একই accounting flag-গুলো box-এর অন্য যেকোনো service-কে সীমাবদ্ধ রাখে এবং capping memory and CPU with systemd বাকিগুলোর ক্ষেত্রে কাজ করে।
নিজের সার্ভারের সর্বোচ্চ মেমোরি ব্যবহার পরিমাপ করুন
প্রকাশিত পরিসংখ্যানগুলো অন্য কারো হার্ডওয়্যার এবং অন্য কারো পেজ থেকে নেওয়া হয়েছে। আপনার সার্ভার টিকে থাকবে কি না তা নির্ভর করে সর্বোচ্চ মেমোরি ব্যবহারের ওপর, আর এই ব্যবহার সম্পূর্ণভাবে নির্ভর করে আপনি কী লোড করছেন তার ওপর। তাই সার্ভারের আকার নির্ধারণের আগে পরিমাপ করুন।
একবার কোনো কিছু ফেচ (fetch) করার জন্য time বাইনারিটি ব্যবহার করুন, যা একই নামের শেল বিল্টইন কমান্ডের চেয়ে অনেক বেশি তথ্য প্রদান করে:
sudo apt update && sudo apt install -y time
/usr/bin/time -v moli fetch --dump markdown --wait-until networkidle https://example.com > /dev/nullআউটপুটটি রিসোর্স পরিসংখ্যানের একটি ব্লকের মাধ্যমে শেষ হয়, যার মধ্যে Maximum resident set size (kbytes) অন্তর্ভুক্ত থাকে। MiB-এ রূপান্তর করতে একে 1024 দিয়ে ভাগ করুন। আপনার এজেন্ট সচরাচর যে দশটি পেজ ভিজিট করে তার ওপর এটি চালান, example.com-এর ওপর নয়। গড় ফলাফলের পরিবর্তে সবচেয়ে খারাপ ফলাফলটি বিবেচনা করুন, কারণ OOM killer সর্বোচ্চ ব্যবহারের ওপর ভিত্তি করেই কাজ করে।
সার্ভিসের ক্ষেত্রে, কার্নেল তার cgroup-এর জন্য যে কাউন্টারটি রাখে তা পড়ুন:
cat /sys/fs/cgroup/system.slice/moli.service/memory.peak
systemd-cgtop -mmemory.peak হলো বাইট কাউন্ট এবং এটি ইউনিটটি সর্বশেষ চালু হওয়ার পর থেকে সর্বোচ্চ ব্যবহারের নির্দেশক (high-water mark), তাই রিস্টার্ট দিলে এটি রিসেট হয়ে যায়। এই সংখ্যাটির চেয়ে MemoryMax-এর মান বেশি হতে হবে এবং এর ওপর আপনার ভিজিট করা হয়নি এমন সবচেয়ে ভারী পেজটির জন্য পর্যাপ্ত জায়গা রাখতে হবে। systemd-cgtop -m প্রতিটি ইউনিটের বর্তমান ব্যবহার দেখায়, যা সার্ভারের কোন সার্ভিসটি বর্তমানে সবচেয়ে বেশি মেমোরি খরচ করছে তা দেখার দ্রুততম উপায়।
Moli কোথায় ব্যর্থ হয় এবং কখন আপনার এখনও Chrome প্রয়োজন?
এই প্রজেক্টটি 1,308টি তুলনামূলক ব্রাউজার অটোমেশন টাস্কের একটি বেঞ্চমার্ক চালায় এবং বেশ কয়েকটি ইঞ্জিনের স্কোর প্রকাশ করে।
এই চার্টের পেছনের ডেটা
[
{
"engine": "Chrome",
"success_rate_pct": 99.85
},
{
"engine": "Moli 0.1.1",
"success_rate_pct": 81.88
},
{
"engine": "Kitesurf",
"success_rate_pct": 62.08
},
{
"engine": "Lightpanda",
"success_rate_pct": 53.29
},
{
"engine": "Obscura",
"success_rate_pct": 44.88
}
]এই 5 টি ইঞ্জিনের মধ্যে, Moli 0.1.1 মোট টাস্কের 81.88 শতাংশ সম্পন্ন করেছে এবং রেফারেন্স ইঞ্জিন হিসেবে Chrome সম্পন্ন করেছে 99.85 শতাংশ। যেহেতু প্রজেক্টটি নিজস্ব সুইটের ওপর ভিত্তি করে এই স্কোর দিয়েছে, তাই এটিকে একটি নিরপেক্ষ ফলাফলের পরিবর্তে তাদের নিজস্ব দাবি হিসেবে দেখা উচিত।
ব্যবহারিক দিক থেকে বিষয়টি সহজ। Chrome যে কাজগুলো সম্পন্ন করতে পারে, তার মধ্যে প্রতি পাঁচটি কাজের প্রায় একটি Moli-তে ব্যর্থ হয়। যদি আপনার এজেন্ট আপনার নিয়ন্ত্রণে থাকা নির্দিষ্ট কিছু পেজ ভিজিট করে, তবে এই অনুপাতটি খুব একটা গুরুত্বপূর্ণ নয়, কারণ আপনার পেজগুলো হয় কাজ করবে অথবা করবে না—যা আপনি আজ বিকেলেই যাচাই করে নিতে পারবেন। কিন্তু যদি আপনার এজেন্ট উন্মুক্ত ওয়েব ব্রাউজ করে, তবে এটি একটি বাস্তব ব্যর্থতার হার, যার জন্য আপনাকে আগে থেকেই পরিকল্পনা করতে হবে।
প্রজেক্টের ঘোষিত পরিধি অনুযায়ী কী কী ব্যর্থ হবে তা অনুমান করা সম্ভব।
- যেসব অ্যাপ্লিকেশন তাদের ইন্টারফেস DOM-এর পরিবর্তে Canvas এলিমেন্টে ড্র করে, কারণ Canvas-এর নির্ভুলতা স্পষ্টভাবে প্রজেক্টের পরিধির বাইরে।
- WebGL বা GPU কম্পোজিটিং প্রয়োজন এমন যেকোনো কিছু, কারণ এতে কোনো GPU কম্পোজিটর নেই।
- DRM-সুরক্ষিত ভিডিও এবং জটিল মিডিয়া প্লেব্যাক।
- যেসব ভিজ্যুয়াল টেস্ট Chrome-এর সাথে পিক্সেল-নিখুঁত স্ক্রিনশট মেলানোর দাবি করে, কারণ Chrome-এর সাথে সামঞ্জস্য বজায় রাখা এর লক্ষ্য নয়।
প্রজেক্টটি যে আরেকটি পরিসংখ্যান উল্লেখ করেছে—1.612 মিলিয়ন ওয়েব প্ল্যাটফর্ম টেস্ট সফলভাবে সম্পন্ন হওয়া—তা মূলত স্ট্যান্ডার্ড কভারেজ সম্পর্কে একটি বিবৃতি। এটি আপনার এজেন্টের ভিজিট করা সাইটগুলোর জন্য কোনো নিশ্চয়তা নয়। একটি পেজ শুধুমাত্র সু-সমর্থিত স্ট্যান্ডার্ড ব্যবহার করেও বট চেকের কারণে ব্যর্থ হতে পারে, এবং কোনো ইঞ্জিনের স্কোরই তা কভার করে না।
তাই ডিজাইনে ফলব্যাক (fallback) ব্যবস্থা রাখুন। প্রতিটি URL প্রথমে Moli-তে পাঠান। যখন কোনো পেজ থেকে কোনো তথ্য না আসে বা কোনো সিলেক্টর খুঁজে পাওয়া না যায়, তখন সেই নির্দিষ্ট URL-টি Playwright ব্যবহার করে আসল Chrome-এর মাধ্যমে পুনরায় চেষ্টা করুন। এটি বড় কোনো মেশিনে অথবা এমন কোনো শিডিউলে করুন যেখানে 773 MiB প্রসেস চালানো সাশ্রয়ী। বেশিরভাগ এজেন্ট তাদের সময়ের বড় অংশ সাধারণ পেজগুলোতে ব্যয় করে, তাই ছোট ইঞ্জিনটি মূল কাজের চাপ সামলায় এবং ব্যয়বহুল ইঞ্জিনটি বাকি কাজগুলো সম্পন্ন করে।
FAQ
Moli কি আমার agent-এর জন্য headless Chrome-এর বিকল্প হতে পারে?
পেজ পড়া, text extract করা এবং সাধারণ click করার ক্ষেত্রে সাধারণত পারে। প্রকল্পটির নিজস্ব 1,308-task benchmark-এ এটি 81.88 percent task সম্পন্ন করেছে, যেখানে Chrome-এর হার 99.85 percent। অর্থাৎ প্রায় প্রতি পাঁচটির মধ্যে একটি task-এর জন্য Moli যা করতে পারে না, তা প্রয়োজন হয়। Canvas-rendered application, WebGL এবং DRM video-এর ক্ষেত্রে সীমাবদ্ধতা জানা আছে। এসব URL real Chrome-এ পাঠান; সবকিছু আবার Chrome-এ ফিরিয়ে নেবেন না।
VPS-এ Moli-এর কত RAM প্রয়োজন?
প্রকল্পের প্রতিবেদনে 192-URL crawl জুড়ে median RSS হিসেবে 73 MiB এবং একটি sample agent episode-এ peak PSS হিসেবে 102.46 MiB দেখানো হয়েছে। headless Chrome-এর median ছিল 773 MiB। এগুলো তাদের পেজে মাপা সংখ্যা। একবারের ব্যবহারে একটি moli fetch call-এর চারপাশে /usr/bin/time -v ব্যবহার করে নিজের usage মাপুন, অথবা service-এর জন্য /sys/fs/cgroup/system.slice/moli.service/memory.peak পড়ুন। এরপর দেখা সর্বোচ্চ মানের চেয়ে বেশি করে MemoryMax সেট করুন।
Port 9222 Internet-এ প্রকাশ করা কি নিরাপদ?
না। CDP-তে authentication নেই। তাই ওই port-এ পৌঁছাতে পারে এমন যে কেউ আপনার browser নিয়ন্ত্রণ করতে এবং browser যে resource-এ পৌঁছাতে পারে, সেগুলো পড়তে পারে। --host 127.0.0.1 রাখুন। অন্য machine থেকে SSH tunnel অথবা private VPN interface ব্যবহার করে endpoint-এ পৌঁছান। অন্য address-এ bind করতেই হলে private interface-এ bind করুন এবং firewall দিয়ে access নিয়ন্ত্রণ করুন।
আমার screenshot ফাঁকা কেন, অথবা click কোনো কিছুর ওপর পড়ছে না কেন?
Layout ডিফল্টভাবে বন্ধ থাকে। README-তে ডিফল্ট policy হিসেবে LayoutPolicy::Mock উল্লেখ করা হয়েছে। ফলে element geometry প্রকৃত নয়, এবং পেজের কোনো box-এর ওপর নির্ভরশীল কাজ করার মতো geometry পায় না। moli serve --layout দিয়ে server চালু করুন, অথবা moli fetch-এ --layout যোগ করুন। এরপর screenshot এবং coordinate-ভিত্তিক path কাজ করবে। Missing image-এর জন্য আলাদা flag আছে: --resource।
Moli-এর কোন version install করা উচিত?
একটি version নির্দিষ্ট করে সেটিই ব্যবহার করুন এবং কোনটি ব্যবহার করেছেন তা নথিভুক্ত করুন। 20 October 2026 অনুযায়ী newest release হলো 1.1.18, এবং নতুন release কয়েক দিন পরপর প্রকাশিত হয়। প্রকল্পটির Lexbench score 0.1.1 version-এ মাপা হয়েছিল। তাই অন্য কারও ফলাফলের সঙ্গে তুলনা করার আগে version তুলনা করুন। ওই tag-এর moli-x86_64-unknown-linux-gnu.tar.gz download করে binary নিজে install করুন। Shell installer-এর ওপর নির্ভর করবেন না, কারণ এটি আপনি যে tag fetch করেছেন সেটির পরিবর্তে newest release resolve করে। এরপর moli --version দিয়ে নিশ্চিত করুন।