tyndale-ai-service/app/prompts.py

137 lines
4.4 KiB
Python

"""System prompts for RAG-based codebase Q&A."""
from app.embeddings.retriever import RetrievedChunk
from app.memory.conversation import Message
# For codebase questions WITH retrieved context
CODEBASE_SYSTEM_PROMPT = """You answer questions about the Tyndale trading system using ONLY the provided YAML artifacts.
HARD CONSTRAINTS:
- Do NOT assume access to source code
- Do NOT invent implementation details not in the artifacts
- Do NOT speculate about code mechanics beyond what artifacts describe
- If artifacts do not contain enough information, say so explicitly
RESPONSE STYLE:
- Prefer architectural and behavioral explanations over mechanics
- Reference source files by path (e.g., ./trader.py)
- Explain trading concepts for developers without finance background
- Keep responses focused and concise"""
# For general questions (no RAG context)
GENERAL_SYSTEM_PROMPT = """You are an assistant for the Tyndale trading system documentation.
You can answer general questions, but for specific codebase questions, you need artifact context.
If the user asks about specific code without context, ask them to rephrase or be more specific."""
# For clarification/follow-ups (uses conversation history)
CLARIFICATION_SYSTEM_PROMPT = """You are continuing a conversation about the Tyndale trading system.
Use the conversation history to answer follow-up questions.
If you need to look up new information, ask the user to rephrase as a standalone question."""
def select_system_prompt(intent: str, has_context: bool) -> str:
"""Select appropriate system prompt based on intent and context.
Args:
intent: Classified intent (codebase, general, clarification)
has_context: Whether RAG context was retrieved
Returns:
System prompt string
"""
if intent == "codebase" and has_context:
return CODEBASE_SYSTEM_PROMPT
elif intent == "codebase" and not has_context:
return CODEBASE_SYSTEM_PROMPT + "\n\nNOTE: No relevant artifacts were found for this question. Acknowledge this limitation in your response."
elif intent == "clarification":
return CLARIFICATION_SYSTEM_PROMPT
else:
return GENERAL_SYSTEM_PROMPT
def format_context(chunks: list[RetrievedChunk]) -> str:
"""Format retrieved chunks as context for the LLM.
Args:
chunks: List of retrieved chunks
Returns:
Formatted context string
"""
if not chunks:
return ""
context_parts = ["## Retrieved Artifact Context\n"]
for i, chunk in enumerate(chunks, 1):
context_parts.append(f"### Source {i}: {chunk.source_file} ({chunk.chunk_type})")
context_parts.append(chunk.content)
context_parts.append("") # Empty line separator
return "\n".join(context_parts)
def format_history(history: list[Message], max_messages: int = 10) -> str:
"""Format conversation history for the LLM.
Args:
history: List of conversation messages
max_messages: Maximum messages to include
Returns:
Formatted history string
"""
if not history:
return ""
# Take most recent messages
recent = history[-max_messages:] if len(history) > max_messages else history
history_parts = ["## Conversation History\n"]
for msg in recent:
role = "User" if msg.role == "user" else "Assistant"
history_parts.append(f"**{role}**: {msg.content}")
history_parts.append("")
return "\n".join(history_parts)
def build_rag_prompt(
user_message: str,
intent: str,
chunks: list[RetrievedChunk],
history: list[Message],
) -> tuple[str, str]:
"""Build complete RAG prompt with system message and user content.
Args:
user_message: Current user message
intent: Classified intent
chunks: Retrieved context chunks
history: Conversation history
Returns:
Tuple of (system_prompt, user_content)
"""
# Select system prompt
system_prompt = select_system_prompt(intent, bool(chunks))
# Build user content
parts = []
# Add context if available
if chunks:
parts.append(format_context(chunks))
# Add history for clarification intent
if intent == "clarification" and history:
parts.append(format_history(history))
# Add current question
parts.append(f"## Current Question\n{user_message}")
user_content = "\n\n".join(parts)
return system_prompt, user_content