From wa-whatsapp-agent
Characterize a WhatsApp AI agent before writing any code. Use when a student has finished wa-setup and is ready to define what the bot does, or says 'wa-characterize', 'אפיין סוכן', 'בוא נתכנן את הבוט', 'מה הבוט עושה', 'הגדר את הבוט'. This skill asks the hard questions (who does it answer? what's its knowledge? which tools?) and produces a spec.json that wa-build reads to generate the bot.
How this skill is triggered — by the user, by Claude, or both
Slash command
/wa-whatsapp-agent:wa-characterizeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Define precisely what the agent does, who it answers, and what tools it needs - before a single line of code is written.
Define precisely what the agent does, who it answers, and what tools it needs - before a single line of code is written.
This skill does not write agent code. It extracts a specification. The output is a spec.json file that wa-build reads.
Prerequisites: wa-setup completed (.env with Green API credentials exists).
Simple Hebrew. Ask one question at a time. Wait for the answer. Summarize back what you understood before moving on. The student is making product decisions, not technical ones.
digraph wa_characterize {
rankdir=TB;
"Pick bot archetype\n(personal assistant vs customer service)" [shape=diamond];
"Identity & voice" [shape=box];
"Audience:\nwho does it answer?" [shape=box];
"Scope:\nwhat's in / what's out?" [shape=box];
"Knowledge base" [shape=box];
"Tools needed" [shape=box];
"Human handoff rules" [shape=box];
"Summarize spec\n+ show to student" [shape=box];
"Student approves?" [shape=diamond];
"Write spec.json" [shape=box];
"Done - suggest wa-build" [shape=doublecircle];
"Pick bot archetype\n(personal assistant vs customer service)" -> "Identity & voice";
"Identity & voice" -> "Audience:\nwho does it answer?";
"Audience:\nwho does it answer?" -> "Scope:\nwhat's in / what's out?";
"Scope:\nwhat's in / what's out?" -> "Knowledge base";
"Knowledge base" -> "Tools needed";
"Tools needed" -> "Human handoff rules";
"Human handoff rules" -> "Summarize spec\n+ show to student";
"Summarize spec\n+ show to student" -> "Student approves?";
"Student approves?" -> "Identity & voice" [label="no - revise"];
"Student approves?" -> "Write spec.json" [label="yes"];
"Write spec.json" -> "Done - suggest wa-build";
}
Everything downstream splits on this. Ask first:
"איזה בוט אנחנו מאפיינים - עוזר אישי לעצמך, או בוט שירות לקוחות?"
The defaults for every subsequent question differ:
| Personal Assistant | Customer Service | |
|---|---|---|
| Audience | Whitelist (you + spouse + assistant) | Public (anyone who messages) |
| Knowledge | Thin (delegates to tools) | Thick (business info embedded in prompt) |
| Tools | Many (calendar, email, groups, reminders) | Usually one (human handoff) |
| Group messages | Often reads groups for context | Ignores groups |
| Human handoff | N/A (you are the human) | Required feature |
Use these as defaults, then let the student override.
"בוא נתחיל בזהות. מה השם של הבוט? איך הוא ידבר - רשמי, חברי, מצחיק? תן לי דוגמה קצרה איך אתה רוצה שיענה להודעה 'היי'."
Record: name, tone_description, greeting_example.
Critical for personal assistants. Re-read Speaker 1 at 13:27 in the source transcript: "אחת השאלות המאוד, מאוד חשובות שהוא ישאל זה לאיזה מספרים הוא עונה."
For personal assistant: "הבוט הזה יהיה מחובר ליומן שלך, למייל שלך. אנחנו לא רוצים שיענה לכל מי שכותב. לאיזה מספרים הוא כן עונה? תן לי שמות ומספרי טלפון - אני, אשתי, עוזרת, וכו'."
Record as a whitelist: authorized_contacts: [{name, phone_e164}, ...]
For customer service: "הבוט יענה לכולם חוץ מקבוצות. נכון?"
Record: answer_groups: false, answer_public: true, blocklist: [] (optional future).
"תן לי שלושה נושאים שהבוט יענה עליהם, ושלושה שהוא יסרב. הרעיון הוא שאם מישהו ישאל על משהו מחוץ לתחום, הבוט יגיד בנימוס שזה לא התפקיד שלו."
Record: in_scope: [...], out_of_scope: [...], out_of_scope_response (how to decline politely).
For personal assistant: Skip this question if the student's tools (calendar, mail) cover it. Otherwise: "יש דברים שכדאי שהבוט ידע מראש עליך - למשל כתובת העסק, שעות שבהן אתה לא זמין?"
Record as static_knowledge (short paragraphs).
For customer service — offer a skeleton, don't start from a blank page:
"נכתוב את מאגר הידע. במקום להתחיל מאפס, אני אשאל אותך סעיף-סעיף. ענה על מה שרלוונטי, דלג על מה שלא:"
Ask each, wait for answer, summarize back:
Record as kb_sections: {hours, location, offerings, pricing, policies, faq}.
Important: the student may ramble or give too much. Summarize back every 3-4 sentences: "אז אני מבין ש: [סיכום]. נכון?" Keep kb_sections tight and factual - the prompt grows fast.
Explain the concept first: "הבוט יכול 'לעשות דברים', לא רק לדבר. כל 'עושה דברים' הוא כלי. בוא נחליט אילו כלים הוא צריך."
Show the menu with concrete WhatsApp examples — this makes the choice much easier than abstract capability descriptions:
"אילו מהכלים האלה הבוט צריך? אני אעזור לחבר אותם אחר כך - עכשיו רק מסמנים."
Record: tools: ["google_calendar", "gmail", "whatsapp_groups", "reminders", "human_handoff"] (subset).
For each selected tool, ask a follow-up:
Record per-tool config under tools_config.
Re-read Speaker 1 at 15:59 in the transcript - it's "מעבר לנציג אנושי" not "הסלמה".
"כשלקוח רוצה לדבר עם נציג אנושי, מה קורה? יש כמה אפשרויות:"
Strong default: option 2 or 3. Explain the loop problem explicitly: "אם תדבר ללקוח מהמספר של הבוט, הבוט יענה במקומך. לכן הכי טוב שהבוט יעביר לך את הטלפון של הלקוח, ואתה תתקשר/תכתוב מהמספר האישי שלך."
Record: handoff: {trigger_phrases: [...], manager_phone: "...", manager_name: "...", mode: "phone_number_relay"}.
Ask these only if time permits:
After collecting all answers, say:
"זה מה שקלטתי. תקרא, תגיד לי מה לשנות:"
Then print a Hebrew human-readable summary of the spec - not JSON. Example:
שם הבוט: רוני
סוג: עוזר אישי
מדבר: חברי ועם הומור קל
עונה ל: אשר (0501234567), מיטל (0527654321)
יודע לדבר על: יומן, מייל, תזכורות
מסרב: כל השאר בנימוס
כלים: יומן גוגל, Gmail (קריאה בלבד), תזכורות
Iterate until the student says yes.
Only after approval, write spec.json to the project directory (same dir as .env):
{
"version": 1,
"archetype": "personal_assistant | customer_service",
"identity": {
"name": "...",
"tone_description": "...",
"greeting_example": "..."
},
"audience": {
"answer_groups": false,
"mode": "whitelist | public",
"authorized_contacts": [{"name": "...", "phone_e164": "972..."}],
"blocklist": []
},
"scope": {
"in_scope": ["..."],
"out_of_scope": ["..."],
"out_of_scope_response": "..."
},
"knowledge": {
"static_knowledge": "...",
"kb_sections": { "hours": "...", "location": "...", "offerings": "...", "pricing": "...", "policies": "...", "faq": "..." }
},
"tools": ["google_calendar", "gmail"],
"tools_config": {
"google_calendar": {"calendars": ["primary"]},
"gmail": {"mode": "read_only"}
},
"handoff": null | {
"trigger_phrases": ["נציג אנושי", "לדבר עם בן אדם"],
"manager_phone": "972...",
"manager_name": "...",
"mode": "phone_number_relay | notification | both"
},
"extras": {
"response_length": "short | medium | long",
"language_mode": "hebrew_only | match_sender",
"off_hours_mode": "always_reply | business_hours_only"
}
}
Fields not answered are set to sensible defaults - document the default in a comment nearby.
Update .wa-state.json:
"characterize" to completed_stagescurrent_stage: "build"bot_name from spec.identity.namearchetype matches spec.archetype (update if mismatch)last_touched_isoThen say:
"האפיון מוכן. יש לנו spec.json שמתאר בדיוק מה הבוט יודע לעשות. השלב הבא: לבנות את הקוד. רוצה להמשיך עכשיו?"
wa-build via Skill tool/wa כשתחזור."wa-build with an updated spec instead of regenerating from scratch.main.py cannot. The spec drives both.wa-connect: the prompt that wa-build generates must describe the tools to the LLM. If we decide tools later, we'd have to regenerate the prompt. Easier to decide once.out_of_scope vs blocklist: out_of_scope is about topics (bot refuses); blocklist is about senders (bot doesn't see them). Students confuse these - ask the right question.trigger_phrases for handoff: also detected semantically by the LLM in the prompt, but explicit phrases give deterministic behavior for the common cases.| Problem | Solution |
|---|---|
| Student can't articulate the bot's purpose | Ask for a concrete example of a conversation they want to have |
| Student wants 20 tools | Negotiate down to 2-3 for MVP. Promise: "נוסיף עוד אחרי שזה עובד" |
| Knowledge base becoming enormous | Cap at ~2000 words. Offer: "נשים את הקצר בפרומפט; אם תרצה מאגר ידע אמיתי, זה עתידי" |
| Student skipping audience decision ("כולם") | Push back for personal assistant - explicitly list contacts. Loop bugs live here. |
No .env file exists | Skill precondition failed - send student back to wa-setup |
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
npx claudepluginhub asher-pro/wa-whatsapp-agent --plugin wa-whatsapp-agent