mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 07:19:35 +00:00
160 lines
5.1 KiB
Python
160 lines
5.1 KiB
Python
"""
|
|
Content Creator Agent — generates bilingual articles, LinkedIn posts, case studies.
|
|
وكيل إنشاء المحتوى — يُنشئ مقالات و منشورات و دراسات حالة ثنائية اللغة.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from core.agents.base import BaseAgent
|
|
from core.config.models import Task
|
|
from core.llm.base import Message
|
|
from core.prompts import get_prompt
|
|
from core.utils import generate_id, utcnow
|
|
|
|
ContentType = Literal["article", "linkedin_post", "case_study", "newsletter", "tweet_thread"]
|
|
Channel = Literal["blog", "linkedin", "twitter", "email", "whatsapp_broadcast"]
|
|
|
|
|
|
@dataclass
|
|
class ContentPiece:
|
|
id: str
|
|
content_type: ContentType
|
|
channel: Channel
|
|
locale: str
|
|
topic: str
|
|
title: str
|
|
body_markdown: str
|
|
word_count: int
|
|
tags: list[str] = field(default_factory=list)
|
|
cta: str = ""
|
|
created_at: datetime = field(default_factory=utcnow)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"id": self.id,
|
|
"content_type": self.content_type,
|
|
"channel": self.channel,
|
|
"locale": self.locale,
|
|
"topic": self.topic,
|
|
"title": self.title,
|
|
"body_markdown": self.body_markdown,
|
|
"word_count": self.word_count,
|
|
"tags": self.tags,
|
|
"cta": self.cta,
|
|
"created_at": self.created_at.isoformat(),
|
|
}
|
|
|
|
|
|
DEFAULT_LENGTHS: dict[ContentType, int] = {
|
|
"article": 800,
|
|
"linkedin_post": 200,
|
|
"case_study": 600,
|
|
"newsletter": 400,
|
|
"tweet_thread": 250,
|
|
}
|
|
|
|
DEFAULT_AUDIENCE: dict[str, str] = {
|
|
"ar": "مدراء العمليات والمبيعات في الشركات السعودية المتوسطة والكبيرة",
|
|
"en": "Operations and sales leaders at mid-to-large Saudi companies",
|
|
}
|
|
|
|
DEFAULT_CTA: dict[str, str] = {
|
|
"ar": "احجز استشارة مجانية 30 دقيقة لمعرفة كيف نطبّق هذا على شركتك",
|
|
"en": "Book a free 30-minute consultation to see how this applies to your company",
|
|
}
|
|
|
|
|
|
class ContentCreatorAgent(BaseAgent):
|
|
"""Generates channel-appropriate content."""
|
|
|
|
name = "content_creator"
|
|
|
|
async def run(
|
|
self,
|
|
*,
|
|
topic: str,
|
|
content_type: ContentType = "article",
|
|
channel: Channel = "blog",
|
|
locale: str = "ar",
|
|
audience: str | None = None,
|
|
goal: str = "educate prospects and drive consultation bookings",
|
|
length: int | None = None,
|
|
cta: str | None = None,
|
|
**_: Any,
|
|
) -> ContentPiece:
|
|
"""Generate a content piece."""
|
|
length = length or DEFAULT_LENGTHS.get(content_type, 500)
|
|
audience = audience or DEFAULT_AUDIENCE.get(locale, DEFAULT_AUDIENCE["en"])
|
|
cta = cta or DEFAULT_CTA.get(locale, DEFAULT_CTA["en"])
|
|
|
|
prompt = get_prompt(
|
|
"content_writer",
|
|
audience=audience,
|
|
goal=goal,
|
|
channel=channel,
|
|
locale=locale,
|
|
topic=topic,
|
|
length=length,
|
|
cta=cta,
|
|
)
|
|
|
|
# Arabic content → GLM (stronger for Arabic), else Claude
|
|
task = Task.ARABIC_TASKS if locale == "ar" else Task.PAGE_COPY
|
|
response = await self.router.run(
|
|
task=task,
|
|
messages=[Message(role="user", content=prompt)],
|
|
max_tokens=min(length * 4, 4000),
|
|
temperature=0.65,
|
|
)
|
|
|
|
body = response.content.strip()
|
|
title = self._extract_title(body) or topic
|
|
word_count = len(body.split())
|
|
tags = self._extract_tags(topic, content_type)
|
|
|
|
piece = ContentPiece(
|
|
id=generate_id("cnt"),
|
|
content_type=content_type,
|
|
channel=channel,
|
|
locale=locale,
|
|
topic=topic,
|
|
title=title,
|
|
body_markdown=body,
|
|
word_count=word_count,
|
|
tags=tags,
|
|
cta=cta,
|
|
)
|
|
self.log.info(
|
|
"content_generated",
|
|
id=piece.id,
|
|
content_type=content_type,
|
|
locale=locale,
|
|
words=word_count,
|
|
)
|
|
return piece
|
|
|
|
# ── Helpers ─────────────────────────────────────────────────
|
|
@staticmethod
|
|
def _extract_title(body: str) -> str | None:
|
|
for line in body.splitlines():
|
|
stripped = line.strip()
|
|
if stripped.startswith("# "):
|
|
return stripped.lstrip("# ").strip()
|
|
if stripped and not stripped.startswith("#"):
|
|
# First non-empty line as fallback title
|
|
return stripped[:120]
|
|
return None
|
|
|
|
@staticmethod
|
|
def _extract_tags(topic: str, content_type: ContentType) -> list[str]:
|
|
tags = [content_type, "ai", "saudi-arabia"]
|
|
keywords = ["healthcare", "real_estate", "logistics", "retail", "finance", "education"]
|
|
for k in keywords:
|
|
if k in topic.lower().replace(" ", "_"):
|
|
tags.append(k)
|
|
return tags
|