Updated README

This commit is contained in:
extio-de
2025-06-28 17:26:38 +02:00
parent 9ca590a8d4
commit fda6971a46

179
README.md
View File

@@ -171,9 +171,186 @@ Streaming is also supported.
See class AgentTest for examples.
# Agent implementations
## Documentation for `BaseAgent` Interface Methods
This document details the public methods of the `BaseAgent` interface, providing developers with information on their purpose and usage within agentic LLM flows. These methods allow customization of agent behavior, pre/post processing of contexts, merging of results, and choosing the next agent in a flow.
**1. `String name()`**
* **Purpose:** Returns a unique string identifier for this agent. This name is used to reference the agent within an agent flow and for logging/debugging purposes.
* **Usage:** Implementations *must* override this method to return a meaningful name. This name should be consistent across all instances of the same agent type.
* **Returns:** A `String` representing the agent's name.
* **Throws:** `UnsupportedOperationException` if not implemented.
**2. `AgentType agentType()`**
* **Purpose:** Defines the core functionality or role of this agent within the overall flow. The `AgentType` enum (not shown in provided code but implied) likely defines categories like "completion", "processing", "start conversation" etc., influencing how the agent interacts with the LLM client and other agents.
* **Usage:** Implementations *must* override this method to return the appropriate `AgentType`. This is crucial for controlling the execution logic within the `execute()` method.
* **Returns:** An `AgentType` enum value.
* **Throws:** `UnsupportedOperationException` if not implemented.
**3. `ModelCategory modelCategory()`**
* **Purpose:** Specifies the desired size/capability category of the language model to use when interacting with the LLM client. This allows for cost optimization or performance tuning by selecting models appropriate for the task.
* **Usage:** Implementations can optionally override this method to customize the model category. The default implementation returns `ModelCategory.MEDIUM`.
* **Returns:** A `ModelCategory` enum value.
* **Default:** `ModelCategory.MEDIUM`
**4. `String systemPrompt()`**
* **Purpose:** Provides a system-level instruction that sets the context and behavior for the LLM. This prompt is prepended to user input when creating a conversation.
* **Usage:** Implementations can optionally override this method to provide a custom system prompt tailored to their specific task. If no prompt is needed, it should return `null`.
* **Returns:** A `String` containing the system prompt.
* **Default:** `null`
**5. `String textTemplate()`**
* **Purpose:** Defines a template string for generating input text for the LLM. The template can include placeholders (e.g., `{{key}}`) which are dynamically replaced with values from the `AgentContext`. `[[key]]` style placeholders are a convenient way to branch out / parallelize execution flows. See also the section about text template placeholders below.
* **Usage:** Implementations can optionally override this method to define a template. This is useful for constructing prompts based on data in the agent context.
* **Returns:** A `String` containing the text template.
* **Default:** `null`
**6. `AgentResponseHandler responseHandler()`**
* **Purpose:** Provides a mechanism for processing the raw LLM response. Different handlers can parse responses into structured data, extract specific information, or perform validation.
* **Usage:** Implementations can optionally override this method to return a custom `AgentResponseHandler`. The default implementation returns a `TextAgentResponseHandler` configured to handle responses under the "response" key.
* **Returns:** An `AgentResponseHandler` instance.
* **Default:** `new TextAgentResponseHandler("response")`
**7. `void preProcess(final AgentContext context)`**
* **Purpose:** Allows performing actions *before* the main logic of the agent is executed. This could involve modifying the `AgentContext`, preparing data, or setting up necessary resources.
* **Usage:** Implementations can optionally override this method to add pre-processing steps.
* **Parameters:** `context`: The `AgentContext` object representing the current state of the agent flow.
**8. `void postProcess(final AgentContext context)`**
* **Purpose:** Allows performing actions *after* the main logic of the agent is executed. This could involve cleaning up resources, updating the `AgentContext`, or logging results.
* **Usage:** Implementations can optionally override this method to add post-processing steps.
* **Parameters:** `context`: The `AgentContext` object representing the current state of the agent flow.
**9. `List<AgentContext> merge(final List<AgentContext> contexts)`**
* **Purpose:** Combines multiple `AgentContext` objects into a collection of potentially new `AgentContext` objects. This method provides flexible context aggregation, supporting scenarios beyond simply reducing many contexts to one. It allows for one-to-one, one-to-many, many-to-one, and many-to-many merging strategies. The merge() step is executed after all branch executions (depth 1) and their postProcess() steps have been completed.
* **Usage:** Implementations can optionally override this method to define a custom merging strategy tailored to their specific needs. The default implementation returns `null`, indicating no merging is performed. A non-null return value signifies the merged contexts.
* **Parameters:** `contexts`: A list of `AgentContext` objects to merge.
* **Returns:** A `List<AgentContext>` containing the resulting merged contexts. The size of the returned list can vary depending on the implemented merging logic:
* **One-to-One:** If each input context results in exactly one output context, the returned list will have the same size as the input list.
* **Many-to-One:** If multiple input contexts are combined into a single output context, the returned list will typically contain only one element.
* **One-to-Many:** If a single input context generates multiple output contexts, the returned list will be larger than the input list.
* **Many-to-Many:** The returned list’s size may not directly correlate with the input list’s size, reflecting a more complex merging relationship.
* **Default:** `null` (no merging occurs).
**Important Considerations:**
The flexibility of the `merge()` method allows developers to implement sophisticated context aggregation strategies based on the requirements of their agentic flow. For example, you could use it to:
* Combine data from parallel executions (depth 1).
* Split a single context into multiple contexts based on certain criteria.
* Filter or transform contexts during the merging process.
* Create new contexts based on relationships between existing ones.
**10. `AgentNext chooseNextAgent(final AgentContext context)`**
* **Purpose:** Determines which agent should be executed next in the flow based on the current `AgentContext`.
* **Usage:** Implementations *must* override this method to implement the logic for selecting the next agent. It should return an `AgentNext` enum value indicating either the name of the next agent or `AgentNext.END` to signal the end of the flow.
* **Parameters:** `context`: The `AgentContext` object representing the current state of the agent flow.
* **Returns:** An `AgentNext` enum value.
* **Throws:** `UnsupportedOperationException` if not implemented.
## Important Considerations for Overall Agent Flow & AgentContext Flags
Beyond the individual methods, several parameters and flags within the `AgentContext` significantly influence the behavior of the overall agent flow. Developers should understand these to fine-tune their agents effectively. Here's a breakdown:
**1. `skipNextCompletion` Flag (Boolean):**
* **Purpose:** Controls whether the LLM client (`client.conversation()` or `client.streamConversation()`) is called for the next turn in the conversation. This is useful for agents that perform processing *without* requiring an LLM completion.
* **Usage:** Set this flag to `true` within an agent's `preProcess()` or `postProcess()` method if the agent only needs to manipulate the context and doesn’t require generating text from the LLM. It's automatically reset after the completion attempt (or skip) within the `execute()` method.
* **Impact:** Reduces API calls and processing time when an LLM interaction isn't necessary.
**2. `isStreaming` Flag (Boolean):**
* **Purpose:** Enables or disables streaming responses from the LLM. Streaming provides incremental results as they become available, improving perceived responsiveness.
* **Usage:** Set this flag in the `AgentContext` before calling `execute()`. Agents can also dynamically adjust this flag based on conditions during execution.
* **Impact:** Affects how the response is handled by the `responseHandler()`. If `true`, a `StreamedAgentResponseHandler` is expected to handle chunks of data instead of a complete response. You can register a callback handler (Consumer) when calling AgentExecutorService.execute() to receive streamed responses.
**3. `conversation` (Conversation Object):**
* **Purpose:** Stores the ongoing conversation history with the LLM. This is crucial for maintaining context across multiple turns.
* **Usage:** Agents should update the `conversation` object with each turn. The `setupConversation()` method handles initial creation and potential merging of system prompts.
* **Impact:** Directly influences the LLM's behavior by providing conversational context.
**4. `requestStatistic` (Object tracking request details - implementation not shown):**
* **Purpose:** Collects statistics about API requests made by the agent.
* **Usage:** The `execute()` method adds each `Completion` to this statistic tracker.
* **Impact:** Helps monitor cost, performance, and identify potential issues with API calls.
**5. `error` Flag (Boolean):**
* **Purpose:** Indicates whether an error occurred during the agent’s execution.
* **Usage:** Set to `true` within `postProcess()` or during exception handling in `execute()`.
* **Impact:** Used by the `execute()` method to filter out erroneous results and prevent further processing of failed agents.
**6. `graph` (Object representing the execution graph - implementation not shown):**
* **Purpose:** A visual representation of the agent flow, showing the path taken through different agents and any branching that occurred.
* **Usage:** Agents add nodes to the graph using methods like `add()`, indicating their execution and status.
* **Impact:** Valuable for debugging, understanding complex flows, and visualizing the decision-making process.
**7. Custom Context Keys:**
* **Purpose:** The `AgentContext` allows storing arbitrary data in its `context` map (a `HashMap`).
* **Usage:** Developers can add custom keys and values to the context to share information between agents or track application-specific state. This is heavily leveraged by the template system.
* **Impact:** Enables highly flexible communication and data sharing within the agentic flow.
## Text Template Placeholders
**1. `{{{key}}}` - JSON String Replacement**
* **Purpose:** This placeholder type expects the value associated with the given `key` in the `AgentContext` to be a valid JSON-serializable object (e.g., a string, number, boolean, list, or map). The entire object will be converted into a JSON string using an `ObjectMapper` and inserted into the template.
* **How it works:** The code iterates through the entries in the `AgentContext`'s context map (`context.getContext()`). If it finds a key matching the placeholder (e.g., `{{{myObject}}}`), it attempts to serialize the corresponding value into a JSON string using `objectMapper.writeValueAsString(entry.getValue())`.
* **Example:**
* If `context.getContext().get("myObject")` returns `[1, 2, 3]`, then `{{{myObject}}}` would be replaced with `"[1, 2, 3]"`.
* If `context.getContext().get("userName")` returns `"Alice"`, then `{{{userName}}}` would be replaced with `"Alice"`.
**2. `{{key}}` - Text / List-to-String Conversion**
* **Purpose:** This placeholder type expects the value associated with the given `key` in the `AgentContext` to be a *List* of objects. It joins the elements of this list into a single string, separated by newline characters (`\n\n`).
* **How it works:** The code checks if `context.getContext().get(key)` returns a List. If so, it uses `entry.getValue().stream().map(Object::toString).collect(Collectors.joining("\n\n"))` to convert each element of the list to its string representation and concatenate them with double newlines.
* **Example:**
* If `context.getContext().get("item")` returns `["apple"]`, then `{{items}}` would be replaced with:
```
apple
```
* If `context.getContext().get("names")` returns `[1, 2, 3]`, then `{{names}}` would be replaced with:
```
1
2
3
```
**3. `[[key]]` - Branched Template Expansion (Advanced)**
* **Purpose:** This placeholder enables branching or looping within the template based on the size of a list stored in the `AgentContext`. It's used to create multiple variations of the template, one for each element in the list.
* **How it works:**
* The code identifies all keys using the `[[key]]` pattern.
* It determines the minimum size of all lists associated with these branched keys. This minimum size dictates how many "splits" are created.
* For each index from 0 up to the minimum size, it creates a new `Split` object. Each split has:
* A modified version of the template where `[[key]]` is replaced with the element at that index from each corresponding list.
* A new `AgentContext` containing only the selected elements from the branched lists.
* **Example:**
* If `context.getContext().get("[[cities]]")` returns `["London", "Paris", "Tokyo"]` and `context.getContext().get("[[countries]]")` returns `["UK", "France", "Japan"]`, then two splits will be created (because the minimum size is 2).
* **Split 1:** `text` will have `[[cities]]` replaced with `"London"` and `[[countries]]` replaced with `"UK"`. The context will contain only `{"cities": ["London"], "countries": ["UK"]}`.
* **Split 2:** `text` will have `[[cities]]` replaced with `"Paris"` and `[[countries]]` replaced with `"France"`. The context will contain only `{"cities": ["Paris"], "countries": ["France"]}`.
**In Summary:**
| Placeholder | Value Type Expected | Replacement Behavior |
|-------------|---------------------|-----------------------------------------------------------|
| `{{{key}}}` | Any JSON-serializable | Serializes the entire value to a JSON string. |
| `{{key}}}` | List | Joins list elements into a string with newlines. |
| `[[key]]` | List | Creates multiple template variations based on list size. |
# Announcement generated by AgentTest#agenticFlow