Agent Engineering
מערכות סוכנים בפרודקשן — LangGraph, evals, RAG, memory, multi-agent.
דפוסי סוכנים
reflection, ReAct, planning, multi-agent.
בעולם פיתוח ה-AI המתקדם, מעבר ממענה פשוט (Single-prompt) למערכות אוטונומיות דורש הבנה מעמיקה של דפוסי עיצוב של סוכנים (Agentic Design Patterns). במקום להסתמך על ריצה חד-פעמית של מודל שפה, דפוסים אלו מאפשרים למודל לפעול בלולאות איטרטיביות, לתכנן מראש, לבקר את עצמו ולשתף פעולה עם סוכנים אחרים כדי לפתור משימות מורכבות. ארבעת הדפוסים המרכזיים שהוגדרו על ידי Andrew Ng הם: Reflection (משוב עצמי), Tool Use (שימוש בכלים), Planning (תכנון) ו-Multi-agent (מערכות מרובות סוכנים).
דפוס ה-Reflection מאפשר לסוכן לבחון את הפלט של עצמו, לזהות טעויות ולתקן אותן לפני הצגת התוצאה הסופית למשתמש. שילוב של תכונה זו עם ReAct (Reasoning and Acting) מייצר מחזור עבודה שבו הסוכן חושב (Thought), פועל בעזרת כלי (Action), ומתבונן בתוצאה (Observation). גישה זו מונעת הזיות (Hallucinations) ומשפרת משמעותית את דיוק התוצאות במשימות כתיבת קוד או מחקר.
כאשר המשימה גדולה מדי עבור סוכן בודד, אנו פונים ל-Planning ו-Multi-agent systems. דפוס התכנון מפרק מטרה מורכבת לתתי-משימות קטנות ומגדיר סדר פעולות הגיוני. במערך מרובה סוכנים (Multi-agent), אנו מחלקים תפקידים מוגדרים לסוכנים שונים (למשל: סוכן כותב, סוכן עורך וסוכן בודק קוד). הפרדת הסמכויות הזו מאפשרת לכל סוכן להתמחות בכלי עבודה ספציפיים ולהגיע לרמת ביצועים גבוהה בהרבה, כפי שניתן לראות בספריות מודרניות כמו CrewAI או Pydantic AI.
כדי להבין איך זה עובד בפועל, נראה דוגמה בסיסית של דפוס Reflection פשוט בפייתון. בדוגמה זו, אנו מבקשים מהסוכן לכתוב קוד, ואז מריצים שלב נוסף שמבקר את הקוד ומשפר אותו:
def generate_with_reflection(prompt, client):
# שלב 1: יצירת טיוטה ראשונית
draft = client.generate(f"Write code for: {prompt}")
# שלב 2: ביקורת עצמית (Reflection)
critique = client.generate(f"Critique this code for bugs and efficiency:\n{draft}")
# שלב 3: שיפור על בסיס הביקורת
final_code = client.generate(f"Improve this code:\n{draft}\nBased on critique:\n{critique}")
return final_code
כיום, ענקיות הטכנולוגיה והקהילה מפתחות כלים ייעודיים ליישום דפוסים אלו בקלות. לדוגמה, חברת Anthropic שחררה את ה-Claude Agent SDK המפשט בניית סוכנים אינטראקטיביים, בעוד OpenAI מציעה את ספריית OpenAI Agents לניהול שיחות מרובות סוכנים ומעברי מצבים (handoffs). בחירה בפריים-וורק הנכון תלויה במורכבות הפרויקט ובשפת הפיתוח המועדפת עליכם.
הצעד הבא שלכם: כדי לראות את הדפוסים האלו בפעולה בצורה מוחשית, מומלץ לשכפל ולהריץ את פרויקט הקוד AI Agentic Design Patterns. פרויקט זה מיישם בצורה פשוטה וברורה את ארבעת הדפוסים המרכזיים ומספק נקודת זינוק מעולה להבנת הארכיטקטורה של סוכני AI מתקדמים.
LangGraph
graph-based agent flows — state machines, checkpoints.
בעולם פיתוח סוכני ה-AI (Agents), מודלים ליניאריים פשוטים (כמו שרשראות קלאסיות) כבר אינם מספיקים למשימות מורכבות ומרובות שלבים. כאן נכנסת לתמונה LangGraph – ספרייה מתקדמת מבית LangChain המאפשרת לבנות סוכנים מבוססי גרפים (Graph-based Agents) המנוהלים כמכונות מצבים (State Machines). בניגוד לשרשראות קשיחות, LangGraph מאפשרת לולאות (Loops), תנאים מורכבים וקבלת החלטות דינמית, שבהם כל שלב בגרף מעדכן מצב מרכזי משותף (State).
היתרון המשמעותי ביותר של LangGraph טמון ביכולת לנהל "מצב" (State) מורכב בצורה רובסטית ולשמור על עקביות לאורך זמן. בעזרת מנגנון ה-Checkpoints (נקודות שמירה), המערכת מסוגלת לשמור את מצב הגרף בכל שלב ושלב. יכולת זו קריטית עבור תרחישים של Human-in-the-loop (שבהם נדרש אישור אנושי לפני ביצוע פעולה), התאוששות משגיאות (Fault Tolerance), וניהול שיחות ארוכות טווח. הגרסאות האחרונות, כמו LangGraph 1.2.0, מציגות שיפורים משמעותיים בארכיטקטורה זו.
כדי להתחיל לעבוד עם LangGraph, אנו מגדירים StateGraph שמקבל טיפוס נתונים המייצג את ה-State. לאחר מכן, אנו מוסיפים צמתים (Nodes) המייצגים פונקציות או קריאות ל-LLM, וקצוות (Edges) המגדירים את זרימת המידע ביניהם. כדי לאפשר שמירת מצב אוטומטית, נחבר לגרף Checkpointer, למשל באמצעות הרחבות ייעודיות כמו langgraph-checkpoint או פתרונות פרודקשן מבוססי בסיסי נתונים כמו LangGraph-Checkpoint-Postgres.
הנה דוגמה מינימלית להגדרת גרף בסיסי עם State ו-Checkpointer פנימי בזיכרון (Memory):
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict
class AgentState(TypedDict):
messages: list[str]
def call_model(state: AgentState):
return {"messages": state["messages"] + ["AI Response"]}
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_edge(START, "agent")
workflow.add_edge("agent", END)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
ברמת הפרודקשן, ניהול ה-State הופך למאתגר במיוחד. השימוש ב-Checkpointers מאפשר לנו לבצע "מסע בזמן" (Time Travel) בתוך הריצה של הסוכן – להחזיר את המצב לאחור, לתקן שגיאה של המודל, או להריץ מחדש ענף ספציפי בגרף. ארכיטקטורה זו הופכת מערכות AI לאמינות וצפויות בהרבה, ומאפשרת ניטור מלא של כל שלב בקבלת ההחלטות של הסוכן.
הצעד הבא שלכם: כדי לצלול לעומק ולהתחיל לפתח באופן מעשי, מומלץ לשכפל את מאגר הקוד הרשמי LangGraph 101. זהו קורס מעשי מצוין המכסה בניית State Machines, עבודה עם Checkpointers ושילוב בני אדם בתהליך העבודה (Human-in-the-loop). לאחר מכן, תוכלו לחקור עשרות דוגמאות מתקדמות נוספות ב-Repository הראשי של LangGraph.
RAG בפרודקשן
ingestion, chunking, hybrid search, re-ranking, evals.
מערכות Retrieval-Augmented Generation (RAG) פשוטות קל מאוד לבנות, אך העברתן לסביבת ייצור (Production) אמיתית דורשת התמודדות עם אתגרים מורכבים של דיוק, מהירות וסקלאביליות. RAG בפרודקשן הוא תהליך הנדסי מקיף הכולל אופטימיזציה של כל שלב בצינור המידע (Pipeline): החל משלב הזנת הנתונים (Ingestion) וחלוקתם לפיסות מידע משמעותיות (Chunking), דרך חיפוש היברידי (Hybrid Search) ודירוג מחדש (Re-ranking), ועד להערכה שיטתית (Evaluation) של ביצועי המערכת. ללא שלבים אלו, המערכת עלולה לסבול מהזיות (Hallucinations), זמני תגובה ארוכים וחוסר רלוונטיות של המידע שנשלף.
השלב הראשון והקריטי ביותר הוא הכנת המידע. במקום לחתוך טקסטים בצורה שרירותית לפי מספר תווים קבוע, בפרודקשן נרצה להשתמש באסטרטגיות Chunking מתקדמות הלוקחות בחשבון את המבנה הסמנטי של המסמך (כמו פסקאות, כותרות או טבלאות). חלוקה נכונה מונעת אובדן הקשר ומאפשרת למודל השפה לקבל בדיוק את המידע הדרוש לו. בנוסף, תהליך ה-Ingestion צריך לתמוך בעדכונים שוטפים בזמן אמת ובניהול גרסאות של המידע השמור בבסיס הנתונים הווקטורי.
כדי להשיג את תוצאות השליפה הטובות ביותר, מערכות פרודקשן משלבות חיפוש סמנטי (Dense Retrieval) המבוסס על Embeddings יחד עם חיפוש טקסטואלי מסורתי (Sparse Retrieval כמו BM25). שילוב זה נקרא Hybrid Search. לאחר קבלת התוצאות הראשוניות משני העולמות, אנו מפעילים שלב של Re-ranking באמצעות מודלים ייעודיים (כמו Cross-Encoders). שלב זה מדרג מחדש את עשרות המסמכים שנשלפו ומסנן מתוכם רק את ה-Top-K הרלוונטיים ביותר עבור ה-LLM, מה שחוסך בעלויות ה-Tokens ומשפר את דיוק התשובה.
הנה דוגמה קצרה למימוש צינור שליפה ודירוג מחדש (Re-ranking) בסיסי באמצעות LlamaIndex:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.postprocessor import LLMRerank
# טעינת מסמכים ויצירת אינדקס
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
# הגדרת מנוע שאילתות עם Re-ranker
query_engine = index.as_query_engine(
similarity_top_k=10,
node_postprocessors=[LLMRerank(top_n=3)]
)
response = query_engine.query("כיצד לבצע אופטימיזציה ל-RAG?")
print(response)
השלב הסוגר של מחזור החיים בפרודקשן הוא הערכה (Evaluation). לא ניתן לשפר את מה שלא מודדים. באמצעות כלים כמו Ragas או TruLens, אנו מעריכים מדדים קריטיים כגון Faithfulness (האם התשובה מבוססת אך ורק על ההקשר שנשלף), Answer Relevance (האם התשובה עונה על השאלה), ו-Context Recall (האם שלפנו את כל המידע הרלוונטי). תהליך זה מאפשר לנו לבצע שינויים בארכיטקטורה בביטחון מלא ולמנוע נסיגה בביצועים. למידע נוסף על שיפור עקביות והערכה מבוססת AI, מומלץ לקרוא את המאמר שיפור עקביות תיוג באמצעות הגדרות חוקתיות מפורטות והערכה מבוססת AI. בנוסף, הבנת המגבלות של מודלי שפה קטנים או מתורגמים יכולה לסייע בעיצוב ה-Evals, כפי שמתואר במחקר על תרגום ספקטרלי תלוי בהשפעה לרציפות נמוכה של LLM.
הצעד הבא שלכם: כדי להתחיל ליישם RAG ברמת פרודקשן בצורה המהירה והיעילה ביותר, מומלץ להוריד ולהריץ את פרויקט ה-Starter של LlamaIndex Starter. פרויקט זה יאפשר לכם להתנסות בחיבור מקורות מידע מקומיים (כמו קובצי PDF) ולראות כיצד מתבצעים שלבי ה-Ingestion וה-Chunking הלכה למעשה.
Evals
איך מודדים סוכן — golden sets, LLM-as-judge, regression testing.
בעולם של פיתוח סוכני AI (Agents), בניית פרומפט ראשוני היא החלק הקל. האתגר האמיתי מתחיל כשמנסים לשפר את הסוכן מבלי לקלקל התנהגויות קודמות. כאן נכנס לתמונה המושג Evals (הערכות) — תשתית מדידה שיטתית ומבוססת נתונים שמחליפה את ה-"vibe check" (בדיקה ידנית אקראית) במדדים כמותיים. ללא מערכת Evals אמינה, כל שינוי קטן בקוד, בפרומפט או במודל הבסיס (כמו מעבר בין GPT-4o ל-Claude 3.5 Sonnet) עלול להוביל לנסיגה (Regression) בביצועים מבלי שנשים לב.
כדי להקים מערך Evals אפקטיבי, אנו זקוקים לשלושה רכיבים מרכזיים. הראשון הוא Golden Set (או Ground Truth) — קובץ נתונים איכותי ומנופה המכיל עשרות עד מאות דוגמאות של קלטים והתשובות האידיאליות המצופות מהסוכן. הרכיב השני הוא מנגנון ההערכה עצמו, שלעיתים קרובות משתמש בטכניקת LLM-as-judge. במקום להסתמך על השוואת טקסט מדויקת (שהיא קשיחה מדי עבור שפה טבעית), אנו רותמים מודל חזק כדי להעריך את איכות תשובת הסוכן על פי קריטריונים מוגדרים מראש כמו דיוק עובדתי, טון, ועמידה בהנחיות.
הרכיב השלישי הוא Regression Testing (בדיקות נסיגה) המשולבות בתהליך ה-CI/CD. בכל פעם שמתבצע שינוי בקוד הסוכן או ב-Prompts, מערכת ה-Evals מריצה מחדש את ה-Golden Set ומשווה את אחוזי ההצלחה לגרסה הקודמת. כלים מתקדמים כמו DSPy 3.2.0 מאפשרים לא רק להעריך, אלא גם לאפטם (Optimize) את הפרומפטים והמשקלים של הסוכן באופן אוטומטי על בסיס הציון שמתקבל מה-Eval. בנוסף, ספריות מודרניות כמו Pydantic AI v1.97.0 מספקות תמיכה מובנית בייצוג נתונים מובנה (Structured Outputs), מה שמקל משמעותית על כתיבת אסרטיות (Assertions) מדויקות בבדיקות.
הנה דוגמה פשוטה הממחישה כיצד נראה קוד הערכה בסיסי המשתמש בגישת LLM-as-judge כדי לבדוק האם תשובת הסוכן עונה על ציפיות ה-Golden Set:
from openai import OpenAI
client = OpenAI()
def evaluate_answer(query: str, agent_output: str, expected: str) -> bool:
judge_prompt = f"Query: {query}\nAgent: {agent_output}\nExpected: {expected}\nIs the agent correct? Answer only YES or NO."
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": judge_prompt}]
)
return response.choices[0].message.content.strip() == "YES"
# Test run
print(evaluate_answer("What is 2+2?", "The result is four.", "4")) # True
כדי להתחיל ליישם Evals בפרויקט שלכם, מומלץ להשתמש בכלים ייעודיים מהתעשייה במקום לפתח הכל מאפס. פרויקט OpenAI Evals מציע פריימוורק מקיף ומאות בדיקות מוכנות מראש שיכולות לשמש כבסיס מצוין. עבור עבודה יומיומית מהירה ואינטגרציה חלקה ל-CI/CD, כלי ה-CLI הפופולרי promptfoo מאפשר להגדיר בדיקות בצורה דקלרטיבית (קובצי YAML) ולהריץ השוואות בין מודלים ופרומפטים שונים בקלות רבה.
הצעד הבא שלכם: התקינו את promptfoo באופן מקומי, צרו קובץ promptfooconfig.yaml פשוט עם 3 דוגמאות של קלט ופלט מצופה (Golden Set), והריצו את הפקודה promptfoo eval. זוהי הדרך המהירה ביותר לעבור מניחושים למדידה מדעית ומדויקת של ביצועי הסוכן שלכם.
Memory לסוכנים
short-term, long-term, vector + symbolic memory.
במערכות בינה מלאכותית מתקדמות, סוכנים (Agents) נדרשים ליותר מאשר פשוט להגיב לקלט מיידי בתוך חלון ההקשר (Context Window). כדי לפעול בצורה אוטונומית ואפקטיבית לאורך זמן, סוכנים זקוקים למערכת זיכרון מורכבת. אנו מחלקים את זיכרון הסוכנים לשלושה סוגים מרכזיים: זיכרון לטווח קצר (Short-term memory) השומר את הקשר השיחה הנוכחי, זיכרון לטווח ארוך (Long-term memory) המאפשר שימור מידע בין הפעלות שונות, וזיכרון היברידי המשלב זיכרון וקטורי (Vector memory) לחיפוש סמנטי יחד עם זיכרון סימבולי (Symbolic memory) לשמירת עובדות וחוקים מובנים.
מדוע זה קריטי? למרות שחלונות ההקשר של מודלים מודרניים כמו Claude או Gemini הולכים וגדלים, הזנת כל ההיסטוריה לתוך הפרומפט היא יקרה, איטית ופוגעת בדיוק המודל (תופעת "הלכת לאיבוד באמצע"). ניהול זיכרון חכם מאפשר לסוכן לשלוף רק את המידע הרלוונטי ביותר למשימה הנוכחית. עדכונים אחרונים בתעשייה, כמו אלו של CrewAI 1.14.6a1 וכן היכולות החדשות ב-Pydantic AI v1.98.0, שמים דגש רב על ניהול מצב (State) וזיכרון מובנה כדי לאפשר סוכנים אמינים יותר בעולם האמיתי.
ההבדל בין זיכרון וקטורי לסימבולי הוא קריטי לארכיטקטורה מתקדמת. זיכרון וקטורי משתמש ב-Embeddings כדי למצוא דמיון רעיוני (למשל, "איך פתרתי בעיה דומה בעבר?"), בעוד שזיכרון סימבולי שומר ישויות וקשרים מוגדרים היטב (למשל, "מזהה המשתמש הוא X והוא מעדיף שפת Python"). שילוב של השניים מאפשר לסוכן גם אינטואיציה אסוציאטיבית וגם דיוק לוגי מוחלט.
כדי להתחיל ליישם זיכרון מתקדם בפרויקטים שלכם, מומלץ להשתמש בספריות ייעודיות שמנהלות את השכבה הזו עבורכם. פרויקט קוד פתוח מוביל בתחום זה הוא Mem0, אשר מספק שכבת זיכרון חכמה, סמנטית והיררכית עבור סוכני AI, ומאפשר להם ללמוד ולהשתפר מאינטראקציה לאינטראקציה.
הנה דוגמה קצרה ופשוטה המציגה כיצד להשתמש ב-Mem0 כדי לשמור ולשלוף זיכרון מותאם אישית למשתמש:
from mem0 import Memory
# אתחול שכבת הזיכרון
memory = Memory()
# שמירת מידע על העדפות המשתמש (זיכרון לטווח ארוך)
memory.add("The user prefers dark mode and writes code in Python", user_id="user_123")
# שליפת המידע הרלוונטי על בסיס שאילתה סמנטית
relevant_memories = memory.search("What is the user's preferred programming language?", user_id="user_123")
print(relevant_memories)
הצעד הבא שלכם: התקינו את Mem0 באמצעות pip install mem0ai, והקימו סוכן פשוט (למשל בעזרת OpenAI Agents או CrewAI) שזוכר את השם והעדפות העבודה של המשתמש שלו גם לאחר הפעלה מחדש של הסקריפט. נסו לעדכן את הזיכרון באופן דינמי במהלך השיחה ולראות כיצד הסוכן משנה את התנהגותו בהתאם.
Multi-agent systems
CrewAI, AutoGen — orchestration, role definition.
בעולם ה-AI המתקדם, מעבר מ-Prompt Engineering פשוט למערכות מרובות סוכנים (Multi-agent systems) מייצג קפיצת מדרגה ארכיטקטונית. במקום להסתמך על מודל שפה יחיד (LLM) שמנסה לפתור משימה מורכבת מקצה לקצה, אנו מפרקים את הבעיה לתפקידים מוגדרים היטב. כל "סוכן" (Agent) במערכת מתפקד כמומחה בתחומו, בעל הגדרת תפקיד (Role), מטרה (Goal), וכלים (Tools) ייעודיים. התיאום ביניהם (Orchestration) מאפשר לפתור בעיות מורכבות הדורשות תכנון, ביקורת עצמית ושיתוף פעולה.
הצורך במערכות אלו עולה כאשר משימות דורשות מספר שלבים של חשיבה, אימות נתונים וקבלת החלטות איטרטיבית. פלטפורמות מובילות כמו CrewAI ו-AutoGen מציעות גישות שונות לניהול האורקסטרציה הזו. בעוד ש-CrewAI מתמקדת בתהליכים מובנים מבוססי תפקידים (Role-playing) הדומים למבנה ארגוני קלאסי (למשל: חוקר, כותב ועורך), AutoGen של Microsoft מציעה גמישות גבוהה יותר בשיחות דינמיות ואינטראקטיביות בין סוכנים, כולל שילוב קל של קוד המורץ בזמן אמת ואינטראקציה עם משתמש אנושי (Human-in-the-loop).
בלב העבודה עם סוכנים מרובים עומד עקרון ה-Role Definition. עלינו להגדיר לכל סוכן את ה-Backstory שלו, שמספק לו את ההקשר והאישיות המקצועית, ואת ה-Task הספציפי שעליו לבצע. האורקסטרציה קובעת כיצד המידע זורם ביניהם: האם בצורה סדרתית (Sequential), שבה פלט של סוכן אחד הוא קלט של הבא בתור, או בצורה היררכית (Hierarchical) המנוהלת על ידי סוכן "מנהל" (Manager Agent).
כדי להבין זאת הלכה למעשה, נראה דוגמה קצרה ב-CrewAI המגדירה שני סוכנים: חוקר שוק וכותב תוכן:
from crewai import Agent, Task, Crew
researcher = Agent(
role='Senior Researcher',
goal='Uncover cutting-edge developments in AI',
backstory='You are a world-class researcher.',
verbose=True
)
writer = Agent(
role='Tech Writer',
goal='Write engaging blog posts about technology',
backstory='You translate complex topics into simple articles.',
verbose=True
)
task1 = Task(description='Analyze 2024 AI trends.', agent=researcher, expected_output='Top 3 trends.')
task2 = Task(description='Write a post based on trends.', agent=writer, expected_output='A blog post.')
crew = Crew(agents=[researcher, writer], tasks=[task1, task2])
result = crew.kickoff()
בגרסאות האחרונות של הכלים הללו, כמו למשל בעדכונים השוטפים של CrewAI releases, אנו רואים שיפורים משמעותיים בניהול הזיכרון (Memory) של הסוכנים, תמיכה משופרת ב-Multi-modal, ויכולות תכנון עצמאיות מתקדמות יותר. מנגד, פרויקטים המבוססים על AutoGen מאפשרים בניית ארכיטקטורות מורכבות אף יותר באמצעות ממשק גרפי נוח כמו AutoGenStudio, המקל על עיצוב זרימות העבודה ללא כתיבת קוד מרובה.
הצעד הבא שלכם: כדי להתחיל ללכלך את הידיים, מומלץ לשכפל את מאגר הדוגמאות הרשמי CrewAI Examples. הריצו את פרויקט ה-Trip Planner או את ה-Stock Analysis כדי לראות כיצד סוכנים מתקשרים ביניהם, משתמשים ב-APIs חיצוניים ומייצרים תוצר סופי משותף ומורכב.