Amazon Bedrock AgentCore
Beta
This feature is part of a beta module (1.2.0-beta). The API may change in future releases.
See module versioning for details.
Koog provides integrations for running agents with Amazon Bedrock AgentCore services.
Amazon Bedrock AgentCore Runtime
The koog-bedrock-agentcore-runtime module provides a Ktor route installer that exposes a Koog agent through the
Amazon Bedrock AgentCore Runtime HTTP
contract. It installs the following endpoints relative to the Ktor route on which it is configured:
POST /invocationsprocesses agent requests.GET /pingreports agent health and background activity.
The module supports typed JSON handlers as well as text, binary, multipart, and streaming payloads. Invocation handlers
run in a Ktor RoutingContext, so they can use Koog routing extensions such as aiAgent() when the koog-ktor plugin
is installed.
Add the dependency
Add the AgentCore Runtime module to your Gradle build:
The module requires JVM 17 or later, Kotlin 2.x, and Ktor 3.x.
Install the Runtime routes
The following example installs Koog and Ktor content negotiation, then exposes a typed JSON invocation handler:
import ai.koog.agentcore.runtime.agentCoreRuntime
import ai.koog.agentcore.runtime.handle
import ai.koog.ktor.Koog
import ai.koog.ktor.aiAgent
import ai.koog.ktor.llm
import ai.koog.prompt.executor.clients.bedrock.BedrockModels
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
import kotlinx.serialization.Serializable
@Serializable
data class InvocationRequest(val prompt: String)
@Serializable
data class InvocationResponse(val answer: String)
fun Application.module() {
install(ContentNegotiation) {
json()
}
install(Koog) {
llm {
bedrock()
}
}
routing {
agentCoreRuntime {
handle<InvocationRequest, InvocationResponse> { request, context ->
val sessionId = context.getHeader("X-Amzn-Bedrock-AgentCore-Runtime-Session-Id")
val answer = aiAgent(
input = request.prompt,
model = BedrockModels.AmazonNovaMicro,
)
InvocationResponse(answer)
}
}
}
}
Typed handlers delegate request deserialization and response serialization to Ktor's ContentNegotiation plugin. The
host application must install a converter for the media types it accepts, such as json() for JSON requests and
responses. The server engine, port, and other application plugins also remain under the host application's control.
Handle different payload types
For non-JSON payloads or multimodal responses, configure the unified handler. It receives an InvocationInput and an
AgentCoreContext, and returns an InvocationResult:
routing {
agentCoreRuntime {
handler = { input, context ->
when (input) {
is InvocationInput.Text -> InvocationResult.Text(
aiAgent(input.body, model = BedrockModels.AmazonNovaMicro)
)
is InvocationInput.Binary -> InvocationResult.Binary(input.bytes, input.contentType)
is InvocationInput.Stream -> InvocationResult.Text("Received a streamed request")
is InvocationInput.Multipart -> InvocationResult.Text("Received multipart data")
}
}
}
}
The unified handler supports:
InvocationResult.Textfor one-shot text output withAccept-based content negotiation.InvocationResult.Binaryfor raw image, audio, video, or document bytes with an explicit content type.InvocationResult.TextStreamfor aFlow<String>emitted as immediately flushedtext/event-streamevents.InvocationResult.BinaryStreamfor raw streamed chunks with a caller-selected content type.
Streaming responses are written directly and do not require Ktor's SSE plugin.
Configure request handling
AgentCoreRuntimeConfig provides the following options:
| Option | Description | Default |
|---|---|---|
handler |
Unified handler used unless a typed handle<I, O> handler is registered. |
Not set |
binaryStreamThresholdBytes |
Binary bodies above this size, or without Content-Length, are exposed as InvocationInput.Stream. |
1 MiB |
maxRequestBytes |
Rejects requests with a declared Content-Length above the limit with HTTP 413. |
100 MiB |
handlerTimeoutMillis |
Returns HTTP 504 when the handler exceeds this timeout. A non-positive value disables the timeout. | 0 |
pingService |
Custom health service for the /ping endpoint. |
Task-aware default service |
taskTracker |
Tracker exposed through AgentCoreContext and used by the default health service. |
New AgentCoreTaskTracker |
Requests without a Content-Length header are not pre-checked against maxRequestBytes; the underlying server engine's
limits still apply.
Monitor health and background tasks
The /ping endpoint returns:
Healthywith HTTP 200 when the agent has no active background tasks.HealthyBusywith HTTP 200 while theAgentCoreTaskTrackerreports active work.Unhealthywith HTTP 503 when the health check detects a problem.
Use the tracker available from AgentCoreContext when starting long-running background work. This keeps the Runtime
informed that the agent is still active. You can replace the default behavior by assigning a custom
AgentCorePingService to pingService.
Rate limiting is also controlled by the host application. Install Ktor's RateLimit plugin globally or wrap the
agentCoreRuntime route in a named rateLimit block to apply the desired policy.
Amazon Bedrock AgentCore Memory
Koog integrates with Amazon Bedrock AgentCore Memory in two ways:
- The
agents-features-chat-history-awsmodule persists conversational history as AgentCore events. - The
agents-features-longterm-memory-awsmodule retrieves records produced by AgentCore memory strategies and adds them to the agent prompt.
Both integrations require JVM 17 or later and an AgentCore memory resource. Configure AWS credentials and a region through the standard AWS SDK credential and region provider chains.
Add the dependencies
Add one or both Memory integration modules to your Gradle build:
dependencies {
implementation("ai.koog:agents-features-chat-history-aws:$koogVersion")
implementation("ai.koog:agents-features-longterm-memory-aws:$koogVersion")
}
Both modules expose the AWS SDK for Kotlin BedrockAgentCoreClient used by their public APIs. Long-term memory also
exposes BedrockAgentCoreControlClient for memory strategy discovery.
Persist conversational history
AgentcoreChatHistoryProvider implements Koog's ChatHistoryProvider with the AgentCore createEvent and
listEvents APIs. Install it through the ChatMemory feature:
import ai.koog.agents.chatMemory.feature.ChatMemory
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.features.chathistory.aws.AgentcoreChatHistoryProvider
import aws.sdk.kotlin.services.bedrockagentcore.BedrockAgentCoreClient
val agentCoreClient = BedrockAgentCoreClient { region = "us-west-2" }
val chatHistoryProvider = AgentcoreChatHistoryProvider(
client = agentCoreClient,
memoryId = "memory-id",
)
val agent = AIAgent(/* ... */) {
install(ChatMemory) {
chatHistoryProvider = chatHistoryProvider
}
}
val result = agent.run(
agentInput = "Remember that I prefer window seats.",
conversationId = "user-123:trip-456",
)
A conversation ID can be either actorId:sessionId or just actorId. When the session part is omitted, the provider
uses default-session unless you set defaultSession in its constructor.
The provider stores plain-text Message.User and Message.Assistant messages. Messages loaded from AgentCore carry
their event ID in metadata, allowing the provider to store only new messages when the full history is saved again.
System, tool, reasoning, and non-text content is skipped by default; set ignoreUnsupportedValues = false to reject
it instead. Use pageSize to control listEvents pagination and totalEventsLimit to cap the number of loaded events.
Retrieve long-term memory
LongTermMemory can query one or more AgentCore memory strategies before each LLM request. The agentcore DSL creates
a composite retrieval, so a single block can combine multiple strategy types and namespace scopes:
import ai.koog.agents.features.longtermmemory.aws.dsl.agentcore
import ai.koog.agents.longtermmemory.feature.LongTermMemory
val agent = AIAgent(/* ... */) {
install(LongTermMemory) {
retrieval {
agentcore(agentCoreClient, memoryId = "memory-id") {
semantic(
strategyId = "semantic-strategy-id",
actorId = "user-123",
topK = 5,
)
userPreferences(
strategyId = "preference-strategy-id",
actorId = "user-123",
limit = 20,
)
summary(
strategyId = "summary-strategy-id",
actorId = "user-123",
sessionId = "trip-456",
topK = 3,
)
}
}
}
}
The DSL provides the following helpers:
| Helper | AgentCore strategy | Namespace scope | Retrieval |
|---|---|---|---|
semantic |
Semantic memory | Actor | Similarity search |
userPreferences |
User preferences | Actor | Record listing |
summary |
Summarization | Actor and session | Similarity search |
episodes |
Episodic episodes | Actor and session | Similarity search |
reflections |
Episodic reflections | Actor | Similarity search |
episodic |
Episodes and reflections | Both scopes | Composite similarity search |
By default, namespaces follow AWS's documented layout:
/strategies/{strategyId}/actors/{actorId}/ for actor-scoped memory and
/strategies/{strategyId}/actors/{actorId}/sessions/{sessionId}/ for session-scoped memory. If the memory resource
uses custom namespace templates, assign an AgentcoreNamespaceResolver in the agentcore block.
The default AgentcorePromptAugmenter places semantic, preference, episode, and reflection records in the system
message. Summary records are appended to the latest user message. Set augmenter in the block to use another Koog
PromptAugmenter.
Discover configured memory strategies
When strategy IDs or namespace templates should not be hard-coded, use AgentcoreStrategyDiscovery with an AWS
BedrockAgentCoreControlClient, then pass its result to agentcoreDiscovered. The discovery DSL configures all
supported strategies returned for the memory resource and lets you override retrieval limits, scores, filters, and
namespace patterns or exclude individual strategies. A sessionId is required when the discovered set includes a
summary or episodic strategy.
AgentCore creates long-term records asynchronously from stored events. An event written by ChatMemory might therefore
not be available to LongTermMemory immediately.