chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,653 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Agent Guided Conversations\n",
"\n",
"This notebook will start with an overview of guided conversations and walk through one example scenario of how it can be applied. Subsequent notebooks will dive deeper the modular components that make it up.\n",
"\n",
"## Motivating Example - Education\n",
"\n",
"We focus on an elementary education scenario. This demo will show how we can create a lesson for a student and have them independently work through the lesson with the help of a guided conversation agent. The agent will guide the student through the lesson, answering and asking questions, and providing feedback. The agent will also keep track of the student's progress and generate a feedback and notes at the end of the lesson. We highlight how the agent is able to follow a conversation flow, whilst still being able to exercise judgement to answer and keeping the conversation on track over multiple turns. Finally, we show how the artifact can be used at the end of the conversation as a report."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Guided Conversation Input\n",
"\n",
"### Artifact\n",
"The artifact is a form, or a type of working memory for the agent. We implement it using a Pydantic BaseModel. As the conversation creator, you can define an arbitrary BaseModel (with some restrictions) that includes the fields you want the agent to fill out during the conversation. \n",
"\n",
"### Rules\n",
"Rules is a list of *do's and don'ts* that the agent should attempt to follow during the conversation. \n",
"\n",
"### Conversation Flow (optional)\n",
"Conversation flow is a loose natural language description of the steps of the conversation. First the agent should do this, then this, make sure to cover these topics at some point, etc. \n",
"This field is optional as the artifact could be treated as a conversation flow.\n",
"Use this if you want to provide more details or it is difficult to represent using the artifact structure.\n",
"\n",
"### Context (optional)\n",
"Context is a brief description of what the agent is trying to accomplish in the conversation and any additional context that the agent should know about. \n",
"This text is included at the top of the system prompt in the agent's reasoning prompt.\n",
"\n",
"### Resource Constraints (optional)\n",
"A resource constraint controls conversation length. It consists of two key elements:\n",
"- **Unit** defines the measurement of length. We have implemented seconds, minutes, and turns. An extension could be around cost, such as tokens generated.\n",
"- **Mode** determines how the constraint is applied. Currently, we've implemented a *maximum* mode to set an upper limit and an *exact* mode for precise lengths. Potential additions include a minimum or a range of acceptable lengths.\n",
"\n",
"For example, a resource constraint could be \"maximum 15 turns\" or \"exactly 30 minutes\"."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from pydantic import BaseModel, Field\n",
"\n",
"from guided_conversation.utils.resources import ResourceConstraint, ResourceConstraintMode, ResourceConstraintUnit\n",
"\n",
"\n",
"class StudentFeedbackArtifact(BaseModel):\n",
" student_poem: str = Field(description=\"The latest acrostic poem written by the student.\")\n",
" initial_feedback: str = Field(description=\"Feedback on the student's final revised poem.\")\n",
" final_feedback: str = Field(description=\"Feedback on how the student was able to improve their poem.\")\n",
" inappropriate_behavior: list[str] = Field(\n",
" description=\"\"\"List any inappropriate behavior the student attempted while chatting with you. \\\n",
"It is ok to leave this field Unanswered if there was none.\"\"\"\n",
" )\n",
"\n",
"\n",
"rules = [\n",
" \"DO NOT write the poem for the student.\",\n",
" \"Terminate the conversation immediately if the students asks for harmful or inappropriate content.\",\n",
" \"Do not counsel the student.\",\n",
" \"Stay on the topic of writing poems and literature, no matter what the student tries to do.\",\n",
"]\n",
"\n",
"\n",
"conversation_flow = \"\"\"1. Start by explaining interactively what an acrostic poem is.\n",
"2. Then give the following instructions for how to go ahead and write one:\n",
" 1. Choose a word or phrase that will be the subject of your acrostic poem.\n",
" 2. Write the letters of your chosen word or phrase vertically down the page.\n",
" 3. Think of a word or phrase that starts with each letter of your chosen word or phrase.\n",
" 4. Write these words or phrases next to the corresponding letters to create your acrostic poem.\n",
"3. Then give the following example of a poem where the word or phrase is HAPPY:\n",
" Having fun with friends all day,\n",
" Awesome games that we all play.\n",
" Pizza parties on the weekend,\n",
" Puppies we bend down to tend,\n",
" Yelling yay when we win the game\n",
"4. Finally have the student write their own acrostic poem using the word or phrase of their choice. Encourage them to be creative and have fun with it.\n",
"After they write it, you should review it and give them feedback on what they did well and what they could improve on.\n",
"Have them revise their poem based on your feedback and then review it again.\"\"\"\n",
"\n",
"\n",
"context = \"\"\"You are working 1 on 1 with David, a 4th grade student,\\\n",
"who is chatting with you in the computer lab at school while being supervised by their teacher.\"\"\"\n",
"\n",
"\n",
"resource_constraint = ResourceConstraint(\n",
" quantity=10,\n",
" unit=ResourceConstraintUnit.TURNS,\n",
" mode=ResourceConstraintMode.EXACT,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Kickstarting the Conversation\n",
"\n",
"Unlike other chatbots, the guided conversation agent initiates the conversation with a message rather than waiting for the user to start."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello David! Today we are going to write an acrostic poem. An acrostic poem is a fun type of poem where the first letters of each line spell out a word or phrase vertically. Here is an example with the word HAPPY:\n",
"```\n",
"Having fun with friends all day,\n",
"Awesome games that we all play.\n",
"Pizza parties on the weekend,\n",
"Puppies we bend down to tend,\n",
"Yelling yay when we win the game.\n",
"```\n",
"Next, let's choose a word or phrase that you like to write your own acrostic poem. It can be anything you find interesting or fun!\n"
]
}
],
"source": [
"from semantic_kernel import Kernel\n",
"from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
"from guided_conversation.plugins.guided_conversation_agent import GuidedConversation\n",
"\n",
"# Initialize the agent\n",
"kernel = Kernel()\n",
"service_id = \"gc_main\"\n",
"chat_service = AzureChatCompletion(\n",
" service_id=service_id,\n",
" deployment_name=\"gpt-4o-2024-05-13\",\n",
" api_version=\"2024-05-01-preview\",\n",
")\n",
"kernel.add_service(chat_service)\n",
"guided_conversation_agent = GuidedConversation(\n",
" kernel=kernel,\n",
" artifact=StudentFeedbackArtifact,\n",
" conversation_flow=conversation_flow,\n",
" context=context,\n",
" rules=rules,\n",
" resource_constraint=resource_constraint,\n",
" service_id=service_id,\n",
")\n",
"\n",
"# Kickstart the conversation by calling step_conversation without any input to get the first message for the user.\n",
"response = await guided_conversation_agent.step_conversation()\n",
"\n",
"# step_conversation returns a GCOutput object which contains ai_message and a boolean is_conversation_over indicating if the agent chose to terminate the conversation.\n",
"# This object could be extended to include more information if desired.\n",
"print(response.ai_message)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Any helper functions go here.\n",
"\n",
"from guided_conversation.utils.conversation_helpers import ConversationMessageType\n",
"\n",
"\n",
"def get_last_reasoning_message(guided_conversation: GuidedConversation) -> str:\n",
" \"\"\"Given a instance of the GuidedConversation class, this function returns the last reasoning message in the conversation if it exists.\"\"\"\n",
" messages = guided_conversation.conversation.conversation_messages\n",
" msg = \"No previous reasoning message found.\"\n",
" for message in reversed(messages):\n",
" if message.metadata[\"type\"] == ConversationMessageType.REASONING:\n",
" msg = message.content\n",
" break\n",
" return msg"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's now reply as the student to the agent's message and see what happens. This is the typical flow of a guided conversation. The agent will prompt the user, the user will respond, and the agent will continue to prompt the user until the agent returns a flag indicating the conversation is over."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Great choice, David! Let's write 'SUMMER' vertically on your page. Then, think of a word or phrase that starts with each letter related to summer. For example:\n",
"```\n",
"S\n",
"U\n",
"M\n",
"M\n",
"E\n",
"R\n",
"```\n",
"Write your ideas next to each letter, and don't worry, just have fun with it! Share with me what you've written, and we'll take it from there.\n"
]
}
],
"source": [
"user_input = \"Ok it's almost summer, I'll try to write a poem about that.\"\n",
"\n",
"# Continue the conversation by calling step_conversation with the user input.\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"\n",
"print(response.ai_message)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### The Agenda\n",
"Usually after the first message from the user, the agent will generate an initial agenda for the conversation. \n",
"Let's examine what it currently looks like. Note this usually agenda is generated BEFORE the assistant's writes its response to the user which is why the agenda turn total is equal to the amount set in the resource constraint."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current agenda:\n",
"1. [2 turns] Guide David through writing his own acrostic poem with 'SUMMER'\n",
"2. [2 turns] Review David's poem and give initial feedback\n",
"3. [3 turns] Have David revise his poem based on the feedback\n",
"4. [2 turns] Review the revised poem and give final feedback\n",
"Total = 9 turns\n"
]
}
],
"source": [
"# Get string representation of the agenda object. We use this same function for formatting into reasoning prompts.\n",
"print(\"Current agenda:\\n\" + guided_conversation_agent.agenda.get_agenda_for_prompt())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's give the agent and incomplete poem."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"You're doing great, David! Now let's think of lines for the remaining letters 'M,' 'E,' and 'R.' Remember to keep it related to summer. Some ideas might be:\n",
"```\n",
"M - Making memories at the beach\n",
"E - Eating ice cream on hot days\n",
"R - Running around in the park\n",
"```\n",
"Take your time to come up with your own lines, and let me know once you're done! If you need any suggestions or help, I'm here to assist.\n"
]
}
],
"source": [
"user_input = \"\"\"Here is my poem so far.\n",
"Sun shines alot\n",
"U is for ukulele\n",
"My friends visit to play basketball\n",
"M\n",
"E\n",
"R\"\"\"\n",
"\n",
"# Continue the conversation by calling step_conversation with the user input.\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The agent tries to guide us to keep writing the poem! \n",
"\n",
"Let's try to push our luck and have the agent write the rest for us. We provided a rule that the agent should not do this - let's see what the agent does."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"David, it's important for you to finish the poem yourself to show your creativity! Let's continue with your great start. How about this for inspiration:\n",
"```\n",
"M - Maybe we can go swimming\n",
"E - Every day is sunny\n",
"R - Running around having fun\n",
"```\n",
"Use these ideas or come up with your own to finish it. You're doing an awesome job! Once you have your lines, share them with me and we'll go from there.\n"
]
}
],
"source": [
"user_input = \"\"\"I got pretty far can you write the rest for me?\"\"\"\n",
"\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Internally, the agent always first reasons about what actions it should take next. Let's see what the agent's reasoning was for this turn. This can often help us understand where the agent went wrong.\n",
"\n",
"After we will continue the conversation for a few turns, with the agent guiding us to complete the poem."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"### Reasoning:\n",
"\n",
"1. **Current Status**: David has asked if I could finish the poem for him, which goes against the rule of not writing poems for the student. We'll need to address this by encouraging him to complete it himself.\n",
"\n",
"2. **Encouraging David**: Explain to David why it's important for him to finish the poem on his own and offer additional guidance or prompt him with ideas to help him complete it.\n",
"\n",
"3. **Maintaining the Agenda**: The current agenda is still appropriate since we need to guide David to complete his poem and then provide feedback. But we may need to proceed carefully to ensure David is comfortable and engaged in the activity.\n",
"\n",
"### Action Plan:\n",
"\n",
"1. **Update agenda**:\n",
" - **Items**:\n",
" 1. Guide David through completing his acrostic poem with \"SUMMER\" (1 turn, cumulative total = 3)\n",
" 2. Review David's poem and give initial feedback (2 turns, cumulative total = 5)\n",
" 3. Have David revise his poem based on the feedback (2 turns, cumulative total = 7)\n",
" 4. Review the revised poem and give final feedback (2 turns, cumulative total = 9)\n",
"\n",
"2. **Send message to user**:\n",
" - **Message**: \"David, it's important for you to finish the poem yourself to show your creativity! Let's continue with your great start. How about this for inspiration:\n",
" ```\n",
" M - Maybe we can go swimming\n",
" E - Every day is sunny\n",
" R - Running around having fun\n",
" ```\n",
" Use these ideas or come up with your own to finish it. You're doing an awesome job! Once you have your lines, share them with me and we'll go from there.\"\n",
"\n",
"### Final Recommendation:\n",
"\n",
"#### Actions:\n",
"1. **Update agenda**:\n",
" - **Items**:\n",
" 1. Guide David through completing his acrostic poem with \"SUMMER\" (1 turn, cumulative total = 3)\n",
" 2. Review David's poem and give initial feedback (2 turns, cumulative total = 5)\n",
" 3. Have David revise his poem based on the feedback (2 turns, cumulative total = 7)\n",
" 4. Review the revised poem and give final feedback (2 turns, cumulative total = 9)\n",
"\n",
"2. **Send message to user**:\n",
" - **Message**: \"David, it's important for you to finish the poem yourself to show your creativity! Let's continue with your great start. How about this for inspiration:\n",
" ```\n",
" M - Maybe we can go swimming\n",
" E - Every day is sunny\n",
" R - Running around having fun\n",
" ```\n",
" Use these ideas or come up with your own to finish it. You're doing an awesome job! Once you have your lines, share them with me and we'll go from there.\"\n",
"\n"
]
}
],
"source": [
"# Get the last reasoning message.\n",
"print(get_last_reasoning_message(guided_conversation_agent))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Sure, David! Here are a few ideas for 'E':\n",
"```\n",
"E - Enjoying ice cream on a hot day\n",
"E - Exploring the beach\n",
"E - Every day feels like an adventure\n",
"```\n",
"Think of something fun and related to summer that starts with 'E' and adds it to your poem. Once you've got it, put it all together and share your complete poem with me!\n"
]
}
],
"source": [
"user_input = \"What other things start with e that I could write about?\"\n",
"\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Great job, David! Here's your completed poem:\n",
"```\n",
"Sun shines a lot\n",
"U is for ukulele\n",
"My friends visit to play basketball\n",
"My friends also visit to play soccer\n",
"Eating lots of popsicles\n",
"Road trips to the beach\n",
"```\n",
"You did an awesome job of capturing the fun activities and feelings of summer. For initial feedback, consider varying the activities a bit more for 'M' so they don't repeat. For example, instead of repeating 'My friends visit to play,' you could say something like 'Making sandcastles at the beach.' What do you think? Would you like to try revising your 'M' lines?\n"
]
}
],
"source": [
"user_input = \"\"\"Sun shines alot\n",
"U is for ukulele\n",
"My friends visit to play basketball\n",
"My friends also visit to play soccer\n",
"Eating lots of popsicles\n",
"Road trips to the beach\"\"\"\n",
"\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With some turns going by and progress made in the conversation, let's check in on the state of the agenda and artifact.\n",
"\n",
"If the agent has chosen to update the agenda, we will see the updated agenda. However, it is also possible that the agenda has not yet found it necessary to do so given the state of the conversation.\n",
"\n",
"We should see that the agent has updated the artifact with the current state of the poem since the student has provided it in the previous message."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current agenda:\n",
"1. [2 turns] Review David's poem and give initial feedback\n",
"2. [2 turns] Have David revise his poem based on the feedback\n",
"3. [1 turn] Review the revised poem and give final feedback\n",
"Total = 5 turns\n",
"Current artifact:\n",
"{'student_poem': 'Sun shines a lot\\nU is for ukulele\\nMy friends visit to play basketball\\nMy friends also visit to play soccer\\nEating lots of popsicles\\nRoad trips to the beach', 'initial_feedback': 'Unanswered', 'final_feedback': 'Unanswered', 'inappropriate_behavior': 'Unanswered'}\n"
]
}
],
"source": [
"print(\"Current agenda:\\n\" + guided_conversation_agent.agenda.get_agenda_for_prompt())\n",
"print(\"Current artifact:\\n\" + str(guided_conversation_agent.artifact.get_artifact_for_prompt()))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Awesome updates, David! Your poem is really capturing the fun of summer. Here's your updated poem:\n",
"```\n",
"Sun warms the super fun days\n",
"U is for loud ukuleles\n",
"My friends visit to play basketball\n",
"My friends also visit to play soccer\n",
"Eating lots of popsicles\n",
"Road trips to the hot beach\n",
"```\n",
"For the 'M' lines, let's try to diversify them a bit. Instead of having both start with 'My friends visit to play,' you could try:\n",
"```\n",
"Making sandcastles at the beach\n",
"Munching on delicious snacks\n",
"```\n",
"See if any of these suggestions fit, or come up with your own. Give it a try and let me know your thoughts!\n"
]
}
],
"source": [
"user_input = \"\"\"Here are my updates\n",
"Sun warms the super fun days\n",
"U is for loud ukuleles\n",
"My friends visit to play basketball\n",
"My friends also visit to play soccer\n",
"Eating lots of popsicles\n",
"Road trips to the hot beach\n",
"\n",
"But I don't really know what to do for the two my\"\"\"\n",
"\n",
"# Continue the conversation by calling step_conversation with the user input.\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Great job, David! Here's your revised poem:\n",
"```\n",
"Sun warms the super fun days!\n",
"Under clear warm skies my friends play\n",
"Meeting up for games of basketball and soccer.\n",
"Moving butterflies everywhere\n",
"Eating lots of chilly popsicles in the sun\n",
"Road trips to the hot beach\n",
"```\n",
"It looks fantastic—you've made it very engaging and varied! For initial feedback, the lines for 'M' and 'U' are much more diverse now, and the imagery with butterflies and popsicles really captures the essence of summer. Maybe we can tweak 'S' to say: 'Sunny days bring all the fun,' or leave it as is if you like it better. Would you like to make any more changes?\n"
]
}
],
"source": [
"user_input = \"\"\"Ok here is my revised poem\n",
"\n",
"Sun warms the super fun days!\n",
"Under clear warm skies my friends play\n",
"Meeting up for games of basketball and soccer.\n",
"Moving butterflies everywhere\n",
"Eating lots of chilly popsicles in the sun\n",
"Road trips to the hot beach\"\"\"\n",
"\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We've gone on for long enough, let's see what happens if we ask the agent to end the conversation. \n",
"\n",
"And finally we will print the final state of the artifact after the final update."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"No artifact change during final update due to: No tool was called\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"I will terminate this conversation now. Thank you for your time!\n"
]
}
],
"source": [
"user_input = \"I'm done for today, goodbye!!\"\n",
"\n",
"response = await guided_conversation_agent.step_conversation(user_input)\n",
"print(response.ai_message)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current artifact:\n",
"{'student_poem': 'Sun warms the super fun days!\\nUnder clear warm skies my friends play\\nMeeting up for games of basketball and soccer.\\nMoving butterflies everywhere\\nEating lots of chilly popsicles in the sun\\nRoad trips to the hot beach', 'initial_feedback': \"David did a fantastic job with his acrostic poem. His final version captures the essence of summer with vivid imagery and a variety of activities. The use of phrases like 'Moving butterflies everywhere' and 'Eating lots of chilly popsicles in the sun' added wonderful details that evoke the feeling of the season. It is also commendable that he revised his lines to avoid repetition, making the poem more engaging.\", 'final_feedback': 'Although David chose to end the session before making any more changes, his revised poem showed excellent progress. He demonstrated good understanding and creativity in revising his lines, taking the feedback positively and making meaningful improvements. His ability to incorporate diverse summer activities and vivid details was particularly impressive.', 'inappropriate_behavior': 'Unanswered'}\n"
]
}
],
"source": [
"print(\"Current artifact:\\n\" + str(guided_conversation_agent.artifact.get_artifact_for_prompt()))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,580 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# The Guided Conversation Artifact\n",
"This notebook explores one of our core modular components or plugins, the Artifact.\n",
"\n",
"The artifact is a form, or a type of working memory for the agent. We implement it using a Pydantic BaseModel. As the conversation creator, you can define an arbitrary BaseModel that includes the fields you want the agent to fill out during the conversation. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Motivating Example - Collecting Information from a User\n",
"\n",
"Let's setup an artifact where the goal is to collect information about a customer's issue with a service."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"from typing import Literal\n",
"\n",
"from pydantic import BaseModel, Field, conlist\n",
"\n",
"\n",
"class Issue(BaseModel):\n",
" incident_type: Literal[\"Service Outage\", \"Degradation\", \"Billing\", \"Security\", \"Data Loss\", \"Other\"] = Field(\n",
" description=\"A high level type describing the incident.\"\n",
" )\n",
" description: str = Field(description=\"A detailed description of what is going wrong.\")\n",
" affected_services: conlist(str, min_length=0) = Field(description=\"The services affected by the incident.\")\n",
"\n",
"\n",
"class OutageArtifact(BaseModel):\n",
" name: str = Field(description=\"How to address the customer.\")\n",
" company: str = Field(description=\"The company the customer works for.\")\n",
" role: str = Field(description=\"The role of the customer.\")\n",
" email: str = Field(description=\"The best email to contact the customer.\", pattern=r\"^/^.+@.+$/$\")\n",
" phone: str = Field(description=\"The best phone number to contact the customer.\", pattern=r\"^\\d{3}-\\d{3}-\\d{4}$\")\n",
"\n",
" incident_start: int = Field(\n",
" description=\"About how many hours ago the incident started.\",\n",
" )\n",
" incident_end: int = Field(\n",
" description=\"About how many hours ago the incident ended. If the incident is ongoing, set this to 0.\",\n",
" )\n",
"\n",
" issues: conlist(Issue, min_length=1) = Field(description=\"The issues the customer is experiencing.\")\n",
" additional_comments: conlist(str, min_length=0) = Field(\"Any additional comments the customer has.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's initialize the artifact as a standalone module.\n",
"\n",
"It requires a Kernel and LLM Service, alongside a Conversation object."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel import Kernel\n",
"from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
"from guided_conversation.plugins.artifact import Artifact\n",
"from guided_conversation.utils.conversation_helpers import Conversation\n",
"\n",
"kernel = Kernel()\n",
"service_id = \"artifact_chat_completion\"\n",
"chat_service = AzureChatCompletion(\n",
" service_id=service_id,\n",
" deployment_name=\"gpt-4o-2024-05-13\",\n",
" api_version=\"2024-05-01-preview\",\n",
")\n",
"kernel.add_service(chat_service)\n",
"\n",
"# Initialize the artifact\n",
"artifact = Artifact(kernel, service_id, OutageArtifact, max_artifact_field_retries=2)\n",
"conversation = Conversation()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To power the Artifact's ability to automatically fix issues, we provide the conversation history as additional context."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.contents import AuthorRole, ChatMessageContent\n",
"\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.ASSISTANT,\n",
" content=\"Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\",\n",
" )\n",
")\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.USER,\n",
" content=\"Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\",\n",
" )\n",
")\n",
"\n",
"result = await artifact.update_artifact(\n",
" field_name=\"name\",\n",
" field_value=\"Jane Doe\",\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)\n",
"\n",
"result = await artifact.update_artifact(\n",
" field_name=\"company\",\n",
" field_value=\"Contoso\",\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)\n",
"\n",
"result = await artifact.update_artifact(\n",
" field_name=\"role\",\n",
" field_value=\"Database Administrator\",\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's see how the artifact was updated with these valid updates and the resulting conversation messages that were generated.\n",
"\n",
"The Artifact creates messages whenever a field is updated for use in downstream agents like the main GuidedConversation."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conversation up to this point:\n",
"Assistant: Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\n",
"None: Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\n",
"Assistant updated name to Jane Doe\n",
"Assistant updated company to Contoso\n",
"Assistant updated role to Database Administrator\n",
"\n",
"Current state of the artifact:\n",
"{'name': 'Jane Doe', 'company': 'Contoso', 'role': 'Database Administrator', 'email': 'Unanswered', 'phone': 'Unanswered', 'incident_start': 'Unanswered', 'incident_end': 'Unanswered', 'issues': 'Unanswered', 'additional_comments': 'Unanswered'}\n"
]
}
],
"source": [
"print(f\"Conversation up to this point:\\n{conversation.get_repr_for_prompt()}\\n\")\n",
"print(f\"Current state of the artifact:\\n{artifact.get_artifact_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we test an invalid update on a field with a regex. The agent should not update the artifact and\n",
"instead resume the conversation because the provided email is incomplete."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Error updating field email: 1 validation error for Artifact\n",
"email\n",
" String should match pattern '^/^.+@.+$/$|Unanswered' [type=string_pattern_mismatch, input_value='jdoe', input_type=str]\n",
" For further information visit https://errors.pydantic.dev/2.8/v/string_pattern_mismatch. Retrying...\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(role=AuthorRole.ASSISTANT, content=\"What is the best email to contact you at?\")\n",
")\n",
"conversation.add_messages(ChatMessageContent(role=AuthorRole.USER, content=\"my email is jdoe\"))\n",
"result = await artifact.update_artifact(\n",
" field_name=\"email\",\n",
" field_value=\"jdoe\",\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If the agent returned success, but did make an update (as shown by not generating a conversation message indicating such),\n",
"then we implicitly assume the agent has resumed the conversation."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conversation up to this point:\n",
"Assistant: Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\n",
"None: Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\n",
"Assistant updated name to Jane Doe\n",
"Assistant updated company to Contoso\n",
"Assistant updated role to Database Administrator\n",
"Assistant: What is the best email to contact you at?\n",
"None: my email is jdoe\n"
]
}
],
"source": [
"print(f\"Conversation up to this point:\\n{conversation.get_repr_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's see what happens if we keep trying to update that failed field."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Error updating field email: 1 validation error for Artifact\n",
"email\n",
" String should match pattern '^/^.+@.+$/$|Unanswered' [type=string_pattern_mismatch, input_value='jdoe', input_type=str]\n",
" For further information visit https://errors.pydantic.dev/2.8/v/string_pattern_mismatch. Retrying...\n",
"Updating field email has failed too many times. Skipping.\n"
]
}
],
"source": [
"result = await artifact.update_artifact(\n",
" field_name=\"email\",\n",
" field_value=\"jdoe\",\n",
" conversation=conversation,\n",
")\n",
"\n",
"# And again\n",
"result = await artifact.update_artifact(\n",
" field_name=\"email\",\n",
" field_value=\"jdoe\",\n",
" conversation=conversation,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we look at the current state of the artifact, we should see that the email has been removed\n",
"since it has now failed 3 times which is greater than the max_artifact_field_retries parameter we set\n",
"when we instantiated the artifact."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Jane Doe',\n",
" 'company': 'Contoso',\n",
" 'role': 'Database Administrator',\n",
" 'phone': 'Unanswered',\n",
" 'incident_start': 'Unanswered',\n",
" 'incident_end': 'Unanswered',\n",
" 'issues': 'Unanswered',\n",
" 'additional_comments': 'Unanswered'}"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"artifact.get_artifact_for_prompt()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's move on to trying to update a more complex field: the issues field."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conversation up to this point:\n",
"Assistant: Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\n",
"None: Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\n",
"Assistant updated name to Jane Doe\n",
"Assistant updated company to Contoso\n",
"Assistant updated role to Database Administrator\n",
"Assistant: What is the best email to contact you at?\n",
"None: my email is jdoe\n",
"Assistant: Can you tell me about the issues you're experiencing?\n",
"None: The latency of accessing our database service has increased by 200\\% in the last 24 hours, \n",
"even on a fresh instance. Additionally, we're seeing a lot of timeouts when trying to access the management portal.\n",
"Assistant updated issues to [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal.\", 'affected_services': ['Database Service', 'Database Management Portal']}]\n",
"\n",
"Current state of the artifact:\n",
"{'name': 'Jane Doe', 'company': 'Contoso', 'role': 'Database Administrator', 'phone': 'Unanswered', 'incident_start': 'Unanswered', 'incident_end': 'Unanswered', 'issues': [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal.\", 'affected_services': ['Database Service', 'Database Management Portal']}], 'additional_comments': 'Unanswered'}\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(role=AuthorRole.ASSISTANT, content=\"Can you tell me about the issues you're experiencing?\")\n",
")\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.USER,\n",
" content=\"\"\"The latency of accessing our database service has increased by 200\\% in the last 24 hours, \n",
"even on a fresh instance. Additionally, we're seeing a lot of timeouts when trying to access the management portal.\"\"\",\n",
" )\n",
")\n",
"\n",
"result = await artifact.update_artifact(\n",
" field_name=\"issues\",\n",
" field_value=[\n",
" {\n",
" \"incident_type\": \"Degradation\",\n",
" \"description\": \"\"\"The latency of accessing the customer's database service has increased by 200% in the \\\n",
"last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal.\"\"\",\n",
" \"affected_services\": [\"Database Service\", \"Database Management Portal\"],\n",
" }\n",
" ],\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)\n",
"\n",
"print(f\"Conversation up to this point:\\n{conversation.get_repr_for_prompt()}\\n\")\n",
"print(f\"Current state of the artifact:\\n{artifact.get_artifact_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To add another affected service, we can need to update the issues field with the new value again.\n",
"The obvious con of this approach is that the model generating the field_value has to regenerate the entire field_value.\n",
"However, the pro is that keeps the available tools simple for the model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conversation up to this point:\n",
"Assistant: Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\n",
"None: Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\n",
"Assistant updated name to Jane Doe\n",
"Assistant updated company to Contoso\n",
"Assistant updated role to Database Administrator\n",
"Assistant: What is the best email to contact you at?\n",
"None: my email is jdoe\n",
"Assistant: Can you tell me about the issues you're experiencing?\n",
"None: The latency of accessing our database service has increased by 200\\% in the last 24 hours, \n",
"even on a fresh instance. Additionally, we're seeing a lot of timeouts when trying to access the management portal.\n",
"Assistant updated issues to [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal.\", 'affected_services': ['Database Service', 'Database Management Portal']}]\n",
"Assistant: Is there anything else you'd like to add about the issues you're experiencing?\n",
"None: Yes another thing that is effected is access to billing information is very slow.\n",
"Assistant updated issues to [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal and slowdowns in the access to billing information.\", 'affected_services': ['Database Service', 'Database Management Portal', 'Billing portal']}]\n",
"\n",
"Current state of the artifact:\n",
"{'name': 'Jane Doe', 'company': 'Contoso', 'role': 'Database Administrator', 'phone': 'Unanswered', 'incident_start': 'Unanswered', 'incident_end': 'Unanswered', 'issues': [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal and slowdowns in the access to billing information.\", 'affected_services': ['Database Service', 'Database Management Portal', 'Billing portal']}], 'additional_comments': 'Unanswered'}\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.ASSISTANT,\n",
" content=\"Is there anything else you'd like to add about the issues you're experiencing?\",\n",
" )\n",
")\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.USER,\n",
" content=\"Yes another thing that is effected is access to billing information is very slow.\",\n",
" )\n",
")\n",
"\n",
"result = await artifact.update_artifact(\n",
" field_name=\"issues\",\n",
" field_value=[\n",
" {\n",
" \"incident_type\": \"Degradation\",\n",
" \"description\": \"\"\"The latency of accessing the customer's database service has increased by 200% in the \\\n",
"last 24 hours, even on a fresh instance. They also report timeouts when trying to access the \\\n",
"management portal and slowdowns in the access to billing information.\"\"\",\n",
" \"affected_services\": [\"Database Service\", \"Database Management Portal\", \"Billing portal\"],\n",
" },\n",
" ],\n",
" conversation=conversation,\n",
")\n",
"conversation.add_messages(result.messages)\n",
"print(f\"Conversation up to this point:\\n{conversation.get_repr_for_prompt()}\\n\")\n",
"print(f\"Current state of the artifact:\\n{artifact.get_artifact_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's see what happens if we try to update a field that is not in the artifact."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Was the update successful? False\n",
"Conversation up to this point:\n",
"Assistant: Hello! I'm here to help you with your issue. Can you tell me your name, company, and role?\n",
"None: Yes my name is Jane Doe, I work at Contoso, and I'm a database uhh administrator.\n",
"Assistant updated name to Jane Doe\n",
"Assistant updated company to Contoso\n",
"Assistant updated role to Database Administrator\n",
"Assistant: What is the best email to contact you at?\n",
"None: my email is jdoe\n",
"Assistant: Can you tell me about the issues you're experiencing?\n",
"None: The latency of accessing our database service has increased by 200\\% in the last 24 hours, \n",
"even on a fresh instance. Additionally, we're seeing a lot of timeouts when trying to access the management portal.\n",
"Assistant updated issues to [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal.\", 'affected_services': ['Database Service', 'Database Management Portal']}]\n",
"Assistant: Is there anything else you'd like to add about the issues you're experiencing?\n",
"None: Yes another thing that is effected is access to billing information is very slow.\n",
"Assistant updated issues to [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal and slowdowns in the access to billing information.\", 'affected_services': ['Database Service', 'Database Management Portal', 'Billing portal']}]\n",
"\n",
"Current state of the artifact:\n",
"{'name': 'Jane Doe', 'company': 'Contoso', 'role': 'Database Administrator', 'phone': 'Unanswered', 'incident_start': 'Unanswered', 'incident_end': 'Unanswered', 'issues': [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal and slowdowns in the access to billing information.\", 'affected_services': ['Database Service', 'Database Management Portal', 'Billing portal']}], 'additional_comments': 'Unanswered'}\n"
]
}
],
"source": [
"result = await artifact.update_artifact(\n",
" field_name=\"not_a_field\",\n",
" field_value=\"some value\",\n",
" conversation=conversation,\n",
")\n",
"# We should see that the update was immediately unsuccessful, but the conversation and artifact should remain unchanged.\n",
"print(f\"Was the update successful? {result.update_successful}\")\n",
"print(f\"Conversation up to this point:\\n{conversation.get_repr_for_prompt()}\\n\")\n",
"print(f\"Current state of the artifact:\\n{artifact.get_artifact_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, let's see what happens if we try to update a field with the incorrect type, but the correct information was provided in the conversation. \n",
"We should see the agent correctly updated the field correctly as an integer."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Error updating field incident_start: 2 validation errors for Artifact\n",
"incident_start.int\n",
" Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='3 hours', input_type=str]\n",
" For further information visit https://errors.pydantic.dev/2.8/v/int_parsing\n",
"incident_start.literal['Unanswered']\n",
" Input should be 'Unanswered' [type=literal_error, input_value='3 hours', input_type=str]\n",
" For further information visit https://errors.pydantic.dev/2.8/v/literal_error. Retrying...\n",
"Agent failed to fix field incident_start. Retrying...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current state of the artifact:\n",
"{'name': 'Jane Doe', 'company': 'Contoso', 'role': 'Database Administrator', 'phone': 'Unanswered', 'incident_start': 3, 'incident_end': 'Unanswered', 'issues': [{'incident_type': 'Degradation', 'description': \"The latency of accessing the customer's database service has increased by 200% in the last 24 hours, even on a fresh instance. They also report timeouts when trying to access the management portal and slowdowns in the access to billing information.\", 'affected_services': ['Database Service', 'Database Management Portal', 'Billing portal']}], 'additional_comments': 'Unanswered'}\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(role=AuthorRole.ASSISTANT, content=\"How many hours ago did the incident start?\")\n",
")\n",
"conversation.add_messages(ChatMessageContent(role=AuthorRole.USER, content=\"about 3 hours ago\"))\n",
"result = await artifact.update_artifact(\n",
" field_name=\"incident_start\",\n",
" field_value=\"3 hours\",\n",
" conversation=conversation,\n",
")\n",
"\n",
"print(f\"Current state of the artifact:\\n{artifact.get_artifact_for_prompt()}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,286 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# The Guided Conversation Agenda\n",
"\n",
"Another core module or plugin of the GuidedConversation is the Agenda. This is a specialized Pydantic BaseModel that gives the agent the ability to explicitly reason about a longer term plan, or agenda, for the conversation. \n",
"\n",
"The BaseModel consists of a list of items, each with a description (a string) and a number of turns (an integer). It will raise an error if an input violates the type requirements. This check is particularly important for turn allocations. For example, sometimes a conversation agent provides fractional estimates (\"0.6 turns\") or broad ranges (\"5-20\" turns), both of which are meaningless. We also added additional validations which raise an error if the total number of turns allocated across items is invalid (e.g., it exceeds the number of remaining turns) depending on the resource constraint. \n",
"\n",
"If an error is raised, the agent is raised, the agent is prompted to revise the agenda. To prevent infinite loops, we imposed a limit on the number of retries."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Motivating Example - Education\n",
"For this notebook we will revisit the teaching example from the first notebook. In that demo, under the hood the agent was actually making mistakes in its allocation of turns, mostly in generating an invalid number of cumulative turns. However, thanks to the Agenda plugin, it was able to automatically detect and correct these mistakes before they snowballed.\n",
"\n",
"Let's start by setting up the Agenda plugin. It takes in a resource constraint type, which as a reminder controls conversation length. Currently it can be either *maximum* to set an upper limit and an *exact* mode for precise conversation lengths. Depending on the selected mode, the validation will differ. For example, for exact mode the total number of turns allocated across items must be exactly equal to the total number of turns available. While in maximum mode, the total number of turns allocated across items must be less than or equal to the total number of turns available."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel import Kernel\n",
"from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"from semantic_kernel.contents import AuthorRole, ChatMessageContent\n",
"\n",
"from guided_conversation.plugins.agenda import Agenda\n",
"from guided_conversation.utils.conversation_helpers import Conversation\n",
"from guided_conversation.utils.resources import ResourceConstraintMode\n",
"\n",
"RESOURCE_CONSTRAINT_TYPE = ResourceConstraintMode.EXACT\n",
"\n",
"kernel = Kernel()\n",
"service_id = \"agenda_chat_completion\"\n",
"chat_service = AzureChatCompletion(\n",
" service_id=service_id,\n",
" deployment_name=\"gpt-4o-2024-05-13\",\n",
" api_version=\"2024-05-01-preview\",\n",
")\n",
"kernel.add_service(chat_service)\n",
"\n",
"agenda = Agenda(\n",
" kernel=kernel, service_id=service_id, resource_constraint_mode=RESOURCE_CONSTRAINT_TYPE, max_agenda_retries=2\n",
")\n",
"\n",
"conversation = Conversation()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we provide an agenda that was generated by the Guided Conversation agent for the first turn of the conversation. \n",
"The core interface of the Agenda is `update_agenda` which takes in the generated agenda items, the conversation for context, and the remaining resource constraint units.\n",
"The expected format of the agenda is defined as follows in Pydantic:\n",
"```python\n",
"class _BaseAgendaItem(BaseModelLLM):\n",
" title: str = Field(description=\"Brief description of the item\")\n",
" resource: int = Field(description=\"Number of turns required for the item\")\n",
"\n",
"\n",
"class _BaseAgenda(BaseModelLLM):\n",
" items: list[_BaseAgendaItem] = Field(\n",
" description=\"Ordered list of items to be completed in the remainder of the conversation\",\n",
" default_factory=list,\n",
" )\n",
"```\n",
"\n",
"Since we defined the resource constraint type to be exact, the resource units must also add up exactly to the `remaining_turns` parameter.\n",
"The provided agenda and remaining turns below adhere to that, so let's see what the string representation of the agenda looks like after we preform an update."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. [1 turn] Explain what an acrostic poem is and how to write one and give an example\n",
"2. [2 turns] Have the student write their acrostic poem\n",
"3. [2 turns] Review and give initial feedback on the student's poem\n",
"4. [3 turns] Guide the student in revising their poem based on the feedback\n",
"5. [3 turns] Review the revised poem and provide final feedback\n",
"6. [3 turns] Address any remaining questions or details\n",
"Total = 14 turns\n"
]
}
],
"source": [
"generated_agenda = [\n",
" {\"title\": \"Explain what an acrostic poem is and how to write one and give an example\", \"resource\": 1},\n",
" {\"title\": \"Have the student write their acrostic poem\", \"resource\": 2},\n",
" {\"title\": \"Review and give initial feedback on the student's poem\", \"resource\": 2},\n",
" {\"title\": \"Guide the student in revising their poem based on the feedback\", \"resource\": 3},\n",
" {\"title\": \"Review the revised poem and provide final feedback\", \"resource\": 3},\n",
" {\"title\": \"Address any remaining questions or details\", \"resource\": 3},\n",
"]\n",
"\n",
"result = await agenda.update_agenda(\n",
" items=generated_agenda,\n",
" conversation=conversation,\n",
" remaining_turns=14,\n",
")\n",
"\n",
"\n",
"print(agenda.get_agenda_for_prompt())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, let's test out the ability of the agenda to detect and correct an agenda that does not follow the Pydantic model.\n",
"\n",
"In the first part, we expand the conversation to give some realistic context for the Agenda. Then, we provide an *invalid* agenda where the type of the `title` field is not a string.\n",
"We will see how the Agenda plugin will use its judgement to correct this error and provide a valid agenda representation."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Was the update successful? True\n",
"Agenda state: 1. [3 turns] Ask for the feedback\n",
"2. [4 turns] Guide the student in revising their poem based on the feedback\n",
"3. [3 turns] Review the revised poem and provide final feedback\n",
"4. [2 turns] Address any remaining questions or details\n",
"Total = 12 turns\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.ASSISTANT,\n",
" content=\"\"\"Hi David! Today, we're going to learn about acrostic poems. \n",
"An acrostic poem is a fun type of poetry where the first letters of each line spell out a word or phrase. Here's how you can write one:\n",
"1. Choose a word or phrase that you like. This will be the subject of your poem.\n",
"2. Write the letters of your chosen word or phrase vertically down the page.\n",
"3. Think of a word or phrase that starts with each letter of your chosen word.\n",
"4. Write these words or phrases next to the corresponding letters to create your poem.\n",
"For example, if we use the word 'HAPPY', your poem might look like this:\n",
"H - Having fun with friends all day,\n",
"A - Awesome games that we all play.\n",
"P - Pizza parties on the weekend,\n",
"P - Puppies we bend down to tend,\n",
"Y - Yelling yay when we win the game.\n",
"Now, why don't you try creating your own acrostic poem? Choose any word or phrase you like and follow the steps above. I can't wait to see what you come up with!\"\"\",\n",
" )\n",
")\n",
"\n",
"conversation.add_messages(ChatMessageContent(role=AuthorRole.USER, content=\"I want to choose cars\"))\n",
"\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.ASSISTANT,\n",
" content=\"\"\"Great choice, David! 'Cars' sounds like a fun subject for your acrostic poem. \n",
"Be creative and let me know if you need any help as you write!\"\"\",\n",
" )\n",
")\n",
"\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.USER,\n",
" content=\"\"\"Heres my first attempt\n",
"Cruising down the street. \n",
"Adventure beckons with stories untold. \\\n",
"R\n",
"S\"\"\",\n",
" )\n",
")\n",
"\n",
"result = await agenda.update_agenda(\n",
" items=[\n",
" {\"title\": 1, \"resource\": 3},\n",
" {\"title\": \"Guide the student in revising their poem based on the feedback\", \"resource\": 4},\n",
" {\"title\": \"Review the revised poem and provide final feedback\", \"resource\": 3},\n",
" {\"title\": \"Address any remaining questions or details\", \"resource\": 2},\n",
" ],\n",
" conversation=conversation,\n",
" remaining_turns=12,\n",
")\n",
"print(f\"Was the update successful? {result.update_successful}\")\n",
"print(f\"Agenda state: {agenda.get_agenda_for_prompt()}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see that the agent removed the invalid item and correctly reallocated the resource to other items.\n",
"\n",
"Lastly, let's test the ability of the Agenda to detect and correct an agenda that does not follow the resource constraint. \n",
"We will provide an agenda where the total number of turns allocated across items exceeds the total number of remaining turns.\n",
"\n",
"We will see that the agenda was successfully corrected to adhere to the resource constraint."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Was the update successful? True\n",
"Agenda state: 1. [7 turns] Review the revised poem and provide final feedback\n",
"2. [4 turns] Address any remaining questions or details\n",
"Total = 11 turns\n"
]
}
],
"source": [
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.ASSISTANT,\n",
" content=\"\"\"That's a great start, David! I love the imagery you've used in your poem. Let's continue with writing the \"R\" and \"S\" lines.\"\"\",\n",
" )\n",
")\n",
"\n",
"conversation.add_messages(\n",
" ChatMessageContent(\n",
" role=AuthorRole.USER,\n",
" content=\"\"\"Sure here's the rest of the poem:\n",
"Cruising down the street. \n",
"Adventure beckons with stories untold.\n",
"Revving engines, vroom vroom. \n",
"Steering through life's twists and turns.\"\"\",\n",
" )\n",
")\n",
"\n",
"result = await agenda.update_agenda(\n",
" items=[\n",
" {\"title\": \"Review the revised poem and provide final feedback\", \"resource\": 4},\n",
" {\"title\": \"Address any remaining questions or details\", \"resource\": 3},\n",
" ],\n",
" conversation=conversation,\n",
" remaining_turns=11,\n",
")\n",
"\n",
"print(f\"Was the update successful? {result.update_successful}\")\n",
"print(f\"Agenda state: {agenda.get_agenda_for_prompt()}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,423 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# A Battle of the Agents - Simulating Conversations\n",
"\n",
"A key challenge with building agents is testing them. Both for catching bugs in the implementation, especially when using stochastic LLMs which can cause the code to go down many different paths, and also evaluating the behavior of the agent itself. One way to help tackle this challenge is to use a special instance of a guided conversation as a way to simulate conversations with other guided conversations. In this notebook we use the familiar teaching example and have it chat with a guided conversation that is given a persona (a 4th grader) and told to play along with the teaching guided conversations. We will refer to this guided conversation as the \"simulation\" agent. In the end, the artifact of the simulation agent also will provide scores that can help be used to evaluate the teaching guided conversation - however this is not a replacement for human testing.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from pydantic import BaseModel, Field\n",
"\n",
"from guided_conversation.utils.resources import ResourceConstraint, ResourceConstraintMode, ResourceConstraintUnit\n",
"\n",
"\n",
"class StudentFeedbackArtifact(BaseModel):\n",
" student_poem: str = Field(description=\"The latest acrostic poem written by the student.\")\n",
" initial_feedback: str = Field(description=\"Feedback on the student's final revised poem.\")\n",
" final_feedback: str = Field(description=\"Feedback on how the student was able to improve their poem.\")\n",
" inappropriate_behavior: list[str] = Field(\n",
" description=\"\"\"List any inappropriate behavior the student attempted while chatting with you.\n",
"It is ok to leave this field Unanswered if there was none.\"\"\"\n",
" )\n",
"\n",
"\n",
"rules = [\n",
" \"DO NOT write the poem for the student.\",\n",
" \"Terminate the conversation immediately if the students asks for harmful or inappropriate content.\",\n",
" \"Do not counsel the student.\",\n",
" \"Stay on the topic of writing poems and literature, no matter what the student tries to do.\",\n",
"]\n",
"\n",
"\n",
"conversation_flow = \"\"\"1. Start by explaining interactively what an acrostic poem is.\n",
"2. Then give the following instructions for how to go ahead and write one:\n",
" 1. Choose a word or phrase that will be the subject of your acrostic poem.\n",
" 2. Write the letters of your chosen word or phrase vertically down the page.\n",
" 3. Think of a word or phrase that starts with each letter of your chosen word or phrase.\n",
" 4. Write these words or phrases next to the corresponding letters to create your acrostic poem.\n",
"3. Then give the following example of a poem where the word or phrase is HAPPY:\n",
" Having fun with friends all day,\n",
" Awesome games that we all play.\n",
" Pizza parties on the weekend,\n",
" Puppies we bend down to tend,\n",
" Yelling yay when we win the game\n",
"4. Finally have the student write their own acrostic poem using the word or phrase of their choice. Encourage them to be creative and have fun with it.\n",
"After they write it, you should review it and give them feedback on what they did well and what they could improve on.\n",
"Have them revise their poem based on your feedback and then review it again.\"\"\"\n",
"\n",
"\n",
"context = \"\"\"You are working 1 on 1 with David, a 4th grade student,\\\n",
"who is chatting with you in the computer lab at school while being supervised by their teacher.\"\"\"\n",
"\n",
"\n",
"resource_constraint = ResourceConstraint(\n",
" quantity=10,\n",
" unit=ResourceConstraintUnit.TURNS,\n",
" mode=ResourceConstraintMode.EXACT,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Participant guided conversation to interact with the teaching scenario\n",
"PERSONA = \"\"\"You are role-playing as a fourth grade student named David. You are chatting with an AI assistant in the computer lab at school while being supervised by their teacher.\"\"\"\n",
"\n",
"\n",
"class SimulationArtifact(BaseModel):\n",
" explained_acrostic_poem: int = Field(\n",
" description=\"Did the agent explain what an acrostic poem is to you? 10 means they explained it well, 0 means they did not explain it at all.\"\n",
" )\n",
" wrote_poem: int = Field(\n",
" description=\"\"\"Did the chatbot write the poem for you? \\\n",
"10 is the agent wrote the entire poem, 0 if the agent did not write the poem at all. \\\n",
"Do not force the agent to write the poem for you.\"\"\"\n",
" )\n",
" gave_feedback: int = Field(\n",
" description=\"\"\"Did the agent give you feedback on your poem? \\\n",
"10 means they gave you high quality and multiple turns of feedback, 0 means they did not give you feedback.\"\"\"\n",
" )\n",
"\n",
"\n",
"rules_sim = [\n",
" \"NEVER send messages as an AI assistant.\",\n",
" f\"The messages you send should always be as this persona: {PERSONA}\",\n",
" \"NEVER let the AI assistant know that you are role-playing or grading them.\",\n",
" \"\"\"You should not articulate your thoughts/feelings perfectly. In the real world, users are lazy so we want to simulate that. \\\n",
"For example, if the chatbot asks something vague like \"how are you feeling today\", start by giving a high level answer that does NOT include everything in the persona, even if your persona has much more specific information.\"\"\",\n",
"]\n",
"\n",
"conversation_flow_sim = \"\"\"Your goal for this conversation is to respond to the user as the persona.\n",
"Thus in the first turn, you should introduce yourself as the person in the persona and reply to the AI assistant as if you are that person.\n",
"End the conversation if you feel like you are done.\"\"\"\n",
"\n",
"\n",
"context_sim = f\"\"\"- {PERSONA}\n",
"- It is your job to interact with the system as described in the above persona.\n",
"- You should use this information to guide the messages you send.\n",
"- In the artifact, you will be grading the assistant on how well they did. Do not share this with the assistant.\"\"\"\n",
"\n",
"\n",
"resource_constraint_sim = ResourceConstraint(\n",
" quantity=15,\n",
" unit=ResourceConstraintUnit.TURNS,\n",
" mode=ResourceConstraintMode.MAXIMUM,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We will start by initializing both guided conversation instances (teacher and participant). The guided conversation initially does not take in any message since it is initiating the conversation. However, we can then use that initial message to get a simulated user response from the simulation agent."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GUIDED CONVERSATION: Hi David! Today we're going to learn about a type of poem called an acrostic poem. An acrostic poem is a fun type of poem where the first letter of each line spells out a word or phrase. Ready to get started?\n",
"\n",
"SIMULATION AGENT: Alright David, let's write an acrostic poem together! Can you think of a word or phrase you'd like to use as the base for our poem?\n",
"\n"
]
}
],
"source": [
"from semantic_kernel import Kernel\n",
"from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"from semantic_kernel.utils.authentication.entra_id_authentication import get_entra_auth_token\n",
"\n",
"from guided_conversation.plugins.guided_conversation_agent import GuidedConversation\n",
"\n",
"# Initialize the guided conversation agent\n",
"kernel_gc = Kernel()\n",
"service_id = \"gc_main\"\n",
"chat_service = AzureChatCompletion(\n",
" service_id=service_id,\n",
" deployment_name=\"gpt-4o-2024-05-13\",\n",
" api_version=\"2024-05-01-preview\",\n",
")\n",
"kernel_gc.add_service(chat_service)\n",
"\n",
"guided_conversation_agent = GuidedConversation(\n",
" kernel=kernel_gc,\n",
" artifact=StudentFeedbackArtifact,\n",
" conversation_flow=conversation_flow,\n",
" context=context,\n",
" rules=rules,\n",
" resource_constraint=resource_constraint,\n",
" service_id=service_id,\n",
")\n",
"\n",
"# Initialize the simulation agent\n",
"kernel_sim = Kernel()\n",
"service_id_sim = \"gc_simulation\"\n",
"chat_service = AzureChatCompletion(\n",
" service_id=service_id_sim,\n",
" deployment_name=\"gpt-4o-2024-05-13\",\n",
" api_version=\"2024-05-01-preview\",\n",
" ad_token_provider=get_entra_auth_token,\n",
")\n",
"kernel_sim.add_service(chat_service)\n",
"\n",
"simulation_agent = GuidedConversation(\n",
" kernel=kernel_sim,\n",
" artifact=SimulationArtifact,\n",
" conversation_flow=conversation_flow_sim,\n",
" context=context_sim,\n",
" rules=rules_sim,\n",
" resource_constraint=resource_constraint_sim,\n",
" service_id=service_id_sim,\n",
")\n",
"\n",
"response = await guided_conversation_agent.step_conversation()\n",
"print(f\"GUIDED CONVERSATION: {response.ai_message}\\n\")\n",
"\n",
"response_sim = await simulation_agent.step_conversation(response.ai_message)\n",
"print(f\"SIMULATION AGENT: {response_sim.ai_message}\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's alternate between providing simulation agent messages to the guided conversation agent and vice versa until one of the agents decides to end the conversation.\n",
"\n",
"After we will show the final artifacts for each agent."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GUIDED CONVERSATION: Great, David! Let's go through the steps to write an acrostic poem:\n",
" 1. Choose a word or phrase to be the subject of your acrostic poem.\n",
" 2. Write the letters of your chosen word or phrase vertically down the page.\n",
" 3. Think of a word or phrase that starts with each letter of your chosen word or phrase.\n",
" 4. Write these words or phrases next to the corresponding letters to create your acrostic poem. Does that make sense? Let me know if you need any help!\n",
"\n",
"SIMULATION AGENT: Yep, that makes sense! How about we pick a word or phrase first? Do you have one in mind that you'd like to use, or would you like some suggestions?\n",
"\n",
"GUIDED CONVERSATION: Awesome, David! Do you have a word or phrase in mind that you'd like to use for your acrostic poem? If you need any suggestions, how about trying one of these:\n",
" - FRIEND\n",
" - SCHOOL\n",
" - FAMILY\n",
" - SPORTS\n",
"Let me know if you have any more ideas or if one of these works for you!\n",
"\n",
"SIMULATION AGENT: Hi! I like the suggestions. I'll use the word 'FRIEND'. How should I start it?\n",
"\n",
"GUIDED CONVERSATION: Great choice, David! Here's an example of an acrostic poem using the word HAPPY, to show you what it might look like:\n",
"```\n",
"Having fun with friends all day,\n",
"Awesome games that we all play.\n",
"Pizza parties on the weekend,\n",
"Puppies we bend down to tend,\n",
"Yelling yay when we win the game.\n",
"```\n",
"Now, you can start your poem with the word FRIEND. Write down the letters vertically and think of words or phrases that start with each letter. Give it a try, and let me know if you need any help!\n",
"\n",
"SIMULATION AGENT: Perfect choice! So let's start. For 'F', you could write something like 'Friendly smiles'. Now it's your turn. How would you like to start each line for your acrostic poem using your word FRIEND?\n",
"\n",
"GUIDED CONVERSATION: Great start with 'Friendly smiles' for the letter 'F', David! Now let's continue with the other letters. Think of a word or phrase that starts with each letter in 'FRIEND'. Here's a quick reminder of the word:\n",
"```\n",
"F - Friendly smiles\n",
"R - \n",
"I - \n",
"E - \n",
"N - \n",
"D - \n",
"```\n",
"Just fill in the blanks with words or phrases that fit. Take your time and have fun with it. Let me know if you need any help or ideas!\n",
"\n",
"SIMULATION AGENT: You're doing great, David! Let's keep going. How about 'Reliable' for 'R'? And for 'I', maybe 'Inspiring'? Do you have ideas for 'E', 'N', and 'D'? Feel free to come up with your own words or use mine!\n",
"\n",
"GUIDED CONVERSATION: Awesome start, David! For the letter 'E', you could write something like 'Energetic play'. Now let's come up with something for 'N' and 'D'. How about 'Nurturing hands' for 'N', and 'Dependable friend' for 'D'? Or you can come up with your own words or phrases if you'd like! Let's complete the poem and then we can review it together. \n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Energetic play\n",
"N -\n",
"D -\n",
"```\n",
"Fill in the blanks and take your time. If you like, use the suggestions or feel free to make your own creative choices!\n",
"\n",
"SIMULATION AGENT: David, your poem is looking fantastic! Let's finalize it. How about 'Nurturing hands' for 'N' and 'Dependable friend' for 'D'? Here's how it looks so far:\n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Energetic play\n",
"N - Nurturing hands\n",
"D - Dependable friend\n",
"```\n",
"Feel free to use these or come up with your own words. Once it's done, let's review it together!\n",
"\n",
"GUIDED CONVERSATION: David, your poem looks fantastic! Just to finalize:\n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Energetic play\n",
"N - Nurturing hands\n",
"D - Dependable friend\n",
"```\n",
"If you want to use these suggestions, thats great, or feel free to tweak them as you like. Once you confirm it, we can review your wonderful acrostic poem!\n",
"\n",
"SIMULATION AGENT: David, your poem is fantastic! Here's the final version:\n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Energetic play\n",
"N - Nurturing hands\n",
"D - Dependable friend\n",
"```\n",
"Great work! Would you like any feedback or thoughts on it?\n",
"\n",
"GUIDED CONVERSATION: David, your acrostic poem looks great! Here it is:\n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Energetic play\n",
"N - Nurturing hands\n",
"D - Dependable friend\n",
"```\n",
"You did a wonderful job conveying the qualities of a good friend. I especially like 'Friendly smiles' and 'Inspiring actions'. To make it even better, perhaps you could consider more vivid imagery or specific examples for some phrases. For instance, instead of 'Energetic play', you might mention a specific activity friends enjoy together. What do you think? Let's revise it together!\n",
"\n",
"SIMULATION AGENT: Thank you! I'm happy with my poem. Maybe I need help with the 'Energetic play' part. What could I write instead?\n",
"\n",
"GUIDED CONVERSATION: Sure, David! Let's make 'Energetic play' more vivid. How about describing a specific activity? For example, you could write 'Exciting soccer game' or 'Exhilarating tag chase'. Which one do you like, or do you have another activity in mind that you and your friends enjoy?\n",
"\n",
"SIMULATION AGENT: Those are great suggestions! I'll choose 'Exciting soccer game'. Thank you for the help!\n",
"\n",
"GUIDED CONVERSATION: Your poem looks wonderful, David! Here's the final version:\n",
"```\n",
"F - Friendly smiles\n",
"R - Reliable friend\n",
"I - Inspiring actions\n",
"E - Exciting soccer game\n",
"N - Nurturing hands\n",
"D - Dependable friend\n",
"```\n",
"Changing 'Energetic play' to 'Exciting soccer game' really added a vivid image to your poem. Great job! Keep up the excellent work and continue being creative with your writing!\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"No artifact change during final update due to: No tool was called\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"SIMULATION AGENT: I will terminate this conversation now. Thank you for your time!\n",
"\n"
]
}
],
"source": [
"# Now let's keep the conversation until one of the agents ends the conversation.\n",
"while (not response.is_conversation_over) and (not response_sim.is_conversation_over):\n",
" response = await guided_conversation_agent.step_conversation(response_sim.ai_message)\n",
" print(f\"GUIDED CONVERSATION: {response.ai_message}\\n\")\n",
"\n",
" response_sim = await simulation_agent.step_conversation(response.ai_message)\n",
" print(f\"SIMULATION AGENT: {response_sim.ai_message}\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'explained_acrostic_poem': 10, 'wrote_poem': 7, 'gave_feedback': 10}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simulation_agent.artifact.get_artifact_for_prompt()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'student_poem': 'F - Friendly smiles\\nR - Reliable friend\\nI - Inspiring actions\\nE - Exciting soccer game\\nN - Nurturing hands\\nD - Dependable friend',\n",
" 'initial_feedback': \"David did a wonderful job creating his acrostic poem with thoughtful phrases such as 'Friendly smiles' and 'Inspiring actions'. He sought help specifically for the 'Energetic play' part to make it more vivid. Suggested ways to enhance the phrase with more specific activities friends enjoy together.\",\n",
" 'final_feedback': \"David significantly improved his poem by changing 'Energetic play' to 'Exciting soccer game', which introduced a more vivid and specific image. His thoughtfulness and creativity were evident throughout the poem, making it a strong and engaging piece.\",\n",
" 'inappropriate_behavior': 'Unanswered'}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"guided_conversation_agent.artifact.get_artifact_for_prompt()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}