API / continue-agentic-tools-across-turns

API

Continue agentic tool chats across turns

Continue agentic tool chats across turns

When Grok runs web search, X search, or other server-side tools, follow-up prompts need the prior reasoning, tool calls, and tool results — plain chat history is not enough. Official Advanced Usage docs give two paths: store the agentic state on xAI (store_messages=True + previous_response_id), or keep encrypted agentic content on the client (use_encrypted_content=True). Plain Responses chaining without tools is covered in Chain Responses with previous_response_id. Mixed client/server tools: Mix client-side and server-side tools on Grok.

Advanced agentic patterns are not supported in the Vercel AI SDK yet; use the xAI SDK or OpenAI SDK.

Store on the server

  1. First turn: store_messages=True so the service keeps reasoning, server-side tool calls, and tool results.
  2. Later turn: previous_response_id=response.id from the turn you want to resume.

The follow-up may change tools, model, or other settings — the agentic state still rehydrates from the stored response.

import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, x_search

client = Client(api_key=os.getenv("XAI_API_KEY"))

chat = client.chat.create(
    model="grok-4.6",
    tools=[web_search(), x_search()],
    store_messages=True,
)
chat.append(user("What is xAI?"))
for response, chunk in chat.stream():
    if chunk.content:
        print(chunk.content, end="", flush=True)
print("\nUsage:", response.server_side_tool_usage)

chat = client.chat.create(
    model="grok-4.6",
    tools=[web_search(), x_search()],
    previous_response_id=response.id,
)
chat.append(user("What is its latest mission?"))
for response, chunk in chat.stream():
    if chunk.content:
        print(chunk.content, end="", flush=True)
print("\nUsage:", response.server_side_tool_usage)

Stored responses follow the same retention window as other Responses API history (see chain guide). Past that window, use the encrypted path below.

Keep encrypted state on the client (ZDR-friendly)

  1. First turn: use_encrypted_content=True so the reply includes encrypted reasoning and encrypted tool output with the final content.
  2. Before the next sample/stream: chat.append(response), then append the new user turn.
chat = client.chat.create(
    model="grok-4.6",
    tools=[web_search(), x_search()],
    use_encrypted_content=True,
)
chat.append(user("What is xAI?"))
for response, chunk in chat.stream():
    if chunk.content:
        print(chunk.content, end="", flush=True)

chat.append(response)
chat.append(user("What is its latest mission?"))
for response, chunk in chat.stream():
    if chunk.content:
        print(chunk.content, end="", flush=True)

Treat the encrypted blobs as opaque. For Responses API include: ["reasoning.encrypted_content"] without tools, see Use encrypted reasoning content.

Pitfalls

  • Resending only user/assistant text drops tool transcripts — follow-ups then “forget” what search already found.
  • previous_response_id needs a stored prior response; ZDR / store: false teams should use use_encrypted_content instead.
  • Client-side tool pauses reset max_turns on the next request — see the mix-tools how-to.