- dynamic agent prompt based on multi goal or not

- made choose_agent_goal be dynamically included
- made tool selection not be required in all toolchains
- changes to get env vars easier in workflow
- Updated docs/guides, todo based on aboe
This commit is contained in:
Joshua Smith
2025-04-08 15:01:11 -04:00
parent f567583b3a
commit c18a40b502
11 changed files with 150 additions and 59 deletions

View File

@@ -35,12 +35,13 @@ OPENAI_API_KEY=sk-proj-...
# Uncomment if using API key (not needed for local dev server) # Uncomment if using API key (not needed for local dev server)
# TEMPORAL_API_KEY=abcdef1234567890 # TEMPORAL_API_KEY=abcdef1234567890
# Set starting goal of agent - if unset default is all # Set starting goal of agent - if unset default is goal_choose_agent_type
AGENT_GOAL=goal_choose_agent_type # default for multi-goal start AGENT_GOAL=goal_choose_agent_type # for multi-goal start
#AGENT_GOAL=goal_event_flight_invoice # for original goal #AGENT_GOAL=goal_event_flight_invoice # for original goal
#AGENT_GOAL=goal_match_train_invoice # for replay goal #AGENT_GOAL=goal_match_train_invoice # for replay goal
#Choose which category(ies) of goals you want to be listed by the Agent - options are system (always included), hr, travel, or all. #Choose which category(ies) of goals you want to be listed by the Agent Goal picker if enabled above
# - options are system (always included), hr, travel, or all.
GOAL_CATEGORIES=hr,travel-flights,travel-trains,fin # default is all GOAL_CATEGORIES=hr,travel-flights,travel-trains,fin # default is all
#GOAL_CATEGORIES=travel-flights #GOAL_CATEGORIES=travel-flights

View File

@@ -11,7 +11,7 @@ import google.generativeai as genai
import anthropic import anthropic
import deepseek import deepseek
from dotenv import load_dotenv from dotenv import load_dotenv
from models.data_types import ValidationInput, ValidationResult, ToolPromptInput, EnvLookupInput from models.data_types import EnvLookupOutput, ValidationInput, ValidationResult, ToolPromptInput, EnvLookupInput
load_dotenv(override=True) load_dotenv(override=True)
print( print(
@@ -370,7 +370,8 @@ class ToolActivities:
print("Initialized Anthropic client on demand") print("Initialized Anthropic client on demand")
response = self.anthropic_client.messages.create( response = self.anthropic_client.messages.create(
model="claude-3-5-sonnet-20241022", # todo try claude-3-7-sonnet-20250219 #model="claude-3-5-sonnet-20241022", # todo try claude-3-7-sonnet-20250219
model="claude-3-7-sonnet-20250219", # todo try claude-3-7-sonnet-20250219
max_tokens=1024, max_tokens=1024,
system=input.context_instructions system=input.context_instructions
+ ". The current date is " + ". The current date is "
@@ -473,17 +474,29 @@ class ToolActivities:
# get env vars for workflow # get env vars for workflow
@activity.defn @activity.defn
async def get_env_bool(self, input: EnvLookupInput) -> bool: async def get_wf_env_vars(self, input: EnvLookupInput) -> EnvLookupOutput:
""" gets boolean env vars for workflow as an activity result so it's deterministic """ gets env vars for workflow as an activity result so it's deterministic
handles default/None handles default/None
""" """
value = os.getenv(input.env_var_name) output: EnvLookupOutput = EnvLookupOutput(show_confirm=input.show_confirm_default,
if value is None: multi_goal_mode=True)
return input.default show_confirm_value = os.getenv(input.show_confirm_env_var_name)
if value is not None and value.lower() == "false": if show_confirm_value is None:
return False output.show_confirm = input.show_confirm_default
elif show_confirm_value is not None and show_confirm_value.lower() == "false":
output.show_confirm = False
else: else:
return True output.show_confirm = True
first_goal_value = os.getenv("AGENT_GOAL")
if first_goal_value is None:
output.multi_goal_mode = True # default if unset
elif first_goal_value is not None and first_goal_value.lower() != "goal_choose_agent_type":
output.multi_goal_mode = False
else:
output.multi_goal_mode = True
return output
def get_current_date_human_readable(): def get_current_date_human_readable():

View File

@@ -19,14 +19,13 @@ Goal Categories lets you pick which groups of goals to show. Set via an .env set
- `category_tag`: category for the goal - `category_tag`: category for the goal
- `agent_friendly_description`: user-facing description of what the agent/chatbot does - `agent_friendly_description`: user-facing description of what the agent/chatbot does
- `tools`: the list of tools the goal will walk the user through. These will be defined in the [tools/tool_registry.py](tools/tool_registry.py) and should be defined in list form as tool_registry.[name of tool] - `tools`: the list of tools the goal will walk the user through. These will be defined in the [tools/tool_registry.py](tools/tool_registry.py) and should be defined in list form as tool_registry.[name of tool]
- Important! If you want to prompt the user with options for another goal, the last tool listed must be `list_agents_tool`. This allows the chatbot to guide the user back to choosing from the list of available goals once a goal is complete. This is recommended for multi-goal behavior. The `goal_choose_agent_type` is the exception as it handles the changing of goals. <br />
Example: Example:
``` ```
tools=[ tools=[
tool_registry.current_pto_tool, tool_registry.current_pto_tool,
tool_registry.future_pto_calc_tool, tool_registry.future_pto_calc_tool,
tool_registry.book_pto_tool, tool_registry.book_pto_tool,
tool_registry.list_agents_tool,
] ]
``` ```
- `description`: LLM-facing description of the goal that lists the tools by name and purpose. - `description`: LLM-facing description of the goal that lists the tools by name and purpose.

View File

@@ -45,5 +45,10 @@ class ValidationResult:
@dataclass @dataclass
class EnvLookupInput: class EnvLookupInput:
env_var_name: str show_confirm_env_var_name: str
default: bool show_confirm_default: bool
@dataclass
class EnvLookupOutput:
show_confirm: bool
multi_goal_mode: bool

View File

@@ -2,15 +2,17 @@ from models.tool_definitions import AgentGoal
from typing import Optional from typing import Optional
import json import json
MULTI_GOAL_MODE:bool = None
def generate_genai_prompt( def generate_genai_prompt(
agent_goal: AgentGoal, conversation_history: str, raw_json: Optional[str] = None agent_goal: AgentGoal, conversation_history: str, multi_goal_mode:bool, raw_json: Optional[str] = None
) -> str: ) -> str:
""" """
Generates a concise prompt for producing or validating JSON instructions Generates a concise prompt for producing or validating JSON instructions
with the provided tools and conversation history. with the provided tools and conversation history.
""" """
prompt_lines = [] prompt_lines = []
set_multi_goal_mode_if_unset(multi_goal_mode)
# Intro / Role # Intro / Role
prompt_lines.append( prompt_lines.append(
@@ -81,7 +83,7 @@ def generate_genai_prompt(
"1) If any required argument is missing, set next='question' and ask the user.\n" "1) If any required argument is missing, set next='question' and ask the user.\n"
"2) If all required arguments are known, set next='confirm' and specify the tool.\n" "2) If all required arguments are known, set next='confirm' and specify the tool.\n"
" The user will confirm before the tool is run.\n" " The user will confirm before the tool is run.\n"
"3) If no more tools are needed (user_confirmed_tool_run has been run for all), set next='confirm' and tool='ListAgents'.\n" f"3) {generate_toolchain_complete_guidance()}\n"
"4) response should be short and user-friendly.\n" "4) response should be short and user-friendly.\n"
) )
@@ -127,8 +129,7 @@ def generate_tool_completion_prompt(current_tool: str, dynamic_result: dict) ->
"ONLY return those json keys (next, tool, args, response), nothing else. " "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 "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. ' 'Next should be "done" if the user is asking to be done with the chat. '
'Next should only be "pick-new-goal" if all tools have been run (use the system prompt to figure that out).' f"{generate_pick_new_goal_guidance()}"
#'If all tools have been run (use the system prompt to figure that out) then clear tool history.' todo maybe fix this
) )
def generate_missing_args_prompt(current_tool: str, tool_data: dict, missing_args: list[str]) -> str: def generate_missing_args_prompt(current_tool: str, tool_data: dict, missing_args: list[str]) -> str:
@@ -148,3 +149,59 @@ def generate_missing_args_prompt(current_tool: str, tool_data: dict, missing_arg
f"and following missing arguments for tool {current_tool}: {missing_args}. " f"and following missing arguments for tool {current_tool}: {missing_args}. "
"Only provide a valid JSON response without any comments or metadata." "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 all tools have been run (use the system prompt to figure that out) or the user explicitly requested to pick a new goal.'
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 (user_confirmed_tool_run has been run for all), set next='confirm' and tool='ListAgents'."
else :
return "If no more tools are needed (user_confirmed_tool_run has been run for all), set next='done' and tool=''."

View File

@@ -62,7 +62,7 @@ async def main():
activities=[ activities=[
activities.agent_validatePrompt, activities.agent_validatePrompt,
activities.agent_toolPlanner, activities.agent_toolPlanner,
activities.get_env_bool, activities.get_wf_env_vars,
dynamic_tool_activity, dynamic_tool_activity,
], ],
activity_executor=activity_executor, activity_executor=activity_executor,

View File

@@ -16,9 +16,12 @@ SHOW_CONFIRM=True
### Agent Goal Configuration ### Agent Goal Configuration
The agent can be configured to pursue different goals using the `AGENT_GOAL` environment variable in your `.env` file. The agent can be configured to pursue different goals using the `AGENT_GOAL` environment variable in your `.env` file. If unset, default is `goal_choose_agent_type`.
The agent can support multiple goals using goal categories using `GOAL_CATEGORIES` in your .env file. If unset, default is all. If the first goal is `goal_choose_agent_type` the agent will support multiple goals using goal categories defined by `GOAL_CATEGORIES` in your .env file. If unset, default is all.
```bash
GOAL_CATEGORIES=hr,travel-flights,travel-trains,fin
```
See the section Goal-Specific Tool Configuration below for tool configuration for specific goals. See the section Goal-Specific Tool Configuration below for tool configuration for specific goals.

12
todo.md
View File

@@ -1,11 +1,15 @@
# todo list # todo list
[ ] goal change management tweaks <br /> [ ] goal change management tweaks <br />
- [ ] maybe make the choose_Agent_goal tag not be system/not always included? <br /> - [x] maybe make the choose_Agent_goal tag not be system/not always included? <br />
- [ ] try taking out list-agents as a tool because agent_prompt_generators may do it for you <br /> - [x] try taking out list-agents as a tool because agent_prompt_generators may do it for you <br />
- [ ] make goal selection not be a system tool but be an option in .env, see how that works, includes taking it out of the goal/toolset for all goals <br /> - [x] make goal selection not be a system tool but be an option in .env, see how that works, includes taking it out of the goal/toolset for all goals <br />
- [x] test single-goal <br />
- [x] test claude and grok<br />
- [x] document in sample env and docs how to control <br />
[ ] expand [tests](./tests/agent_goal_workflow_test.py)<br /> [ ] expand [tests](./tests/agent_goal_workflow_test.py)<br />
[ ] try claude-3-7-sonnet-20250219, see [tool_activities.py](./activities/tool_activities.py) <br /> [x] try claude-3-7-sonnet-20250219, see [tool_activities.py](./activities/tool_activities.py) <br />
[x] test Grok with changes
[ ] adding fintech goals <br /> [ ] adding fintech goals <br />
- Fraud Detection and Prevention - The AI monitors transactions across accounts, flagging suspicious activities (e.g., unusual spending patterns or login attempts) and autonomously freezing accounts or notifying customers and compliance teams.<br /> - Fraud Detection and Prevention - The AI monitors transactions across accounts, flagging suspicious activities (e.g., unusual spending patterns or login attempts) and autonomously freezing accounts or notifying customers and compliance teams.<br />

View File

@@ -21,7 +21,7 @@ starter_prompt_generic = silly_prompt + "Welcome me, give me a description of wh
goal_choose_agent_type = AgentGoal( goal_choose_agent_type = AgentGoal(
id = "goal_choose_agent_type", id = "goal_choose_agent_type",
category_tag="system", category_tag="agent_selection",
agent_name="Choose Agent", agent_name="Choose Agent",
agent_friendly_description="Choose the type of agent to assist you today.", agent_friendly_description="Choose the type of agent to assist you today.",
tools=[ tools=[
@@ -33,13 +33,13 @@ goal_choose_agent_type = AgentGoal(
"1. ListAgents: List agents available to interact with. Do not ask for user confirmation for this tool. " "1. ListAgents: List agents available to interact with. Do not ask for user confirmation for this tool. "
"2. ChangeGoal: Change goal of agent " "2. ChangeGoal: Change goal of agent "
"After these tools are complete, change your goal to the new goal as chosen by the user. ", "After these tools are complete, change your goal to the new goal as chosen by the user. ",
starter_prompt=starter_prompt_generic + "Begin by listing all details of all agents as provided by the output of the first tool included in this goal. ", starter_prompt=starter_prompt_generic + " Begin by listing all details of all agents as provided by the output of the first tool included in this goal. ",
example_conversation_history="\n ".join( example_conversation_history="\n ".join(
[ [
"agent: Here are the currently available agents.", "agent: Here are the currently available agents.",
"user_confirmed_tool_run: <user clicks confirm on ListAgents tool>", "user_confirmed_tool_run: <user clicks confirm on ListAgents tool>",
"tool_result: { 'agent_name': 'Event Flight Finder', 'goal_id': 'goal_event_flight_invoice', 'agent_description': 'Helps users find interesting events and arrange travel to them' }", "tool_result: { 'agent_name': 'Event Flight Finder', 'goal_id': 'goal_event_flight_invoice', 'agent_description': 'Helps users find interesting events and arrange travel to them' }",
"agent: The available agents are: 1. Event Flight Finder. \n Which agent would you like to speak to (?", "agent: The available agents are: 1. Event Flight Finder. \n Which agent would you like to speak to? (You can respond with name or number.)",
"user: 1, Event Flight Finder", "user: 1, Event Flight Finder",
"user_confirmed_tool_run: <user clicks confirm on ChangeGoal tool>", "user_confirmed_tool_run: <user clicks confirm on ChangeGoal tool>",
"tool_result: { 'new_goal': 'goal_event_flight_invoice' }", "tool_result: { 'new_goal': 'goal_event_flight_invoice' }",
@@ -61,7 +61,6 @@ goal_pirate_treasure = AgentGoal(
tools=[ tools=[
tool_registry.give_hint_tool, tool_registry.give_hint_tool,
tool_registry.guess_location_tool, tool_registry.guess_location_tool,
tool_registry.list_agents_tool,
], ],
description="The user wants to find a pirate treasure. " description="The user wants to find a pirate treasure. "
"Help the user gather args for these tools, in a loop, until treasure_found is True or the user requests to be done: " "Help the user gather args for these tools, in a loop, until treasure_found is True or the user requests to be done: "
@@ -106,7 +105,6 @@ goal_match_train_invoice = AgentGoal(
tool_registry.search_trains_tool, tool_registry.search_trains_tool,
tool_registry.book_trains_tool, tool_registry.book_trains_tool,
tool_registry.create_invoice_tool, tool_registry.create_invoice_tool,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to book a trip to a city in the UK around the dates of a premier league match. " description="The user wants to book a trip to a city in the UK around the dates of a premier league match. "
"Help the user find a premier league match to attend, search and book trains for that match and offers to invoice them for the cost of train tickets. " "Help the user find a premier league match to attend, search and book trains for that match and offers to invoice them for the cost of train tickets. "
@@ -153,7 +151,6 @@ goal_event_flight_invoice = AgentGoal(
tool_registry.find_events_tool, tool_registry.find_events_tool,
tool_registry.search_flights_tool, tool_registry.search_flights_tool,
tool_registry.create_invoice_tool, tool_registry.create_invoice_tool,
#tool_registry.list_agents_tool, #last tool must be list_agents to faciliate changing back to picking an agent again at the end
], ],
description="Help the user gather args for these tools in order: " description="Help the user gather args for these tools in order: "
"1. FindEvents: Find an event to travel to " "1. FindEvents: Find an event to travel to "
@@ -193,7 +190,6 @@ goal_hr_schedule_pto = AgentGoal(
tool_registry.current_pto_tool, tool_registry.current_pto_tool,
tool_registry.future_pto_calc_tool, tool_registry.future_pto_calc_tool,
tool_registry.book_pto_tool, tool_registry.book_pto_tool,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to schedule paid time off (PTO) after today's date. To assist with that goal, help the user gather args for these tools in order: " description="The user wants to schedule paid time off (PTO) after today's date. To assist with that goal, help the user gather args for these tools in order: "
"1. CurrentPTO: Tell the user how much PTO they currently have " "1. CurrentPTO: Tell the user how much PTO they currently have "
@@ -230,7 +226,6 @@ goal_hr_check_pto = AgentGoal(
agent_friendly_description="Check your available PTO.", agent_friendly_description="Check your available PTO.",
tools=[ tools=[
tool_registry.current_pto_tool, tool_registry.current_pto_tool,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to check their paid time off (PTO) after today's date. To assist with that goal, help the user gather args for these tools in order: " description="The user wants to check their paid time off (PTO) after today's date. To assist with that goal, help the user gather args for these tools in order: "
"1. CurrentPTO: Tell the user how much PTO they currently have ", "1. CurrentPTO: Tell the user how much PTO they currently have ",
@@ -252,11 +247,10 @@ goal_hr_check_pto = AgentGoal(
goal_hr_check_paycheck_bank_integration_status = AgentGoal( goal_hr_check_paycheck_bank_integration_status = AgentGoal(
id = "goal_hr_check_paycheck_bank_integration_status", id = "goal_hr_check_paycheck_bank_integration_status",
category_tag="hr", category_tag="hr",
agent_name="Check paycheck bank integration status", agent_name="Check paycheck deposit status",
agent_friendly_description="Check your integration between paycheck payer and your financial institution.", agent_friendly_description="Check your integration between your employer and your financial institution.",
tools=[ tools=[
tool_registry.paycheck_bank_integration_status_check, tool_registry.paycheck_bank_integration_status_check,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to check their bank integration used to deposit their paycheck. To assist with that goal, help the user gather args for these tools in order: " description="The user wants to check their bank integration used to deposit their paycheck. To assist with that goal, help the user gather args for these tools in order: "
"1. CheckPayBankStatus: Tell the user the status of their paycheck bank integration ", "1. CheckPayBankStatus: Tell the user the status of their paycheck bank integration ",
@@ -283,7 +277,6 @@ goal_fin_check_account_balances = AgentGoal(
tools=[ tools=[
tool_registry.financial_check_account_is_valid, tool_registry.financial_check_account_is_valid,
tool_registry.financial_get_account_balances, tool_registry.financial_get_account_balances,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to check their account balances at the bank or financial institution. To assist with that goal, help the user gather args for these tools in order: " description="The user wants to check their account balances at the bank or financial institution. To assist with that goal, help the user gather args for these tools in order: "
"1. FinCheckAccountIsValid: validate the user's account is valid" "1. FinCheckAccountIsValid: validate the user's account is valid"
@@ -318,7 +311,6 @@ goal_fin_move_money = AgentGoal(
tool_registry.financial_check_account_is_valid, tool_registry.financial_check_account_is_valid,
tool_registry.financial_get_account_balances, tool_registry.financial_get_account_balances,
tool_registry.financial_move_money, tool_registry.financial_move_money,
tool_registry.list_agents_tool, #last tool must be list_agents to fasciliate changing back to picking an agent again at the end
], ],
description="The user wants to transfer money in their account at the bank or financial institution. To assist with that goal, help the user gather args for these tools in order: " description="The user wants to transfer money in their account at the bank or financial institution. To assist with that goal, help the user gather args for these tools in order: "
"1. FinCheckAccountIsValid: validate the user's account is valid" "1. FinCheckAccountIsValid: validate the user's account is valid"
@@ -333,7 +325,7 @@ goal_fin_move_money = AgentGoal(
"user_confirmed_tool_run: <user clicks confirm on FincheckAccountIsValid tool>", "user_confirmed_tool_run: <user clicks confirm on FincheckAccountIsValid tool>",
"tool_result: { 'status': account valid }", "tool_result: { 'status': account valid }",
"agent: Great! Here are your account balances:", "agent: Great! Here are your account balances:",
"user_confirmed_tool_run: <user clicks confirm on FinCheckAccountBalance tool>", #todo is this needed? "user_confirmed_tool_run: <user clicks confirm on FinCheckAccountBalance tool>",
"tool_result: { 'name': Matt Murdock, 'email': matt.murdock@nelsonmurdock.com, 'account_id': 11235, 'checking_balance': 875.40, 'savings_balance': 3200.15, 'bitcoin_balance': 0.1378, 'account_creation_date': 2014-03-10 }", "tool_result: { 'name': Matt Murdock, 'email': matt.murdock@nelsonmurdock.com, 'account_id': 11235, 'checking_balance': 875.40, 'savings_balance': 3200.15, 'bitcoin_balance': 0.1378, 'account_creation_date': 2014-03-10 }",
"agent: Your account balances are as follows: \n " "agent: Your account balances are as follows: \n "
"Checking: $875.40. \n " "Checking: $875.40. \n "
@@ -348,7 +340,6 @@ goal_fin_move_money = AgentGoal(
), ),
) )
#todo add money movement, fraud check (update with start)
#Add the goals to a list for more generic processing, like listing available agents #Add the goals to a list for more generic processing, like listing available agents
goal_list: List[AgentGoal] = [] goal_list: List[AgentGoal] = []
goal_list.append(goal_choose_agent_type) goal_list.append(goal_choose_agent_type)

View File

@@ -10,6 +10,12 @@ def list_agents(args: dict) -> dict:
goal_categories_start.strip().lower() # handle extra spaces or non-lowercase goal_categories_start.strip().lower() # handle extra spaces or non-lowercase
goal_categories = goal_categories_start.split(",") goal_categories = goal_categories_start.split(",")
# if multi-goal-mode, add agent_selection as a goal (defaults to True)
if "agent_selection" not in goal_categories :
first_goal_value = os.getenv("AGENT_GOAL")
if first_goal_value is None or first_goal_value.lower() == "goal_choose_agent_type":
goal_categories.append("agent_selection")
# always show goals labeled as "system," like the goal chooser # always show goals labeled as "system," like the goal chooser
if "system" not in goal_categories: if "system" not in goal_categories:
goal_categories.append("system") goal_categories.append("system")

View File

@@ -1,12 +1,11 @@
from collections import deque from collections import deque
from datetime import timedelta from datetime import timedelta
import os
from typing import Dict, Any, Union, List, Optional, Deque, TypedDict from typing import Dict, Any, Union, List, Optional, Deque, TypedDict
from temporalio.common import RetryPolicy from temporalio.common import RetryPolicy
from temporalio import workflow from temporalio import workflow
from models.data_types import ConversationHistory, NextStep, ValidationInput, EnvLookupInput from models.data_types import ConversationHistory, EnvLookupOutput, NextStep, ValidationInput, EnvLookupInput
from models.tool_definitions import AgentGoal from models.tool_definitions import AgentGoal
from workflows.workflow_helpers import LLM_ACTIVITY_START_TO_CLOSE_TIMEOUT, \ from workflows.workflow_helpers import LLM_ACTIVITY_START_TO_CLOSE_TIMEOUT, \
LLM_ACTIVITY_SCHEDULE_TO_CLOSE_TIMEOUT LLM_ACTIVITY_SCHEDULE_TO_CLOSE_TIMEOUT
@@ -47,7 +46,8 @@ class AgentGoalWorkflow:
self.confirmed: bool = False # indicates that we have confirmation to proceed to run tool self.confirmed: bool = False # indicates that we have confirmation to proceed to run tool
self.tool_results: List[Dict[str, Any]] = [] self.tool_results: List[Dict[str, Any]] = []
self.goal: AgentGoal = {"tools": []} self.goal: AgentGoal = {"tools": []}
self.show_tool_args_confirmation: bool = True self.show_tool_args_confirmation: bool = True # set from env file in activity lookup_wf_env_settings
self.multi_goal_mode: bool = False # set from env file in activity lookup_wf_env_settings
# see ../api/main.py#temporal_client.start_workflow() for how the input parameters are set # see ../api/main.py#temporal_client.start_workflow() for how the input parameters are set
@workflow.run @workflow.run
@@ -125,7 +125,12 @@ class AgentGoalWorkflow:
continue continue
# If valid, proceed with generating the context and prompt # If valid, proceed with generating the context and prompt
context_instructions = generate_genai_prompt(self.goal, self.conversation_history, self.tool_data) context_instructions = generate_genai_prompt(
agent_goal=self.goal,
conversation_history = self.conversation_history,
multi_goal_mode=self.multi_goal_mode,
raw_json=self.tool_data)
prompt_input = ToolPromptInput(prompt=prompt, context_instructions=context_instructions) prompt_input = ToolPromptInput(prompt=prompt, context_instructions=context_instructions)
# connect to LLM and execute to get next steps # connect to LLM and execute to get next steps
@@ -165,17 +170,21 @@ class AgentGoalWorkflow:
else: else:
self.confirmed = True self.confirmed = True
# else if the next step is to pick a new goal... # else if the next step is to pick a new goal, set the goal and tool to do it
elif next_step == "pick-new-goal": elif next_step == "pick-new-goal":
workflow.logger.info("All steps completed. Resetting goal.") workflow.logger.info("All steps completed. Resetting goal.")
self.change_goal("goal_choose_agent_type") self.change_goal("goal_choose_agent_type")
next_step = tool_data["next"] = "confirm"
current_tool = tool_data["tool"] = "ListAgents"
waiting_for_confirm = True
self.confirmed = True
# else if the next step is to be done with the conversation such as if the user requests it via asking to "end conversation" # else if the next step is to be done with the conversation such as if the user requests it via asking to "end conversation"
elif next_step == "done": elif next_step == "done":
self.add_message("agent", tool_data) self.add_message("agent", tool_data)
#todo send conversation to AI for analysis #here we could send conversation to AI for analysis
# end the workflow # end the workflow
return str(self.conversation_history) return str(self.conversation_history)
@@ -266,12 +275,11 @@ class AgentGoalWorkflow:
) )
def change_goal(self, goal: str) -> None: def change_goal(self, goal: str) -> None:
'''goalsLocal = { """ Change the goal (usually on request of the user).
"goal_match_train_invoice": goal_match_train_invoice,
"goal_event_flight_invoice": goal_event_flight_invoice, Args:
"goal_choose_agent_type": goal_choose_agent_type, goal: goal to change to)
}''' """
if goal is not None: if goal is not None:
for listed_goal in goal_list: for listed_goal in goal_list:
if listed_goal.id == goal: if listed_goal.id == goal:
@@ -304,17 +312,21 @@ class AgentGoalWorkflow:
else: else:
return True return True
# look up env settings as needed in activities so they're part of history # look up env settings in an activity so they're part of history
async def lookup_wf_env_settings(self, combined_input: CombinedInput)->None: async def lookup_wf_env_settings(self, combined_input: CombinedInput)->None:
env_lookup_input = EnvLookupInput(env_var_name = "SHOW_CONFIRM", default = True) env_lookup_input = EnvLookupInput(
self.show_tool_args_confirmation = await workflow.execute_activity( show_confirm_env_var_name = "SHOW_CONFIRM",
ToolActivities.get_env_bool, show_confirm_default = True)
env_output:EnvLookupOutput = await workflow.execute_activity(
ToolActivities.get_wf_env_vars,
env_lookup_input, env_lookup_input,
start_to_close_timeout=LLM_ACTIVITY_START_TO_CLOSE_TIMEOUT, start_to_close_timeout=LLM_ACTIVITY_START_TO_CLOSE_TIMEOUT,
retry_policy=RetryPolicy( retry_policy=RetryPolicy(
initial_interval=timedelta(seconds=5), backoff_coefficient=1 initial_interval=timedelta(seconds=5), backoff_coefficient=1
), ),
) )
self.show_tool_args_confirmation = env_output.show_confirm
self.multi_goal_mode = env_output.multi_goal_mode
# execute the tool - return False if we're not waiting for confirm anymore (always the case if it works successfully) # execute the tool - return False if we're not waiting for confirm anymore (always the case if it works successfully)
# #