Merge pull request #207 from ggozad/feat/prompt-overrides

Add prompt customization and domain preable
This commit is contained in:
Yiorgis Gozadinos 2025-12-26 13:36:07 +02:00 committed by GitHub
commit e9657c2044
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 173 additions and 9 deletions

View file

@ -1,6 +1,13 @@
# Changelog
## [Unreleased]
### Added
- **Prompt Customization**: Configure agent prompts via `prompts` config section
- `domain_preamble`: Prepended to all agent prompts for domain context
- `qa`: Full replacement for QA agent prompt
- `synthesis`: Full replacement for research synthesis prompt
### Changed
- **Embeddings**: Migrated to pydantic-ai's embeddings module

View file

@ -114,6 +114,11 @@ agui:
cors_methods: ["GET", "POST", "OPTIONS"]
cors_headers: ["*"]
prompts:
domain_preamble: "" # Prepended to all agent prompts
qa: null # Custom QA agent prompt (null = use default)
synthesis: null # Custom research synthesis prompt (null = use default)
processing:
converter: docling-local # docling-local or docling-serve
chunker: docling-local # docling-local or docling-serve
@ -189,3 +194,4 @@ For detailed configuration of specific topics, see:
- **[Search and Question Answering](qa-research.md)** - Search settings, question answering, and research workflows
- **[Document Processing](processing.md)** - Document conversion, chunking, and file monitoring
- **[Storage](storage.md)** - Database, remote storage, and vector indexing
- **[Prompts](prompts.md)** - Customize agent prompts for your domain

View file

@ -0,0 +1,108 @@
# Prompt Customization
Customize the prompts used by haiku.rag's AI agents to better match your domain and use case.
## Configuration
```yaml
prompts:
# Prepended to all agent prompts
domain_preamble: |
You are answering questions about our internal documentation.
Technical terms like "time travel" refer to database versioning features.
# Full replacement for QA agent prompt (optional)
qa: null
# Full replacement for research synthesis prompt (optional)
synthesis: null
```
## Domain Preamble
The `domain_preamble` field is prepended to **all** agent prompts (QA, research planning, search, evaluation, and synthesis). Use this to:
- Add domain context that clarifies terminology
- Set the tone or personality of responses
- Specify what the knowledge base contains
**Example:**
```yaml
prompts:
domain_preamble: |
You are a technical support assistant for Acme Corp products.
The knowledge base contains product documentation, FAQs, and troubleshooting guides.
Always be helpful and professional.
```
## Custom QA Prompt
Replace the default QA agent prompt entirely by setting `prompts.qa`. The prompt should instruct the agent how to:
1. Use the `search_documents` tool to find relevant content
2. Interpret search results with scores and metadata
3. Cite sources using chunk IDs
4. Handle insufficient information
**Example:**
```yaml
prompts:
qa: |
You are a concise technical assistant. Answer questions using only the knowledge base.
Process:
1. Search for relevant documents using the search_documents tool
2. Review results and their relevance scores
3. Provide a brief, direct answer based on retrieved content
Guidelines:
- Use only information from search results
- Include chunk IDs in cited_chunks for sources you use
- If information is insufficient, say so clearly
- Be concise - avoid unnecessary elaboration
```
## Custom Synthesis Prompt
Replace the research report synthesis prompt by setting `prompts.synthesis`. This controls how the multi-agent research workflow generates its final report.
The prompt should produce a `ResearchReport` with: `title`, `executive_summary`, `main_findings`, `conclusions`, `recommendations`, `limitations`, and `sources_summary`.
**Example:**
```yaml
prompts:
synthesis: |
Generate a research report based on the gathered evidence.
Output format:
- title: 5-12 word title
- executive_summary: 3-5 sentence overview
- main_findings: 4-8 bullet points of key findings
- conclusions: 2-4 bullet points
- recommendations: 2-5 actionable recommendations
- limitations: 1-3 limitations or gaps
- sources_summary: Brief description of sources used
Guidelines:
- Base all content strictly on collected evidence
- Be specific and objective
- Avoid meta-commentary like "This report covers..."
```
## Programmatic Configuration
```python
from haiku.rag.config import AppConfig
from haiku.rag.config.models import PromptsConfig
config = AppConfig(
prompts=PromptsConfig(
domain_preamble="You are answering questions about our product documentation.",
qa=None, # Use default QA prompt
synthesis=None, # Use default synthesis prompt
)
)
```

View file

@ -16,6 +16,7 @@ from haiku.rag.config.models import (
MonitorConfig,
OllamaConfig,
ProcessingConfig,
PromptsConfig,
ProvidersConfig,
QAConfig,
RerankingConfig,
@ -35,6 +36,7 @@ __all__ = [
"MonitorConfig",
"OllamaConfig",
"ProcessingConfig",
"PromptsConfig",
"ProvidersConfig",
"QAConfig",
"RerankingConfig",

View file

@ -162,6 +162,12 @@ class AGUIConfig(BaseModel):
cors_headers: list[str] = ["*"]
class PromptsConfig(BaseModel):
domain_preamble: str = ""
qa: str | None = None
synthesis: str | None = None
class AppConfig(BaseModel):
environment: str = "production"
storage: StorageConfig = Field(default_factory=StorageConfig)
@ -175,3 +181,4 @@ class AppConfig(BaseModel):
search: SearchConfig = Field(default_factory=SearchConfig)
providers: ProvidersConfig = Field(default_factory=ProvidersConfig)
agui: AGUIConfig = Field(default_factory=AGUIConfig)
prompts: PromptsConfig = Field(default_factory=PromptsConfig)

View file

@ -31,7 +31,7 @@ from haiku.rag.graph.research.prompts import (
SYNTHESIS_PROMPT,
)
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
from haiku.rag.utils import get_model
from haiku.rag.utils import build_prompt, get_model
def format_context_for_prompt(context: ResearchContext) -> str:
@ -76,6 +76,18 @@ def build_research_graph(
Configured Research graph
"""
model_config = config.research.model
# Build prompts with system_context if configured
plan_prompt = build_prompt(
PLAN_PROMPT
+ "\n\nUse the gather_context tool once on the main question before planning.",
config,
)
search_prompt = build_prompt(SEARCH_PROMPT, config)
decision_prompt = build_prompt(DECISION_PROMPT, config)
synthesis_prompt = build_prompt(
config.prompts.synthesis or SYNTHESIS_PROMPT, config
)
g = GraphBuilder(
state_type=ResearchState,
deps_type=ResearchDeps,
@ -98,10 +110,7 @@ def build_research_graph(
plan_agent = Agent(
model=get_model(model_config, config),
output_type=ResearchPlan,
instructions=(
PLAN_PROMPT
+ "\n\nUse the gather_context tool once on the main question before planning."
),
instructions=plan_prompt,
retries=3,
output_retries=3,
deps_type=ResearchDependencies,
@ -178,7 +187,7 @@ def build_research_graph(
agent = Agent(
model=get_model(model_config, config),
output_type=ToolOutput(RawSearchAnswer, max_retries=3),
instructions=SEARCH_PROMPT,
instructions=search_prompt,
retries=3,
deps_type=ResearchDependencies,
)
@ -281,7 +290,7 @@ def build_research_graph(
agent = Agent(
model=get_model(model_config, config),
output_type=EvaluationResult,
instructions=DECISION_PROMPT,
instructions=decision_prompt,
retries=3,
output_retries=3,
deps_type=ResearchDependencies,
@ -438,7 +447,7 @@ def build_research_graph(
agent = Agent(
model=get_model(model_config, config),
output_type=ResearchReport,
instructions=SYNTHESIS_PROMPT,
instructions=synthesis_prompt,
retries=3,
output_retries=3,
deps_type=ResearchDependencies,

View file

@ -1,6 +1,8 @@
from haiku.rag.client import HaikuRAG
from haiku.rag.config import AppConfig, Config
from haiku.rag.qa.agent import QuestionAnswerAgent
from haiku.rag.qa.prompts import QA_SYSTEM_PROMPT
from haiku.rag.utils import build_prompt
def get_qa_agent(
@ -13,11 +15,18 @@ def get_qa_agent(
Args:
client: HaikuRAG client instance.
config: Configuration to use. Defaults to global Config.
system_prompt: Optional custom system prompt.
system_prompt: Optional custom system prompt (overrides config).
Returns:
A configured QuestionAnswerAgent instance.
"""
# Determine the base prompt: explicit > config > default
if system_prompt is None:
system_prompt = config.prompts.qa or QA_SYSTEM_PROMPT
# Prepend system_context if configured
system_prompt = build_prompt(system_prompt, config)
return QuestionAnswerAgent(
client=client,
model_config=config.qa.model,

View file

@ -398,6 +398,21 @@ def get_default_data_dir() -> Path:
return data_path
def build_prompt(base_prompt: str, config: "AppConfig") -> str:
"""Build a prompt with domain_preamble prepended if configured.
Args:
base_prompt: The base prompt to use
config: AppConfig with prompts.domain_preamble
Returns:
Prompt with domain_preamble prepended if configured
"""
if config.prompts.domain_preamble:
return f"{config.prompts.domain_preamble}\n\n{base_prompt}"
return base_prompt
async def is_up_to_date() -> tuple[bool, Version, Version]:
"""Check whether haiku.rag is current.

View file

@ -65,6 +65,7 @@ nav:
- Search and Question Answering: configuration/qa-research.md
- Document Processing: configuration/processing.md
- Storage: configuration/storage.md
- Prompts: configuration/prompts.md
- CLI: cli.md
- Python: python.md
- Custom Pipelines: custom-pipelines.md