From 195d2daa59293461b9cdab14b230d29bee90aefa Mon Sep 17 00:00:00 2001 From: Mahesh Kommareddi Date: Tue, 16 Jul 2024 21:36:12 -0400 Subject: [PATCH] Fixed LLM prompt and team parsing --- src/main/java/com/ioa/IoASystem.java | 108 +++++++++++++----- src/main/java/com/ioa/team/TeamFormation.java | 67 +++++++---- target/classes/com/ioa/IoASystem.class | Bin 5555 -> 9248 bytes .../classes/com/ioa/team/TeamFormation.class | Bin 5416 -> 6565 bytes 4 files changed, 126 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/ioa/IoASystem.java b/src/main/java/com/ioa/IoASystem.java index 86b64de..3ec8824 100644 --- a/src/main/java/com/ioa/IoASystem.java +++ b/src/main/java/com/ioa/IoASystem.java @@ -67,39 +67,89 @@ public class IoASystem { TeamFormation teamFormation = context.getBean(TeamFormation.class); TaskManager taskManager = context.getBean(TaskManager.class); - // Register some example agents - AgentInfo agent1 = new AgentInfo("agent1", "General Assistant", + // Register all agents + agentRegistry.registerAgent("agent1", new AgentInfo("agent1", "General Assistant", Arrays.asList("general", "search"), - Arrays.asList("webSearch", "getWeather", "setReminder")); - AgentInfo agent2 = new AgentInfo("agent2", "Travel Expert", + Arrays.asList("webSearch", "getWeather", "setReminder"))); + agentRegistry.registerAgent("agent2", new AgentInfo("agent2", "Travel Expert", Arrays.asList("travel", "booking"), - Arrays.asList("bookTravel", "calculateDistance", "findRestaurants")); + Arrays.asList("bookTravel", "calculateDistance", "findRestaurants"))); + agentRegistry.registerAgent("agent3", new AgentInfo("agent3", "Event Planner Extraordinaire", + Arrays.asList("event planning", "team management", "booking"), + Arrays.asList("findRestaurants", "bookTravel", "scheduleAppointment", "getWeather"))); + agentRegistry.registerAgent("agent4", new AgentInfo("agent4", "Fitness Guru", + Arrays.asList("health", "nutrition", "motivation"), + Arrays.asList("findFitnessClasses", "getRecipe", "setReminder", "getWeather"))); + agentRegistry.registerAgent("agent5", new AgentInfo("agent5", "Research Specialist", + Arrays.asList("research", "writing", "analysis"), + Arrays.asList("webSearch", "getNewsUpdates", "translate", "compareProductPrices"))); + agentRegistry.registerAgent("agent6", new AgentInfo("agent6", "Digital Marketing Expert", + Arrays.asList("marketing", "social media", "content creation"), + Arrays.asList("webSearch", "getNewsUpdates", "scheduleAppointment", "getMovieRecommendations"))); + agentRegistry.registerAgent("agent7", new AgentInfo("agent7", "Family Travel Coordinator", + Arrays.asList("travel", "family planning", "budgeting"), + Arrays.asList("bookTravel", "calculateDistance", "getWeather", "findRestaurants", "getFinancialAdvice"))); + + // Create all tasks + List tasks = Arrays.asList( + new Task("task1", "Plan a weekend trip to Paris", + Arrays.asList("travel", "booking"), + Arrays.asList("bookTravel", "findRestaurants", "getWeather")), + new Task("task2", "Organize a corporate team-building event in New York", + Arrays.asList("event planning", "team management"), + Arrays.asList("findRestaurants", "bookTravel", "scheduleAppointment")), + new Task("task3", "Develop a personalized fitness and nutrition plan", + Arrays.asList("health", "nutrition"), + Arrays.asList("getWeather", "findFitnessClasses", "getRecipe")), + new Task("task4", "Research and summarize recent advancements in renewable energy", + Arrays.asList("research", "writing"), + Arrays.asList("webSearch", "getNewsUpdates", "translate")), + new Task("task5", "Plan and execute a social media marketing campaign for a new product launch", + Arrays.asList("marketing", "social media"), + Arrays.asList("webSearch", "getNewsUpdates", "scheduleAppointment")), + new Task("task6", "Assist in planning a multi-city European vacation for a family of four", + Arrays.asList("travel", "family planning"), + Arrays.asList("bookTravel", "calculateDistance", "getWeather", "findRestaurants")), + + new Task("task7", "Organize an international tech conference with virtual and in-person components", + Arrays.asList("event planning", "tech expertise", "marketing", "travel coordination", "content creation"), + Arrays.asList("scheduleAppointment", "webSearch", "bookTravel", "getWeather", "findRestaurants", "getNewsUpdates")), - agentRegistry.registerAgent(agent1.getId(), agent1); - agentRegistry.registerAgent(agent2.getId(), agent2); + new Task("task8", "Develop and launch a multi-lingual mobile app for sustainable tourism", + Arrays.asList("software development", "travel", "language expertise", "environmental science", "user experience design"), + Arrays.asList("webSearch", "translate", "getWeather", "findRestaurants", "getNewsUpdates", "compareProductPrices")), + + new Task("task9", "Create a comprehensive health and wellness program for a large corporation, including mental health support", + Arrays.asList("health", "nutrition", "psychology", "corporate wellness", "data analysis"), + Arrays.asList("findFitnessClasses", "getRecipe", "setReminder", "getWeather", "scheduleAppointment", "getFinancialAdvice")), + + new Task("task10", "Plan and execute a global product launch campaign for a revolutionary eco-friendly technology", + Arrays.asList("marketing", "environmental science", "international business", "public relations", "social media"), + Arrays.asList("webSearch", "getNewsUpdates", "scheduleAppointment", "translate", "compareProductPrices", "bookTravel")), + + new Task("task11", "Design and implement a smart city initiative focusing on transportation, energy, and public safety", + Arrays.asList("urban planning", "environmental science", "data analysis", "public policy", "technology integration"), + Arrays.asList("webSearch", "getWeather", "calculateDistance", "getNewsUpdates", "getFinancialAdvice", "findHomeServices")) + + ); - // Create a sample task - Task task = new Task("task1", "Plan a weekend trip to Paris", - Arrays.asList("travel", "booking"), - Arrays.asList("bookTravel", "findRestaurants", "getWeather")); - - // Form a team for the task - List team = teamFormation.formTeam(task); - System.out.println("Formed team: " + team); - - if (team.isEmpty()) { - System.out.println("No suitable agents found for the task. Exiting."); - return; + for (Task task : tasks) { + System.out.println("\nProcessing task: " + task.getDescription()); + List team = teamFormation.formTeam(task); + System.out.println("Formed team: " + team); + + if (!team.isEmpty()) { + // Assign the task to all team members + for (AgentInfo agent : team) { + Task agentTask = new Task(task.getId() + "_" + agent.getId(), task.getDescription(), task.getRequiredCapabilities(), task.getRequiredTools()); + agentTask.setAssignedAgent(agent); + taskManager.addTask(agentTask); + taskManager.executeTask(agentTask.getId()); + System.out.println("Task result for " + agent.getId() + ": " + agentTask.getResult()); + } + } else { + System.out.println("No suitable agents found for this task. Consider updating the agent pool or revising the task requirements."); + } } - - // Assign the task to the first agent in the team - task.setAssignedAgent(team.get(0)); - - // Execute the task - taskManager.addTask(task); - taskManager.executeTask(task.getId()); - - // Print the result - System.out.println("Task result: " + task.getResult()); } } \ No newline at end of file diff --git a/src/main/java/com/ioa/team/TeamFormation.java b/src/main/java/com/ioa/team/TeamFormation.java index c60a8dd..19b30d4 100644 --- a/src/main/java/com/ioa/team/TeamFormation.java +++ b/src/main/java/com/ioa/team/TeamFormation.java @@ -7,6 +7,7 @@ import com.ioa.task.Task; import org.springframework.stereotype.Component; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -25,38 +26,64 @@ public class TeamFormation { List requiredTools = task.getRequiredTools(); List potentialAgents = agentRegistry.searchAgents(requiredCapabilities); - System.out.println("Potential agents: " + potentialAgents); + System.out.println("DEBUG: Potential agents: " + potentialAgents); - String teamFormationTask = "Form the best team for this task: " + task.getDescription() + - "\nRequired capabilities: " + requiredCapabilities + - "\nRequired tools: " + requiredTools + - "\nAvailable agents and their tools: " + formatAgentTools(potentialAgents) + - "\nPlease respond with a comma-separated list of agent IDs that form the best team for this task."; - - System.out.println("Sending prompt to language model: " + teamFormationTask); + String initialPrompt = "Task: " + task.getDescription() + "\n" + + "Required capabilities: " + String.join(", ", requiredCapabilities) + "\n" + + "Required tools: " + String.join(", ", requiredTools) + "\n" + + "Available agents:\n" + formatAgentDetails(potentialAgents) + "\n" + + "Instructions: Analyze the task requirements and the available agents. " + + "Form the best team by selecting agents whose combined capabilities and tools meet the task requirements. " + + "Consider the following steps:\n" + + "1. Identify which capabilities and tools are crucial for the task.\n" + + "2. Match these requirements with the available agents.\n" + + "3. Consider how agents can complement each other's skills and tools.\n" + + "4. Aim to cover all required capabilities and tools with the smallest effective team.\n" + + "5. If a perfect match isn't possible, prioritize the most important requirements.\n" + + "Provide your reasoning for each step, then conclude with a final team selection in this format: 'Selected Team: agent1, agent2, ...'"; - String response = model.generate(teamFormationTask, null); - - System.out.println("Language model response: " + response); + System.out.println("DEBUG: Sending initial prompt to language model: " + initialPrompt); - return parseTeamComposition(response, potentialAgents); + String reasoning = model.generate(initialPrompt, null); + System.out.println("DEBUG: Language model reasoning:\n" + reasoning); + + return parseTeamComposition(reasoning, potentialAgents); } - private String formatAgentTools(List agents) { - return agents.stream() - .map(agent -> agent.getId() + " (capabilities: " + agent.getCapabilities() + ", tools: " + agent.getTools() + ")") - .collect(Collectors.joining(", ")); + private String formatAgentDetails(List agents) { + StringBuilder sb = new StringBuilder(); + for (AgentInfo agent : agents) { + sb.append(agent.getId()).append(":\n") + .append(" Capabilities: ").append(String.join(", ", agent.getCapabilities())).append("\n") + .append(" Tools: ").append(String.join(", ", agent.getTools())).append("\n\n"); + } + return sb.toString(); } - private List parseTeamComposition(String composition, List potentialAgents) { - List selectedIds = Arrays.asList(composition.split(",")); - System.out.println("Parsed agent IDs: " + selectedIds); + private List parseTeamComposition(String reasoning, List potentialAgents) { + // Extract the team selection from the reasoning + String[] lines = reasoning.split("\n"); + String selectedTeamLine = ""; + for (String line : lines) { + if (line.startsWith("Selected Team:")) { + selectedTeamLine = line.substring("Selected Team:".length()).trim(); + break; + } + } + + if (selectedTeamLine.isEmpty()) { + System.out.println("DEBUG: No team selection found in the response."); + return Collections.emptyList(); + } + + List selectedIds = Arrays.asList(selectedTeamLine.split(",\\s*")); + System.out.println("DEBUG: Parsed agent IDs: " + selectedIds); List team = potentialAgents.stream() .filter(agent -> selectedIds.contains(agent.getId().trim())) .collect(Collectors.toList()); - System.out.println("Final team: " + team); + System.out.println("DEBUG: Final team: " + team); return team; } } \ No newline at end of file diff --git a/target/classes/com/ioa/IoASystem.class b/target/classes/com/ioa/IoASystem.class index 9d479b3a42ada15b980467c35ab46d6b47ecc981..525f66ab205e17b38dcd5dc34c6e6f5b128bf334 100644 GIT binary patch literal 9248 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 S`3OIW`7Gw1;%E3dYX2XIwofSl delta 1771 zcmaJ>c~n$Y82{bb-kZn6k?{aNP#Lw(0UVs8qI5=&ia{x2E>JdR9){sDjtm3BgOHYK zwJa;`+P-ME*kUE3AmBoKQdwD+t)`ako0X^2AK7=`fQS5}b7t=Q?(crz@B4kV;+Me7}jIP1b))CpOx_4_Jwg7)dTQE8Os@x z%Nv5tdel?gQm;1{GFH&CVL~#Z3{quXT}>nwmJwr6#3bP_qn;rp6pV(NqCrD1iZmL* zSV))Az~HKm#HtqQgfuk-V@9Kb5*R9)Fp8zX9@dRHRSL%93KcC_#nT;yRtZ<~EN7Z} zHG|yezGyHO z_RX(cp@)pA3R>}miYKv!k4%{?;b|4m;8`9>Id@7c3cZ7*Fo(7YbFqZ>ADEJXSN%&bb?-!mt{GbIV z`O)Dy7W~0gZTbxCpkwAE4UE3_+)l7IGA!6hqr(K#(LuJFa6l(^(Tt%B-Q=}{uhABh zGe4${37dYoupt+z=)rD^2QKW7kTHW|_E6+d6ksp*QFJe@XTo|KBS9y8ngmILBH=|w zT1Ni`J6tMKGQ5h{%#<}5T6~bcY%^~fEdkE{Y{E)p`!B`octcQ-XFjY%2`D|t z4!Egq?>3qZt~+l~-AX_209$fG(SG!qiiT!p3|2JJjPy_j;yN+4&r?(f?8-2O7oD?A08?q@lS7tpWE{?rZ*+ zX{BIVmSDQpAD8kpsRP&f z>A>TfO~gKx_-yUKbAp4AZQW=$ImGp_nH{T!M)pidn1x|DA6Y1ZmkxY<7G~p8%s~ac zkIk5iwJ4<*>mqE#e0nDqQKm9{h;n>^#W+Epr?7;M;$?J)mog_Rn1&GZpo--p%=}nx zZpqR_tMLI2QBsNJ6T?Te5yM!vx#t#Su@Tsh!?4ETWiA}R$E33YpJE<9p#!x6TTo4w zgVJ73=x3z+1TMzsG?#INNW~RTAYT&FOUOB3r%91h!chsu62?mKNywK_V*XksYNhDL lF&Zs6-hae$7``IlYx)l%_+ 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 delta 2128 zcmZ`)`Fj&Z7=9h$eO7zOjj@#GZ>co z`LPTiZ?bymDb2Q^T*hovJ1|E<4Qjb;HSF~YE{sG%1*m1)SM(d975M zXT?GVmthfuO^e6Fs~OJs^UYFzK}~nG)da3z-V!%~qiBcjC_Ci@#Y zwUt^$w-E|gwDnt8oO9#Md0xJRs~Ie1s*GjO9B5I{iXb2DREIk{W1(m$+RkuMf0(cq zZLgT!p>;LuE4uY)>lSA|pXPMB%=~Q%+R?$+I;Z7G=wx7JGD;Cv5J8l0vR~kq5yuJ# zx)dZ}@IM_jZX3!yuw#{iYq6TIl%`6Hu~xx#xSpSuYNci9QLql{d95>$?}0;*w*fah zaEpRlF@>R!?{F86AX?gWqb?}Y-LBvcabh*PLJ`5{Mg@0a6F=b`FLJ1J7eYS3P=8LykyeIkKVPrp@uf^UAj@9n$uV>- zQe$=msj*N?Ea&50g-)qUPsC%tGSOHk@ql);tI!+NWs z2kU~=M^+Jx|E+9f8IEHco#>-o*x%`lOUFA`R+@_d8 zsX|&>v(V?p?dcsgeB9%hYPAH)od|BqK9#xA5g++*VMPHMv+!WTe zgs!Hm3973?Mu)1YR6;~6Pv~*2OC#)RI7A_1ZRwP1T{T7Q&Baggidg>@#YrG@Va>|oy1vHmK`U>hxxXGUi4+7 zPr`eG0j(=Rn9TA87SnGN8J`lyA1XMA7qjs(Ly^f|-RLC}K4oy%grdZnAjahEGlpSI z^|$`3Q)s_zfSS=1@r;Zw@TCJ^iB9>NPxTzn!nZQM!}kuHRqz9TJdl;^VK)52Fp|&D zE0%o?vDotNxC6iO#=JQ$JE(*4AdOr3_B^%uA-Z!su=bC+a3T+x$j3I?QISDo79PeU zG>erB3VDiE7P4WX^>#c;^HzWrQCtU|g?UcCzo)Dhqsr+Cc&$lPrf@M(Y14Gyf+i0#I;|V3Pyce-XqFZQ_LbpkHnTfAFg_Q!}8Vfb|4gK&ME3HBst7(0sY3;QR zLM}EHpGa(8NYYHgNK7UPreIPJs!&U02>cV%=?C!FV=qxF+NjY8>5(_;8~)6L40OlOcFOGaa$kmwDglB+DUu^Af$uj zW}4|~lX^dim%u0>ec=NEGfa;m0SZUY-J-9P*kY1$AE+Fm{$S(^Y`c!O(%K9$1A>|) zWtttr%QOoJhiMkS{2BDoeNOL3%<-5xzGjZE;~T2>1m40s=KH(m`}_C+ADQo;nB!^u PL{Vgl@H2kJ@9_QuJrdSQ