Orkas Orkas
डाउनलोड GitHub
मुखपृष्ठ ब्लॉग आर्किटेक्चर
आर्किटेक्चर

व्यवहार में कई एजेंटों का समन्वय: Orkas मुख्य एजेंट और उसके उप-एजेंटों को कैसे चलाता है

Orkas के कई एजेंटों के समन्वय के भीतर: मुख्य एजेंट एक अनुरोध को योजना में बदलता है, निर्भरताओं के अनुसार उप-एजेंटों को काम सौंपता है, चरणों के बीच संदर्भ पहुँचाता है और विफलता से उबरता है।

यह पिछला लेख एक एजेंट को भरोसेमंद बनाने के बारे में था: रन लूप, टूल रूटिंग, संदर्भ संपीड़न और क्रैश से सुरक्षित सत्र। वह स्तर इस सवाल का जवाब देता है कि "एक एजेंट बिना विफल हुए कोई काम कैसे पूरा करता है?" यह लेख उसके ऊपर के स्तर के बारे में है — जब एक एजेंट पर्याप्त न हो और काम को एक टीम.

में बाँटना पड़े, तो क्या होता है। आमतौर पर लोग इसी को कहते हैं बहु-एजेंट समन्वय: बातचीत सँभालने वाला मुख्य एजेंट अनुरोध को हिस्सों में बाँटता है, हर हिस्सा किसी विशेषज्ञ उप-एजेंट को देता है, अगला काम शुरू करने से पहले आवश्यक एजेंटों के पूरा होने का इंतज़ार करता है, परिणाम आगे पहुँचाता है और किसी चरण के विफल होने पर पूरी प्रक्रिया को सही रास्ते पर रखता है। Orkas यह सब पूरी तरह उपयोगकर्ता की अपनी मशीन पर चलाता है। नीचे बताया गया है कि समन्वय की यह परत कैसे बनी है — कोड से विशिष्ट विवरण हटाकर उसे सामान्य रूप दिया गया है, लेकिन संरचना वास्तविक है।

संक्षिप्त विवरण एक ही कार्यक्षेत्र में मुख्य एजेंट और उसके उप-एजेंट Orkas आज इस तरह काम बाँटता है: Commander विशेषज्ञों को बुलाता है, उन्हें समानांतर या क्रम से चलाता है, और आप यह सब होते हुए देखते हैं।
Orkas डाउनलोड करें — मुफ़्त

मुख्य एजेंट और उप-एजेंट

इसे निर्देशों की स्पष्ट श्रृंखला वाली एक छोटी टीम की तरह समझें। एक मुख्य एजेंट (हम इसे commander कहते हैं) बातचीत और पूरे संदर्भ की ज़िम्मेदारी रखता है। वह सारा काम खुद नहीं करता; उसका काम तय करना है कि क्या करना है, किस क्रम में और किसके द्वारा। उप-एजेंट विशेषज्ञ होते हैं — हर एक का अपना सिस्टम प्रॉम्प्ट, अपने अनुमत टूल और अपनी स्किल होती हैं। उप-एजेंट काम के एक हिस्से में अच्छा होता है और उस हिस्से की ज़रूरत पड़ने पर उसे बुलाया जाता है।

दो बातें इसे सिर्फ़ चर्चित शब्द से आगे ले जाती हैं। पहली, उप-एजेंट और स्किल प्रथम-श्रेणी की इकाइयाँहैं, प्रॉम्प्ट की तरकीबें नहीं: उप-एजेंट एक वास्तविक, अलग से कॉन्फ़िगर किया गया एजेंट है, और उसे काम सौंपना अपने संदर्भ के साथ वास्तविक हस्तांतरण है। दूसरी, समन्वय को मॉडल की अच्छी नीयत पर नहीं छोड़ा गया है — यह एक स्पष्ट संरचना से चलता है जिसे मॉडल नहीं, सिस्टम सही रखता है। वह संरचना योजना है।

योजना एक ग्राफ़ है, स्क्रिप्ट नहीं

जब मुख्य एजेंट तय करता है कि अनुरोध के लिए एक से अधिक चरण चाहिए, तो वह एक योजनालिखता है। योजना न मनमाना गद्य है, न सीधी जाँच-सूची — यह एक छोटा निर्भरता ग्राफ़ (DAG) है। हर नोड एक चरण है, और हर चरण में वह सब कुछ होता है जिसकी समन्वयक को उसे सौंपने के लिए ज़रूरत है:

interface PlanStep {
  index: number;            // 1-based, stable, never renumbered
  title: string;            // human-readable, shown in the UI
  assignee: string;         // who runs it: "user" | "commander" | a sub-agent
  input?: string;           // the dispatch payload — a template (see below)
  wait_for?: number[];      // upstream step indexes; defaults to [index - 1]
  on_failure?: "abort_plan" | "continue" | "ask_commander";

  // --- runtime state, owned by the orchestrator, NOT written by the model ---
  status: "pending" | "in_progress" | "done" | "failed" | "skipped" | "blocked";
  output_summary?: string;  // short summary of what the step produced
  output_files?: string[];  // files the step produced
  failure_reason?: string;
}

यह wait_for फ़ील्ड ही सूची को ग्राफ़ में बदलता है। डिफ़ॉल्ट रूप से कोई चरण अपने ठीक पहले वाले चरण का इंतज़ार करता है (एक साधारण श्रृंखला), लेकिन वह कई पिछले चरणों पर निर्भरता घोषित कर सकता है — "सारांश बनाएँ" को "बाज़ार का शोध करें" और "प्रतिस्पर्धियों का सर्वेक्षण करें", दोनों का इंतज़ार हो सकता है। यह रेखा नहीं, हीरे के आकार का ढाँचा है, और समन्वयक इसे उसी तरह सँभालता है।

सच्ची स्थिति कौन तय करता है: समन्वयक, मॉडल नहीं

उस संरचना के विभाजन को फिर देखें। मॉडल पहली बार योजना लिखते समय भरता है आशय — शीर्षक, ज़िम्मेदार एजेंट, इनपुट और निर्भरताएँ। लेकिन इससे जुड़ी हर चीज़: क्रियान्वयन की स्थिति — status, output_summary, failure_reason — पूरी तरह समन्वयक के नियंत्रण में है। मॉडल एक बार योजना प्रस्तावित करता है; उसे अपने चरणों को खुद "पूरा" चिह्नित करने की अनुमति कभी नहीं मिलती।

यह विभाजन जानबूझकर किया गया है, और पूरे डिज़ाइन का सबसे महत्वपूर्ण निर्णय यही है। भाषा मॉडल खुशी-खुशी "चरण 3 पूरा" घोषित कर सकता है जबकि चरण 3 में त्रुटि आई हो, या लंबी बातचीत के बीच भूल सकता है कि कौन-से चरण अभी बाकी हैं। अगर स्थिति मॉडल के दिमाग में रहती, तो योजना वास्तविकता से भटक जाती। स्थिति को ऐसी संरचित वस्तु बनाकर जिसे सिर्फ़ निष्पादक लिखता है — और सिर्फ़ किसी चरण के वास्तव में पूरा होने पर लिखता है — योजना वास्तव में हुई घटनाओं का सटीक प्रतिबिंब बनी रहती है। मॉडल काम का ढाँचा तय करता है; रनटाइम तय करता है कि उसकी प्रगति की सच्ची स्थिति क्या है।

तैयार चरणों को काम सौंपना

योजना बन जाने के बाद एक छोटा इंजन — निष्पादक — उसे आगे बढ़ाता है। मुख्य क्रिया है "तैयार चरण ढूँढ़ें और उन्हें काम सौंपें।" कोई चरण तैयार तब होता है जब वह अभी भी pending हो और जिन चरणों का वह इंतज़ार कर रहा है, वे सभी पर्याप्त रूप से सफल अंतिम स्थिति तक पहुँच चुके हों:

function findReadySteps(plan): PlanStep[] {
  return plan.steps.filter((s) => {
    if (s.status !== "pending") return false;
    const deps = s.wait_for ?? (s.index > 1 ? [s.index - 1] : []);
    return deps.every((d) => isTerminal(plan.step(d).status)); // done or skipped
  });
}

यह टाइमर पर नहीं, घटनाओं के जवाब में चलता है। हर बार कोई चरण पूरा होता है — कोई उप-एजेंट लौटता है, commander सार-संयोजन का टर्न पूरा करता है — निष्पादक स्थिति का मिलान करता है: अभी पूरे हुए चरण का परिणाम दर्ज करता है, फिर दोबारा देखता है कि उसके कारण कौन-से चरण तैयार हुए हैं और उन्हें काम सौंपता है। समन्वय यही मिलान लूप है, जो तब तक चलता रहता है जब तक कोई चरण बाकी न रहे।

काम उसी चैट से सौंपा जाता है, किसी अलग माध्यम से नहीं

यह एक ऐसा निर्णय है जो सिस्टम को पारदर्शी रखता है: किसी चरण को सौंपने के लिए छिपे हुए RPC चैनल का उपयोग नहीं होता। यह उसी समूह बातचीत में संदेश डालता है जिसे उपयोगकर्ता देख रहा है, मुख्य एजेंट की ओर से, उप-एजेंट को @-मेंशन करते हुए। उप-एजेंट के लिए काम सौंपा जाना और चैट में संबोधित किया जाना एक ही बात है — वह बस अपना सामान्य टर्न चलाता है। पहले क्रियान्वयन रास्ते के साथ तालमेल में रखने के लिए कोई दूसरा रास्ता नहीं है।

काम पाने वाला तीन प्रकार का हो सकता है, और हर एक को काम थोड़ा अलग ढंग से सौंपा जाता है:

  • एक उप-एजेंट — सामान्य मामला। निष्पादक एजेंट के नाम से उसकी id ढूँढ़ता है और पोस्ट करता है @<agent> <rendered input> commander की ओर से। उप-एजेंट इसे ग्रहण करता है और पूरा एजेंट टर्न चलाता है।
  • उपयोगकर्ता — जब किसी चरण को सचमुच मानवीय इनपुट चाहिए, तो वह चरण एक फ़ॉर्म बन जाता है और योजना रुक जाती है (इसके बारे में नीचे और जानकारी है)। उपयोगकर्ता के जवाब देने तक आगे का कोई काम नहीं बढ़ता।
  • commander स्वयं — सार-संयोजन या निर्णय वाले चरणों के लिए ("ऊपर की सारी बातें पढ़ें और सारांश लिखें")। यह निजी रूप से सक्रिय करने वाला संकेत है, जो उपयोगकर्ता को दिखने वाला अनावश्यक संदेश पोस्ट नहीं करता; मुख्य एजेंट बस जुटाए गए संदर्भ के साथ एक टर्न लेता है।

एक चरण से अगले चरण तक संदर्भ पहुँचाना

टीम तभी उपयोगी है जब उसके सदस्यों के बीच काम आगे बढ़ता रहे। इसका माध्यम है input टेम्प्लेट। जब मुख्य एजेंट कोई चरण लिखता है, तो इनपुट कोई स्थिर स्ट्रिंग नहीं होता — वह पिछले परिणामों का संदर्भ दे सकता है, और निष्पादक काम सौंपते समय उन संदर्भों को वास्तविक सामग्री में बदलता है:

// step 3.input, as written by the lead agent
"Using the findings below, draft the launch note.\n\n{{step_1.output_summary}}"

// what the sub-agent actually receives at dispatch time
"Using the findings below, draft the launch note.\n\n- Market is growing ~20% YoY; two incumbents…"

ध्यान दें कि आगे क्या भेजा जाता है: output_summary, कोई संक्षिप्त सारांश, हर पूरे हुए चरण का — उसका पूरा वार्तालेख नहीं। यह बजट का निर्णय है, और इसके पीछे वही सोच है जो एजेंट संचालन ढाँचे में संदर्भ संपीड़न के पीछे है। अगर हर अगले चरण को उससे पहले की हर चीज़ का पूरा टोकन-दर-टोकन इतिहास मिलता, तो कुछ ही चरणों में संदर्भ बहुत बड़ा हो जाता और लागत तेज़ी से बढ़ती। सारांश हर हस्तांतरण को कम खर्चीला रखते हैं और हर उप-एजेंट को इस बात पर केंद्रित रखते हैं कि उसे पिछले चरणों से वास्तव में क्या चाहिए, बजाय यह खँगालने के कि वे वहाँ तक कैसे पहुँचे। उपयोगकर्ता का शुरुआती संदेश और संलग्न फ़ाइलें भी साथ आगे भेजी जाती हैं, इसलिए श्रृंखला में तीन चरण आगे का चरण भी मूल अनुरोध जानता है।

डिफ़ॉल्ट रूप से क्रमिक — और क्यों

आप उम्मीद कर सकते हैं कि जब कई चरण एक साथ तैयार हों — मान लें, हीरे जैसे ग्राफ़ की दो शाखाएँ — तो समन्वयक उन सभी को समानांतर चला दे। वह ऐसा कर सकता है; लेकिन आज वह सौंपता है एक समय में एक तैयार चरण, इंडेक्स के अनुसार सबसे पहले वाला, और बाकी को अगले मिलान तक इंतज़ार करने देता है। पूरी टीम को सख्ती से एक-एक करके चलाना जानबूझकर लिया गया सावधानीपूर्ण निर्णय है, और इसकी वजह साफ़ बताना ज़रूरी है।

वजह है एक साथ हो रही गतिविधियों में शुद्धता बनाए रखना। सोचिए कि दो उप-एजेंट लगभग एक ही पल पूरे होते हैं। दोनों के पूरा होने से मिलान शुरू होता है; दोनों मिलान योजना पढ़ते हैं; दोनों देखते हैं कि आगे का वही चरण अभी भी इस स्थिति में है: pending — और दोनों उसे सौंप देते हैं। अब वही चरण दो बार चलता है। इसे असंभव बनाने के लिए, किसी बातचीत में पढ़ने-बदलने-सौंपने का हर चक्र उस बातचीत के लिए अलग लॉक के तहत क्रम से चलता है:

// all state-changing paths for one conversation run under one mutex
planLock(uid, cid).runExclusive(async () => {
  const plan = await readPlan(uid, cid);
  applyOutcomeOfFinishedStep(plan);   // mark done / failed / skipped
  await dispatchReady(plan);          // dispatch the next ready step
});

लॉक सुनिश्चित करता है कि "क्या पूरा हुआ, उसे दर्ज करना" और "अगला क्या होगा, यह तय करना" एक अविभाज्य इकाई के रूप में हों, ताकि आगे का कोई चरण दो बार सौंपा ही न जा सके। इस लॉक के साथ, एक समय में एक चरण सौंपना सबसे सरल तरीका है जिसकी शुद्धता स्पष्ट है। इस आधार पर वास्तविक समानांतर वितरण जोड़ना संभव विस्तार है — लेकिन आधार क्रमबद्ध, रेस कंडीशन से मुक्त निष्पादक है, और प्राथमिकताओं का यही क्रम (पहले सही, फिर तेज़) मुख्य बात है।

जब कोई चरण गलत हो जाए

वास्तविक मशीन पर बाहरी मॉडल API के साथ चलते समय विफलता सामान्य है, और समन्वयक हर त्रुटि को एक जैसा मानने के बजाय उसे कुछ श्रेणियों में बाँटता है।

सबसे पहले वह जाँचता है कि विफलता केवल अस्थायी — कनेक्शन टूटना, अनुरोधों की दर-सीमा, कोई क्षणिक गड़बड़ी। अगर ऐसा है, और चरण ने दोबारा प्रयास करने की अपनी छोटी-सी सीमा पहले ही पूरी नहीं कर ली है, तो उसे चुपचाप वापस इस स्थिति में कर दिया जाता है: pending ताकि अगला स्थिति-मिलान उसे फिर से भेज दे। (यह व्यवस्था के ऊपर हार्नेस के अपने रन के दौरान होने वाले पुनः प्रयासों के स्तर पर काम करती है; योजना तभी चरण को दोबारा भेजती है जब एजेंट के अपने प्रयास समाप्त हो चुके हों। इसकी एक सख्त सीमा होती है, ताकि सचमुच खराब चरण अनंत चक्र में न फँसे।)

अगर विफलता वास्तविक है, तो चरण की घोषित on_failure नीति तय करती है कि टीम आगे क्या करेगी:

  • abort_plan — यह चरण इतना ज़रूरी था कि इसके बिना आगे के किसी काम का अर्थ नहीं है। इसे विफल चिह्नित करें, और यह असर आगे तक लागू करें: अभी तक लंबित हर चरण को skippedचिह्नित किया जाता है। किसी ग़ायब नींव पर आगे निर्माण करने के बजाय योजना व्यवस्थित ढंग से रुक जाती है।
  • continue — यह चरण वैकल्पिक था। इसे चिह्नित करें skipped और आगे के चरणों को ऐसे चलने दें जैसे इससे बस कोई परिणाम मिला ही न हो।
  • ask_commander (डिफ़ॉल्ट) — न आँख मूँदकर रोकें, न आँख मूँदकर जारी रखें। इसे विफल चिह्नित करें और मुख्य एजेंट को सक्रिय करें, ताकि वह देख सके कि क्या हुआ और तय करे — किसी दूसरे तरीके से पुनः प्रयास करना, वैकल्पिक रास्ता अपनाना, या रुककर उपयोगकर्ता से पूछना।

एक छठी स्थिति भी है, जिसका उल्लेख ज़रूरी है: blocked। अपने चरण पर काम करते हुए किसी उप-एजेंट को महसूस हो सकता है कि उसे ऐसी कोई चीज़ चाहिए जो केवल उपयोगकर्ता दे सकता है, और वह कोई फ़ॉर्म या सवाल सामने रख सकता है। चरण विफल नहीं होता — वह इस स्थिति में चला जाता है: blocked, और पूरी योजना ठहर जाती है। जैसे ही उपयोगकर्ता जवाब देता है, निष्पादक स्थिति का मिलान करता है और टीम ठीक वहीं से काम आगे बढ़ाती है जहाँ उसने छोड़ा था। अवरुद्ध योजना रुकी हुई योजना है, टूटी हुई नहीं।

हर चरण एजेंट का एक पूरा रन है

यहाँ पिछले लेख से फिर कड़ी जोड़ना उपयोगी होगा। जब ऑर्केस्ट्रेटर किसी चरण को उप-एजेंट के पास भेजता है, तो वह उप-एजेंट कोई सीमित प्रक्रिया नहीं चलाता — वह चलाता है पूरा हार्नेस लूप: अपना स्ट्रीमिंग रन लूप, अपने टूल कॉल, संक्षेपण सहित अपनी संदर्भ विंडो, और अपना क्रैश-सुरक्षित सत्र। ऑर्केस्ट्रेशन स्पष्ट रूप से स्थित है इसके ऊपर: एकल-एजेंट रनटाइम; वह कभी उसके अंदर हस्तक्षेप नहीं करता। मुख्य एजेंट काम का स्वरूप और हस्तांतरणों का क्रम तय करता है; हर उप-एजेंट, अपना हिस्सा मिलने के बाद, अपने आप में एक संपूर्ण एजेंट होता है।

परतों की यही व्यवस्था दोनों लेखों को एक-दूसरे से जोड़ती है। हार्नेस एक कार्य के लिए एक एजेंट को भरोसेमंद बनाता है। ऑर्केस्ट्रेटर कई भरोसेमंद एजेंटों को ऐसी टीम में जोड़ता है जो किसी एक एजेंट के लिए बहुत बड़े या बहुत विविध कार्य को संभाल सके।

कुछ निर्णय जो अहम साबित हुए

निष्पादन की स्थिति ऑर्केस्ट्रेटर के नियंत्रण में है, मंशा मॉडल के। मॉडल योजना प्रस्तावित करता है; केवल रनटाइम ही चरणों को पूरा, विफल या छोड़ा गया चिह्नित करता है, और वह भी केवल वास्तव में कुछ घटित होने पर। यही एक सीमा योजना को मॉडल का आशावादी अनुमान बनने के बजाय वास्तविकता का सच्चा प्रतिबिंब बनाए रखती है।

काम उसी बातचीत के ज़रिए भेजें, किसी अलग माध्यम से नहीं। भेजा गया चरण बस मुख्य एजेंट की ओर से उप-एजेंट को एक संदेश होता है। निष्पादन का एक ही रास्ता, तालमेल से बाहर होने वाली कोई छिपी चीज़ नहीं, और उपयोगकर्ता उसी थ्रेड में टीम को काम करते देख सकता है जिसे वह पहले से पढ़ रहा है।

चरणों के बीच सारांश भेजें, पूरी बातचीत नहीं। हर हस्तांतरण में पिछले चरणों के परिणाम का एक छोटा सारांश होता है। इससे लंबी शृंखलाओं में संदर्भ की सीमाएँ संभालने योग्य रहती हैं और हर उप-एजेंट उस चीज़ पर केंद्रित रहता है जिसकी उसे ज़रूरत है, इस पर नहीं कि पिछला एजेंट वहाँ कैसे पहुँचा।

समानांतरता से पहले शुद्धता। हर बातचीत के लिए एक लॉक, पढ़ने-संशोधित करने-भेजने के हर चक्र को क्रमिक बनाता है, और चरण एक-एक करके भेजे जाते हैं। पूरी तरह क्रमिक संस्करण स्पष्ट रूप से दोहरे प्रेषण की प्रतिस्पर्धी स्थितियों से मुक्त है; समानांतर रूप से काम बाँटना एक अनुकूलन है, जिसे पहले से सही नींव पर जोड़ा जाना चाहिए।

समापन

Orkas की ऑर्केस्ट्रेशन परत के केंद्र में कोई अनोखा एल्गोरिदम नहीं है। इसका मूल्य कुछ सीमाओं को दृढ़ता से बनाए रखने में है: ऐसी योजना जो स्क्रिप्ट के बजाय निर्भरता ग्राफ़ हो; निष्पादन की स्थिति जिसका नियंत्रण मॉडल के बजाय रनटाइम के पास हो; काम का प्रेषण जो उसी चैट से गुज़रे जिसे उपयोगकर्ता देख रहा है; संदर्भ जो चरणों के बीच सारांश के रूप में जाए; और ऐसा निष्पादक जो समवर्ती निष्पादन से पहले शुद्धता को रखे। इनमें से हर बात अपने आप में सरल है। साथ मिलकर ये एक भरोसेमंद एजेंट को ऐसी टीम में बदल देती हैं जो काम बाँटती है, उसे आगे बढ़ाती है और किसी हिस्से में गड़बड़ी होने पर सँभल जाती है।

अगर आप इसके नीचे की परत जानना चाहते हैं, तो पढ़ें एक एजेंट को भरोसेमंद ढंग से चलाने के लिए कैसे बनाया जाता है। अगर आप वह परत जानना चाहते हैं जो उपयोग के साथ हर एजेंट को बेहतर बनाती है, तो पढ़ें Orkas एजेंट अपने काम से कैसे सीखते हैं। और अगर आप इस परत को बनाने के बजाय इसे निर्देशित करना चाहते हैं, तो Orkas इसे उपलब्ध कराता है ओपन-सोर्स AI एजेंट ऑर्केस्ट्रेशन के रूप में जो आपकी अपनी मशीन पर चलता है।