- Content Creation Agent עובד -- סוכן שמייצר פוסט בלוג שלם מ-keyword, כולל SEO meta tags, כותרות, ו-call-to-action
- Analytics Agent מחובר ל-GA4 ו-Search Console -- סוכן ששולף מטריקות, מזהה אנומליות, ומייצר דו"ח שבועי
- Competitor Monitoring Agent -- סוכן שעוקב אחרי 3 מתחרים ומתריע על שינויים בתוכן, מיקומים, ומחירים
- Ad Management Agent -- סוכן שמנתח ביצועי קמפיינים ומייצר המלצות אופטימיזציה (READ-ONLY, ללא שינויים ישירים)
- Marketing Orchestrator מלא -- CrewAI crew שמתאם את כל הסוכנים עם שגרה יומית, שבועית, וחודשית
- לוח תוכן ל-30 יום -- content calendar שנוצר אוטומטית מנתוני SEO ופערים תחרותיים
- ROI Calculator -- מסמך שמשווה עלות הסוכן מול עלות משווק אנושי לאותן משימות
- מערכת שיווק אוטומטית מלאה -- content + analytics + competitor monitoring + ad management
- תוכלו לתכנן ארכיטקטורה של מערכת multi-agent שיווקית -- חלוקת אחריות, ממשקי תקשורת, ו-data flow בין סוכנים
- תוכלו לבנות סוכן יצירת תוכן שמייצר פוסטים ב-brand voice עקבי עם אופטימיזציית SEO מובנית
- תוכלו לחבר סוכנים ל-marketing APIs אמיתיים (Google Analytics, Search Console, Ads) ולעבד את הנתונים
- תוכלו לתזמר צוות סוכנים עם CrewAI כ-orchestrator שמנהל שגרות יומיות, שבועיות, וחודשיות
- תוכלו להעריך ROI של אוטומציה שיווקית -- עלות API, זמן חסכון, איכות תוצאות, והשפעה עסקית
- פרקים קודמים: פרק 9 (CrewAI), פרק 11 (Tool Use Mastery), פרק 13 (Multi-Agent Systems), פרק 14 (Safety & Guardrails), פרק 16 (Build: Research Agent)
- מה תצטרכו: Python 3.11+ ו/או Node.js 18+, מפתח API (Anthropic / OpenAI), חשבון Google Analytics (אפילו demo account), חשבון Google Search Console (רצוי), חשבון Google Ads (אופציונלי, לקריאה בלבד)
- ידע נדרש: בניית סוכנים עם CrewAI, הגדרת tools ו-MCP servers, הבנת agent loops, ניסיון בסיסי עם APIs של Google
- זמן משוער: 5-7 שעות | עלות משוערת: $10-25 (קריאות API + LLM)
בפרק 16 בניתם Research Agent שחוקר נושא, אוסף מידע ממקורות מרובים, ומייצר דו"ח מחקר מקיף -- למדתם parallel research, data fusion, ו-report generation. בפרק הזה תיקחו את אותם דפוסים -- חיפוש מידע, ניתוח, ויצירת תוכן -- ותיישמו אותם לdomain ספציפי: שיווק דיגיטלי. תבנו מערכת של 4 סוכנים מתמחים שעובדים כצוות תחת orchestrator מרכזי. בפרק 18 תבנו Code Review & DevOps Agent ותראו איך אותם דפוסים של multi-agent orchestration מתאימים גם ל-domain אחר לחלוטין.
| מונח (English) | תרגום | הסבר |
|---|---|---|
| Marketing Orchestrator | מתזמר שיווקי | הסוכן המרכזי שמתאם בין כל סוכני השיווק -- מחליט מה לעשות, מתי, ובאיזה סדר. בפרויקט שלנו הוא CrewAI Crew שמפעיל את שאר הסוכנים |
| Brand Voice | טון מותגי | הסגנון הכתיבתי הייחודי של המותג -- כולל טון (רשמי/ידידותי), אוצר מילים, ערכים, ו-do/don't. הסוכן חייב לשמור על עקביות |
| Content Calendar | לוח תוכן | תוכנית פרסום מתוזמנת ל-30+ יום -- כוללת נושאים, פלטפורמות, תאריכי פרסום, ומילות מפתח. הסוכן מייצר אותו אוטומטית מנתוני SEO |
| SERP Tracking | מעקב תוצאות חיפוש | ניטור מיקומים בתוצאות החיפוש של Google לאורך זמן -- לך ולמתחרים. שינויים מהותיים מפעילים התראות |
| Anomaly Detection | זיהוי חריגות | מנגנון שמזהה שינויים לא רגילים במטריקות שיווקיות -- למשל ירידה של 30% בתנועה, עלייה חדה ב-CPC, או bounce rate חריג |
| Ad Copy | נוסח פרסומי | הטקסט שמופיע במודעות פרסום -- כותרות, תיאורים, CTA. הסוכן מייצר variants לבדיקות A/B |
| CrewAI Crew | צוות CrewAI | אובייקט ב-CrewAI שמאגד agents, tasks, ו-process type (sequential/hierarchical) למשימה מורכבת אחת |
| SEO Meta Tags | תגיות SEO | title tag, meta description, og:tags -- המטא-דאטא שמשפיעה על מיקום בגוגל ועל איך התוכן מוצג בשיתוף ברשתות |
| Content Brief | תקציר תוכן | מסמך מפורט שמנחה את כתיבת התוכן: keyword ראשי, keywords משניים, קהל יעד, מבנה, מתחרים, tone of voice |
| CPC (Cost Per Click) | עלות לקליק | הסכום שמשלמים על כל קליק במודעה. הסוכן מנתח CPC לפי קמפיין, מילת מפתח, ותקופה כדי למצוא אופטימיזציות |
| ROAS (Return on Ad Spend) | החזר על הוצאה פרסומית | כמה הכנסה מתקבלת על כל שקל שמושקע בפרסום. ROAS של 4 אומר: על כל 1 ש"ח, חזרו 4 ש"ח |
| Competitive Gap Analysis | ניתוח פערים תחרותיים | זיהוי נושאים שמתחרים כותבים עליהם ואתם לא, או מילות מפתח שהם מדורגים עליהן ואתם לא -- הזדמנויות לתוכן חדש |
סקירת הפרויקט וארכיטקטורה
בואו נדמיין סטארטאפ ישראלי שמוכר SaaS לעסקים קטנים -- נקרא לו "ClickFlow". צוות השיווק שלהם מורכב מ-2 אנשים שמנסים לכסות הכל: כתיבת בלוגים, ניהול רשתות חברתיות, ניתוח Google Analytics, מעקב אחרי מתחרים, ואופטימיזציה של קמפיינים ב-Google Ads ו-Facebook. הם מבלים 15+ שעות בשבוע על משימות שחוזרות על עצמן -- שליפת דו"חות, בדיקת מיקומים בגוגל, כתיבת drafts ראשונים, ניתוח ביצועי מודעות.
מה אם סוכן AI היה מטפל ב-70% מהמשימות הטכניות האלה, ומשאיר לצוות האנושי את ההחלטות האסטרטגיות, הקשרים האישיים, והיצירתיות הגבוהה? זה בדיוק מה שנבנה בפרק הזה.
הארכיטקטורה של המערכת
המערכת בנויה כ-multi-agent system עם ארכיטקטורת orchestrator:
┌──────────────────────┐
│ Marketing │
│ Orchestrator │
│ (CrewAI Crew) │
└──────┬───────────────┘
│
┌──────────────┼──────────────┐──────────────┐
│ │ │ │
┌───────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ ┌────▼───────┐
│ Content │ │ Analytics │ │ Competitor │ │ Ads │
│ Agent │ │ Agent │ │ Agent │ │ Agent │
│ │ │ │ │ │ │ │
│ - Blog posts │ │ - GA4 │ │ - SERP │ │ - Campaign │
│ - Social │ │ - GSC │ │ - Content │ │ analysis │
│ - Email │ │ - Reports │ │ - Pricing │ │ - Budget │
│ - Ad copy │ │ - Anomaly │ │ - Alerts │ │ - Ad copy │
└──────────────┘ └────────────┘ └────────────┘ └────────────┘
למה CrewAI? כי זה ה-SDK שנבנה בדיוק לזה -- הגדרת agents עם roles, goals, ו-tools, שילוב שלהם ב-crew עם process type (sequential או hierarchical), וביצוע tasks שמעבירים תוצאות מסוכן לסוכן. למדנו את CrewAI בפרק 9, ועכשיו נשתמש בו ב-scale אמיתי.
עקרונות עיצוב
| עיקרון | מימוש | למה |
|---|---|---|
| READ-ONLY לפלטפורמות חיצוניות | API access לקריאה בלבד, שום כתיבה ישירה | בטיחות -- סוכן לא מוציא כסף ולא מפרסם בלי אישור אנושי |
| Recommendations, not actions | הסוכן ממליץ, האדם מאשר | Human-on-the-Loop -- שלמדנו בפרק 14 |
| Brand voice consistency | קובץ brand guidelines נטען לכל סוכן תוכן | כל output חייב להישמע כמו המותג |
| Modular agents | כל סוכן עצמאי, אפשר להשתמש בו בלבד | גמישות -- אם צריכים רק content, מפעילים רק אותו |
צרו תיקייה חדשה לפרויקט:
mkdir marketing-agent && cd marketing-agent
mkdir -p agents tools config output
touch config/brand_guidelines.json
touch config/competitors.json
touch config/.env
ב-config/.env הוסיפו את המפתחות שלכם:
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
# GOOGLE_ANALYTICS_PROPERTY_ID=properties/123456789
# GOOGLE_ADS_CUSTOMER_ID=123-456-7890
מפתה לתת לסוכן write access ל-Google Ads או Facebook Ads -- "שהוא ישנה bids אוטומטית." אל תעשו את זה בהתחלה. סוכן שמשנה bids בלי פיקוח יכול לשרוף תקציב של אלפי שקלים בכמה שעות. התחילו תמיד ב-read-only, בנו אמון בהמלצות הסוכן, ואז -- אולי, אחרי שבועות של validation -- שקלו automated actions עם budget caps ו-approval workflows.
Content Creation Agent -- יצירת תוכן
הסוכן הראשון שנבנה הוא ה-Content Creation Agent -- לב השיווק. הוא מקבל keyword, חוקר את הנושא, ומייצר תוכן מוכן לפרסום: בלוג פוסטים, פוסטים לרשתות חברתיות, קמפיינים באימייל, ונוסחי מודעות.
Brand Voice -- שמירה על טון עקבי
לפני שהסוכן כותב מילה, הוא חייב להבין את ה-brand voice. זה ההבדל בין תוכן שנראה כאילו AI כתב אותו לבין תוכן שנשמע כמו המותג. ניצור קובץ brand guidelines:
# config/brand_guidelines.json
{
"company_name": "ClickFlow",
"tagline": "שיווק דיגיטלי שעובד בשבילך",
"tone": "ידידותי ומקצועי, לא פורמלי מדי, לא סלנגי",
"audience": "בעלי עסקים קטנים ובינוניים בישראל, גילאי 30-55",
"language_rules": {
"primary": "עברית",
"english_ok_for": ["מונחים טכניים", "שמות כלים", "ראשי תיבות"],
"avoid": ["ז'רגון טכני מיותר", "משפטים ארוכים מ-25 מילים", "passive voice"],
"prefer": ["דוגמאות מספריות", "שאלות רטוריות", "פסקאות קצרות"]
},
"content_rules": {
"blog_length": "1200-2000 מילים",
"cta_style": "רך -- 'רוצים ללמוד עוד?' ולא 'קנו עכשיו!'",
"seo_required": true,
"internal_links": "לפחות 2 לינקים לתוכן קיים"
},
"competitors_to_differentiate_from": ["monday.com", "Wix", "Elementor"],
"unique_selling_points": [
"מתאים לעסקים ישראליים",
"תמיכה בעברית מלאה",
"אינטגרציה עם חשבשבת ו-Priority"
]
}
מימוש ב-Python עם CrewAI
הנה ה-Content Agent המלא. שימו לב איך ה-brand guidelines נטענים ומוזרקים ל-system prompt:
# agents/content_agent.py
import json
from crewai import Agent, Task
from crewai.tools import tool
from pathlib import Path
# טוען brand guidelines
def load_brand_guidelines() -> dict:
path = Path(__file__).parent.parent / "config" / "brand_guidelines.json"
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
BRAND = load_brand_guidelines()
# --- Tools ---
@tool("web_search")
def web_search(query: str) -> str:
"""חיפוש באינטרנט למחקר תוכן. מחזיר תוצאות רלוונטיות."""
# בפרודקשן: חיבור ל-Brave Search, Tavily, או SerpAPI
import requests
resp = requests.get(
"https://api.search.brave.com/res/v1/web/search",
headers={"X-Subscription-Token": os.environ["BRAVE_API_KEY"]},
params={"q": query, "count": 5}
)
results = resp.json().get("web", {}).get("results", [])
return "\n".join([
f"- {r['title']}: {r['description']}" for r in results
])
@tool("seo_analyzer")
def seo_analyzer(keyword: str) -> str:
"""מנתח keyword: volume, difficulty, related keywords."""
# בפרודקשן: חיבור ל-SEMrush, Ahrefs, או Google Keyword Planner
return json.dumps({
"keyword": keyword,
"monthly_volume": 2400,
"difficulty": "medium",
"related_keywords": [
f"{keyword} לעסקים קטנים",
f"איך לעשות {keyword}",
f"{keyword} 2026",
f"{keyword} בישראל"
],
"top_questions": [
f"מה זה {keyword}?",
f"כמה עולה {keyword}?",
f"איך מתחילים עם {keyword}?"
]
}, ensure_ascii=False)
@tool("brand_guidelines_reader")
def brand_guidelines_reader(aspect: str) -> str:
"""קורא את הנחיות המותג. aspect: tone, audience, content_rules, all."""
if aspect == "all":
return json.dumps(BRAND, ensure_ascii=False, indent=2)
return json.dumps(BRAND.get(aspect, f"No guidelines for: {aspect}"),
ensure_ascii=False)
# --- Agent ---
content_agent = Agent(
role="Content Marketing Specialist",
goal="ליצור תוכן שיווקי איכותי שתואם את ה-brand voice, "
"ממוקד SEO, ומניע את הקורא לפעולה",
backstory=f"""אתה כותב תוכן שיווקי מנוסה שעובד עם {BRAND['company_name']}.
אתה מכיר את קהל היעד: {BRAND['audience']}.
הטון שלך: {BRAND['tone']}.
אתה תמיד כותב בעברית, עם מונחים טכניים באנגלית כשצריך.
כל תוכן שאתה כותב עובר אופטימיזציית SEO ומכוון לדירוג בגוגל.""",
tools=[web_search, seo_analyzer, brand_guidelines_reader],
verbose=True,
llm="anthropic/claude-sonnet-4-20250514"
)
# --- Tasks ---
def create_blog_post_task(keyword: str) -> Task:
return Task(
description=f"""כתוב פוסט בלוג מלא על "{keyword}".
שלבים:
1. קרא את הנחיות המותג (brand_guidelines_reader, aspect="all")
2. חקור את ה-keyword (seo_analyzer)
3. חפש מידע עדכני (web_search)
4. כתוב את הפוסט:
- כותרת (H1) עם ה-keyword
- מבוא מושך (2-3 משפטים)
- 4-6 sections עם H2
- דוגמאות מספריות ומהעולם הישראלי
- סיכום עם CTA רך
- אורך: {BRAND['content_rules']['blog_length']}
5. הוסף SEO meta:
- title tag (עד 60 תווים)
- meta description (עד 155 תווים)
- 3-5 internal link suggestions
הפלט חייב להיות בעברית, בטון של {BRAND['tone']}.""",
expected_output="פוסט בלוג מלא בעברית עם SEO meta tags",
agent=content_agent
)
צרו את config/brand_guidelines.json עבור פרויקט אמיתי שלכם (או עסק ישראלי שאתם מכירים). מלאו לפחות: company_name, tone, audience, ו-content_rules. אם אין לכם עסק -- השתמשו בדוגמת ClickFlow מלמעלה.
מימוש ב-TypeScript עם Vercel AI SDK
אם אתם מעדיפים TypeScript, הנה אותו Content Agent עם Vercel AI SDK:
// agents/content-agent.ts
import { generateText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { readFileSync } from "fs";
import { join } from "path";
const brandGuidelines = JSON.parse(
readFileSync(join(__dirname, "../config/brand_guidelines.json"), "utf-8")
);
const contentTools = {
webSearch: tool({
description: "Search the web for content research",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
const resp = await fetch(
`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=5`,
{ headers: { "X-Subscription-Token": process.env.BRAVE_API_KEY! } }
);
const data = await resp.json();
return data.web?.results?.map(
(r: any) => `- ${r.title}: ${r.description}`
).join("\n") ?? "No results found";
},
}),
seoAnalyzer: tool({
description: "Analyze keyword for SEO: volume, difficulty, related terms",
parameters: z.object({ keyword: z.string() }),
execute: async ({ keyword }) => {
// Production: connect to SEMrush / Ahrefs API
return JSON.stringify({
keyword,
monthly_volume: 2400,
difficulty: "medium",
related_keywords: [
`${keyword} לעסקים קטנים`,
`איך לעשות ${keyword}`,
`${keyword} 2026`,
],
});
},
}),
readBrandGuidelines: tool({
description: "Read brand voice and content guidelines",
parameters: z.object({
aspect: z.enum(["tone", "audience", "content_rules", "all"]),
}),
execute: async ({ aspect }) => {
if (aspect === "all") return JSON.stringify(brandGuidelines);
return JSON.stringify(brandGuidelines[aspect] ?? "Not found");
},
}),
};
export async function generateBlogPost(keyword: string) {
const { text } = await generateText({
model: anthropic("claude-sonnet-4-20250514"),
tools: contentTools,
maxSteps: 10,
system: `אתה כותב תוכן שיווקי של ${brandGuidelines.company_name}.
טון: ${brandGuidelines.tone}. קהל: ${brandGuidelines.audience}.
כתוב תמיד בעברית. שלב מונחים טכניים באנגלית כשמתאים.`,
prompt: `כתוב פוסט בלוג מלא על "${keyword}".
1. קרא את brand guidelines
2. נתח את ה-keyword ל-SEO
3. חפש מידע עדכני
4. כתוב פוסט: H1 + מבוא + 4-6 sections + סיכום + CTA
5. הוסף title tag (60 תווים), meta description (155 תווים)
אורך: ${brandGuidelines.content_rules.blog_length}`,
});
return text;
}
בחרו keyword אמיתי שרלוונטי לעסק שלכם (למשל "אוטומציה שיווקית" או "CRM לעסקים קטנים"). רשמו אותו -- נשתמש בו בתרגיל המלא.
סוגי תוכן נוספים
Content Agent לא מסתכם בבלוגים. הנה איך מרחיבים אותו לסוגי תוכן נוספים:
| סוג תוכן | אורך | פלטפורמה | מאפיינים ייחודיים |
|---|---|---|---|
| בלוג פוסט | 1,200-2,000 מילים | אתר | SEO, H2 structure, internal links, CTA |
| פוסט LinkedIn | 150-300 מילים | Hook בשורה ראשונה, שורות קצרות, emoji מינימלי, hashtags | |
| פוסט Facebook | 80-150 מילים | שאלה פותחת, ויזואלי, CTA ברור, בעברית מדוברת | |
| אימייל שיווקי | 200-400 מילים | Subject line (עד 50 תווים), preview text, כפתור CTA אחד | |
| נוסח מודעה | 30-90 תווים | Google/Meta Ads | 3 variants לכל מודעה, keyword בכותרת, USP ברור |
| אם... | אז צרו... | למה |
|---|---|---|
| יש לכם keyword עם volume גבוה | בלוג פוסט + פוסטים חברתיים | בלוג מושך תנועה אורגנית, פוסטים מגבירים reach |
| יש לכם מוצר חדש / פיצ'ר | אימייל + LinkedIn + מודעות | הודעה ישירה לקהל קיים + acquisition |
| מתחרים פרסמו תוכן על הנושא | בלוג פוסט שלם ומעמיק יותר | "10x content" -- תוכן שעשרת מונים טוב מהמתחרה |
| יש לכם נתונים / case study | בלוג + LinkedIn + email sequence | נתונים מקוריים הם זהב שיווקי, לפזר על כל הערוצים |
בנו Content Agent שלם שמייצר בלוג פוסט:
- צרו את
config/brand_guidelines.jsonעם כל השדות (טון, קהל, חוקי תוכן) - ממשו את
agents/content_agent.py(או.ts) עם ה-3 tools: web_search, seo_analyzer, brand_guidelines_reader - הריצו את הסוכן עם keyword אמיתי (למשל: "אוטומציה שיווקית לעסקים קטנים")
- בדקו את הפלט: האם הטון תואם? האם יש SEO meta? האם האורך בטווח?
- שמרו את הפלט ב-
output/blog_draft_01.mdובדקו אם הייתם מפרסמים את זה - שפרו את ה-prompt עד שהתוצאה מספקת -- תעדו מה שיניתם ולמה
תוצאה צפויה: פוסט בלוג של 1,200+ מילים בעברית, עם title tag, meta description, ו-3 הצעות ל-internal links.
Content Agent יכול להמציא סטטיסטיקות. אם הוא כותב "72% מהעסקים בישראל משתמשים באוטומציה" -- הוא כנראה המציא את המספר. כל עובדה ונתון חייבים לעבור fact-check אנושי לפני פרסום. הוסיפו ל-prompt הנחיה: "אם אתה מציין סטטיסטיקה, ציין את המקור. אם אין לך מקור -- כתוב 'לפי הערכות' או השמט."
Analytics Agent -- ניתוח ביצועים
הסוכן השני הוא ה-Analytics Agent -- העיניים של המערכת. הוא מתחבר ל-Google Analytics 4 ו-Google Search Console, שולף נתונים, מזהה מגמות ואנומליות, ומייצר דו"חות שאפשר לפעול לפיהם.
הבעיה עם אנליטיקס היא לא חוסר נתונים -- הבעיה היא עודף נתונים. GA4 מחזיר מאות מטריקות, מימדים, ו-segments. בעל עסק קטן לא יודע מה לעשות עם bounce rate של 67.3%. ה-Analytics Agent הופך נתונים גולמיים ל-insights פעילים: "התנועה מגוגל ירדה ב-25% השבוע כי המאמר על CRM נדחף למקום 8 -- כדאי לעדכן אותו."
חיבור ל-Google Analytics 4
# agents/analytics_agent.py
import os
import json
from datetime import datetime, timedelta
from crewai import Agent, Task
from crewai.tools import tool
# --- Google Analytics Tool ---
@tool("ga4_report")
def ga4_report(metric: str, date_range: str = "last_7_days") -> str:
"""שולף דו"ח מ-GA4. metric: sessions, users, pageviews, bounce_rate, conversions.
date_range: last_7_days, last_30_days, last_month, custom."""
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
RunReportRequest, DateRange, Metric, Dimension
)
client = BetaAnalyticsDataClient()
property_id = os.environ["GA4_PROPERTY_ID"]
# חישוב טווח תאריכים
end_date = datetime.now()
if date_range == "last_7_days":
start_date = end_date - timedelta(days=7)
elif date_range == "last_30_days":
start_date = end_date - timedelta(days=30)
else:
start_date = end_date - timedelta(days=7)
# מפה מטריקות לשמות GA4
metric_map = {
"sessions": "sessions",
"users": "totalUsers",
"pageviews": "screenPageViews",
"bounce_rate": "bounceRate",
"conversions": "conversions",
}
request = RunReportRequest(
property=f"properties/{property_id}",
date_ranges=[DateRange(
start_date=start_date.strftime("%Y-%m-%d"),
end_date=end_date.strftime("%Y-%m-%d")
)],
metrics=[Metric(name=metric_map.get(metric, metric))],
dimensions=[Dimension(name="date")],
)
response = client.run_report(request)
rows = [
{"date": row.dimension_values[0].value,
metric: row.metric_values[0].value}
for row in response.rows
]
return json.dumps(rows, ensure_ascii=False)
@tool("search_console_report")
def search_console_report(days: int = 7) -> str:
"""שולף נתוני Search Console: queries, clicks, impressions, position."""
from googleapiclient.discovery import build
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file(
"config/service_account.json",
scopes=["https://www.googleapis.com/auth/webmasters.readonly"]
)
service = build("searchconsole", "v1", credentials=credentials)
end_date = datetime.now()
start_date = end_date - timedelta(days=days)
response = service.searchanalytics().query(
siteUrl=os.environ["SITE_URL"],
body={
"startDate": start_date.strftime("%Y-%m-%d"),
"endDate": end_date.strftime("%Y-%m-%d"),
"dimensions": ["query"],
"rowLimit": 20
}
).execute()
return json.dumps(response.get("rows", []), ensure_ascii=False)
@tool("anomaly_detector")
def anomaly_detector(current_value: float, previous_value: float,
metric_name: str) -> str:
"""מזהה אם שינוי במטריקה הוא חריג. מחזיר alert אם השינוי > 20%."""
if previous_value == 0:
return json.dumps({"alert": False, "reason": "No previous data"})
change_pct = ((current_value - previous_value) / previous_value) * 100
alert = abs(change_pct) > 20
direction = "עלייה" if change_pct > 0 else "ירידה"
return json.dumps({
"metric": metric_name,
"current": current_value,
"previous": previous_value,
"change_percent": round(change_pct, 1),
"direction": direction,
"alert": alert,
"severity": "critical" if abs(change_pct) > 50 else
"warning" if abs(change_pct) > 20 else "normal",
"message": f"{direction} של {abs(round(change_pct, 1))}% ב-{metric_name}"
if alert else "ללא שינוי מהותי"
}, ensure_ascii=False)
# --- Agent ---
analytics_agent = Agent(
role="Marketing Analytics Specialist",
goal="לשלוף נתוני שיווק, לזהות מגמות ואנומליות, "
"ולייצר insights פעילים שמנהלים יכולים לפעול לפיהם",
backstory="""אתה אנליסט שיווקי מנוסה שיודע להפוך נתונים גולמיים
ל-insights ברורים. אתה לא רק מציג מספרים -- אתה מסביר למה הם
חשובים ומה צריך לעשות. כשיש ירידה -- אתה מציע סיבות ופתרונות.
כשיש עלייה -- אתה מזהה מה עבד ומציע להרחיב.
הנתונים שלך תמיד בשקלים (ש"ח) ובהקשר ישראלי.""",
tools=[ga4_report, search_console_report, anomaly_detector],
verbose=True,
llm="anthropic/claude-sonnet-4-20250514"
)
# --- Tasks ---
def create_weekly_report_task() -> Task:
return Task(
description="""ייצר דו"ח שבועי של ביצועי שיווק:
1. שלוף מ-GA4: sessions, users, pageviews, bounce_rate, conversions (last_7_days)
2. שלוף מ-Search Console: top 20 queries
3. בדוק כל מטריקה מול השבוע הקודם עם anomaly_detector
4. ייצר דו"ח מובנה:
## דו"ח שבועי -- [תאריך]
### מטריקות מרכזיות
- Sessions: X (▲/▼ Y%)
- Users: X (▲/▼ Y%)
- Conversions: X (▲/▼ Y%)
### התראות
- [כל anomaly שזוהתה]
### Top Performing Content
- [דפים/queries עם הכי הרבה תנועה]
### המלצות
- [3 פעולות ספציפיות]""",
expected_output="דו"ח שבועי מובנה עם מטריקות, התראות, והמלצות",
agent=analytics_agent
)
אם אין לכם Google Analytics property משלכם, השתמשו ב-GA4 Demo Account של Google: לכו ל-analytics.google.com/analytics/web/demoAccount וגשו ל-Google Merchandise Store. רשמו את ה-Property ID -- נשתמש בו בתרגילים.
TypeScript: Analytics Agent
// agents/analytics-agent.ts
import { generateText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
const analyticsTools = {
ga4Report: tool({
description: "Pull GA4 report for a specific metric and date range",
parameters: z.object({
metric: z.enum(["sessions", "users", "pageviews", "bounce_rate", "conversions"]),
dateRange: z.enum(["last_7_days", "last_30_days"]).default("last_7_days"),
}),
execute: async ({ metric, dateRange }) => {
// Production: use @google-analytics/data
// Demo: return mock data
const mockData = {
sessions: [
{ date: "2026-03-18", value: 1250 },
{ date: "2026-03-19", value: 1180 },
{ date: "2026-03-20", value: 1340 },
{ date: "2026-03-21", value: 980 },
{ date: "2026-03-22", value: 870 },
{ date: "2026-03-23", value: 1410 },
{ date: "2026-03-24", value: 1290 },
],
};
return JSON.stringify(mockData[metric] ?? mockData.sessions);
},
}),
anomalyDetector: tool({
description: "Detect anomalies by comparing current vs previous values",
parameters: z.object({
currentValue: z.number(),
previousValue: z.number(),
metricName: z.string(),
}),
execute: async ({ currentValue, previousValue, metricName }) => {
const changePct = previousValue === 0
? 0
: ((currentValue - previousValue) / previousValue) * 100;
const alert = Math.abs(changePct) > 20;
return JSON.stringify({
metric: metricName,
change_percent: Math.round(changePct * 10) / 10,
alert,
severity: Math.abs(changePct) > 50 ? "critical" :
Math.abs(changePct) > 20 ? "warning" : "normal",
});
},
}),
};
export async function generateWeeklyReport() {
const { text } = await generateText({
model: anthropic("claude-sonnet-4-20250514"),
tools: analyticsTools,
maxSteps: 15,
system: `אתה אנליסט שיווקי. הפוך נתונים גולמיים ל-insights ברורים.
תמיד הסבר למה מספרים חשובים ומה לעשות בעקבותם.
הנתונים בש"ח, ההקשר ישראלי.`,
prompt: `ייצר דו"ח שבועי: שלוף sessions, users, conversions מ-GA4,
בדוק אנומליות, וסכם עם 3 המלצות ספציפיות.`,
});
return text;
}
Google APIs: חינם (GA4 Data API ו-Search Console API הם בחינם לשימוש סביר). LLM: ~$0.10-0.30 לכל דו"ח (תלוי בכמות הנתונים). סה"כ חודשי: אם מריצים דו"ח יומי -- כ-$3-9 בחודש. דו"ח שבועי -- כ-$0.50-1.50 בחודש. השוואה: אנליסט אנושי שמפיק דו"ח שבועי מקביל עולה ~1-2 שעות עבודה (~200-400 ש"ח).
רשמו 3 מטריקות שיווקיות שהכי חשובות לעסק שלכם (או לעסק שבניתם brand guidelines בשבילו). לכל מטריקה, הגדירו: מה נחשב "חריג" (threshold). למשל: sessions -- ירידה של מעל 25% שבוע-על-שבוע.
ל-Google Analytics Data API יש rate limits: 10 בקשות בו-זמנית ו-quota יומי שתלוי בסוג הנכס. אם הסוכן שולף נתונים בלולאה אגרסיבית, הוא יקבל שגיאת 429. פתרון: הוסיפו caching -- שמרו את תוצאות ה-API ל-file/Redis עם TTL של שעה, והסוכן ישתמש ב-cache במקום לקרוא ל-API שוב.
Competitor Monitoring Agent -- ניטור מתחרים
הסוכן השלישי הוא ה-Competitor Monitoring Agent -- המודיעין של המערכת. הוא עוקב אחרי מתחרים ומתריע כשמשהו משתנה: תוכן חדש, שינויים במיקומים בגוגל, שינויי מחירים, או פעילות חדשה ברשתות חברתיות.
בשוק הישראלי, ניטור מתחרים הוא קריטי במיוחד. השוק קטן, התחרות צפופה, ושינוי קטן אצל מתחרה יכול להשפיע עליכם מהר. ClickFlow (הסטארטאפ שלנו) מתחרה מול 3 חברות ישראליות -- ובעל העסק צריך לדעת מה הם עושים בלי לבזבז שעתיים ביום על מעקב ידני.
הגדרת מתחרים
# config/competitors.json
{
"competitors": [
{
"name": "CompetitorA",
"domain": "competitor-a.co.il",
"blog_url": "https://competitor-a.co.il/blog",
"social": {
"linkedin": "https://linkedin.com/company/competitor-a",
"facebook": "https://facebook.com/competitorA"
},
"tracked_keywords": [
"CRM לעסקים קטנים",
"אוטומציה שיווקית",
"ניהול לידים"
],
"pricing_page": "https://competitor-a.co.il/pricing"
},
{
"name": "CompetitorB",
"domain": "competitor-b.com",
"blog_url": "https://competitor-b.com/blog",
"social": {
"linkedin": "https://linkedin.com/company/competitor-b"
},
"tracked_keywords": [
"marketing automation",
"email marketing ישראל",
"דיוור אלקטרוני"
],
"pricing_page": "https://competitor-b.com/pricing"
},
{
"name": "CompetitorC",
"domain": "competitor-c.co.il",
"blog_url": "https://competitor-c.co.il/blog",
"social": {
"facebook": "https://facebook.com/competitorC"
},
"tracked_keywords": [
"שיווק דיגיטלי לעסקים",
"קמפיינים בגוגל",
"פרסום ממומן"
],
"pricing_page": "https://competitor-c.co.il/plans"
}
],
"monitoring_schedule": {
"content_check": "daily",
"serp_check": "daily",
"pricing_check": "weekly",
"social_check": "daily"
}
}
מימוש ב-Python
# agents/competitor_agent.py
import json
import hashlib
from datetime import datetime
from pathlib import Path
from crewai import Agent, Task
from crewai.tools import tool
SNAPSHOT_DIR = Path("output/competitor_snapshots")
SNAPSHOT_DIR.mkdir(parents=True, exist_ok=True)
@tool("scrape_page")
def scrape_page(url: str) -> str:
"""סורק דף וב ומחזיר את הטקסט. לניטור שינויים אצל מתחרים."""
import requests
from bs4 import BeautifulSoup
try:
resp = requests.get(url, timeout=10, headers={
"User-Agent": "Mozilla/5.0 (compatible; MarketingBot/1.0)"
})
soup = BeautifulSoup(resp.text, "html.parser")
# הסר scripts ו-styles
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
text = soup.get_text(separator="\n", strip=True)
return text[:5000] # חתוך ל-5000 תווים
except Exception as e:
return f"Error scraping {url}: {str(e)}"
@tool("check_serp_position")
def check_serp_position(keyword: str, domain: str) -> str:
"""בודק מיקום של domain בתוצאות גוגל עבור keyword."""
# בפרודקשן: SerpAPI, DataForSEO, או Brightdata
import requests
resp = requests.get(
"https://serpapi.com/search",
params={
"q": keyword,
"location": "Israel",
"hl": "he",
"gl": "il",
"api_key": os.environ.get("SERPAPI_KEY", "demo")
}
)
results = resp.json().get("organic_results", [])
for i, result in enumerate(results):
if domain in result.get("link", ""):
return json.dumps({
"keyword": keyword,
"domain": domain,
"position": i + 1,
"title": result.get("title", ""),
"snippet": result.get("snippet", "")
}, ensure_ascii=False)
return json.dumps({
"keyword": keyword,
"domain": domain,
"position": "not_found",
"message": f"{domain} לא נמצא ב-top 100 עבור '{keyword}'"
}, ensure_ascii=False)
@tool("detect_content_changes")
def detect_content_changes(url: str, competitor_name: str) -> str:
"""משווה תוכן נוכחי מול snapshot קודם ומזהה שינויים."""
current_content = scrape_page.run(url)
current_hash = hashlib.md5(current_content.encode()).hexdigest()
snapshot_file = SNAPSHOT_DIR / f"{competitor_name}_{url_to_filename(url)}.json"
if snapshot_file.exists():
with open(snapshot_file, "r") as f:
previous = json.load(f)
changed = previous["hash"] != current_hash
result = {
"url": url,
"competitor": competitor_name,
"changed": changed,
"previous_check": previous["timestamp"],
"current_check": datetime.now().isoformat()
}
else:
result = {
"url": url,
"competitor": competitor_name,
"changed": False,
"message": "First check -- snapshot saved",
"current_check": datetime.now().isoformat()
}
# שמור snapshot
with open(snapshot_file, "w") as f:
json.dump({
"hash": current_hash,
"timestamp": datetime.now().isoformat(),
"content_preview": current_content[:500]
}, f, ensure_ascii=False)
return json.dumps(result, ensure_ascii=False)
def url_to_filename(url: str) -> str:
return hashlib.md5(url.encode()).hexdigest()[:12]
# --- Agent ---
competitor_agent = Agent(
role="Competitive Intelligence Analyst",
goal="לעקוב אחרי מתחרים, לזהות שינויים, "
"ולייצר התראות ודו"חות תחרותיים",
backstory="""אתה מנתח מודיעין תחרותי שעוקב אחרי מתחרים בשוק הישראלי.
אתה סורק אתרים, בודק מיקומים בגוגל, ומזהה שינויים בתוכן ובמחירים.
כשיש שינוי מהותי -- אתה מתריע מיד עם ניתוח: מה השתנה, למה זה חשוב,
ומה אנחנו צריכים לעשות בתגובה. אתה מכיר היטב את השוק הישראלי כולל
פלטפורמות מקומיות כמו Walla, Ynet, וCalcalist.""",
tools=[scrape_page, check_serp_position, detect_content_changes],
verbose=True,
llm="anthropic/claude-sonnet-4-20250514"
)
מלאו את config/competitors.json עם 3 מתחרים אמיתיים שלכם. לכל מתחרה רשמו: domain, כתובת בלוג (אם יש), ו-3 מילות מפתח שאתם מתחרים עליהן.
ניטור תוכן עברי
ניטור מתחרים ישראליים דורש התייחסות מיוחדת לעברית. כמה נקודות:
- Encoding: ודאו שה-scraper תומך ב-UTF-8. אתרים ישראליים ישנים עלולים להשתמש ב-Windows-1255
- פלטפורמות מקומיות: מעבר לגוגל, עקבו גם אחרי Walla News, Ynet, Calcalist, וGlobes -- כתבה שם יכולה להשפיע על SERP
- תוכן דו-לשוני: הרבה אתרים ישראליים כותבים בעברית ובאנגלית -- הסוכן חייב לזהות תוכן חדש בשתי השפות
- Waze Effect: בשוק קטן כמו ישראל, שינוי מחיר של מתחרה מתפשט מהר דרך קבוצות WhatsApp ופייסבוק -- כדאי לנטר גם רשתות חברתיות
- ממשו את
agents/competitor_agent.pyעם 3 tools: scrape_page, check_serp_position, detect_content_changes - הגדירו 3 מתחרים ב-
config/competitors.json - הריצו סריקה ראשונית -- היא תיצור snapshots בסיסיים
- חכו יום (או שנו ידנית snapshot) והריצו שוב -- ודאו שמזהה שינויים
- בדקו מיקומי SERP ל-3 מילות מפתח
- ייצרו "דו"ח תחרותי יומי" עם כל הממצאים
תוצאה צפויה: דו"ח שמציג: שינויי תוכן (אם יש), מיקומי SERP, והמלצות פעולה.
Ad Management Agent -- ניהול פרסום
הסוכן הרביעי הוא ה-Ad Management Agent -- המנתח הפיננסי של המערכת. הוא מנתח ביצועי קמפיינים ב-Google Ads ו-Meta Ads, מזהה מודעות שמבזבזות תקציב, ומייצר המלצות אופטימיזציה. חשוב: הוא פועל בREAD-ONLY -- ממליץ בלבד, לא מבצע שינויים.
למה READ-ONLY? כי שינוי bid של 50 אגורות על מילת מפתח פופולרית יכול לעלות אלפי שקלים ביום. עד שבונים אמון מלא ביכולת הסוכן, הגישה הבטוחה היא: הסוכן מנתח ← הסוכן ממליץ ← אדם מאשר ← אדם מבצע.
מימוש ב-Python
# agents/ads_agent.py
import json
from crewai import Agent, Task
from crewai.tools import tool
@tool("google_ads_report")
def google_ads_report(report_type: str = "campaign_performance") -> str:
"""שולף דו"ח ביצועים מ-Google Ads.
report_type: campaign_performance, keyword_performance, ad_copy_performance."""
from google.ads.googleads.client import GoogleAdsClient
client = GoogleAdsClient.load_from_env()
ga_service = client.get_service("GoogleAdsService")
queries = {
"campaign_performance": """
SELECT campaign.name, campaign.status,
metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions,
metrics.cost_per_conversion
FROM campaign
WHERE segments.date DURING LAST_7_DAYS
AND campaign.status = 'ENABLED'
ORDER BY metrics.cost_micros DESC
LIMIT 20
""",
"keyword_performance": """
SELECT ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions,
metrics.average_cpc
FROM keyword_view
WHERE segments.date DURING LAST_7_DAYS
ORDER BY metrics.cost_micros DESC
LIMIT 30
""",
"ad_copy_performance": """
SELECT ad_group_ad.ad.responsive_search_ad.headlines,
ad_group_ad.ad.responsive_search_ad.descriptions,
metrics.impressions, metrics.clicks,
metrics.ctr, metrics.conversions
FROM ad_group_ad
WHERE segments.date DURING LAST_7_DAYS
ORDER BY metrics.impressions DESC
LIMIT 15
"""
}
query = queries.get(report_type, queries["campaign_performance"])
customer_id = os.environ["GOOGLE_ADS_CUSTOMER_ID"].replace("-", "")
response = ga_service.search(customer_id=customer_id, query=query)
results = []
for row in response:
results.append({
"campaign": row.campaign.name if hasattr(row, 'campaign') else "N/A",
"impressions": row.metrics.impressions,
"clicks": row.metrics.clicks,
"cost_ils": round(row.metrics.cost_micros / 1_000_000, 2),
"conversions": round(row.metrics.conversions, 1),
"cpc_ils": round(row.metrics.average_cpc / 1_000_000, 2)
if hasattr(row.metrics, 'average_cpc') else None
})
return json.dumps(results, ensure_ascii=False)
@tool("budget_analyzer")
def budget_analyzer(campaigns_data: str) -> str:
"""מנתח הקצאת תקציב ומזהה בזבוז. מחזיר המלצות."""
campaigns = json.loads(campaigns_data)
total_spend = sum(c["cost_ils"] for c in campaigns)
total_conversions = sum(c["conversions"] for c in campaigns)
analysis = {
"total_spend_ils": total_spend,
"total_conversions": total_conversions,
"avg_cpa_ils": round(total_spend / max(total_conversions, 1), 2),
"recommendations": []
}
for c in campaigns:
cpa = c["cost_ils"] / max(c["conversions"], 0.1)
if c["conversions"] < 1 and c["cost_ils"] > 100:
analysis["recommendations"].append({
"campaign": c["campaign"],
"action": "pause_or_reduce",
"reason": f"הוציא {c['cost_ils']} ש\"ח ללא המרות. שקלו להשהות.",
"potential_saving_ils": c["cost_ils"]
})
elif cpa < analysis["avg_cpa_ils"] * 0.5:
analysis["recommendations"].append({
"campaign": c["campaign"],
"action": "increase_budget",
"reason": f"CPA של {round(cpa, 2)} ש\"ח -- מתחת לממוצע. הגדילו תקציב.",
"potential_impact": "more conversions at good CPA"
})
return json.dumps(analysis, ensure_ascii=False)
@tool("generate_ad_variants")
def generate_ad_variants(product: str, target_audience: str,
usp: str) -> str:
"""מייצר 3 וריאנטים של נוסחי מודעה לבדיקות A/B."""
return json.dumps({
"product": product,
"variants": [
{
"headline_1": f"{product} -- הפתרון שחיכיתם לו",
"headline_2": f"חיסכון של 50% על {product}",
"description": f"{usp}. מתאים ל{target_audience}. נסו בחינם!",
"style": "benefit-focused"
},
{
"headline_1": f"עדיין מבזבזים זמן על {product}?",
"headline_2": f"אוטומציה של {product} ב-5 דקות",
"description": f"אלפי עסקים בישראל כבר עברו. {usp}. התחילו היום.",
"style": "pain-point"
},
{
"headline_1": f"{product} לעסקים בישראל",
"headline_2": f"תמיכה מלאה בעברית",
"description": f"{usp}. תוצאות תוך 30 יום או כסף חזרה.",
"style": "trust-focused"
}
]
}, ensure_ascii=False)
# --- Agent ---
ads_agent = Agent(
role="Digital Advertising Analyst",
goal="לנתח ביצועי קמפיינים פרסומיים, לזהות בזבוז תקציב, "
"ולהמליץ על אופטימיזציות שמשפרות ROAS",
backstory="""אתה מומחה פרסום דיגיטלי שמנתח קמפיינים ב-Google Ads
ו-Meta Ads. אתה מזהה מודעות שמבזבזות תקציב, מילות מפתח שלא ממירות,
ומצליח למצוא הזדמנויות לאופטימיזציה. אתה תמיד מדבר בשקלים (ש"ח),
מכיר את השוק הישראלי, ומדגיש ROI בכל המלצה.
חשוב: אתה ממליץ בלבד -- לעולם לא מבצע שינויים ישירות.""",
tools=[google_ads_report, budget_analyzer, generate_ad_variants],
verbose=True,
llm="anthropic/claude-sonnet-4-20250514"
)
| סוג המלצה | רמת אמון | פעולה |
|---|---|---|
| "קמפיין X הוציא 500 ש"ח ללא המרות" | גבוהה (נתונים עובדתיים) | בדקו ופעלו -- עובדה חד-משמעית |
| "הגדילו bid ב-20% על keyword Y" | בינונית (ניתוח טוב, אבל market dynamics מורכבים) | בחנו עם שינוי קטן (10%) ועקבו |
| "שנו את ה-landing page" | נמוכה (הסוכן לא רואה את ה-UX) | קחו כהשראה, לא כהנחיה ישירה |
| "הוסיפו קהל יעד חדש" | בינונית-נמוכה (תלוי במורכבות השוק) | בדקו עם תקציב קטן, A/B test |
אם יש לכם Google Ads account, היכנסו ורשמו: מהו הקמפיין הכי יקר שלכם? מהו ה-CPA שלו? ומהו הקמפיין עם ה-ROAS הכי טוב? אם אין לכם -- השתמשו בנתונים הבאים לתרגול: קמפיין "Brand" -- 200 ש"ח, 15 המרות. קמפיין "Generic" -- 800 ש"ח, 3 המרות.
ל-Google Ads ו-Meta Ads יש מדיניות פרסום מחמירה. מודעה שמבטיחה "תוצאות מובטחות" או "הכי טוב בישראל" עלולה להידחות. בשוק הישראלי, חובת סימון מודעות (חוק הגנת הצרכן) מוסיפה שכבה נוספת. כל ad copy שהסוכן מייצר חייב לעבור בדיקת compliance -- ידנית או עם guardrail אוטומטי שבודק claims מוגזמים.
Marketing Orchestrator -- תזמור המערכת
עכשיו מגיע החלק המרכזי: חיבור כל הסוכנים למערכת אחת מתואמת. ה-Marketing Orchestrator הוא CrewAI Crew שמנהל את ארבעת הסוכנים לפי שגרה מוגדרת.
CrewAI Crew -- המימוש המלא
# orchestrator.py
from crewai import Crew, Process
from agents.content_agent import content_agent, create_blog_post_task
from agents.analytics_agent import analytics_agent, create_weekly_report_task
from agents.competitor_agent import competitor_agent
from agents.ads_agent import ads_agent
from crewai import Task
import json
from datetime import datetime
# --- Orchestrator Tasks ---
def daily_tasks() -> list[Task]:
"""משימות יומיות: analytics + competitor check."""
return [
Task(
description="""בדוק את המטריקות היומיות:
1. שלוף sessions ו-conversions מאתמול מ-GA4
2. השווה מול הממוצע של 7 ימים אחרונים
3. אם יש anomaly -- דווח מיד""",
expected_output="סיכום יומי: מטריקות + התראות (אם יש)",
agent=analytics_agent
),
Task(
description="""בדוק מתחרים:
1. סרוק את דפי הבלוג של כל 3 המתחרים
2. בדוק אם יש תוכן חדש
3. בדוק מיקומי SERP ל-3 מילות מפתח עיקריות
4. דווח על כל שינוי מהותי""",
expected_output="דו"ח מתחרים יומי: שינויים + התראות",
agent=competitor_agent
),
]
def weekly_tasks() -> list[Task]:
"""משימות שבועיות: full report + content suggestions."""
return [
create_weekly_report_task(),
Task(
description="""בהתבסס על דו"ח האנליטיקס השבועי ודו"ח המתחרים:
1. זהה 3 נושאים לתוכן חדש
2. לכל נושא -- הסבר למה הוא רלוונטי עכשיו
3. תעדף: מה הכי דחוף ומה יכול לחכות""",
expected_output="רשימת 3 נושאים מתועדפים לתוכן חדש",
agent=content_agent
),
Task(
description="""נתח את ביצועי הקמפיינים של השבוע:
1. שלוף דו"ח campaign_performance
2. זהה קמפיינים שמבזבזים תקציב
3. זהה קמפיינים שמביאים ROI טוב
4. ייצר 3 המלצות אופטימיזציה ספציפיות""",
expected_output="דו"ח קמפיינים שבועי + 3 המלצות",
agent=ads_agent
),
]
def monthly_tasks(keyword: str) -> list[Task]:
"""משימות חודשיות: content calendar + strategy review."""
return [
Task(
description=f"""ייצר לוח תוכן ל-30 הימים הקרובים:
1. שלוף top keywords מ-Search Console
2. זהה פערים תחרותיים (נושאים שמתחרים כתבו ואנחנו לא)
3. שלב עם calendar events (חגים, אירועים עסקיים)
4. ייצר brief לכל פריט תוכן
פורמט הפלט:
| שבוע | נושא | סוג | פלטפורמה | keyword | סטטוס |
חגים רלוונטיים: פסח, יום העצמאות, שבועות (בהתאם לתאריך)""",
expected_output="לוח תוכן ל-30 יום עם briefs",
agent=content_agent
),
create_blog_post_task(keyword),
]
# --- The Crew ---
def run_daily():
"""הפעלת שגרה יומית."""
crew = Crew(
agents=[analytics_agent, competitor_agent],
tasks=daily_tasks(),
process=Process.sequential,
verbose=True
)
result = crew.kickoff()
save_report(result, "daily")
return result
def run_weekly():
"""הפעלת שגרה שבועית."""
crew = Crew(
agents=[analytics_agent, content_agent, competitor_agent, ads_agent],
tasks=weekly_tasks(),
process=Process.sequential,
verbose=True
)
result = crew.kickoff()
save_report(result, "weekly")
return result
def run_monthly(keyword: str):
"""הפעלת שגרה חודשית."""
crew = Crew(
agents=[content_agent, analytics_agent, competitor_agent],
tasks=monthly_tasks(keyword),
process=Process.hierarchical,
manager_llm="anthropic/claude-sonnet-4-20250514",
verbose=True
)
result = crew.kickoff()
save_report(result, "monthly")
return result
def save_report(result, report_type: str):
"""שומר דו"ח לקובץ."""
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
filename = f"output/{report_type}_report_{timestamp}.md"
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# דו\"ח {report_type} -- {timestamp}\n\n")
f.write(str(result))
print(f"Report saved: {filename}")
if __name__ == "__main__":
import sys
mode = sys.argv[1] if len(sys.argv) > 1 else "daily"
if mode == "daily":
run_daily()
elif mode == "weekly":
run_weekly()
elif mode == "monthly":
keyword = sys.argv[2] if len(sys.argv) > 2 else "אוטומציה שיווקית"
run_monthly(keyword)
TypeScript Orchestrator
// orchestrator.ts
import { generateBlogPost } from "./agents/content-agent";
import { generateWeeklyReport } from "./agents/analytics-agent";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { writeFileSync } from "fs";
interface OrchestratorResult {
type: "daily" | "weekly" | "monthly";
timestamp: string;
reports: Record<string, string>;
}
async function runDaily(): Promise<OrchestratorResult> {
console.log("Running daily marketing check...");
const analyticsReport = await generateWeeklyReport(); // daily subset
// const competitorReport = await checkCompetitors();
const result: OrchestratorResult = {
type: "daily",
timestamp: new Date().toISOString(),
reports: {
analytics: analyticsReport,
// competitors: competitorReport,
},
};
saveReport(result);
return result;
}
async function runWeekly(): Promise<OrchestratorResult> {
console.log("Running weekly marketing report...");
const [analyticsReport, blogPost] = await Promise.all([
generateWeeklyReport(),
generateBlogPost("אוטומציה שיווקית"),
]);
const result: OrchestratorResult = {
type: "weekly",
timestamp: new Date().toISOString(),
reports: {
analytics: analyticsReport,
content_draft: blogPost,
},
};
saveReport(result);
return result;
}
function saveReport(result: OrchestratorResult) {
const filename = `output/${result.type}_report_${
new Date().toISOString().split("T")[0]
}.json`;
writeFileSync(filename, JSON.stringify(result, null, 2), "utf-8");
console.log(`Report saved: ${filename}`);
}
// CLI
const mode = process.argv[2] ?? "daily";
if (mode === "daily") runDaily();
else if (mode === "weekly") runWeekly();
תזמון אוטומטי
מערכת שיווק אוטומטית צריכה לרוץ בלי שמישהו מפעיל אותה ידנית. הנה שלוש אפשרויות:
| שיטה | מתאים ל... | הגדרה |
|---|---|---|
| Cron Job | שרת Linux / Mac | 0 8 * * * cd /app && python orchestrator.py daily (כל יום ב-8:00) |
| GitHub Actions | פרויקטים ב-GitHub | Schedule trigger עם cron expression |
| Cloudflare Workers + Cron Triggers | serverless | Worker שמפעיל את הסוכן, trigger כל X שעות |
# .github/workflows/marketing-daily.yml
name: Daily Marketing Check
on:
schedule:
- cron: '0 6 * * *' # כל יום ב-6:00 UTC (8:00 ישראל)
workflow_dispatch: # גם ידנית
jobs:
daily-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install -r requirements.txt
- run: python orchestrator.py daily
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GA4_PROPERTY_ID: ${{ secrets.GA4_PROPERTY_ID }}
- uses: actions/upload-artifact@v4
with:
name: daily-report
path: output/daily_report_*.md
- חברו את כל 4 הסוכנים (content, analytics, competitor, ads) ל-Crew אחד
- הגדירו שלוש שגרות: daily (analytics + competitors), weekly (+ content + ads), monthly (+ calendar)
- הריצו את השגרה היומית ובדקו: האם כל סוכן הפיק output? האם הסדר הגיוני?
- הגדירו cron job או GitHub Action שמריץ את השגרה היומית אוטומטית
- בדקו שהדו"חות נשמרים ב-
output/עם timestamps
תוצאה צפויה: orchestrator שמריץ שגרה יומית מלאה, שומר דו"ח, ומדווח על anomalies.
רשמו את לוח הזמנים האידיאלי שלכם: באיזו שעה הייתם רוצים שהסוכן יריץ את הבדיקה היומית? באיזה יום השגרה השבועית? ובאיזה תאריך בחודש השגרה החודשית? רשמו -- תשתמשו בזה ב-cron expression.
Content Calendar Automation -- לוח תוכן אוטומטי
אחת המשימות הכי מתישות בשיווק היא תכנון לוח תוכן חודשי. צריך לשלב: נתוני SEO (אילו keywords כדאי לכתוב עליהם), פערים תחרותיים (מה המתחרים כתבו ואנחנו לא), חגים ואירועים (פסח, יום העצמאות, Black Friday), ומטרות עסקיות (השקת מוצר, קמפיין חדש).
הסוכן עושה את הכל אוטומטית. הוא שולף נתונים מ-Search Console, בודק מה המתחרים פרסמו לאחרונה, מצליב עם לוח שנה ישראלי, ומייצר content calendar מלא עם briefs.
# tools/calendar_generator.py
import json
from datetime import datetime, timedelta
# לוח חגים ואירועים ישראליים 2026
ISRAELI_EVENTS = {
"2026-04-02": "ערב פסח",
"2026-04-03": "פסח -- חג ראשון",
"2026-04-09": "פסח -- חג שביעי",
"2026-04-15": "יום השואה",
"2026-04-22": "יום הזיכרון",
"2026-04-23": "יום העצמאות",
"2026-05-13": "ל\"ג בעומר",
"2026-05-22": "שבועות",
"2026-06-14": "אירוע eCommerce Israel",
"2026-09-12": "ראש השנה",
"2026-09-21": "יום כיפור",
"2026-11-27": "Black Friday",
"2026-12-06": "חנוכה",
}
CONTENT_TYPES = {
"blog": {"frequency": "2/week", "lead_time_days": 5},
"social_linkedin": {"frequency": "3/week", "lead_time_days": 2},
"social_facebook": {"frequency": "4/week", "lead_time_days": 1},
"email": {"frequency": "1/week", "lead_time_days": 3},
"ad_copy": {"frequency": "as_needed", "lead_time_days": 2},
}
def generate_calendar(
start_date: datetime,
keywords: list[str],
competitor_gaps: list[str],
business_goals: list[str],
days: int = 30
) -> list[dict]:
"""מייצר לוח תוכן ל-N ימים."""
calendar = []
current = start_date
for day in range(days):
date = current + timedelta(days=day)
date_str = date.strftime("%Y-%m-%d")
day_of_week = date.strftime("%A")
entry = {
"date": date_str,
"day": day_of_week,
"content": [],
"event": ISRAELI_EVENTS.get(date_str, None),
}
# Blog: Sunday + Wednesday
if day_of_week in ["Sunday", "Wednesday"]:
keyword = keywords[day % len(keywords)]
entry["content"].append({
"type": "blog",
"keyword": keyword,
"status": "draft_needed",
"deadline": (date - timedelta(days=5)).strftime("%Y-%m-%d"),
"brief": f"פוסט על {keyword}. אורך: 1200-2000 מילים."
})
# LinkedIn: Sunday + Tuesday + Thursday
if day_of_week in ["Sunday", "Tuesday", "Thursday"]:
entry["content"].append({
"type": "linkedin",
"topic": keywords[(day + 1) % len(keywords)],
"status": "draft_needed"
})
# Event-based content
if entry["event"]:
entry["content"].append({
"type": "event_post",
"event": entry["event"],
"platforms": ["facebook", "linkedin", "email"],
"brief": f"תוכן ל{entry['event']}. קשרו למוצר."
})
calendar.append(entry)
return calendar
הריצו את generate_calendar() עם 5 keywords שלכם ובדקו את הפלט. ספרו: כמה פריטי תוכן יש בחודש? האם יש "ימים עמוסים מדי"? שנו את הפרמטרים עד שהלוח ריאלי לצוות שלכם.
Content Brief אוטומטי
לוח תוכן בלי content briefs הוא רשימת קניות בלי מתכון. ה-brief מפרט לכותב (אנושי או AI) בדיוק מה לכתוב:
# דוגמת content brief שהסוכן מייצר
content_brief = {
"title_suggestion": "אוטומציה שיווקית לעסקים קטנים: המדריך המלא 2026",
"primary_keyword": "אוטומציה שיווקית",
"secondary_keywords": [
"אוטומציה שיווקית לעסקים קטנים",
"כלי אוטומציה שיווקית",
"אוטומציה שיווקית בישראל"
],
"target_audience": "בעלי עסקים קטנים, 30-55, ישראל",
"content_angle": "practical_guide",
"word_count": "1,500-2,000",
"structure": [
"H2: מה זה אוטומציה שיווקית? (הגדרה + דוגמאות)",
"H2: למה עסקים קטנים צריכים אוטומציה (5 סיבות עם נתונים)",
"H2: 5 הכלים הכי טובים לעסקים בישראל",
"H2: איך מתחילים -- מדריך צעד אחר צעד",
"H2: כמה זה עולה? השוואת מחירים בש\"ח",
"H2: טעויות נפוצות (ואיך להימנע מהן)",
],
"competitor_content": [
"CompetitorA כתב על זה לפני 8 חודשים -- 1,200 מילים, ללא נתונים",
"CompetitorB לא כתב על הנושא כלל"
],
"our_angle": "יותר מעמיק + נתונים ישראליים + השוואת מחירים בש\"ח",
"internal_links": [
"/blog/crm-for-small-business",
"/blog/email-marketing-guide"
],
"cta": "הירשמו לניסיון חינם של ClickFlow"
}
| סוג התוכן | AI | אדם | שילוב (הכי טוב) |
|---|---|---|---|
| בלוג -- draft ראשון | מצוין. מהיר, עקבי, SEO | איטי אבל מקורי | AI כותב draft, אדם עורך |
| פוסט LinkedIn אישי | חסר אותנטיות | אישי ואמיתי | AI מציע מבנה, אדם ממלא סיפור אישי |
| אימייל שיווקי | טוב ל-templates | טוב ל-VIP / אישי | AI יוצר sequence, אדם מתאים אישית |
| Ad copy | מצוין ל-variants | טוב ל-creative direction | AI מייצר 10 variants, אדם בוחר 3 |
| Case study / עדות | לא מתאים | הכרחי -- צריך אותנטיות | אדם אוסף, AI מעצב ועורך |
הערכת ביצועים ו-ROI
בניתם מערכת שיווקית אוטומטית. עכשיו השאלה הקריטית: האם היא שווה את ההשקעה? בואו נחשב.
עלות הסוכן
| רכיב | עלות חודשית | הערות |
|---|---|---|
| LLM API (Anthropic/OpenAI) | $15-60 | תלוי בתדירות ובאורך outputs |
| Google APIs (GA4, GSC) | $0 | חינם לשימוש סביר |
| Search API (SerpAPI/Brave) | $5-20 | תלוי במספר חיפושים |
| Hosting (GitHub Actions / Cloudflare) | $0-10 | GitHub Actions: 2,000 דק/חודש חינם |
| סה"כ | $20-90 (~75-330 ש"ח) |
עלות אנושית מקבילה
| משימה | זמן אנושי / שבוע | עלות חודשית (150 ש"ח/שעה) |
|---|---|---|
| כתיבת 2 בלוגים | 4-6 שעות | 2,400-3,600 ש"ח |
| דו"ח אנליטיקס שבועי | 1-2 שעות | 600-1,200 ש"ח |
| מעקב מתחרים | 2-3 שעות | 1,200-1,800 ש"ח |
| ניתוח קמפיינים | 2-3 שעות | 1,200-1,800 ש"ח |
| לוח תוכן חודשי | 3-4 שעות (חודשי) | 450-600 ש"ח |
| סה"כ | 12-18 שעות/שבוע | 5,850-9,000 ש"ח |
חיסכון חודשי: 5,850-9,000 ש"ח (עבודה אנושית) פחות 75-330 ש"ח (עלות סוכן) = 5,520-8,670 ש"ח חיסכון נטו. ROI: 2,600%-7,260%. חשוב: הסוכן לא מחליף משווק -- הוא מפנה לו 12-18 שעות לשבוע לעבודה אסטרטגית, יצירתית, וניהול קשרים אישיים. ה-draft של הבלוג עדיין צריך עריכה אנושית (30-60 דק במקום 3-4 שעות כתיבה מאפס).
מדדי איכות
ROI הוא לא רק כסף. צריך לבדוק איכות:
| מדד | איך מודדים | יעד |
|---|---|---|
| Content usability rate | % drafts שפורסמו (אחרי עריכה) מתוך כלל הdrafts | 70%+ |
| Edit ratio | % שינויים שאדם עושה ב-draft של הסוכן | מתחת ל-30% |
| Alert accuracy | % התראות שבאמת היו רלוונטיות (לא false positives) | 80%+ |
| Recommendation adoption | % המלצות אופטימיזציה שנאמצו ע"י הצוות | 50%+ |
| Time to insight | כמה זמן עובר עד שהצוות מקבל insight פעיל | תוך שעה מריצה |
- רשמו את 5 המשימות השיווקיות הכי חוזרות שלכם
- לכל משימה, העריכו: כמה שעות בשבוע? מה עלות השעה?
- העריכו כמה מכל משימה סוכן AI יכול לעשות (אחוז)
- חשבו: עלות סוכן חודשית vs. חיסכון חודשי
- רשמו: באילו משימות ה-ROI הכי גבוה? (שם תתחילו)
- הוסיפו מדד איכות אחד שתעקבו אחריו
תוצאה צפויה: טבלת ROI מותאמת אישית עם תעדוף -- מאילו משימות להתחיל עם אוטומציה.
חשבו תשובה אחת: מהי המשימה השיווקית היחידה שאם תאטמטו אותה, תחסכו הכי הרבה זמן? רשמו אותה.
שגרת עבודה שיווקית עם סוכן
בנוסף לשגרת הסוכנים מפרקים קודמים (monitoring מפרק 14, research workflow מפרק 16), הנה השגרה הספציפית לניהול מערכת השיווק האוטומטית:
| תדירות | משימה | זמן |
|---|---|---|
| יומי | בדקו את הדו"ח היומי של הסוכן: יש anomalies? שינויים אצל מתחרים? | 5 דק |
| יומי | אם יש draft תוכן -- סקרו ואשרו או בקשו שינויים | 10-15 דק |
| יומי | בדקו alert accuracy: האם ההתראות האחרונות היו מוצדקות? | 2 דק |
| שבועי | סקרו את הדו"ח השבועי: מטריקות, המלצות תוכן, ניתוח מתחרים | 20 דק |
| שבועי | פעלו על המלצות אופטימיזציה -- בחנו, אשרו, או דחו | 15 דק |
| שבועי | עדכנו brand guidelines אם השתנה משהו (מוצר חדש, טון, תחרות) | 5 דק |
| חודשי | סקרו את לוח התוכן שהסוכן ייצר -- אשרו, שנו, או הוסיפו | 30 דק |
| חודשי | בדקו ROI: עלות סוכן מול חיסכון. עדכנו את ה-ROI calculator | 15 דק |
| חודשי | שפרו prompts -- בהתבסס על drafts שדרשו הרבה עריכה | 20 דק |
קבעו תזכורת בלוח השנה: "בדיקת דו"ח סוכן שיווקי" -- כל בוקר ב-8:30. גם אם עדיין לא הפעלתם את המערכת, ההרגל חשוב יותר מהכלי.
בנו את Content Agent -- הסוכן שמקבל keyword ומייצר draft של בלוג פוסט עם SEO meta tags. זה הסוכן עם ה-ROI הכי גבוה (חוסך 3-4 שעות כתיבה, עולה $0.10-0.30 לריצה), ותוכלו לראות תוצאות ביום הראשון. אחרי שזה עובד -- הוסיפו analytics, ואז competitor monitoring. צעד אחד בכל פעם.
- למה ה-Ads Agent צריך לפעול ב-READ-ONLY? מה הסיכונים של write access, ובאילו תנאים הייתם שוקלים לתת גישת כתיבה? (רמז: budget, bids, approval workflows)
- מה ההבדל בין Content Calendar ל-Content Brief? למה צריך את שניהם? (רמז: planning vs. execution guidance)
- תארו את 3 סוגי השגרות (יומית, שבועית, חודשית) של ה-Orchestrator. מה כל שגרה בודקת, ולמה הסדר הזה הגיוני? (רמז: urgency, scope, strategy)
- איך מודדים alert accuracy של Competitor Agent? למה false positives מסוכנים לא פחות מ-false negatives? (רמז: alert fatigue, trust)
- חשבו ROI: סוכן שיווקי עולה 200 ש"ח/חודש ומייצר drafts שדורשים 30 דקות עריכה במקום 3 שעות כתיבה. אם מייצרים 8 בלוגים בחודש, מהו ה-ROI? (רמז: (savings - cost) / cost, שעת משווק = 150 ש"ח)
עברתם 4 מתוך 5? מצוין -- אתם מוכנים לפרק 18.
בפרק הזה בניתם מערכת שיווק אוטומטית מלאה -- ממשימה בודדת ועד orchestrator שמנהל צוות של ארבעה סוכנים מתמחים. התחלתם עם Content Agent שיודע לייצר בלוג פוסט מ-keyword עם brand voice עקבי ו-SEO מובנה. הוספתם Analytics Agent שמתחבר ל-GA4 ו-Search Console, שולף נתונים, ומזהה anomalies -- במקום שתבלו שעה-שעתיים בשליפת דו"חות ידנית. בניתם Competitor Agent שעוקב אחרי מתחרים ישראליים -- תוכן חדש, מיקומים בגוגל, שינויי מחירים -- עם snapshots שמאפשרים השוואה לאורך זמן. הוספתם Ads Agent שמנתח קמפיינים ב-Google Ads בREAD-ONLY ומייצר המלצות אופטימיזציה שחוסכות אלפי שקלים. חיברתם הכל עם Marketing Orchestrator -- CrewAI Crew עם שגרות יומיות, שבועיות, וחודשיות שרצות אוטומטית. ייצרתם Content Calendar אוטומטי שמבוסס על SEO data, פערים תחרותיים, ולוח חגים ישראלי. ולבסוף, חישבתם ROI -- ומצאתם שהסוכן חוסך 5,500-8,700 ש"ח בחודש בעלות של 75-330 ש"ח.
הנקודה המרכזית: סוכן שיווקי לא מחליף משווק -- הוא מעצים אותו. הוא עושה את העבודה הטכנית (שליפת דו"חות, drafts ראשונים, ניטור מתחרים), ומשאיר לאדם את מה שרק אדם יודע לעשות: אסטרטגיה, יצירתיות, קשרים אישיים, והחלטות עסקיות.
בפרק הבא (פרק 18) תבנו Code Review & DevOps Agent -- סוכן שעושה review לPull Requests, עוזר עם deployments, ומנטר production. תראו איך אותם דפוסים של multi-agent orchestration מתאימים לdomain אחר לחלוטין.
צ'קליסט -- סיום פרק 17
- יצרתי brand_guidelines.json מלא עם טון, קהל, חוקי תוכן, ונקודות בידול
- בניתי Content Agent עם 3 tools (web search, SEO analyzer, brand guidelines reader)
- הרצתי את Content Agent וייצרתי draft של בלוג פוסט מ-keyword אמיתי
- בדקתי שה-draft כולל SEO meta tags (title tag + meta description)
- בניתי Analytics Agent עם חיבור ל-GA4 (או demo account) ו-anomaly detection
- ייצרתי דו"ח שבועי אוטומטי עם מטריקות, התראות, והמלצות
- בניתי Competitor Agent עם scraping, SERP tracking, ו-change detection
- הגדרתי 3 מתחרים ב-competitors.json עם keywords ל-tracking
- בניתי Ads Agent ב-READ-ONLY שמנתח ביצועי קמפיינים ומייצר המלצות
- חיברתי את כל הסוכנים ל-Marketing Orchestrator (CrewAI Crew)
- הגדרתי 3 שגרות: יומית, שבועית, וחודשית -- עם tasks לכל שגרה
- ייצרתי Content Calendar ל-30 יום עם briefs ואירועים ישראליים
- חישבתי ROI -- עלות סוכן מול חיסכון בשעות עבודה
- הגדרתי cron job / GitHub Action להפעלה אוטומטית של השגרה היומית
- מבין/ה למה READ-ONLY לפלטפורמות פרסום היא ברירת מחדל הכרחית