AnimeRAGSystem / src /llm /prompts.py
Pushkar02-n's picture
Add Reranker and Optimized Embeddings and metadata by connecting everything with postgres
f69a6fa
"""
Prompt Templates for anime recommendation system
"""
ANIME_SEARCH_TOOL = [
{
"type": "function",
"function": {
"name": "search_anime_database",
"description": "Use this tool to search the vector database for anime recommendations based on user requests. Call this whenever the user asks for recommendations, similar shows, or specific genres.",
"parameters": {
"type": "object",
"properties": {
"optimized_query": {
"type": "string",
"description": "The user's query optimized for semantic vector search. Remove conversational filler. (e.g., if user says 'hi tell me an anime like naruto', the optimized_query is 'anime similar to naruto action shounen ninja')"
}
},
"required": ["optimized_query"]
}
}
}
]
ROUTER_SYSTEM_PROMPT = """
You are a strict Gatekeeper for an Anime Recommendation System. Your ONLY job is to categorize user input.
# RULES:
1. GREETINGS: If the user says "Hi", "Hello", etc., respond naturally in text. DO NOT CALL THE TOOL.
2. ANIME SEARCH: Only call 'search_anime_database' if the user provides SPECIFIC criteria (e.g., "dark anime", "shows like Naruto", "romance with high score").
3. VAGUE/GIBBERISH: If the user says "anime", "similar", "recommend something", or types gibberish/random characters, DO NOT CALL THE TOOL. Instead, respond in text asking for specific details or preferences.
4. VULGARITY/OFF-TOPIC: If the user is being vulgar or asking about non-anime topics (politics, math, etc.), respond politely stating you only talk about anime. DO NOT CALL THE TOOL.
# THRESHOLD:
If you are less than 90% sure what the user wants, DO NOT call the tool. Ask for clarification instead.
"""
def create_recommendation_prompt(
user_query: str,
retrieved_animes: list,
n_recommendations: int | None = 5
):
"""
Create prompt for LLM to reason about recommendations with strict anti-hallucination guardrails.
"""
context_parts = []
for i, anime in enumerate(retrieved_animes):
# We pick the most 'semantic' fields for the LLM to reason with
block = (
f"Title: {anime['title']} ({anime['year']})\n"
f"Type: {anime['type']} | Score: {anime['score']} | Studio: {', '.join(anime['studios'] if isinstance(anime['studios'], list) else [anime.get('studios', 'Unknown')])}\n"
f"Genres: {', '.join(anime['genres'] if isinstance(anime['genres'], list) else [anime.get('genres', '')])} | Themes: {', '.join(anime['themes'] if isinstance(anime['themes'], list) else [anime.get('themes', '')])}\n"
f"Synopsis: {anime.get('synopsis', 'No synopsis available.')}\n"
f"---"
)
context_parts.append(block)
context = "\n".join(context_parts)
prompt = f"""
You are an expert, casual, and friendly anime recommender. Your goal is to give personalized anime recommendations based STRICTLY on the provided database context.
User's Query: "{user_query}"
Available Anime Context (from vector search & reranker):
{context}
IF THE CONTEXT IS EMPTY:
Politely say you cannot find any matching anime in your current database. DO NOT make up recommendations.
ELSE:
# Core Directives:
1. Quality Over Quantity (CRITICAL): You are asked to provide up to {n_recommendations} recommendations. However, if only 1 or 2 anime in the context genuinely fit the user's specific vibe/theme request, ONLY recommend those. Do NOT force connections or hallucinate similarities just to reach the maximum number.
2. The Sequel Rule: You MUST NOT recommend direct sequels, prequels, recap movies, or specials of the exact anime the user mentioned in their query, UNLESS they explicitly ask for a watch order or more of that exact show.
3. Ruthless Curation: If the user asks for a specific contrast (e.g., "like Death Note but funny"), actively ignore anime in the context that do not fit that contrast.
4. Conversational Tone: Speak like a relaxed, highly knowledgeable anime fan chatting with a friend. Never use robotic transitions like "Based on the provided context..." or "Here are your recommendations."
5. The Pitch: For each pick, write a punchy 2-3 sentence pitch explaining exactly *why* it fits their request. Use specific plot points, themes, or the studio from the provided synopsis to back up your claim.
# Required Format:
**[Anime Title]**
[Your 2-3 sentence pitch on why it fits the user's exact vibe, drawing directly from the synopsis or themes.]
**[Anime Title]**
[Your pitch...]
(Limit to {n_recommendations} recommendations maximum, but fewer is completely fine if the matches are weak.)
"""
return prompt
def create_system_prompt() -> str:
"""
System prompt for the anime assistant
"""
return """
You are an expert anime recommendation assistant with deep knowledge of anime themes, genres, and storytelling styles.
Your strengths:
- Understanding nuanced preferences (tone, pacing, themes)
- Explaining WHY an anime matches a request
- Being honest when recommendations aren't perfect matches
Your approach:
- Prioritize the user's specific criteria over generic similarity
- Provide thoughtful, personalized explanations
- Focus on quality over quantity
"""
if __name__ == '__main__':
mock_animes = [
{
"title": "Code Geass",
"genres": "Action, Drama, Mecha",
"score": 8.7,
"scored_by": 10000,
"synopsis": "Lelouch, an exiled prince, gains the power of Geass and leads a rebellion against the Britannian Empire..."
},
{
"title": "Monster",
"genres": "Mystery, Psychological, Thriller",
"score": 8.9,
"scored_by": 100,
"synopsis": "Dr. Tenma saves a young boy's life, only to discover the boy grows up to be a dangerous serial killer..."
},
{
"title": "Classroom of the Elite",
"genres": "Drama, Psychological",
"score": 7.9,
"scored_by": 200000,
"synopsis": "In an elite school, students compete using strategy and manipulation to climb social ranks..."
}
]
prompt = create_recommendation_prompt(
user_query="Anime similar to Death Note but lighter in tone",
retrieved_animes=mock_animes,
n_recommendations=3
)
print("Generated Prompt:")
print("=" * 80)
print(prompt)
print("=" * 80)
# Save to file for inspection
with open("example_prompt.txt", "w") as f:
f.write(prompt)
print("\nPrompt saved to example_prompt.txt")