Skip to main content

Overview

AIAgentEnvironment provides a controlled mechanism for AI agents to interface with external tools and report problems. It ensures that tool execution happens within a safe, monitored context.
Tools should only be executed through the environment interface, not called directly. Direct tool calls bypass feature pipelines, event handlers, and testing infrastructure.

Interface Definition

public interface AIAgentEnvironment

Methods

executeTool

Executes a single tool call and returns its result.
public suspend fun executeTool(
    toolCall: Message.Tool.Call
): ReceivedToolResult
toolCall
Message.Tool.Call
required
A tool call message containing:
  • Tool identifier
  • Tool name
  • Request content (arguments)
  • Associated metadata
return
ReceivedToolResult
The result of executing the tool call, including:
  • Tool name
  • Tool identifier
  • Response content
  • Associated metadata

executeTools

Executes multiple tool calls in parallel and processes their results.
public suspend fun executeTools(
    toolCalls: List<Message.Tool.Call>
): List<ReceivedToolResult>
toolCalls
List<Message.Tool.Call>
required
A list of tool call messages to be executed concurrently
return
List<ReceivedToolResult>
A list of results corresponding to the executed tool calls, in the same order
The default implementation uses supervisorScope to execute tools in parallel with proper error handling. Individual tool failures are isolated and don’t affect other tool executions.

reportProblem

Reports a problem that occurred within the environment.
public suspend fun reportProblem(
    exception: Throwable
)
exception
Throwable
required
The exception representing the problem to report. This is used to handle errors encountered during agent execution.
Problem reporting is integrated with the agent’s feature pipeline, allowing features to intercept and handle errors.

Usage Examples

Executing a Tool in a Strategy

class MyStrategy : AIAgentGraphStrategy<String, String> {
    override val name = "my-strategy"
    
    override suspend fun execute(
        context: AIAgentGraphContext,
        input: String
    ): String? {
        // Get LLM to decide which tools to use
        val response = context.llm.prompt {
            user(input)
        }.execute()
        
        // Execute tools through the environment
        if (response is Message.Assistant.ToolCall) {
            val toolCall = response.toolCalls.first()
            
            // Use environment to execute the tool
            val result = context.environment.executeTool(toolCall)
            
            return "Tool executed: ${result.content}"
        }
        
        return response.content
    }
}

Batch Tool Execution

suspend fun processBatchTools(
    environment: AIAgentEnvironment,
    toolCalls: List<Message.Tool.Call>
) {
    // Execute all tools in parallel
    val results = environment.executeTools(toolCalls)
    
    // Process results
    results.forEach { result ->
        println("Tool ${result.toolName}: ${result.content}")
    }
}

Error Handling with reportProblem

class SafeStrategy : AIAgentGraphStrategy<String, String> {
    override val name = "safe-strategy"
    
    override suspend fun execute(
        context: AIAgentGraphContext,
        input: String
    ): String? {
        return try {
            val response = context.llm.prompt {
                user(input)
            }.execute()
            
            response.content
        } catch (e: Exception) {
            // Report the problem through the environment
            context.environment.reportProblem(e)
            
            // Return fallback response
            "An error occurred: ${e.message}"
        }
    }
}

Conditional Tool Execution

suspend fun executeToolsConditionally(
    environment: AIAgentEnvironment,
    response: Message.Assistant
) {
    when (response) {
        is Message.Assistant.ToolCall -> {
            // Execute all requested tools
            val results = environment.executeTools(response.toolCalls)
            
            // Send results back to LLM
            processToolResults(results)
        }
        is Message.Assistant.Text -> {
            // No tools needed, use text response
            println(response.content)
        }
    }
}

Tool Execution Flow

Environment Context

The environment is available through the agent context:
// In a strategy
override suspend fun execute(
    context: AIAgentGraphContext,
    input: String
): String? {
    // Access environment from context
    val environment = context.environment
    
    // Execute tools safely
    val result = environment.executeTool(toolCall)
    
    return result.content
}

SafeTool Wrapper

For type-safe tool access, use the SafeTool wrapper:
// Get a safe tool wrapper
val safeTool = context.environment.findTool(MyTool::class)

// Execute with typed arguments
val result = safeTool.execute(MyTool.Args(
    query = "search query"
))

Advanced: Custom Environment

You can implement custom environments for testing or special behavior:
class LoggingEnvironment(
    private val delegate: AIAgentEnvironment
) : AIAgentEnvironment {
    override suspend fun executeTool(
        toolCall: Message.Tool.Call
    ): ReceivedToolResult {
        println("Executing tool: ${toolCall.name}")
        
        val result = delegate.executeTool(toolCall)
        
        println("Tool result: ${result.content}")
        return result
    }
    
    override suspend fun executeTools(
        toolCalls: List<Message.Tool.Call>
    ): List<ReceivedToolResult> {
        println("Executing ${toolCalls.size} tools in parallel")
        return delegate.executeTools(toolCalls)
    }
    
    override suspend fun reportProblem(exception: Throwable) {
        println("Problem reported: ${exception.message}")
        delegate.reportProblem(exception)
    }
}

Best Practices

Tool Execution
  • Always use the environment interface for tool execution
  • Never call tool.execute() directly in agent code
  • Use executeTools() for parallel execution when possible
  • Handle tool results appropriately in your strategy
Error Handling
  • Use reportProblem() to report errors to the agent pipeline
  • Don’t swallow exceptions without reporting them
  • Features and event handlers depend on proper error reporting
  • Consider retry logic for transient tool failures

Testing

Mock environments for testing:
val mockEnvironment = object : AIAgentEnvironment {
    override suspend fun executeTool(
        toolCall: Message.Tool.Call
    ): ReceivedToolResult {
        return ReceivedToolResult(
            toolName = toolCall.name,
            content = "mocked result"
        )
    }
    
    override suspend fun reportProblem(exception: Throwable) {
        // Log for test verification
        println("Test: Problem reported: ${exception.message}")
    }
}
  • AIAgent - Uses environment for tool execution
  • AIAgentStrategy - Accesses environment through context
  • Tool - Executed through the environment
  • ToolRegistry - Tools available in the environment

Source Reference

Defined in: agents-core/src/commonMain/kotlin/ai/koog/agents/core/environment/AIAgentEnvironment.kt

Build docs developers (and LLMs) love