tung
11/13/2025, 2:12 PMfun dashboardStrategy(
userTools: UserTools,
getWidgetsTools: GetDashboardDetailTool,
dashboardTools: GetDashboardsTool
) = strategy<UserInput, String>("dashboard_strategy") {
val dashboardKey = createStorageKey<List<Dashboard>>("dashboards_list")
val setup by node<UserInput, String>("setup_strategy") { userInput ->
llm.writeSession {
appendPrompt {
system {
xml {
tag("instructions") {
+"""
Today's date is ${userInput.currentDate}.
Users can ask multiple questions in this conversation.
IMPORTANT: You MUST use tools to fetch data before responding.
After gathering all necessary data, provide a summary response.
If the user wants to exit or end the conversation (says goodbye, exit, quit, bye, etc.), call the __exit__ tool."
""".trimIndent()
}
}
}
}
}
userInput.message
}
val nodeExecuteTool by nodeExecuteTool("nodeExecuteTool")
val nodeSendToolResult by nodeLLMSendToolResult("nodeSendToolResult")
val clarifyUserQuestion by subgraphWithTask<String, ClassifiedDashboardRequest>(
tools = userTools.asTools(),
defineTask = { input ->
xml {
markdown {
h1("Requirements")
text("Ask the user what do they want to do.")
text("You should determine only GetDashboards or GetDashboardDetail.")
text("Consider the user's message. Determine whether the previous question is complete. If yes, ask the user for the next question.")
}
tag("initial_user_message") {
+input
}
}
})
val getDashboardSubGraph by subgraphWithTask<ClassifiedDashboardRequest, DashboardsResultConvert>(
tools = listOf(dashboardTools) + userTools.asTools(),
defineTask = { input ->
xml {
markdown {
h1("Requirements:")
h2("Tool usage guidelines")
bulleted {
item("Get dashboards list:") {
text("Use the get_dashboards tool to get dashboards list. This tool does not require any parameters, just trigger it.")
text("Always format the result before showing it to the user. Make it clean and user-friendly.")
}
}
h2("After fetching data, provide a clear summary of what you found.")
}
tag("user_input") {
+"""
${input.toMarkdownString()}
""".trimIndent()
}
}
})
val clarifyGetDashboardDetailRequest by subgraphWithTask<ClassifiedDashboardRequest, GetWidgetsParams>(
tools = userTools.asTools()
) { input ->
val dashboardsAsString = storage.get(dashboardKey)
xml {
tag("instructions")
markdown {
h1("Requirements:")
text("Ask the user which dashboard they want to get detailed information for. You only support dashboards that are saved in the list below.")
}
tag("dashboards") {
dashboardsAsString?.forEach { it.toMarkdownString() }
}
tag("user_message:") {
+input.toMarkdownString()
}
}
}
val getDashboardDetailSubGraph by subgraphWithTask<GetWidgetsParams, KoogGetWidgetResult>(
tools = userTools.asTools() + getWidgetsTools
) { input ->
llm.writeSession {
appendPrompt {
user {
xml {
markdown {
h1("Requirements:")
text("Get detail information for specific dashboard")
h2("Tool usage guidelines")
bulleted {
item("Get dashboards detail:") {
item("Use get_widgets tool to get dashboard detail information.")
item("You may need to call get_widgets multiple times for different dashboards.")
item("Always format the result before showing it to the user. Make it clean and user-friendly.")
item("If the widget includes data, try to analyze it and return an overview.")
item("Don't ask for user information like IDs, as users may not be familiar with IDs.")
}
}
h2("After fetching data, provide a clear summary of what you found.")
}
tag("user_input") {
+"""
${json.encodeToString(input)}
""".trimIndent()
}
}
}
}
input.toMarkdownString()
}
}
val saveDashboards by node<DashboardsResultConvert, DashboardsResultConvert> { dashboards ->
storage.set(dashboardKey, dashboards.dashboard)
dashboards
}
val showMessageToUser by node<String, Message.Response> { message ->
llm.writeSession {
appendPrompt {
user(
"Don't chat with plain text! Call `showMessage` tolls instead"
)
}
requestLLM()
}
}
nodeStart then setup then clarifyUserQuestion
edge(
edgeIntermediate = clarifyUserQuestion forwardTo getDashboardSubGraph
onCondition { it.requestType == RequestType.GetDashboards }
)
edge(
edgeIntermediate = clarifyUserQuestion forwardTo clarifyGetDashboardDetailRequest
onCondition { it.requestType == RequestType.GetDashboardDetail })
edge(
edgeIntermediate = clarifyUserQuestion forwardTo nodeExecuteTool
onToolCall { true }
)
edge(edgeIntermediate = getDashboardSubGraph forwardTo saveDashboards)
edge(
edgeIntermediate = saveDashboards forwardTo showMessageToUser
transformed { json.encodeToString(it) }
)
edge(edgeIntermediate = clarifyGetDashboardDetailRequest forwardTo getDashboardDetailSubGraph)
edge(
edgeIntermediate = getDashboardDetailSubGraph forwardTo showMessageToUser
transformed { json.encodeToString(it) }
)
edge(edgeIntermediate = showMessageToUser forwardTo showMessageToUser onAssistantMessage { true }
)
edge(edgeIntermediate = showMessageToUser forwardTo nodeExecuteTool onToolCall { true }
)
edge(edgeIntermediate = nodeExecuteTool forwardTo nodeSendToolResult)
edge(
edgeIntermediate = nodeSendToolResult forwardTo clarifyUserQuestion
transformed { it.content }
)
edge(
edgeIntermediate = nodeSendToolResult forwardTo nodeFinish
onToolCall { tc -> tc.tool == "__exit__" }
transformed { "Chat finished" }
)
}
class UserTools(private val showUserMessage: suspend (String) -> String) : ToolSet {
@Tool
@LLMDescription("Show user a message from the agent and wait for a response. Call this tool to ask the user something.")
suspend fun showMessage(
@LLMDescription("The message to show to the user.")
message: String
): String {
return showUserMessage(message)
}
}rcd27
11/13/2025, 2:54 PMrcd27
11/13/2025, 2:56 PMkoogs predefined singleRunStrategy), maybe this can improve performancetung
11/13/2025, 2:57 PMrcd27
11/13/2025, 3:06 PMrcd27
11/13/2025, 3:08 PMonToolCall { tc -> tc.tool == "__exit__"} !tung
11/13/2025, 3:09 PMrcd27
11/13/2025, 3:09 PMtung
11/13/2025, 3:14 PMrcd27
11/13/2025, 3:22 PMuser_input , system_messages and so on from "pretty formed" requirements for a particular task.rcd27
11/13/2025, 3:24 PM