Session Loop Tool Calls#

After you have a Session and sandbox, the agent loop turns model tool calls into real tool execution. run_session_loop(...) appends an assistant row, executes tools, writes tool_result, and feeds the result into the next model request.

Coverage#

Topic

Result

Request assembly

The agent Session comes first, followed by the user Session.

Tool schema

Built-in and custom tools are merged into the schema visible to the model.

Tool round

The assistant emits tool calls, then the loop executes tools and appends tool_result.

Output Session

The output Session inherits user-side rows and appends new rows.

Sandbox sharing

The input Session’s sandbox handle is shared with the output Session through refcounting.

Step 1: Prepare Agent and User Sessions#

from rath import flow
from rath.session import Session, run_session_loop

agent_session = Session.from_agent_prompt(
    "Use tools when the user asks for file work."
)
user_session = Session.from_user_message(
    "Create a file, then read it back."
).to("local")
provider = flow.Provider(api_key="sk-...", model="gpt-5.5")

Key lines:

Line

Explanation

Session.from_agent_prompt(...)

Creates a system chunk for the agent-side Session.

Session.from_user_message(...)

Creates a user chunk for the user-side Session.

.to("local")

Binds the user Session to the local Backend so tools have an execution location.

flow.Provider(...)

Stores the model configuration for this request.

Step 2: Run the Loop#

In real runs, run_session_loop(...) uses the default executor to issue an OpenAI-compatible request. Tests and tutorials can pass a scripted executor to make model responses deterministic.

out = run_session_loop(
    user_session=user_session,
    agent_session=agent_session,
    agent_provider=provider,
    executor=scripted_executor,
)

The executor is responsible for two operations:

Method

Purpose

complete(req)

Returns one chat completion response.

dispatch_tool(session, tool, arguments)

Executes the selected FlowToolCall.

When no executor is passed, OpenRath creates DefaultSessionLoopExecutor and uses the default OpenAI-compatible client. In that mode, configure the model gateway through process environment variables or your deployment secret store.

Step 3: Understand One Tool Round#

When the model returns a tool call, the loop appends an assistant row, executes the tool, then appends a tool result row:

user
assistant       contains tool_calls
tool_result     contains serialized tool output
assistant       final answer or next tool_calls

This ordering comes from run_session_loop(...):

Stage

What happens

Completion

The model returns an assistant message from the messages and tools.

Assistant row

If there is a tool call, the assistant row is added to rows_list first.

Dispatch

The loop finds the matching FlowToolCall and passes parsed arguments to it.

Tool result row

The tool result is serialized as JSON text and written to a tool_result chunk.

Next completion

The next request includes the earlier assistant row and tool result row.

Step 4: Inspect the Chunk Table#

for row in out.chunk_table.rows:
    print(row.kind, row.payload)

If the model writes a file and then reads it back, the typical order is:

user
assistant       # write_workspace_file call
tool_result     # bytes_written
assistant       # run_shell_command call
tool_result     # command stdout / stderr
assistant       # final answer

Observed behavior:

  • The name in the tool_result row matches the called tool name.

  • The content in the tool_result row is the tool output visible to the next model round.

  • The output Session still starts with user-side content; the agent system prompt is not copied into it.

Step 5: Confirm Sandbox Sharing#

sandbox = out.require_sandbox()
print(sandbox.backend.name)

run_session_loop(...) shares the input user Session’s sandbox handle with the output Session (refcount +1). If another agent or Workflow runs later, tools can still use the same sandbox. Either Session can call close_sandbox() independently; the backend closes only after the last reference is released.

Troubleshooting#

Symptom

Check

Model did not call a tool

Check whether the system prompt, user prompt, and tool schema are explicit enough.

unknown_tool

The tool name returned by the model is not in the tool table.

invalid_tool_arguments

The model returned JSON arguments that could not be parsed.

Tool execution failed

Check the message and detail in tool_execution_exception.

Tool cannot find a sandbox

Confirm the user Session has already called .to("local") or .bind_sandbox(...).

Exercises#

  1. Change the user prompt to “Answer only; do not call tools” and observe whether tool_result still appears in the chunk table.

  2. Pass a custom tool into run_session_loop(...) and confirm the tool table includes it.

  3. Run run_session_loop(...) twice in sequence and confirm the second agent can see the tool_result left by the first agent.

Summary#

  • run_session_loop(...) assembles agent/user messages and starts a completion.

  • Built-in tools come from global_system_tools(), currently including run_shell_command and write_workspace_file.

  • Tool results are serialized into tool_result chunks for the next LLM request.

  • The output Session is the core object for continuing work in later Workflows.