Claude-ன் tool-calling loops, Node.js-இல் சிக்கலான promise code-களை உருவாக்கும் என்ற பெயர் பெற்றுள்ளது. Node.js 22-இன் புதிய Promise.withResolvers() வசதி, டெவலப்பர்கள் அதிகப்படியான boilerplate கொண்ட new Promise முறையைத் தவிர்த்துவிட்டு, ஒரே வரியில் promise மற்றும் அதன் resolve/reject செயல்பாடுகளைப் பெற அனுமதிக்கிறது. இதன் மூலம், resolve அழைப்புகளை மறப்பது குறையும், double-reject எச்சரிக்கைகள் வராது, மேலும் serverless சூழல்களில் எளிதாகச் சோதனை செய்யவும் மற்றும் இயக்கி வைத்திருக்கவும் உதவும் ஒரு நேர்த்தியான control flow கிடைக்கும்.

பழைய முறை ஏன் செயல்பாட்டைப் பாதிக்கிறது

Claude போன்ற ஒரு LLM ஒரு tool-ஐக் கேட்கும்போது, வழக்கமான Node implementation இவ்வாறு இருக்கும்:

return new Promise((resolve, reject) => {
  // launch the tool, attach callbacks, maybe fire another async call
});

இதில் மூன்று தொடர்ச்சியான சிக்கல்கள் எழுகின்றன:

  • Forgotten resolve – ஒருவேளை code path resolve-ஐ அழைக்கவில்லை என்றால், ஒரு Lambda அல்லது பிற serverless handler timeout ஆகும் வரை காத்திருக்கும், இது செலவை அதிகரிக்கும்.
  • Double reject – ஒரு error path reject-ஐ இரண்டு முறை அழைத்தால், அது “unhandled rejection” எச்சரிக்கைகளைத் தூண்டும், இது strict mode-இல் process-ஐ முடக்கக்கூடும்.
  • Deep nesting – ஒவ்வொரு async படியும் constructor-க்குள் மற்றொரு callback-ஐத் தந்து (nesting), logic-ஐச் சிதறடித்து unit test-களைச் செய்வது கடினமாக்குகிறது.

இந்த அனைத்துப் பிரச்சனைகளும், promise-இன் control functions constructor-இன் closure-க்குள் பூட்டப்பட்டிருப்பதாலேயே ஏற்படுகின்றன; இதனால் மற்ற code அந்த functions-ஐத் தேடிச் செல்ல வேண்டிய கட்டாயம் ஏற்படுகிறது.

ஒரே வரியில் Promise.withResolvers()

Node 22, ஒரு promise மற்றும் அதைச் செயல்படுத்தும் (settle) இரண்டு functions அடங்கிய object-ஐத் திருப்பித் தரும் ஒரு static helper-ஐச் சேர்த்துள்ளது:

const { promise, resolve, reject } = Promise.withResolvers();

இப்போது அந்த promise-ஐ system-இன் எந்தப் பகுதிக்கும்—ஒரு HTTP handler, database listener அல்லது background worker—அளிக்க முடியும்; அதே நேரத்தில் அழைப்பவர் (caller) அந்த promise-க்காக await செய்யலாம். முழு tool-execution block-ஐயும் new Promise constructor-க்குள் கொண்டு வர வேண்டிய அவசியம் இல்லை.

இதை Claude-ன் tool loop-இல் பயன்படுத்துதல்

Claude-இன் workflow இவ்வாறு அமைகிறது:

  1. LLM ஒரு tool request-ஐ வெளியிடுகிறது.
  2. உங்கள் code அந்த tool-ஐ இயக்குகிறது (உதாரணமாக, ஒரு API call அல்லது file read).
  3. tool-இன் முடிவு அடுத்த சுற்றிற்காக Claude-க்குத் திருப்பி அனுப்பப்படுகிறது.

withResolvers-ஐப் பயன்படுத்தும்போது, அந்த loop இவ்வாறு சுருங்கிவிடும்:

async function runTool(request) {
  const { promise, resolve, reject } = Promise.withResolvers();

  // Kick off the tool; it can call resolve/reject from anywhere
  executeTool(request, { resolve, reject });

  // Optional timeout wrapper
  const timeout = setTimeout(() => reject(new Error('Tool timed out')), 10_000);
  try {
    const result = await promise;
    clearTimeout(timeout);
    return result;               // feed back to Claude
  } finally {
    // clean-up if needed
  }
}

Tool implementation-ஐ இனி ஒரு புதிய promise-க்குள் கொண்டு வரத் தேவையில்லை; அது நேரடியாக resolve மற்றும் reject-ஐப் பெற்றுக்கொள்ளும். இது மேலே குறிப்பிடப்பட்ட மூன்று தோல்வி முறைகளையும் நீக்குகிறது.

நடைமுறைப் பயன்பாட்டில் (Production) கவனிக்க வேண்டியவை

promise வடிவம் எளிமையாக இருந்தாலும், நிஜ உலக agents வேறு சில கட்டுப்பாடுகளைச் சந்திக்கின்றன:

  • Timeouts – மேலே உள்ள snippet, tool ஒரு குறிப்பிட்ட நேரத்தைத் (threshold) தாண்டினால் reject செய்யும் ஒரு எளிய timer-ஐக் காட்டுகிறது. SLA எதிர்பார்ப்புகளுக்கு ஏற்ப கால அளவைச் சரிசெய்யவும்.
  • Throttling – அடிப்படைச் சேவை ஒரு throttling error-ஐத் தரும்போது (உதாரணமாக, Bedrock-இன் ThrottlingException), அதைத் தடுத்து (catch), சிறிது இடைவெளி விட்டு, exponential back-off முறையில் மீண்டும் முயற்சிக்கவும். resolve/reject ஜோடி மாறாது; retry logic மட்டுமே மாறும்.
  • Lambda cost – AWS Lambda-வில், callbackWaitsForEmptyEventLoop = false என அமைக்கவும். இது handler முடிவடைந்தவுடன், streams அல்லது பிற background handles திறந்திருந்தாலும், function-ஐ முடிக்க runtime-இடம் கூறுகிறது. இது promise வேறு எங்கோ முடிவடையும் வரை function தேவையற்ற முறையில் இயங்கிக் கொண்டிருப்பதைத் தடுக்கிறது.

புதிய helper எல்லாப் பிரச்சனைகளையும் தீர்க்காது

Promise.withResolvers() Node 22 மற்றும் அதற்குப் பிந்தைய பதிப்புகளில் மட்டுமே கிடைக்கும். பழைய LTS பதிப்புகளைப் பயன்படுத்தும் திட்டங்கள், இந்த முறையை polyfill செய்ய வேண்டும் அல்லது பழைய constructor முறையையே தொடர வேண்டும். Polyfills API-ஐப் போலவே செயல்படலாம், ஆனால் native performance நன்மைகளைத் தராது. மேலும், இந்த helper தர்க்கரீதியான (logical) பிழைகளைத் தானாகத் தீர்க்காது: ஒவ்வொரு request-க்கும் resolve அல்லது reject-இல் ஒன்று மட்டுமே அழைக்கப்படுவதை டெவலப்பர்கள் உறுதி செய்ய வேண்டும், இல்லையெனில் promise முடிவில்லாமல் (indefinitely) காத்திருக்கும் நிலையில் இருக்கும்.

அடுத்து கவனிக்க வேண்டியவை

  • Framework adoption – LLM agent loops-ஐ எளிமையாக்கும் libraries (உதாரணமாக, open-source Claude wrappers) withResolvers-ஐ ஒரு விருப்பத்தேர்வாக (opt-in feature) வழங்கத் தொடங்கியுள்ளன. இந்த முறை இயல்பான ஒன்றாக (default) மாறும் அப்டேட்களைக் கவனித்துக் கொண்டே இருங்கள்.
  • Node ecosystem – அதிகப்படியான சேவைகள் Node 22-க்கு மாறும்போது, இந்த helper LLM agents-க்கு மட்டுமல்லாமல், எந்தவொரு “fire-and-wait” async pattern-க்கும் ஒரு தரநிலையாக (de-facto standard) மாறும்.
  • Tool-calling standards – LLM tool calls-க்கான புதிய விதிமுறைகள் (specifications) ஒரு “single-promise” ஒப்பந்தத்தைப் பரிந்துரைக்கலாம், இது withResolvers அணுகுமுறையுடன் சரியாகப் பொருந்தும்.

சுருக்கம் (Takeaway): அதிகப்படியான new Promise முறையைத் தவிர்த்துவிட்டு, ஒரே வரியில் Promise.withResolvers() முறையைப் பயன்படுத்துவதன் மூலம், Claude-அடிப்படையிலான agents தெளிவான செயல்பாட்டையும், குறைவான runtime சிக்கல்களையும், serverless செலவுகளைக் கட்டுப்படுத்தும் திறனையும் பெறுகின்றன—நிச்சயமாக runtime Node 22-ஐ ஆதரித்தால் மட்டுமே.