DanLevy.net

הפסקו לבנות סוכנים מתפרצים: השתמשו בתהליכי עבודה וזיכרון

תבניות דטרמיניסטיות למודלים לא‑דטרמיניסטיים.

LLMs יש להם תכונה מוזרה: הם מצטיינים בהבנת ניואנסים אך גרועים במילוי מתכונים. תן ל‑GPT‑4 בעיה מעורפלת והוא ינתח אפשרויות. תן לו רצף מדויק של שלבים, והוא עלול לדלג על שלב 3 כי שלב 5 “הרגיש רלוונטי יותר”.

זה לא באג במודל. זו תכונה יסודית של מערכות הסתברותיות שמנסות לפתור בעיות דטרמיניסטיות.

צפיתי בצוותים שנאבקים באי‑התאמה הזו. הם בונים סוכן שמטפל בהחזרים ללקוחות, נותנים לו עשר כלים ומצפים ממנו לבצע תהליך עסקי באופן אמין. לפעמים זה עובד מושלם. לפעמים הוא מדמיין אישורים שמעולם לא קרו. לפעמים הוא נתקע ושואל את אותה המידע שלוש פעמים.

הפתרון אינו פרומפטים משופרים. זה לדעת מתי להפסיק לבקש מה‑LLM “לחשוב” ולהתחיל לומר לו “להציית”.

כשדטרמיניזם מנצח על יצירתיות

חשוב על מה שקורה כשצריך לעבד כרטיס תמיכה. הלוגיקה העסקית במציאות נראית בערך כך:

  1. שלוף את פרטי הכרטיס מהמסד נתונים
  2. בדוק אם המשתמש זכאי להחזר (כללי מדיניות)
  3. אמת שהעסקה קיימת ושלא הוחזרה כבר
  4. חשב את סכום ההחזר
  5. עבד הפיכת התשלום
  6. עדכן את מצב הכרטיס
  7. שלח אימייל אישור

אפשר להעביר זאת ל‑LLM כתרגיל קריאת כלים. מניסיוני, זה מזמין בעיות. המודל עלול להחליט שהשלבים 2 ו‑3 “בעצם אותו דבר” ולדלג על אחד מהם. או שהוא עשוי לעבד את ההחזר לפני בדיקת הזכאות כי המשתמש נראה כועס.

זרימות עבודה קיימות בדיוק לתרחיש הזה. הן לא מרגשות, אבל זה בדיוק המטרה.

בניית מתכנן פעילות מזג אוויר

הנה דוגמה מעשית שממחישה את הדפוס. אנחנו צריכים נתוני מזג אוויר קשים, עובדתיים, משולבים עם הצעות פעילות יצירתיות. שליפת מזג האוויר לעולם לא צריכה להיות יצירתית, אך ההצעות כן.

src/mastra/workflows/activity-planner.ts
import { createWorkflow, createStep } from '@mastra/core/workflows';
import { Agent } from '@mastra/core/agent';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
// Step 1: Fetch weather data (Deterministic)
const fetchWeather = createStep({
id: 'fetch-weather',
description: 'Fetches weather forecast for a given city',
inputSchema: z.object({
city: z.string(),
}),
outputSchema: z.object({
location: z.string(),
temperature: z.number(),
conditions: z.string(),
precipitationChance: z.number(),
}),
execute: async ({ inputData }) => {
// ... (fetch logic) ...
const weather = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=temperature_2m,weather_code&daily=precipitation_probability_mean`).then(r => r.json());
return {
location: inputData.city,
temperature: weather.current.temperature_2m,
conditions: getWeatherCondition(weather.current.weather_code),
precipitationChance: weather.daily.precipitation_probability_mean[0],
};
},
});
// Step 2: Agent suggests activities (Creative)
const activityPlanner = new Agent({
id: 'activity-planner-agent',
name: 'Activity Planner',
instructions: `You are a local activities expert. Based on weather conditions, suggest 3-5 appropriate activities.
- For rain (>50% precipitation), prioritize indoor activities
- For extreme temperatures, consider climate-appropriate options
- Always include one adventurous and one relaxing option`,
model: openai('gpt-5'),
});
const planActivities = createStep({
id: 'plan-activities',
description: 'Uses AI to suggest activities based on weather',
inputSchema: z.object({
location: z.string(),
temperature: z.number(),
conditions: z.string(),
precipitationChance: z.number(),
}),
outputSchema: z.object({
activities: z.string(),
}),
execute: async ({ inputData }) => {
const prompt = `Weather in ${inputData.location}: ${inputData.temperature}°C...`;
const response = await activityPlanner.generate(prompt);
return { activities: response.text };
},
});
// The Pipeline
export const activityPlannerWorkflow = createWorkflow({
id: 'activity-planner',
inputSchema: z.object({ city: z.string() }),
outputSchema: z.object({ activities: z.string() }),
})
.then(fetchWeather)
.then(planActivities);
activityPlannerWorkflow.commit();

ה‑LLM לעולם לא נוגע ב‑API של מזג האוויר. הוא מקבל נתוני אמת כקלט, ואז עושה מה שהוא באמת טוב בו: להציע הצעות קונטקסטואליות. אם תסובב את זה ותאפשר לסוכן לשאוב את נתוני מזג האוויר, בסופו של דבר תקבל תחזית שמשית כשבאמת יורד גשם.

מתי לשקול זרימות עבודה:


בעיית חלון ההקשר שמישהו לא מדבר עליה

יש דפוס שאני רואה שוב ושוב. מישהו בונה צ’אטבוט. הוא עובד מצוין בזמן בדיקות. ואז בייצור, משתמשים מנהלים שיחות ארוכות והבוט פתאום מתבלבל.

המפתח בודק את היומנים ומבין שהם שולחים את כל היסטוריית השיחה עם כל בקשה. כל 47 ההודעות. הם שורפים טוקנים ומרחב הקשר עבור מידע שרובו אינו רלוונטי.

גרוע יותר, יש תופעה שחוקרים קוראים לה „אבודים באמצע“ – מודלים מתפקדים פחות כשמידע רלוונטי קבור בהקשר ארוך. המודל פשוט לא רואה את היער בגלל העצים.

שליחת היסטוריית השיחה המלאה מרגישה בטוחה. אתה נותן למודל „את כל המידע“. אבל בעצם אתה מקשה עליו להתמקד במה שחשוב.

זיכרון עבודה מול אחסון ארוך‑טווח

מערכת הזיכרון של Mastra מספקת את שני הסוגים. זיכרון העבודה שומר הודעות אחרונות בחלון ההקשר. שליפה סמנטית מחפשת הודעות היסטוריות כאשר השאילתה הנוכחית נראית קשורה.

src/mastra/agents/memory-agent.ts
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { LibSQLStore } from '@mastra/libsql';
export const memoryAgent = new Agent({
id: 'memory-agent',
name: 'Memory Agent',
instructions: 'You are a helpful assistant with perfect recall of our conversations.',
model: openai('gpt-5'),
memory: new Memory({
storage: new LibSQLStore({
id: 'memory-agent-store',
url: 'file:../mastra.db',
}),
options: {
lastMessages: 20, // Keep last 20 messages in context
semanticRecall: {
enabled: true, // Use embeddings to find old stuff
topK: 5,
threshold: 0.7,
},
},
}),
});

כך זה מתרחש בפועל. משתמש שואל: “מה היה המסעדה האיטלקית שהמלצת עליה בחודש שעבר?”

בלי שליפה סמנטית, הסוכן רואה רק את 20 ההודעות האחרונות. המלצת המסעדה הייתה הודעה 487 מתוך 506. היא נעלמת. הסוכן משיב “אין לי את המידע הזה.”

עם שליפה סמנטית:

  1. השאילתה מומרת להטמעה: [0.234, -0.567, 0.891, ...]
  2. ההטמעה מושווית מול הודעות היסטוריות
  3. הודעה 487 (“אני ממליץ על Trattoria Bella – הקארבונרה שלהם מדהימה”) מקבלת ציון דמיון של 0.89
  4. ההודעה מוזרקת לתוך ההקשר הנוכחי
  5. הסוכן משיב: “המלצתי על Trattoria Bella. הקארבונרה שלהם הוא מה שתפס את תשומת ליבי.”

הסוכן נראה כאילו יש לו זיכרון מושלם, ובכל זאת משתמש רק בחלק קטן מחלון ההקשר. זה לא רק תכנון חכם – זה צורך פונקציונלי ברגע שהשיחות חורגות כמה עשרות הודעות.


תיאום דרך רשתות סוכנים

לפעמים צריך גם מבנה וגם גמישות. זרימות עבודה טהורות קשות מדי. סוכנים טהורים בלתי צפויים מדי.

רשתות סוכנים מספקות מתאם שמחליט איזה סוכן מתמחה או איזו זרימת עבודה להפעיל בהתאם למשימה. אפשר לחשוב על זה כמפצל עומסים חכם ליכולות AI.

export const coordinatorAgent = new Agent({
id: 'coordinator-agent',
name: 'Research Coordinator',
instructions: `You are a network of researchers and writers.
- Use researchAgent for gathering facts
- Use writingAgent for producing final content
- Use weatherTool for current weather data
- Use activityPlannerWorkflow for location-based planning
Always produce comprehensive, well-structured responses.`,
model: openai('gpt-5'),
// Available primitives
agents: { researchAgent, writingAgent },
workflows: { activityPlannerWorkflow },
tools: { weatherTool },
// Network requires memory
memory: new Memory({
storage: new LibSQLStore({ id: 'network-store', url: 'file:../network.db' }),
}),
});

כאשר אתה שואל את הרשת, המתאם מנתח את הבקשה ומנתב בהתאם:

הדפוס הזה מתרחב טוב יותר מאשר לנסות לשים הכל בתוך סוכן‑מגה אחד. סוכנים מתמחים מפתחים מומחיות ממוקדת. המתאם מטפל בניתוב. כל חלק עושה את מה שהוא טוב בו.


הרכבת המערכת

מערכות AI בייצור אמיתי דורשות ארכיטקטורה, לא רק פרומפטים. אתה בונה מערכות מבוזרות שבהן חלק מהצמתים הם מודלים גדולים של שפה.

זרימות עבודה נותנות לך הבטחות כשאתה צריך שהדברים יקרו בדיוק כפי שתכננת. זיכרון מספק הקשר בלי לצרוך את תקציב הא tokens שלך. רשתות סוכנים מאפשרות להרכיב מורכבות מחלקים פשוטים יותר.

זה לא נוצץ. אבל אחרי שראיתי מספיק “סוכנים אוטונומיים לחלוטין” נכשלים בייצור, למדתי להעריך אמינות משעממת יותר מאשר חוסר צפייה מרגש.

החוויה שלך יכולה להיות שונה, אבל מניסיוני, המערכות שמגיעות לשוק ונשארות פעילות הן אלו שמטפלות במודלים הגדולים כמרכיבים בארכיטקטורה רחבה ולא כקופסאות קסם שמפתרות הכל.

משאבים

קרא את הסדרה

  1. ניתוב LLM
  2. אבטחה ומעקבים
  3. MCP ואינטגרציות כלי
  4. תהליכים ו‑זיכרון (פוסט זה)