एक AI agent एक ऐसा language model है जो लूप में काम करता है। यह टास्क को पढ़ता है, "read this file" या "delete that file" जैसे किसी टूल का इस्तेमाल करता है, नतीजे को देखता है, और जब तक काम पूरा नहीं हो जाता तब तक लगा रहता है। हर बार जब मॉडल किसी टूल का इस्तेमाल करने की माँग करता है, तो उस रिक्वेस्ट को tool call कहा जाता है।
इस लूप को चलाने वाले कोड को harness कहते हैं। मॉडल खुद तय करता है कि उसे क्या करना है। Harness इसे चलाता है और साथ ही यह भी तय करता है कि मॉडल को क्या-क्या करने की छूट है।
एक अच्छा harness रास्ते में कई छोटे-छोटे फैसले लेता है। इस रिक्वेस्ट को कौन सा मॉडल संभाले? क्या यह tool call चलाना सुरक्षित है? क्या यह जवाब वापस भेजने लायक काफी अच्छा है? ज़्यादातर harness इन सवालों के जवाब किसी chat model से पूछकर और उसका रिप्लाई पढ़कर निकालते हैं। हर बार इसमें एक पूरा मॉडल कॉल खर्च होता है, इसलिए असल में ज़्यादातर चेक्स को छोड़ दिया जाता है।
TypeSafe AI के Jev एक छोटा मॉडल है जिसे सिर्फ इन्हीं फैसलों के लिए बनाया गया है। आप स्थिति बताते हैं और कुछ सवाल पूछते हैं, और यह हर सवाल का जवाब एक नंबर में देता है। यह कभी टेक्स्ट नहीं लिखता।
यह सबसे ज़्यादा तब काम आता है जब आप एक custom harness बनाते हैं — यानी कोई रेडीमेड agent इस्तेमाल करने के बजाय अपना खुद का agent loop। Custom harness आपको चुनने देता है कि कौन से मॉडल चलेंगे, agent किन चीज़ों को छू सकता है, और काम पूरा होने का मतलब क्या है। Jev इन चुनावों के पीछे की जाँच-परख को इतना सस्ता बना देता है कि आप इसे हर स्टेप पर चला सकते हैं।
इस ट्यूटोरियल में, आप Pi SDK (agents बनाने के लिए एक TypeScript toolkit) के साथ एक harness बनाएँगे और तीन जगहों पर Jev का इस्तेमाल करेंगे। अंत में, आप तैयार harness को एक लाइव sandbox में चलाएँगे और उसकी सेटिंग्स खुद बदलेंगे।
पूरा इंटरैक्टिव ट्यूटोरियल और playground यहाँ एक्सेस करें:
https://academy.dair.ai/resources/jev-decisions-in-a-pi-sdk-harness
यह गाइड Sydney Runkle के LangChain ब्लॉग पर लिखे आर्टिकल Building a Harness with Jev से प्रेरित है, जिसमें model routing और tool gating को रेडीमेड LangChain middleware के रूप में दिखाया गया है। यहाँ आप उन्हीं आइडियाज़ को Pi SDK पर खुद बनाएँगे, और फिर failures को संभालने व जवाबों की जाँच करने के लिए दो और पैटर्न जोड़ेंगे।
आप क्या बनाएँगे
Harness के तीन हिस्से हैं। हर हिस्सा अलग-अलग मौके पर Jev से एक सवाल पूछता है, और इस गाइड के बाकी हिस्से में इन्हें इन्हीं नामों से बुलाया जाएगा।

इसमें इस्तेमाल किया गया उदाहरण एक ऐसे agent का है जो trail survey notes वाले फोल्डर में काम कर रहा है — हर ट्रिप के लिए एक फाइल। यह उन नोट्स को पढ़, लिख और सच में डिलीट भी सकता है, और यही वजह है कि gate इतना ज़रूरी है।
Jev क्या है
TypeSafe, Jev को एक System One model कहता है। यह नाम मनोवैज्ञानिक Daniel Kahneman से आया है, जिन्होंने सोचने के दो तरीकों का वर्णन किया था। System One तेज़ और अपने-आप काम करने वाला होता है, जैसे बिना सोचे समझ आ जाना कि पैन गर्म है। System Two धीमा और सोच-समझकर काम करने वाला होता है, जैसे लंबा भाग (long division) हल करना।
इस harness में, एक आम language model फाइलें पढ़ने और जवाब लिखने का धीमा काम करता है। Jev इसके आसपास के फौरन फैसले लेता है। Jev इतना सस्ता और तेज़ है कि आप सिर्फ उन tool calls के बारे में नहीं, बल्कि हर tool call के बारे में पूछ सकते हैं जिनके खतरनाक होने का आपको पहले से शक हो।

हर नंबर 0 से 1 के बीच की probability होती है। 0.83 का मतलब है कि Jev को काफी यकीन है कि जवाब 'हाँ' है। 0.03 का मतलब है कि उसे काफी यकीन है कि जवाब 'नहीं' है।
तीन तरह के सवाल
Jev की हर रिक्वेस्ट के दो हिस्से होते हैं। State वह स्थिति है जिसका फैसला आप चाहते हैं, जैसे कोई tool call या यूज़र की रिक्वेस्ट। Questions वे बातें हैं जो आप उसके बारे में जानना चाहते हैं। Jev एक ही कॉल में, एक साथ सभी सवालों के जवाब देता है, इसलिए तीन सवाल पूछने में लगभग उतना ही समय लगता है जितना एक सवाल पूछने में।
Jev तीन तरह के सवालों को सपोर्ट करता है। पहला, जिसे Jev noul कहता है, एक हाँ-या-ना वाला सवाल है।

Jev को सिर्फ उतना ही पता होता है जितना आप उसे बताते हैं, इसलिए हर विकल्प और हर लेवल को साफ शब्दों में समझाएँ। ये डिस्क्रिप्शन ही prompt होते हैं।
सेटअप
Jev OpenRouter के ज़रिए उपलब्ध है — यह एक ऐसी सर्विस है जो एक ही API key के पीछे कई AI models देती है। वह एक key language model और Jev दोनों के लिए काम करती है। Pi के दोनों packages इंस्टॉल करें और अपनी key सेट करें।
1npm install @earendil-works/pi-agent-core @earendil-works/pi-ai
1OPENROUTER_API_KEY=...
Jev का अपना एक वेब एड्रेस है, जो आम chat वाले एड्रेस से अलग है। हमेशा कोई exact version बताएँ, जैसे typesafe/jev-1.13। पूरा Jev client सिर्फ एक fetch call है।
1const JEV = "typesafe/jev-1.13";23async function ask(state: unknown, questions: unknown) {4 const response = await fetch("https://openrouter.ai/api/alpha/decisions", {5 method: "POST",6 headers: {7 "Content-Type": "application/json",8 Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,9 },10 body: JSON.stringify({ model: JEV, state, questions }),11 signal: AbortSignal.timeout(2_000),12 });13 if (!response.ok) throw new Error(await response.text());14 return response.json();15}
Agent तब तक इंतज़ार करता है जब तक Jev जवाब नहीं देता, इसलिए यह कॉल दो सेकंड बाद हार मान लेता है। एक सामान्य जवाब में 200 से 400 मिलीसेकंड लगते हैं।
Jev कहाँ जुड़ता है
Pi का Agent class आपके लिए लूप चलाता है। यह आपके कोड को उस लूप में तय मौकों पर चलने देता है। इन जगहों को hooks कहते हैं। Harness अपने तीनों हिस्सों में से हर एक के लिए एक hook का इस्तेमाल करता है।

पहला gate
Gate के सबसे छोटे और काम के वर्ज़न से शुरू करते हैं। हर tool call से पहले, Jev से एक हाँ-या-ना वाला सवाल पूछें और अगर जवाब 'हाँ' जैसा लगे तो कॉल को रोक दें।
1import { Agent } from "@earendil-works/pi-agent-core";23const agent = new Agent({4 initialState: { systemPrompt, model, tools },5 streamFn: models.streamSimple.bind(models),67 beforeToolCall: async ({ toolCall, args }) => {8 const { answers } = await ask(9 { tool: toolCall.name, arguments: args },10 {11 destructive: {12 type: "noul",13 instructions: "This tool call destroys or overwrites data that was not created by this run.",14 criteria: {15 true: "Deletes files, truncates or overwrites existing content, drops data, or force-pushes over history.",16 false: "Reads, lists, creates a new file, or appends to a file this run already created.",17 },18 },19 },20 );2122 if (answers.destructive.noul >= 0.65) {23 return { block: true, reason: "Blocked: this looks destructive.", terminate: true };24 }25 },26});
यह 0.65 एक threshold है — वह सीमा जहाँ पहुँचकर harness किसी कॉल पर भरोसा करना बंद कर देता है। Jev जिस भी चीज़ को इस पर या इससे ऊपर स्कोर देता है, उसे रोक दिया जाता है।
अब कोई agent आपके नोट्स डिलीट करने की कोशिश करे तो डिलीट होने से पहले ही रुक जाएगा। किसी नोट को डिलीट करने पर स्कोर लगभग 0.83 आता है, और उसे पढ़ने पर 0.01। हालाँकि, यह gate थोड़ा मोटा है। बिल्कुल नई फाइल लिखने पर स्कोर लगभग 0.70 आता है, इसलिए वह भी रुक जाती है।
पूछने का खर्च बहुत कम है। Jev हर दस लाख input tokens के $0.042 चार्ज करता है, जो GLM 5.3 Flash (इस harness में इस्तेमाल होने वाले दो मॉडल्स में से सस्ता वाला) की input price के एक-तिहाई से भी कम है।
पहले gate से पूरे harness तक
पहला gate काम करता है, लेकिन इसमें चार कमियाँ रह जाती हैं।
- Threshold hook के अंदर दबा हुआ है, इसलिए उसे बदलना या टेस्ट करना मुश्किल है।
- चाहे रिक्वेस्ट आसान हो या कठिन, हर रिक्वेस्ट एक ही मॉडल पर चलती है।
- अगर Jev तक पहुँचा ही न जा सके तो क्या होगा, यह कहीं तय नहीं है।
- अंतिम जवाब कितना अच्छा है, यह कोई चेक नहीं करता।
नीचे दिए गए हर नंबर वाले सेक्शन में एक कमी दूर की गई है।
1. Thresholds को एक जगह रखें
यह सेक्शन gate को बेहतर बनाता है।
Thresholds तय करते हैं कि agent क्या कर सकता है, और असली नतीजे देखने के बाद आप इन्हें बार-बार बदलेंगे। इसलिए इन्हें hook से निकालकर एक साधारण फंक्शन decideGate() में डाल दें। यह Jev के नंबर लेता है और एक verdict लौटाता है: किसी कॉल के बारे में harness का आखिरी फैसला। इन नियमों को एक जगह रखने को policy कहते हैं।
Policy एक बीच का विकल्प भी जोड़ती है। एक threshold सिर्फ allow या block कह सकता है। दो thresholds तीन verdicts देते हैं।
- blockAt पर या उससे ऊपर होने पर कॉल रोक दी जाती है।
- askAt से नीचे होने पर कॉल चल जाती है।
- इन दोनों के बीच होने पर कॉल किसी इंसान की मंज़ूरी का इंतज़ार करती है।
यह बीच वाली रेंज उन कॉल्स को पकड़ लेती है जिनके बारे में Jev पक्का नहीं होता — एक अकेली सीमा उन्हें किसी न किसी तरफ गलत ठहरा देती।

1export function decideGate(signals: GateSignals, policy = DEFAULT_GATE_POLICY): GateVerdict {2 const worst = Math.max(3 signals.destructive.noul,4 signals.irreversible.noul,5 signals.outsideWorkspace.noul,6 );7 if (worst >= policy.blockAt) return { action: "block", ... };8 if (worst >= policy.askAt) return { action: "ask", ... };9 return { action: "allow", ... };10}
चूँकि यह एक साधारण फंक्शन है, आप बिना किसी लाइव मॉडल के, ऊपर दिए गए नंबरों जैसे नंबर पास करके इसे टेस्ट कर सकते हैं।
2. हर रिक्वेस्ट के लिए मॉडल चुनें
यह सेक्शन router जोड़ता है।
कुछ रिक्वेस्ट आसान होती हैं, जैसे एक फाइल पढ़ना। कुछ कठिन होती हैं, जैसे यह पता लगाना कि कोई चीज़ टूटी क्यों। सब कुछ सबसे ताकतवर मॉडल पर चलाने से पैसा बर्बाद होता है, और सब कुछ सस्ते मॉडल पर चलाने से कठिन रिक्वेस्ट के कमज़ोर जवाब मिलते हैं। Router हर रिक्वेस्ट को सही tier से मैच करता है — यानी तेज़ और सस्ता मॉडल, या ताकतवर और महँगा मॉडल।
रिक्वेस्ट शुरू होने से पहले, router एक ही कॉल में Jev से दो सवाल पूछता है। एक choice tier चुनता है, और एक score रिक्वेस्ट की जटिलता को नापता है।

1const ROUTER_QUESTIONS = {2 tier: choice("Which model tier should handle this request?", {3 fast: "Reading one file, pulling a fact out of it, or a small edit in a single place.",4 powerful: "Work that spans several files, or a failure with no obvious cause.",5 }),6 complexity: score("How much reasoning does this request need?", [7 "Mechanical. One step, no judgement.",8 "Localized. A few steps inside one area.",9 "Architectural. Many moving parts or an unknown root cause.",10 ]),11};
Router की policy इन दोनों जवाबों का इस्तेमाल इस तरह करती है।
- अगर complexity score ज़्यादा है, तो ताकतवर मॉडल इस्तेमाल करें, भले ही Jev ने fast चुना हो।
- अगर Jev को अपने चुनाव पर भरोसा नहीं है, तो सुरक्षा के लिए ताकतवर मॉडल इस्तेमाल करें।
- वरना, Jev ने जो मॉडल चुना है वही इस्तेमाल करें।
1if (complexity.score >= policy.escalateAtComplexity) return powerful;2if (tierAnswer.confidence < policy.minConfidence) return policy.fallbackTier;3return tierAnswer.choice;
यह policy का एक उदाहरण है। आप अपनी ज़रूरत के हिसाब से जवाबों को अलग तरह से तवज्जो दे सकते हैं — मसलन, ज़्यादा वॉल्यूम वाले batch job के लिए सस्ते मॉडल की तरफ झुकना, या production को छूने वाली हर चीज़ को हमेशा escalate करना। Jev सिर्फ जवाब देता है; उनका क्या करना है यह आपका कोड तय करता है।
सिर्फ एक बार ही क्यों चुनें
Router रिक्वेस्ट शुरू होने पर एक बार मॉडल चुनता है और रिक्वेस्ट पूरी होने तक उसी पर टिका रहता है। इसकी वजह prompt caching है।
हर बार जब agent कोई कदम उठाता है, मॉडल अब तक की पूरी बातचीत दोबारा पढ़ता है। AI providers हाल ही में पढ़ी गई बातचीत को स्टोर करके रखते हैं ताकि उन्हें दोबारा पढ़ना सस्ता पड़े। लेकिन हर मॉडल का अपना अलग store होता है। बीच में मॉडल बदलेंगे तो नए मॉडल को पूरी कीमत चुकाकर सब कुछ फिर से पढ़ना पड़ेगा।
Jev के फाउंडर ने coding agents पर एक design doc में इन आँकड़ों को विस्तार से समझाया है। एक लंबे session में, Claude Opus से सस्ते Sonnet पर और फिर वापस जाने में, पूरे समय Opus पर बने रहने के मुकाबले लगभग 50% ज़्यादा खर्च आया। इसलिए बातचीत अभी छोटी हो तभी शुरू में मॉडल चुन लें और उसी पर टिके रहें।
3. Jev के डाउन होने की तैयारी रखें
यह सेक्शन gate और router दोनों को बदलता है।
जैसे ही harness हर tool call के बारे में Jev से पूछने लगता है, agent Jev पर निर्भर हो जाता है। किसी भी ऑनलाइन सर्विस की तरह, Jev भी धीमा या डाउन हो सकता है। पहले से तय कर लें कि जवाब न मिलने पर हर हिस्सा क्या करेगा। हर हिस्से के लिए सही चुनाव अलग-अलग होता है।
Gate कॉल को रोक देता है। अगर gate Jev से पूछ नहीं पाता, तो उसे पता ही नहीं होता कि कॉल सुरक्षित है या नहीं। इसे जाने देने से फाइलें डिलीट हो सकती हैं, इसलिए gate मना कर देता है। इंजीनियर इसे failing closed कहते हैं — जैसे बिजली जाने पर दरवाज़ा अपने-आप लॉक हो जाए।
Router ताकतवर मॉडल इस्तेमाल करता है। अगर router Jev से पूछ नहीं पाता, तो उसे पता नहीं होता कि रिक्वेस्ट कितनी कठिन है। ताकतवर मॉडल कुछ भी संभाल सकता है, इसलिए रिक्वेस्ट का अच्छा जवाब फिर भी मिल जाता है, बस आपको थोड़ा ज़्यादा खर्च करना पड़ता है। इसे failing open कहते हैं, यानी काम को आगे बढ़ने देना।

4. जवाब की जाँच करें
यह सेक्शन verifier जोड़ता है। Agent harnesses में, verifier वह स्टेप होता है जो काम को पूरा मानने से पहले agent के काम की जाँच करता है।
Agent ऐसा जवाब देकर खत्म हो सकता है जिसमें कुछ छूट गया हो, या जिसमें उसने ऐसी बातें लिख दी हों जो उसने फाइल्स में असल में चेक ही न की हों। इसे पढ़ने वाले को अक्सर पता भी नहीं चलता। जवाब लौटाने से पहले उसकी जाँच करने से यह गलती तब पकड़ में आ जाती है जब agent फिर से कोशिश कर सकता है।
Verifier, Jev को तैयार जवाब के साथ वे फाइलें और tool results भी भेजता है जिन पर वह आधारित था। Jev जवाब की quality को स्कोर करता है और बताता है कि उसकी बातें grounded हैं या नहीं — यानी agent ने जो असल में पढ़ा है, क्या वे उससे मेल खाती हैं।

1const VERIFY_QUESTIONS = {2 quality: score("How well does the answer satisfy the request?", [3 "Does not answer the request.",4 "Partly answers it, with a gap the reader would notice.",5 "Fully answers the request.",6 ]),7 grounded: noul("Every factual claim is supported by the files or tool results in the transcript."),8};
दो नियम agent को हमेशा के लिए कोशिश करते रहने से रोकते हैं। उसे कुल मिलाकर ज़्यादा से ज़्यादा दो मौके मिलते हैं। और जब Jev को अपने ही ग्रेड पर भरोसा नहीं होता, तो harness एक और कोशिश पर पैसा खर्च करने के बजाय जवाब को मान लेता है।
Safety और logging
Jev आपको एक probability देता है, और probability गलत भी हो सकती है। इसलिए जो कुछ भी साधारण कोड पक्के तौर पर चेक कर सकता है, उसे कोड में ही चेक करना चाहिए। इस harness में, Jev चाहे जो कहे, हर file tool प्रोजेक्ट फोल्डर से बाहर के किसी भी path को मना कर देता है। Jev को उन फैसलों के लिए बचाकर रखें जो कोड नहीं ले सकता।
Gate सिर्फ खुद tool call को देखता है, यानी टूल का नाम और उसके inputs। इससे prompt injection में मदद मिलती है — वह स्थिति जब किसी फाइल या वेब पेज में छिपा टेक्स्ट मॉडल को कुछ नुकसानदेह काम करने के लिए बहका देता है। Gate उस चाल को कभी नहीं देखता, लेकिन उससे निकलने वाली नुकसानदेह कॉल को ज़रूर देख लेता है।
हर फैसले को उसके पीछे के नंबरों के साथ log करें। Log बताता है कि कोई चीज़ क्यों रोकी गई और thresholds सेट करने के लिए असली नंबर दिखाता है। Harness हर फैसले के लिए decisions.jsonl नाम की फाइल में एक लाइन लिखता है। Gate agent की हर कोशिश को देखता है, इसलिए log ईमेल एड्रेस और keys को छिपा देता है और लंबे inputs को छोटा कर देता है।
आज़मा कर देखें
नीचे दिया गया sandbox इस ट्यूटोरियल का तैयार harness चलाता है, जो असली Jev से जुड़ा है। यह trail survey notes वाले फोल्डर पर काम करता है, और इसका delete_path टूल सच में उन्हें डिलीट कर सकता है।
यहाँ आज़माएँ: https://academy.dair.ai/resources/jev-decisions-in-a-pi-sdk-harness
अपना harness खुद क्यों बनाएँ
रेडीमेड agents ये फैसले अपने अंदर बने नियमों से लेते हैं। Custom harness इन्हें आपके कोड में रख देता है। आप मॉडल चुनते हैं, thresholds सेट करते हैं, तय करते हैं कि इंसान कब बीच में आएगा, और log में पढ़ सकते हैं कि हर कॉल क्यों की गई।
Jev इसे व्यावहारिक बनाता है। हर फैसले में कुछ सौ मिलीसेकंड लगते हैं और सेंट का एक छोटा सा हिस्सा खर्च होता है, इसलिए आप जहाँ भी ज़रूरत हो वहाँ चेक जोड़ सकते हैं — सिर्फ वहीं नहीं जहाँ आप पूरा मॉडल कॉल afford कर सकते हैं। इस ट्यूटोरियल के तीन हिस्से एक शुरुआती बिंदु हैं। आपके अपने domain के लिए बना custom harness Jev से वे सारे सवाल पूछ सकता है जो वहाँ मायने रखते हैं।
अन्य इस्तेमाल
ये तीनों हिस्से coding agents के बाहर भी काम आते हैं।
- Code review bots. हर सुझाए गए बदलाव को स्कोर करें और इंसान को सिर्फ वही दिखाएँ जो पढ़ने लायक हों।
- Record cleanup. दो records को मर्ज करने से पहले पूछें कि क्या वे एक ही चीज़ के बारे में हैं, और जिनके बारे में पक्का न हो उन्हें इंसान के लिए छोड़ दें।
- Document pipelines. निकाले गए हर page को स्कोर करें और सिर्फ कम स्कोर वालों को दोबारा चलाएँ।
- Approval queues. सिर्फ ask-a-person रेंज वाली कॉल्स ही किसी इंसान reviewer तक पहुँचती हैं।
हर मामले में, language model खुला काम करता है, और Jev उसके आसपास के छोटे सवालों के जवाब देता है।
इस ट्यूटोरियल में दिए गए सवाल, thresholds और policies सीखने के उदाहरण हैं, production के लिए ट्यून की गई सेटिंग्स नहीं। हम benchmark कर रहे हैं कि harness में किए गए ये बदलाव cost और जवाब की quality को कैसे प्रभावित करते हैं, और उन नतीजों के साथ एक फॉलो-अप गाइड जल्द आ रही है।
मैंने Opus 5.5 के साथ एक शाम बिताकर यह गाइड और sandbox तैयार किया। अगर आपको कोई दिक्कत आए, तो मुझे DM करें। बेझिझक इस आर्टिकल की कॉपी बनाएँ और इन आइडियाज़ पर और प्रयोग करने के लिए अपने agents को खिलाएँ।





