vastly improved prompt history

This commit is contained in:
Steve Androulakis
2025-01-03 11:15:01 -08:00
parent dde076f3ed
commit 20d375b4ea
6 changed files with 82 additions and 96 deletions

View File

@@ -31,6 +31,8 @@ class ToolActivities:
response: ChatResponse = chat(model=model_name, messages=messages) response: ChatResponse = chat(model=model_name, messages=messages)
print(f"Chat response: {response.message.content}")
try: try:
data = json.loads(response.message.content) data = json.loads(response.message.content)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:

View File

@@ -7,62 +7,47 @@ def generate_genai_prompt(
tools_data: ToolsData, conversation_history: str, raw_json: Optional[str] = None tools_data: ToolsData, conversation_history: str, raw_json: Optional[str] = None
) -> str: ) -> str:
""" """
Generates json containing a unified prompt for an AI system to: Generates a concise prompt for producing or validating JSON instructions.
- 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.
""" """
prompt_lines = [] prompt_lines = []
# Intro / Role # Intro / Role
prompt_lines.append( prompt_lines.append(
"You are an AI assistant that must produce or validate JSON instructions " "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 # Conversation History
prompt_lines.append("=== Conversation History ===") prompt_lines.append("=== Conversation History ===")
prompt_lines.append( 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("") prompt_lines.append("")
# Tools Definitions # Tools Definitions
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: for tool in tools_data.tools:
prompt_lines.append(f"Tool name: {tool.name}") prompt_lines.append(f"Tool name: {tool.name}")
prompt_lines.append(f" Description: {tool.description}") prompt_lines.append(f" Description: {tool.description}")
prompt_lines.append(" Required arguments:") prompt_lines.append(" Required args:")
for arg in tool.arguments: for arg in tool.arguments:
prompt_lines.append(f" - {arg.name} ({arg.type}): {arg.description}") prompt_lines.append(f" - {arg.name} ({arg.type}): {arg.description}")
prompt_lines.append("") prompt_lines.append("")
# Instructions for Generating JSON (Always Shown) # Instructions for JSON Generation
prompt_lines.append("=== Instructions for JSON Generation ===") prompt_lines.append("=== Instructions for JSON Generation ===")
prompt_lines.append( prompt_lines.append(
"1. You may sequentially call multiple tools, each requiring specific arguments." "Your JSON format must be:\n"
)
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"
"{\n" "{\n"
' "response": "<plain text>",\n' ' "response": "<plain text>",\n'
' "next": "<question|confirm|done>",\n' ' "next": "<question|confirm|done>",\n'
' "tool": "<tool_name or none>",\n' ' "tool": "<tool_name or null>",\n'
' "args": {\n' ' "args": {\n'
' "<arg1>": "<value1 or null>",\n' ' "<arg1>": "<value1 or null>",\n'
' "<arg2>": "<value2 or null>",\n' ' "<arg2>": "<value2 or null>",\n'
@@ -71,79 +56,75 @@ def generate_genai_prompt(
"}" "}"
) )
prompt_lines.append( prompt_lines.append(
"6. Use 'next': 'question' if you lack any required arguments based on the history and prompt. " "1. You may call multiple tools sequentially. Each requires specific arguments.\n"
"Use 'next': 'confirm' only if NO arguments are missing. " '2. If ANY required argument is missing, use "next": "question" and prompt the user.\n'
"Use 'next': 'done' and tool: 'null' if you have successfully completed all tools " '3. If all required arguments are known, use "next": "confirm" and set "tool" to the tool name.\n'
"Don't offer any additional tools beyond the tools listed. " '4. If no further actions are needed, use "next": "done" and "tool": "null".\n'
"DON'T editorialize or add extra information to a 'done' response. " '5. Keep "response" short and user-friendly. Do not include any metadata or editorializing.\n'
"Example done response: {'response': 'All tools completed.', 'next': 'done', 'tool': 'null', 'args': {}} "
) )
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: 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("")
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(json.dumps(raw_json, indent=2))
prompt_lines.append("") 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( prompt_lines.append(
"3. Check 'args' matches the required arguments for that tool; fill in from context or set null if unknown." "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("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."
) )
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: <insert result here>",\n'
' "next": "done",\n'
' "tool": "",\n'
' "args": {}\n'
"}"
)
# Prompt Start
if raw_json is not None: if raw_json is not None:
prompt_lines.append("") prompt_lines.append("")
prompt_lines.append( prompt_lines.append("Begin by validating the provided JSON if necessary.")
"Begin by validating (and correcting) the JSON above, if needed."
)
else: else:
prompt_lines.append("") prompt_lines.append("")
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) return "\n".join(prompt_lines)

View File

@@ -4,4 +4,5 @@ def create_invoice(args: dict) -> dict:
return { return {
"invoiceStatus": "generated", "invoiceStatus": "generated",
"invoiceURL": "https://pay.example.com/invoice/12345", "invoiceURL": "https://pay.example.com/invoice/12345",
"reference": "INV-12345",
} }

View File

@@ -16,8 +16,8 @@ def search_flights(args: dict) -> dict:
return { return {
"tool": "SearchFlights", "tool": "SearchFlights",
"searchResults": [ "searchResults": [
"QF 123: $1200", "QF123: $1200",
"VA 456: $1000", "VA456: $1000",
], ],
"status": "search-complete", "status": "search-complete",
"args": args, "args": args,

View File

@@ -48,7 +48,7 @@ search_flights_tool = ToolDefinition(
# 3) Define the CreateInvoice tool # 3) Define the CreateInvoice tool
create_invoice_tool = ToolDefinition( create_invoice_tool = ToolDefinition(
name="CreateInvoice", name="CreateInvoice",
description="Generate an invoice with flight information or other items to purchase", description="Generate an invoice with flight information.",
arguments=[ arguments=[
ToolArgument( ToolArgument(
name="amount", name="amount",
@@ -58,7 +58,7 @@ create_invoice_tool = ToolDefinition(
ToolArgument( ToolArgument(
name="flightDetails", name="flightDetails",
type="string", 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",
), ),
], ],
) )

View File

@@ -81,8 +81,10 @@ class ToolWorkflow:
# Enqueue a follow-up prompt for the LLM # Enqueue a follow-up prompt for the LLM
self.prompt_queue.append( self.prompt_queue.append(
f"### The '{current_tool}' tool completed successfully with {dynamic_result}. " f"### The '{current_tool}' tool completed successfully with {dynamic_result}. "
"INSTRUCTIONS: Use this tool result, and the conversation history to figure out next steps. " "INSTRUCTIONS: Use this tool result, and the conversation history to figure out next steps, if any. "
"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." "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 # Loop around again
continue continue
@@ -91,15 +93,15 @@ class ToolWorkflow:
if self.prompt_queue: if self.prompt_queue:
prompt = self.prompt_queue.popleft() prompt = self.prompt_queue.popleft()
if prompt.startswith("###"): if prompt.startswith("###"):
# this is a custom prompt where the tool result is sent to the LLM pass
self.add_message("tool_result_to_llm", prompt)
else: else:
self.add_message("user", prompt) self.add_message("user", prompt)
# Pass entire conversation + Tools to LLM # Pass entire conversation + Tools to LLM
context_instructions = generate_genai_prompt( 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_input = ToolPromptInput(
prompt=prompt, prompt=prompt,
context_instructions=context_instructions, context_instructions=context_instructions,