Skip to content

Commit f4539ef

Browse files
authored
Share more logic for system prompt templating (#727)
1 parent c351f3a commit f4539ef

File tree

9 files changed

+293
-98
lines changed

9 files changed

+293
-98
lines changed

CLAUDE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,12 @@ Check in pyproject.toml and NEVER use a marker/tag that doesn't exist there. Ask
234234
- `runbooks: {}` - No runbooks available (empty catalog)
235235
- `runbooks: {catalog: [...]}` - Custom runbook catalog with entries pointing to .md files in the same directory
236236
- If `runbooks` field is not specified, default system runbooks are used
237+
238+
## Documentation Lookup
239+
240+
When asked about content from the HolmesGPT documentation website (https://robusta-dev.github.io/holmesgpt/), look in the local `docs/` directory:
241+
- Python SDK examples: `docs/installation/python-installation.md`
242+
- CLI installation: `docs/installation/cli-installation.md`
243+
- Kubernetes deployment: `docs/installation/kubernetes-installation.md`
244+
- Toolset documentation: `docs/data-sources/builtin-toolsets/`
245+
- API reference: `docs/reference/`

docs/installation/python-installation.md

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ pip install "https://github.com/robusta-dev/holmesgpt/archive/refs/heads/master.
1818
```python
1919
import os
2020
from holmes.config import Config
21-
from holmes.plugins.prompts import load_and_render_prompt
21+
from holmes.core.prompt import build_initial_ask_messages
22+
from rich.console import Console
2223

2324
print("🚀 Initializing HolmesGPT...")
2425

@@ -31,22 +32,28 @@ config = Config(
3132
)
3233
print(f"✅ Configuration created with model: {config.model}")
3334

34-
# Create AI instance
35+
# Create AI instance and console
3536
print("Creating AI instance...")
3637
ai = config.create_console_toolcalling_llm()
38+
console = Console()
3739
print("✅ AI instance ready")
3840

3941
# Ask a question
40-
print("Loading system prompt...")
41-
system_prompt = load_and_render_prompt(
42-
"builtin://generic_ask.jinja2",
43-
{"toolsets": ai.tool_executor.toolsets}
42+
question = "what pods are failing in production?"
43+
print(f"\n🔍 Asking: '{question}'")
44+
45+
# Build initial messages with system prompt
46+
messages = build_initial_ask_messages(
47+
console=console,
48+
initial_user_prompt=question,
49+
file_paths=None,
50+
tool_executor=ai.tool_executor,
51+
runbooks=config.get_runbook_catalog(),
52+
system_prompt_additions=None
4453
)
45-
print("✅ System prompt loaded")
4654

47-
print("\n🔍 Asking: 'what pods are failing in production?'")
4855
print("Holmes is thinking...")
49-
response = ai.prompt_call(system_prompt, "what pods are failing in production?")
56+
response = ai.call(messages)
5057
print(f"Holmes: {response.result}")
5158
```
5259

@@ -62,7 +69,8 @@ Complete example of using HolmesGPT Python SDK with progress tracking
6269

6370
import os
6471
from holmes.config import Config
65-
from holmes.plugins.prompts import load_and_render_prompt
72+
from holmes.core.prompt import build_initial_ask_messages
73+
from rich.console import Console
6674

6775
def main():
6876
print("🚀 Starting HolmesGPT Python SDK Example")
@@ -81,8 +89,9 @@ def main():
8189
print(f"✅ Configuration created with model: {config.model}")
8290

8391
print("\nStep 2: Creating AI instance...")
84-
# Create AI instance
92+
# Create AI instance and console
8593
ai = config.create_console_toolcalling_llm()
94+
console = Console()
8695
print("✅ AI instance created successfully")
8796

8897
print("\nStep 3: Listing available toolsets...")
@@ -99,16 +108,7 @@ def main():
99108
for tool in sorted(available_tools):
100109
print(f"{tool}")
101110

102-
print("\nStep 5: Loading system prompt...")
103-
# Load system prompt
104-
system_prompt = load_and_render_prompt(
105-
"builtin://generic_ask.jinja2",
106-
{"toolsets": ai.tool_executor.toolsets}
107-
)
108-
print("✅ System prompt loaded successfully")
109-
print(f"Prompt length: {len(system_prompt)} characters")
110-
111-
print("\nStep 6: Asking questions...")
111+
print("\nStep 5: Asking questions...")
112112
# Ask questions
113113
questions = [
114114
"what pods are failing in production?",
@@ -122,7 +122,18 @@ def main():
122122

123123
try:
124124
print("Holmes is thinking...")
125-
response = ai.prompt_call(system_prompt, question)
125+
126+
# Build initial messages
127+
messages = build_initial_ask_messages(
128+
console=console,
129+
initial_user_prompt=question,
130+
file_paths=None,
131+
tool_executor=ai.tool_executor,
132+
runbooks=config.get_runbook_catalog(),
133+
system_prompt_additions=None
134+
)
135+
136+
response = ai.call(messages)
126137
print(f"Holmes: {response.result}")
127138

128139
# Show tools that were used
@@ -200,20 +211,19 @@ def main():
200211
ai = config.create_console_toolcalling_llm()
201212
console = Console()
202213

203-
# Load system prompt
204-
system_prompt = load_and_render_prompt(
205-
"builtin://generic_ask.jinja2",
206-
{"toolsets": ai.tool_executor.toolsets}
207-
)
208-
209214
# First question
210215
print("\n🔍 First Question:")
211216
first_question = "what pods are failing in my cluster?"
212217
print(f"User: {first_question}")
213218

214-
# Build initial messages (system + first user message)
219+
# Build initial messages (includes system prompt + first user message)
215220
messages = build_initial_ask_messages(
216-
console, system_prompt, first_question, None
221+
console=console,
222+
initial_user_prompt=first_question,
223+
file_paths=None,
224+
tool_executor=ai.tool_executor,
225+
runbooks=config.get_runbook_catalog(),
226+
system_prompt_additions=None
217227
)
218228

219229
# Call AI with initial messages

holmes/core/prompt.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from rich.console import Console
2-
from typing import Optional, List, Dict
2+
from typing import Optional, List, Dict, Any, Union
33
from pathlib import Path
4+
from holmes.plugins.prompts import load_and_render_prompt
5+
from holmes.plugins.runbooks import RunbookCatalog
46

57

68
def append_file_to_user_prompt(user_prompt: str, file_path: Path) -> str:
79
with file_path.open("r") as f:
8-
user_prompt += f"\n\n<attached-file path='{file_path.absolute()}>'\n{f.read()}\n</attached-file>"
10+
user_prompt += f"\n\n<attached-file path='{file_path.absolute()}'>\n{f.read()}\n</attached-file>"
911

1012
return user_prompt
1113

@@ -25,11 +27,34 @@ def append_all_files_to_user_prompt(
2527

2628
def build_initial_ask_messages(
2729
console: Console,
28-
system_prompt_rendered: str,
2930
initial_user_prompt: str,
3031
file_paths: Optional[List[Path]],
32+
tool_executor: Any, # ToolExecutor type
33+
runbooks: Union[RunbookCatalog, Dict, None] = None,
34+
system_prompt_additions: Optional[str] = None,
3135
) -> List[Dict]:
32-
"""Build the initial messages for the AI call."""
36+
"""Build the initial messages for the AI call.
37+
38+
Args:
39+
console: Rich console for output
40+
initial_user_prompt: The user's prompt
41+
file_paths: Optional list of files to include
42+
tool_executor: The tool executor with available toolsets
43+
runbooks: Optional runbook catalog
44+
system_prompt_additions: Optional additional system prompt content
45+
"""
46+
# Load and render system prompt internally
47+
system_prompt_template = "builtin://generic_ask.jinja2"
48+
template_context = {
49+
"toolsets": tool_executor.toolsets,
50+
"runbooks": runbooks or {},
51+
"system_prompt_additions": system_prompt_additions or "",
52+
}
53+
system_prompt_rendered = load_and_render_prompt(
54+
system_prompt_template, template_context
55+
)
56+
57+
# Append files to user prompt
3358
user_prompt_with_files = append_all_files_to_user_prompt(
3459
console, initial_user_prompt, file_paths
3560
)

holmes/interactive.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,12 +761,13 @@ def display_recent_tool_outputs(
761761
def run_interactive_loop(
762762
ai: ToolCallingLLM,
763763
console: Console,
764-
system_prompt_rendered: str,
765764
initial_user_input: Optional[str],
766765
include_files: Optional[List[Path]],
767766
post_processing_prompt: Optional[str],
768767
show_tool_output: bool,
769768
tracer=None,
769+
runbooks=None,
770+
system_prompt_additions: Optional[str] = None,
770771
) -> None:
771772
# Initialize tracer - use DummyTracer if no tracer provided
772773
if tracer is None:
@@ -965,7 +966,12 @@ def get_bottom_toolbar():
965966

966967
if messages is None:
967968
messages = build_initial_ask_messages(
968-
console, system_prompt_rendered, user_input, include_files
969+
console,
970+
user_input,
971+
include_files,
972+
ai.tool_executor,
973+
runbooks,
974+
system_prompt_additions,
969975
)
970976
else:
971977
messages.append({"role": "user", "content": user_input})

holmes/main.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,6 @@ def ask(
180180
destination: Optional[DestinationType] = opt_destination,
181181
slack_token: Optional[str] = opt_slack_token,
182182
slack_channel: Optional[str] = opt_slack_channel,
183-
# advanced options for this command
184-
system_prompt: Optional[str] = typer.Option(
185-
"builtin://generic_ask.jinja2", help=system_prompt_help
186-
),
187183
show_tool_output: bool = typer.Option(
188184
False,
189185
"--show-tool-output",
@@ -214,6 +210,11 @@ def ask(
214210
"--trace",
215211
help="Enable tracing to the specified provider (e.g., 'braintrust')",
216212
),
213+
system_prompt_additions: Optional[str] = typer.Option(
214+
None,
215+
"--system-prompt-additions",
216+
help="Additional content to append to the system prompt",
217+
),
217218
):
218219
"""
219220
Ask any question and answer using available tools
@@ -252,12 +253,6 @@ def ask(
252253
refresh_toolsets=refresh_toolsets, # flag to refresh the toolset status
253254
tracer=tracer,
254255
)
255-
template_context = {
256-
"toolsets": ai.tool_executor.toolsets,
257-
"runbooks": config.get_runbook_catalog(),
258-
}
259-
260-
system_prompt_rendered = load_and_render_prompt(system_prompt, template_context) # type: ignore
261256

262257
if prompt_file and prompt:
263258
raise typer.BadParameter(
@@ -292,20 +287,23 @@ def ask(
292287
run_interactive_loop(
293288
ai,
294289
console,
295-
system_prompt_rendered,
296290
prompt,
297291
include_file,
298292
post_processing_prompt,
299293
show_tool_output,
300294
tracer,
295+
config.get_runbook_catalog(),
296+
system_prompt_additions,
301297
)
302298
return
303299

304300
messages = build_initial_ask_messages(
305301
console,
306-
system_prompt_rendered,
307302
prompt, # type: ignore
308303
include_file,
304+
ai.tool_executor,
305+
config.get_runbook_catalog(),
306+
system_prompt_additions,
309307
)
310308

311309
with tracer.start_trace(

holmes/plugins/prompts/generic_ask.jinja2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,7 @@ Relevant logs:
3636
```
3737

3838
Validation error led to unhandled Java exception causing a crash.
39+
40+
{% if system_prompt_additions %}
41+
{{ system_prompt_additions }}
42+
{% endif %}

0 commit comments

Comments
 (0)