craftpilot / agents /copywriter.py
skamathramesh's picture
CraftPilot: multi-agent craft business assistant
98a986c verified
Raw
History Blame Contribute Delete
2 kB
"""Copywriter agent: generates product descriptions and social captions."""
from jinja2 import Template
from agents.llm import LLMClient
from agents.models import AgentTrace, CatalogerOutput, CopywriterOutput
SYSTEM_PROMPT = (
"You are a warm, authentic copywriter for handmade crafts. You write "
"product descriptions and social media captions that feel personal and "
"artisan -- never corporate or salesy.\n\n"
"Your tone: warm, genuine, storytelling, inviting. Write as if a real "
"craftsperson is sharing their work.\n\n"
"Always respond with valid JSON."
)
USER_TEMPLATE = Template(
"Write marketing copy for this {{ craft_type }} item:\n\n"
"Category: {{ catalog.category }} / {{ catalog.sub_category }}\n"
"Materials: {{ catalog.materials | join(', ') }}\n"
"Colors: {{ catalog.colors | join(', ') }}\n"
"Size: {{ catalog.estimated_size }}\n"
"Complexity: {{ catalog.complexity }}\n"
"{% if user_notes %}Creator's notes: {{ user_notes }}{% endif %}\n\n"
"Provide:\n"
"1. A catchy product title (under 60 characters)\n"
"2. A short description (1-2 sentences)\n"
"3. A longer description (1 paragraph, warm and personal)\n"
"4. Exactly 3 Instagram caption variations (each under 200 characters, "
"include relevant hashtags)"
)
async def run(
llm: LLMClient,
craft_type: str,
catalog: CatalogerOutput,
user_notes: str | None = None,
) -> tuple[CopywriterOutput, AgentTrace]:
prompt = USER_TEMPLATE.render(
craft_type=craft_type,
catalog=catalog,
user_notes=user_notes,
)
result, duration_ms = await llm.agenerate(
system=SYSTEM_PROMPT,
prompt=prompt,
output_schema=CopywriterOutput,
max_tokens=1024,
)
trace = AgentTrace(
agent_name="copywriter",
input_text=prompt,
output_data=result.model_dump(),
duration_ms=duration_ms,
model_id=llm.model_id,
)
return result, trace