Agent loop

Framework for building autonomous AI agents that iteratively solve tasks using tool calling. Provides unified interface for OpenAI, Claude, and custom backends.

Key Features:

  • Unified API for OpenAI and Claude

  • Type-safe tool definitions

  • Type-safe structured output (optionally)

  • Full execution history tracking

  • Support for Identity, cats-effect, ZIO, and other effect systems

  • Easy custom backend implementation

Quick Start

//> using dep com.softwaremill.sttp.ai::openai:0.5.1

import sttp.ai.core.agent.*
import sttp.ai.openai.OpenAI
import sttp.ai.openai.agent.OpenAIAgent
import sttp.client4.DefaultSyncBackend
import sttp.tapir.Schema

object BasicExample extends App {
  case class WeatherInput(location: String) derives io.circe.Codec.AsObject, Schema

  val weatherTool = AgentTool.fromFunction(
    "get_weather",
    "Get the current weather for a location"
  ) { (input: WeatherInput) =>
    s"The weather in ${input.location} is 22°C, sunny"
  }

  val backend = DefaultSyncBackend()
  try {
    val agent = OpenAIAgent
      .synchronous(OpenAI.fromEnv, "gpt-4o-mini")
      .maxIterations(5)
      .tools(weatherTool)
      .build

    val result = agent.run("What's the weather in Paris?")(backend)

    println(s"Answer: ${result.finalAnswer}")
    println(s"Iterations: ${result.iterations}")
  } finally backend.close()
}

For Claude: Use ClaudeAgent.synchronous(ClaudeConfig.fromEnv, "claude-3-haiku-20240307") instead.

For effect systems: use OpenAIAgent.builder[F] / ClaudeAgent.builder[F] (e.g. builder[IO]), then add configuration and .build.