Skip to content
3 min read · 564 words

Prompt System Overview

Seven prompts drive every intelligent behavior. They live as string templates inside the two LLM service modules, follow shared structural conventions, and are catalogued verbatim in these docs.

Where prompts live

ModulePrompts
services/openai_service.pyagent chat wrapper · email analyzer · RAG answer · retrieval evaluator
services/multi_agent_email_service.pyreply agent · reviewer agent

There is no prompt registry, versioning, or template engine — prompts are Python f-strings/literals, versioned by git like any other code. That is a deliberate stage-appropriate choice; a managed prompt store is roadmap territory.

The two prompt species

1 · Composed prompts (runtime data woven in)

The chat and RAG-answer prompts are wrappers around agent configurationname, system_prompt, language, tone are interpolated per request:

You are {agent_name}.
Agent instructions:
{system_prompt}          ← the user-authored part
Language: {language}
Tone: {tone}
Rules: ...               ← platform-enforced guardrails

The user authors only the core; the platform supplies identity framing and non-negotiable rules ("do not invent facts", "use only the provided context"). This split means every agent inherits baseline behavior for free — see Agents.

2 · Fixed-role prompts (strict JSON contracts)

The analyzer, evaluator, and reviewer are judge/extractor roles with hardcoded prompts demanding exact JSON shapes:

Return ONLY valid JSON with this exact structure:
{ ... explicit schema with field semantics ... }
Rules:
- Return only JSON.  - Do not include markdown.  - <domain constraints>

The backend enforces the contract with json.loads + defensive normalization; a violation raises and becomes a failed, observable ToolCall (Design Decisions).

Shared conventions

ConventionSeen in
Role opening — "You are …"every prompt
Explicit Rules: block, one constraint per lineevery prompt
JSON schema shown literally, with enum values spelled out (low | medium | high)analyzer, evaluator, reviewer
Mandated refusal/fallback stringsRAG answer ("I could not find this information…")
Temperature paired to role — judges 0, extractors 0.2, prose 0.3all (ladder)

The catalog

Every prompt, verbatim from source, with design commentary:

Prompt Catalog

PromptRoleOutput
Agent chatwrap the agent's identity around a conversationfree text
Email analyzerextract structure from raw emailstrict JSON
Reply agentdraft the outbound replyfree text
Reviewer agentjudge the workflow packagestrict JSON
RAG answeranswer only from contextgrounded text
Retrieval evaluatorjudge retrieval sufficiencystrict JSON

Editing prompts safely

  1. Keep the JSON schemas in sync with parsers. The analyzer's fields are consumed by name in two routers; the reviewer's by the workflow finalizer. Renaming a field in the prompt without updating consumers breaks silently (.get() defaults mask it).
  2. Never weaken the refusal contract. The RAG refusal string is matched semantics across refusal_node and the answer prompt — change both together or neither.
  3. Mind the temperature pairing — don't raise a judge's temperature for "more interesting" verdicts.
  4. Test with recorded traces — replay real WorkflowStep inputs from your dev DB against the new prompt before committing (Testing).