סוכני AI

איך סוכנים אוטונומיים רצים על האתר, מה ה-stream עושה ומה ה-CMS עושה, טוקני ריצה, תור אישורים, זיכרון, chat jobs והמחלקה ai_agents_lib.

⏱ 8 דק' קריאה 1451 מילים ערוך דף זה ב-GitHub

סוכן AI הוא תהליך שרץ בשרת WizzoAI (ה-stream), חושב בלולאת LLM וקורא לכלים של האתר דרך שרת ה-MCP. הדף הזה מסביר מה בדיוק יושב ב-CMS ומה לא, איך סוכן מקבל זהות והרשאות, איך עובד תור האישורים, ואיך קוד של האתר יכול להפעיל סוכן רקע.

חלוקת האחריות#

ה-CMS לא מריץ מודל ולא מתזמן. מגדירים סוכן במסך WizzoAI במרקט (נפתח מ-{admin}/wizzo_market?tool=wizzoai), וה-stream מפעיל אותו. האתר מספק שלושה דברים: כלים, הרשאות ואישורים.

נושאה-stream (stream.ai.wizzo.media)ה-CMS
הגדרת סוכן, builder, הפעלה וכיבויכןשורת shadow ב-CRM_ai_agents, נכתבת בכל mint
תזמון, לולאת LLM, web_search, fetch_url, תקציבים, kill switchכןלא
כליםמגלה דרך tools/listPOST /system/mcp ו-AGENT_TOOLS
הרשאותלאbot admin וקבוצה לכל סוכן, וטוקן ריצה קצר עם allowed_tools
אישוריםמציגהתור, ה-snapshot והביצוע
זיכרוןלסוכני streamרק ל-chat jobs, ועוד הכלים remember, recall, update_playbook
ריצות וצעדיםכןshadow ב-CRM_ai_agents_runs, וצעדים ב-CRM_ai_agents_run_steps ל-chat jobs
מידע

אין צורך לגעת בטבלאות הסוכנים ידנית. ai_agents_lib::ensure_schema() יוצרת אותן (CREATE TABLE IF NOT EXISTS) בקריאה הראשונה של ה-stream לאתר.

מחזור חיים של ריצה#

  1. ה-stream מחליט להריץ סוכן (cron, אירוע, ידני, או chat job) וקורא ל-POST /system/ai_agent_runner/mint עם הגדרת הסוכן.
  2. ה-CMS מעדכן את שורת ה-shadow, דואג ל-bot admin של הסוכן, ואם הוגדר precheck_sql ואין עבודה מחזיר {ok, skip} והריצה מסתיימת בלי להפעיל מודל.
  3. אחרת נפתחת שורת ריצה ונוצר טוקן ריצה: {ok, token, url, cms_run_id, agent_id}. הטוקן תקף שעתיים (RUN_TOKEN_TTL_HOURS) ומוגבל לרשימת הכלים של הסוכן.
  4. ה-stream קורא ל-/system/mcp עם הטוקן. כל קריאה רצה בזהות ה-bot admin, לכן הסוכן לא יכול לעשות יותר ממה שקבוצת ההרשאות שלו מאפשרת.
  5. בסיום ה-stream קורא ל-finish (status הוא done, failed או cancelled), והטוקן נמחק.

ה-bot admin#

לכל סוכן נוצר מנהל agent_<slug> בקבוצה AI Agent: <slug>. ההרשאות של הקבוצה נגזרות מרשימת ה-panels של הסוכן, ומתעדכנות בכל mint. מכאן שהדרך להגביל סוכן היא לצמצם את רשימת הפאנלים שלו, לא להסתמך על ההנחיות בפרומפט.

נקודות הקצה של ai_agent_runner#

כל הפעולות הן POST /system/ai_agent_runner/<action>, פרט ל-run, tick ו-reconcile (GET).

actionגוף או פרמטריםאימותמה עושה
catalog-מפתח WizzoAIמחזיר {tools, panels, site}: כלי הליבה האופציונליים, כלי האתר (custom:true) ופאנלים שאינם למפתחים בלבד
mint{agent:{id, slug, title, goal, tools[], panels[], trigger_type, event_key, precheck_sql, approval_mode, notify}, run:{id, trigger_src, dry_run, precheck}}מפתח WizzoAIפותח ריצה ומנפיק טוקן, כמתואר למעלה
finish{cms_run_id, status, summary, error, tokens_in, tokens_out, wizzo_tokens, steps_count}מפתח WizzoAIסוגר את הריצה
approvals{count_only?}מפתח WizzoAIרשימת אישורים ממתינים (עד 100) או {total, by_slug}
approval_decide{id, approve, reason}מפתח WizzoAIמחליט באישור
run?id=<shadow id>&trigger=event|manual|chat&tk=cron_runner_token או סשן אדמיןמעביר את ההפעלה ל-stream, מחזיר טקסט OK: ... או ERROR: ...
report{run_secret, steps[], phase?, status, summary, error, tokens_*}run_secret של ה-chat jobכותב צעדים וסוגר ריצה
tick, reconcile?tk=טוקן cron או אדמיןמסמן ריצות תקועות (ישנות מ-120 דקות) ככושלות
register-אדמין או מפתח WizzoAIרושם את האתר ב-stream
migrate-אדמין או מפתח WizzoAIמייצא סוכנים ישנים (stream_agent_id=0) ל-stream

מפתח WizzoAI מגיע ב-Authorization, ב-X-Wizzo-Key או בגוף (_key, api_key, _token), ומושווה למפתח האתר ב-hash_equals.

כלים של סוכן#

לכל טוקן ריצה מתווספים כלי הבסיס, ai_agents_lib::BASELINE_TOOLS: cms:list_panels, cms:panel_schema, cms:list_rows, cms:search_media, cms:db_schema, cms:db_query, cms:remember, cms:recall, cms:update_playbook, cms:notify_admin. כלים נוספים מגדירים בהגדרת הסוכן, מתוך OPTIONAL_TOOLS: cms:save_record, cms:set_field, cms:delete_record, cms:claim_items, cms:generate_image, cms:import_media. עוד אפשר: builtin:web_search, builtin:fetch_url, כלי מוצרים (<product>:*) וכלי האתר שרשמתם ב-כלי MCP מותאמים (כ-cms:<name>).

שימו לב
cms:db_query נמצא בכלי הבסיס של כל סוכן, ולכן כל סוכן יכול לקרוא כל טבלה שה-bot admin שלו מורשה אליה בשכבת ה-SQL. תכננו את panels וההנחיות בהתאם, ואל תשמרו סודות בטבלאות שסוכן עשוי לקרוא. פירוט הכלים ב-כלי ה-MCP.

סוגי הפעלה#

trigger_typeמשמעות
scheduleלפי crontime, מתזמן ה-stream
eventאחרי שמירת טופס מוצלחת, ראו למטה
manualכפתור הפעלה, או קריאה ל-run
continuousריצה מתמשכת בצד ה-stream

טריגר אירוע#

כשטופס נשמר בהצלחה, Form::save() מחפש סוכנים עם trigger_type='event', is_active=1, status='active' ו-event_key שווה ל-form_save:<table> (שם הטבלה בלי CRM_). לכל סוכן כזה נשלח MISC::ping("system/ai_agent_runner/run", ...), אסינכרוני, כך שהשמירה לא מתעכבת ולא נכשלת בגלל הסוכן. שמירה שביצע ה-bot admin של אותו סוכן מדולגת כדי למנוע לולאות.

precheck_sql#

הוא SELECT יחיד שמחזיר שורות רק כשיש עבודה. כשהוא ריק מהתוצאה הריצה מדולגת בלי עלות מודל. אם הבדיקה עצמה נכשלת, הריצה כן יוצאת לדרך (fail-open).

מצבי אישור#

השדה approval_mode של הסוכן הוא auto, draft או approve_writes.

הערה

רק approve_writes נאכף בצד ה-CMS. draft ו-auto מתנהגים אותו דבר באתר: הכלים כותבים ישירות. אל תסתמכו על draft כמנגנון הגנה.

ב-approve_writes, כלים כותבים (save_record, set_field, delete_record, form_action, delete_media, כלי אתר עם write=true וכלי מוצר כותבים) לא מתבצעים. נכתבת שורה ב-CRM_ai_agents_approvals והכלי מחזיר {queued_for_approval:true, approval_id, note}.

  • payload מכיל את הארגומנטים ואת __before, הערכים הנוכחיים של השדות שישתנו.
  • record_snapshot הוא md5 של הרשומה בזמן ההצעה.
  • ריצה שנגמרה עם אישורים ממתינים מקבלת את הסטטוס waiting_approval.

אישור מריץ את הכלי בזהות ה-bot admin של הסוכן, אבל רק אם הרשומה לא השתנתה מאז: אם ה-md5 שונה, הסטטוס הופך ל-stale ושום דבר לא נכתב. דחייה מסמנת rejected. שתי ההחלטות נשמרות כזיכרון מסוג feedback, כך שהסוכן לומד מהן.

SELECT id, agent_id, action_type, panel, record_id, status, created_at
FROM CRM_ai_agents_approvals
WHERE status = 'pending'
ORDER BY id DESC
LIMIT 50

סטטוסי אישור אפשריים: pending, approved, rejected, expired, stale.

זיכרון#

הטבלה CRM_ai_agents_memory מסווגת לפי kind:

kindמה נשמר
workרשימות עבודה, remember, recall ו-claim_items
episodeסיכום ריצה (20 אחרונות), רק ל-chat jobs
insightתובנות שה-stream כותב
feedbackאישור או דחייה מתור האישורים
playbookמדריך אחד לסוכן, דרך update_playbook
הערה

באתר ישן שה-ENUM שלו לא כולל playbook, הכלי update_playbook מזהה זאת ומחזיר שגיאה ברורה במקום להיכשל בשקט.

Chat jobs: משימות רקע מהקוד#

ai_agents_lib::dispatch_chat_job מפעיל משימת רקע בשם מנהל נתון, על סוכן נסתר בשם chat_jobs_<admin_id> (או $opts["slug"]). ההגבלות ברירת המחדל: 60 צעדים ו-600,000 טוקנים. הצ'אט משתמש בזה בכלי start_background_job (צ'אט AI), והמודול של SEO המאוחסן משתמש בו עם approve_writes.

$this->load->library("ai_agents_lib");

$res = ai_agents_lib::dispatch_chat_job(
    ADMIN::get_id(),
    "עבור על כל הכתבות בלי תיאור מטא והצע תיאור לכל אחת",
    ["articles"]            // רשימת פאנלים: גבול ההרשאות של הריצה
);

if (!$res["ok"]) {
    // $res["error"] מכיל את הסיבה
} else {
    $run_id = $res["run_id"];
}

מצב הריצה נבדק דרך GET {admin}/ai_agents/chat_job_status?run_id=<id>&pmode=empg, והתראות סיום מגיעות ל-CRM_admin_chat לפי agent.notify (on_fail פעיל כברירת מחדל, on_success לא).

ai_agents_lib: API סטטי#

המחלקה נטענת עם $this->load->library("ai_agents_lib").

חתימהמחזירהערות
full_tool_list($agent)מערך ייחודי של שמות כליםBASELINE_TOOLS בתוספת ה-tools של הסוכן
get_agent($id)שורת סוכן
available_external_servers()מפה של שרתי מוצריםמעטפת ל-external_mcp::servers()
external_tool_servers($agent)רשימת {name, url, token}לפי הקידומות <product>: ב-tools, ל-chat jobs
ensure_schema()-יוצרת את טבלאות הסוכנים
catalog(){tools, panels}מה שה-builder מציע
shadow_upsert($def)שורה או nullupsert לפי slug ([a-z0-9_])
shadow_by_stream_id($id)שורה
open_shadow_run($agent, $run)cms_run_ididempotency_key ייחודי לכל ריצה
ensure_agent_bot(&$agent)bot_admin_idיוצרת או מעדכנת מנהל וקבוצה
precheck_has_work($agent)boolfail-open
mint_run_token($agent, $run_id)טוקןתקף RUN_TOKEN_TTL_HOURS (2) שעות
drop_run_token($run_id)-מוחק גם טוקנים שפגו
dispatch_chat_job($admin_id, $instruction, $panels = [], $opts = []){ok, run_id} או {ok:false, error}ראו למעלה
close_run($run, $status, $summary, $error, $tokens = [], $remember = true)סטטוס סופיdone עם אישורים ממתינים הופך ל-waiting_approval
notify_owner($agent, $msg)-הודעה בצ'אט המנהלים בשם ה-bot
pending_approvals($with_rows = true)שורות, או {total, by_slug}
decide_approval($id, $approve, $reason, $decided_by){ok, status}status הוא approved, rejected, stale או error
export_legacy(), mark_migrated($results)-העברת סוכנים ישנים ל-stream
reconcile_run($run_id), reconcile_stuck()-סוגרים ריצות תקועות
stream_api($path, $data = null, $method = "POST")תשובה מפוענחת, {error, http, body} או nullקריאה ל-API של ה-stream עם מפתח האתר
stream_request_ok()bool

מלכודות#

אזהרה

מפתח WizzoAI של האתר מוזרק לדפדפן של כל מנהל שנכנס לאדמין כשהצ'אט פעיל (הממצא AI-01 בביקורת). מי שמחזיק בו יכול לדבר עם ה-stream בשם האתר. התייחסו למפתח כמפתח רגיש, סובבו אותו כשמנהל עוזב, ושקלו MISC::$GLOBALS["allow_ai_chat"] = false באתרים שבהם אין צורך בצ'אט. ראו אבטחת ה-MCP והצ'אט.

  • ביטול סוכן מתבצע ב-WizzoAI. מצד ה-CMS אפשר רק לשנות is_active בשורת ה-shadow (משפיע על טריגרי אירוע) או למחוק טוקנים.
  • ריצה שנתקעה (pending או running יותר מ-120 דקות) נסגרת ככושלת על ידי reconcile_stuck.
  • שינוי panels של סוכן משפיע רק בריצה הבאה, כי הקבוצה מתעדכנת ב-mint.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.