एक निजी लोकल AI स्टैक बनाएँ (एंड-टू-एंड)
आपने टुकड़ों को अलग-अलग देखा है: एक लोकल मॉडल, एक लोकल एजेंट लूप, MCP के ज़रिए एक्सपोज़ किए गए टूल्स, और Claude+लोकल हाइब्रिड पैटर्न। यह कैपस्टोन है — वह पेज जो इन्हें आपकी अपनी मशीन पर एक कार्यशील निजी असिस्टेंट में जोड़ता है: एक ओपन-वेट मॉडल जो लोकली चल रहा हो, एक मॉडल-अज्ञेयवादी एजेंट लूप जो टूल्स कॉल कर सके, वे टूल्स एक लोकल MCP सर्वर के ज़रिए एक्सपोज़्ड, खतरनाक टूल्स के आगे एक गार्डरेल, और — वैकल्पिक रूप से — सबसे कठिन 5% स्टेप्स के लिए एक ऑप्ट-इन "स्मार्ट लेयर" के रूप में Claude। मूल विचार: हर संवेदनशील चीज़ डिवाइस पर ही रहती है; क्लाउड वैकल्पिक है और कठिन अल्पसंख्या के लिए सुरक्षित रखा गया है।
- पूरे स्टैक को एक ही डायग्राम के रूप में देखें: लोकल मॉडल + एजेंट लूप + लोकल MCP टूल्स + गार्डरेल (+ वैकल्पिक Claude)
- एक ओपन-वेट मॉडल लोकली चलाएँ और पुष्टि करें कि वह टूल कॉलिंग कर सकता है
- एक न्यूनतम एजेंट लूप खड़ा करें जो मॉडल-अज्ञेयवादी हो — वही लूप, बस एंडपॉइंट बदल दें
- कुछ टूल्स को एक लोकल MCP सर्वर के ज़रिए एक्सपोज़ करें और एजेंट को उन्हें कॉल करने दें
- एक गार्डरेल जोड़ें: विनाशकारी कार्यों के लिए अनुमोदन, एक लूप/बजट सीमा, और अविश्वसनीय-परिणाम हैंडलिंग
- वैकल्पिक रूप से केवल सबसे कठिन रीज़निंग को Claude पर रूट करें, डिफ़ॉल्ट पथ को पूरी तरह लोकल रखते हुए
पूरा स्टैक, एक ही तस्वीर में
मानसिक मॉडल कुछ ही बॉक्सों का है, जिनमें से हर एक को आप किसी सहोदर पेज पर पहले ही मिल चुके हैं। असिस्टेंट बस इन्हीं बॉक्सों को एक साथ जोड़ना है:
इसे एक लूप के रूप में पढ़ें। एजेंट लोकल मॉडल से पूछता है कि आगे क्या करना है। मॉडल या तो जवाब देता है, या एक टूल कॉल उत्सर्जित करता है। हर टूल कॉल लोकल MCP सर्वर तक पहुँचने से पहले एक गार्डरेल से होकर गुज़रती है, जो वास्तव में काम करता है (एक फ़ाइल पढ़ता है, एक कमांड चलाता है, आपके नोट्स खोजता है) और एक परिणाम लौटाता है। एजेंट परिणाम को वापस मॉडल में फ़ीड करता है और तब तक दोहराता है जब तक कार्य पूरा नहीं हो जाता। Claude तक का बिंदुदार पथ ऑप्ट-इन है: एजेंट केवल उन्हीं स्टेप्स को एस्केलेट करता है जिन्हें लोकल मॉडल संभाल नहीं सकता, और केवल तभी जब आप अनुमति दें।
तीन गुण इस स्टैक को बनाने लायक बनाते हैं:
- डिफ़ॉल्ट रूप से लोकल। मॉडल, लूप, टूल्स, और आपका डेटा सब आपके हार्डवेयर पर रहता है। कुछ भी बॉक्स से बाहर नहीं जाता जब तक वैकल्पिक Claude पथ सक्रिय न हो — और तब भी, केवल वही जो आप भेजना चुनते हैं।
- मॉडल-अज्ञेयवादी लूप। एजेंट एक OpenAI-आकार वाले चैट एंडपॉइंट से बात करता है। इसे आज Ollama के लोकल एंडपॉइंट की ओर इंगित करें; कल इसे किसी दूसरे प्रोवाइडर की ओर इंगित करें बिना लूप को फिर से लिखे।
- एक ही मानक के पीछे टूल्स। क्षमताएँ एक MCP सर्वर में रहती हैं, न कि लूप में हार्ड-कोडेड। एक टूल एक बार बनाएँ और कोई भी MCP-बोलने वाला क्लाइंट (आपका एजेंट, Claude Code, कोई अन्य ऐप) उसका उपयोग कर सकता है।
चरण-दर-चरण निर्माण
- Ollama इंस्टॉल करें और एक ऐसा मॉडल शुरू करें जो टूल कॉलिंग का समर्थन करता हो। ollama run पहले उपयोग पर डाउनलोड करता है और localhost:11434 पर एक लोकल OpenAI-संगत API एक्सपोज़ करता है। यह आपका डिफ़ॉल्ट 'दिमाग' है — निजी और ऑफ़लाइन। (पूरा सेटअप: Run Models Locally पेज।)
- एक छोटा लूप लिखें: चैट एंडपॉइंट को मैसेज + एक टूल स्कीमा भेजें, उत्तर पढ़ें, यदि उसमें tool_calls हैं तो उन्हें निष्पादित करें, परिणाम जोड़ें, और तब तक लूप करें जब तक मॉडल एक अंतिम उत्तर न लौटा दे। लूप को यह नहीं पता कि वह किस मॉडल से बात करता है — केवल OpenAI चैट आकार जानता है।
- अपनी वास्तविक क्षमताओं (एक फ़ाइल पढ़ना, एक कमांड चलाना, नोट्स खोजना) को हार्ड-कोड करने के बजाय stdio पर एक लोकल MCP सर्वर में रखें। एजेंट सर्वर के टूल्स की सूची बनाता है, उन्हें मॉडल के टूल स्कीमा में मैप करता है, और मांग पर उन्हें कॉल करता है। एक बार बनाएँ, कई क्लाइंट्स में पुनः उपयोग करें।
- किसी भी टूल के चलने से पहले, उसे गेट करें: केवल-पढ़ने वाले टूल्स को ऑटो-अनुमति दें, विनाशकारी टूल्स (run_shell, write_file, delete) के लिए स्पष्ट अनुमोदन की मांग करें, लूप पुनरावृत्तियों की संख्या और कुल टोकन को सीमित करें, और हर टूल परिणाम को एक अविश्वसनीय इनपुट मानें जो मॉडल को गुमराह करने की कोशिश कर सकता है।
- लोकल पथ को डिफ़ॉल्ट रखें। जब कोई स्टेप वास्तव में कठिन हो — पेचीदा मल्टी-स्टेप रीज़निंग, एक योजना जिसे लोकल मॉडल बार-बार बिगाड़ता रहे — तो एजेंट को केवल उस स्टेप को Claude API पर एस्केलेट करने दें, फिर लोकल लूप में वापस आएँ। यह हाइब्रिड पेज से राउटर / ड्राफ्ट-फिर-रिफाइन विचार है, जो एक बार में एक स्टेप पर लागू होता है।
1. लोकल मॉडल (आपका डिफ़ॉल्ट दिमाग)
मॉडल शुरू करें और पुष्टि करें कि लोकल एंडपॉइंट चालू है। एक ऐसा मॉडल चुनें जो टूल कॉलिंग का विज्ञापन करता हो — एजेंट लूप उस पर निर्भर करता है।
एक टूल-सक्षम लोकल मॉडल चलाएँ + API की पुष्टि करें
# Start a model that supports tool/function calling
ollama run llama3.1
# In another terminal, confirm the local OpenAI-compatible endpoint is live.
# Ollama serves it at http://localhost:11434/v1 — no internet required.
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.1",
"messages": [{"role": "user", "content": "Reply with the single word: ready"}]
}'2. मॉडल-अज्ञेयवादी एजेंट लूप
लूप जानबूझकर मूर्ख है: यह चैट एंडपॉइंट को मैसेज और एक टूल स्कीमा अग्रेषित करता है, और जब भी मॉडल किसी टूल को कॉल करने के लिए कहता है, यह टूल चलाता है और परिणाम वापस फ़ीड करता है। चूँकि यह केवल OpenAI चैट आकार बोलता है, वही लूप अभी लोकल एंडपॉइंट के विरुद्ध और बाद में किसी दूसरे प्रोवाइडर के विरुद्ध काम करता है — आप एक base_url बदलते हैं, तर्क नहीं।
from openai import OpenAI
# Point at the LOCAL model. Swap base_url/api_key later to change providers —
# the loop below does not change. That is what "model-agnostic" means here.
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
MODEL = "llama3.1"
MAX_STEPS = 8 # hard cap on loop iterations (a guardrail — see step 4)
def run_agent(user_goal, tool_schemas, dispatch):
messages = [
{"role": "system", "content": "You are a local assistant. Use tools when needed."},
{"role": "user", "content": user_goal},
]
for _ in range(MAX_STEPS):
resp = client.chat.completions.create(
model=MODEL, messages=messages, tools=tool_schemas,
)
msg = resp.choices[0].message
if not msg.tool_calls:
return msg.content # model gave a final answer
messages.append(msg)
for call in msg.tool_calls:
result = dispatch(call) # runs through the guardrail + MCP server
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
return "Stopped: hit the step cap." # never loop forever
tool_schemas टूल्स की सूची है (OpenAI फ़ंक्शन-कॉलिंग प्रारूप में), और dispatch वह एकमात्र फ़ंक्शन है जो तय करता है कि किसी अनुरोधित टूल को वास्तव में चलाना है या नहीं और कैसे चलाना है — वहीं गार्डरेल और MCP सर्वर रहते हैं।
3. एक लोकल MCP सर्वर के ज़रिए टूल्स
लूप के अंदर टूल्स को हार्ड-कोड करने के बजाय, उन्हें एक लोकल MCP सर्वर के ज़रिए एक्सपोज़ करें। MCP एक AI क्लाइंट को बाहरी टूल्स से जोड़ने के लिए एक ओपन मानक है; एक लोकल सर्वर आपकी मशीन पर एक छोटे प्रोग्राम के रूप में चलता है और क्लाइंट से stdio पर बात करता है, इसलिए आपका डेटा और कार्य बॉक्स पर ही रहते हैं। (यह सही सीमा क्यों है, और एक सर्वर कैसे बनाया जाए, यह MCP के साथ Claude को लोकल टूल्स से जोड़ें पर कवर किया गया है।)
एक न्यूनतम Python MCP सर्वर जो एक सुरक्षित, केवल-पढ़ने वाला टूल एक्सपोज़ करता है:
# server.py — a tiny local MCP server exposing one read-only tool.
# Run it over stdio; an MCP client (your agent, Claude Code, ...) connects to it.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("local-tools")
@mcp.tool()
def search_notes(query: str) -> str:
"""Search the user's local notes folder and return matching snippets."""
# ... read from a LOCAL directory only; never reach outside it ...
return f"(stub) matches for: {query}"
if __name__ == "__main__":
mcp.run() # stdio transport by default — local, no network
एजेंट इस सर्वर से कनेक्ट होता है, उससे अपने टूल्स की सूची बनाने के लिए कहता है, हर एक को उस OpenAI टूल स्कीमा में परिवर्तित करता है जिसे आपका लूप पहले से समझता है, और मॉडल की टूल कॉल्स को सर्वर पर रूट करता है। वही लूप, वास्तविक क्षमताएँ — और सर्वर किसी भी MCP-बोलने वाले क्लाइंट द्वारा पुनः उपयोग योग्य है।
4. गार्डरेल (इसे न छोड़ें)
यही एक खिलौने और उस चीज़ के बीच का अंतर है जिस पर आप अपनी अपनी मशीन पर भरोसा करेंगे। स्टेप 2 का dispatch फ़ंक्शन वह एकमात्र चोकपॉइंट है जहाँ हर टूल कॉल के चलने से पहले उसका निरीक्षण किया जाता है। तीन काम:
READ_ONLY = {"search_notes", "read_file", "list_dir"}
def dispatch(call):
name = call.function.name
args = call.function.arguments
# 1) APPROVAL: read-only tools auto-run; everything else asks a human first.
if name not in READ_ONLY:
if not human_approves(name, args): # destructive => require consent
return "DENIED by user."
# 2) The MCP server does the actual work (it, too, is sandboxed to safe paths).
result = call_mcp_tool(name, args)
# 3) UNTRUSTED RESULT: a tool result is data, not instructions. Do not let it
# silently become a new command to the model (prompt-injection defense).
return f"<tool_result name={name}>\n{result}\n</tool_result>"
इसे लूप में पहले से मौजूद लूप/बजट सीमाओं (MAX_STEPS, साथ ही एक टोकन सीमा जिसे आप प्रति रन ट्रैक करते हैं) के साथ जोड़ें और आपके पास वे तीन नियंत्रण हैं जो मायने रखते हैं: किसी भी विनाशकारी चीज़ के लिए लूप में एक मानव, एक कठोर स्टॉप ताकि एजेंट हमेशा के लिए घूम या खर्च न कर सके, और टूल आउटपुट को अविश्वसनीय टेक्स्ट मानने की आदत।
5. वैकल्पिक — स्मार्ट लेयर के रूप में Claude
डिफ़ॉल्ट रूप से, कभी क्लाउड को कॉल न करें। लेकिन कुछ स्टेप्स वास्तव में एक छोटे लोकल मॉडल की क्षमता से परे होते हैं — पेचीदा मल्टी-स्टेप योजना, एक रिफैक्टर जो सही होना चाहिए, लंबे संदर्भ में एक संश्लेषण। केवल उन स्टेप्स के लिए, एजेंट Claude API पर एस्केलेट कर सकता है, एक बेहतर उत्तर पा सकता है, और लोकल लूप में वापस गिर सकता है। यह Claude + लोकल मॉडल्स से राउटर / ड्राफ्ट-फिर-रिफाइन विचार है, जो एक बार में एक स्टेप पर लागू होता है।
import anthropic
cloud = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
def hard_step(prompt, allow_cloud=False):
"""Escalate ONE hard step to Claude — only when explicitly allowed."""
if not allow_cloud:
return None # default: stay fully local, send nothing off-device
msg = cloud.messages.create(
model="claude-sonnet-4-5", # check current model ids before pinning
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
दो नियम इसे ईमानदार रखते हैं: क्लाउड पथ ऑप्ट-इन है (डिफ़ॉल्ट रूप से बंद), और आप केवल वही भेजते हैं जिसकी उस एकल स्टेप को ज़रूरत है — आपका पूरा संदर्भ नहीं। लोकल मॉडल वर्कहॉर्स बना रहता है; Claude वह विशेषज्ञ है जिसे आप कठिन 5% के लिए बुलाते हैं। सटीक वर्तमान मॉडल ids और कीमत के लिए, नीचे दिया गया वेरिफ़ाई नोट देखें।
- लोकल एजेंट अब भी आपकी मशीन पर वास्तविक कार्य करते हैं — टूल्स को सैंडबॉक्स करें, विनाशकारी स्टेप्स के लिए अनुमोदन की मांग करें, लूप्स/बजट को सीमित करें, और टूल परिणामों को अविश्वसनीय (प्रॉम्प्ट-इंजेक्शन) मानें।
खुद को परखें
खुद को परखें
0/4- एक निजी असिस्टेंट एक लूप में जुड़े चार बॉक्स हैं: लोकल मॉडल + मॉडल-अज्ञेयवादी एजेंट + लोकल MCP टूल्स + एक गार्डरेल — Claude एक वैकल्पिक पाँचवें बॉक्स के रूप में
- लोकल डिफ़ॉल्ट है और गोपनीयता की गारंटी: मॉडल, लूप, टूल्स, और आपका डेटा सब आपकी मशीन पर रहते हैं जब तक आप क्लाउड पथ में ऑप्ट-इन न करें
- लूप को मूर्ख और मॉडल-अज्ञेयवादी रखें (OpenAI चैट आकार) और वास्तविक क्षमताओं को एक लोकल MCP सर्वर के पीछे रखें — एक बार बनाएँ, क्लाइंट्स में पुनः उपयोग करें
- गार्डरेल वह हिस्सा है जिसे आप छोड़ नहीं सकते: विनाशकारी स्टेप्स को अनुमोदित करें, लूप्स/बजट को सीमित करें, टूल्स को सैंडबॉक्स करें, और टूल परिणामों को अविश्वसनीय मानें
- Claude कठिन 5% के लिए ऑप्ट-इन स्मार्ट लेयर है — एक बार में एक स्टेप एस्केलेट करें और केवल वही भेजें जिसकी उस स्टेप को ज़रूरत है
- अस्थिर विवरण (मॉडल नाम, ids, कीमतें, SDK APIs) वेरिफ़ाई नोट्स के पीछे बैठते हैं; आर्किटेक्चर टिकाऊ है, संख्याएँ नहीं
स्रोत और अतिरिक्त पठन
- Ollama — OpenAI-संगत API (localhost:11434, tools पैरामीटर)
- Ollama — टूल समर्थन घोषणा
- Ollama मॉडल लाइब्रेरी (वर्तमान टूल-सक्षम मॉडल)
- Model Context Protocol — परिचय
- Model Context Protocol — आधिकारिक SDKs (Python, TypeScript)
- MCP Python SDK (GitHub)
- MCP TypeScript SDK (GitHub)
- Anthropic — Claude मॉडल और कीमत