Skip to content

Add prompt_template parameter for per-sample prompt customization #201

@SokolovAnatoliy

Description

@SokolovAnatoliy

Add a prompt_template parameter to Task$new() that allows users to customize prompts on a per-sample basis using metadata columns from their dataset.

Motivation

Currently, if you want to vary the prompt context per sample (e.g., different instructions for each row), you need to build the prompts manually before creating the Task. This is a common need when:

  • Running evaluations where each sample needs different instructions
  • Testing how models respond to varied prompting strategies
  • Parameterizing evaluations across different conditions

Solution (submitting as a PR)

Add a prompt_template argument to Task$new() that:

  1. Uses {{ column_name }} syntax (via ellmer::interpolate()) to reference metadata columns
  2. Automatically prepends the interpolated template to each sample's input
  3. Works with both generate() and generate_structured() solvers

Examples

With generate()

library(vitals)                                                                 
library(ellmer)                                                                 
library(tibble)                                                                 
                                                                               
shapes_data <- tibble(                                                          
 input = c(                                                                    
   "The shapes are square, circle and rhombus",                                
   "The shapes are square, circle and rhombus",                                
   "The shapes are square, circle and rhombus"                                 
 ),                                                                            
 target = c("square", "circle", "rhombus"),                                    
 shape_to_pick = c("square", "circle", "rhombus")                              
)                                                                               
                                                                               
tsk <- Task$new(                                                                
 dataset = shapes_data,                                                        
 solver = generate(chat_anthropic(model = "claude-sonnet-4-5")),               
 scorer = detect_match("any"),                                                 
 prompt_template = "Always pick {{ shape_to_pick }}. Just return the single chosen shape as 1 word.",                                                       
 dir = tempdir()                                                               
)                                                                               
                                                                               
tsk$eval()                                                                      

Desired result:

tsk$get_samples()$result
[1] "square"  "circle"  "rhombus"

With generate_structured()

type_shape <- type_object(                                                      
 shape = type_string("The name of the shape that was picked")                  
)                                                                               
                                                                               
shapes_data <- tibble(                                                          
 input = c(                                                                    
   "The shapes are square, circle and rhombus",                                
   "The shapes are square, circle and rhombus",                                
   "The shapes are square, circle and rhombus"                                 
 ),                                                                            
 target = c("square", "circle", "rhombus"),                                    
 shape_to_pick = c("square", "circle", "rhombus")                              
)                                                                               
                                                                               
tsk_str <- Task$new(                                                            
 dataset = shapes_data,                                                        
 solver = generate_structured(                                                 
   solver_chat = chat_anthropic(model = "claude-sonnet-4-5"),                  
   type = type_shape                                                           
 ),                                                                            
 scorer = detect_match("any"),                                                 
 prompt_template = "Always pick {{ shape_to_pick }}. Just return the single chosen shape as 1 word.",                                                       
 dir = tempdir()                                                               
)                                                                               
                                                                               
tsk_str$eval()                                                                  

Desired result:

tsk_str$get_samples()$result
[1] "[{\"shape\":\"square\"}]"  "[{\"shape\":\"circle\"}]"  "[{\"shape\":\"rhombus\"}]"

Implementation Notes

  • The template is interpolated at the Task level in $solve(), keeping solvers simple and unchanged
  • Uses ellmer::interpolate() which uses {{ }} syntax (avoids conflicts with JSON/R code)
  • The interpolated template is prepended to the input with \n\n separator
  • When prompt_template = NULL (default), behavior is unchanged

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions