Files
wehub-resource-sync e768098d0e
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:52 +08:00

202 lines
7.8 KiB
Python

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())