Skip to content

Commit 64bcd2f

Browse files
committed
updated dynamic prompting
1 parent 85dcd36 commit 64bcd2f

5 files changed

Lines changed: 344 additions & 304 deletions

File tree

stringsight/prompts/clustering/prompts.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66

77
def get_clustering_prompt_with_context(task_description: str | None = None) -> str:
88
"""Get clustering prompt with optional task description context.
9-
9+
1010
Args:
1111
task_description: Optional task description to provide context.
12-
12+
1313
Returns:
1414
Clustering prompt string with task context if provided.
1515
"""
1616
base_prompt = """You are an expert machine learning engineer tasked with summarizing LLM response behaviors. Given a list of properties seen in LLM responses that belong to the same cluster, create a clear description (1-3 sentences) that accurately describes most or all properties in the cluster. This should be a specific behavior of a model response, not a category of behaviors. Think: if a user saw this property, would they be able to understand the model behavior and gain valuable insight about the models specific behavior on a task?
1717
1818
To improve readability, provide a 2-5 word summary of the behavior in bold before the description. Format: **[Summary]**: [Description]. If the behavior is abstract or complex, you MUST provide a short, concrete example within the description to ensure it is clearly understood (e.g. "...such as repeating the same search query"). Avoid vague phrases like "fails to adapt" or "inefficiently" without specifying *how*."""
19-
19+
2020
if task_description:
2121
context = f"\n\n**Task Context:** The properties in this cluster were extracted from responses to the following task:\n{task_description}\n\nUse this context to ensure your cluster description is relevant and specific to this task."
2222
return base_prompt + context
23-
23+
2424
return base_prompt
2525

2626
clustering_systems_prompt = f"""You are an expert machine learning engineer tasked with summarizing LLM response behaviors. Given a list of properties seen in LLM responses that belong to the same cluster, create a clear description (1-3 sentences) that accurately describes most or all properties in the cluster. This should be a specific behavior of a model response, not a category of behaviors. Think: if a user saw this property, would they be able to understand the model behavior and gain valuable insight about the models specific behavior on a task?

stringsight/prompts/dynamic/generator.py

Lines changed: 112 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ def generate_all_prompts(
5959
method: str,
6060
num_samples: int = 5,
6161
model: str = "gpt-4.1",
62-
max_reflection_attempts: int = 3
62+
max_reflection_attempts: int = 3,
63+
skip_verification: bool = True,
64+
customize_clustering: bool = True
6365
) -> DynamicPromptResult:
6466
"""Generate all custom prompts for the pipeline.
6567
@@ -70,6 +72,8 @@ def generate_all_prompts(
7072
num_samples: Number of conversations to sample for expansion.
7173
model: LLM model for meta-prompting.
7274
max_reflection_attempts: Maximum number of reflection attempts to fix failed prompts (default: 3).
75+
skip_verification: If True, skip verification and use expanded task with base template (default: True).
76+
customize_clustering: If True, customize clustering prompts for the task (default: True).
7377
7478
Returns:
7579
DynamicPromptResult with all generated prompts.
@@ -78,7 +82,7 @@ def generate_all_prompts(
7882
random.seed(self.seed)
7983

8084
# Step 1: Expand task description
81-
logger.info("Step 1/4: Expanding task description...")
85+
logger.info("Step 1: Expanding task description...")
8286
expanded_description = self.task_expander.expand(
8387
task_description=task_description,
8488
conversations=conversations,
@@ -87,8 +91,7 @@ def generate_all_prompts(
8791
)
8892
logger.info(f"Task description expanded ({len(expanded_description)} chars)")
8993

90-
# Step 2: Generate discovery prompt
91-
logger.info("Step 2/4: Generating custom discovery prompt...")
94+
# Get base config for the method
9295
from ..extraction.universal import (
9396
single_model_config,
9497
sbs_config,
@@ -106,100 +109,126 @@ def generate_all_prompts(
106109

107110
base_config = config_map.get(method, single_model_config)
108111

109-
custom_config = self.discovery_generator.generate(
110-
expanded_task_description=expanded_description,
111-
method=method,
112-
base_config=base_config,
113-
model=model
114-
)
115-
116-
# Format into full prompt
117-
discovery_prompt = format_universal_prompt(
118-
task_description=expanded_description,
119-
config=custom_config
120-
)
121-
logger.info(f"Discovery prompt generated ({len(discovery_prompt)} chars)")
112+
if skip_verification:
113+
# Simple path: Just use expanded task with base template (no verification needed)
114+
logger.info("Step 2: Using base template with expanded task (skipping custom generation and verification)...")
115+
discovery_prompt = format_universal_prompt(
116+
task_description=expanded_description,
117+
config=base_config
118+
)
119+
verification_passed = True # No verification performed, assume valid
120+
reflection_attempts = 0
121+
logger.info(f"Discovery prompt created ({len(discovery_prompt)} chars)")
122+
else:
123+
# Complex path: Generate custom prompt and verify (old behavior)
124+
logger.info("Step 2: Generating custom discovery prompt...")
125+
custom_config = self.discovery_generator.generate(
126+
expanded_task_description=expanded_description,
127+
method=method,
128+
base_config=base_config,
129+
model=model
130+
)
122131

123-
# Step 3: Verify prompt with reflection loop
124-
logger.info("Step 3/4: Verifying prompt produces correct JSON...")
125-
sample_conv = self._select_verification_sample(conversations)
132+
# Format into full prompt
133+
discovery_prompt = format_universal_prompt(
134+
task_description=expanded_description,
135+
config=custom_config
136+
)
137+
logger.info(f"Discovery prompt generated ({len(discovery_prompt)} chars)")
126138

127-
verification_passed = False
128-
current_prompt = discovery_prompt
129-
reflection_attempts = 0
139+
# Step 3: Verify prompt with reflection loop
140+
logger.info("Step 3: Verifying prompt produces correct JSON...")
141+
sample_conv = self._select_verification_sample(conversations)
130142

131-
for attempt in range(max_reflection_attempts + 1):
132-
logger.info(f"Verification attempt {attempt + 1}/{max_reflection_attempts + 1}")
143+
verification_passed = False
144+
current_prompt = discovery_prompt
145+
reflection_attempts = 0
133146

134-
# Verify current prompt
135-
result = self.prompt_verifier.verify(
136-
custom_prompt=current_prompt,
137-
sample_conversation=sample_conv,
138-
method=method,
139-
model=model
140-
)
147+
for attempt in range(max_reflection_attempts + 1):
148+
logger.info(f"Verification attempt {attempt + 1}/{max_reflection_attempts + 1}")
141149

142-
if result.passed:
143-
logger.info(f"Verification passed on attempt {attempt + 1}!")
144-
verification_passed = True
145-
discovery_prompt = current_prompt
146-
break
147-
else:
148-
logger.warning(
149-
f"Verification failed on attempt {attempt + 1}: "
150-
f"{result.error_type} - {result.error_details}"
150+
# Verify current prompt
151+
result = self.prompt_verifier.verify(
152+
custom_prompt=current_prompt,
153+
sample_conversation=sample_conv,
154+
method=method,
155+
model=model
151156
)
152157

153-
# If this was the last attempt, fall back to static
154-
if attempt >= max_reflection_attempts:
158+
if result.passed:
159+
logger.info(f"Verification passed on attempt {attempt + 1}!")
160+
verification_passed = True
161+
discovery_prompt = current_prompt
162+
break
163+
else:
155164
logger.warning(
156-
f"All {max_reflection_attempts} reflection attempts exhausted. "
157-
"Falling back to static prompt."
165+
f"Verification failed on attempt {attempt + 1}: "
166+
f"{result.error_type} - {result.error_details}"
158167
)
159-
discovery_prompt = format_universal_prompt(
160-
task_description=expanded_description,
161-
config=base_config
168+
169+
# If this was the last attempt, fall back to static
170+
if attempt >= max_reflection_attempts:
171+
logger.warning(
172+
f"All {max_reflection_attempts} reflection attempts exhausted. "
173+
"Falling back to static prompt."
174+
)
175+
discovery_prompt = format_universal_prompt(
176+
task_description=expanded_description,
177+
config=base_config
178+
)
179+
break
180+
181+
# Reflect and fix the prompt
182+
logger.info("Running LLM reflection to fix prompt...")
183+
reflection_attempts += 1
184+
current_prompt = self.prompt_reflector.reflect_and_fix(
185+
original_prompt=current_prompt,
186+
verification_result=result,
187+
expanded_task_description=expanded_description,
188+
method=method,
189+
model=model
162190
)
163-
break
164191

165-
# Reflect and fix the prompt
166-
logger.info("Running LLM reflection to fix prompt...")
167-
reflection_attempts += 1
168-
current_prompt = self.prompt_reflector.reflect_and_fix(
169-
original_prompt=current_prompt,
170-
verification_result=result,
171-
expanded_task_description=expanded_description,
172-
method=method,
192+
# Clustering customization (optional)
193+
if customize_clustering:
194+
step_num = "Step 3" if skip_verification else "Step 4"
195+
logger.info(f"{step_num}: Customizing clustering prompts in parallel...")
196+
197+
with ThreadPoolExecutor(max_workers=3) as executor:
198+
# Submit all three clustering prompts in parallel
199+
future_clustering = executor.submit(
200+
self.clustering_customizer.customize_clustering_prompt,
201+
task_description=expanded_description,
202+
model=model
203+
)
204+
future_dedup = executor.submit(
205+
self.clustering_customizer.customize_deduplication_prompt,
206+
task_description=expanded_description,
207+
model=model
208+
)
209+
future_outlier = executor.submit(
210+
self.clustering_customizer.customize_outlier_prompt,
211+
task_description=expanded_description,
173212
model=model
174213
)
175214

176-
# Step 4: Clustering customization (parallelized)
177-
logger.info("Step 4/4: Customizing clustering prompts in parallel...")
178-
179-
with ThreadPoolExecutor(max_workers=3) as executor:
180-
# Submit all three clustering prompts in parallel
181-
future_clustering = executor.submit(
182-
self.clustering_customizer.customize_clustering_prompt,
183-
task_description=expanded_description,
184-
model=model
215+
# Collect results
216+
clustering_prompt = future_clustering.result()
217+
dedup_prompt = future_dedup.result()
218+
outlier_prompt = future_outlier.result()
219+
220+
logger.info("Clustering prompts customized (parallel execution)")
221+
else:
222+
# Use default clustering prompts
223+
logger.info("Using default clustering prompts (customization skipped)")
224+
from ..clustering.prompts import (
225+
clustering_systems_prompt,
226+
deduplication_clustering_systems_prompt,
227+
outlier_clustering_systems_prompt
185228
)
186-
future_dedup = executor.submit(
187-
self.clustering_customizer.customize_deduplication_prompt,
188-
task_description=expanded_description,
189-
model=model
190-
)
191-
future_outlier = executor.submit(
192-
self.clustering_customizer.customize_outlier_prompt,
193-
task_description=expanded_description,
194-
model=model
195-
)
196-
197-
# Collect results
198-
clustering_prompt = future_clustering.result()
199-
dedup_prompt = future_dedup.result()
200-
outlier_prompt = future_outlier.result()
201-
202-
logger.info("Clustering prompts customized (parallel execution)")
229+
clustering_prompt = clustering_systems_prompt
230+
dedup_prompt = deduplication_clustering_systems_prompt
231+
outlier_prompt = outlier_clustering_systems_prompt
203232

204233
return DynamicPromptResult(
205234
discovery_prompt=discovery_prompt,

0 commit comments

Comments
 (0)