Fluent DSL and Kotlin DSL for building autonomous agents with planning capabilities on the JVM, featuring annotation-based and programmatic configuration for agentic flows with Spring Boot integration
The chat and conversation interfaces enable building interactive chatbot applications with message history management, multi-turn conversations, and session handling.
Main interface for creating chat sessions.
interface Chatbot {
/** Create new chat session for user */
fun createSession(user: User): ChatSession
/** Get existing session by ID */
fun getSession(sessionId: String): ChatSession?
/** List all sessions for user */
fun listSessions(user: User): List<ChatSession>
}Basic Chatbot Example:
@Service
class CustomerSupportChatbot(
private val agentPlatform: AgentPlatform,
private val supportAgent: Agent
) {
fun startChat(user: User): ChatSession {
val chatbot = AgentProcessChatbot.builder()
.agentPlatform(agentPlatform)
.agent(supportAgent)
.user(user)
.build()
return chatbot.createSession(user)
}
}Represents an ongoing conversation session.
interface ChatSession {
/** Session identifier */
val sessionId: String
/** User participating in session */
val user: User
/** Conversation history */
val conversation: Conversation
/** Session status */
val status: SessionStatus
/** Send message and get response */
fun sendMessage(message: Message): Message
/** Send user text and get response */
fun sendUserMessage(text: String): Message
/** Close the session */
fun close()
/** Get session metadata */
fun metadata(): Map<String, Any>
}
enum class SessionStatus {
ACTIVE,
PAUSED,
CLOSED,
ERROR
}ChatSession Usage:
fun chatWithAgent(session: ChatSession, userInput: String): String {
// Send user message
val response = session.sendUserMessage(userInput)
// Return assistant response
return response.content
}
fun multiTurnConversation(session: ChatSession) {
// Turn 1
session.sendUserMessage("I need help with my order")
// Agent responds and maintains context
// Turn 2
session.sendUserMessage("The order number is #12345")
// Agent has access to previous messages
// Turn 3
val finalResponse = session.sendUserMessage("Can you expedite it?")
// Agent can reference entire conversation history
session.close()
}Manages message history.
interface Conversation {
/** All messages in conversation */
val messages: List<Message>
/** Conversation ID */
val conversationId: String
/** Add message to conversation */
fun addMessage(message: Message)
/** Add multiple messages */
fun addMessages(messages: List<Message>)
/** Get messages since timestamp */
fun getMessagesSince(timestamp: Instant): List<Message>
/** Get last N messages */
fun getLastMessages(count: Int): List<Message>
/** Clear all messages */
fun clear()
}Conversation Management:
@Service
class ConversationService {
fun getConversationHistory(conversation: Conversation): ConversationHistory {
return ConversationHistory(
id = conversation.conversationId,
messageCount = conversation.messages.size,
messages = conversation.messages.map { MessageSummary(it) },
startTime = conversation.messages.firstOrNull()?.timestamp,
lastTime = conversation.messages.lastOrNull()?.timestamp
)
}
fun addContextToConversation(
conversation: Conversation,
context: String
) {
conversation.addMessage(
Message.system("Context: $context")
)
}
fun exportConversation(conversation: Conversation): String {
return conversation.messages.joinToString("\n\n") { msg ->
"${msg.role.name}: ${msg.content}"
}
}
}Individual message in conversation.
interface Message {
/** Message role (user, assistant, system, tool) */
val role: MessageRole
/** Message content */
val content: String
/** Message timestamp */
val timestamp: Instant
/** Message ID */
val id: String
companion object {
fun user(content: String): Message
fun assistant(content: String): Message
fun system(content: String): Message
fun tool(content: String, toolCallId: String): Message
}
}
enum class MessageRole {
USER,
ASSISTANT,
SYSTEM,
TOOL
}
interface AssistantMessage : Message {
/** Tool calls made by assistant */
val toolCalls: List<ToolCall>?
/** Stop reason */
val stopReason: String?
}Message Creation:
fun createMessages(): List<Message> {
return listOf(
Message.system("You are a helpful customer service agent"),
Message.user("I have a question about my order"),
Message.assistant("I'd be happy to help with your order. What's your order number?"),
Message.user("It's #12345"),
Message.assistant("Let me look that up for you...")
)
}Part of multimodal message content.
interface ContentPart {
val type: ContentPartType
}
enum class ContentPartType {
TEXT,
IMAGE,
DOCUMENT
}
data class TextContentPart(
override val type: ContentPartType = ContentPartType.TEXT,
val text: String
) : ContentPart
data class ImageContentPart(
override val type: ContentPartType = ContentPartType.IMAGE,
val image: Image
) : ContentPartUtility for building complex messages.
object MessageBuilders {
fun user(text: String): Message
fun assistant(text: String): Message
fun system(text: String): Message
fun userWithImage(text: String, image: Image): Message
fun multimodal(role: MessageRole, parts: List<ContentPart>): Message
}MessageBuilder Example:
fun createMultimodalMessage(): Message {
val image = Image.fromUrl("https://example.com/product.jpg")
return MessageBuilders.userWithImage(
text = "What is this product?",
image = image
)
}
fun createComplexMessage(): Message {
return MessageBuilders.multimodal(
role = MessageRole.USER,
parts = listOf(
TextContentPart(text = "Analyze these images:"),
ImageContentPart(image = Image.fromUrl("url1")),
ImageContentPart(image = Image.fromUrl("url2")),
TextContentPart(text = "What are the differences?")
)
)
}Chatbot backed by agent process.
class AgentProcessChatbot private constructor(
private val agentPlatform: AgentPlatform,
private val agent: Agent,
private val defaultUser: User?
) : Chatbot {
companion object {
fun builder(): Builder
}
interface Builder {
fun agentPlatform(platform: AgentPlatform): Builder
fun agent(agent: Agent): Builder
fun user(user: User): Builder
fun outputChannel(channel: OutputChannel): Builder
fun build(): AgentProcessChatbot
}
}AgentProcessChatbot Example:
@Configuration
class ChatConfiguration {
@Bean
fun customerSupportChatbot(
agentPlatform: AgentPlatform,
supportAgent: Agent
): Chatbot {
return AgentProcessChatbot.builder()
.agentPlatform(agentPlatform)
.agent(supportAgent)
.outputChannel(loggingOutputChannel)
.build()
}
}
@Service
class ChatService(
private val chatbot: Chatbot
) {
fun startConversation(user: User): String {
val session = chatbot.createSession(user)
val response = session.sendUserMessage("Hello!")
return response.content
}
}Builder for chat agents with default configuration.
class DefaultChatAgentBuilder {
fun withName(name: String): DefaultChatAgentBuilder
fun withSystemPrompt(prompt: String): DefaultChatAgentBuilder
fun withTools(tools: List<Tool>): DefaultChatAgentBuilder
fun withModel(model: String): DefaultChatAgentBuilder
fun build(): Agent
}Chat Agent Builder Example:
fun createChatAgent(): Agent {
return DefaultChatAgentBuilder()
.withName("support-chat-agent")
.withSystemPrompt("""
You are a helpful customer support agent.
Be friendly, professional, and concise.
Always ask for order numbers when discussing orders.
""")
.withTools(listOf(
OrderLookupTool(),
ShippingTrackingTool(),
RefundProcessingTool()
))
.withModel(AnthropicModels.CLAUDE_3_5_SONNET)
.build()
}Track conversation status and metrics.
interface ConversationStatus {
val totalMessages: Int
val userMessages: Int
val assistantMessages: Int
val toolCalls: Int
val averageResponseTime: Duration
val conversationStartTime: Instant
}Handle attachments and assets in conversations.
interface Asset {
val id: String
val type: AssetType
val name: String
val url: String?
val data: ByteArray?
}
enum class AssetType {
IMAGE,
DOCUMENT,
AUDIO,
VIDEO
}
interface AssetView {
fun render(): String
}
interface AssetTracker {
fun trackAsset(asset: Asset): String
fun getAsset(id: String): Asset?
fun listAssets(): List<Asset>
}Asset Example:
@Service
class ConversationAssetService(
private val assetTracker: AssetTracker
) {
fun addImageToConversation(
session: ChatSession,
imageUrl: String
): Message {
// Track asset
val asset = Asset.fromUrl(imageUrl, AssetType.IMAGE)
val assetId = assetTracker.trackAsset(asset)
// Create message with image
val image = Image.fromUrl(imageUrl)
return session.sendMessage(
MessageBuilders.userWithImage(
text = "Please analyze this image",
image = image
)
)
}
}Format conversations for display or logging.
interface ConversationFormatter {
fun format(conversation: Conversation): String
fun formatMessage(message: Message): String
}
class WindowingConversationFormatter(
private val windowSize: Int
) : ConversationFormatter {
override fun format(conversation: Conversation): String {
val recent = conversation.getLastMessages(windowSize)
return recent.joinToString("\n") { formatMessage(it) }
}
override fun formatMessage(message: Message): String {
val timestamp = message.timestamp.toString()
return "[$timestamp] ${message.role.name}: ${message.content}"
}
}Formatter Example:
@Service
class ConversationLogger(
private val formatter: ConversationFormatter
) {
fun logConversation(conversation: Conversation) {
val formatted = formatter.format(conversation)
logger.info("Conversation:\n$formatted")
}
fun exportConversationMarkdown(conversation: Conversation): String {
return conversation.messages.joinToString("\n\n") { msg ->
when (msg.role) {
MessageRole.USER -> "**User**: ${msg.content}"
MessageRole.ASSISTANT -> "**Assistant**: ${msg.content}"
MessageRole.SYSTEM -> "*System: ${msg.content}*"
MessageRole.TOOL -> "`Tool: ${msg.content}`"
}
}
}
}Simple in-memory conversation implementation.
class InMemoryConversation : Conversation {
private val messageList = mutableListOf<Message>()
override val conversationId: String = UUID.randomUUID().toString()
override val messages: List<Message> get() = messageList.toList()
override fun addMessage(message: Message) {
messageList.add(message)
}
override fun addMessages(messages: List<Message>) {
messageList.addAll(messages)
}
override fun getMessagesSince(timestamp: Instant): List<Message> {
return messageList.filter { it.timestamp.isAfter(timestamp) }
}
override fun getLastMessages(count: Int): List<Message> {
return messageList.takeLast(count)
}
override fun clear() {
messageList.clear()
}
}Handle tool-related messages in conversations.
object ToolMessages {
fun toolCall(
toolName: String,
arguments: String,
callId: String
): Message
fun toolResult(
result: Tool.Result,
callId: String
): Message
}Define agent personas for different chat scenarios.
object Personas {
val HELPFUL_ASSISTANT: String = """
You are a helpful, friendly assistant.
Provide clear and concise responses.
"""
val TECHNICAL_SUPPORT: String = """
You are a technical support specialist.
Provide step-by-step troubleshooting guidance.
"""
val SALES_AGENT: String = """
You are a knowledgeable sales consultant.
Help customers find the right products.
"""
}interface User {
val id: String
val displayName: String
val username: String
val email: String
}
data class SimpleUser(
override val id: String,
override val displayName: String,
override val username: String,
override val email: String
) : User
interface ToolCall {
val id: String
val name: String
val arguments: String
}Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-api@0.3.0docs