स्टेट मशीन और एजेंट: LangGraph के साथ विश्वसनीय वर्कफ्लो बनाना
अधिकांश एजेंट ट्यूटोरियल एक सरल लूप दिखाते हैं: Claude से पूछो, प्रतिक्रिया पार्स करो, टूल कॉल करो, दोहराओ। यह डेमो के लिए काम करता है। प्रोडक्शन में, आपको निश्चितता, त्रुटि पुनर्प्राप्ति, मानव अनुमोदन गेट और ऑडिटेबिलिटी की जरूरत होती है।
LangGraph एजेंट वर्कफ्लो में स्टेट मशीन लाता है। if स्टेटमेंट से जुड़े एड-हॉक लूप के बजाय, आपको एक स्पष्ट ग्राफ मिलता है: नामित नोड (लॉजिक यूनिट), टाइप किए गए किनारे (ट्रांजिशन) और एक साझा स्टेट स्कीमा जो पूरे निष्पादन में प्रवाहित होती है।
एजेंट के लिए स्टेट मशीन क्यों?
एड-हॉक लूप की समस्या
एक विशिष्ट एजेंट लूप इस तरह दिखता है:
messages = []while True: response = claude.messages.create(model=..., messages=messages) if response.stop_reason == "end_turn": break for tool_call in get_tool_calls(response): result = execute_tool(tool_call) messages.append(tool_result(tool_call.id, result))यह दो-तीन टूल्स के लिए पठनीय है। पांच टूल्स, सशर्त पथ, एक मानव अनुमोदन चरण और रिट्री लॉजिक जोड़ें — और आपके पास सैकड़ों लाइनों का उलझा हुआ कंट्रोल फ्लो होगा।
गहरी समस्या अंतर्निहित स्टेट है। एजेंट किस चरण में है? उसने क्या डेटा एकत्र किया है? सब कुछ messages में रहता है — बिना लागू स्कीमा के एक अनटाइप्ड ब्लॉब।
स्टेट मशीन एक समाधान के रूप में
स्टेट मशीन अंतर्निहित को स्पष्ट बनाती है। आप परिभाषित करते हैं:
- नोड — असतत लॉजिक यूनिट जो वर्तमान स्टेट प्राप्त करती है, एक काम करती है और स्टेट अपडेट लौटाती है।
- किनारा — नोड्स के बीच ट्रांजिशन, बिना शर्त (
A → B हमेशा) या सशर्त। - स्टेट — एक टाइप किया हुआ डिक्शनरी जो पूरे ग्राफ में प्रवाहित होती है।
LangGraph कब नहीं उपयोग करें
सरल एकल-चरण कार्यों के लिए, सीधा API कॉल तेज और स्पष्ट है। LangGraph का उपयोग करें जब वर्कफ्लो में हो:
- अनुक्रम में चलने वाले कई अलग-अलग चरण
- मध्यवर्ती परिणामों पर आधारित सशर्त शाखाएं
- मानव भागीदारी वाले चरण
- त्रुटि पुनर्प्राप्ति या रिट्री लॉजिक
- ऑडिटेबिलिटी आवश्यकताएं
LangGraph के मूल तत्व
स्टेट परिभाषित करना
from typing import TypedDict
class ResearchState(TypedDict): query: str research_notes: str draft: str review_feedback: str is_approved: boolनोड
import anthropic
client = anthropic.Anthropic()
def research_node(state: ResearchState) -> dict: response = client.messages.create( model="claude-opus-4-6", max_tokens=2048, messages=[{ "role": "user", "content": f"इस विषय पर गहन शोध करें: {state['query']}" }] ) return {"research_notes": response.content[0].text}
def draft_node(state: ResearchState) -> dict: response = client.messages.create( model="claude-opus-4-6", max_tokens=2048, messages=[{ "role": "user", "content": f"इन नोट्स के आधार पर एक ड्राफ्ट लिखें:\n{state['research_notes']}" }] ) return {"draft": response.content[0].text}किनारे और सशर्त रूटिंग
from typing import Literal
def route_after_check(state: ResearchState) -> Literal["human_review", "draft"]: if state.get("review_feedback"): return "human_review" return "draft"
workflow.add_conditional_edges( "research", route_after_check, {"human_review": "review_node", "draft": "draft_node"})ग्राफ बनाना और चलाना
from langgraph.graph import StateGraph, END
workflow = StateGraph(ResearchState)workflow.add_node("research", research_node)workflow.add_node("draft", draft_node)workflow.set_entry_point("research")workflow.add_edge("research", "draft")workflow.add_edge("draft", END)
graph = workflow.compile()result = graph.invoke({"query": "MCP प्रोटोकॉल क्या है?"})print(result["draft"])दस्तावेज़ समीक्षा वर्कफ्लो बनाना
स्टेट डिज़ाइन
class DocumentState(TypedDict): document: str key_terms: list[str] compliance_issues: list[str] summary: str human_feedback: str is_approved: boolनोड्स लागू करना
import json
def extract_terms_node(state: DocumentState) -> dict: response = client.messages.create( model="claude-opus-4-6", max_tokens=512, messages=[{ "role": "user", "content": ( "इस दस्तावेज़ से ठीक 5 मुख्य शब्द निकालें। " "उन्हें स्ट्रिंग्स के JSON ऐरे के रूप में लौटाएं।\n\n" f"दस्तावेज़: {state['document']}" ) }] ) try: raw = response.content[0].text.strip() if raw.startswith("```"): raw = raw.split("```")[1] if raw.startswith("json"): raw = raw[4:] terms = json.loads(raw.strip()) except (json.JSONDecodeError, IndexError): terms = [t.strip() for t in response.content[0].text.split("\n") if t.strip()][:5] return {"key_terms": terms}
def check_compliance_node(state: DocumentState) -> dict: response = client.messages.create( model="claude-opus-4-6", max_tokens=1024, messages=[{ "role": "user", "content": ( "इस दस्तावेज़ में अनुपालन समस्याओं की जांच करें। " "देखें: PII (नाम, आधार नंबर, ईमेल, फोन), " "गोपनीय चिह्न, असत्यापित दावे।\n\n" "समस्या विवरण का JSON ऐरे लौटाएं। " "कोई समस्या नहीं होने पर खाली ऐरे [] लौटाएं।\n\n" f"दस्तावेज़: {state['document']}" ) }] ) try: raw = response.content[0].text.strip() if raw.startswith("```"): raw = raw.split("```")[1] if raw.startswith("json"): raw = raw[4:] issues = json.loads(raw.strip()) except (json.JSONDecodeError, IndexError): issues = [] return {"compliance_issues": issues}
def human_review_node(state: DocumentState) -> dict: print("\n=== मानव समीक्षा आवश्यक ===") for issue in state["compliance_issues"]: print(f" - {issue}") return { "human_feedback": "PII हटाने के बाद समीक्षा और अनुमोदित", "is_approved": True }
def summarize_node(state: DocumentState) -> dict: response = client.messages.create( model="claude-opus-4-6", max_tokens=256, messages=[{ "role": "user", "content": f"इस दस्तावेज़ को ठीक दो वाक्यों में सारांशित करें:\n\n{state['document']}" }] ) return {"summary": response.content[0].text, "is_approved": True}ग्राफ जोड़ना
def route_after_compliance(state: DocumentState) -> Literal["human_review", "summarize"]: if state.get("compliance_issues"): return "human_review" return "summarize"
workflow = StateGraph(DocumentState)workflow.add_node("extract", extract_terms_node)workflow.add_node("check", check_compliance_node)workflow.add_node("review", human_review_node)workflow.add_node("summarize", summarize_node)
workflow.set_entry_point("extract")workflow.add_edge("extract", "check")workflow.add_conditional_edges( "check", route_after_compliance, {"human_review": "review", "summarize": "summarize"})workflow.add_edge("review", "summarize")workflow.add_edge("summarize", END)
graph = workflow.compile()
result = graph.invoke({ "document": "रोगी राम शर्मा (आधार: 1234-5678-9012) को उच्च रक्तचाप है।", "key_terms": [], "compliance_issues": [], "summary": "", "human_feedback": "", "is_approved": False,})print(result["summary"])मानव चेकपॉइंट और व्यवधान
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()graph = workflow.compile( checkpointer=checkpointer, interrupt_before=["review"])
thread_config = {"configurable": {"thread_id": "doc-review-001"}}result = graph.invoke(initial_state, config=thread_config)
current_state = graph.get_state(thread_config)print("समस्याएं:", current_state.values["compliance_issues"])
graph.update_state( thread_config, {"human_feedback": "मैन्युअल PII हटाने के बाद अनुमोदित", "is_approved": True})
final_result = graph.invoke(None, config=thread_config)दृढ़ता और प्रोडक्शन तैनाती
from langgraph.checkpoint.postgres import PostgresSaver
with PostgresSaver.from_conn_string("postgresql://...") as checkpointer: graph = workflow.compile(checkpointer=checkpointer)
import asyncio
async def batch_process(documents: list[str]) -> list[dict]: return await asyncio.gather(*[ graph.ainvoke({"document": doc, "key_terms": [], "compliance_issues": [], "summary": "", "human_feedback": "", "is_approved": False}) for doc in documents ])सामान्य पैटर्न और गलतियां
- फैन-आउट/फैन-इन:
Sendका उपयोग करके कई नोड्स को समानांतर में चलाएं। - अत्यधिक सशर्त रूटिंग: यदि रूटिंग फ़ंक्शन में बहुत अधिक शाखाएं हैं तो उप-ग्राफ में विभाजित करें।
- स्टेट का अधिक भार: बड़े डेटा के बजाय संदर्भ (डेटाबेस ID, S3 कुंजियां) संग्रहीत करें।
- नोड्स का अलग परीक्षण: चूंकि नोड्स सरल फ़ंक्शन हैं, उन्हें सीधे यूनिट-टेस्ट किया जा सकता है।
def test_extract_terms_node(): state = { "document": "कृत्रिम बुद्धिमत्ता आधुनिक समाज को तेजी से बदल रही है।", "key_terms": [], "compliance_issues": [], "summary": "", "human_feedback": "", "is_approved": False, } result = extract_terms_node(state) assert "key_terms" in result assert isinstance(result["key_terms"], list)LangGraph एड-हॉक लूप को स्पष्ट, डीबगेबल और फिर से शुरू करने योग्य वर्कफ्लो से बदल देता है। एक रेखीय ग्राफ (A → B → C → समाप्त) से शुरू करें। जब शाखाओं की जरूरत हो तो एक सशर्त किनारा जोड़ें। जब अनुमोदन की जरूरत हो तो एक मानव चेकपॉइंट जोड़ें। अधिकांश प्रोडक्शन एजेंट वर्कफ्लो को बस इन तीन पैटर्न की जरूरत होती है।
संबंधित लेख
- एजेंट एरर रिकवरी: प्रोडक्शन विश्वसनीयता के लिए 5 पैटर्न
- मल्टी-एजेंट पैटर्न: ऑर्केस्ट्रेटर, Workers और पाइपलाइन
- स्वायत्त एजेंट सिस्टम में डिबगिंग और ऑब्जर्वेबिलिटी
- एजेंटिक विकास का परिचय