fun dashboardStrategy( userTools: UserTools, getWidgetsTools: GetDashboardDetailTool, dashboardTools: GetDashboardsTool ) = strategy("dashboard_strategy") { val dashboardKey = createStorageKey>("dashboards_list") val setup by node("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( tools = userTools.asTools(), llmModel = GoogleModels.Gemini2_0Flash, 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( tools = listOf(dashboardTools) + userTools.asTools(), llmModel = GoogleModels.Gemini2_0Flash, 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( tools = userTools.asTools(), llmModel = GoogleModels.Gemini2_0Flash, ) { 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.") text("Don't ask the user for user information like IDs, dashboard_id,..., as users may not be familiar with IDs.") text("Your input should be dashboard name, and output should be dashboardId") text("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( tools = userTools.asTools() + getWidgetsTools, llmModel = GoogleModels.Gemini2_0Flash, ) { 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("Don't ask for user information like IDs, dashboard_id,..., as users may not be familiar with IDs.") } } h2("Result Requirements:") text("Always format the result before showing it to the user. Make it clean and user-friendly.") text("When the widget contains data as timestamp and value, you should present the result clearly,example 'day : value'") text("When the widget contains storage data, convert the values to human-readable units (MB, GB, KB, etc.).") text("Provide a detail of what you found.") } tag("user_input") { +""" ${json.encodeToString(input)} """.trimIndent() } } } } input.toMarkdownString() } } val saveDashboards by node { dashboards -> storage.set(dashboardKey, dashboards.dashboard) dashboards } val verifyDashboardResult by subgraphWithVerification( tools = userTools.asTools(), llmModel = GoogleModels.Gemini2_0Flash, ) { dashboards -> """ Verify that: 1. The result should be formatted in a user-friendly way. 2. If there is an error, it should be clear and easy to understand. """.trimIndent() } val showMessageToUser by node { message -> llm.writeSession { appendPrompt { user( "Don't chat with plain text! Call showMessage tolls instead" ) } requestLLM() } } val adjustDashboardResponse by subgraphWithTask( tools = userTools.asTools(), llmModel = GoogleModels.Gemini2_0Flash ) { feedback -> "You must fix the following problems: $feedback" } 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 verifyDashboardResult) edge( edgeIntermediate = verifyDashboardResult forwardTo saveDashboards onCondition { it.successful } transformed { it.input } ) edge( edgeIntermediate = verifyDashboardResult forwardTo adjustDashboardResponse onCondition { !it.successful } transformed { it.feedback } ) edge(edgeIntermediate = adjustDashboardResponse forwardTo verifyDashboardResult) //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" } ) }