-
Notifications
You must be signed in to change notification settings - Fork 299
feat: add configurable tool-use enforcement #505
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
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7957a4b
feat: add configurable tool-use enforcement
jamiepine 085fb1c
Merge branch 'main' into feat/tool-use-enforcement
jamiepine 3fb01be
fix: address PR review feedback for tool-use enforcement
jamiepine 47e9a57
Merge branch 'main' into feat/tool-use-enforcement
jamiepine 9d582fe
style: fix cargo fmt
jamiepine 4ba54e9
Merge branch 'main' into feat/tool-use-enforcement
jamiepine 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| ## Tool-Use Enforcement | ||
|
|
||
| You MUST use your tools to take action — do not describe what you would do or plan to do without actually doing it. When you say you will perform an action (e.g. "I will run the tests", "Let me check the file", "I will create the project"), you MUST immediately make the corresponding tool call in the same response. Never end your turn with a promise of future action — execute it now. | ||
|
|
||
| Keep working until the task is actually complete. Do not stop with a summary of what you plan to do next time. If you have tools available that can accomplish the task, use them instead of telling the user what you would do. | ||
|
|
||
| Every response should either (a) contain tool calls that make progress, or (b) deliver a final result to the user. Responses that only describe intentions without acting are not acceptable. |
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 | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1497,8 +1497,21 @@ fn handle_cortex_receiver_result( | |||||||||||||||||||||||||
| pub fn spawn_cortex_loop(deps: AgentDeps, logger: CortexLogger) -> tokio::task::JoinHandle<()> { | ||||||||||||||||||||||||||
| tokio::spawn(async move { | ||||||||||||||||||||||||||
| let prompt_engine = deps.runtime_config.prompts.load(); | ||||||||||||||||||||||||||
| let routing = deps.runtime_config.routing.load(); | ||||||||||||||||||||||||||
| let model_name = routing.resolve(ProcessType::Cortex, None).to_string(); | ||||||||||||||||||||||||||
| let tool_use_enforcement = deps.runtime_config.tool_use_enforcement.load(); | ||||||||||||||||||||||||||
| let system_prompt = match prompt_engine.render_static("cortex") { | ||||||||||||||||||||||||||
| Ok(prompt) => prompt, | ||||||||||||||||||||||||||
| Ok(prompt) => match prompt_engine.maybe_append_tool_use_enforcement( | ||||||||||||||||||||||||||
| prompt.clone(), | ||||||||||||||||||||||||||
| tool_use_enforcement.as_ref(), | ||||||||||||||||||||||||||
| &model_name, | ||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||
| Ok(prompt) => prompt, | ||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to append tool-use enforcement, using base cortex prompt"); | ||||||||||||||||||||||||||
|
Contributor
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. Minor robustness: on append failure you can just fall back to the already-rendered base prompt instead of re-rendering the template (and potentially failing twice).
Suggested change
|
||||||||||||||||||||||||||
| prompt | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to render cortex prompt, using empty preamble"); | ||||||||||||||||||||||||||
| String::new() | ||||||||||||||||||||||||||
|
|
@@ -3276,6 +3289,9 @@ async fn pickup_one_ready_task(deps: &AgentDeps, logger: &CortexLogger) -> anyho | |||||||||||||||||||||||||
| let current_time_line = temporal_context.current_time_line(); | ||||||||||||||||||||||||||
| let worker_status_text = Some(system_info.render_for_worker(¤t_time_line)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| let routing = deps.runtime_config.routing.load(); | ||||||||||||||||||||||||||
| let model_name = routing.resolve(ProcessType::Worker, None).to_string(); | ||||||||||||||||||||||||||
| let tool_use_enforcement = deps.runtime_config.tool_use_enforcement.load(); | ||||||||||||||||||||||||||
| let worker_system_prompt = prompt_engine | ||||||||||||||||||||||||||
| .render_worker_prompt( | ||||||||||||||||||||||||||
| &deps.runtime_config.instance_dir.display().to_string(), | ||||||||||||||||||||||||||
|
|
@@ -3288,6 +3304,13 @@ async fn pickup_one_ready_task(deps: &AgentDeps, logger: &CortexLogger) -> anyho | |||||||||||||||||||||||||
| browser_config.persist_session, | ||||||||||||||||||||||||||
| worker_status_text, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| .and_then(|prompt| { | ||||||||||||||||||||||||||
| prompt_engine.maybe_append_tool_use_enforcement( | ||||||||||||||||||||||||||
| prompt, | ||||||||||||||||||||||||||
| tool_use_enforcement.as_ref(), | ||||||||||||||||||||||||||
| &model_name, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||
| .map_err(|error| anyhow::anyhow!("failed to render worker prompt: {error}"))?; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| let mut task_prompt = format!("Execute task #{}: {}", task.task_number, task.title); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
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
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.
Right now the enforcement fragment gets appended before the skills listing, so the final lines of the worker preamble are the skills section. If the goal is “last instruction wins”, it might be more effective to append enforcement after skills.