Based Mdels

Agent Loop docs

Conversations and transcripts

A Transcript holds a conversation as provider-independent messages. Use converse() when you want to keep those messages and continue later. With OpenAI configured as in Hello World:

import os

from agentloop import Agent, Transcript, final_text

transcript = Transcript()
transcript.add_user_message("Explain recursion in one sentence.")

with Agent.from_env(os.environ, tools=[]) as agent:
    agent.converse(transcript)

print(final_text(transcript))
print(f"messages: {len(transcript.messages)}")
transcript.check_invariants()

Output:

Recursion is a technique where a function solves a problem by calling a smaller version of itself until it reaches a simple base case that it can solve directly.
messages: 2

Messages contain typed parts such as text, model thinking, tool calls, and tool results. Agent.converse() runs registered tools and adds their results to the transcript for you.

Add tool results yourself

Call add_tool_results() when you execute a pending tool call outside the agent loop or construct a transcript manually. Results may arrive out of order; the method places them in the model's original call order:

from agentloop import Transcript
from agentloop.transcript import ToolCallPart, ToolResultPart

transcript = Transcript()
transcript.add_user_message("Look up A and B.")
transcript.add_assistant_message([
    ToolCallPart(id="call-a", tool_name="lookup", arguments={"key": "A"}),
    ToolCallPart(id="call-b", tool_name="lookup", arguments={"key": "B"}),
])
transcript.add_tool_results([
    ToolResultPart(call_id="call-b", tool_name="lookup", content="Result B"),
    ToolResultPart(call_id="call-a", tool_name="lookup", content="Result A"),
])

print([part.call_id for part in transcript.messages[-1].content])
transcript.check_invariants()

Output:

['call-a', 'call-b']

Every pending call needs exactly one result. A missing, unexpected, or duplicate call ID raises TranscriptError before an invalid result message is added.

Check transcript invariants

check_invariants() verifies that tool-call IDs are unique, results immediately follow their calls, every call has one result, and no calls remain unanswered. It returns None when the transcript is safe to send and raises TranscriptError otherwise:

from agentloop import Transcript
from agentloop.errors import TranscriptError
from agentloop.transcript import ToolCallPart, ToolResultPart

transcript = Transcript()
transcript.add_user_message("Look up A.")
transcript.add_assistant_message([
    ToolCallPart(id="call-a", tool_name="lookup", arguments={"key": "A"}),
])

try:
    transcript.check_invariants()
except TranscriptError as error:
    print(error)

transcript.add_tool_results([
    ToolResultPart(call_id="call-a", tool_name="lookup", content="Result A"),
])
transcript.check_invariants()
print("transcript is sendable")

Output:

message 1 ends with unanswered tool calls ['call-a']; the transcript is not sendable
transcript is sendable

Call this check before sending a transcript assembled or loaded by your application. Deserialization does not run it automatically: a saved transcript may legitimately end mid-turn, before a tool has returned. Complete pending calls with add_tool_results() and then check again.

See transcript storage and the transcript API.