Agent Types

Cognotik is designed around a strongly typed, object-oriented approach to LLM interaction. At the core is the abstract BaseAgent<I, R>, where I is the Input type and R is the Result type.

1. Core Abstraction: BaseAgent

BaseAgent<I, R> (Abstract)

File: BaseAgent.kt

All agents inherit from this class. It standardizes how inputs are converted into chat messages and how responses are returned.

Generic Types:

Key Properties:

Key Methods:

2. Text & Conversational Agents

ChatAgent (Stable)

The standard agent for conversational text generation. It takes a history of strings and returns a raw string response.

It constructs a chat history starting with the system prompt, followed by each string in the input list as a separate user message.

val agent = ChatAgent(
    prompt = "You are a helpful assistant.",
    model = myChatModel,
    temperature = 0.7
)
val response = agent.respond(listOf("Hello", "Tell me a joke"))

Best For: Chatbots, summarization, creative writing, and general Q&A.

3. Structured Data Agents (JSON/POJO)

These agents are designed to force the LLM to output structured data (JSON) which is then automatically deserialized into Kotlin/Java objects.

ParsedAgent<T>

Converts natural language input into a specific class instance (T).

Key Features:

Best For: Data extraction, converting unstructured text to structured data, API payload generation.

ParsedResponse<T>

File: ParsedResponse.kt

A wrapper holding both the raw text and the deserialized obj.

val response: ParsedResponse<User> = agent.respond(listOf("Extract user info"))
val transformed: ParsedResponse<UserDTO> = response.map(UserDTO::class.java) { user ->
    UserDTO(user.name.uppercase(), user.email)
}

ParsedImageAgent<T>

Similar to ParsedAgent, but accepts images as input. It performs Visual Question Answering (VQA) where the answer is a structured object.

Best For: Extracting data from invoices, describing UI elements in JSON, categorizing visual content, OCR with structured output.

ProxyAgent<T> (Advanced)

File: ProxyAgent.kt

Note: Does not inherit BaseAgent.

This is a "Magic" agent. It creates a dynamic Java Proxy for a given interface or class. When you call a method on the proxy, the arguments are serialized, sent to the LLM, and the LLM "executes" the logic, returning the result.

interface SentimentAnalyzer {
    fun analyze(text: String): SentimentResult
}
val proxy = ProxyAgent(SentimentAnalyzer::class.java, model).create()
val result = proxy.analyze("I love this library!") // LLM determines the return value

Best For: Rapid prototyping, implementing complex logic without writing code, semantic routing, simulating service behavior.

Schema Best Practices

To ensure reliable parsing and validation with ParsedAgent and ParsedImageAgent, follow these guidelines when defining your data classes:

Validation Tip: Do not be too strict. Use validation to canonicalize data (e.g., fixing formatting) rather than just rejecting it.

4. Action & Code Agents

CodeAgent (Core)

An autonomous agent capable of writing, executing, and fixing code in a sandboxed runtime environment.

Input: CodeRequest

Output: CodeResult

Key Components:

Self-Correction Loop: If autoEvaluate is true, the agent executes the code. If it throws an exception, the agent feeds the error back to the LLM to generate a fix (up to fixIterations times). If the code still fails, it retries the entire process (up to fixRetries times).

val agent = CodeAgent(
    codeRuntime = KotlinScriptRuntime(),
    symbols = mapOf("api" to myApiClient),
    model = myChatModel,
    temperature = 0.1
)
val result = agent.respond(CodeAgent.CodeRequest(
    messages = listOf("Calculate the sum of 1 to 100" to Role.user),
    autoEvaluate = true,
    fixIterations = 3
))
println(result.code)
println(result.result.resultOutput)

Best For: Data analysis, complex math, controlling external APIs via script, tasks requiring iterative logic, automation.

5. Media Agents

ImageAndText

File: ImageAndText.kt

A simple data class that pairs text with an optional image.

Best For: Passing multimodal data to agents that accept both text and images.

ImageGenerationAgent

Generates images from text descriptions.

Key Components:

Workflow:

  1. Refinement: Uses the text LLM to transform the user request into an optimized image generation prompt.
  2. Length Validation: If the prompt exceeds the model's maxPrompt limit, it's automatically shortened.
  3. Generation: Sends the refined prompt to the ImageClientInterface.
  4. Decoding: Handles both URL-based and Base64-encoded image responses.

Best For: Creating assets, visualizing concepts, generating illustrations.

ImageProcessingAgent

Handles Vision tasks. It can analyze images or (depending on the backend model) edit them.

Best For: Image captioning, visual analysis, describing scenes, OCR, image editing (with capable models).

Summary Table

Agent Class Input Type Output Type Primary Use Case
ChatAgent List<String> String Conversation, Q&A
ParsedAgent<T> List<String> ParsedResponse<T> Text-to-Object, Data Extraction
CodeAgent CodeRequest CodeResult Writing & Executing Code, Tool Use
ImageGenerationAgent List<String> ImageAndText Creating Images from text
ImageProcessingAgent List<ImageAndText> ImageAndText Analyzing/Captioning Images
ParsedImageAgent<T> List<ImageAndText> ParsedResponse<T> Image-to-Object (Visual Data Extraction)
ProxyAgent<T> Method Args Method Return Implementing Interfaces via LLM

Advanced Topics

Code Interception in CodeAgent

The codeInterceptor function allows you to transform code before execution. Useful for:

val agent = CodeAgent(
    codeRuntime = runtime,
    model = model,
    codeInterceptor = { code ->
        "println(\"Executing code...\")\n$code\nprintln(\"Code executed.\")"
    }
)

Fallback Models in CodeAgent

If the primary model fails to generate valid code after all retries, the fallbackModel is used. Useful for:

val agent = CodeAgent(
    codeRuntime = runtime,
    model = cheaperModel,
    fallbackModel = moreCapableModel,
    temperature = 0.1
)

Example-Based Learning in ProxyAgent

Improve ProxyAgent accuracy by providing examples:

val agent = ProxyAgent(MyInterface::class.java, model)

// Add examples
agent.addExample(SentimentResult(score = 0.9, label = "positive")) { proxy ->
    proxy.analyze("I love this!")
}

agent.addExample(SentimentResult(score = 0.1, label = "negative")) { proxy ->
    proxy.analyze("This is terrible.")
}

val finalProxy = agent.create()