पाठ 19 / 29

LLM Calls का Tracing और Logging

अनुरोध का हर चरण दर्ज करें ताकि धीमे या ग़लत उत्तरों का निदान हो सके।

जो दिखे नहीं उसे debug नहीं कर सकते

LLM अनुरोध एक pipeline है, इसलिए trace spans का वृक्ष है: अनुरोध सँभालना, retrieve, rerank, मॉडल call, validate, हर एक की शुरुआती समय, अवधि और attributes (मॉडल, token गिनती, chunks की संख्या, validation परिणाम, release ID) सहित। Traces से आप उत्तर दे सकते हैं "यह धीमा क्यों था?" (1.6 में से 1.2 सेकंड मॉडल call ने लिए), "यह ग़लत क्यों था?" (retrieval ने ग़लत chunk लौटाया), और "इसकी क्या लागत थी?"। समस्या दोहराने के लिए पर्याप्त log करें: release ID, अंतिम prompt (या उसका संदर्भ), retrieved दस्तावेज़ IDs, tool calls व परिणाम, कच्चा आउटपुट और parsed आउटपुट, साथ में user प्रतिक्रिया। इसे privacy से संतुलित करें: व्यक्तिगत डेटा redact करें, पहुँच सीमित करें, और retention सीमाएँ रखें। मानक tools (OpenTelemetry-संगत tracing और LLM-विशिष्ट observability प्लेटफ़ॉर्म) spans, dashboards और खोज देते हैं; अवधारणा उदाहरण जैसी ही है।

देखें, बाँधें, उबरें

Traces दिखाते हैं क्या हुआ, drift पहचान दिखाती है क्या बदला, और परतदार विफलता-सँभाल users को सेवा देती रहती है।

तीन औज़ार: traces, drift जाँच, विफलता-सँभाल।
चित्र 5.1 — Traces, drift जाँच और विफलता-सँभाल।

वृक्ष के रूप में अनुरोध trace, चलाकर

मैंने यह सादे Python 3 (सिर्फ़ standard library) से चलाया। Trace 1.62 s का अनुरोध दिखाता है जिसके child चरण retrieve (0.18 s), rerank (0.11 s), मॉडल call (1.20 s, token गिनती सहित) और आउटपुट validation (0.01 s) हैं। सबसे धीमा child चरण मॉडल call है। समय संरचना दिखाने को गढ़े हैं।

class Tracer:
    def __init__(self): self.t = 0.0; self.events = []; self.depth = 0
    def span(self, name, duration, attrs=None):
        start = self.t
        self.events.append((self.depth, name, start, duration, attrs or {}))
        return self
    def advance(self, d): self.t += d

tr = Tracer()
tr.span("handle_request", 1.62); tr.depth = 1
tr.span("retrieve", 0.18, {"chunks": 5}); tr.advance(0.18)
tr.span("rerank", 0.11, {"kept": 3}); tr.advance(0.11)
tr.span("llm_call", 1.20, {"in_tokens": 1450, "out_tokens": 210, "model": "model-a"}); tr.advance(1.20)
tr.span("validate_output", 0.01, {"valid": True}); tr.advance(0.01)
for depth, name, start, dur, attrs in tr.events:
    print("  " * depth + f"{name:16} start {start:5.2f}s  took {dur:4.2f}s  {attrs if attrs else ''}")
slowest = max((e for e in tr.events if e[0] == 1), key=lambda e: e[3])
print("slowest step:", slowest[1])

Output:

handle_request   start  0.00s  took 1.62s  
  retrieve         start  0.00s  took 0.18s  {'chunks': 5}
  rerank           start  0.18s  took 0.11s  {'kept': 3}
  llm_call         start  0.29s  took 1.20s  {'in_tokens': 1450, 'out_tokens': 210, 'model': 'model-a'}
  validate_output  start  1.49s  took 0.01s  {'valid': True}
slowest step: llm_call

Release ID log करें

हर trace बताए कि कौन-से prompt और मॉडल बंडल ने उसे बनाया।

त्वरित जाँच: Trace आपको क्या करने देता है जो समग्र metric नहीं?

  • विफलताएँ छिपाना
  • मॉडल को होशियार बनाना
  • Tests की ज़रूरत हटाना
  • एक विशिष्ट अनुरोध के भीतर देखना कि समय और त्रुटियाँ कहाँ हुईं
Answer

एक विशिष्ट अनुरोध के भीतर देखना कि समय और त्रुटियाँ कहाँ हुईं — समग्र बताते हैं कि कुछ धीमा है; traces बताते हैं कहाँ।