--- title: Filtering Input/Output --- The input and output of Agents can be filtered/transformed using the `filterInput` and `filterOutput` blocks respectively. **FilterInput** The filterInput blocks have access to the following fields: | Name | | | | |--------------|---------------------|--------------------------------------------------------|----------| | input | Conversation | The entire conversation. | writable | | inputMessage | ConversationMessage | The latest message of the conversation. | writable | | message | String | The content of the latest message of the conversation. | writable | **FilterOutput** The filterInput blocks have access to the following fields: | Name | | | | |---------------|---------------------|----------------------------------------------|----------| | input | Conversation | The conversation that was provided as input. | readonly | | output | Conversation | The conversation plus the output message. | writable | | outputMessage | ConversationMessage | The message generated by the Agent. | writable | | message | String | The content of message generated by the Agent. | writable | ### Overriding In/Output Filters can be used change the input and output of the conversation. Simply assign a new value to one of the message fields, i.e. `message`, `inputMessage`, `outputMessage`, `input`, or `output`. ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { message = "This is the input message $message" } } ``` ### The breakWith function The `breakWith` function can be used to stop the agent from further processing the message and a return a dedicated response. This is similar to the throwing an exception with the difference that the Agent does not fail, but instead returns the provided message. ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { val eval = llm(""" Evaluate if the following input is referring to bananas. If so return BANANAS otherwise return CONTINUE. Input: $message""").getOrNull() if ("BANANAS" == eval?.content) { breakWith("I cannot answer questions about bananas...") } } } ``` ### Running Filters in Parallel The `runAsync` function can be used to run one or more filters in parallel. ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { runAsync { +MyFilter() } runAsync { +MySecondFilter() } } } ``` **Caution**: The `runAsync` function should only be used with filters that do not try to modify the Conversation as this will have unpredictable results. ### Replacing values Within the filter blocks the keyword `replaces` can be used to replace occurrences of values with new values. Example ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { "A transformed question" replaces "A question" "A new message replacing the old" replaces ".*".toRegex() } } ``` ### Removing values Within the filter blocks the unaryMinus on String and Regex `-` can be used to remove values. Example ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { -"Bad Word" -"Malware.*".toRegex() } } ``` ### Custom Filter classes Sometimes filter logic can be complex, or you may wish to reuse filters. In these cases, the `AgentFilter` interface can be used. ```kt fun interface AgentFilter { /** * Filters or transform Conversation Messages. * If the fun returns null, the message will be removed from the conversation transcript. */ suspend fun filter(message: ConversationMessage): ConversationMessage? } ``` Any `AgentFilter` implementation on the classpath, can be added to the `filterInput` and `filterOutput` blocks using the UnaryPlus `+` operator. For example, adding a custom filter class `MyFilter` ```kts agent { name = "weather" description = "Agent that provides weather data." prompt { """ Some system prompt """ } filterInput { +MyFilter::class // context is used to lookup instance of MyFilter +MyFilter() // MyFilter is directly instantiated } } ``` Filters can be directly instantiated in the filter blocks, or if the filter class is provided, then a matching bean will be retrieved from the context, for example, the Spring Boot container.