-
Notifications
You must be signed in to change notification settings - Fork 276
Add model_picker toolset for dynamic model switching #1937
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
Merged
+425
−34
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| #!/usr/bin/env docker agent run | ||
|
|
||
| # This example demonstrates the model_picker toolset, which lets the agent | ||
| # dynamically switch between models mid-conversation. The agent can pick the | ||
| # best model for each sub-task (e.g. a fast model for simple questions, a | ||
| # powerful one for complex reasoning) and revert back when done. | ||
|
|
||
| agents: | ||
| root: | ||
| model: google/gemini-2.5-flash-lite | ||
| description: A versatile assistant that picks the best model for each task | ||
| instruction: | | ||
| You are a helpful assistant with access to multiple AI models. | ||
| toolsets: | ||
| - type: filesystem | ||
| - type: shell | ||
| - type: model_picker | ||
| instruction: | | ||
| {ORIGINAL_INSTRUCTIONS} | ||
|
|
||
| ## Model selection policy | ||
|
|
||
| Your default model (`gemini-2.5-flash-lite`) is fast and cheap but | ||
| limited. You MUST follow this policy for every user message: | ||
|
|
||
| 1. **Classify first.** Decide whether the request is *trivial* | ||
| (greetings, single-fact lookups, yes/no answers, short | ||
| clarifications) or *non-trivial* (anything else: writing, coding, | ||
| analysis, planning, multi-step reasoning, tool use, etc.). | ||
|
|
||
| 2. **Trivial → stay on the default model.** Answer directly. | ||
|
|
||
| 3. **Non-trivial → switch before you do any work.** | ||
| Call `change_model` to `claude-haiku-4-5` as the very first action, | ||
| *before* reasoning, planning, or calling any other tool. | ||
| Then carry out the task. | ||
|
|
||
| 4. **ALWAYS revert when done.** After completing a non-trivial task, | ||
| you MUST call `revert_model` as your very last action so the next | ||
| turn starts on the cheap default again. This is mandatory—treat | ||
| it as the final step of every non-trivial request. Never end your | ||
| turn on a non-default model. | ||
|
|
||
| **Important:** never start working on a non-trivial task while still | ||
| on the default model. When in doubt, switch. | ||
| models: | ||
| - google/gemini-2.5-flash-lite | ||
| - anthropic/claude-haiku-4-5 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package builtin | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| "github.com/docker/cagent/pkg/tools" | ||
| ) | ||
|
|
||
| const ( | ||
| ToolNameChangeModel = "change_model" | ||
| ToolNameRevertModel = "revert_model" | ||
| ) | ||
|
|
||
| // ModelPickerTool provides tools for dynamically switching the agent's model mid-conversation. | ||
| type ModelPickerTool struct { | ||
| models []string // list of available model references | ||
| } | ||
|
|
||
| // Verify interface compliance | ||
| var ( | ||
| _ tools.ToolSet = (*ModelPickerTool)(nil) | ||
| _ tools.Instructable = (*ModelPickerTool)(nil) | ||
| ) | ||
|
|
||
| // ChangeModelArgs are the arguments for the change_model tool. | ||
| type ChangeModelArgs struct { | ||
| Model string `json:"model" jsonschema:"The model to switch to. Must be one of the available models."` | ||
| } | ||
|
|
||
| // NewModelPickerTool creates a new ModelPickerTool with the given list of allowed models. | ||
| func NewModelPickerTool(models []string) *ModelPickerTool { | ||
| return &ModelPickerTool{models: models} | ||
| } | ||
|
|
||
| // Instructions returns guidance for the LLM on when and how to use the model picker tools. | ||
| func (t *ModelPickerTool) Instructions() string { | ||
| return "## Model Switching\n\n" + | ||
| "You have access to multiple models and can switch between them mid-conversation " + | ||
| "using the `" + ToolNameChangeModel + "` and `" + ToolNameRevertModel + "` tools.\n\n" + | ||
| "Available models: " + strings.Join(t.models, ", ") + ".\n\n" + | ||
| "Use `" + ToolNameChangeModel + "` when the current task would benefit from a different model's strengths " + | ||
| "(e.g., switching to a faster model for simple tasks or a more capable model for complex reasoning).\n" + | ||
| "Use `" + ToolNameRevertModel + "` to return to the original model after the specialized task is complete." | ||
| } | ||
|
|
||
| // AllowedModels returns the list of models this tool allows switching to. | ||
| func (t *ModelPickerTool) AllowedModels() []string { | ||
| return t.models | ||
| } | ||
|
|
||
| // Tools returns the change_model and revert_model tool definitions. | ||
| func (t *ModelPickerTool) Tools(context.Context) ([]tools.Tool, error) { | ||
| return []tools.Tool{ | ||
| { | ||
| Name: ToolNameChangeModel, | ||
| Category: "model", | ||
| Description: fmt.Sprintf( | ||
| "Change the current model to one of the available models: %s. "+ | ||
| "Use this when you need a different model for the current task.", | ||
| strings.Join(t.models, ", "), | ||
| ), | ||
| Parameters: tools.MustSchemaFor[ChangeModelArgs](), | ||
| Annotations: tools.ToolAnnotations{ | ||
| ReadOnlyHint: true, | ||
| Title: "Change Model", | ||
| }, | ||
| }, | ||
| { | ||
| Name: ToolNameRevertModel, | ||
| Category: "model", | ||
| Description: "Revert to the agent's original/default model. " + | ||
| "Use this after completing a task that required a different model.", | ||
| Annotations: tools.ToolAnnotations{ | ||
| ReadOnlyHint: true, | ||
| Title: "Revert Model", | ||
| }, | ||
| }, | ||
| }, nil | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
MEDIUM: Missing validation for individual model strings
The validation checks that
len(t.Models) > 0, but it doesn't validate whether each string in theModelsslice is non-empty or valid. A configuration like this would pass validation:But would cause runtime errors when the agent tries to switch to the empty model string. Consider adding: