Build

Best practices

Add parallelism, timeouts, retries, and turn caps to the retrieval loop before you ship.

The loop works as written, but a production path needs a few more guards: parallelism, timeouts, and a turn cap.

Run tool calls in parallel

A turn often returns several tool calls. Run them concurrently with a per-tool timeout so one slow search does not block the rest.

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor() as executor:
    results = list(executor.map(execute_tool_call, assistant_msg.tool_calls))
const results = await Promise.all(assistantMsg.tool_calls.map(executeToolCall));

Time out and retry requests

Set a timeout on every SID-1 request, and retry transient failures. Back off on 429 rate-limit responses rather than retrying them immediately; do not retry 4xx errors that will fail again.

Keep results lean

Return snippets from the search tools and capped text from read so the context window stays manageable (see run the tools on your backend). Extra metadata fields cost context and make the loop noisier. When a turn makes several tool calls, separate the responses with a blank line.

Cap and instrument the loop

Always bound the loop with MAX_TURNS so a run terminates even if report_helpful_ids is never called. After you append a turn's tool messages, tell SID-1 how many turns remain by adding a budget notice to the content of the last tool message. When one turn is left, add a sentence telling it to report next turn, so it stops before the cap returns an empty list.

def turn_budget_notice(turns_left: int) -> str:
    notice = f"\n\nYou have {turns_left} out of {MAX_TURNS} turns left."
    if turns_left == 1:
        notice += " You must call `report_helpful_ids` in the next turn."
    return notice


# After appending the turn's tool messages, before the next request:
turns_left = MAX_TURNS - (turn + 1)
if turns_left >= 1:
    messages[-1]["content"] += turn_budget_notice(turns_left)
function turnBudgetNotice(turnsLeft: number): string {
  let notice = `\n\nYou have ${turnsLeft} out of ${MAX_TURNS} turns left.`;
  if (turnsLeft === 1) {
    notice += " You must call `report_helpful_ids` in the next turn.";
  }
  return notice;
}

// After appending the turn's tool messages, before the next request:
const turnsLeft = MAX_TURNS - (turn + 1);
if (turnsLeft >= 1) {
  lastToolMessage.content += turnBudgetNotice(turnsLeft);
}

See tell SID-1 how many turns remain for where this fits in the loop.

Tell SID-1 when a search is empty or fails

A search can return nothing, a backend call can time out or raise, and the model's tool cal can be misformed. Return a short natural-language note in the tool message instead of empty content or a raw exception, so SID-1 reads what happened and adjusts its next query. See handle empty results and timeouts for the messages and code.

Follow the tool call instructions closely

Define your tools entirely in the system prompt: every tool needs a corresponding description AND example there. The API tools field only switches on tool-call parsing and its contents are ignored, so a single placeholder stub is enough; the system prompt is the one place that determines which tools SID-1 can call.

Content