-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature(dspy): Copro optimizer prompts are now injectable #942
Changes from 2 commits
c3d7770
8c8375d
6b666ef
9fc4593
38946af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
* results_latest: The min,max,avg,stddev of newest prompt scores for each predictor at each depth. | ||
* total_calls: The total number of calls to the task metric. | ||
These statistics will be returned as attributes of the best program. | ||
* additional_instructions: Instructions appended to the generation signatures. Can be used to provide explicit details on the optimization metric. | ||
""" | ||
|
||
|
||
|
@@ -63,6 +64,7 @@ def __init__( | |
depth=3, | ||
init_temperature=1.4, | ||
track_stats=False, | ||
additional_instructions=None, | ||
**_kwargs, | ||
): | ||
if breadth <= 1: | ||
|
@@ -74,8 +76,25 @@ def __init__( | |
self.prompt_model = prompt_model | ||
self.track_stats = track_stats | ||
|
||
self.basic_generate_instruction = ( | ||
self._get_signature(BasicGenerateInstruction).with_instructions( | ||
" ".join([BasicGenerateInstruction.instructions, additional_instructions]) | ||
) | ||
if additional_instructions | ||
else BasicGenerateInstruction | ||
) | ||
self.generate_instruction_given_attempts = ( | ||
self._get_signature(GenerateInstructionGivenAttempts).with_instructions( | ||
" ".join([GenerateInstructionGivenAttempts.instructions, additional_instructions]) | ||
) | ||
if additional_instructions | ||
else GenerateInstructionGivenAttempts | ||
) | ||
|
||
if "verbose" in _kwargs: | ||
dspy.logger.warning("DeprecationWarning: 'verbose' has been deprecated. To see all information for debugging, use 'dspy.set_log_level('debug')'. In the future this will raise an error.") | ||
dspy.logger.warning( | ||
"DeprecationWarning: 'verbose' has been deprecated. To see all information for debugging, use 'dspy.set_log_level('debug')'. In the future this will raise an error." | ||
) | ||
|
||
def _check_candidates_equal(self, candidate1, candidate2): | ||
for p1, p2 in zip(candidate1["program"].predictors(), candidate2["program"].predictors()): | ||
|
@@ -153,13 +172,13 @@ def compile(self, student, *, trainset, eval_kwargs): | |
if self.prompt_model: | ||
with dspy.settings.context(lm=self.prompt_model): | ||
instruct = dspy.Predict( | ||
BasicGenerateInstruction, | ||
self.basic_generate_instruction, | ||
n=self.breadth - 1, | ||
temperature=self.init_temperature, | ||
)(basic_instruction=basic_instruction) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These calls to Predict, and later attempts to access members of instruct place pretty strict requirements on the acceptable signatures - maybe more flexible to allow the partial-creation of the entire instruct object? Think a protocol or the like which specifies the signature of the signature you pass in is probably a must to allow users to understand exactly what they can put in here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @mikeedjones, yeah I agree it was a little bit too loose given the requirements of the signatures. Instead of making the signature injectable, I allowed the user to add instructions and create the signatures from the base class. Let me know if you see an issue with this approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does seem much more sensible - but maybe worth adding a method to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah - that's what "with_instructions" does! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I want to preserve the initial instructions as well; it would be something more towards the line of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it would be more straightforward for users to specify a protocol which defines the signature of an injectable I think there's a wider discussion on how to manage the internal prompts of dspy and how to enable user access to them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think down the line, it would make sense to allow the user to change the whole instructions. One could even consider some kind of meta-learning approach on the instructions of the signature used to generate the other prompts (i.e., what signatures lead to the best prompt under a few-shot learning approach). There should be a discussion about enabling users to access these signatures safely IMO. I am merely starting to get familiar with this library and wanted to keep the changes very scoped in the context of this PR. 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the concern would be making a small change here actually alters the interface to Copro, which then has to be supported until a breaking 3.x release. Maybe better to figure out how to more modify the internal prompts more generally, and then apply that here? |
||
else: | ||
instruct = dspy.Predict( | ||
BasicGenerateInstruction, | ||
self.basic_generate_instruction, | ||
n=self.breadth - 1, | ||
temperature=self.init_temperature, | ||
)(basic_instruction=basic_instruction) | ||
|
@@ -299,19 +318,21 @@ def compile(self, student, *, trainset, eval_kwargs): | |
if self.prompt_model: | ||
with dspy.settings.context(lm=self.prompt_model): | ||
instr = dspy.Predict( | ||
GenerateInstructionGivenAttempts, | ||
self.generate_instruction_given_attempts, | ||
n=self.breadth, | ||
temperature=self.init_temperature, | ||
)(attempted_instructions=attempts) | ||
else: | ||
instr = dspy.Predict( | ||
GenerateInstructionGivenAttempts, | ||
self.generate_instruction_given_attempts, | ||
n=self.breadth, | ||
temperature=self.init_temperature, | ||
)(attempted_instructions=attempts) | ||
|
||
if self.prompt_model: | ||
dspy.logger.debug(f"(self.prompt_model.inspect_history(n=1)) {self.prompt_model.inspect_history(n=1)}") | ||
dspy.logger.debug( | ||
f"(self.prompt_model.inspect_history(n=1)) {self.prompt_model.inspect_history(n=1)}" | ||
) | ||
# Get candidates for each predictor | ||
new_candidates[id(p_base)] = instr.completions | ||
all_candidates[id(p_base)].proposed_instruction.extend(instr.completions.proposed_instruction) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to update the fields, but the instructions. Unless I am mistaken, the Signature class doesn't have a method for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry - that's what
with_instructions
does!