mirror of
https://github.com/temporal-community/temporal-ai-agent.git
synced 2026-03-15 05:58:08 +01:00
* initial mcp * food ordering with mcp * prompt eng * splitting out goals and updating docs * a diff so I can get tests from codex * a diff so I can get tests from codex * oops, missing files * tests, file formatting * readme and setup updates * setup.md link fixes * readme change * readme change * readme change * stripe food setup script * single agent mode default * prompt engineering for better multi agent performance * performance should be greatly improved * Update goals/finance.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update activities/tool_activities.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * co-pilot PR suggested this change, and now fixed it * stronger wording around json format response * formatting * moved docs to dir * moved image assets under docs * cleanup env example, stripe guidance * cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
286 lines
12 KiB
Python
286 lines
12 KiB
Python
import json
|
|
from typing import Optional
|
|
|
|
from models.tool_definitions import AgentGoal
|
|
|
|
MULTI_GOAL_MODE: bool = None
|
|
|
|
|
|
def generate_genai_prompt(
|
|
agent_goal: AgentGoal,
|
|
conversation_history: str,
|
|
multi_goal_mode: bool,
|
|
raw_json: Optional[str] = None,
|
|
mcp_tools_info: Optional[dict] = None,
|
|
) -> str:
|
|
"""
|
|
Generates a concise prompt for producing or validating JSON instructions
|
|
with the provided tools and conversation history.
|
|
"""
|
|
prompt_lines = []
|
|
set_multi_goal_mode_if_unset(multi_goal_mode)
|
|
|
|
# Intro / Role
|
|
prompt_lines.append(
|
|
"You are an AI agent that helps fill required arguments for the tools described below. "
|
|
"CRITICAL: You must respond with ONLY valid JSON using the exact schema provided. "
|
|
"DO NOT include any text before or after the JSON. Your entire response must be parseable JSON."
|
|
)
|
|
|
|
# Main Conversation History
|
|
prompt_lines.append("=== Conversation History ===")
|
|
prompt_lines.append(
|
|
"This is the ongoing history to determine which tool and arguments to gather:"
|
|
)
|
|
prompt_lines.append("*BEGIN CONVERSATION HISTORY*")
|
|
prompt_lines.append(json.dumps(conversation_history, indent=2))
|
|
prompt_lines.append("*END CONVERSATION HISTORY*")
|
|
prompt_lines.append(
|
|
"REMINDER: You can use the conversation history to infer arguments for the tools."
|
|
)
|
|
|
|
# Example Conversation History (from agent_goal)
|
|
if agent_goal.example_conversation_history:
|
|
prompt_lines.append("=== Example Conversation With These Tools ===")
|
|
prompt_lines.append(
|
|
"Use this example to understand how tools are invoked and arguments are gathered."
|
|
)
|
|
prompt_lines.append("BEGIN EXAMPLE")
|
|
prompt_lines.append(agent_goal.example_conversation_history)
|
|
prompt_lines.append("END EXAMPLE")
|
|
prompt_lines.append("")
|
|
|
|
# Add MCP server context if present
|
|
if agent_goal.mcp_server_definition:
|
|
prompt_lines.append("=== MCP Server Information ===")
|
|
prompt_lines.append(
|
|
f"Connected to MCP Server: {agent_goal.mcp_server_definition.name}"
|
|
)
|
|
if mcp_tools_info and mcp_tools_info.get("success", False):
|
|
tools = mcp_tools_info.get("tools", {})
|
|
server_name = mcp_tools_info.get("server_name", "Unknown")
|
|
prompt_lines.append(
|
|
f"MCP Tools loaded from {server_name} ({len(tools)} tools):"
|
|
)
|
|
for tool_name, tool_info in tools.items():
|
|
prompt_lines.append(
|
|
f" - {tool_name}: {tool_info.get('description', 'No description')}"
|
|
)
|
|
else:
|
|
prompt_lines.append("Additional tools available via MCP integration:")
|
|
prompt_lines.append("")
|
|
|
|
# Tools Definitions
|
|
prompt_lines.append("=== Tools Definitions ===")
|
|
prompt_lines.append(f"There are {len(agent_goal.tools)} available tools:")
|
|
prompt_lines.append(", ".join([t.name for t in agent_goal.tools]))
|
|
prompt_lines.append(f"Goal: {agent_goal.description}")
|
|
prompt_lines.append(
|
|
"CRITICAL: You MUST follow the complete sequence described in the Goal above. "
|
|
"Do NOT skip steps or assume the goal is complete until ALL steps are done."
|
|
)
|
|
prompt_lines.append(
|
|
"Gather the necessary information for each tool in the sequence described above."
|
|
)
|
|
prompt_lines.append(
|
|
"Only ask for arguments listed below. Do not add extra arguments."
|
|
)
|
|
prompt_lines.append("")
|
|
for tool in agent_goal.tools:
|
|
prompt_lines.append(f"Tool name: {tool.name}")
|
|
prompt_lines.append(f" Description: {tool.description}")
|
|
prompt_lines.append(" Required args:")
|
|
for arg in tool.arguments:
|
|
prompt_lines.append(f" - {arg.name} ({arg.type}): {arg.description}")
|
|
prompt_lines.append("")
|
|
prompt_lines.append(
|
|
"When all required args for a tool are known, you can propose next='confirm' to run it."
|
|
)
|
|
|
|
# JSON Format Instructions
|
|
prompt_lines.append("=== CRITICAL: JSON-ONLY RESPONSE FORMAT ===")
|
|
prompt_lines.append(
|
|
"MANDATORY: Your response must be ONLY valid JSON with NO additional text.\n"
|
|
"NO explanations, NO comments, NO text before or after the JSON.\n"
|
|
"Your entire response must start with '{' and end with '}'.\n\n"
|
|
"Required JSON format:\n"
|
|
"{\n"
|
|
' "response": "<plain text>",\n'
|
|
' "next": "<question|confirm|pick-new-goal|done>",\n'
|
|
' "tool": "<tool_name or null>",\n'
|
|
' "args": {\n'
|
|
' "<arg1>": "<value1 or null>",\n'
|
|
' "<arg2>": "<value2 or null>",\n'
|
|
" ...\n"
|
|
" }\n"
|
|
"}\n\n"
|
|
"INVALID EXAMPLE: 'Thank you for providing... {\"response\": ...}'\n"
|
|
'VALID EXAMPLE: \'{"response": "Thank you for providing...", "next": ...}\''
|
|
)
|
|
prompt_lines.append(
|
|
"DECISION LOGIC (follow this exact order):\n"
|
|
"1) Do I need to run a tool next?\n"
|
|
" - If your response says 'let's get/proceed/check/add/create/finalize...' -> YES, you need a tool\n"
|
|
" - If you're announcing what you're about to do -> YES, you need a tool\n"
|
|
" - If no more steps needed for current goal -> NO, go to step 3\n\n"
|
|
"2) If YES to step 1: Do I have all required arguments?\n"
|
|
" - Check tool definition for required args\n"
|
|
" - Can I fill missing args from conversation history?\n"
|
|
" - Can I use sensible defaults (limit=100, etc.)?\n"
|
|
" - If ALL args available/inferrable -> set next='confirm', specify tool and args\n"
|
|
" - If missing required args -> set next='question', ask for missing args, tool=null\n\n"
|
|
"3) If NO to step 1: Is the entire goal complete?\n"
|
|
" - Check Goal description in system prompt - are ALL steps done?\n"
|
|
" - Check recent conversation for completion indicators ('finalized', 'complete', etc.)\n"
|
|
f" - If complete -> {generate_toolchain_complete_guidance()}\n"
|
|
" - If not complete -> identify next needed tool, go to step 2\n\n"
|
|
"CRITICAL RULES:\n"
|
|
"• RESPOND WITH JSON ONLY - NO TEXT BEFORE OR AFTER THE JSON OBJECT\n"
|
|
"• Your response must start with '{' and end with '}' - nothing else\n"
|
|
"• NEVER set next='question' without asking an actual question in your response\n"
|
|
"• NEVER set tool=null when you're announcing you'll run a specific tool\n"
|
|
"• If response contains 'let's proceed to get pricing' -> next='confirm', tool='list_prices'\n"
|
|
"• If response contains 'Now adding X' -> next='confirm', tool='create_invoice_item'\n"
|
|
"• Use conversation history to infer arguments (customer IDs, product IDs, etc.)\n"
|
|
"• Use sensible defaults rather than asking users for technical parameters\n"
|
|
"• Carry forward arguments between tools (same customer, same invoice, etc.)\n"
|
|
"• If force_confirm='False' in history, be declarative, don't ask permission\n\n"
|
|
"EXAMPLES:\n"
|
|
"WRONG: response='let\\'s get pricing', next='question', tool=null\n"
|
|
"RIGHT: response='let\\'s get pricing', next='confirm', tool='list_prices'\n"
|
|
"WRONG: response='adding pizza', next='question', tool='create_invoice_item'\n"
|
|
"RIGHT: response='adding pizza', next='confirm', tool='create_invoice_item'\n"
|
|
)
|
|
|
|
# Validation Task (If raw_json is provided)
|
|
if raw_json is not None:
|
|
prompt_lines.append("")
|
|
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(
|
|
"Check syntax, 'tool' validity, 'args' completeness, "
|
|
"and set 'next' appropriately. Return ONLY corrected JSON."
|
|
)
|
|
|
|
# Prompt Start
|
|
prompt_lines.append("")
|
|
prompt_lines.append("=== FINAL REMINDER ===")
|
|
prompt_lines.append("RESPOND WITH VALID JSON ONLY. NO ADDITIONAL TEXT.")
|
|
prompt_lines.append("")
|
|
if raw_json is not None:
|
|
prompt_lines.append(
|
|
"Validate the provided JSON and return ONLY corrected JSON."
|
|
)
|
|
else:
|
|
prompt_lines.append(
|
|
"Return ONLY a valid JSON response. Start with '{' and end with '}'."
|
|
)
|
|
|
|
return "\n".join(prompt_lines)
|
|
|
|
|
|
def generate_tool_completion_prompt(current_tool: str, dynamic_result: dict) -> str:
|
|
"""
|
|
Generates a prompt for handling tool completion and determining next steps.
|
|
|
|
Args:
|
|
current_tool: The name of the tool that just completed
|
|
dynamic_result: The result data from the tool execution
|
|
|
|
Returns:
|
|
str: A formatted prompt string for the agent to process the tool completion
|
|
"""
|
|
return (
|
|
f"### The '{current_tool}' tool completed successfully with {dynamic_result}. "
|
|
"INSTRUCTIONS: Parse this tool result as plain text, and use the system prompt containing the list of tools in sequence and the conversation history (and previous tool_results) to figure out next steps, if any. "
|
|
"You will need to use the tool_results to auto-fill arguments for subsequent tools and also to figure out if all tools have been run. "
|
|
'{"next": "<question|confirm|pick-new-goal|done>", "tool": "<tool_name or null>", "args": {"<arg1>": "<value1 or null>", "<arg2>": "<value2 or null>}, "response": "<plain text (can include \\n line breaks)>"}'
|
|
"ONLY return those json keys (next, tool, args, response), nothing else. "
|
|
'Next should be "question" if the tool is not the last one in the sequence. '
|
|
'Next should be "done" if the user is asking to be done with the chat. '
|
|
f"{generate_pick_new_goal_guidance()}"
|
|
)
|
|
|
|
|
|
def generate_missing_args_prompt(
|
|
current_tool: str, tool_data: dict, missing_args: list[str]
|
|
) -> str:
|
|
"""
|
|
Generates a prompt for handling missing arguments for a tool.
|
|
|
|
Args:
|
|
current_tool: The name of the tool that needs arguments
|
|
tool_data: The current tool data containing the response
|
|
missing_args: List of argument names that are missing
|
|
|
|
Returns:
|
|
str: A formatted prompt string for requesting missing arguments
|
|
"""
|
|
return (
|
|
f"### INSTRUCTIONS set next='question', combine this response response='{tool_data.get('response')}' "
|
|
f"and following missing arguments for tool {current_tool}: {missing_args}. "
|
|
"Only provide a valid JSON response without any comments or metadata."
|
|
)
|
|
|
|
|
|
def set_multi_goal_mode_if_unset(mode: bool) -> None:
|
|
"""
|
|
Set multi-mode (used to pass workflow)
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
bool: True if in multi-goal mode, false if not
|
|
"""
|
|
global MULTI_GOAL_MODE
|
|
if MULTI_GOAL_MODE is None:
|
|
MULTI_GOAL_MODE = mode
|
|
|
|
|
|
def is_multi_goal_mode() -> bool:
|
|
"""
|
|
Centralized logic for if we're in multi-goal mode.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
bool: True if in multi-goal mode, false if not
|
|
"""
|
|
return MULTI_GOAL_MODE
|
|
|
|
|
|
def generate_pick_new_goal_guidance() -> str:
|
|
"""
|
|
Generates a prompt for guiding the LLM to pick a new goal or be done depending on multi-goal mode.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
str: A prompt string prompting the LLM to when to go to pick-new-goal
|
|
"""
|
|
if is_multi_goal_mode():
|
|
return 'Next should only be "pick-new-goal" if EVERY SINGLE STEP in the Goal description has been completed (check the system prompt Goal section carefully), or the user explicitly requested to pick a new goal. If any step is missing (like customer creation, invoice creation, or payment processing), continue with the next required tool.'
|
|
else:
|
|
return 'Next should never be "pick-new-goal".'
|
|
|
|
|
|
def generate_toolchain_complete_guidance() -> str:
|
|
"""
|
|
Generates a prompt for guiding the LLM to handle the end of the toolchain.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
str: A prompt string prompting the LLM to prompt for a new goal, or be done
|
|
"""
|
|
if is_multi_goal_mode():
|
|
return "If no more tools are needed for the current goal (EVERY step in the Goal description has been completed AND user_confirmed_tool_run has been run for all required tools), set next='pick-new-goal' and tool=null to allow the user to choose their next action."
|
|
else:
|
|
return "If no more tools are needed (EVERY step in the Goal description has been completed AND user_confirmed_tool_run has been run for all), set next='done' and tool=null."
|