الدرس 11 من 22

بناء التطبيقات مع Ollama

LangGraph مع Ollama

3 دقيقة للقراءة

LangGraph يمكّن من بناء سير عمل ذكاء اصطناعي متعدد الخطوات والحالات مع دورات ومنطق شرطي. مع Ollama، يمكنك إنشاء وكلاء ذكاء اصطناعي محليين متطورين.

التثبيت

pip install langgraph langchain-ollama

مفاهيم LangGraph

┌─────────────────────────────────────────────────────────────────┐
│                    المفاهيم الأساسية لـ LangGraph                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  الحالة: بيانات مشتركة تستمر عبر العقد                          │
│  العقد: دوال تعالج وتحدث الحالة                                 │
│  الحواف: اتصالات بين العقد (يمكن أن تكون شرطية)                 │
│  الرسم البياني: تعريف سير العمل الكامل                          │
│                                                                 │
│  الميزة الرئيسية: الدورات! العقد يمكنها الرجوع للتكرار          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

رسم بياني خطي بسيط

from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_ollama import ChatOllama

# حدد الحالة
class State(TypedDict):
    question: str
    answer: str

# هيئ Ollama
llm = ChatOllama(model="llama3.2")

# حدد العقد
def think(state: State) -> State:
    """ولّد إجابة."""
    response = llm.invoke(f"Answer concisely: {state['question']}")
    return {"answer": response.content}

# ابنِ الرسم البياني
graph = StateGraph(State)
graph.add_node("think", think)
graph.set_entry_point("think")
graph.add_edge("think", END)

# جمّع وشغّل
app = graph.compile()
result = app.invoke({"question": "What is Python?"})
print(result["answer"])

التوجيه الشرطي

from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END
from langchain_ollama import ChatOllama

class State(TypedDict):
    query: str
    query_type: str
    response: str

llm = ChatOllama(model="llama3.2")

def classify_query(state: State) -> State:
    """صنف نوع الاستعلام."""
    response = llm.invoke(
        f"Classify this query as 'code' or 'general'. "
        f"Reply with just the word.\nQuery: {state['query']}"
    )
    query_type = response.content.strip().lower()
    return {"query_type": query_type}

def handle_code_query(state: State) -> State:
    """تعامل مع أسئلة البرمجة."""
    response = llm.invoke(
        f"As a coding expert, answer: {state['query']}"
    )
    return {"response": response.content}

def handle_general_query(state: State) -> State:
    """تعامل مع الأسئلة العامة."""
    response = llm.invoke(
        f"Answer this general question: {state['query']}"
    )
    return {"response": response.content}

def route_query(state: State) -> Literal["code", "general"]:
    """وجّه بناءً على نوع الاستعلام."""
    if "code" in state["query_type"]:
        return "code"
    return "general"

# ابنِ رسم بياني مع توجيه شرطي
graph = StateGraph(State)

graph.add_node("classify", classify_query)
graph.add_node("code", handle_code_query)
graph.add_node("general", handle_general_query)

graph.set_entry_point("classify")
graph.add_conditional_edges(
    "classify",
    route_query,
    {"code": "code", "general": "general"}
)
graph.add_edge("code", END)
graph.add_edge("general", END)

app = graph.compile()

# اختبر
result = app.invoke({"query": "How do I write a for loop in Python?"})
print(result["response"])

التحسين التكراري (الدورات)

from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_ollama import ChatOllama

class State(TypedDict):
    task: str
    draft: str
    feedback: str
    iteration: int

llm = ChatOllama(model="llama3.2")

def write_draft(state: State) -> State:
    """اكتب أو حسّن مسودة."""
    if state.get("draft"):
        prompt = f"""Improve this draft based on feedback:
Draft: {state['draft']}
Feedback: {state['feedback']}
Write an improved version:"""
    else:
        prompt = f"Write a short paragraph about: {state['task']}"

    response = llm.invoke(prompt)
    return {
        "draft": response.content,
        "iteration": state.get("iteration", 0) + 1
    }

def critique(state: State) -> State:
    """انتقد المسودة الحالية."""
    response = llm.invoke(
        f"Critique this draft briefly. What could be better?\n\n{state['draft']}"
    )
    return {"feedback": response.content}

def should_continue(state: State) -> str:
    """قرر إذا يجب التحسين أكثر."""
    if state["iteration"] >= 3:  # حد أقصى 3 تكرارات
        return "end"
    return "continue"

# ابنِ رسم بياني مع دورة
graph = StateGraph(State)

graph.add_node("write", write_draft)
graph.add_node("critique", critique)

graph.set_entry_point("write")
graph.add_edge("write", "critique")
graph.add_conditional_edges(
    "critique",
    should_continue,
    {"continue": "write", "end": END}
)

app = graph.compile()

result = app.invoke({"task": "Explain machine learning to a child"})
print(f"المسودة النهائية (بعد {result['iteration']} تكرارات):")
print(result["draft"])

نمط وكيل ReAct

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_ollama import ChatOllama
import operator

class AgentState(TypedDict):
    question: str
    thoughts: Annotated[list[str], operator.add]
    answer: str
    step: int

llm = ChatOllama(model="llama3.2")

def think(state: AgentState) -> AgentState:
    """فكر في المشكلة."""
    response = llm.invoke(
        f"Question: {state['question']}\n"
        f"Previous thoughts: {state['thoughts']}\n"
        f"Think step by step. What's your next thought?"
    )
    return {
        "thoughts": [response.content],
        "step": state.get("step", 0) + 1
    }

def decide(state: AgentState) -> AgentState:
    """قرر إذا لدينا ما يكفي للإجابة."""
    response = llm.invoke(
        f"Question: {state['question']}\n"
        f"Thoughts so far: {state['thoughts']}\n"
        f"Can you answer now? Reply YES or NO."
    )
    return {"answer": response.content}

def answer(state: AgentState) -> AgentState:
    """ولّد الإجابة النهائية."""
    response = llm.invoke(
        f"Question: {state['question']}\n"
        f"Based on these thoughts: {state['thoughts']}\n"
        f"Give a clear, final answer:"
    )
    return {"answer": response.content}

def should_answer(state: AgentState) -> str:
    """تحقق إذا جاهز للإجابة."""
    if state["step"] >= 3:  # فرض الإجابة بعد 3 خطوات
        return "answer"
    if "YES" in state.get("answer", "").upper():
        return "answer"
    return "think"

graph = StateGraph(AgentState)

graph.add_node("think", think)
graph.add_node("decide", decide)
graph.add_node("answer", answer)

graph.set_entry_point("think")
graph.add_edge("think", "decide")
graph.add_conditional_edges(
    "decide",
    should_answer,
    {"think": "think", "answer": "answer"}
)
graph.add_edge("answer", END)

app = graph.compile()

result = app.invoke({
    "question": "What's the best way to learn programming?",
    "thoughts": [],
    "step": 0
})
print(f"عملية التفكير: {len(result['thoughts'])} خطوات")
print(f"الإجابة: {result['answer']}")

تصور الرسم البياني

# ولّد تصور الرسم البياني (يتطلب graphviz)
from IPython.display import Image, display

# إذا تستخدم Jupyter
display(Image(app.get_graph().draw_mermaid_png()))

# أو احفظ لملف
with open("graph.png", "wb") as f:
    f.write(app.get_graph().draw_mermaid_png())

ملخص الأنماط الرئيسية

النمط حالة الاستخدام
خطي معالجة تسلسلية بسيطة
شرطي توجيه بناءً على المحتوى/التصنيف
دوري تحسين تكراري، تصحيح ذاتي
ReAct حلقات استدلال + عمل

LangGraph يمكّن سير عمل ذكاء اصطناعي معقد ومتعدد الحالات بالكامل على عتاد محلي. في الوحدة التالية، سنستكشف أنماط متقدمة بما في ذلك RAG المحلي واستدعاء الوظائف. :::

اختبار

الوحدة 3: بناء التطبيقات مع Ollama

خذ الاختبار