Community Engagement Automation¶
Community engagement is the difference between broadcasting into a void and building an audience that trusts you. But genuine engagement at scale requires systems that can listen, classify, respond, and escalate — without sounding like a bot.
This guide covers building an automated community engagement system with Hermes: help-first strategy, platform-specific response patterns, sentiment detection, escalation rules, and measurement frameworks.
The Help-First Strategy¶
Every community interaction falls on a spectrum:
Ignore ─── Auto-Respond ─── Agent-Assisted ─── Human Required
↑ ↑ ↑ ↑
Spam Common Qs Complex Qs Crisis/Sensitive
The help-first strategy means: when in doubt, be helpful. An imperfect helpful response beats a perfect silence. But "being helpful" doesn't mean answering everything — it means routing correctly.
Decision Framework¶
For every inbound message, ask three questions:
- Does this need a response? (Spam, bots, pure negativity without substance = no)
- Can this be answered from existing knowledge? (FAQ, docs, known solutions = yes → auto)
- Does this require judgment, empathy, or authority? (Sensitive topics, complaints, legal = yes → human)
def classify_inbound(message: dict) -> str:
"""Classify an inbound community message."""
classification = hermes_llm.complete(
messages=[
{"role": "system", "content": """Classify this community message into exactly one category:
- spam: Unsolicited promotion, bot-like, irrelevant links
- faq: Common question answerable from documentation
- technical_support: Specific technical issue needing troubleshooting
- feedback: Product feedback, feature request, suggestion
- complaint: Negative experience, needs empathy and resolution
- praise: Positive feedback, testimonial
- sensitive: Contains legal, safety, or PR risk implications
- conversation: General discussion, doesn't need a specific answer
Return ONLY the category name."""},
{"role": "user", "content": message["text"]}
],
tier="lightweight"
)
return classification.strip().lower()
Routing Matrix¶
| Category | Auto-Response | Agent-Assisted | Human Review |
|---|---|---|---|
| spam | Ignore + hide | - | Weekly audit |
| faq | Answer from knowledge base | - | Spot-check 5% |
| technical_support | Initial troubleshooting | Escalate if unresolved | Complex cases |
| feedback | Acknowledge + log | Summarize trends | Product decision |
| complaint | Empathetic acknowledgment | Draft response | Must approve before send |
| praise | Thank + engage | - | - |
| sensitive | Only acknowledge receipt | - | Must handle |
| conversation | Engage naturally | Monitor tone | Check sentiment weekly |
Response Patterns¶
FAQ Auto-Response¶
def handle_faq(message: dict) -> str:
"""Auto-respond to FAQ using knowledge base retrieval."""
# Search knowledge base for relevant answers
results = hermes_tools.query_knowledge_base(
query=message["text"],
top_k=3,
min_score=0.7
)
if not results:
# Can't answer confidently — escalate
return escalate_to_support(message)
# Generate a helpful, human-sounding response
response = hermes_llm.complete(
messages=[
{"role": "system", "content": """You are a helpful community manager.
Answer this question using the provided knowledge base results.
Rules:
- Be concise but complete
- Use a friendly, conversational tone
- Include a link to the full documentation if applicable
- Ask if they need further clarification
- Never make up information not in the knowledge base
- If the answer is uncertain, say so and offer to escalate"""},
{"role": "user", "content": f"""
Question: {message['text']}
Knowledge base results:
{json.dumps(results)}
"""}
],
tier="standard"
)
return response
Complaint Handling¶
Complaints require care. The goal is de-escalation and resolution:
def handle_complaint(message: dict) -> dict:
"""Draft a complaint response for human review before sending."""
# Analyze complaint sentiment and specifics
analysis = hermes_llm.complete(
messages=[
{"role": "system", "content": """Analyze this complaint:
1. What specifically is the user upset about?
2. How severe is this? (1-10)
3. Is this a one-off issue or systemic?
4. What would resolve this for the user?
5. Does this pose any PR or legal risk?
Return as JSON."""},
{"role": "user", "content": message["text"]}
],
tier="standard"
)
analysis_data = json.loads(analysis)
# Draft response
draft = hermes_llm.complete(
messages=[
{"role": "system", "content": """Draft a response to this complaint.
Structure:
1. Acknowledge and validate their frustration
2. Apologize for the specific issue (not generic "sorry for inconvenience")
3. Explain what happened (if known) or what you'll do to find out
4. Offer a concrete next step or resolution
5. Provide a way to follow up directly
Tone: Empathetic, professional, action-oriented. Never defensive."""},
{"role": "user", "content": message["text"]}
],
tier="premium" # Complaints deserve the best model
)
return {
"action": "hold_for_review",
"severity": analysis_data["severity"],
"draft_response": draft,
"requires_human": analysis_data["severity"] >= 7,
"analysis": analysis_data
}
Conversation Engagement¶
Not every message needs a solution — some just need engagement:
def engage_conversation(message: dict, context: dict) -> str:
"""Engage with general conversation naturally."""
response = hermes_llm.complete(
messages=[
{"role": "system", "content": """You're participating in a community conversation.
Guidelines:
- Be genuinely interested and engaging
- Add value — insight, humor, or a useful question
- Keep it brief (2-3 sentences typically)
- Match the tone of the community
- Never be argumentative or dismissive
- Don't force product mentions unless natural
- If they share something personal, acknowledge it warmly"""},
{"role": "user", "content": f"""
Community context: {context.get('channel_description', 'General discussion')}
Message: {message['text']}
Recent thread context: {context.get('recent_messages', [])}
"""}
],
tier="standard"
)
return response
Platform-Specific Approaches¶
LinkedIn¶
Audience: Professional, business-focused. Response style: Substantive, adds professional insight. Best practices: - Respond within 4 hours during business days - Add value to the discussion, not just "great post!" - Share relevant experience or data - Keep responses professional but personable - Tag relevant connections sparingly and only when genuinely relevant
linkedin_voice = """
Tone: Professional, insightful, collaborative
Do: Share industry perspective, ask thoughtful questions, cite experience
Don't: Overly casual, self-promotional in others' threads, engage with controversial topics
Response time target: 4 business hours
"""
X/Twitter¶
Audience: Fast-moving, opinionated, rewards wit. Response style: Concise, timely, authentic. Best practices: - Respond within 1-2 hours (the platform moves fast) - Be concise — threads are for depth, replies are for engagement - Authenticity matters more than polish - Don't engage with obvious trolls - Quote-tweet for adding substantial commentary
twitter_voice = """
Tone: Concise, authentic, sometimes playful
Do: Respond quickly, be helpful, show personality
Don't: Overly formal, ignore legitimate questions, feed trolls
Response time target: 2 hours
"""
Reddit¶
Audience: Skeptical, values substance over marketing. Response style: Thorough, transparent, helpful. Best practices: - Disclose affiliation transparently - Provide genuine value — tutorials, explanations, data - Never astroturf or use fake accounts - Accept criticism gracefully - Follow subreddit rules meticulously - Upvote good content from others
reddit_voice = """
Tone: Transparent, helpful, humble
Do: Disclose affiliation, provide deep value, respect community norms
Don't: Market or sell, argue defensively, brigade or manipulate votes
Response time target: 6 hours
CRITICAL: Always disclose you're representing the product.
"""
YouTube Comments¶
Audience: Engaged viewers, often asking follow-up questions. Response style: Appreciative, informative, community-building. Best practices: - Heart and reply to thoughtful comments - Answer questions from the video content - Pin helpful FAQs or updates - Thank supporters genuinely - Address constructive criticism professionally
Sentiment Detection¶
Monitor overall community sentiment, not just individual messages:
def analyze_community_sentiment(platform: str, lookback_hours: int = 24) -> dict:
"""Analyze sentiment trends across community interactions."""
messages = fetch_recent_messages(platform, lookback_hours)
if not messages:
return {"status": "no_data"}
# Classify sentiment for each message
sentiment_distribution = {"positive": 0, "neutral": 0, "negative": 0}
topics = defaultdict(lambda: {"positive": 0, "negative": 0, "mentions": []})
for msg in messages:
sentiment = classify_sentiment(msg)
sentiment_distribution[sentiment["label"]] += 1
for topic in sentiment.get("topics", []):
topics[topic][sentiment["label"]] += 1
topics[topic]["mentions"].append(msg["text"][:200])
# Calculate trend (comparing to previous period)
prev_distribution = get_sentiment_distribution(platform, lookback_hours * 2, lookback_hours)
trend = {}
for label in ["positive", "neutral", "negative"]:
current_pct = sentiment_distribution[label] / len(messages) * 100
prev_pct = prev_distribution.get(label, 0) / max(prev_distribution.get("total", 1), 1) * 100
trend[label] = {
"current": round(current_pct, 1),
"change": round(current_pct - prev_pct, 1)
}
# Flag concerning patterns
alerts = []
if trend["negative"]["change"] > 10:
alerts.append(f"⚠️ Negative sentiment spiking (+{trend['negative']['change']}%)")
# Identify hot topics
hot_topics = sorted(
topics.items(),
key=lambda x: x[1]["negative"] - x[1]["positive"],
reverse=True
)[:3]
return {
"period": f"Last {lookback_hours}h",
"total_messages": len(messages),
"sentiment_distribution": sentiment_distribution,
"trend": trend,
"alerts": alerts,
"top_concerns": [
{"topic": t, "sentiment_ratio": f"{d['positive']}/{d['negative']}"}
for t, d in hot_topics
]
}
Sentiment Alert Thresholds¶
sentiment_alerts:
negative_spike:
threshold: "+10% in 24 hours"
action: "Alert community team, review recent posts for trigger"
positive_spike:
threshold: "+20% in 24 hours"
action: "Identify what's working, consider amplifying"
sustained_negative:
threshold: ">30% negative for 48 hours"
action: "Escalate to leadership, pause scheduled posts, investigate"
topic_emergence:
threshold: "New topic with >10 mentions and >50% negative"
action: "Investigate immediately — may indicate a product issue"
Measurement Framework¶
Key Metrics¶
| Metric | Definition | Target | Alert Threshold |
|---|---|---|---|
| Response Rate | % of legitimate comments receiving a response | >90% | <80% |
| Response Time | Median time to first response | <2 hours | >6 hours |
| Sentiment Ratio | Positive / (Positive + Negative) | >0.80 | <0.70 |
| Resolution Rate | % of support issues resolved in-thread | >60% | <40% |
| Escalation Rate | % of interactions escalated to human | <10% | >25% |
| Community Growth | Net new followers/subscribers | Platform-dependent | Negative for 2+ weeks |
Weekly Community Health Dashboard¶
def community_health_report() -> dict:
"""Generate weekly community health dashboard."""
platforms = ["linkedin", "twitter", "reddit", "youtube"]
report = {}
overall = {
"total_interactions": 0,
"total_responses": 0,
"avg_response_time_minutes": 0,
"sentiment_ratio": 0
}
for platform in platforms:
data = fetch_platform_engagement_data(platform, days=7)
report[platform] = {
"interactions": data["total_messages"],
"responses_sent": data["responses"],
"response_rate": data["responses"] / max(data["total_messages"], 1) * 100,
"avg_response_time_min": data["avg_response_time"],
"sentiment": data["sentiment_distribution"],
"top_issues": data["top_topics"][:5],
"human_escalations": data["escalation_count"]
}
overall["total_interactions"] += data["total_messages"]
overall["total_responses"] += data["responses"]
overall["response_rate"] = overall["total_responses"] / max(overall["total_interactions"], 1) * 100
# Generate executive summary
summary = hermes_llm.complete(
messages=[
{"role": "system", "content": "Summarize this community health report in 3-5 bullet points for leadership. Focus on trends, concerns, and wins."},
{"role": "user", "content": json.dumps({"overall": overall, "by_platform": report})}
],
tier="premium"
)
return {
"period": "Last 7 days",
"summary": summary,
"overall": overall,
"by_platform": report
}
Human Escalation Rules¶
Some things should never be automated:
MUST_ESCALATE_PATTERNS = [
# Legal risks
r"(?i)(sue|lawsuit|legal action|attorney|lawyer)",
r"(?i)(GDPR|CCPA|data privacy violation|personal data)",
# Safety concerns
r"(?i)(threat|harm|danger|emergency|suicide|self-harm)",
# Serious product failures
r"(?i)(data loss|corrupted|deleted all|lost everything)",
r"(?i)(billing fraud|unauthorized charge|stolen)",
# PR risks
r"(?i)(discrimination|harassment|hate speech)",
r"(?i)(security breach|hacked|vulnerability|exploit)",
]
def check_escalation_triggers(message: str) -> bool:
"""Check if a message triggers mandatory human escalation."""
for pattern in MUST_ESCALATE_PATTERNS:
if re.search(pattern, message):
return True
return False
Continuous Improvement¶
Feedback Loop¶
Every week, review auto-responses that received follow-up questions (indicating incomplete answers) and those that received no further engagement (indicating resolved issues):
def review_response_quality():
"""Analyze which responses are working and which aren't."""
# Find responses that led to follow-ups (incomplete)
follow_up_threads = fetch_threads_with_follow_ups(days=7)
# Find responses that resolved the conversation
resolved_threads = fetch_resolved_threads(days=7)
# Compare patterns
analysis = hermes_llm.complete(
messages=[
{"role": "system", "content": "Analyze these support threads. What patterns make responses effective vs ineffective?"},
{"role": "user", "content": f"""
Responses needing follow-up ({len(follow_up_threads)}):
{json.dumps(follow_up_threads[:10])}
Responses that resolved cleanly ({len(resolved_threads)}):
{json.dumps(resolved_threads[:10])}
"""}
],
tier="premium"
)
# Update response templates based on findings
update_response_templates(analysis)
Next: Social Publishing · Video Production · Outputs & Results