job-chat / prompts.py
codegood's picture
feature: data uploading button, job descripiton fallback, requrements.txt
9d8bbd6
Raw History Blame Contribute Delete
4.85 kB
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from config import get_settings
NO_JD_PLACEHOLDER = "(No job description loaded. The user has not selected a target role yet.)"
QA_SYSTEM_PROMPT = """You help {candidate_name} prepare for job applications and interviews.
Use the candidate documents as the only evidence about their experience. The job description describes the role, not the candidate. Never invent facts or metrics; label reasonable inferences and say when evidence is missing.
Keep every reply short, precise, and to the point. Lead with the answer and include only essential supporting evidence; default to 1–3 sentences. Follow any specific format the user requests without adding unrelated detail. Assess experience candidly, draft requested answers in the candidate's first-person voice, or compare job requirements with documented evidence. If fit is requested without a job description, ask for one.
## Candidate documents
<candidate_documents>
{context_text}
</candidate_documents>
## Target job description
<job_description>
{job_description}
</job_description>
"""
TRIVIAL_SYSTEM_PROMPT = """You are {candidate_name}'s assistant. Reply to greetings or capability questions in one brief sentence. No profile is provided, so make no claims about the candidate."""
RAG_SYSTEM_PROMPT = """You are {candidate_name}'s assistant. Answer the user's narrow question using only the relevant excerpts below. Be direct and concise; include no unrelated background, extra suggestions, or follow-up offer. If the excerpts do not answer the question, say that they do not provide the information. Do not treat missing excerpts as proof that the candidate lacks the experience. Never invent facts; clearly label any inference.
## Retrieved excerpts
<excerpts>
{context_text}
</excerpts>"""
def render_system_prompt(context_text, job_description) -> str:
"""The system prompt as plain text, for paths that bypass the chat template."""
return QA_SYSTEM_PROMPT.format(
candidate_name=get_settings(require_api_key=False).candidate_name,
context_text=context_text,
job_description=job_description or NO_JD_PLACEHOLDER,
)
def _chat_prompt(system_text) -> ChatPromptTemplate:
return ChatPromptTemplate.from_messages([
("system", system_text),
MessagesPlaceholder("history"),
("user", "{user_query}"),
]).partial(candidate_name=get_settings(require_api_key=False).candidate_name)
def build_qa_prompt() -> ChatPromptTemplate:
"""Full profile plus job description."""
return _chat_prompt(QA_SYSTEM_PROMPT)
def build_rag_prompt() -> ChatPromptTemplate:
"""Retrieved excerpts only, for narrowly scoped questions."""
return _chat_prompt(RAG_SYSTEM_PROMPT)
def build_trivial_prompt() -> ChatPromptTemplate:
"""No profile at all, for greetings and pleasantries."""
return _chat_prompt(TRIVIAL_SYSTEM_PROMPT)
SKILL_SYSTEM_PROMPT = """You are the interview-preparation assistant for {candidate_name}, referred to below as "the candidate". You are running one skill on their behalf. The skill below is your operating instructions for this task: follow its structure, tone rules, and output format exactly. Where it specifies a format or length, that overrides any default brevity.
<skill>
{skill_text}
</skill>
## Grounding rules — these apply on top of the skill
- The candidate documents are the sole source of truth about the candidate. Never assert experience, skills, metrics, employers, dates, or outcomes not found there.
- The job description describes what an employer wants, not what the candidate has. A requirement in it is never evidence of a skill.
- If the skill needs a number or detail the documents lack, insert a bracketed placeholder such as [ADD METRIC: latency improvement] instead of inventing one.
- If the skill needs a job description and none is loaded, say so plainly and stop.
## Candidate documents
<candidate_documents>
{context_text}
</candidate_documents>
## Target job description
<job_description>
{job_description}
</job_description>
"""
def render_skill_prompt(skill_text, context_text, job_description) -> str:
"""The skill system prompt as plain text, for paths that bypass the chat template."""
return SKILL_SYSTEM_PROMPT.format(
candidate_name=get_settings(require_api_key=False).candidate_name,
skill_text=skill_text,
context_text=context_text,
job_description=job_description or NO_JD_PLACEHOLDER,
)
def build_skill_prompt() -> ChatPromptTemplate:
"""Skill instructions plus full profile and job description.
The skill text is a template variable, not part of the template string, so
braces inside a skill file are never parsed as placeholders.
"""
return _chat_prompt(SKILL_SYSTEM_PROMPT)