Files
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

581 lines
25 KiB
Plaintext

{
"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
}