VPS-এ Deer Workflow সেটআপ করার নিয়ম
VPS-এ Deer Workflow ইনস্টল করার পূর্ণাঙ্গ গাইড। Bun রানটাইম ব্যবহার করে কীভাবে systemd সার্ভিসে এজেন্ট গ্রাফ চালাবেন এবং রান টাইম এরর এড়াতে ভার্সন পিন করবেন তা এখানে জানুন।
আপনি যা তৈরি করছেন
Deer Workflow হলো এজেন্ট গ্রাফের জন্য একটি কোড-ফার্স্ট রানটাইম: এর কন্ট্রোল ফ্লো একটি TypeScript ফাইলে থাকে যা আপনি পর্যালোচনা করতে পারেন এবং একটি কোডিং এজেন্ট শুধুমাত্র সেই অংশগুলো সম্পন্ন করে যেখানে বিচারবুদ্ধির প্রয়োজন হয়। এই নির্দেশিকাটি একটি Ubuntu VPS-এ এটি ইনস্টল করবে, systemd-এর অধীনে হেডলেস মোডে একটি উদাহরণ গ্রাফ চালাবে এবং মেশিন-রিডেবল ইভেন্ট স্ট্রিমটিকে একটি লগ ফাইলে লিখবে, যাতে কোনো রান ভোর তিনটার সময় ব্যর্থ হলে আপনি তা অনুসন্ধান করতে পারেন।
এর অংশগুলো ছোট। Bun রানটাইম CLI চালায়। একটি কোডিং এজেন্ট CLI, যেমন Codex বা Claude Code, মডেলের কাজগুলো করে। একটি পিন করা npm প্যাকেজ রানটাইমটিকে ধরে রাখে। একটি TypeScript ফাইলে আপনার গ্রাফটি থাকে। একটি systemd সার্ভিস এবং টাইমার এটিকে নির্ধারিত সময়ে চালায়। এই নির্দেশিকার বেশিরভাগ অংশই সেই বিষয়গুলো নিয়ে আলোচনা করে যা সাধারণত সমস্যা তৈরি করে: systemd ইউনিটের ভেতরে PATH, লগইন শেল ছাড়া সেশনে এজেন্টের ক্রেডেনশিয়াল এবং জুলাই 2026-এ প্রথম প্রকাশিত কোনো ডিপেন্ডেন্সি পিন করা।
ভিজ্যুয়াল বিল্ডার, কোড, নাকি সরাসরি প্রম্পটিং
একজন সেলফ-হোস্টার যখন কোনো মডেলের মাধ্যমে কাজ অটোমেট করেন, তখন তিনি তিনটি পদ্ধতির যেকোনো একটি বেছে নিতে পারেন এবং প্রতিটি পদ্ধতির ব্যর্থতার ধরন ভিন্ন।
একটি ভিজ্যুয়াল বিল্ডার আপনাকে একটি ক্যানভাস, নোড লাইব্রেরি এবং এমন একটি ইউজার ইন্টারফেস দেয় যা একজন নন-প্রোগ্রামারও ব্যবহার করতে পারেন। এটি একটি বড় সুবিধা এবং এই ক্ষেত্রটি এতটাই বিস্তৃত যে self-hosted n8n alternatives-এর একটি পূর্ণাঙ্গ তালিকা রয়েছে যেখান থেকে আপনি বেছে নিতে পারেন। এর অসুবিধা হলো, লজিকটি শেষ পর্যন্ত একটি JSON ডকুমেন্ট হিসেবে জমা হয় যা UI দ্বারা তৈরি। এই ডকুমেন্টের ডিফারেন্স (diff) বোঝা কঠিন, তাই কোনো পরিবর্তন পর্যালোচনা করতে হলে প্যাচ পড়ার পরিবর্তে ক্যানভাসটি খুলতে হয়।
দ্বিতীয় পদ্ধতিটি হলো সরাসরি কোনো এজেন্টকে প্রম্পট করা। আপনি একটি অনুচ্ছেদে পুরো কাজটি বর্ণনা করেন এবং মডেলটিকে সিদ্ধান্ত নিতে দেন যে কাজের ক্রম কী হবে, কখন পুনরায় চেষ্টা (retry) করতে হবে এবং কখন থামতে হবে। এটি ততক্ষণ কাজ করে যতক্ষণ না এটি ভিন্ন কোনো সিদ্ধান্ত নেয়। এখানে কোনো ডিফারেন্স (diff) থাকে না, কারণ কোনো আর্টিফ্যাক্ট তৈরি হয় না: পরিকল্পনাটি কেবল কথোপকথনের মধ্যেই সীমাবদ্ধ থাকে এবং কথোপকথন শেষ হলে সেটিও হারিয়ে যায়।
তৃতীয় পদ্ধতিটি হলো কোডের মাধ্যমে অর্কেস্ট্রেশন। ধাপের ক্রম, ফ্যান-আউট, রিট্রাই এবং এরর হ্যান্ডলিং সবই git-এ থাকা সাধারণ TypeScript কোড। মডেলটিকে কেবল সেখানেই কল করা হয় যেখানে বিচার-বিবেচনার প্রয়োজন, অন্য কোথাও নয়। এর অসুবিধা হলো, কাউকে সেই কোড লিখতে এবং রক্ষণাবেক্ষণ করতে হয়, এবং আপনার কোনো সহকর্মী যিনি TypeScript জানেন না, তিনি এটি এডিট করতে পারেন না।
গ্রাফ রানটাইম কী সুবিধা দেয় এবং এর খরচ কী
- পর্যালোচনাযোগ্য কন্ট্রোল ফ্লো। গ্রাফটি একটি ফাইল। রিট্রাই পলিসিতে কোনো পরিবর্তন হলে তা পুল রিকোয়েস্টে তিনটি পরিবর্তিত লাইন হিসেবে দেখা যায়, কোনো সরানো বক্স হিসেবে নয়।
- ভার্সন কন্ট্রোলে ফেইলিওর হ্যান্ডলিং। চতুর্থ ধাপ ব্যর্থ হলে কী ঘটবে তা লিখে রাখা, পরীক্ষা করা এবং আপনার অন্যান্য ইনফ্রাস্ট্রাকচারের সাথে ট্যাগ করে রাখা যায়।
- পরিবর্তনযোগ্য এজেন্ট। রানটাইমটি Codex, Claude Code এবং Pi-এর জন্য অ্যাডাপ্টার সরবরাহ করে। কোনো ধাপে কোনটি কাজ করবে তা পরিবর্তন করা কেবল একটি ইমপোর্টের বিষয়।
- পর্যবেক্ষণযোগ্য এক্সিকিউশন। রানটাইম থেকে ফেজ এবং ইভেন্টগুলো স্ট্রাকচার্ড ডেটা হিসেবে বের হয়, তাই হেডলেস রান চললেও আপনি পরে তা কুয়েরি করে রেকর্ড দেখতে পারেন।
মডেল যে লুপের ভেতরে চলে তা ডিজাইন করা এবং একটি মাত্র প্রম্পটকে নিখুঁত করার চেয়ে এই সাধারণ অনুশীলনটিকে বলা হয় loop engineering, এবং গ্রাফ রানটাইম হলো এটি করার একটি বাস্তব উপায়। এর খরচ হলো সেটআপ: একটি রানটাইম ইনস্টল করা, অথেন্টিকেশনের জন্য একটি এজেন্ট CLI, নন-প্রোগ্রামারদের জন্য কোনো ইন্টারফেস না থাকা এবং একটি নতুন ডিপেন্ডেন্সি যার ওপর নজর রাখতে হয়।
প্রজেক্টটি নতুন, তাই ভার্সন পিন করে রাখুন
Deer Workflow একটি MIT লাইসেন্সপ্রাপ্ত প্রজেক্ট এবং এটি নতুন। 19 আগস্ট 2026 পর্যন্ত রিপোজিটরিতে main-এ 47টি কমিট রয়েছে। npm-এ তিনটি প্রকাশিত ভার্সন রয়েছে: 26 জুলাই 2026-এ 0.0.1 এবং 0.1.0, এবং 27 জুলাই 2026-এ 0.2.0। প্রতিটির জন্য একটি করে git tag রয়েছে এবং changelog থেকে আপনি জানতে পারবেন সেগুলোর মধ্যে কী কী পরিবর্তন হয়েছে। এর Unreleased সেকশন থেকে ইতিমধ্যে deer-workflow agent কমান্ডটি সরিয়ে ফেলা হয়েছে, তাই main এবং নতুন প্রকাশিত ভার্সনগুলোতে আগের মতো একই CLI আর পাওয়া যাবে না।
এটি প্রজেক্টটি এড়িয়ে চলার কোনো কারণ নয়। বরং এটি একটি নির্দিষ্ট ভার্সন ইনস্টল করার এবং আপনি কোনটি ইনস্টল করেছেন তা জেনে রাখার কারণ।
- সবসময় একটি নির্দিষ্ট ভার্সন ইনস্টল করুন, কোনো রেঞ্জ ব্যবহার করবেন না।
- আপনার গ্রাফগুলো যে রিপোজিটরিতে আছে, সেখানেই সেই ভার্সনটি রেকর্ড করে রাখুন।
- যেকোনো আপগ্রেডের পর, টাইমার পুনরায় চালানোর আগে নিজে একবার হাতে আপনার গ্রাফটি রান করে দেখুন।
Bun এবং একটি এজেন্ট রানটাইম ইনস্টল করা
নিচের সবকিছু sudo অধিকারসহ একজন সাধারণ ব্যবহারকারী হিসেবে চালাতে হবে। root হিসেবে এটি চালাবেন না। এজেন্ট CLI-এর ক্রেডেনশিয়ালগুলো সেই ব্যবহারকারীর হোম ডিরেক্টরিতে জমা থাকে যিনি সাইন-ইন করেছেন, এবং পরবর্তীতে systemd ইউনিটটিকেও সেই একই ব্যবহারকারী হিসেবে চলতে হবে যাতে সে ক্রেডেনশিয়ালগুলো খুঁজে পায়।
sudo apt update
sudo apt install -y curl unzip jq git nodejs npm
curl -fsSL https://bun.com/install | bashBun ইনস্টলার একটি zip আর্কাইভ আনপ্যাক করে, তাই প্রথমে unzip থাকা আবশ্যক। ইনস্টলার আপনার শেল প্রোফাইলে PATH লাইনগুলো যুক্ত করে, এবং আপনার বর্তমান শেল ইতিমধ্যে সেই ফাইলটি পড়ে ফেলেছে, তাই একটি নতুন শেল খুলুন অথবা নিজেই ~/.bashrc-এ এই দুটি লাইন যোগ করে তা রিলোড করুন।
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$HOME/.npm-global/bin:$PATH"bun --versionএটি একটি ভার্সন নম্বর প্রিন্ট করবে। bun: command not found আসার মানে হলো আপনি যে শেলে আছেন সেখানে PATH লাইনটি অনুপস্থিত, ইনস্টল ব্যর্থ হয়নি। যেকোনো কিছু পুনরায় ইনস্টল করার আগে ls ~/.bun/bin চালান।
এখন এজেন্ট রানটাইম। Codex CLI হলো ডিফল্ট, এবং এটি npm থেকে ইনস্টল হয়। একটি ইউজার-লেভেল npm প্রিফিক্স সেট করুন যাতে গ্লোবাল ইনস্টলেশনের জন্য root-এর প্রয়োজন না হয়।
npm config set prefix "$HOME/.npm-global"
npm install -g @openai/codex
command -v codex
codexcommand -v codex কমান্ডটি $HOME/.npm-global/bin-এর অধীনে একটি পাথ প্রিন্ট করবে। codex একা চালালে CLI খুলে যাবে, যেখানে আপনি আপনার ChatGPT অ্যাকাউন্ট দিয়ে সাইন-ইন করবেন। এখন একবার এটি করুন, যখন আপনি স্ক্রিনটি দেখতে পাচ্ছেন।
Claude Code একটি বিকল্প রানটাইম হিসেবে কাজ করে এবং এর নিজস্ব ইনস্টলার রয়েছে।
curl -fsSL https://claude.ai/install.sh | bash
claude --versionএকটি সফল ইনস্টলেশন 2.1.211 (Claude Code)-এর মতো একটি ভার্সন প্রিন্ট করবে। লগ ইন করার জন্য একবার claude চালান। এটি আপনার হোস্ট করা অন্য যেকোনো এজেন্টের মতোই একই ধরনের প্রসেস এবং আপনার ফাইলগুলোতে একই রকম অ্যাক্সেস রাখে, তাই VPS-এ কোডিং এজেন্ট চালানো-এর অ্যাকাউন্ট এবং হার্ডেনিং সংক্রান্ত নোটগুলো এখানেও অপরিবর্তিতভাবে প্রযোজ্য।
Deer Workflow ইনস্টল করুন এবং নির্দিষ্ট ভার্সনটি পিন করুন
bun install --global @deerwork-ai/deer-workflow@0.2.0
command -v deer-workflowcommand -v কমান্ডটি absolute path প্রিন্ট করে, যা সাধারণত /home/<your user>/.bun/bin/deer-workflow হয়। এটি কোথাও কপি করে রাখুন। systemd unit-এ শুধুমাত্র কমান্ডের নাম ব্যবহার করা যাবে না।
ইনস্টল কমান্ডে ভার্সনটি বজায় রাখুন। @0.2.0 বাদ দিলে আপনি যেদিন কমান্ডটি চালাবেন, সেদিন যে ভার্সনটি নতুন থাকবে সেটিই ইনস্টল হবে। 47টি কমিট থাকা একটি প্রজেক্টে এটি করলে CLI-এর আচরণ এমন কোনো টাইমারের অধীনে পরিবর্তিত হতে পারে যা কেউ পর্যবেক্ষণ করছে না।
গ্রাফগুলোকে একটি git repository-তে রাখুন
mkdir -p ~/workflows/logs
cd ~/workflows
git initCodex পরীক্ষা করে দেখে যে এটি কোনো git repository-এর ভেতরে চলছে কি না। এই কারণেই CodexAgentConfig-তে একটি skipGitRepositoryCheck অপশন রয়েছে, যা এমন সব ক্ষেত্রে ব্যবহার করা হয় যেখানে আপনি কোনো repository দিতে পারেন না। আপনার নিজস্ব VPS-এ আপনি এটি দিতে পারেন এবং আপনার তা করা উচিত: একটি গ্রাফ হলো কোড, আর orchestration-কে কোড হিসেবে লেখার যৌক্তিকতা তখনই থাকে যখন কোডটি version control-এর অধীনে থাকে। এখন logs ডিরেক্টরি তৈরি করুন, কারণ systemd আপনার জন্য এটি তৈরি করবে না।
একটি গ্রাফ লিখুন
একটি ওয়ার্কফ্লো হলো সাধারণ একটি TypeScript মডিউল। এটি meta এক্সপোর্ট করে, যা একটি অবজেক্ট এবং এতে নাম, বর্ণনা ও পর্যায়ক্রমিক ধাপের তালিকা থাকে। এছাড়া এটি হ্যান্ডলারকে default হিসেবে অথবা একটি নামযুক্ত run এক্সপোর্ট হিসেবে প্রদান করে। হ্যান্ডলারের ভেতরে আপনি প্যাকেজ থেকে বিভিন্ন হেল্পার কল করতে পারেন। phase() চিহ্নিত করে রানটি বর্তমানে কোন পর্যায়ে আছে, log() একটি প্রগ্রেস লাইন লেখে, agent() কোডিং এজেন্টের কাছে একটি প্রম্পট পাঠায়, parallel() একই সময়ে একাধিক টাস্কের তালিকা চালায় এবং pipeline() আইটেমের একটি তালিকাকে কয়েকটি ধাপের মধ্য দিয়ে পাঠায়।
এটি ~/workflows/log-triage.ts হিসেবে সেভ করুন।
import { agent, log, parallel, phase } from "@deerwork-ai/deer-workflow";
export const meta = {
name: "log-triage",
description: "Groups recent service errors and writes one short report.",
phases: [{ title: "Collect" }, { title: "Classify" }, { title: "Report" }],
exampleArgs: { service: "nginx", hours: 24 },
};
export default async function workflow(args: { service: string; hours: number }) {
if (!args?.service) throw new Error("input needs a service name");
phase("Collect");
log(`Reading ${args.hours}h of logs for ${args.service}`);
const found = await agent<{ patterns: string[] }>(
`Read the last ${args.hours} hours of journalctl -u ${args.service} and list the distinct error patterns.`,
{
sandbox: "read-only",
schema: {
type: "object",
properties: { patterns: { type: "array", items: { type: "string" } } },
required: ["patterns"],
additionalProperties: false,
},
},
);
phase("Classify");
log(`Classifying ${found.patterns.length} patterns`);
const notes = await parallel(
found.patterns.map((pattern) => () =>
agent(`Explain this error and its most likely cause: ${pattern}`, { sandbox: "read-only" }),
),
);
phase("Report");
return agent(`Write a short operations report from these notes: ${JSON.stringify(notes.filter(Boolean))}`);
}এই ফাইলের চারটি বিষয় অত্যন্ত গুরুত্বপূর্ণ।
agent()কলেschemaব্যবহার করলে তা স্ট্রাকচার্ড আউটপুট চায় এবং কলটি পার্স করা অবজেক্ট রিটার্ন করে।found.patternsহলো একটি প্রকৃত অ্যারে, যার ওপর গ্রাফের বাকি অংশ লুপ চালাতে পারে। কোনো স্কিমা ছাড়া,agent()একটি স্ট্রিং রিটার্ন করে এবং আপনাকে গদ্য বা টেক্সট পার্স করতে হয়।sandboxনির্ধারণ করে যে ওই ধাপটি কী কী পরিবর্তন করতে পারবে।read-onlyরাইট বা লেখা ব্লক করে,workspace-writeনিয়ন্ত্রিত রাইট করার অনুমতি দেয় এবংdanger-full-accessসেই গার্ড সরিয়ে ফেলে। এটি প্রতি কলের জন্য সেট করা হয়, তাই একটি গ্রাফ অনেক জায়গা থেকে পড়তে পারে কিন্তু নির্দিষ্ট একটি জায়গায় লিখতে পারে।parallel()ফাংশন গ্রহণ করে, প্রমিজ (promise) নয়।map((pattern) => () => agent(...))থাঙ্ক (thunk)-এর একটি তালিকা তৈরি করে, যাতে রানটাইম সিদ্ধান্ত নিতে পারে কখন কোনটি শুরু হবে। সরাসরিagent(...)পাস করলে তালিকা তৈরির মুহূর্তেই প্রতিটি কল শুরু হয়ে যাবে।parallel()-এর ভেতরে কোনো টাস্ক ব্যর্থ হলে তাnullহয়ে যায় এবং রান চলতে থাকে, কারণ ডিজাইনের নিয়ম অনুযায়ী আংশিক সম্পন্ন হওয়া গ্রহণযোগ্য। তাইnotes.filter(Boolean)কোনো সাজসজ্জা নয়: এটি বাদ দিলে একটি ব্যর্থ শাখা পরবর্তী ধাপের প্রম্পটেnullটেক্সটটি যুক্ত করে দেয়।
সাধারণ agent() হেল্পারটি ডিফল্ট রানটাইম, Codex ব্যবহার করে। কোনো একটি ধাপ Claude Code-এ পাঠাতে চাইলে, এজেন্ট ক্লাসটি ইমপোর্ট করে সরাসরি কল করুন।
import { ClaudeAgent } from "@deerwork-ai/deer-workflow";
const claude = new ClaudeAgent({ sandbox: "read-only" });
const summary = await claude.run<string>("Summarise ./report.md in five lines.");বাস্তবে একটি সোয়াপযোগ্য এজেন্ট দেখতে এমনই হয়: একটি ইমপোর্ট এবং একটি কনস্ট্রাক্টর, যার চারপাশের গ্রাফটি অপরিবর্তিত থাকে। CLI-তে থাকা --agent codex|claude|pi ফ্ল্যাগটি deer-workflow create-এর অন্তর্ভুক্ত, যা একটি বর্ণনা থেকে ওয়ার্কফ্লো ফাইল তৈরি করে। এটি deer-workflow run কোন রানটাইম ব্যবহার করবে তা পরিবর্তন করে না।
প্রথমে ম্যানুয়ালি চালান, তারপর হেডলেস মোডে
cd ~/workflows
deer-workflow run ./log-triage.ts --input '{"service":"nginx","hours":24}'ইন্টারঅ্যাক্টিভভাবে আপনি একটি টার্মিনাল ইন্টারফেস পাবেন: একদিকে meta থেকে আসা ধাপগুলো এবং অন্যদিকে লাইভ লগ। কোনো কিছু অটোমেট করার আগে এভাবে অন্তত একবার সম্পূর্ণ রান পর্যবেক্ষণ করুন। যদি এজেন্ট লগ-ইন করা না থাকে, অথবা আপনার ইনপুট হ্যান্ডলার সিগনেচারের সাথে না মেলে, তবে আপনি তা পরের সপ্তাহে লগ ফাইলে খোঁজার পরিবর্তে কয়েক সেকেন্ডের মধ্যেই দেখতে পাবেন।
অটোমেশনের জন্য, ইনপুটটিকে একটি ফাইলে স্থানান্তর করুন। ~/workflows/input.json সেভ করুন:
{ "service": "nginx", "hours": 24 }deer-workflow run ./log-triage.ts --input-file ./input.json --print >> logs/run.jsonl--print, যার সংক্ষিপ্ত রূপ -p, ইন্টারফেসটিকে বন্ধ করে দেয় এবং ইভেন্ট স্ট্রিমটিকে stdout-এ লিখে, যেখানে প্রতি লাইনে একটি করে JSON অবজেক্ট থাকে। এই মোডে stdout-এ অন্য কিছু যায় না, তাই সরাসরি একটি .jsonl ফাইলে অ্যাপেন্ড করলে আপনি এমন একটি ফাইল পাবেন যার প্রতিটি লাইন পার্স করা সম্ভব।
ইভেন্ট স্ট্রিম এবং রাত 3টার সময় যা grep করবেন
প্রতিটি লাইনে type, sequence, timestamp, workflowId, depth এবং scriptPath থাকে। এর ধরনগুলো হলো workflow:start, workflow:meta, workflow:end, workflow:error, workflow:phase:start, workflow:phase:end এবং log। ফেজ ইভেন্টগুলোতে phase থাকে, শেষ ইভেন্টগুলোতে durationMs থাকে, একটি log ইভেন্টে message থাকে এবং একটি workflow:error ইভেন্টে error-এর সাথে name, message এবং সাধারণত stack থাকে।
রাত 3টার সময় আপনার মনে আসা দুটি প্রশ্নের উত্তর দেওয়ার জন্য এই কাঠামোটিই যথেষ্ট: এটি কি শেষ হয়েছে, এবং এটি কোথায় থেমেছে।
grep workflow:error logs/run.jsonl
jq -r 'select(.type == "workflow:error") | .error.message' logs/run.jsonl
jq -r 'select(.type == "workflow:phase:end") | [.phase, .durationMs] | @tsv' logs/run.jsonl
jq -r 'select(.type == "log") | .message' logs/run.jsonlবর্তমানে চলমান কোনো রান পর্যবেক্ষণ করতে ফাইলটি অনুসরণ করুন: tail -f logs/run.jsonl | jq -c 'select(.type == "log")'। একটি রান অল্প কিছু লাইন লেখে, কিন্তু ফাইলটি ক্রমাগত বড় হতে থাকে। তাই টাইমারটি কয়েক সপ্তাহ ধরে চলতে থাকলে ~/workflows/logs/*.jsonl-এর জন্য একটি logrotate রুল যোগ করুন।
systemd-এর অধীনে চালানো
একটি দীর্ঘস্থায়ী daemon-এর পরিবর্তে একটি oneshot service এবং একটি timer ব্যবহার করুন। graph-টি চালু হয়, কাজ সম্পন্ন করে এবং বন্ধ হয়ে যায়। /etc/systemd/system/log-triage.service লিখুন এবং deploy-এর জায়গায় আপনার user-এর নাম বসান।
[Unit]
Description=Log triage workflow
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=deploy
WorkingDirectory=/home/deploy/workflows
Environment=HOME=/home/deploy
Environment=PATH=/home/deploy/.bun/bin:/home/deploy/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/deploy/.bun/bin/deer-workflow run ./log-triage.ts --input-file ./input.json --print
StandardOutput=append:/home/deploy/workflows/logs/run.jsonl
StandardError=journal
TimeoutStartSec=3600এরপর /etc/systemd/system/log-triage.timer চালান:
[Unit]
Description=Run the log triage workflow every night
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl start log-triage.service
systemctl status log-triage.service
sudo systemctl enable --now log-triage.timer
systemctl list-timers log-triage.timerপ্রথমে ম্যানুয়ালি service-টি চালু করুন। একটি সফল রান শেষে unit-টি সঠিকভাবে নিষ্ক্রিয় (deactivating) হয়ে যাবে এবং logs/run.jsonl-এ ইভেন্টের একটি ব্লক দেখা যাবে যা workflow:end দিয়ে শেষ হবে। শুধুমাত্র তখনই timer-টি enable করুন। list-timers পরবর্তী নির্ধারিত রান-এর সময় দেখাবে এবং Persistent=true-এর অর্থ হলো সার্ভার বন্ধ থাকার সময় কোনো রান মিস হলে তা পরবর্তী boot-এর সময় একবার সম্পন্ন হবে। StandardOutput=append: ইভেন্ট স্ট্রিমটিকে ফাইলে পাঠাবে এবং বাকি সবকিছুর জন্য journal ব্যবহার করবে, যাতে journalctl -u log-triage.service সহজে পড়া যায়।
আমার শেল-এ গ্রাফটি কাজ করলেও systemd-এর অধীনে কেন ব্যর্থ হয়?
এই চারটি বিষয় এই ক্রমানুসারে পরীক্ষা করুন।
ইউনিটটি বাইনারি ফাইলগুলো খুঁজে পাচ্ছে না। systemd কখনোই ~/.bashrc পড়ে না এবং এর ডিফল্ট PATH-এ ~/.bun/bin বা ~/.npm-global/bin কোনোটিই থাকে না। ইউনিটটি এক সেকেন্ডের কম সময়ে ব্যর্থ হয় এবং journalctl -u log-triage.service কমান্ডের নামে exec ব্যর্থ হওয়ার বিষয়টি দেখায়। এই কারণেই ExecStart-এ অ্যাবসোলিউট পাথ ব্যবহার করা হয় এবং Environment=PATH=-এ উভয় ডিরেক্টরি তালিকাভুক্ত থাকে: রানটাইমটিকে যখন কোনো এজেন্ট ধাপ শুরু করতে হয়, তখন তাকে অবশ্যই codex বা claude খুঁজে পেতে হয়।
এজেন্টটি তার ক্রেডেনশিয়াল খুঁজে পাচ্ছে না। এজেন্ট CLI তার লগইন তথ্য হোম ডিরেক্টরি থেকে পড়ে, তাই User= এবং Environment=HOME= স্পষ্টভাবে সেট করুন এবং যে হোম ডিরেক্টরি দিয়ে আপনি সাইন-ইন করেছেন সেটি প্রদান করুন। কোনো রান যদি workflow:start পর্যন্ত পৌঁছায় এবং তারপর এমন একটি workflow:error তৈরি করে যার বার্তাটি আপনার নিজের কোড থেকে না এসে এজেন্ট CLI থেকে আসে, তবে সেটি প্রায় সবসময়ই এই সমস্যার কারণে হয়।
90 সেকেন্ড পর রানটি বন্ধ হয়ে যাচ্ছে। Type=oneshot-এর ক্ষেত্রে, systemd পুরো কমান্ডের ওপর তার স্টার্ট টাইমআউট প্রয়োগ করে এবং ডিফল্ট সময় হলো 90 সেকেন্ড। একটি এজেন্ট গ্রাফ সম্পন্ন হতে কয়েক মিনিট সময় নিতে পারে। জার্নালটি Start operation timed out. Terminating. রেকর্ড করে, ইউনিটটি একটি ফেইলড স্টেটে শেষ হয় এবং লগ ফাইলে কোনো workflow:end ছাড়াই অর্ধেক রান জমা থাকে। TimeoutStartSec=3600 এটিকে এক ঘণ্টা সময় দেয়। যদি আপনি চান যে এটি সময়ের কারণে কখনোই বন্ধ না হোক, তবে infinity ব্যবহার করুন।
রিলেটিভ পাথ অন্য কোথাও রেজলভ হচ্ছে। ./log-triage.ts এবং ./input.json হলো WorkingDirectory-এর সাপেক্ষে। সেই লাইনটি বাদ দিলে systemd প্রসেসটিকে /-এ শুরু করে, যেখানে কোনো ফাইলই বিদ্যমান নেই।
অরকেস্ট্রেটর যা করতে পারে
একটি অরকেস্ট্রেটর যা টাইমারের মাধ্যমে এজেন্টের ধাপগুলো পরিচালনা করে, তা আপনার সার্ভারে এমন একটি প্রসেস হিসেবে কাজ করে যা কেউ পর্যবেক্ষণ করছে না। এক্ষেত্রে দুটি নিয়ন্ত্রণ ব্যবস্থা এবং একটি বাজেট গুরুত্বপূর্ণ।
প্রথম নিয়ন্ত্রণ ব্যবস্থাটি হলো প্রতিটি agent() কলের জন্য স্যান্ডবক্স। যেসব ধাপ শুধুমাত্র তথ্য পড়ে (যেমন: লগ, মেট্রিক্স বা কোনো রিপোজিটরি যা আপনি সারসংক্ষেপ করছেন), সেগুলোর জন্য read-only হলো সঠিক ডিফল্ট সেটিংস। যখন কোনো ধাপের প্রকৃতপক্ষে লেখার প্রয়োজন হয়, তখন সেটিকে workspace-write-এ স্থানান্তর করুন এবং danger-full-access ব্যবহার না করে additionalWritableDirectories-এর মাধ্যমে রাইটেবল এরিয়া বা লেখার জায়গাটিকে ছোট রাখুন।
দ্বিতীয় নিয়ন্ত্রণ ব্যবস্থাটি হলো একজন ব্যক্তি। কিছু ধাপ কখনোই মানুষের নজরদারি ছাড়া চালানো উচিত নয়: যেমন ইমেইল পাঠানো, অর্থ স্থানান্তর করা, ডেটা মুছে ফেলা বা প্রোডাকশন কনফিগারেশন পরিবর্তন করা। কোড-ফার্স্ট গ্রাফে এই গেট বসানো সহজ, কারণ প্রতিটি ধাপ একটি কোড লাইন। রান থামিয়ে দিন, প্রস্তাবিত অ্যাকশনটি রেকর্ড করুন, মানুষের উত্তরের জন্য অপেক্ষা করুন এবং তারপর পুনরায় শুরু করুন। এজেন্ট অ্যাকশনের সামনে একটি অনুমোদন গেট বসানো এই প্যাটার্নটি বিস্তারিত আলোচনা করে এবং টাইমার দ্বারা শুরু হওয়া যেকোনো গ্রাফে এটি থাকা উচিত।
বাজেট হলো অর্থ। প্রতিটি agent() কল একটি পূর্ণাঙ্গ এজেন্ট সেশন এবং parallel() একসাথে বেশ কয়েকটি সেশন শুরু করে। তাই একটি গ্রাফ যা বারোটি শাখায় বিভক্ত হয়, তা প্রতি রাতে বারোটি সেশন চালায়, কেউ রিপোর্টটি পড়ুক বা না পড়ুক। VPS-এ AI এজেন্টের খরচ নিয়ন্ত্রণে রাখা-তে উল্লিখিত পরিমাপ এবং সীমাগুলো সরাসরি একটি শিডিউল করা গ্রাফের ক্ষেত্রে প্রযোজ্য।
রানটাইম আপগ্রেড করার আগে চেঞ্জলগ পড়ুন, নতুন সঠিক ভার্সনটি ইনস্টল করুন এবং --print ব্যবহার করে হাতে কলমে একবার আপনার গ্রাফটি চালিয়ে দেখুন। এই নতুন প্রজেক্টে CLI ইন্টারফেস এখনো পরিবর্তনশীল: Unreleased সেকশনে এমন একটি কমান্ড বাদ দেওয়া হয়েছে যা 0.2.0 ভার্সনে বিদ্যমান। টাইমারের অধীনে থাকা একটি গ্রাফ ঠিক ততটাই নির্ভরযোগ্য, যতটা আপনি পিন করা ভার্সন এবং সর্বশেষ রান যা আপনি নিজে পর্যবেক্ষণ করেছেন।
FAQ
আমার কি Bun প্রয়োজন, নাকি Node.js-এ Deer Workflow চলবে?
Bun ইনস্টল করুন। প্রকাশিত প্যাকেজটি এর deer-workflow বাইনারিকে src/cli.ts-এর দিকে নির্দেশ করে, যা একটি TypeScript সোর্স ফাইল এবং ডকুমেন্টেশনে Bun-কে একটি পূর্বশর্ত হিসেবে উল্লেখ করা হয়েছে। Bun সরাসরি TypeScript এক্সিকিউট করে, তাই কোনো বিল্ড ধাপের প্রয়োজন নেই। sudo apt install -y unzip এবং এরপর curl -fsSL https://bun.com/install | bash দিয়ে এটি ইনস্টল করুন, তারপর bun --version দিয়ে নিশ্চিত করুন। আপনি যদি npm থেকে Codex CLI ইনস্টল করেন, তবে আপনার আলাদাভাবে Node.js এবং npm-এর প্রয়োজন হবে।
আমার workflow টার্মিনালে চললেও systemd-এর অধীনে কেন ব্যর্থ হয়?
প্রায় সবসময়ই এর কারণ হলো PATH, HOME অথবা স্টার্ট টাইমআউট। systemd আপনার শেল প্রোফাইল পড়ে না, তাই ExecStart-এর জন্য deer-workflow-এর অ্যাবসোলিউট পাথ প্রয়োজন এবং Environment=PATH=-এর জন্য সেই ডিরেক্টরি প্রয়োজন যেখানে codex বা claude থাকে। এজেন্ট CLI তার ক্রেডেনশিয়াল $HOME থেকে পড়ে, তাই User= এবং Environment=HOME=-কে সেই অ্যাকাউন্টে সেট করুন যেটিতে আপনি সাইন-ইন করেছেন। এছাড়া Type=oneshot ডিফল্টভাবে 90 সেকেন্ডের স্টার্ট টাইমআউট অনুসরণ করে, যা এজেন্ট রানকে মাঝপথে বন্ধ করে দেয় এবং জার্নালে Start operation timed out. Terminating. রেখে যায়, তাই TimeoutStartSec=3600 সেট করুন।
একটি ধাপের জন্য Codex-এর পরিবর্তে আমি কীভাবে Claude Code ব্যবহার করব?
সাধারণ agent() হেল্পার ডিফল্ট রানটাইম, Codex ব্যবহার করে। প্যাকেজ থেকে ClaudeAgent ইমপোর্ট করুন, এটি কনস্ট্রাক্ট করুন এবং যে ধাপগুলো আপনি Claude Code দিয়ে পরিচালনা করতে চান তার জন্য .run() কল করুন। --agent codex|claude|pi ফ্ল্যাগটি deer-workflow create-এর অন্তর্গত, যা বর্ণনা থেকে একটি workflow ফাইল তৈরি করে এবং এটি deer-workflow run-কে প্রভাবিত করে না। আপনি যে এজেন্টই ব্যবহার করুন না কেন, সেটির নিজস্ব CLI ইনস্টল করা থাকতে হবে এবং সার্ভিসটি যে ইউজার দিয়ে চলছে, সেই ইউজার হিসেবেই লগ-ইন থাকতে হবে।
আমার Deer Workflow-এর কোন সংস্করণটি ইনস্টল করা উচিত?
যেটি আপনি পরীক্ষা করেছেন ঠিক সেটিই। 19 আগস্ট 2026 অনুযায়ী সর্বশেষ প্রকাশিত সংস্করণ হলো 0.2.0, যা 27 জুলাই 2026-এ এসেছে এবং রিপোজিটরিতে 47টি কমিট রয়েছে। ইনস্টল কমান্ডে @0.2.0 বা আপনি যখন এটি পড়ছেন তখন যা বর্তমান, তা লিখুন। আপনার গ্রাফের পাশে git-এ সেই নম্বরটি রাখুন এবং প্রতিটি আপগ্রেডের পর টাইমার পুনরায় চালু হওয়ার আগে হাতে একটি গ্রাফ চালিয়ে দেখুন।