Repository navigation
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
Changes from 1 commit
7957a4b
085fb1c
3fb01be
47e9a57
9d582fe
4ba54e9
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 |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -125,8 +125,23 @@ impl Compactor { | |||||||||||||||||||||||||
| let channel_id = self.channel_id.clone(); | ||||||||||||||||||||||||||
| let deps = self.deps.clone(); | ||||||||||||||||||||||||||
| let prompt_engine = deps.runtime_config.prompts.load(); | ||||||||||||||||||||||||||
| let routing = deps.runtime_config.routing.load(); | ||||||||||||||||||||||||||
| let model_name = routing.resolve(ProcessType::Compactor, None).to_string(); | ||||||||||||||||||||||||||
| let tool_use_enforcement = deps.runtime_config.tool_use_enforcement.load(); | ||||||||||||||||||||||||||
| let compactor_prompt = match prompt_engine.render_static("compactor") { | ||||||||||||||||||||||||||
| Ok(p) => p, | ||||||||||||||||||||||||||
| Ok(prompt) => match prompt_engine.maybe_append_tool_use_enforcement( | ||||||||||||||||||||||||||
| prompt, | ||||||||||||||||||||||||||
| tool_use_enforcement.as_ref(), | ||||||||||||||||||||||||||
| &model_name, | ||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||
| Ok(prompt) => prompt, | ||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||
| tracing::error!(%error, "failed to append tool-use enforcement to compactor 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. This makes compaction bail if the enforcement fragment render fails. That feels like a non-critical failure path — I'd fall back to the base prompt and keep going.
Suggested change
|
||||||||||||||||||||||||||
| let mut flag = is_compacting.write().await; | ||||||||||||||||||||||||||
| *flag = false; | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||
| tracing::error!(%error, "failed to render compactor prompt"); | ||||||||||||||||||||||||||
| let mut flag = is_compacting.write().await; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1496,8 +1496,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, | ||||||||||||||||||||||||||
| 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_engine.render_static("cortex").unwrap_or_default() | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to render cortex prompt, using empty preamble"); | ||||||||||||||||||||||||||
| String::new() | ||||||||||||||||||||||||||
|
|
@@ -3275,6 +3288,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(), | ||||||||||||||||||||||||||
|
|
@@ -3287,6 +3303,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); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
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.