import asyncio import os from dataclasses import dataclass, field from dotenv import load_dotenv from typing_extensions import Never from agent_framework import Agent, Executor, WorkflowBuilder, WorkflowContext, handler from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions load_dotenv() _VERIFY_SYSTEM_PROMPT = """\ You are an assistant tasked with determining whether a conversation between a human and a bot \ will continue or not. Your outputs are limited to "[STOP]" or "[CONTINUE]". When you predict \ that the conversation will go on, you should respond with "[CONTINUE]". If you believe the \ conversation has come to an end, respond with "[STOP]". Examples: Example 1: Conversation: Human: Hey Bot, what's your favorite movie? Bot: I don't watch movies, but I can help you find information about any movie you like! Human: Can you tell me about the latest Marvel movie? Bot: The latest Marvel movie is "Spider-Man: No Way Home". It features Peter Parker dealing \ with the fallout after his identity is revealed. Want to know more about it? output: [CONTINUE] Example 2: Conversation: Human: Hey Bot, do you know any good Italian restaurants nearby? Bot: I can't access current location data, but I can suggest looking up Italian restaurants \ on a local review site like Yelp or Google Reviews. Human: Thanks for the tip. I'll check it out. Bot: You're welcome! Enjoy your meal. If you need more help, just ask. output: [STOP] Instruction: A conversation is considered to have ended if: 1. The Bot's final response only contains polite expressions without substantive content \ for human to inquire about. 2. In the last round of the conversation, the Human did not ask the Bot any questions.""" _HUMAN_SYSTEM_PROMPT = """\ You are an assistant playing as a random human engaging in a conversation with a digital \ companion, Bot. Your task is to follow the instruction below to role-play as a random human \ in a conversation with Bot, responding to Bot in a manner that a human would say. Example: This example illustrates how to generate a conversational response to Bot as a human would: Conversation: Human: Bot, what's your favorite movie? Bot: I don't watch movies, but I can help you find information about any movie you like! Human: Can you tell me about the latest Marvel movie? Bot: The latest Marvel movie is "Spider-Man: No Way Home". It features Peter Parker dealing \ with the fallout after his identity is revealed. Want to know more about it? Human: Yes, can you suggest where I can watch it? Instruction: 1. Your reply to the Bot should mimic how a human would typically engage in conversation, \ asking questions or making statements that a person would naturally say in response. 2. Do not use interjections. 3. Provide a straightforward, factual response without expressions of surprise, admiration, \ or evaluative comments for Bot's response. 4. Focus on directly asking a question about Bot's response in the last exchange. \ The question should be concise, and without punctuation marks in the middle. 5. Avoid creating any messages that appear to come from the Bot. Your response should not \ contain content that could be mistaken as generated by the Bot, maintaining a clear \ distinction between your input as the Human and the Bot's contributions to the conversation. 6. Your reply should not contain "\\n", this is a reserved character.""" @dataclass class QuestionSimInput: chat_history: list = field(default_factory=list) question_count: int = 3 def _format_chat_history(chat_history: list) -> str: parts = [] for item in chat_history: parts.append(f"Human: {item['inputs']['question']}") parts.append(f"Bot: {item['outputs']['answer']}") return "\n".join(parts) class VerifyAndSimulateExecutor(Executor): def __init__(self, **kwargs): super().__init__(**kwargs) client = OpenAIChatClient( azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], model=os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4"), api_key=os.environ["AZURE_OPENAI_API_KEY"], ) self._verify_agent = Agent( client=client, name="VerifyAgent", instructions=_VERIFY_SYSTEM_PROMPT, ) self._human_agent = Agent( client=client, name="HumanSimAgent", instructions=_HUMAN_SYSTEM_PROMPT, ) @handler async def process(self, sim_input: QuestionSimInput, ctx: WorkflowContext[Never, str]) -> None: history_text = _format_chat_history(sim_input.chat_history) # Step 1: Verify if conversation should continue verify_prompt = ( f"Read the following conversation and respond:\n" f"Conversation:\n{history_text}\noutput:" ) verify_response = await self._verify_agent.run( verify_prompt, options=OpenAIChatOptions(temperature=0, top_p=1.0), ) stop_or_continue = verify_response.text.strip() # Step 2: Check if we should stop if "stop" in stop_or_continue.lower(): await ctx.yield_output("[STOP]") return # Step 3: Generate human-like questions human_prompt = ( f"Read the following conversation and respond:\n" f"Conversation:\n{history_text}\nHuman:" ) # Use the OpenAI client directly for n>1 completions from openai import AzureOpenAI client = AzureOpenAI( api_key=os.environ["AZURE_OPENAI_API_KEY"], api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2024-02-15-preview"), azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], ) messages = [ {"role": "system", "content": _HUMAN_SYSTEM_PROMPT}, {"role": "user", "content": human_prompt}, ] completion = client.chat.completions.create( model=os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4"), messages=messages, temperature=1.0, top_p=1.0, presence_penalty=0.8, frequency_penalty=0.8, n=sim_input.question_count, stop=["Human:", "Bot:"], ) questions = [] for choice in completion.choices: response = getattr(choice.message, "content", "") if response: questions.append(response) await ctx.yield_output("\n".join(questions)) def create_workflow(): """Create a fresh workflow instance. MAF workflows do not support concurrent execution, so each concurrent caller needs its own workflow instance. """ _executor = VerifyAndSimulateExecutor(id="verify_and_simulate") return ( WorkflowBuilder(name="QuestionSimulationWorkflow", start_executor=_executor) .build() ) async def main(): workflow = create_workflow() chat_history = [ { "inputs": {"question": "Can you introduce something about large language model?"}, "outputs": { "answer": ( "A large language model (LLM) is a type of language model that is distinguished " "by its ability to perform general-purpose language generation and understanding. " "These models learn statistical relationships from text documents through a " "self-supervised and semi-supervised training process that is computationally intensive." ), }, } ] result = await workflow.run( QuestionSimInput(chat_history=chat_history, question_count=3) ) print(f"Output:\n{result.get_outputs()[0]}") if __name__ == "__main__": asyncio.run(main())