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