diff --git a/arc-assistants/src/main/kotlin/extensions/UseCasePromptProvider.kt b/arc-assistants/src/main/kotlin/extensions/UseCasePromptProvider.kt new file mode 100644 index 00000000..ad0e5449 --- /dev/null +++ b/arc-assistants/src/main/kotlin/extensions/UseCasePromptProvider.kt @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2025 Deutsche Telekom AG and others +// +// SPDX-License-Identifier: Apache-2.0 + +package org.eclipse.lmos.arc.assistants.support.extensions + +import org.eclipse.lmos.arc.agents.dsl.DSLContext + +/** + * Builds the system prompt for [org.eclipse.lmos.arc.assistants.support.filters.UseCaseMatcher]. + * + * Register an implementation as a Spring bean (or via [org.eclipse.lmos.arc.agents.dsl.BeanProvider]) + * to override the default classification prompt at runtime. + */ +fun interface UseCasePromptProvider { + /** + * @param useCases Formatted use case definitions passed to the matcher LLM call. + * @param context Current DSL context (e.g. for loading external prompt files). + */ + suspend fun buildSystemPrompt(useCases: String, context: DSLContext): String +} diff --git a/arc-assistants/src/main/kotlin/filters/UseCaseMatcher.kt b/arc-assistants/src/main/kotlin/filters/UseCaseMatcher.kt index f9b2e2a5..dfeddb73 100644 --- a/arc-assistants/src/main/kotlin/filters/UseCaseMatcher.kt +++ b/arc-assistants/src/main/kotlin/filters/UseCaseMatcher.kt @@ -6,6 +6,8 @@ package org.eclipse.lmos.arc.assistants.support.filters import org.eclipse.lmos.arc.agents.dsl.DSLContext import org.eclipse.lmos.arc.agents.dsl.extensions.llm +import org.eclipse.lmos.arc.agents.dsl.getOptional +import org.eclipse.lmos.arc.assistants.support.extensions.UseCasePromptProvider import org.eclipse.lmos.arc.core.getOrNull import org.eclipse.lmos.arc.core.onFailure import org.slf4j.LoggerFactory @@ -14,101 +16,16 @@ class UseCaseMatcher(private val model: String? = null) { private val log = LoggerFactory.getLogger(this.javaClass) - private fun system(useCases: String) = """ - You are a classification system. - - Your task is to match an assistant message to the most appropriate use case, - based on the provided use case definitions and their solutions. - - ## Rules - Each use case includes a Solution, describing the expected assistant behavior. - Match based on semantic meaning, not exact wording. - Prefer the use case whose solution best aligns with the intent of the assistant message. - If no use case clearly matches, return . - Output must be only one label in angle brackets. - - ---- - - ## Examples - - ### Example 1 - UseCases - UseCase: password_reset - Solution - Ask the customer to restart their router to reset their router. - - #### UseCase: password_forgotton - Solution - Ask the customer to send an email to customer support. - - Assistant - Please restart your router. - - Output: - - ### Example 2 - UseCases - UseCase: billing_issue - Solution - Ask the customer to check their invoice or contact billing support. - - UseCase: technical_issue - Solution - Guide the customer through troubleshooting steps. - - Assistant - Please review your invoice or reach out to billing support. - - Output: - - ### Example 3 - UseCases - UseCase: account_locked - Solution - Tell the customer to wait 15 minutes before trying again. - - UseCase: password_reset - Solution - Ask the customer to reset their password using the reset link. - - Assistant - You can reset your password using the link we sent you. - - Output: - - ### Example 4 (No Match) - UseCases - UseCase: shipping_delay - Solution - Inform the customer about delayed delivery. - - UseCase: refund_request - Solution - Guide the customer through requesting a refund. - - Assistant - Our office hours are Monday to Friday, 9am–5pm. - - Output: - - ---- - - Now classify: - - #UseCases - $useCases - - - Output: (only the label in angle brackets) - """ - suspend fun matchUseCase( message: String, useCases: String, context: DSLContext ): String? { + val systemPrompt = context.getOptional() + ?.buildSystemPrompt(useCases, context) + ?: UseCaseMatcherPrompts.build(useCases) val result = context.llm( - system = system(useCases), + system = systemPrompt, user = "Assistant Message: $message", model = model ).onFailure { @@ -122,4 +39,4 @@ class UseCaseMatcher(private val model: String? = null) { log.info("Matched UseCase found: $result") return useCase } -} \ No newline at end of file +} diff --git a/arc-assistants/src/main/kotlin/filters/UseCaseMatcherPrompts.kt b/arc-assistants/src/main/kotlin/filters/UseCaseMatcherPrompts.kt new file mode 100644 index 00000000..ba4c86ed --- /dev/null +++ b/arc-assistants/src/main/kotlin/filters/UseCaseMatcherPrompts.kt @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2025 Deutsche Telekom AG and others +// +// SPDX-License-Identifier: Apache-2.0 + +package org.eclipse.lmos.arc.assistants.support.filters + +/** + * Default system prompt template for [UseCaseMatcher]. + * Replace `{useCases}` with the formatted use case definitions at runtime. + */ +object UseCaseMatcherPrompts { + const val TEMPLATE = """ + You are a classification system. + + Your task is to match an assistant message to the most appropriate use case, + based on the provided use case definitions and their solutions. + + ## Rules + Each use case includes a Solution, describing the expected assistant behavior. + Match based on semantic meaning, not exact wording. + Prefer the use case whose solution best aligns with the intent of the assistant message. + If no use case clearly matches, return . + Output must be only one label in angle brackets. + + ---- + + ## Examples + + ### Example 1 + UseCases + UseCase: password_reset + Solution + Ask the customer to restart their router to reset their router. + + #### UseCase: password_forgotton + Solution + Ask the customer to send an email to customer support. + + Assistant + Please restart your router. + + Output: + + ### Example 2 + UseCases + UseCase: billing_issue + Solution + Ask the customer to check their invoice or contact billing support. + + UseCase: technical_issue + Solution + Guide the customer through troubleshooting steps. + + Assistant + Please review your invoice or reach out to billing support. + + Output: + + ### Example 3 + UseCases + UseCase: account_locked + Solution + Tell the customer to wait 15 minutes before trying again. + + UseCase: password_reset + Solution + Ask the customer to reset their password using the reset link. + + Assistant + You can reset your password using the link we sent you. + + Output: + + ### Example 4 (No Match) + UseCases + UseCase: shipping_delay + Solution + Inform the customer about delayed delivery. + + UseCase: refund_request + Solution + Guide the customer through requesting a refund. + + Assistant + Our office hours are Monday to Friday, 9am–5pm. + + Output: + + ---- + + Now classify: + + #UseCases + {useCases} + + + Output: (only the label in angle brackets) + """ + + fun build(useCases: String): String = TEMPLATE.replace("{useCases}", useCases) +}