संक्षिप्त उत्तर: ज़्यादातर वाइनरी सिस्टम टैंक के वॉल्यूम को एक ऐसी संख्या के रूप में रखते हैं जिसे लोग एडिट कर सकते हैं। जब dip किताब से मेल नहीं खाता, तो कोई नई संख्या टाइप कर देता है, और अंतर बिना किसी कारण के गायब हो जाता है। सेलर का event-sourcing मतलब हर movement को append-only event के रूप में रखना और हर बैलेंस उन्हीं से निकालना। तब किताब खुद को समझाती है, GenAI एजेंट के पास अंदाज़ा लगाने की बजाय तर्क करने के लिए असली इतिहास होता है, और “एजेंट प्रस्ताव दें, लोग मंज़ूर करें” का नियम लागू करना आसान हो जाता है क्योंकि कुछ भी ओवरराइट नहीं हो सकता।

टैंक 12: स्टोर की गई स्थिति बनाम निकाली गई स्थिति एडिट होने वाला फ़ील्ड tank_12.volume_l 4,980 → 4,920 60 L गायब, न कारण, न इतिहास APPEND-ONLY MOVEMENT LEDGER receive · प्रेस 3 से+5,000 top · बैरल B-17 से+40 sample · लैब−2 rack · lees, lees टैंक में−58 observe · dip 4,920 पढ़ता है (किताब 4,980)0 adjust · कारण: टैंक 12 से बैरल topping दर्ज नहीं हुई−60 बैलेंस = events का जोड़4,920 अंतिम संख्या एक ही · पर क्यों, यह सिर्फ़ एक बता सकता है
दोनों किताबें 4,920 लीटर पर खत्म होती हैं। ऑडिटर को, या एजेंट को, जवाब सिर्फ़ ledger दे सकता है।

टैंक 12 में 4,980 लीटर होने चाहिए। सेलर हैंड उसे dip करता है और 4,920 पढ़ता है। सिस्टम में एक वॉल्यूम फ़ील्ड है, तो वह 4,920 टाइप करता है और अपनी सुबह के काम में लग जाता है। व्यस्त दिन में यह बिल्कुल समझदारी भरा काम है। पर इसका मतलब यह भी है कि अब 60 लीटर वाइन वाइनरी के रिकॉर्ड से निकल गई, न कोई कारण, न नुकसान की तारीख, और बाद में पता लगाने का कोई तरीका नहीं।

इसे सौ बर्तनों और पूरी harvest की topping, racking और blending से गुणा कीजिए, और आपको साल के अंत में गायब वाइन की वही जानी-पहचानी खोज मिलती है। इस सीरीज़ की पहली पोस्ट एक ऐसी मेट्रिक के बारे में थी जिसे कभी परिभाषित नहीं किया गया। यह पोस्ट एक ऐसी मात्रा के बारे में है जिसे कभी इतिहास रखने ही नहीं दिया गया।

स्टोर की गई स्थिति बनाम निकाली गई स्थिति

ज़्यादातर सेलर सॉफ़्टवेयर, और लगभग हर सेलर स्प्रेडशीट, स्थिति स्टोर करती है: हर बर्तन में मौजूदा वॉल्यूम। movements होते हैं, और कोई स्थिति को उनसे मिलाने के लिए अपडेट कर देता है। movement खुद या तो कहीं और दर्ज होता है या कहीं नहीं।

event sourcing इसे उलट देता है। आप movements स्टोर करते हैं, और वॉल्यूम कभी स्टोर नहीं करते। सेलर में हर बदलाव एक event है:

  • receive (प्रेस से, ट्रक से, किसी दूसरी साइट से)
  • transfer और blend (बर्तन से बर्तन, लॉट की संरचना के साथ)
  • top, rack, filter, fine
  • सैंपल और लैब में इस्तेमाल
  • bottle (तैयार माल की ओर बाहर)
  • observe (dip या gauge रीडिंग, जो अपने आप कुछ नहीं बदलती)
  • adjust (देखे गए और गणना किए गए के बीच का अंतर, ज़रूरी कारण के साथ)

किसी भी पल किसी भी बर्तन का वॉल्यूम एक क्वेरी है: उस समय तक के हर event का जोड़। कोई उसे एडिट नहीं करता क्योंकि एडिट करने को कुछ है ही नहीं।

SELECT vessel_id,
       SUM(signed_volume_l_20c) AS volume_l
FROM   cellar_movements
WHERE  event_ts <= :as_of
GROUP  BY vessel_id;

:as_of बदलिए और पिछले पाँच साल के किसी भी दिन के अंत का सेलर आपके सामने है। हर excise return, हर ऑडिट और हर “Grenache कहाँ गई” वाली बातचीत असल में यही क्वेरी चाहती है।

वह बारीकी जो सबको पकड़ लेती है: किस तापमान पर लीटर?

ऊपर कॉलम का नाम देखिए: 20 डिग्री C पर लीटर। वाइन गरम होने पर फैलती है, लगभग 0.02 से 0.03 प्रतिशत प्रति डिग्री। 5,000 लीटर का एक टैंक 10 डिग्री पर और फिर 20 डिग्री पर पढ़ा जाए, तो बिना कोई वाइन कहीं गए लगभग 10 से 15 लीटर का अंतर आता है।

स्टोर-स्थिति वाले सिस्टम में यह अंतर वसंत के पहले गरम हफ़्ते में एक रहस्यमय नुकसान या फ़ायदे के रूप में सामने आता है। event मॉडल में हर observation अपना तापमान साथ रखता है, पाइपलाइन किताब से मिलाने से पहले उसे reference तापमान पर सुधार देती है, और तापमान की डगमगाहट कभी adjustment नहीं बनती। यह छोटी बात है। पर यह उसी तरह की बात है जिसकी वजह से एक सेलर मास्टर पहले ही महीने में सिस्टम पर भरोसा करना छोड़ देता है।

इसे lakehouse में बनाना

इसके लिए किसी अनोखे टूल की ज़रूरत नहीं। जो भी lakehouse आप पहले से चलाते हैं (Fabric या Databricks में Delta, Snowflake में Iceberg), उसमें एक append-only टेबल काम कर देती है। कुछ नियम इसे ईमानदार रखते हैं:

  1. सिर्फ़ append, और वह लागू हो। movement टेबल पर insert की अनुमति दें, update या delete की नहीं। सुधार उलटे events से होते हैं: गलत प्लस 500 को रद्द करने के लिए माइनस 500, फिर सही वाला। गलती दिखती रहती है, और मुद्दा यही है।
  2. adjustments पर कारण अनिवार्य है। data contract खाली कारण वाले adjust event को अस्वीकार करता है। “अज्ञात” चलेगा। खाली नहीं।
  3. हो सके तो स्रोत पर ही पकड़ें। अगर वाइनरी सिस्टम पहले से है, तो change data capture (Debezium, Fabric mirroring या vendor की अपनी feed) उसके updates को events में बदल सकता है, ताकि harvest के दौरान सेलर से नया ऐप सीखने को न कहना पड़े।
  4. गति के लिए snapshot, सच के लिए कभी नहीं। डैशबोर्ड के लिए रात को बनने वाली बैलेंस टेबल ठीक है। वह events से दोबारा बनती है और कभी एडिट नहीं होती।

lakehouse के time travel को कभी-कभी शॉर्टकट के रूप में पेश किया जाता है। यह वही चीज़ नहीं है। time travel दिखाता है कि टेबल कैसी दिखती थी। event ledger बताता है कि सेलर में क्या हुआ। “क्यों” का जवाब सिर्फ़ दूसरा देता है।

GenAI के लिए यह क्यों मायने रखता है

यहीं पुराने ढंग का डेटा मॉडल नए ढंग के टूल्स की कीमत चुकाता है।

एजेंट सिर्फ़ वही समझा सकता है जो दर्ज हुआ। GenAI असिस्टेंट से पूछिए “टैंक 12 में 60 लीटर कम क्यों है?” स्टोर-स्थिति वाले सिस्टम पर उसके पास दो वॉल्यूम snapshot हैं और बीच में कुछ नहीं, इसलिए वह या तो कहता है कि उसे नहीं पता या, इससे भी बुरा, वाष्पीकरण की एक विश्वसनीय-सी कहानी गढ़ देता है। movement ledger पर वह events पढ़ता है, adjustment ढूँढता है और ऑपरेटर व timestamp के साथ कारण बताता है। वही मॉडल, वही प्रॉम्प्ट। पूरा फ़र्क डेटा में है।

एजेंट प्रस्ताव दें, कभी लिखें नहीं। अगला स्वाभाविक कदम है एजेंट से डेटा एंट्री करवाना: सेलर वर्क ऑर्डर पढ़े और movements दर्ज करे। यह उपयोगी है और थोड़ा डरावना भी। append-only डिज़ाइन इसे आज़माना सुरक्षित बनाता है। एजेंट अपने तर्क के साथ events को एक pending टेबल में ड्राफ़्ट करता है (“वर्क ऑर्डर 4417 कहता है टैंक 12 से B-17 को top करो, 40 L”)। कोई व्यक्ति उन्हें मंज़ूर करता है, और तभी वे append होते हैं। अगर कोई मंज़ूर event गलत निकले, तो उसे बाकी सबकी तरह उलट दिया जाता है। एजेंट का कोई भी काम चुपचाप किताब को ओवरराइट नहीं कर सकता।

tool calls सरल हो जाते हैं। अगर आप सेलर को एजेंट के सामने कुछ टूल्स के ज़रिए खोलते हैं, तो event मॉडल ठीक-ठीक बता देता है कि वे क्या हैं: balance(vessel, as_of), history(vessel, from, to), propose_movement(...)। कोई set_volume नहीं है। जो टूल नहीं है, वही सुरक्षा है।

यह कहाँ टूटता है

यह उतना ही पूरा है जितना दर्ज किया गया। अगर व्यस्त हफ़्ते में topping व्हाइटबोर्ड पर जाती है और सिस्टम में कभी नहीं आती, तो ledger हर बार एक adjustment दिखाएगा। यह फिर भी चुप्पी से बेहतर है, क्योंकि adjustment के कारण बताते हैं कि दर्ज करने की आदत कहाँ कमज़ोर है, पर यह गायब events को पैदा नहीं करता।

flow meter और dip मेल नहीं खाते। flow meter से मापा गया ट्रांसफ़र और दोनों बर्तनों को dip करके मापा गया वही ट्रांसफ़र बिल्कुल बराबर नहीं होंगे। तय कीजिए कि किस movement प्रकार के लिए कौन सा reference है, और दूसरे को observation के रूप में दर्ज कीजिए।

harvest अफ़रा-तफ़री है। crush के दौरान events देर से और क्रम से बाहर आते हैं। मॉडल को पिछली तारीख वाले events स्वीकार करने होंगे, जिनमें event time के साथ दर्ज करने का समय भी हो, वरना सेलर नवंबर तक इसे इस्तेमाल करना ही छोड़ देगा।

माइग्रेशन मुफ़्त नहीं है। स्टोर-स्थिति वाले सिस्टम से आने का मतलब है हर बर्तन के लिए एक opening balance event से शुरुआत। उस तारीख से पहले का इतिहास उतना ही अनसमझा रहता है जितना हमेशा था।

निचोड़

जो वॉल्यूम आप एडिट कर सकते हैं, वह अपनी कहानी खो सकता है। जो वॉल्यूम आप movements से निकालते हैं, वह उसे सँभाले रखता है। event ledger एक पुराना विचार है (सदियों से अकाउंटेंट इसी तरह बही-खाते रखते आए हैं), और यह ठीक वही है जो एक GenAI एजेंट को चाहिए: समझाने के लिए असली provenance, और अतीत को दोबारा लिखने के ख़िलाफ़ एक पक्की दीवार। वाइन हमेशा चल रही थी। डेटा मॉडल को बस यह मानना है।

सीरीज़ में आगे: loss map, जहाँ ये events crush से बोतल तक एक waterfall बनते हैं और एक LLM variance नोट का ड्राफ़्ट बनाता है। इन events को भरने वाले सेंसर के लिए देखें वाइनरी में IoT। पूरी सूची Cellar Ledger सीरीज़ पेज पर है।

अक्सर पूछे जाने वाले सवाल

वाइनरी डेटा मॉडल में event sourcing क्या है? हर टैंक के वॉल्यूम को ऐसे फ़ील्ड के रूप में रखने की बजाय जिसे लोग एडिट करते हैं, आप हर movement को एक अपरिवर्तनीय event के रूप में रखते हैं: प्राप्ति, ट्रांसफ़र, topping, racking, blend, filtration, bottling, सैंपल, नुकसान। किसी भी पल किसी भी बर्तन का वॉल्यूम उस पल तक के events का जोड़ है। कुछ भी ओवरराइट नहीं होता, इसलिए हर लीटर का इतिहास होता है।

जो dip रीडिंग किताबी वॉल्यूम से मेल न खाए, उसे कैसे संभालें? dip को एक observation के रूप में दर्ज करें, नए वॉल्यूम के रूप में नहीं। अगर यह गणना किए गए बैलेंस से अलग है, तो अंतर अपना अलग adjustment event बनता है, जिसमें कारण देना ज़रूरी है, जैसे वाष्पीकरण, दर्ज न की गई topping या मीटर की गलती। किताब को एक event से सुधारा जाता है, कभी एडिट से नहीं, इसलिए अंतर दिखता रहता है और समझाया जा सकता है।

क्या AI एजेंट को वाइनरी इन्वेंटरी अपडेट करने देना चाहिए? उसे प्रस्ताव देने दें, लिखने नहीं। एजेंट को movement ledger पढ़ने दें और अपने तर्क के साथ एक movement या adjustment का ड्राफ़्ट बनाने दें, फिर append होने से पहले कोई व्यक्ति उसे मंज़ूर करे। क्योंकि ledger append-only है, इसलिए मंज़ूर की गई गलती भी एक उलटे event से ठीक होती है और कभी गायब नहीं होती।