From 20d375b4ea052544d2160bda49dd0a0e06c5763c Mon Sep 17 00:00:00 2001 From: Steve Androulakis Date: Fri, 3 Jan 2025 11:15:01 -0800 Subject: [PATCH] vastly improved prompt history --- activities/tool_activities.py | 2 + prompts/agent_prompt_generators.py | 155 +++++++++++++---------------- tools/create_invoice.py | 1 + tools/search_flights.py | 4 +- tools/tool_registry.py | 4 +- workflows/tool_workflow.py | 12 ++- 6 files changed, 82 insertions(+), 96 deletions(-) diff --git a/activities/tool_activities.py b/activities/tool_activities.py index c9bfa94..a83c654 100644 --- a/activities/tool_activities.py +++ b/activities/tool_activities.py @@ -31,6 +31,8 @@ class ToolActivities: response: ChatResponse = chat(model=model_name, messages=messages) + print(f"Chat response: {response.message.content}") + try: data = json.loads(response.message.content) except json.JSONDecodeError as e: diff --git a/prompts/agent_prompt_generators.py b/prompts/agent_prompt_generators.py index 0d49d64..ea8b15b 100644 --- a/prompts/agent_prompt_generators.py +++ b/prompts/agent_prompt_generators.py @@ -7,62 +7,47 @@ def generate_genai_prompt( tools_data: ToolsData, conversation_history: str, raw_json: Optional[str] = None ) -> str: """ - Generates json containing a unified prompt for an AI system to: - - Understand the conversation so far. - - Know which tools exist and their arguments. - - Produce or validate JSON instructions accordingly. - - :param tools_data: An object containing your tool definitions. - :param conversation_history: The user's conversation history. - :param raw_json: The existing JSON to validate/correct (if any). - :return: A json containing the merged instructions. + Generates a concise prompt for producing or validating JSON instructions. """ - prompt_lines = [] # Intro / Role prompt_lines.append( "You are an AI assistant that must produce or validate JSON instructions " - "for a set of tools in order to achieve the user's goals." + "to properly call a set of tools. Respond with valid JSON only." ) - prompt_lines.append("") # Conversation History prompt_lines.append("=== Conversation History ===") prompt_lines.append( - "Analyze this history for context on tool usage, known arguments, and what's left to do." + "Use this history to understand needed tools, arguments, and the user's goals:" ) - prompt_lines.append(conversation_history) + prompt_lines.append("BEGIN CONVERSATION HISTORY") + prompt_lines.append(json.dumps(conversation_history, indent=2)) + prompt_lines.append("END CONVERSATION HISTORY") prompt_lines.append("") # Tools Definitions prompt_lines.append("=== Tools Definitions ===") + prompt_lines.append(f"There are {len(tools_data.tools)} available tools:") + prompt_lines.append(", ".join([t.name for t in tools_data.tools])) + prompt_lines.append("") for tool in tools_data.tools: prompt_lines.append(f"Tool name: {tool.name}") prompt_lines.append(f" Description: {tool.description}") - prompt_lines.append(" Required arguments:") + prompt_lines.append(" Required args:") for arg in tool.arguments: prompt_lines.append(f" - {arg.name} ({arg.type}): {arg.description}") prompt_lines.append("") - # Instructions for Generating JSON (Always Shown) + # Instructions for JSON Generation prompt_lines.append("=== Instructions for JSON Generation ===") prompt_lines.append( - "1. You may sequentially call multiple tools, each requiring specific arguments." - ) - prompt_lines.append( - "2. If any required argument is missing, set 'next': 'question' and ask the user for it." - ) - prompt_lines.append( - "3. Once all arguments for a tool are known, set 'next': 'confirm' with 'tool' set to that tool's name." - ) - prompt_lines.append("4. If no further actions are required, set 'next': 'done'.") - prompt_lines.append( - "5. Always respond with valid JSON in this format:\n" + "Your JSON format must be:\n" "{\n" ' "response": "",\n' ' "next": "",\n' - ' "tool": "",\n' + ' "tool": "",\n' ' "args": {\n' ' "": "",\n' ' "": "",\n' @@ -71,79 +56,75 @@ def generate_genai_prompt( "}" ) prompt_lines.append( - "6. Use 'next': 'question' if you lack any required arguments based on the history and prompt. " - "Use 'next': 'confirm' only if NO arguments are missing. " - "Use 'next': 'done' and tool: 'null' if you have successfully completed all tools " - "Don't offer any additional tools beyond the tools listed. " - "DON'T editorialize or add extra information to a 'done' response. " - "Example done response: {'response': 'All tools completed.', 'next': 'done', 'tool': 'null', 'args': {}} " + "1. You may call multiple tools sequentially. Each requires specific arguments.\n" + '2. If ANY required argument is missing, use "next": "question" and prompt the user.\n' + '3. If all required arguments are known, use "next": "confirm" and set "tool" to the tool name.\n' + '4. If no further actions are needed, use "next": "done" and "tool": "null".\n' + '5. Keep "response" short and user-friendly. Do not include any metadata or editorializing.\n' ) - prompt_lines.append( - "7. Keep 'response' user-friendly with no extra commentary. Stick to valid JSON syntax. " - "Your goal is to guide the user through the running of these tools and elicit missing information." - ) - prompt_lines.append("") - # Instructions for Validation (Only if raw_json is provided) + # Validation Task (Only if raw_json is provided) if raw_json is not None: - prompt_lines.append("=== Validation Task ===") - prompt_lines.append( - "We have an existing JSON that may be malformed or incomplete. Validate and correct if needed." - ) prompt_lines.append("") - prompt_lines.append("=== JSON to Validate ===") + prompt_lines.append("=== Validation Task ===") + prompt_lines.append("Validate and correct the following JSON if needed:") prompt_lines.append(json.dumps(raw_json, indent=2)) prompt_lines.append("") - prompt_lines.append("Validation Checks:") - prompt_lines.append("1. Fix any JSON syntax errors.") - prompt_lines.append("2. Ensure 'tool' is one of the defined tools or 'none'.") prompt_lines.append( - "3. Check 'args' matches the required arguments for that tool; fill in from context or set null if unknown." - ) - prompt_lines.append("4. Ensure 'response' is present (plain user-facing text).") - prompt_lines.append( - "5. Ensure 'next' is one of 'question', 'confirm', 'done'. " - "Use 'question' if required args are still null, 'confirm' if all args are set, " - "and 'done' if no more actions remain." - ) - prompt_lines.append( - "6. Use the conversation history to see if arguments can be inferred." - ) - prompt_lines.append( - "7. Return only the fixed JSON if changes are required, with no extra commentary." + "Check syntax, ensure 'tool' is correct or 'null', verify 'args' are valid, " + 'and set "next" appropriately based on missing or complete args.' ) + prompt_lines.append("Return only the corrected JSON, no extra text.") - # Final Guidance + # Common Reminders and Examples + prompt_lines.append("") + prompt_lines.append("=== Usage Examples ===") + prompt_lines.append( + "Example for missing args (needs user input):\n" + "{\n" + ' "response": "I need your departure city.",\n' + ' "next": "question",\n' + ' "tool": "SearchFlights",\n' + ' "args": {\n' + ' "origin": null,\n' + ' "destination": "Melbourne",\n' + ' "dateDepart": "2025-03-26",\n' + ' "dateReturn": "2025-04-20"\n' + " }\n" + "}" + ) + prompt_lines.append( + "Example for confirmed args:\n" + "{\n" + ' "response": "All arguments are set.",\n' + ' "next": "confirm",\n' + ' "tool": "SearchFlights",\n' + ' "args": {\n' + ' "origin": "Seattle",\n' + ' "destination": "Melbourne",\n' + ' "dateDepart": "2025-03-26",\n' + ' "dateReturn": "2025-04-20"\n' + " }\n" + "}" + ) + prompt_lines.append( + "Example when fully done:\n" + "{\n" + ' "response": "All tools completed successfully. Final result: ",\n' + ' "next": "done",\n' + ' "tool": "",\n' + ' "args": {}\n' + "}" + ) + + # Prompt Start if raw_json is not None: prompt_lines.append("") - prompt_lines.append( - "Begin by validating (and correcting) the JSON above, if needed." - ) + prompt_lines.append("Begin by validating the provided JSON if necessary.") else: prompt_lines.append("") prompt_lines.append( - "Begin by generating a valid JSON response for the next step." + "Begin by producing a valid JSON response for the next step." ) - prompt_lines.append( - "REMINDER: If any required argument is missing, set 'next': 'question' and ask the user for it. " - "REMINDER: Use 'next': 'confirm' only if NO arguments are missing. " - ) - prompt_lines.append( - """ - Example JSON for question: - { - "args": { - "dateDepart": "2025-03-26", - "dateReturn": "2025-04-20", - "destination": "Melbourne", - "origin": null - }, - "next": "question", - "response": "I need to know where you're flying from. What's your departure city?", - "tool": "SearchFlights" - } - """ - ) - return "\n".join(prompt_lines) diff --git a/tools/create_invoice.py b/tools/create_invoice.py index f0083d6..f520180 100644 --- a/tools/create_invoice.py +++ b/tools/create_invoice.py @@ -4,4 +4,5 @@ def create_invoice(args: dict) -> dict: return { "invoiceStatus": "generated", "invoiceURL": "https://pay.example.com/invoice/12345", + "reference": "INV-12345", } diff --git a/tools/search_flights.py b/tools/search_flights.py index fe9730b..4efb3a2 100644 --- a/tools/search_flights.py +++ b/tools/search_flights.py @@ -16,8 +16,8 @@ def search_flights(args: dict) -> dict: return { "tool": "SearchFlights", "searchResults": [ - "QF 123: $1200", - "VA 456: $1000", + "QF123: $1200", + "VA456: $1000", ], "status": "search-complete", "args": args, diff --git a/tools/tool_registry.py b/tools/tool_registry.py index d7bca8e..3997a75 100644 --- a/tools/tool_registry.py +++ b/tools/tool_registry.py @@ -48,7 +48,7 @@ search_flights_tool = ToolDefinition( # 3) Define the CreateInvoice tool create_invoice_tool = ToolDefinition( name="CreateInvoice", - description="Generate an invoice with flight information or other items to purchase", + description="Generate an invoice with flight information.", arguments=[ ToolArgument( name="amount", @@ -58,7 +58,7 @@ create_invoice_tool = ToolDefinition( ToolArgument( name="flightDetails", type="string", - description="A summary of the flights, e.g., flight numbers, price breakdown", + description="A summary of the flights, e.g., flight number and airport codes", ), ], ) diff --git a/workflows/tool_workflow.py b/workflows/tool_workflow.py index 7e518d4..a2aadfb 100644 --- a/workflows/tool_workflow.py +++ b/workflows/tool_workflow.py @@ -81,8 +81,10 @@ class ToolWorkflow: # Enqueue a follow-up prompt for the LLM self.prompt_queue.append( f"### The '{current_tool}' tool completed successfully with {dynamic_result}. " - "INSTRUCTIONS: Use this tool result, and the conversation history to figure out next steps. " - "IMPORTANT: If all listed tools have run, you are up to the final step. Mark 'next':'done' and respond with 'All tools run' or similar." + "INSTRUCTIONS: Use this tool result, and the conversation history to figure out next steps, if any. " + "IMPORTANT REMINDER: Always return only JSON in the format: {'response': '', 'next': '', 'tool': '', 'args': {}} " + " Do NOT include any metadata or editorializing in the response. " + "IMPORTANT: If moving on to another tool then ensure you ask next='question' for any missing arguments." ) # Loop around again continue @@ -91,15 +93,15 @@ class ToolWorkflow: if self.prompt_queue: prompt = self.prompt_queue.popleft() if prompt.startswith("###"): - # this is a custom prompt where the tool result is sent to the LLM - self.add_message("tool_result_to_llm", prompt) + pass else: self.add_message("user", prompt) # Pass entire conversation + Tools to LLM context_instructions = generate_genai_prompt( - tools_data, self.format_history(), self.tool_data + tools_data, self.conversation_history, self.tool_data ) + prompt_input = ToolPromptInput( prompt=prompt, context_instructions=context_instructions,