State Management in Agent Loops

Track What Your Agent Knows

As an agent runs step by step, it accumulates information. State is all the information the agent needs to remember.

What Is State

State is data that persists across tool calls. Examples:

  • The original user question
  • Results from previous searches
  • Facts you have learned
  • Current step in a workflow
  • Cost so far
  • Iteration count

Why Track State

Reason 1: The model needs context When the model thinks about the next step, it needs to know what you have learned so far.

Reason 2: Detect progress If state is not changing (same search, same result), the agent is looping.

Reason 3: Limits and budgets You need to track tokens, cost, and iterations to enforce limits.

Reason 4: Debug If something goes wrong, state tells you what happened.

Simple State Example

class AgentState:
  def __init__(self, user_question):
    self.original_question = user_question
    self.sources_found = []
    self.facts_learned = {}
    self.response = ""
    self.iteration_count = 0
    self.total_cost = 0.0
  
  def add_source(self, url, content):
    self.sources_found.append(url)
    self.facts_learned[url] = content
  
  def add_to_response(self, text):
    self.response += text + " "
  
  def get_summary(self):
    return f"""
    Progress:
    - Original question: {self.original_question}
    - Sources found: {len(self.sources_found)}
    - Facts learned: {len(self.facts_learned)}
    - Iterations: {self.iteration_count}
    - Cost: ${self.total_cost:.2f}
    """

# Use the state
state = AgentState("What is renewable energy?")
state.add_source("https://example.com", "Article about renewable energy...")
print(state.get_summary())

Update State in the Agent Loop

def run_agent_with_state(user_question):
  state = AgentState(user_question)
  conversation = []
  max_iterations = 10
  max_cost = 1.0
  
  for iteration in range(max_iterations):
    state.iteration_count = iteration + 1
    
    # Ask model
    response = call_model(conversation)
    
    # Check if done
    if "<tool_use>" not in response:
      state.add_to_response(response)
      return state
    
    # Call tool
    tool_call = parse_tool_call(response)
    tool_name = tool_call["name"]
    result = call_tool(tool_name, tool_call["args"])
    cost = calculate_cost(tool_name, result)
    
    # Update state
    state.total_cost += cost
    if tool_name == "web_search":
      for item in result["results"]:
        state.sources_found.append(item["url"])
    
    # Check limits
    if state.total_cost > max_cost:
      return {"error": f"Cost limit exceeded: ${state.total_cost:.2f}"}
    
    # Add to conversation
    conversation.append({"role": "assistant", "content": response})
    conversation.append({"role": "user", "content": str(result)})
    
    print(state.get_summary())
  
  return state

State for Multi-Step Workflows

For more complex agents, track more state.

class ResearchAgentState:
  def __init__(self, topic):
    self.topic = topic
    self.status = "searching"  # searching, reading, synthesizing, done
    
    # Search phase
    self.search_queries = []
    self.search_results = []
    
    # Read phase
    self.pages_fetched = {}
    self.key_facts = []
    
    # Synthesis phase
    self.draft = ""
    self.final_answer = ""
    
    # Metadata
    self.start_time = time.time()
    self.elapsed_seconds = 0
    self.tokens_used = 0
    self.cost = 0.0
  
  def move_to_next_phase(self):
    phases = ["searching", "reading", "synthesizing", "done"]
    current_index = phases.index(self.status)
    if current_index < len(phases) - 1:
      self.status = phases[current_index + 1]
  
  def update_metadata(self, tokens, cost):
    self.tokens_used += tokens
    self.cost += cost
    self.elapsed_seconds = time.time() - self.start_time

Detect Looping With State

If state is not changing, the agent is looping.

def detect_looping(state, previous_state):
  # If no new sources were found, might be looping
  if len(state.sources_found) == len(previous_state.sources_found):
    loop_count += 1
  else:
    loop_count = 0
  
  if loop_count >= 3:
    return True  # Looping detected
  
  return False

# In the agent loop
previous_state = None
for iteration in range(max_iterations):
  # ... run agent step ...
  
  if detect_looping(state, previous_state):
    print("Agent is looping, breaking out")
    break
  
  previous_state = state.copy()

State Checkpoints

Save state at key points so you can resume.

import json

def save_state(state, filename):
  data = {
    "original_question": state.original_question,
    "sources_found": state.sources_found,
    "facts_learned": state.facts_learned,
    "iteration_count": state.iteration_count,
    "total_cost": state.total_cost,
  }
  with open(filename, "w") as f:
    json.dump(data, f)

def load_state(filename):
  with open(filename, "r") as f:
    data = json.load(f)
  state = AgentState(data["original_question"])
  state.sources_found = data["sources_found"]
  state.facts_learned = data["facts_learned"]
  state.iteration_count = data["iteration_count"]
  state.total_cost = data["total_cost"]
  return state

# Use checkpoints
if checkpoint_file_exists:
  state = load_state(checkpoint_file)
else:
  state = AgentState(user_question)

# ... run agent ...

save_state(state, "agent_state.json")

State Checklist

  • Define what data you need to track
  • Create a state object or dict
  • Update state after every tool call
  • Check state for progress (detecting loops)
  • Use state to enforce limits
  • Log state for debugging
  • Save state at checkpoints for resumption

Discussion

  • Loading…

← Back to Tutorials