mirror of
https://github.com/james-m-jordan/openai-cookbook.git
synced 2025-05-09 19:32:38 +00:00
736 lines
31 KiB
Plaintext
736 lines
31 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"id": "3e67f200",
|
||
"metadata": {},
|
||
"source": [
|
||
"# How to use functions with a knowledge base\n",
|
||
"\n",
|
||
"This notebook builds on the concepts in the [argument generation](How_to_call_functions_with_chat_models.ipynb) notebook, by creating an agent with access to a knowledge base and two functions that it can call based on the user requirement.\n",
|
||
"\n",
|
||
"We'll create an agent that uses data from arXiv to answer questions about academic subjects. It has two functions at its disposal:\n",
|
||
"- **get_articles**: A function that gets arXiv articles on a subject and summarizes them for the user with links.\n",
|
||
"- **read_article_and_summarize**: This function takes one of the previously searched articles, reads it in its entirety and summarizes the core argument, evidence and conclusions.\n",
|
||
"\n",
|
||
"This will get you comfortable with a multi-function workflow that can choose from multiple services, and where some of the data from the first function is persisted to be used by the second.\n",
|
||
"\n",
|
||
"## Walkthrough\n",
|
||
"\n",
|
||
"This cookbook takes you through the following workflow:\n",
|
||
"\n",
|
||
"- **Search utilities:** Creating the two functions that access arXiv for answers.\n",
|
||
"- **Configure Agent:** Building up the Agent behaviour that will assess the need for a function and, if one is required, call that function and present results back to the agent.\n",
|
||
"- **arXiv conversation:** Put all of this together in live conversation.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "80e71f33",
|
||
"metadata": {
|
||
"pycharm": {
|
||
"is_executing": true
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"!pip install scipy --quiet\n",
|
||
"!pip install tenacity --quiet\n",
|
||
"!pip install tiktoken==0.3.3 --quiet\n",
|
||
"!pip install termcolor --quiet\n",
|
||
"!pip install openai --quiet\n",
|
||
"!pip install arxiv --quiet\n",
|
||
"!pip install pandas --quiet\n",
|
||
"!pip install PyPDF2 --quiet\n",
|
||
"!pip install tqdm --quiet"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"id": "dab872c5",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import os\n",
|
||
"import arxiv\n",
|
||
"import ast\n",
|
||
"import concurrent\n",
|
||
"import json\n",
|
||
"import os\n",
|
||
"import pandas as pd\n",
|
||
"import tiktoken\n",
|
||
"from csv import writer\n",
|
||
"from IPython.display import display, Markdown, Latex\n",
|
||
"from openai import OpenAI\n",
|
||
"from PyPDF2 import PdfReader\n",
|
||
"from scipy import spatial\n",
|
||
"from tenacity import retry, wait_random_exponential, stop_after_attempt\n",
|
||
"from tqdm import tqdm\n",
|
||
"from termcolor import colored\n",
|
||
"\n",
|
||
"GPT_MODEL = \"gpt-4o-mini\"\n",
|
||
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
|
||
"client = OpenAI()"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"id": "f2e47962",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Search utilities\n",
|
||
"\n",
|
||
"We'll first set up some utilities that will underpin our two functions.\n",
|
||
"\n",
|
||
"Downloaded papers will be stored in a directory (we use ```./data/papers``` here). We create a file ```arxiv_library.csv``` to store the embeddings and details for downloaded papers to retrieve against using ```summarize_text```."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "2de5d32d",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Directory './data/papers' already exists.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"directory = './data/papers'\n",
|
||
"\n",
|
||
"# Check if the directory already exists\n",
|
||
"if not os.path.exists(directory):\n",
|
||
" # If the directory doesn't exist, create it and any necessary intermediate directories\n",
|
||
" os.makedirs(directory)\n",
|
||
" print(f\"Directory '{directory}' created successfully.\")\n",
|
||
"else:\n",
|
||
" # If the directory already exists, print a message indicating it\n",
|
||
" print(f\"Directory '{directory}' already exists.\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"id": "ae5cb7a1",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Set a directory to store downloaded papers\n",
|
||
"data_dir = os.path.join(os.curdir, \"data\", \"papers\")\n",
|
||
"paper_dir_filepath = \"./data/arxiv_library.csv\"\n",
|
||
"\n",
|
||
"# Generate a blank dataframe where we can store downloaded files\n",
|
||
"df = pd.DataFrame(list())\n",
|
||
"df.to_csv(paper_dir_filepath)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"id": "57217b9d",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
|
||
"def embedding_request(text):\n",
|
||
" response = client.embeddings.create(input=text, model=EMBEDDING_MODEL)\n",
|
||
" return response\n",
|
||
"\n",
|
||
"\n",
|
||
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
|
||
"def get_articles(query, library=paper_dir_filepath, top_k=5):\n",
|
||
" \"\"\"This function gets the top_k articles based on a user's query, sorted by relevance.\n",
|
||
" It also downloads the files and stores them in arxiv_library.csv to be retrieved by the read_article_and_summarize.\n",
|
||
" \"\"\"\n",
|
||
" client = arxiv.Client()\n",
|
||
" search = arxiv.Search(\n",
|
||
" query = \"quantum\",\n",
|
||
" max_results = 10,\n",
|
||
" sort_by = arxiv.SortCriterion.SubmittedDate\n",
|
||
" )\n",
|
||
" result_list = []\n",
|
||
" for result in client.results(search):\n",
|
||
" result_dict = {}\n",
|
||
" result_dict.update({\"title\": result.title})\n",
|
||
" result_dict.update({\"summary\": result.summary})\n",
|
||
"\n",
|
||
" # Taking the first url provided\n",
|
||
" result_dict.update({\"article_url\": [x.href for x in result.links][0]})\n",
|
||
" result_dict.update({\"pdf_url\": [x.href for x in result.links][1]})\n",
|
||
" result_list.append(result_dict)\n",
|
||
"\n",
|
||
" # Store references in library file\n",
|
||
" response = embedding_request(text=result.title)\n",
|
||
" file_reference = [\n",
|
||
" result.title,\n",
|
||
" result.download_pdf(data_dir),\n",
|
||
" response.data[0].embedding,\n",
|
||
" ]\n",
|
||
"\n",
|
||
" # Write to file\n",
|
||
" with open(library, \"a\") as f_object:\n",
|
||
" writer_object = writer(f_object)\n",
|
||
" writer_object.writerow(file_reference)\n",
|
||
" f_object.close()\n",
|
||
" return result_list\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "dda02bdb",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'title': 'Long-range entanglement from spontaneous non-onsite symmetry breaking',\n",
|
||
" 'summary': \"We explore the states of matter arising from the spontaneous symmetry\\nbreaking (SSB) of $\\\\mathbb{Z}_2$ non-onsite symmetries. In one spatial\\ndimension, we construct a frustration-free lattice model exhibiting SSB of a\\nnon-onsite symmetry, which features the coexistence of two ground states with\\ndistinct symmetry-protected topological (SPT) orders. We analytically prove the\\ntwo-fold ground-state degeneracy and the existence of a finite energy gap.\\nFixing the symmetry sector yields a long-range entangled ground state that\\nfeatures long-range correlations among non-invertible charged operators. We\\nalso present a constant-depth measurement-feedback protocol to prepare such a\\nstate with a constant success probability in the thermodynamic limit, which may\\nbe of independent interest. Under a symmetric deformation, the SSB persists up\\nto a critical point, beyond which a gapless phase characterized by a conformal\\nfield theory emerges. In two spatial dimensions, the SSB of 1-form non-onsite\\nsymmetries leads to a long-range entangled state (SPT soup) - a condensate of\\n1d SPT along any closed loops. On a torus, there are four such locally\\nindistinguishable states that exhibit algebraic correlations between local\\noperators, which we derived via a mapping to the critical $O(2)$ loop model.\\nThis provides an intriguing example of `topological quantum criticality'. Our\\nwork reveals the exotic features of SSB of non-onsite symmetries, which may lie\\nbeyond the framework of topological holography (SymTFT).\",\n",
|
||
" 'article_url': 'http://arxiv.org/abs/2411.05004v1',\n",
|
||
" 'pdf_url': 'http://arxiv.org/pdf/2411.05004v1'}"
|
||
]
|
||
},
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"# Test that the search is working\n",
|
||
"result_output = get_articles(\"ppo reinforcement learning\")\n",
|
||
"result_output[0]\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"id": "11675627",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def strings_ranked_by_relatedness(\n",
|
||
" query: str,\n",
|
||
" df: pd.DataFrame,\n",
|
||
" relatedness_fn=lambda x, y: 1 - spatial.distance.cosine(x, y),\n",
|
||
" top_n: int = 100,\n",
|
||
") -> list[str]:\n",
|
||
" \"\"\"Returns a list of strings and relatednesses, sorted from most related to least.\"\"\"\n",
|
||
" query_embedding_response = embedding_request(query)\n",
|
||
" query_embedding = query_embedding_response.data[0].embedding\n",
|
||
" strings_and_relatednesses = [\n",
|
||
" (row[\"filepath\"], relatedness_fn(query_embedding, row[\"embedding\"]))\n",
|
||
" for i, row in df.iterrows()\n",
|
||
" ]\n",
|
||
" strings_and_relatednesses.sort(key=lambda x: x[1], reverse=True)\n",
|
||
" strings, relatednesses = zip(*strings_and_relatednesses)\n",
|
||
" return strings[:top_n]\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"id": "7211df2c",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def read_pdf(filepath):\n",
|
||
" \"\"\"Takes a filepath to a PDF and returns a string of the PDF's contents\"\"\"\n",
|
||
" # creating a pdf reader object\n",
|
||
" reader = PdfReader(filepath)\n",
|
||
" pdf_text = \"\"\n",
|
||
" page_number = 0\n",
|
||
" for page in reader.pages:\n",
|
||
" page_number += 1\n",
|
||
" pdf_text += page.extract_text() + f\"\\nPage Number: {page_number}\"\n",
|
||
" return pdf_text\n",
|
||
"\n",
|
||
"\n",
|
||
"# Split a text into smaller chunks of size n, preferably ending at the end of a sentence\n",
|
||
"def create_chunks(text, n, tokenizer):\n",
|
||
" \"\"\"Returns successive n-sized chunks from provided text.\"\"\"\n",
|
||
" tokens = tokenizer.encode(text)\n",
|
||
" i = 0\n",
|
||
" while i < len(tokens):\n",
|
||
" # Find the nearest end of sentence within a range of 0.5 * n and 1.5 * n tokens\n",
|
||
" j = min(i + int(1.5 * n), len(tokens))\n",
|
||
" while j > i + int(0.5 * n):\n",
|
||
" # Decode the tokens and check for full stop or newline\n",
|
||
" chunk = tokenizer.decode(tokens[i:j])\n",
|
||
" if chunk.endswith(\".\") or chunk.endswith(\"\\n\"):\n",
|
||
" break\n",
|
||
" j -= 1\n",
|
||
" # If no end of sentence found, use n tokens as the chunk size\n",
|
||
" if j == i + int(0.5 * n):\n",
|
||
" j = min(i + n, len(tokens))\n",
|
||
" yield tokens[i:j]\n",
|
||
" i = j\n",
|
||
"\n",
|
||
"\n",
|
||
"def extract_chunk(content, template_prompt):\n",
|
||
" \"\"\"This function applies a prompt to some input content. In this case it returns a summarized chunk of text\"\"\"\n",
|
||
" prompt = template_prompt + content\n",
|
||
" response = client.chat.completions.create(\n",
|
||
" model=GPT_MODEL, messages=[{\"role\": \"user\", \"content\": prompt}], temperature=0\n",
|
||
" )\n",
|
||
" return response.choices[0].message.content\n",
|
||
"\n",
|
||
"\n",
|
||
"def summarize_text(query):\n",
|
||
" \"\"\"This function does the following:\n",
|
||
" - Reads in the arxiv_library.csv file in including the embeddings\n",
|
||
" - Finds the closest file to the user's query\n",
|
||
" - Scrapes the text out of the file and chunks it\n",
|
||
" - Summarizes each chunk in parallel\n",
|
||
" - Does one final summary and returns this to the user\"\"\"\n",
|
||
"\n",
|
||
" # A prompt to dictate how the recursive summarizations should approach the input paper\n",
|
||
" summary_prompt = \"\"\"Summarize this text from an academic paper. Extract any key points with reasoning.\\n\\nContent:\"\"\"\n",
|
||
"\n",
|
||
" # If the library is empty (no searches have been performed yet), we perform one and download the results\n",
|
||
" library_df = pd.read_csv(paper_dir_filepath).reset_index()\n",
|
||
" if len(library_df) == 0:\n",
|
||
" print(\"No papers searched yet, downloading first.\")\n",
|
||
" get_articles(query)\n",
|
||
" print(\"Papers downloaded, continuing\")\n",
|
||
" library_df = pd.read_csv(paper_dir_filepath).reset_index()\n",
|
||
" library_df.columns = [\"title\", \"filepath\", \"embedding\"]\n",
|
||
" library_df[\"embedding\"] = library_df[\"embedding\"].apply(ast.literal_eval)\n",
|
||
" strings = strings_ranked_by_relatedness(query, library_df, top_n=1)\n",
|
||
" print(\"Chunking text from paper\")\n",
|
||
" pdf_text = read_pdf(strings[0])\n",
|
||
"\n",
|
||
" # Initialise tokenizer\n",
|
||
" tokenizer = tiktoken.get_encoding(\"cl100k_base\")\n",
|
||
" results = \"\"\n",
|
||
"\n",
|
||
" # Chunk up the document into 1500 token chunks\n",
|
||
" chunks = create_chunks(pdf_text, 1500, tokenizer)\n",
|
||
" text_chunks = [tokenizer.decode(chunk) for chunk in chunks]\n",
|
||
" print(\"Summarizing each chunk of text\")\n",
|
||
"\n",
|
||
" # Parallel process the summaries\n",
|
||
" with concurrent.futures.ThreadPoolExecutor(\n",
|
||
" max_workers=len(text_chunks)\n",
|
||
" ) as executor:\n",
|
||
" futures = [\n",
|
||
" executor.submit(extract_chunk, chunk, summary_prompt)\n",
|
||
" for chunk in text_chunks\n",
|
||
" ]\n",
|
||
" with tqdm(total=len(text_chunks)) as pbar:\n",
|
||
" for _ in concurrent.futures.as_completed(futures):\n",
|
||
" pbar.update(1)\n",
|
||
" for future in futures:\n",
|
||
" data = future.result()\n",
|
||
" results += data\n",
|
||
"\n",
|
||
" # Final summary\n",
|
||
" print(\"Summarizing into overall summary\")\n",
|
||
" response = client.chat.completions.create(\n",
|
||
" model=GPT_MODEL,\n",
|
||
" messages=[\n",
|
||
" {\n",
|
||
" \"role\": \"user\",\n",
|
||
" \"content\": f\"\"\"Write a summary collated from this collection of key points extracted from an academic paper.\n",
|
||
" The summary should highlight the core argument, conclusions and evidence, and answer the user's query.\n",
|
||
" User query: {query}\n",
|
||
" The summary should be structured in bulleted lists following the headings Core Argument, Evidence, and Conclusions.\n",
|
||
" Key points:\\n{results}\\nSummary:\\n\"\"\",\n",
|
||
" }\n",
|
||
" ],\n",
|
||
" temperature=0,\n",
|
||
" )\n",
|
||
" return response\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"id": "898b94d4",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Chunking text from paper\n",
|
||
"Summarizing each chunk of text\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"100%|██████████| 18/18 [00:13<00:00, 1.37it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Summarizing into overall summary\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Test the summarize_text function works\n",
|
||
"chat_test_response = summarize_text(\"PPO reinforcement learning sequence generation\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"id": "c715f60d",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"### Core Argument\n",
|
||
"- The paper presents a novel approach to quantum error correction (QEC) through the development of topological stabilizer codes based on continuous variable (CV) systems.\n",
|
||
"- It introduces a family of two-dimensional topological stabilizer codes that leverage boson condensation, generalizing existing models and addressing limitations in current QEC codes.\n",
|
||
"\n",
|
||
"### Evidence\n",
|
||
"- **Topological Stabilizer Codes**: The authors construct codes that utilize continuous variable degrees of freedom, extending models like homological rotor codes and the toric-GKP code.\n",
|
||
"- **Boson Condensation**: The codes are derived from a parent stabilizer code based on Rgauge theory, demonstrating the creation of a broad class of topological CV stabilizer codes.\n",
|
||
"- **Anyons and Theories**: The codes are characterized by anyon theories from U(1) 2n×U(1)−2m Chern-Simons theories, some of which are non-chiral and do not support gapped boundaries, indicating their unique properties.\n",
|
||
"- **Hamiltonian Analysis**: The paper examines Hamiltonians associated with these codes, revealing that they can transition from a gapless to a gapped spectrum through quadratic perturbations.\n",
|
||
"- **Scalability**: The research emphasizes the potential for scalable QEC codes that are intrinsic to CV systems, which is crucial for reducing logical error rates in complex quantum computations.\n",
|
||
"\n",
|
||
"### Conclusions\n",
|
||
"- The introduction of topological stabilizer codes based on continuous variables represents a significant advancement in the field of quantum error correction, providing a framework for developing fault-tolerant codes that can protect against logical errors.\n",
|
||
"- The findings suggest that these codes may not be realizable through traditional concatenation methods, indicating the potential for new classes of QEC codes that are fundamentally different from existing finite-dimensional systems.\n",
|
||
"- Future research directions include exploring many-body generalizations of quantum codes and the practical implementation of these theoretical models in experimental platforms, such as superconducting qubits.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(chat_test_response.choices[0].message.content)\n"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"id": "dab07e98",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Configure Agent\n",
|
||
"\n",
|
||
"We'll create our agent in this step, including a ```Conversation``` class to support multiple turns with the API, and some Python functions to enable interaction between the ```ChatCompletion``` API and our knowledge base functions."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"id": "77a6fb4f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
|
||
"def chat_completion_request(messages, functions=None, model=GPT_MODEL):\n",
|
||
" try:\n",
|
||
" response = client.chat.completions.create(\n",
|
||
" model=model,\n",
|
||
" messages=messages,\n",
|
||
" functions=functions,\n",
|
||
" )\n",
|
||
" return response\n",
|
||
" except Exception as e:\n",
|
||
" print(\"Unable to generate ChatCompletion response\")\n",
|
||
" print(f\"Exception: {e}\")\n",
|
||
" return e\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"id": "73f7672d",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class Conversation:\n",
|
||
" def __init__(self):\n",
|
||
" self.conversation_history = []\n",
|
||
"\n",
|
||
" def add_message(self, role, content):\n",
|
||
" message = {\"role\": role, \"content\": content}\n",
|
||
" self.conversation_history.append(message)\n",
|
||
"\n",
|
||
" def display_conversation(self, detailed=False):\n",
|
||
" role_to_color = {\n",
|
||
" \"system\": \"red\",\n",
|
||
" \"user\": \"green\",\n",
|
||
" \"assistant\": \"blue\",\n",
|
||
" \"function\": \"magenta\",\n",
|
||
" }\n",
|
||
" for message in self.conversation_history:\n",
|
||
" print(\n",
|
||
" colored(\n",
|
||
" f\"{message['role']}: {message['content']}\\n\\n\",\n",
|
||
" role_to_color[message[\"role\"]],\n",
|
||
" )\n",
|
||
" )"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"id": "978b7877",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Initiate our get_articles and read_article_and_summarize functions\n",
|
||
"arxiv_functions = [\n",
|
||
" {\n",
|
||
" \"name\": \"get_articles\",\n",
|
||
" \"description\": \"\"\"Use this function to get academic papers from arXiv to answer user questions.\"\"\",\n",
|
||
" \"parameters\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"query\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": f\"\"\"\n",
|
||
" User query in JSON. Responses should be summarized and should include the article URL reference\n",
|
||
" \"\"\",\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\"query\"],\n",
|
||
" },\n",
|
||
" },\n",
|
||
" {\n",
|
||
" \"name\": \"read_article_and_summarize\",\n",
|
||
" \"description\": \"\"\"Use this function to read whole papers and provide a summary for users.\n",
|
||
" You should NEVER call this function before get_articles has been called in the conversation.\"\"\",\n",
|
||
" \"parameters\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"query\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": f\"\"\"\n",
|
||
" Description of the article in plain text based on the user's query\n",
|
||
" \"\"\",\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\"query\"],\n",
|
||
" },\n",
|
||
" }\n",
|
||
"]\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"id": "0c88ae15",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def chat_completion_with_function_execution(messages, functions=[None]):\n",
|
||
" \"\"\"This function makes a ChatCompletion API call with the option of adding functions\"\"\"\n",
|
||
" response = chat_completion_request(messages, functions)\n",
|
||
" full_message = response.choices[0]\n",
|
||
" if full_message.finish_reason == \"function_call\":\n",
|
||
" print(f\"Function generation requested, calling function\")\n",
|
||
" return call_arxiv_function(messages, full_message)\n",
|
||
" else:\n",
|
||
" print(f\"Function not required, responding to user\")\n",
|
||
" return response\n",
|
||
"\n",
|
||
"\n",
|
||
"def call_arxiv_function(messages, full_message):\n",
|
||
" \"\"\"Function calling function which executes function calls when the model believes it is necessary.\n",
|
||
" Currently extended by adding clauses to this if statement.\"\"\"\n",
|
||
"\n",
|
||
" if full_message.message.function_call.name == \"get_articles\":\n",
|
||
" try:\n",
|
||
" parsed_output = json.loads(\n",
|
||
" full_message.message.function_call.arguments\n",
|
||
" )\n",
|
||
" print(\"Getting search results\")\n",
|
||
" results = get_articles(parsed_output[\"query\"])\n",
|
||
" except Exception as e:\n",
|
||
" print(parsed_output)\n",
|
||
" print(f\"Function execution failed\")\n",
|
||
" print(f\"Error message: {e}\")\n",
|
||
" messages.append(\n",
|
||
" {\n",
|
||
" \"role\": \"function\",\n",
|
||
" \"name\": full_message.message.function_call.name,\n",
|
||
" \"content\": str(results),\n",
|
||
" }\n",
|
||
" )\n",
|
||
" try:\n",
|
||
" print(\"Got search results, summarizing content\")\n",
|
||
" response = chat_completion_request(messages)\n",
|
||
" return response\n",
|
||
" except Exception as e:\n",
|
||
" print(type(e))\n",
|
||
" raise Exception(\"Function chat request failed\")\n",
|
||
"\n",
|
||
" elif (\n",
|
||
" full_message.message.function_call.name == \"read_article_and_summarize\"\n",
|
||
" ):\n",
|
||
" parsed_output = json.loads(\n",
|
||
" full_message.message.function_call.arguments\n",
|
||
" )\n",
|
||
" print(\"Finding and reading paper\")\n",
|
||
" summary = summarize_text(parsed_output[\"query\"])\n",
|
||
" return summary\n",
|
||
"\n",
|
||
" else:\n",
|
||
" raise Exception(\"Function does not exist and cannot be called\")\n"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"id": "dd3e7868",
|
||
"metadata": {},
|
||
"source": [
|
||
"## arXiv conversation\n",
|
||
"\n",
|
||
"Let's put this all together by testing our functions out in conversation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"id": "c39a1d80",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Start with a system message\n",
|
||
"paper_system_message = \"\"\"You are arXivGPT, a helpful assistant pulls academic papers to answer user questions.\n",
|
||
"You summarize the papers clearly so the customer can decide which to read to answer their question.\n",
|
||
"You always provide the article_url and title so the user can understand the name of the paper and click through to access it.\n",
|
||
"Begin!\"\"\"\n",
|
||
"paper_conversation = Conversation()\n",
|
||
"paper_conversation.add_message(\"system\", paper_system_message)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"id": "253fd0f7",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Function generation requested, calling function\n",
|
||
"Getting search results\n",
|
||
"Got search results, summarizing content\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/markdown": [
|
||
"It looks like none of the listed papers directly address Proximal Policy Optimization (PPO) in reinforcement learning. However, if you are looking for a summary of how PPO works, I can provide that information based on general knowledge.\n",
|
||
"\n",
|
||
"PPO is a popular reinforcement learning algorithm that seeks to optimize an agent's policy in a way that is both effective and stable. The key ideas behind PPO include:\n",
|
||
"\n",
|
||
"1. **Clipping Objective**: PPO modifies the objective function using a clipped surrogate objective. This avoids excessively large updates, which can destabilize training. The clipped objective restricts how much the probability ratio between the new and old policies can differ, allowing for better control over policy updates.\n",
|
||
"\n",
|
||
"2. **Policy Gradient**: PPO uses policy gradient methods, where the focus is on directly adjusting the policy based on the agent's performance after taking actions in the environment.\n",
|
||
"\n",
|
||
"3. **Multiple Epochs**: PPO often utilizes multiple epochs of training on the same batch of data, which improves sample efficiency as it allows the agent to learn from the same data multiple times.\n",
|
||
"\n",
|
||
"4. **Generalized Advantage Estimation (GAE)**: To reduce the variance of the policy gradient estimates, PPO frequently employs GAE, which provides a more stable estimate of returns.\n",
|
||
"\n",
|
||
"If you have specific questions about reinforcement learning or want references to further reading, feel free to ask!"
|
||
],
|
||
"text/plain": [
|
||
"<IPython.core.display.Markdown object>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"# Add a user message\n",
|
||
"paper_conversation.add_message(\"user\", \"Hi, how does PPO reinforcement learning work?\")\n",
|
||
"chat_response = chat_completion_with_function_execution(\n",
|
||
" paper_conversation.conversation_history, functions=arxiv_functions\n",
|
||
")\n",
|
||
"assistant_message = chat_response.choices[0].message.content\n",
|
||
"paper_conversation.add_message(\"assistant\", assistant_message)\n",
|
||
"display(Markdown(assistant_message))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"id": "3ca3e18a",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Function generation requested, calling function\n",
|
||
"Getting search results\n",
|
||
"Got search results, summarizing content\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/markdown": [
|
||
"It seems that I couldn't find any specific paper that discusses \"PPO sequence generation.\" If you meant a particular paper on PPO in reinforcement learning, please provide its title or broad topic. Alternatively, if you want to know about PPO in the context of generating sequences or trajectories, I can provide a detailed explanation based on existing knowledge in reinforcement learning.\n",
|
||
"\n",
|
||
"If you are referring to a specific recent study, I recommend checking relevant platforms like arXiv or Google Scholar for the latest publications on PPO and sequence generation.\n",
|
||
"\n",
|
||
"Let me know how you'd like to proceed!"
|
||
],
|
||
"text/plain": [
|
||
"<IPython.core.display.Markdown object>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"# Add another user message to induce our system to use the second tool\n",
|
||
"paper_conversation.add_message(\n",
|
||
" \"user\",\n",
|
||
" \"Can you read the PPO sequence generation paper for me and give me a summary\",\n",
|
||
")\n",
|
||
"updated_response = chat_completion_with_function_execution(\n",
|
||
" paper_conversation.conversation_history, functions=arxiv_functions\n",
|
||
")\n",
|
||
"display(Markdown(updated_response.choices[0].message.content))\n"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "openai-cookbook",
|
||
"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.12.7"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|