blob: 4e8d1476aa7615697f89a005416efd6c1e6ef88c [file]
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "40392f86b8ad9a3f",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"!pip install burr[start] openai"
]
},
{
"cell_type": "markdown",
"id": "99997759-30e1-4290-a62f-d606687b0319",
"metadata": {},
"source": [
"# Email assistant\n",
"Developing the email assistant. This is a more complex app with lots of back-and-forth. This demonstrates:\n",
"\n",
"1. How to move in/out of program control -> user control\n",
"2. How to process inputs at multiple points through the process\n",
"3. How to involve multi-shot modeling with an LLM\n",
"\n",
"Note this can easily be extended. While the user provides feedback, for a more complex draft, a multi-shot agent with a \"editor\" agent and a \"writer\" agent could interact until the editor is happy. For an email this is overkill, but for more complex narratives this could be valauble."
]
},
{
"cell_type": "markdown",
"id": "acb89287-02c8-4bfc-8bc8-a1de112e2625",
"metadata": {},
"source": [
"# The Application\n",
"\n",
"We're modeling this as a multi-shot model. No sane person would trust GPT-x to respond on their behalf, \n",
"but it can be quite valuable in helping automate the process of responding to an email and deciding how to respond.\n",
"There are a few interaction points:\n",
"\n",
"1. Initially, the user provides an email and response instructions (note this should probably be changed to be a chain of emails)\n",
"2. The LLM has the option to ask a set of clarifying questions that the user can then respond to\n",
"3. The user then provides feedback. If feedback is empty, the LLM will return a result\n",
"\n",
"This demonstrates how to use Burr to move in/out of more complex flows. \n",
"\n",
"First, we instantiate/visualize the application. To see the code, see [application.py](application.py)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b59e926f-058d-4bac-b97e-ff173d3be7a4",
"metadata": {},
"outputs": [],
"source": [
"from application import application as email_assistant_application"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c7aba1b3-5886-4255-b2f5-b40627896196",
"metadata": {},
"outputs": [],
"source": [
"app = email_assistant_application()\n",
"app.visualize(include_conditions=True, include_state=False)"
]
},
{
"cell_type": "markdown",
"id": "7b69292e-1db9-45d6-9c45-ad1940837cda",
"metadata": {},
"source": [
"# Telemetry\n",
"\n",
"To see the application \"think\", let's open up telemetry. Before running the next cell ensure `burr` is run and you have the UI open. Then navigate to the following link:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6ab6da4d-3011-4fc3-847e-93f948667073",
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import Markdown\n",
"url = f\"[Link to UI](http://localhost:7241/project/demo_email_assistant/{app.uid})\"\n",
"Markdown(url)"
]
},
{
"cell_type": "markdown",
"id": "f3b1cf53-fbbe-42c4-822f-175388f8fb59",
"metadata": {},
"source": [
"# Prompt the machine\n",
"\n",
"I'm going to be asking that the LLM help me turn around a cold-call. We get a lot of these -- I'm curious if it can help me leverage this for OS promotion! Note that this is a dubious (at best) use-case, but hey, these emails are likely written by AI as well.\n",
"\n",
"Change these to your use-case:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f724b1e-e282-4e8d-9c13-8e218ee04e87",
"metadata": {},
"outputs": [],
"source": [
"EMAIL = \"\"\"Hi Elijah,\n",
"\n",
"I hope you are well.\n",
"\n",
"I am connecting again to know your thoughts on my previous email regarding DAGWorks's feature in our forthcoming 8th annual edition on Artificial Intelligence. I want to explain how this recognition will help you attain more qualified prospects and increase your client base.\n",
"\n",
"Our previous clients have seen a visible increase in prospect conversion rate by effectively utilizing this recognition. We would like DAGWorks to also leverage this recognition and witness impressive results.\n",
"\n",
"Please inform me of your availability for a quick chat. I look forward to your kind response.\n",
"\n",
"\n",
"Regards,\n",
"Someone\"\"\"\n",
"\n",
"INSTRUCTIONS = \"\"\"I get a bunch of these emails and usually ignore them. I don't want to pay \n",
"(or really engage) but I want to see if they can help promote our tooling, \n",
"particularly the OS libraries Hamilton and Burr, which are tools for AI practitioners. \n",
"Let's see if we can turn this cold-call around.\n",
"\"\"\""
]
},
{
"cell_type": "markdown",
"id": "02ecf380-0da8-4f25-aa4c-7691c13bf18a",
"metadata": {},
"source": [
"# Prompting for user-response\n",
"\n",
"During execution, there are several points at which the user provides additional feedback. This is:\n",
"\n",
"1. When the LLM has questions for the user (`request_answers`)\n",
"2. When the LLM needs feedback (`request_feedback`)\n",
"\n",
"These are functions we'll call later to ensure you can interact with the LLM. In a web-service this would be the point that we go back and forth (E.G. stop before), and would be replaced by endpoints. We're just using `input()` here as its the simplest way to get user input.\n",
"\n",
"We are going to run this until we get to the `final_result` step. We're going to have two stopping conditions:\n",
"\n",
"- `halt_before`: `clarify_instructions` and `process_feedback` -- these are the two steps that require input. We want to stop before so we can feed input to the user. We will call the above functions.\n",
"- `halt_after` : `final_result` -- this means we're done! We can just print out the draft.\n",
"\n",
"We will break at `final_result`, as the state machine is complete."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c8badad-496f-4d0e-848d-beee0b836243",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The email assistant wants more information:\n"
]
}
],
"source": [
"def request_answers(questions):\n",
" \"\"\"Requests answers from the user for the questions the LLM has\"\"\"\n",
" answers = []\n",
" print(\"The email assistant wants more information:\\n\")\n",
" for question in questions:\n",
" answers.append(input(question))\n",
" return answers\n",
"\n",
"def request_feedback(draft):\n",
" \"\"\"Requests feedback from the user for a draft\"\"\"\n",
" print( \n",
" f\"here's a draft!: \\n {draft} \\n \\n What feedback do you have?\",\n",
" \"If you have no feedback then we'll finish it up.\"\n",
" )\n",
" return input(\"Write feedback or leave blank to continue (if you're happy)\")\n",
"\n",
"inputs = {\n",
" \"email_to_respond\" : EMAIL,\n",
" \"response_instructions\" : INSTRUCTIONS\n",
"}\n",
"while True:\n",
" action, result, state = app.run(\n",
" halt_before=[\"clarify_instructions\", \"process_feedback\"], \n",
" halt_after=[\"final_result\"],\n",
" inputs=inputs\n",
" )\n",
" if action.name == \"clarify_instructions\":\n",
" questions = state[\"clarification_questions\"]\n",
" answers = request_answers(questions)\n",
" inputs = {\n",
" \"clarification_inputs\" : answers\n",
" }\n",
" if action.name == \"process_feedback\":\n",
" feedback = request_feedback(state[\"current_draft\"])\n",
" inputs = {\"feedback\" : feedback}\n",
" if action.name == \"final_result\":\n",
" print(\"final result is:\", state[\"current_draft\"])\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bb34de1f-e9f1-4a49-9204-924b23e783a2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}