एजेंट ऑब्ज़र्वेबिलिटी: Hooks, Alloy, और Grafana
> हमने Claude Code और Codex को OpenTelemetry और Alloy के साथ एक ही Grafana स्टैक में जोड़ा, फिर एजेंट व्यवहार की समस्याओं को उनके स्रोत पर खोजने और ठीक करने के लिए ट्रेस और लॉग का उपयोग किया।
एजेंट सिस्टम अजीबोग़रीब तरीकों से फ़ेल होते हैं।
कभी-कभी समस्या मॉडल में होती है। कभी-कभी समस्या टूल में होती है। कभी-कभी आपका MCP सर्वर बिल्कुल ठीक होता है, लेकिन एजेंट ने ग़लत स्पेशलिस्ट चुन लिया, या सेशन का आधा हिस्सा ऐसे शेल वर्क में बिता दिया जिसकी आपने उम्मीद नहीं की थी, या चुपचाप ऐसे लूप में लागत जला दी जो बाहर से देखने पर उत्पादक लग रहा था।
अगर आप यह अंतर नहीं देख पा रहे हैं, तो आप वास्तव में एक एजेंट सिस्टम को ऑपरेट नहीं कर रहे हैं। आप अंदाज़ा लगा रहे हैं।
तो हमने अपने ही वर्कफ़्लो के लिए एक ऑब्ज़र्वेबिलिटी स्टैक बनाया: Claude Code, Codex, Claude हुक घटनाएँ, Codex सूचना घटनाएँ, नेटिव OpenTelemetry, Grafana Alloy, और दूसरी ओर Grafana Cloud।
दिलचस्प हिस्सा यह नहीं है कि “हमने एक डैशबोर्ड बनाया।” दिलचस्प हिस्सा यह है कि हमें टेलीमेट्री को दो अलग-अलग स्ट्रीम में बांटना पड़ा, क्योंकि किसी एक फ़ीड से हमें पूरी तस्वीर नहीं मिल रही थी।
समस्या: एजेंट टेलीमेट्री बिखरी हुई है
आधुनिक कोडिंग एजेंट पहले से ही कुछ टेलीमेट्री उत्सर्जित करते हैं। इससे मदद मिलती है, लेकिन यह काफ़ी नहीं है।
नेटिव OTEL इन जैसे सवालों के जवाब देने में अच्छा है:
- हमने कितने रिक्वेस्ट किए?
- किसी सेशन की लागत कितनी रही?
- स्पैन और ट्रेस कहां हैं?
- क्या लेटेंसी में उछाल आया?
यह इन जैसे सवालों के जवाब देने में कहीं ज़्यादा कमज़ोर है:
- एजेंट किस MCP सर्वर पर निर्भर रहा?
- क्या यह विफलता
Bashमें थी, किसी बिल्ट-इन फ़ाइल टूल में, या किसी MCP कॉल में? - वास्तव में कौन-सा स्किल एक्टिवेट हुआ?
- किस तरह का सबएजेंट डिस्पैच किया गया?
- क्या सेशन उपयोगी काम कर रहा था, या सिर्फ़ थ्रैशिंग कर रहा था?
सवालों की वह दूसरी श्रेणी ट्रेस की तुलना में hooks के ज़्यादा करीब रहती है।
लेकिन इसका उल्टा भी सच है: कुछ सबसे महत्वपूर्ण परफ़ॉर्मेंस सवाल hooks की तुलना में ट्रेस के ज़्यादा करीब रहते हैं।
अगर आप जानना चाहते हैं कि लेटेंसी वास्तव में कहां जमा हुई, कौन-से स्पैन धीमे थे, या सेशन ने मॉडल कॉल बनाम टूल एक्ज़िक्यूशन में समय कहां जलाया, तो आपको सिमेंटिक इवेंट के साथ-साथ ट्रेस डेटा भी चाहिए।
जिस आर्किटेक्चर पर हम पहुंचे
हम दो टेलीमेट्री पथ एक साथ, समानांतर में चलाते हैं।
Claude Code
native OTEL -> Alloy -> Grafana Cloud
hooks -> send_event.py -> Grafana Cloud Loki
Codex
native OTEL -> Alloy -> Grafana Cloud
notify hook -> codex_notify.py -> shared Loki schema
यह बंटवारा जानबूझकर किया गया है।
यह असममित भी है। Claude Code हमें एक कहीं ज़्यादा समृद्ध लाइफ़साइकल hook सरफ़ेस देता है। Codex हमें नेटिव OTEL के साथ-साथ एक notify सरफ़ेस देता है, इसलिए हम पतले टर्न-कम्प्लीशन इवेंट को उसी लॉग स्कीमा में नॉर्मलाइज़ करते हैं, बजाय यह दिखावा करने के कि दोनों रनटाइम एक जैसे कंट्रोल एक्सपोज़ करते हैं।
नेटिव OTEL हमें बेसलाइन स्ट्रीम देता है: रनटाइम से आने वाले लॉग और ट्रेस, साथ ही वे मेट्रिक्स जहां रनटाइम वास्तव में उन्हें उत्सर्जित करता है।
Hook और notify इवेंट हमें सिमेंटिक लेयर देते हैं: जैसे PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, SubagentStop, SkillActivated, और वह वर्गीकृत मेटाडेटा जिसकी हमें एजेंट व्यवहार डीबग करते समय वास्तव में परवाह है। यहां Claude Code ज़्यादा समृद्ध इवेंट स्ट्रीम देता है। Codex एक पतली लेकिन फिर भी उपयोगी नॉर्मलाइज़्ड स्ट्रीम देता है।
Hooks आख़िर होते ही क्यों हैं
हमारी hook पाइपलाइन इवेंट को Loki तक पहुंचने से पहले समृद्ध करती है।
सिर्फ़ यह कहने के बजाय कि “एक टूल चला,” हम इवेंट को इन जैसे फ़ील्ड में वर्गीकृत करते हैं:
tool_type: builtin, mcp, skill, agent, bashmcp_server: किस MCP बैकएंड ने कॉल हैंडल कियाbash_cli: शेल कमांड फ़ैमिलीsubagent_type: किस तरह का स्पेशलिस्ट डिस्पैच किया गयाagent_tool: क्या स्रोत Claude Code था या Codex
इसका मतलब है कि हम ऐसे सवाल पूछ सकते हैं जो परिचालन रूप से मायने रखते हैं:
{service_name="claude-code-hooks"} | agent_tool="codex-cli"
{service_name="claude-code-hooks"} | tool_type="mcp"
{service_name="claude-code-hooks"} | json | bash_cli="git"
ये दिखावटी फ़ील्ड नहीं हैं। ये “एजेंट धीमा महसूस हुआ” और “एजेंट ने पिछले दस मिनट शेल-भारी git ऑपरेशन में उच्च टूल फ़ेल्योर रेट के साथ बिताए” के बीच का फ़र्क़ हैं।
एक इम्प्लीमेंटेशन डिटेल जो वास्तव में जितनी है उससे ज़्यादा अजीब दिखती है: साझा Loki स्ट्रीम अब भी service_name="claude-code-hooks" को लेबल के रूप में इस्तेमाल करती है, भले ही इवेंट Codex से आया हो। रनटाइम के बीच असली बंटवारा agent_tool पर होता है।
Alloy बीच में क्यों बैठता है
इस सेटअप में Grafana Alloy सिर्फ़ एक फ़ॉरवर्डर नहीं है। यह पॉलिसी सीमा है।
हम Claude Code और Codex से आने वाली नेटिव OTEL स्ट्रीम को localhost:4318 पर एक लोकल Alloy प्रॉक्सी की ओर पॉइंट करते हैं, फिर Alloy को Grafana Cloud तक फ़ॉरवर्ड करने से पहले पेलोड साफ़ करने देते हैं।
यह इसलिए मायने रखता है क्योंकि कच्ची एजेंट टेलीमेट्री उन हाई-कार्डिनैलिटी फ़ील्ड से भरी होती है जो विश्लेषण के लिए उपयोगी हैं लेकिन इंडेक्स्ड लेबल के रूप में भयानक हैं:
session_idprompt_id- टोकन काउंट
- अवधियां
- टूल पैरामीटर ब्लॉब
अगर आप सब कुछ इंडेक्स करते हैं, तो आपको एक लेबल विस्फोट और एक बुरा दिन मिलता है।
तो Alloy हमारे लिए तीन काम करता है:
- लो-कार्डिनैलिटी लेबल का एक बहुत छोटा सेट इंडेक्स्ड रखता है।
- शोरगुल वाले लेकिन उपयोगी फ़ील्ड को संरचित मेटाडेटा में ले जाता है।
- शुद्ध नॉइज़ को पूरी तरह हटा देता है।
महत्वपूर्ण विचार सरल है: ज़्यादा ऑब्ज़र्व करो, कम इंडेक्स करो।
Hook स्ट्रीम, Alloy को क्यों बायपास करती है
Hook स्ट्रीम पहले से ही Loki के लिए आकार में ढली होती है।
जब तक send_event.py कोई इवेंट पुश करता है, तब तक हम पहले ही तय कर चुके होते हैं कि कौन-से फ़ील्ड लेबल ट्रीटमेंट के लायक हैं और कौन-से संरचित JSON बॉडी में जाने चाहिए। वह स्ट्रीम Alloy से दोबारा गुज़रने के बजाय सीधे Grafana Cloud के OTLP गेटवे तक जाती है।
तो सिस्टम में काम का स्पष्ट बंटवारा है:
- Alloy कच्ची नेटिव OTEL स्ट्रीम को क़ाबू में करता है।
- Hook एनरिचमेंट सिमेंटिक इवेंट को क्वेरी करने लायक बनाता है।
इससे सब कुछ एक ही पथ से ज़बरदस्ती गुज़ारने की कोशिश करने की तुलना में आर्किटेक्चर सरल बना रहता है।
डैशबोर्ड वास्तव में क्या दिखाता है
नीचे दिया गया स्क्रीनशॉट हमारे एजेंट वर्कफ़्लो के पीछे मौजूद ऑब्ज़र्वेबिलिटी डैशबोर्ड में से एक का है। यह कोई बेंचमार्क नहीं है, और आंकड़े सिर्फ़ एक निश्चित पल की झलक हैं। मुद्दा डेटा का आकार है: एक्टिविटी फ़ीड, टूल कॉल, विफलताएं, प्रॉम्प्ट, और एजेंट, बिल्ट-इन टूल, MCP उपयोग, शेल कमांड, और स्किल के हिसाब से ब्रेकडाउन।
उपयोगी बात यह है कि यह डैशबोर्ड बाक़ी एजेंट टेलीमेट्री के साथ उसी Grafana स्टैक पर रहता है। हम स्रोत एजेंट और टूल फ़ैमिली के हिसाब से फ़िल्टर कर सकते हैं और हर सिस्टम के लिए अलग ऑब्ज़र्वेबिलिटी कहानी गढ़े बिना रनटाइम के आर-पार देख सकते हैं।
ट्रेस पहली नज़र में दिखने से कहीं ज़्यादा क्यों मायने रखते हैं
लॉग हमें बताते हैं कि किस श्रेणी का काम हुआ। ट्रेस हमें बताते हैं कि काम समय के साथ कैसे सामने आया।
यह फ़र्क़ एजेंट सिस्टम में मायने रखता है क्योंकि “धीमा” उपयोगी होने के लिए बहुत ही अस्पष्ट शब्द है।
एक ट्रेस हमें बता सकता है कि तकलीफ़ कहां से आई:
- मॉडल लेटेंसी
- टूल एक्ज़िक्यूशन समय
- बार-बार रीट्राई
- एक खासतौर पर महंगा MCP इंटरैक्शन
- छोटे ऑपरेशन की एक लंबी पूंछ जो अलग-अलग देखने पर हानिरहित लगती थी
व्यवहार में हम hook स्ट्रीम और Tempo ट्रेस को साथ इस्तेमाल करते हैं।
- Hook लॉग जवाब देते हैं: किस तरह की चीज़ हुई?
- ट्रेस जवाब देते हैं: समय कहां गया?
यही मेल है जो ऑब्ज़र्वेबिलिटी को एक डैशबोर्ड से एक स्पष्टीकरण में बदल देता है।
Codex कहां फ़िट बैठता है
Codex उसी स्टैक का हिस्सा है, लेकिन यह Claude Code जैसा बिल्कुल नहीं है।
Codex के लिए हम दो हिस्से जोड़ते हैं:
- Codex से Alloy में नेटिव OTEL
codex_notify.pyमें एक notify वेबहुक, जो टर्न कम्प्लीशन को उसी Loki स्कीमा में मैप करता है जिसका उपयोग हम hook इवेंट के लिए करते हैं
इससे हमें उसी लॉग स्ट्रीम के भीतर agent_tool="codex-cli" जैसा एक एकीकृत फ़िल्टर मिलता है।
ईमानदार चेतावनी: Codex का notify पेलोड फ़िलहाल Claude Code के hook पेलोड से पतला है, क्योंकि यह उसी तरह की इंटीग्रेशन सरफ़ेस नहीं है। हमारे आज के सेटअप में, Codex टर्न कम्प्लीशन को साझा स्कीमा में नॉर्मलाइज़ किया जा सकता है, लेकिन समृद्ध टूल-दर-टूल एक्सट्रैक्शन अब भी notify ब्रिज की तुलना में नेटिव OTEL स्ट्रीम में बेहतर है।
यह पोस्ट से बचने की कोई वजह नहीं है। यही तो इस पोस्ट का मक़सद है। असली ऑब्ज़र्वेबिलिटी सिस्टम अपूर्ण सिग्नल से मिलकर बनते हैं।
MCP पर Grafana खेल बदल देता है
बड़ा बदलाव यह है कि Grafana सिर्फ़ वह जगह नहीं है जहां इंसान ब्राउज़र में जाते हैं।
इस रिपॉज़िटरी में हम Grafana को MCP के ज़रिए भी एक्सपोज़ करते हैं। इसका मतलब है कि कोई एजेंट, किसी इंसान के पहले मैनुअली डैशबोर्ड देखने का इंतज़ार करने के बजाय, सीधे Loki, Prometheus, और Tempo को क्वेरी कर सकता है।
इससे ऑब्ज़र्वेबिलिटी वर्कफ़्लो का एक सक्रिय इनपुट बन जाती है।
कोई एजेंट पूछ सकता है:
- पिछले एक घंटे में कौन-सी टूल फ़ैमिली सबसे ज़्यादा फ़ेल हुईं?
- किसी सेशन पर किस MCP सर्वर का दबदबा रहा?
- क्या हाल के बदलावों ने टूल फ़ेल्योर घटाए, या बस काम को उन्हीं ग़लतियों के साथ ज़्यादा शेल-भारी पथों में शिफ़्ट कर दिया?
- कौन-से ट्रेस सबसे ज़्यादा लेटेंसी या बार-बार रीट्राई दिखाते हैं?
एक बार आपके पास यह हो, तो आप एक सेल्फ़-इम्प्रूवमेंट लूप के बहुत करीब होते हैं।
डैशबोर्ड से फ़ीडबैक लूप तक
यह वह हिस्सा है जो हमें सबसे दिलचस्प लगता है।
एक बार जब ऑब्ज़र्वेबिलिटी स्टैक एजेंट लेयर से क्वेरी करने लायक हो जाए, तो टेलीमेट्री एक निष्क्रिय रिपोर्टिंग सरफ़ेस बनना बंद कर देती है और एक कंट्रोल सिग्नल बन जाती है।
लूप कुछ ऐसा दिखता है:
- एजेंट गतिविधि ट्रेस, मेट्रिक्स, और समृद्ध hook लॉग उत्सर्जित करती है।
- Grafana इस साक्ष्य को Loki, Tempo, और Prometheus में स्टोर करता है, जहां मेट्रिक्स मौजूद हैं।
- एजेंट Grafana MCP के ज़रिए उस साक्ष्य को क्वेरी करते हैं।
- सिस्टम ख़राब टूल मिश्रण, नाज़ुक स्किल, कमज़ोर रूटिंग, या शेल-भारी वर्कफ़्लो की पहचान करता है जो लगातार टाली जा सकने वाली ग़लतियां पैदा करते रहते हैं।
- एजेंट या ऑपरेटर प्रॉम्प्ट, एजेंट कॉन्फ़िग, स्किल विवरण, रूटिंग नियम, या टूल एक्सेस में बदलाव करते हैं।
- अगला सेशन एक नया टेलीमेट्री आकार पैदा करता है, और चक्र फिर से चलता है।
यही तरीक़ा है “दिलचस्प डैशबोर्ड” से “मापने योग्य सुधार प्रणाली” तक जाने का।
लक्ष्य किसी एक टूल श्रेणी को अधिकतम करना नहीं है। लक्ष्य वास्तव में हो रहे काम के लिए CLI, बिल्ट-इन टूल, MCP कॉल, और स्किल का सही मिश्रण पाना है।
यह हमें क्या जवाब देने देता है
एक बार जब दोनों रनटाइम एक ही Grafana स्टैक में उतर जाते हैं, तो हम परिचालन सवालों के जवाब कहीं तेज़ी से दे सकते हैं:
- क्या विफलताएं किसी एक टूल फ़ैमिली में केंद्रित हैं?
- क्या शेल-भारी वर्कफ़्लो ऐसी टाली जा सकने वाली ग़लतियां पैदा कर रहे हैं जहां एक उच्च-स्तरीय टूल मौजूद होना चाहिए?
- कौन-से MCP सर्वर कार्यभार उठा रहे हैं?
- क्या हम ऐसी एजेंट गतिविधि के लिए भुगतान कर रहे हैं जो सार्थक प्रगति पैदा नहीं कर रही?
- क्या कोई सेशन मॉडल, टूल, या ऑर्केस्ट्रेशन लेयर की वजह से अस्वस्थ है?
यह मल्टी-एजेंट वर्कफ़्लो में खासतौर पर उपयोगी है, जहां “एजेंट व्यस्त था” कहना लगभग कुछ नहीं बताता।
अगर एक स्पेशलिस्ट लगातार डिस्पैच होता रहे और उच्च फ़ेल्योर रेट पैदा करता रहे, तो यह रूटिंग या प्रॉम्प्ट-शेपिंग की समस्या है।
अगर एक MCP सर्वर सभी कॉल पर हावी है, तो यह अच्छा आर्किटेक्चर हो सकता है, या इस बात का संकेत कि बाक़ी सब बेकार बोझ है।
अगर टूल फ़ेल्योर बढ़ते हैं जबकि लागत ऊंची बनी रहती है, तो आपके पास एक परिचालन समस्या है, गुणवत्ता की समस्या नहीं।
अगर शेल वर्क लगातार अनुमानित, टाली जा सकने वाली तरीक़ों से फ़ेल होता रहे जहां एक उच्च-स्तरीय टूल मौजूद होना चाहिए, तो यह एक प्रोडक्ट सिग्नल है।
अगर कोई एक स्किल लगातार एक्टिवेट होती है लेकिन नतीजों को बेहतर नहीं बनाती, तो यह एक प्रॉम्प्ट या रूटिंग सिग्नल है।
असली सबक़
यहां गहरा सबक़ यह है कि एजेंट ऑब्ज़र्वेबिलिटी को रनटाइम टेलीमेट्री और वर्कफ़्लो टेलीमेट्री, दोनों की ज़रूरत है।
रनटाइम टेलीमेट्री आपको बताती है कि सिस्टम ने क्या किया।
वर्कफ़्लो टेलीमेट्री आपको बताती है कि एजेंट को क्या लगा कि वह क्या कर रहा था।
हमें दोनों चाहिए।
अगर आप सिर्फ़ ट्रेस और काउंटर रखते हैं, तो आप सिमेंटिक लेयर चूक जाते हैं। अगर आप सिर्फ़ hook इवेंट रखते हैं, तो आप लेटेंसी, स्पैन, और व्यापक रनटाइम तस्वीर चूक जाते हैं।
और अगर आप दोनों रखते हैं लेकिन उन्हें कभी वापस एजेंट लेयर में फ़ीड नहीं करते, तो आपके पास मॉनिटरिंग है, अनुकूलन नहीं।
यही मेल है जो सिस्टम को ऑपरेट करने लायक़ इतना समझाने योग्य और सुधारने लायक़ इतना ट्यून करने योग्य बनाता है।
अब भी क्या अधूरा है
अब भी कुछ खुरदुरे किनारे हैं।
- हर hook इवेंट में वे अवधि और token डेटा शामिल नहीं होते जो हम चाहते।
- कुछ सबसे अच्छे टाइमिंग व्यू अब भी hook लॉग से नहीं, बल्कि Tempo ट्रेस से आते हैं।
- Codex आज समृद्ध इवेंट स्ट्रीम में Claude Code जितना सिमेंटिक रूप से समृद्ध नहीं है।
- डैशबोर्ड स्क्रीनशॉट एक लाइव ऑपरेशन सरफ़ेस है, कोई पॉलिश किया हुआ मार्केटिंग आर्टिफ़ैक्ट नहीं।
वह आख़िरी बात जानबूझकर है। हम यह दिखावा करने के बजाय, कि एजेंट सिस्टम जादुई रूप से खुद-ब-खुद समझ में आ जाते हैं, असली इंस्ट्रूमेंट पैनल दिखाना पसंद करेंगे।
Maguyva के लिए यह क्यों मायने रखता है
Maguyva एजेंट को बेहतर कोड इंटेलिजेंस देने के बारे में है। लेकिन जैसे ही एजेंट वास्तव में उपयोगी काम करने लगते हैं, एक नई ज़रूरत तुरंत सामने आ जाती है: आपको यह देखने की ज़रूरत है कि वे कैसा व्यवहार कर रहे हैं।
सर्च क्वालिटी, रूटिंग क्वालिटी, टूल सिलेक्शन, और कॉन्टेक्स्ट एफ़िशिएंसी — ये सब ऑब्ज़र्वेबल समस्याएं बन जाती हैं।
यही वजह है कि हमें लगता है कि इस बारे में लिखना सार्थक है। भविष्य का एजेंट स्टैक सिर्फ़ प्रॉम्प्ट और टूल नहीं है। यह प्रॉम्प्ट, टूल, और वह इंस्ट्रुमेंटेशन लेयर है जो आपको बताती है कि पूरी चीज़ काम कर रही है या नहीं।
अगर आप गंभीर एजेंट वर्कफ़्लो बना रहे हैं, तो ऑब्ज़र्वेबिलिटी कोई वैकल्पिक इन्फ़्रास्ट्रक्चर नहीं है। यह प्रोडक्ट का हिस्सा है।
जुड़ी हुई रीडिंग
Maguyva की बिल्ड लॉग से और लेख
हमने कोड सर्च को voyage-4-large में क्यों अपग्रेड किया_
हमने अपनी कोड एम्बेडिंग को voyage-4-large पर शिफ़्ट किया — जो फ़िलहाल पब्लिक RTEB कोड रिट्रीवल लीडरबोर्ड में शीर्ष पर है। ईमानदार वर्ज़न: वह ट्रेड-ऑफ़ जो हम करते हैं, हम वास्तव में क्या इंडेक्स करते हैं, और हम प्रीमियम एम्बेडिंग के लिए भुगतान क्यों करते हैं।
भाषा आवर्ती स्व-सुधार: लगभग 280 भाषाओं में कोड इंटेलिजेंस को अनथक मेहनत से निखारना_
हम लगभग 280 भाषाओं के लिए कोड इंटेलिजेंस सपोर्ट करते हैं। कोई इंसान इसका हाथ से ऑडिट नहीं कर सकता। इसलिए हमने एक भाषा आवर्ती स्व-सुधार लूप बनाया — स्पॉट-चेक, LLM-as-judge, एक चीज़ ठीक करो, दोबारा वैलिडेट करो — और इसे आइसोलेटेड एजेंट्स के एक फ़्लीट के साथ तब तक चलाते हैं जब तक एक्सट्रैक्शन वास्तव में सही न हो जाए, सिर्फ़ ग्रीन न हो।
मल्टी-मोडल फ़्यूज़न सर्च: हर क्वेरी के लिए सही रिट्रीवर चुनना_
"parseConfig कहां परिभाषित है" जैसी क्वेरी को "auth कैसे काम करता है" से अलग तरह की सर्च चाहिए। Maguyva इंटेंट को वर्गीकृत करता है, उसके हिसाब से चार रिट्रीवल मोडैलिटी को वेट देता है, और नतीजों को वेटेड Reciprocal Rank Fusion से जोड़ता है।