Bedrock Converse API: Tool Choice Modes for Production Coding Agents

Bedrock Converse API: Tool Choice Modes for Production Coding Agents

Chatty coding agents burn tokens and stall CI. Bedrock Converse toolConfig.toolChoice is how you force progress: auto for interactive IDE copilots, any when a tool call is mandatory, tool when a named tool must run now.

⚡ TL;DR: Use toolChoice: {tool:{name}} for mechanical steps (apply_patch, run_tests); any when the agent must act but pick among tools; auto only for human-in-the-loop chat. Cap iterations, validate tool schemas, and sandbox side effects. Deep companions: Bedrock Agents guardrails, Lambda sandboxes, prompt caching.

The three modes in practice

Mode When Failure mode if wrong
auto IDE chat, planning Agent narrates instead of patching in CI
any Must call some tool Still may pick a weak tool
tool + name Deterministic step Wrong name → API error (good — fail loud)
import boto3
brt = boto3.client("bedrock-runtime")

TOOLS = [
  {"toolSpec": {"name": "search_repo", "description": "ripgrep", "inputSchema": {"json": {
    "type": "object", "required": ["pattern"], "properties": {
      "pattern": {"type": "string"}, "path": {"type": "string"}
    }, "additionalProperties": False
  }}}},
  {"toolSpec": {"name": "apply_patch", "description": "Apply unified diff", "inputSchema": {"json": {
    "type": "object", "required": ["diff"], "properties": {"diff": {"type": "string"}},
    "additionalProperties": False
  }}}},
  {"toolSpec": {"name": "run_tests", "description": "Run targeted tests", "inputSchema": {"json": {
    "type": "object", "required": ["filter"], "properties": {"filter": {"type": "string"}},
    "additionalProperties": False
  }}}},
]

def converse(messages, choice):
    return brt.converse(
        modelId="anthropic.claude-sonnet-4-20250514-v1:0",
        messages=messages,
        toolConfig={"tools": TOOLS, "toolChoice": choice},
        inferenceConfig={"maxTokens": 4096, "temperature": 0},
    )

Orchestrate a PR agent with forced tools

def pr_fix_loop(issue: str, max_iters: int = 8):
    messages = [{"role": "user", "content": [{"text": issue}]}]
    # 1) Force search first
    r = converse(messages, {"tool": {"name": "search_repo"}})
    messages = append_tool_results(messages, r, execute)
    # 2) Force patch
    r = converse(messages, {"tool": {"name": "apply_patch"}})
    messages = append_tool_results(messages, r, execute)
    # 3) Force tests
    r = converse(messages, {"tool": {"name": "run_tests"}})
    messages = append_tool_results(messages, r, execute)
    # 4) Allow auto only for the final summary to humans
    return converse(messages, {"auto": {}})

✅ State machine owns toolChoice per step.
❌ Single auto loop hoping the model “decides to run tests.”

Guardrails around tool execution

Even with perfect toolChoice, apply_patch must run in a sandbox — see LLM Coding Agents on AWS. Validate diffs (path allowlist, no .env, no lockfile unless intended).

function assertPatchSafe(diff: string) {
  if (/^\+\+\+ b\/\.env/m.test(diff)) throw new Error("forbid_env");
  if (/^\+\+\+ b\/pnpm-lock\.yaml/m.test(diff)) throw new Error("forbid_lockfile");
  // ✅ only packages/** and apps/**
  if (!/^\+\+\+ b\/(packages|apps)\//m.test(diff)) throw new Error("path_not_allowed");
}

Cost and latency notes

Forcing tools cuts wasted assistant tokens (illustrative: 30–50% fewer chat tokens on mechanical PR fixes). Combine with Bedrock prompt caching for large system + tool schemas. Keep temperature: 0 for CI agents.

Closing checklist

✅ Dos
– ✅ Drive toolChoice from an explicit state machine
– ✅ Use tool name for apply_patch / run_tests / search_repo
– ✅ Keep auto for human-facing summaries only
– ✅ Validate tool args against JSON Schema again in the executor
– ✅ Cap iterations and wall-clock time

❌ Don’ts
– ❌ Don’t run unattended CI agents on auto alone
– ❌ Don’t expose aws/kubectl as tools without allowlists
– ❌ Don’t ignore tool errors — feed them back as toolResult status
– ❌ Don’t raise temperature for deterministic codegen
– ❌ Don’t skip path allowlists on apply_patch

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply