From e1ddddb9fb289225032f90781acf9fb4c33b8490 Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Thu, 3 Jul 2025 15:30:23 +0400 Subject: [PATCH 1/6] improve: Update learn\generation\langchain\handbook\06-langchain-agents.ipynb --- .../handbook/06-langchain-agents.ipynb | 876 +++++++++++------- 1 file changed, 522 insertions(+), 354 deletions(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index 4acd1e01..b4b8fd1d 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -41,21 +41,25 @@ }, "outputs": [ { + "name": "stderr", "output_type": "stream", - "name": "stdout", "text": [ - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.5/1.5 MB\u001b[0m \u001b[31m10.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.5/75.5 kB\u001b[0m \u001b[31m6.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.4/49.4 kB\u001b[0m \u001b[31m4.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Building wheel for google-search-results (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Building wheel for wikipedia (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + "ERROR: Directory '\\\\' is not installable. Neither 'setup.py' nor 'pyproject.toml' found.\n", + "\n", + "[notice] A new release of pip is available: 23.1.2 -> 25.1.1\n", + "[notice] To update, run: python.exe -m pip install --upgrade pip\n" ] } ], "source": [ - "!pip install -qU langchain openai google-search-results wikipedia sqlalchemy" + "!pip install -qU \\\n", + " langchain==0.3.25 \\\n", + " langchain-openai==0.3.22 \\\n", + " langchain-experimental==0.3.4 \\\n", + " numexpr==2.11.0 \\\n", + " google-search-results==2.4.2 \\\n", + " wikipedia==1.4.0 \\\n", + " sqlalchemy==2.0.41 \\" ] }, { @@ -79,19 +83,15 @@ "id": "c02c4fa2", "outputId": "ad22dddc-6180-48ac-86dd-265b6b7afa57" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "··········\n" - ] - } - ], + "outputs": [], "source": [ + "import os\n", "from getpass import getpass\n", "\n", - "OPENAI_API_KEY = getpass()" + "os.environ[\"OPENAI_API_KEY\"] = os.getenv(\"OPENAI_API_KEY\") \\\n", + " or getpass(\"Enter your OpenAI API key: \")\n", + "\n", + "OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\")" ] }, { @@ -103,11 +103,12 @@ }, "outputs": [], "source": [ - "from langchain import OpenAI\n", + "from langchain_openai import ChatOpenAI\n", "\n", - "llm = OpenAI(\n", + "llm = ChatOpenAI(\n", " openai_api_key=OPENAI_API_KEY,\n", - " temperature=0\n", + " model_name=\"gpt-4.1-mini\",\n", + " temperature=0.0,\n", ")" ] }, @@ -134,7 +135,7 @@ "\n", "def count_tokens(agent, query):\n", " with get_openai_callback() as cb:\n", - " result = agent(query)\n", + " result = agent.invoke(query)\n", " print(f'Spent a total of {cb.total_tokens} tokens')\n", "\n", " return result" @@ -318,66 +319,32 @@ }, { "cell_type": "markdown", - "source": [ - "We are installing the `langchain_experimental` library here, since the `SQLDatabaseChain` is located there. This might be changed in the future and moved into official `langchain` library." - ], + "id": "H3_tIYndeNAt", "metadata": { "id": "H3_tIYndeNAt" }, - "id": "H3_tIYndeNAt" - }, - { - "cell_type": "code", "source": [ - "!pip install langchain_experimental -qU" - ], - "metadata": { - "id": "KJJz9xXvb1Ej", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "9533f802-804d-4c4c-c19f-87827bbc178c" - }, - "id": "KJJz9xXvb1Ej", - "execution_count": 12, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/70.7 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m70.7/70.7 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h" - ] - } + "We are installing the `langchain_experimental` library here, since the `SQLDatabaseChain` is located there. This might be changed in the future and moved into official `langchain` library." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 11, "id": "9721648e", "metadata": { - "id": "9721648e", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "9721648e", "outputId": "9e77c155-c662-47c2-b585-deb88e8ad51b" }, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.10/dist-packages/langchain_experimental/sql/base.py:66: UserWarning: Directly instantiating an SQLDatabaseChain with an llm is deprecated. Please instantiate with llm_chain argument or using the from_llm class method.\n", - " warnings.warn(\n" - ] - } - ], + "outputs": [], "source": [ "from langchain.utilities import SQLDatabase\n", "from langchain_experimental.sql import SQLDatabaseChain\n", "\n", "db = SQLDatabase(engine)\n", - "sql_chain = SQLDatabaseChain(llm=llm, database=db, verbose=True)" + "sql_chain = SQLDatabaseChain.from_llm(llm=llm, db=db, verbose=True)" ] }, { @@ -427,48 +394,54 @@ }, { "cell_type": "markdown", + "id": "Tgn6dRLEcxli", + "metadata": { + "id": "Tgn6dRLEcxli" + }, "source": [ "In this first example we will use slightly different type of agent - SQL Agent which can be instantiated with it's own method `create_sql_agent`. Other agents will be instantiated in more generic way as we will see below in other examples.\n", "

\n", "This method uses *toolkit* instead of simple list of `tools`. You can read more about them in the [documentation](https://python.langchain.com/docs/modules/agents/toolkits/). For this use case, we will use `SQLDatabaseToolkit`." - ], - "metadata": { - "id": "Tgn6dRLEcxli" - }, - "id": "Tgn6dRLEcxli" + ] }, { "cell_type": "markdown", - "source": [ - "As the name suggests, we will use this agent to perform 'zero shot' tasks on the input. That means that we will not have several, interdependent interactions but only one. In other words, this agent will have no memory." - ], + "id": "mie_37ERl_ac", "metadata": { "id": "mie_37ERl_ac" }, - "id": "mie_37ERl_ac" + "source": [ + "As the name suggests, we will use this agent to perform 'zero shot' tasks on the input. That means that we will not have several, interdependent interactions but only one. In other words, this agent will have no memory." + ] }, { "cell_type": "markdown", - "source": [ - "Now we are ready to initialize the agent! We will use `verbose` in `True` so we can see what is our agent's 'thinking' process." - ], + "id": "O_pYBlCcl11l", "metadata": { "id": "O_pYBlCcl11l" }, - "id": "O_pYBlCcl11l" + "source": [ + "Now we are ready to initialize the agent! We will use `verbose` in `True` so we can see what is our agent's 'thinking' process." + ] }, { "cell_type": "markdown", - "source": [ - "**Important Note:** *When interacting with agents it is really important to set the `max_iterations` parameters because agents can get stuck in infinite loops that consume plenty of tokens. The default value is 15 to allow for many tools and complex reasoning but for most applications you should keep it much lower.*" - ], + "id": "GtSMUCaIlupp", "metadata": { "id": "GtSMUCaIlupp" }, - "id": "GtSMUCaIlupp" + "source": [ + "**Important Note:** *When interacting with agents it is really important to set the `max_iterations` parameters because agents can get stuck in infinite loops that consume plenty of tokens. The default value is 15 to allow for many tools and complex reasoning but for most applications you should keep it much lower.*" + ] }, { "cell_type": "code", + "execution_count": 17, + "id": "5Z4EmMmqiOvZ", + "metadata": { + "id": "5Z4EmMmqiOvZ" + }, + "outputs": [], "source": [ "from langchain.agents import create_sql_agent\n", "from langchain.agents.agent_toolkits import SQLDatabaseToolkit\n", @@ -479,36 +452,24 @@ " toolkit=SQLDatabaseToolkit(db=db, llm=llm),\n", " verbose=True,\n", " agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,\n", - " max_iterations=3\n", + " max_iterations=5\n", ")" - ], - "metadata": { - "id": "5Z4EmMmqiOvZ" - }, - "id": "5Z4EmMmqiOvZ", - "execution_count": 14, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "Let's see our newly created agent in action! We will ask it a question that involves a math operation over the stock prices." - ], + "id": "iBqz6aFzj-2d", "metadata": { "id": "iBqz6aFzj-2d" }, - "id": "iBqz6aFzj-2d" + "source": [ + "Let's see our newly created agent in action! We will ask it a question that involves a math operation over the stock prices." + ] }, { "cell_type": "code", - "source": [ - "result = count_tokens(\n", - " agent_executor,\n", - " \"What is the multiplication of the ratio between stock \" +\n", - " \"prices for 'ABC' and 'XYZ' in January 3rd and the ratio \" +\n", - " \"between the same stock prices in January the 4th?\"\n", - ")" - ], + "execution_count": 18, + "id": "MdvgpwHRic3W", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -516,23 +477,18 @@ "id": "MdvgpwHRic3W", "outputId": "5e63199b-2d92-404a-c712-5e8f4f55e23d" }, - "id": "MdvgpwHRic3W", - "execution_count": 15, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "\n", "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[1m> Entering new SQL Agent Executor chain...\u001b[0m\n", "\u001b[32;1m\u001b[1;3mAction: sql_db_list_tables\n", - "Action Input: \u001b[0m\n", - "Observation: \u001b[38;5;200m\u001b[1;3mstocks\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m I should query the schema of the stocks table to see what columns I can use.\n", + "Action Input: \u001b[0m\u001b[38;5;200m\u001b[1;3mstocks\u001b[0m\u001b[32;1m\u001b[1;3mThought: There is a table named \"stocks\" which likely contains stock prices. I should check the schema of the \"stocks\" table to understand its columns and see how to query the stock prices for 'ABC' and 'XYZ' on January 3rd and January 4th.\n", "Action: sql_db_schema\n", - "Action Input: stocks\u001b[0m\n", - "Observation: \u001b[33;1m\u001b[1;3m\n", + "Action Input: stocks\u001b[0m\u001b[33;1m\u001b[1;3m\n", "CREATE TABLE stocks (\n", "\tobs_id INTEGER NOT NULL, \n", "\tstock_ticker VARCHAR(4) NOT NULL, \n", @@ -547,47 +503,67 @@ "1\tABC\t200.0\t2023-01-01\n", "2\tABC\t208.0\t2023-01-02\n", "3\tABC\t232.0\t2023-01-03\n", - "*/\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m I should query the ratio between the stock prices for 'ABC' and 'XYZ' in January 3rd and the ratio between the same stock prices in January the 4th.\n", - "Action: sql_db_query\n", - "Action Input: SELECT (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-03') / (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-03') / (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-04') * (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-04') AS ratio;\u001b[0m\n", - "Observation: \u001b[36;1m\u001b[1;3m[(1.027234753550543,)]\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m\u001b[0m\n", + "*/\u001b[0m\u001b[32;1m\u001b[1;3mThought: I need to get the prices for 'ABC' and 'XYZ' on January 3rd and January 4th. Then I will calculate the ratio of ABC to XYZ on each date and multiply these two ratios.\n", + "\n", + "Action: sql_db_query_checker\n", + "Action Input: \n", + "SELECT stock_ticker, price, date FROM stocks WHERE stock_ticker IN ('ABC', 'XYZ') AND date IN ('2023-01-03', '2023-01-04') ORDER BY date, stock_ticker LIMIT 10;\u001b[0m\u001b[36;1m\u001b[1;3mSELECT stock_ticker, price, date FROM stocks WHERE stock_ticker IN ('ABC', 'XYZ') AND date IN ('2023-01-03', '2023-01-04') ORDER BY date, stock_ticker LIMIT 10;\u001b[0m\u001b[32;1m\u001b[1;3mAction: sql_db_query\n", + "Action Input: \n", + "SELECT stock_ticker, price, date FROM stocks WHERE stock_ticker IN ('ABC', 'XYZ') AND date IN ('2023-01-03', '2023-01-04') ORDER BY date, stock_ticker LIMIT 10;\u001b[0m\u001b[36;1m\u001b[1;3m[('ABC', 232.0, '2023-01-03'), ('XYZ', 798.0, '2023-01-03'), ('ABC', 225.0, '2023-01-04'), ('XYZ', 795.0, '2023-01-04')]\u001b[0m\u001b[32;1m\u001b[1;3mThought: I have the prices for ABC and XYZ on January 3rd and January 4th. I will calculate the ratio of ABC to XYZ on January 3rd, then the ratio of ABC to XYZ on January 4th, and multiply these two ratios.\n", + "\n", + "Price ABC on 2023-01-03 = 232.0 \n", + "Price XYZ on 2023-01-03 = 798.0 \n", + "Ratio on 2023-01-03 = 232.0 / 798.0\n", + "\n", + "Price ABC on 2023-01-04 = 225.0 \n", + "Price XYZ on 2023-01-04 = 795.0 \n", + "Ratio on 2023-01-04 = 225.0 / 795.0\n", + "\n", + "Multiplication = (232.0 / 798.0) * (225.0 / 795.0)\n", + "\n", + "Calculating:\n", + "\n", + "232.0 / 798.0 ≈ 0.2907 \n", + "225.0 / 795.0 ≈ 0.2830 \n", + "Multiplication ≈ 0.2907 * 0.2830 ≈ 0.0823\n", + "\n", + "Final Answer: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and January 4th is approximately 0.0823.\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n", - "Spent a total of 2398 tokens\n" + "Spent a total of 223 tokens\n", + "Result: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and January 4th is approximately 0.0823.\n" ] } + ], + "source": [ + "result = count_tokens(\n", + " agent_executor,\n", + " \"What is the multiplication of the ratio between stock \" +\n", + " \"prices for 'ABC' and 'XYZ' in January 3rd and the ratio \" +\n", + " \"between the same stock prices in January the 4th?\"\n", + ")\n", + "print(f\"Result: {result['output']}\")" ] }, { "cell_type": "markdown", - "source": [ - "As always, let's see what the prompt is here:" - ], + "id": "LqzuNMgWmFXS", "metadata": { "id": "LqzuNMgWmFXS" }, - "id": "LqzuNMgWmFXS" + "source": [ + "As always, let's see what the prompt is here:" + ] }, { "cell_type": "code", - "source": [ - "print(agent_executor.agent.llm_chain.prompt.template)" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "ny7m_wMCmF4b", - "outputId": "ab8fdf8a-6f83-4f3f-e9dc-7313b48f81ef" - }, - "id": "ny7m_wMCmF4b", - "execution_count": 16, + "execution_count": 22, + "id": "751a79df", + "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "You are an agent designed to interact with a SQL database.\n", "Given an input question, create a syntactically correct sqlite query to run, then look at the results of the query and return the answer.\n", @@ -603,16 +579,13 @@ "If the question does not seem related to the database, just return \"I don't know\" as the answer.\n", "\n", "\n", - "sql_db_query: Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', using sql_db_schema to query the correct table fields.\n", - "sql_db_schema: Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: 'table1, table2, table3'\n", - "sql_db_list_tables: Input is an empty string, output is a comma separated list of tables in the database.\n", - "sql_db_query_checker: Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!\n", + "{tools}\n", "\n", "Use the following format:\n", "\n", "Question: the input question you must answer\n", "Thought: you should always think about what to do\n", - "Action: the action to take, should be one of [sql_db_query, sql_db_schema, sql_db_list_tables, sql_db_query_checker]\n", + "Action: the action to take, should be one of [{tool_names}]\n", "Action Input: the input to the action\n", "Observation: the result of the action\n", "... (this Thought/Action/Action Input/Observation can repeat N times)\n", @@ -626,6 +599,9 @@ "{agent_scratchpad}\n" ] } + ], + "source": [ + "print(agent_executor.agent.runnable.steps[1].template)" ] }, { @@ -700,19 +676,40 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 23, "id": "4b6faff3", "metadata": { "id": "4b6faff3" }, "outputs": [], "source": [ - "from langchain.agents import load_tools\n", + "# Use the modern tool-based approach instead of deprecated LLMMathChain\n", + "from langchain_core.tools import tool\n", + "import math\n", + "import numexpr\n", "\n", - "tools = load_tools(\n", - " [\"llm-math\"],\n", - " llm=llm\n", - ")" + "@tool\n", + "def calculator(expression: str) -> str:\n", + " \"\"\"Calculate expression using Python's numexpr library.\n", + " \n", + " Expression should be a single line mathematical expression\n", + " that solves the problem.\n", + " \n", + " Examples:\n", + " \"37593 * 67\" for \"37593 times 67\"\n", + " \"37593**(1/5)\" for \"37593^(1/5)\"\n", + " \"10000 * (1 + 0.08)**5\" for compound interest\n", + " \"\"\"\n", + " local_dict = {\"pi\": math.pi, \"e\": math.e}\n", + " return str(\n", + " numexpr.evaluate(\n", + " expression.strip(),\n", + " global_dict={}, # restrict access to globals\n", + " local_dict=local_dict, # add common mathematical functions\n", + " )\n", + " )\n", + "\n", + "tools = [calculator]" ] }, { @@ -727,42 +724,163 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 24, "id": "0aff4edf", "metadata": { "id": "0aff4edf" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\Siraj\\AppData\\Local\\Temp\\ipykernel_94028\\4074810399.py:3: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n", + " memory = ConversationBufferMemory(\n" + ] + } + ], "source": [ "from langchain.memory import ConversationBufferMemory\n", "\n", - "memory = ConversationBufferMemory(memory_key=\"chat_history\")" + "memory = ConversationBufferMemory(\n", + " memory_key=\"chat_history\",\n", + " return_messages=True\n", + ")" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 111, "id": "6579cef0", "metadata": { "id": "6579cef0" }, "outputs": [], "source": [ - "from langchain.agents import initialize_agent\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_core.runnables.base import RunnableSerializable\n", + "from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage\n", + "from langchain_core.tools import tool\n", + "import json\n", "\n", - "conversational_agent = initialize_agent(\n", - " agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,\n", - " tools=tools,\n", - " llm=llm,\n", - " verbose=True,\n", - " max_iterations=3,\n", - " memory=memory,\n", - ")" + "# Add a final_answer tool as recommended in the guide\n", + "@tool\n", + "def final_answer(answer: str, tools_used: list[str]) -> str:\n", + " \"\"\"Use this tool to provide a final answer to the user.\n", + " The answer should be in natural language as this will be provided\n", + " to the user directly. The tools_used must include a list of tool\n", + " names that were used within the `scratchpad`.\n", + " \"\"\"\n", + " return {\"answer\": answer, \"tools_used\": tools_used}\n", + "\n", + "# Add tools\n", + "tools = [final_answer, calculator]\n", + "\n", + "prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", (\n", + " \"You're a helpful assistant. When answering a user's question \"\n", + " \"you should first use one of the tools provided. After using a \"\n", + " \"tool the tool output will be provided in the \"\n", + " \"'scratchpad' below. If you have an answer in the \"\n", + " \"scratchpad you should not use any more tools and \"\n", + " \"instead answer directly to the user. \"\n", + " \"MOST IMPORTANT RULE: Always check the conversation history first. \"\n", + " \"If the history contains a calculation result you need, use that result. \"\n", + " \"Do not recalculate anything that has already been calculated in the conversation. \"\n", + " \"This is more efficient and avoids redundant work.\"\n", + " )),\n", + " MessagesPlaceholder(variable_name=\"chat_history\"),\n", + " (\"human\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + "])\n", + "\n", + "# define the agent runnable with tool_choice=\"auto\" instead of \"any\"\n", + "agent: RunnableSerializable = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"chat_history\": lambda x: x[\"chat_history\"],\n", + " \"agent_scratchpad\": lambda x: x.get(\"agent_scratchpad\", [])\n", + " }\n", + " | prompt\n", + " | llm.bind_tools(tools, tool_choice=\"auto\") # Allow LLM to choose whether to use tools\n", + ")\n", + "\n", + "# create tool name to function mapping as per guide\n", + "name2tool = {tool.name: tool.func for tool in tools}\n", + "\n", + "class CustomAgentExecutor:\n", + " def __init__(self, max_iterations: int = 5):\n", + " self.max_iterations = max_iterations\n", + " self.chat_history = [] # Simple list to store conversation history\n", + " self.agent = agent\n", + "\n", + " def invoke(self, input: str) -> dict:\n", + " # invoke the agent but we do this iteratively in a loop until\n", + " # reaching a final answer\n", + " count = 0\n", + " agent_scratchpad = []\n", + " \n", + " while count < self.max_iterations:\n", + " # invoke a step for the agent to generate a tool call\n", + " tool_call = self.agent.invoke({\n", + " \"input\": input,\n", + " \"chat_history\": self.chat_history,\n", + " \"agent_scratchpad\": agent_scratchpad\n", + " })\n", + " \n", + " # add initial tool call to scratchpad\n", + " agent_scratchpad.append(tool_call)\n", + " \n", + " # Handle ALL tool calls, not just the first one\n", + " if tool_call.tool_calls:\n", + " for tool_call_obj in tool_call.tool_calls:\n", + " tool_name = tool_call_obj[\"name\"]\n", + " tool_args = tool_call_obj[\"args\"]\n", + " tool_call_id = tool_call_obj[\"id\"]\n", + " \n", + " # execute the tool\n", + " tool_out = name2tool[tool_name](**tool_args)\n", + " \n", + " # add the tool output to the agent scratchpad\n", + " tool_exec = ToolMessage(\n", + " content=f\"{tool_out}\",\n", + " tool_call_id=tool_call_id\n", + " )\n", + " agent_scratchpad.append(tool_exec)\n", + " \n", + " # add a print so we can see intermediate steps\n", + " print(f\"{count}: {tool_name}({tool_args}) -> {tool_out}\")\n", + " \n", + " count += 1\n", + " \n", + " # Check if any tool call is the final answer tool\n", + " if any(tc[\"name\"] == \"final_answer\" for tc in tool_call.tool_calls):\n", + " # Get the final answer from the final_answer tool\n", + " final_tool_call = next(tc for tc in tool_call.tool_calls if tc[\"name\"] == \"final_answer\")\n", + " final_answer = final_tool_call[\"args\"][\"answer\"]\n", + " break\n", + " else:\n", + " # no tool call, we have a final answer\n", + " final_answer = tool_call.content\n", + " break\n", + " \n", + " # Add to conversation history ONLY the human input and final AI response\n", + " # This preserves memory without corrupting it with tool calls\n", + " self.chat_history.extend([\n", + " HumanMessage(content=input),\n", + " AIMessage(content=final_answer)\n", + " ])\n", + " \n", + " # return the final answer in dict form\n", + " return {\"output\": final_answer}\n", + "\n", + "# Initialize the custom agent executor\n", + "conversational_agent = CustomAgentExecutor()" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 112, "id": "cabbea50", "metadata": { "colab": { @@ -773,30 +891,19 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "\n", - "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3m\n", - "Thought: Do I need to use a tool? Yes\n", - "Action: Calculator\n", - "Action Input: 10,000, 8%, 5 years\u001b[0m\n", - "Observation: \u001b[36;1m\u001b[1;3mAnswer: 14693.280768000006\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m Do I need to use a tool? No\n", - "AI: The result of an investment of $10,000 growing at 8% annually for 5 years with compound interest is $14,693.28.\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n", - "Spent a total of 1246 tokens\n" + "0: calculator({'expression': '10000 * (1 + 0.08)**5'}) -> 14693.280768000006\n", + "1: final_answer({'answer': 'The value of 10000 * (1 + 0.08)**5 is approximately 14693.28.', 'tools_used': ['functions.calculator']}) -> {'answer': 'The value of 10000 * (1 + 0.08)**5 is approximately 14693.28.', 'tools_used': ['functions.calculator']}\n", + "Result: The value of 10000 * (1 + 0.08)**5 is approximately 14693.28.\n" ] } ], "source": [ - "result = count_tokens(\n", - " conversational_agent,\n", - " \"What's the result of an investment of $10,000 growing at 8% annually for 5 years with compound interest?\"\n", - ")" + "# First question\n", + "result = conversational_agent.invoke(\"What is 10000 * (1 + 0.08)**5?\")\n", + "print(f\"Result: {result['output']}\")" ] }, { @@ -811,7 +918,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 113, "id": "de413fd3", "metadata": { "colab": { @@ -822,52 +929,26 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "Assistant is a large language model trained by OpenAI.\n", - "\n", - "Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.\n", - "\n", - "Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.\n", - "\n", - "Overall, Assistant is a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.\n", - "\n", - "TOOLS:\n", - "------\n", - "\n", - "Assistant has access to the following tools:\n", - "\n", - "> Calculator: Useful for when you need to answer questions about math.\n", - "\n", - "To use a tool, please use the following format:\n", - "\n", - "```\n", - "Thought: Do I need to use a tool? Yes\n", - "Action: the action to take, should be one of [Calculator]\n", - "Action Input: the input to the action\n", - "Observation: the result of the action\n", - "```\n", - "\n", - "When you have a response to say to the Human, or if you do not need to use a tool, you MUST use the format:\n", - "\n", - "```\n", - "Thought: Do I need to use a tool? No\n", - "AI: [your response here]\n", - "```\n", - "\n", - "Begin!\n", - "\n", - "Previous conversation history:\n", - "{chat_history}\n", - "\n", - "New input: {input}\n", - "{agent_scratchpad}\n" + "Prompt Template Structure:\n", + "==================================================\n", + "System message: You're a helpful assistant. When answering a user's question you should first use one of the tools provided. After using a tool the tool output will be provided in the 'scratchpad' below. If you have an answer in the scratchpad you should not use any more tools and instead answer directly to the user. MOST IMPORTANT RULE: Always check the conversation history first. If the history contains a calculation result you need, use that result. Do not recalculate anything that has already been calculated in the conversation. This is more efficient and avoids redundant work.\n", + "Human message template: {input}\n", + "Variables: ['agent_scratchpad', 'chat_history', 'input']\n", + "==================================================\n" ] } ], "source": [ - "print(conversational_agent.agent.llm_chain.prompt.template)" + "# To see the actual template content, we need to format it with sample values\n", + "print(\"Prompt Template Structure:\")\n", + "print(\"=\" * 50)\n", + "print(\"System message:\", prompt.messages[0].prompt.template)\n", + "print(\"Human message template:\", prompt.messages[2].prompt.template)\n", + "print(\"Variables:\", prompt.input_variables)\n", + "print(\"=\" * 50)" ] }, { @@ -882,7 +963,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 114, "id": "5e109878", "metadata": { "colab": { @@ -893,30 +974,77 @@ }, "outputs": [ { + "name": "stdout", "output_type": "stream", + "text": [ + "0: calculator({'expression': '15000 * (1 + 0.08)**5'}) -> 22039.92115200001\n", + "0: calculator({'expression': '10000 * (1 + 0.08)**5'}) -> 14693.280768000006\n", + "1: calculator({'expression': '22039.92115200001 - 14693.280768000006'}) -> 7346.640384000004\n", + "2: final_answer({'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator', 'functions.final_answer']}) -> {'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator', 'functions.final_answer']}\n", + "Result: If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.\n" + ] + } + ], + "source": [ + "result = conversational_agent.invoke(\n", + " \"If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\"\n", + ")\n", + "print(f\"Result: {result['output']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "9efbc455", + "metadata": {}, + "outputs": [ + { "name": "stdout", + "output_type": "stream", "text": [ + "Formatted prompt messages:\n", + "==================================================\n", + "0: SystemMessage\n", + " Content: You're a helpful assistant. When answering a user's question you should first use one of the tools provided. After using a tool the tool output will be provided in the 'scratchpad' below. If you have an answer in the scratchpad you should not use any more tools and instead answer directly to the user. MOST IMPORTANT RULE: Always check the conversation history first. If the history contains a calculation result you need, use that result. Do not recalculate anything that has already been calculated in the conversation. This is more efficient and avoids redundant work.\n", "\n", + "1: HumanMessage\n", + " Content: What is 10000 * (1 + 0.08)**5?\n", "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3m\n", - "Thought: Do I need to use a tool? Yes\n", - "Action: Calculator\n", - "Action Input: 15000, 8%, 5 years, compound interest\u001b[0m\n", - "Observation: \u001b[36;1m\u001b[1;3mAnswer: 22039.92115200001\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m Do I need to use a tool? No\n", - "AI: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, we would have $22,039.92, which is $7,346.64 more than the previous scenario.\u001b[0m\n", + "2: AIMessage\n", + " Content: The value of 10000 * (1 + 0.08)**5 is approximately 14693.28.\n", "\n", - "\u001b[1m> Finished chain.\u001b[0m\n", - "Spent a total of 1416 tokens\n" + "3: HumanMessage\n", + " Content: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\n", + "\n", + "4: AIMessage\n", + " Content: If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.\n", + "\n", + "5: HumanMessage\n", + " Content: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\n", + "\n", + "==================================================\n" ] } ], "source": [ - "result = count_tokens(\n", - " conversational_agent,\n", - " \"If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\"\n", - ")" + "# Debug: Let's see what the actual formatted prompt looks like\n", + "print(\"Formatted prompt messages:\")\n", + "print(\"=\" * 50)\n", + "try:\n", + " formatted_messages = prompt.format_messages(\n", + " input=\"If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\",\n", + " chat_history=conversational_agent.chat_history,\n", + " agent_scratchpad=[]\n", + " )\n", + " \n", + " for i, msg in enumerate(formatted_messages):\n", + " print(f\"{i}: {type(msg).__name__}\")\n", + " print(f\" Content: {msg.content}\")\n", + " print()\n", + " \n", + "except Exception as e:\n", + " print(f\"Error formatting prompt: {e}\")\n", + "print(\"=\" * 50)" ] }, { @@ -951,52 +1079,107 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 84, "id": "ecc452af", "metadata": { "id": "ecc452af" }, "outputs": [], "source": [ - "from langchain import Wikipedia\n", - "from langchain.agents.react.base import DocstoreExplorer, Tool\n", + "from langchain_community.utilities import WikipediaAPIWrapper\n", + "from langchain_core.tools import tool\n", "\n", - "docstore=DocstoreExplorer(Wikipedia())\n", - "tools = [\n", - " Tool(\n", - " name=\"Search\",\n", - " func=docstore.search,\n", - " description='search wikipedia'\n", - " ),\n", - " Tool(\n", - " name=\"Lookup\",\n", - " func=docstore.lookup,\n", - " description='lookup a term in wikipedia'\n", - " )\n", - "]" + "@tool\n", + "def Search(query: str) -> str:\n", + " \"\"\"Search Wikipedia for information about a topic.\"\"\"\n", + " try:\n", + " wiki = WikipediaAPIWrapper()\n", + " return wiki.run(query)\n", + " except Exception as e:\n", + " return f\"Error searching Wikipedia: {e}\"\n", + "\n", + "@tool\n", + "def Lookup(term: str) -> str:\n", + " \"\"\"Look up a specific term or phrase in Wikipedia.\"\"\"\n", + " try:\n", + " wiki = WikipediaAPIWrapper()\n", + " return wiki.run(term)\n", + " except Exception as e:\n", + " return f\"Error looking up term: {e}\"\n", + "\n", + "tools = [Search, Lookup]" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 85, "id": "595938a1", "metadata": { "id": "595938a1" }, "outputs": [], "source": [ - "docstore_agent = initialize_agent(\n", - " tools,\n", - " llm,\n", - " agent=\"react-docstore\",\n", - " verbose=True,\n", - " max_iterations=3\n", - ")" + "# Create a custom agent executor for docstore tools\n", + "docstore_prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"You're a helpful assistant that can search and lookup information.\"),\n", + " (\"human\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + "])\n", + "\n", + "docstore_agent_runnable = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: x.get(\"agent_scratchpad\", [])\n", + " }\n", + " | docstore_prompt\n", + " | llm.bind_tools(tools, tool_choice=\"auto\")\n", + ")\n", + "\n", + "docstore_name2tool = {tool.name: tool.func for tool in tools}\n", + "\n", + "class DocstoreAgentExecutor:\n", + " def __init__(self, max_iterations: int = 3):\n", + " self.max_iterations = max_iterations\n", + " self.agent = docstore_agent_runnable\n", + "\n", + " def invoke(self, input: str) -> dict:\n", + " count = 0\n", + " agent_scratchpad = []\n", + " \n", + " while count < self.max_iterations:\n", + " tool_call = self.agent.invoke({\n", + " \"input\": input,\n", + " \"agent_scratchpad\": agent_scratchpad\n", + " })\n", + " \n", + " agent_scratchpad.append(tool_call)\n", + " \n", + " if not tool_call.tool_calls:\n", + " final_answer = tool_call.content\n", + " break\n", + " \n", + " tool_name = tool_call.tool_calls[0][\"name\"]\n", + " tool_args = tool_call.tool_calls[0][\"args\"]\n", + " tool_call_id = tool_call.tool_calls[0][\"id\"]\n", + " tool_out = docstore_name2tool[tool_name](**tool_args)\n", + " \n", + " tool_exec = ToolMessage(\n", + " content=f\"{tool_out}\",\n", + " tool_call_id=tool_call_id\n", + " )\n", + " agent_scratchpad.append(tool_exec)\n", + " \n", + " print(f\"{count}: {tool_name}({tool_args}) = {tool_out[:100]}...\")\n", + " count += 1\n", + " \n", + " return {\"input\": input, \"output\": final_answer}\n", + "\n", + "docstore_agent = DocstoreAgentExecutor()" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 86, "id": "bba6b065", "metadata": { "colab": { @@ -1007,44 +1190,18 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "\n", - "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3mThought: I need to search Archimedes and find his last words.\n", - "Action: Search[Archimedes]\u001b[0m\n", - "Observation: \u001b[36;1m\u001b[1;3mArchimedes of Syracuse (, ARK-ihm-EE-deez; c. 287 – c. 212 BC) was an Ancient Greek mathematician, physicist, engineer, astronomer, and inventor from the ancient city of Syracuse in Sicily. Although few details of his life are known, he is regarded as one of the leading scientists in classical antiquity. Considered the greatest mathematician of ancient history, and one of the greatest of all time, Archimedes anticipated modern calculus and analysis by applying the concept of the infinitely small and the method of exhaustion to derive and rigorously prove a range of geometrical theorems. These include the area of a circle, the surface area and volume of a sphere, the area of an ellipse, the area under a parabola, the volume of a segment of a paraboloid of revolution, the volume of a segment of a hyperboloid of revolution, and the area of a spiral.Archimedes' other mathematical achievements include deriving an approximation of pi, defining and investigating the Archimedean spiral, and devising a system using exponentiation for expressing very large numbers. He was also one of the first to apply mathematics to physical phenomena, working on statics and hydrostatics. Archimedes' achievements in this area include a proof of the law of the lever, the widespread use of the concept of center of gravity, and the enunciation of the law of buoyancy or Archimedes' principle. He is also credited with designing innovative machines, such as his screw pump, compound pulleys, and defensive war machines to protect his native Syracuse from invasion.\n", - "Archimedes died during the siege of Syracuse, when he was killed by a Roman soldier despite orders that he should not be harmed. Cicero describes visiting Archimedes' tomb, which was surmounted by a sphere and a cylinder that Archimedes requested be placed there to represent his mathematical discoveries.\n", - "Unlike his inventions, Archimedes' mathematical writings were little known in antiquity. Mathematicians from Alexandria read and quoted him, but the first comprehensive compilation was not made until c. 530 AD by Isidore of Miletus in Byzantine Constantinople, while commentaries on the works of Archimedes by Eutocius in the 6th century opened them to wider readership for the first time. The relatively few copies of Archimedes' written work that survived through the Middle Ages were an influential source of ideas for scientists during the Renaissance and again in the 17th century, while the discovery in 1906 of previously lost works by Archimedes in the Archimedes Palimpsest has provided new insights into how he obtained mathematical results.\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m The paragraph does not mention Archimedes' last words. I need to look up \"last words\".\n", - "Action: Lookup[last words]\u001b[0m\n", - "Observation: \u001b[33;1m\u001b[1;3m(Result 1/1) \n", - "== Biography ==\n", - "Archimedes was born c. 287 BC in the seaport city of Syracuse, Sicily, at that time a self-governing colony in Magna Graecia. The date of birth is based on a statement by the Byzantine Greek historian John Tzetzes that Archimedes lived for 75 years before his death in 212 BC. In the Sand-Reckoner, Archimedes gives his father's name as Phidias, an astronomer about whom nothing else is known. A biography of Archimedes was written by his friend Heracleides, but this work has been lost, leaving the details of his life obscure. It is unknown, for instance, whether he ever married or had children, or if he ever visited Alexandria, Egypt, during his youth. From his surviving written works, it is clear that he maintained collegiate relations with scholars based there, including his friend Conon of Samos and the head librarian Eratosthenes of Cyrene.The standard versions of Archimedes' life were written long after his death by Greek and Roman historians. The earliest reference to Archimedes occurs in The Histories by Polybius (c. 200–118 BC), written about 70 years after his death. It sheds little light on Archimedes as a person, and focuses on the war machines that he is said to have built in order to defend the city from the Romans. Polybius remarks how, during the Second Punic War, Syracuse switched allegiances from Rome to Carthage, resulting in a military campaign under the command of Marcus Claudius Marcellus and Appius Claudius Pulcher, who besieged the city from 213 to 212 BC. He notes that the Romans underestimated Syracuse's defenses, and mentions several machines Archimedes designed, including improved catapults, crane-like machines that could be swung around in an arc, and other stone-throwers. Although the Romans ultimately captured the city, they suffered considerable losses due to Archimedes' inventiveness.Cicero (106–43 BC) mentions Archimedes in some of his works. While serving as a quaestor in Sicily, Cicero found what was presumed to be Archimedes' tomb near the Agrigentine gate in Syracuse, in a neglected condition and overgrown with bushes. Cicero had the tomb cleaned up and was able to see the carving and read some of the verses that had been added as an inscription. The tomb carried a sculpture illustrating Archimedes' favorite mathematical proof, that the volume and surface area of the sphere are two-thirds that of an enclosing cylinder including its bases. He also mentions that Marcellus brought to Rome two planetariums Archimedes built. The Roman historian Livy (59 BC–17 AD) retells Polybius' story of the capture of Syracuse and Archimedes' role in it.\n", - "Plutarch (45–119 AD) wrote in his Parallel Lives that Archimedes was related to King Hiero II, the ruler of Syracuse. He also provides at least two accounts on how Archimedes died after the city was taken. According to the most popular account, Archimedes was contemplating a mathematical diagram when the city was captured. A Roman soldier commanded him to come and meet Marcellus, but he declined, saying that he had to finish working on the problem. This enraged the soldier, who killed Archimedes with his sword. Another story has Archimedes carrying mathematical instruments before being killed because a soldier thought they were valuable items. Marcellus was reportedly angered by Archimedes' death, as he considered him a valuable scientific asset (he called Archimedes \"a geometrical Briareus\") and had ordered that he should not be harmed.The last words attributed to Archimedes are \"Do not disturb my circles\" (Latin, \"Noli turbare circulos meos\"; Katharevousa Greek, \"μὴ μου τοὺς κύκλους τάραττε\"), a reference to the mathematical drawing that he was supposedly studying when disturbed by the Roman soldier. There is no reliable evidence that Archimedes uttered these words and they do not appear in Plutarch's account. A similar quotation is found in the work of Valerius Maximus (fl. 30 AD), who wrote in Memorable Doings and Sayings, \"... sed protecto manibus puluere 'noli' inquit, 'obsecro, istum disturbare'\" (\"... but protecting the dust with his hands, said 'I beg of you, do not disturb this'\").\u001b[0m\n", - "Thought:\u001b[32;1m\u001b[1;3m The paragraph mentions that the last words attributed to Archimedes are \"Do not disturb my circles\". So the answer is \"Do not disturb my circles\".\n", - "Action: Finish[Do not disturb my circles]\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n", - "Spent a total of 6692 tokens\n" + "0: Lookup({'term': \"Archimedes' last words\"}) = Page: Archimedes\n", + "Summary: Archimedes of Syracuse ( AR-kih-MEE-deez; c. 287 – c. 212 BC) was an Ancie...\n", + "Result: The search did not return specific information about Archimedes' last words. However, historically it is often said that Archimedes' last words were \"Do not disturb my circles,\" which he reportedly said to a Roman soldier who was about to kill him during the siege of Syracuse. This phrase reflects his deep engagement with his mathematical work even at the moment of his death.\n" ] - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'input': \"What were Archimedes' last words?\",\n", - " 'output': 'Do not disturb my circles'}" - ] - }, - "metadata": {}, - "execution_count": 25 } ], "source": [ - "count_tokens(docstore_agent, \"What were Archimedes' last words?\")" + "result = docstore_agent.invoke(\"What were Archimedes' last words?\")\n", + "print(f\"Result: {result['output']}\")" ] }, { @@ -1083,26 +1240,40 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 87, "id": "903660b2", "metadata": { "id": "903660b2" }, "outputs": [], "source": [ - "from langchain import OpenAI, SerpAPIWrapper\n", - "from langchain.agents import initialize_agent, Tool\n", + "from langchain.agents import load_tools\n", "\n", - "search = SerpAPIWrapper(serpapi_api_key='api_key')\n", - "tools = [\n", - " Tool(\n", - " name=\"Intermediate Answer\",\n", - " func=search.run,\n", - " description='google search'\n", - " )\n", - "]\n", + "# Add SerpAPI key setup\n", + "os.environ[\"SERPAPI_API_KEY\"] = os.getenv(\"SERPAPI_API_KEY\") \\\n", + " or getpass(\"Enter your SerpAPI API key: \")\n", + "\n", + "toolbox = load_tools(tool_names=['serpapi'], llm=llm)\n", + "\n", + "# Use modern agent creation pattern\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain.agents import create_tool_calling_agent, AgentExecutor\n", + "\n", + "prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"you're a helpful assistant\"),\n", + " (\"human\", \"{input}\"),\n", + " (\"placeholder\", \"{agent_scratchpad}\")\n", + "])\n", + "\n", + "agent = create_tool_calling_agent(\n", + " llm=llm, tools=toolbox, prompt=prompt\n", + ")\n", "\n", - "self_ask_with_search = initialize_agent(tools, llm, agent=\"self-ask-with-search\", verbose=True)" + "self_ask_with_search = AgentExecutor(\n", + " agent=agent,\n", + " tools=toolbox,\n", + " verbose=True\n", + ")" ] }, { @@ -1117,63 +1288,52 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 88, "id": "e4134930", "metadata": { - "id": "e4134930", - "outputId": "2f58884d-0c4e-4b40-de45-6928e0c60ef4", "colab": { "base_uri": "https://localhost:8080/" - } + }, + "id": "e4134930", + "outputId": "2f58884d-0c4e-4b40-de45-6928e0c60ef4" }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "Question: Who lived longer, Muhammad Ali or Alan Turing?\n", - "Are follow up questions needed here: Yes.\n", - "Follow up: How old was Muhammad Ali when he died?\n", - "Intermediate answer: Muhammad Ali was 74 years old when he died.\n", - "Follow up: How old was Alan Turing when he died?\n", - "Intermediate answer: Alan Turing was 41 years old when he died.\n", - "So the final answer is: Muhammad Ali\n", - "\n", - "Question: When was the founder of craigslist born?\n", - "Are follow up questions needed here: Yes.\n", - "Follow up: Who was the founder of craigslist?\n", - "Intermediate answer: Craigslist was founded by Craig Newmark.\n", - "Follow up: When was Craig Newmark born?\n", - "Intermediate answer: Craig Newmark was born on December 6, 1952.\n", - "So the final answer is: December 6, 1952\n", - "\n", - "Question: Who was the maternal grandfather of George Washington?\n", - "Are follow up questions needed here: Yes.\n", - "Follow up: Who was the mother of George Washington?\n", - "Intermediate answer: The mother of George Washington was Mary Ball Washington.\n", - "Follow up: Who was the father of Mary Ball Washington?\n", - "Intermediate answer: The father of Mary Ball Washington was Joseph Ball.\n", - "So the final answer is: Joseph Ball\n", - "\n", - "Question: Are both the directors of Jaws and Casino Royale from the same country?\n", - "Are follow up questions needed here: Yes.\n", - "Follow up: Who is the director of Jaws?\n", - "Intermediate answer: The director of Jaws is Steven Spielberg.\n", - "Follow up: Where is Steven Spielberg from?\n", - "Intermediate answer: The United States.\n", - "Follow up: Who is the director of Casino Royale?\n", - "Intermediate answer: The director of Casino Royale is Martin Campbell.\n", - "Follow up: Where is Martin Campbell from?\n", - "Intermediate answer: New Zealand.\n", - "So the final answer is: No\n", - "\n", - "Question: {input}\n", - "Are followup questions needed here:{agent_scratchpad}\n" + "Modern Agent Structure:\n", + "==================================================\n", + "Agent type: RunnableMultiActionAgent\n", + "==================================================\n", + "Prompt Template Structure:\n", + "==================================================\n", + "System message: you're a helpful assistant\n", + "Human message template: {input}\n", + "Required variables: ['input']\n", + "Optional variables: ['agent_scratchpad']\n", + "==================================================\n" ] } ], "source": [ - "print(self_ask_with_search.agent.llm_chain.prompt.template)" + "# The modern agent structure is different - let's inspect it properly\n", + "print(\"Modern Agent Structure:\")\n", + "print(\"=\" * 50)\n", + "print(\"Agent type:\", type(self_ask_with_search.agent).__name__)\n", + "print(\"=\" * 50)\n", + "\n", + "# To see the prompt template, we need to access it differently\n", + "agent_runnable = self_ask_with_search.agent.runnable\n", + "prompt_template = agent_runnable.steps[1]\n", + "\n", + "print(\"Prompt Template Structure:\")\n", + "print(\"=\" * 50)\n", + "print(\"System message:\", prompt_template.messages[0].prompt.template)\n", + "print(\"Human message template:\", prompt_template.messages[1].prompt.template)\n", + "print(\"Required variables:\", prompt_template.input_variables)\n", + "print(\"Optional variables:\", prompt_template.optional_variables)\n", + "print(\"=\" * 50)\n" ] }, { @@ -1183,7 +1343,10 @@ "id": "3e7790ae" }, "source": [ - "As we can see, the prompt is basically a series of many examples to show the LLM how to ask follow up questions to a search tool until it can get to the final answer." + "Note: The modern create_tool_calling_agent uses a simpler prompt template.\n", + "The old 'self-ask-with-search' agent type had detailed examples showing\n", + "how to ask follow-up questions, but this modern approach relies more on\n", + "the LLM's inherent capabilities and the tool descriptions." ] }, { @@ -1235,18 +1398,23 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3", + "display_name": "pinecone1", + "language": "python", "name": "python3" }, "language_info": { - "name": "python" - }, - "vscode": { - "interpreter": { - "hash": "578e1e8dce4dc6c542f1ea2d66a2d9db6ef592936dcc314004bdae386f827d38" - } + "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": 5 -} \ No newline at end of file +} From 25f8c35a2463d08e79ba7ce689ad23a0393b42ed Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Thu, 3 Jul 2025 15:32:55 +0400 Subject: [PATCH 2/6] fix: Ensuring that Memory is Being Used Correctly --- .../handbook/06-langchain-agents.ipynb | 103 ++++++------------ 1 file changed, 31 insertions(+), 72 deletions(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index b4b8fd1d..8a210f6b 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -750,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 116, "id": "6579cef0", "metadata": { "id": "6579cef0" @@ -776,7 +776,18 @@ "# Add tools\n", "tools = [final_answer, calculator]\n", "\n", - "prompt = ChatPromptTemplate.from_messages([\n", + "# First, create a prompt that forces the LLM to analyze the history\n", + "history_analysis_prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", (\n", + " \"Analyze the conversation history below and identify any calculations that have already been performed. \"\n", + " \"Extract the results of these calculations so they can be reused instead of recalculating.\"\n", + " )),\n", + " MessagesPlaceholder(variable_name=\"chat_history\"),\n", + " (\"human\", \"What calculations have been done and what are their results?\"),\n", + "])\n", + "\n", + "# Then the main agent prompt\n", + "agent_prompt = ChatPromptTemplate.from_messages([\n", " (\"system\", (\n", " \"You're a helpful assistant. When answering a user's question \"\n", " \"you should first use one of the tools provided. After using a \"\n", @@ -784,25 +795,29 @@ " \"'scratchpad' below. If you have an answer in the \"\n", " \"scratchpad you should not use any more tools and \"\n", " \"instead answer directly to the user. \"\n", - " \"MOST IMPORTANT RULE: Always check the conversation history first. \"\n", - " \"If the history contains a calculation result you need, use that result. \"\n", - " \"Do not recalculate anything that has already been calculated in the conversation. \"\n", - " \"This is more efficient and avoids redundant work.\"\n", + " \"IMPORTANT: Use the analysis of previous calculations to avoid recalculating.\"\n", " )),\n", - " MessagesPlaceholder(variable_name=\"chat_history\"),\n", + " (\"human\", \"Previous calculations analysis: {history_analysis}\"),\n", " (\"human\", \"{input}\"),\n", " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", "])\n", "\n", - "# define the agent runnable with tool_choice=\"auto\" instead of \"any\"\n", + "# define the agent runnable with history analysis first\n", "agent: RunnableSerializable = (\n", " {\n", " \"input\": lambda x: x[\"input\"],\n", " \"chat_history\": lambda x: x[\"chat_history\"],\n", " \"agent_scratchpad\": lambda x: x.get(\"agent_scratchpad\", [])\n", " }\n", - " | prompt\n", - " | llm.bind_tools(tools, tool_choice=\"auto\") # Allow LLM to choose whether to use tools\n", + " | {\n", + " \"history_analysis\": lambda x: llm.invoke(history_analysis_prompt.format_messages(\n", + " chat_history=x[\"chat_history\"]\n", + " )).content,\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: x.get(\"agent_scratchpad\", [])\n", + " }\n", + " | agent_prompt\n", + " | llm.bind_tools(tools, tool_choice=\"auto\")\n", ")\n", "\n", "# create tool name to function mapping as per guide\n", @@ -880,7 +895,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 117, "id": "cabbea50", "metadata": { "colab": { @@ -918,7 +933,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 118, "id": "de413fd3", "metadata": { "colab": { @@ -963,7 +978,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 119, "id": "5e109878", "metadata": { "colab": { @@ -978,10 +993,9 @@ "output_type": "stream", "text": [ "0: calculator({'expression': '15000 * (1 + 0.08)**5'}) -> 22039.92115200001\n", - "0: calculator({'expression': '10000 * (1 + 0.08)**5'}) -> 14693.280768000006\n", - "1: calculator({'expression': '22039.92115200001 - 14693.280768000006'}) -> 7346.640384000004\n", - "2: final_answer({'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator', 'functions.final_answer']}) -> {'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator', 'functions.final_answer']}\n", - "Result: If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.\n" + "0: calculator({'expression': '15000 * (1 + 0.08)**5 - 14693.28'}) -> 7346.641152000009\n", + "1: final_answer({'answer': 'If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator']}) -> {'answer': 'If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator']}\n", + "Result: If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.\n" ] } ], @@ -992,61 +1006,6 @@ "print(f\"Result: {result['output']}\")" ] }, - { - "cell_type": "code", - "execution_count": 115, - "id": "9efbc455", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Formatted prompt messages:\n", - "==================================================\n", - "0: SystemMessage\n", - " Content: You're a helpful assistant. When answering a user's question you should first use one of the tools provided. After using a tool the tool output will be provided in the 'scratchpad' below. If you have an answer in the scratchpad you should not use any more tools and instead answer directly to the user. MOST IMPORTANT RULE: Always check the conversation history first. If the history contains a calculation result you need, use that result. Do not recalculate anything that has already been calculated in the conversation. This is more efficient and avoids redundant work.\n", - "\n", - "1: HumanMessage\n", - " Content: What is 10000 * (1 + 0.08)**5?\n", - "\n", - "2: AIMessage\n", - " Content: The value of 10000 * (1 + 0.08)**5 is approximately 14693.28.\n", - "\n", - "3: HumanMessage\n", - " Content: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\n", - "\n", - "4: AIMessage\n", - " Content: If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have $7,346.64 more compared to the previous scenario.\n", - "\n", - "5: HumanMessage\n", - " Content: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\n", - "\n", - "==================================================\n" - ] - } - ], - "source": [ - "# Debug: Let's see what the actual formatted prompt looks like\n", - "print(\"Formatted prompt messages:\")\n", - "print(\"=\" * 50)\n", - "try:\n", - " formatted_messages = prompt.format_messages(\n", - " input=\"If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\",\n", - " chat_history=conversational_agent.chat_history,\n", - " agent_scratchpad=[]\n", - " )\n", - " \n", - " for i, msg in enumerate(formatted_messages):\n", - " print(f\"{i}: {type(msg).__name__}\")\n", - " print(f\" Content: {msg.content}\")\n", - " print()\n", - " \n", - "except Exception as e:\n", - " print(f\"Error formatting prompt: {e}\")\n", - "print(\"=\" * 50)" - ] - }, { "cell_type": "markdown", "id": "44135b8d", From 91c64f86b155d0031cdd449b14fc6d7de2e23a0e Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Thu, 3 Jul 2025 15:35:00 +0400 Subject: [PATCH 3/6] fix: Memory Not Working for Maths Calculation Before --- .../handbook/06-langchain-agents.ipynb | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index 8a210f6b..88b99599 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -750,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 120, "id": "6579cef0", "metadata": { "id": "6579cef0" @@ -895,7 +895,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 121, "id": "cabbea50", "metadata": { "colab": { @@ -933,7 +933,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 122, "id": "de413fd3", "metadata": { "colab": { @@ -978,7 +978,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 123, "id": "5e109878", "metadata": { "colab": { @@ -993,9 +993,9 @@ "output_type": "stream", "text": [ "0: calculator({'expression': '15000 * (1 + 0.08)**5'}) -> 22039.92115200001\n", - "0: calculator({'expression': '15000 * (1 + 0.08)**5 - 14693.28'}) -> 7346.641152000009\n", - "1: final_answer({'answer': 'If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator']}) -> {'answer': 'If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.', 'tools_used': ['multi_tool_use.parallel', 'functions.calculator', 'functions.calculator']}\n", - "Result: If we start with $15,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $22,039.92. Compared to the previous scenario where we started with $10,000, we would have approximately $7,346.64 more.\n" + "1: calculator({'expression': '22039.92 - 14693.28'}) -> 7346.639999999998\n", + "2: final_answer({'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $7,346.64 more compared to the previous scenario.', 'tools_used': ['functions.calculator', 'functions.calculator']}) -> {'answer': 'If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $7,346.64 more compared to the previous scenario.', 'tools_used': ['functions.calculator', 'functions.calculator']}\n", + "Result: If we start with $15,000 instead of $10,000 and follow the same 8% annual growth for 5 years with compound interest, we would have approximately $7,346.64 more compared to the previous scenario.\n" ] } ], @@ -1038,7 +1038,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 124, "id": "ecc452af", "metadata": { "id": "ecc452af" @@ -1071,7 +1071,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 127, "id": "595938a1", "metadata": { "id": "595938a1" @@ -1138,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 128, "id": "bba6b065", "metadata": { "colab": { @@ -1154,7 +1154,7 @@ "text": [ "0: Lookup({'term': \"Archimedes' last words\"}) = Page: Archimedes\n", "Summary: Archimedes of Syracuse ( AR-kih-MEE-deez; c. 287 – c. 212 BC) was an Ancie...\n", - "Result: The search did not return specific information about Archimedes' last words. However, historically it is often said that Archimedes' last words were \"Do not disturb my circles,\" which he reportedly said to a Roman soldier who was about to kill him during the siege of Syracuse. This phrase reflects his deep engagement with his mathematical work even at the moment of his death.\n" + "Result: The specific last words of Archimedes are not clearly recorded in historical texts. It is known that Archimedes was killed by a Roman soldier during the siege of Syracuse despite orders that he should not be harmed, but his final words before death are not documented.\n" ] } ], @@ -1199,7 +1199,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 129, "id": "903660b2", "metadata": { "id": "903660b2" @@ -1247,7 +1247,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 130, "id": "e4134930", "metadata": { "colab": { From bfc4a073b89a165641be40ab6ca33d9b2ec576c9 Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Thu, 3 Jul 2025 17:06:53 +0400 Subject: [PATCH 4/6] fix: Ensuring Code Returns Answers as Expected --- .../handbook/06-langchain-agents.ipynb | 175 +++++++++++++----- 1 file changed, 128 insertions(+), 47 deletions(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index 88b99599..f5bcb13c 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -436,7 +436,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 12, "id": "5Z4EmMmqiOvZ", "metadata": { "id": "5Z4EmMmqiOvZ" @@ -468,7 +468,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "id": "MdvgpwHRic3W", "metadata": { "colab": { @@ -503,7 +503,7 @@ "1\tABC\t200.0\t2023-01-01\n", "2\tABC\t208.0\t2023-01-02\n", "3\tABC\t232.0\t2023-01-03\n", - "*/\u001b[0m\u001b[32;1m\u001b[1;3mThought: I need to get the prices for 'ABC' and 'XYZ' on January 3rd and January 4th. Then I will calculate the ratio of ABC to XYZ on each date and multiply these two ratios.\n", + "*/\u001b[0m\u001b[32;1m\u001b[1;3mThought: I need to get the prices for 'ABC' and 'XYZ' on January 3rd and January 4th. Then I will calculate the ratio of ABC to XYZ on January 3rd and the ratio of ABC to XYZ on January 4th, and finally multiply these two ratios.\n", "\n", "Action: sql_db_query_checker\n", "Action Input: \n", @@ -521,17 +521,16 @@ "\n", "Multiplication = (232.0 / 798.0) * (225.0 / 795.0)\n", "\n", - "Calculating:\n", - "\n", + "Calculating: \n", "232.0 / 798.0 ≈ 0.2907 \n", "225.0 / 795.0 ≈ 0.2830 \n", "Multiplication ≈ 0.2907 * 0.2830 ≈ 0.0823\n", "\n", - "Final Answer: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and January 4th is approximately 0.0823.\u001b[0m\n", + "Final Answer: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and the ratio between the same stock prices on January 4th is approximately 0.0823.\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n", "Spent a total of 223 tokens\n", - "Result: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and January 4th is approximately 0.0823.\n" + "Result: The multiplication of the ratio between stock prices for 'ABC' and 'XYZ' on January 3rd and the ratio between the same stock prices on January 4th is approximately 0.0823.\n" ] } ], @@ -557,7 +556,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 14, "id": "751a79df", "metadata": {}, "outputs": [ @@ -676,7 +675,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 15, "id": "4b6faff3", "metadata": { "id": "4b6faff3" @@ -724,7 +723,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 16, "id": "0aff4edf", "metadata": { "id": "0aff4edf" @@ -734,7 +733,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\Siraj\\AppData\\Local\\Temp\\ipykernel_94028\\4074810399.py:3: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n", + "C:\\Users\\Siraj\\AppData\\Local\\Temp\\ipykernel_84824\\4074810399.py:3: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n", " memory = ConversationBufferMemory(\n" ] } @@ -750,7 +749,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 17, "id": "6579cef0", "metadata": { "id": "6579cef0" @@ -759,9 +758,8 @@ "source": [ "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", "from langchain_core.runnables.base import RunnableSerializable\n", - "from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage\n", + "from langchain_core.messages import HumanMessage, AIMessage, ToolMessage\n", "from langchain_core.tools import tool\n", - "import json\n", "\n", "# Add a final_answer tool as recommended in the guide\n", "@tool\n", @@ -895,7 +893,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 18, "id": "cabbea50", "metadata": { "colab": { @@ -933,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 19, "id": "de413fd3", "metadata": { "colab": { @@ -949,20 +947,21 @@ "text": [ "Prompt Template Structure:\n", "==================================================\n", - "System message: You're a helpful assistant. When answering a user's question you should first use one of the tools provided. After using a tool the tool output will be provided in the 'scratchpad' below. If you have an answer in the scratchpad you should not use any more tools and instead answer directly to the user. MOST IMPORTANT RULE: Always check the conversation history first. If the history contains a calculation result you need, use that result. Do not recalculate anything that has already been calculated in the conversation. This is more efficient and avoids redundant work.\n", + "System message: You're a helpful assistant. When answering a user's question you should first use one of the tools provided. After using a tool the tool output will be provided in the 'scratchpad' below. If you have an answer in the scratchpad you should not use any more tools and instead answer directly to the user. IMPORTANT: Use the analysis of previous calculations to avoid recalculating.\n", + "History analysis template: Previous calculations analysis: {history_analysis}\n", "Human message template: {input}\n", - "Variables: ['agent_scratchpad', 'chat_history', 'input']\n", + "Variables: ['agent_scratchpad', 'history_analysis', 'input']\n", "==================================================\n" ] } ], "source": [ - "# To see the actual template content, we need to format it with sample values\n", "print(\"Prompt Template Structure:\")\n", "print(\"=\" * 50)\n", - "print(\"System message:\", prompt.messages[0].prompt.template)\n", - "print(\"Human message template:\", prompt.messages[2].prompt.template)\n", - "print(\"Variables:\", prompt.input_variables)\n", + "print(\"System message:\", agent_prompt.messages[0].prompt.template)\n", + "print(\"History analysis template:\", agent_prompt.messages[1].prompt.template) # Show the memory component\n", + "print(\"Human message template:\", agent_prompt.messages[2].prompt.template)\n", + "print(\"Variables:\", agent_prompt.input_variables)\n", "print(\"=\" * 50)" ] }, @@ -978,7 +977,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 20, "id": "5e109878", "metadata": { "colab": { @@ -1038,7 +1037,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 21, "id": "ecc452af", "metadata": { "id": "ecc452af" @@ -1071,7 +1070,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 22, "id": "595938a1", "metadata": { "id": "595938a1" @@ -1138,7 +1137,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 23, "id": "bba6b065", "metadata": { "colab": { @@ -1154,7 +1153,7 @@ "text": [ "0: Lookup({'term': \"Archimedes' last words\"}) = Page: Archimedes\n", "Summary: Archimedes of Syracuse ( AR-kih-MEE-deez; c. 287 – c. 212 BC) was an Ancie...\n", - "Result: The specific last words of Archimedes are not clearly recorded in historical texts. It is known that Archimedes was killed by a Roman soldier during the siege of Syracuse despite orders that he should not be harmed, but his final words before death are not documented.\n" + "Result: The search did not return specific information about Archimedes' last words. However, historically it is often said that Archimedes' last words were \"Do not disturb my circles,\" which he reportedly said to a Roman soldier who was about to kill him during the siege of Syracuse. This phrase reflects his deep engagement with his mathematical work even at the moment of his death.\n" ] } ], @@ -1194,17 +1193,75 @@ "id": "c07039e5" }, "source": [ - "This is the first-choice agent to use when using LLM's to extract information with a search engine. The agent will ask follow-up questions and use the search functionality to get intermediate answers that help it get to a final answer." + "This is the first-choice agent to use when using LLM's to extract information with a search engine. The agent will ask follow-up questions and use the search functionality to get intermediate answers that help it get to a final answer.\n", + "\n", + "> You will need an API key from here: https://serpapi.com/manage-api-key" ] }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 24, "id": "903660b2", "metadata": { "id": "903660b2" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing Self Ask with Search agent...\n", + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `Search` with `latest developments in renewable energy 2024`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m['Renewables 2024 offers a comprehensive country-level analysis on tracking progress towards the global tripling target based on current policies ...', 'Renewable energy consumption in the power, heat and transport sectors increases near 60% over 2024-2030 in our main-case forecast.', \"REN21's Renewables Global Status Report (GSR) has spotlighted ongoing developments and emerging trends that shape the future of renewables.\", '5 Renewable Energy Trends to Navigate for Success in 2024-25 · Policy support and capital influx · Utility-scale and community solar expansion · Energy storage ...', 'Global renewable energy capacity grew by a record-breaking 15.1% in 2024 to reach 4,448 gigawatts (GW). Around the world, an additional 585 ...', 'As of October 2024, 16 GWh of grid-scale battery storage has been tendered in India, with 211 MWh already operational. Batteries are also ...', 'The US Energy Information Administration expects renewable deployment to grow by 17% to 42 GW in 2024 and account for almost a quarter of electricity generation ...', 'The clean energy industry shattered records in 2024, deploying an unprecedented 49 GW of capacity—a remarkable 33% increase over the previous ...', \"Innovations in Renewable Energy: What's New in 2024 · 1. Next-Generation Solar Panels · 2. Floating Wind Farms · 3. Green Hydrogen Production.\", 'RENEWABLE ENERGY: 42 gigawatts (GW) of new renewable power-generating capacity was added to the U.S. grid, driven mainly by robust solar additions. ENERGY ...']\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `Search` with `cost and efficiency comparison of renewable energy and fossil fuels 2024`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m['In 2015, the ratio of clean power to unabated fossil fuel power investments was roughly 2:1. In 2024, this ratio is set to reach 10:1. The rise in solar and ...', 'The world added 473 gigawatts of renewable energy capacity last year, and four-fifths of it produces power more cheaply than fossil fuels ...', 'Cost of renewable energy versus fossil fuels worldwide from 2017 to 2023 with a forecast for 2028, by energy type (in U.S. dollars per MWh).', 'Notably, the maximum cost of solar PV with storage has significantly increased from $102 in 2023 to $210 in 2024, although the cost of solar ...', 'In 2024, the conversation about the cost-effectiveness of solar energy versus traditional fossil fuels is reaching a high point. From homeowners looking to ...', 'https://www.irena.org/Publications/2024/Sep/Renewable-Power-Generation-Costs-in-2023', 'Renewable energy consumption in the power, heat and transport sectors increases near 60% over 2024-2030 in our main-case forecast.', 'The results of our Levelized Cost of Energy (“LCOE”)analysis reinforce what we observe across the Power, Energy & Infrastructure Industry—sizable.', 'After 2024, clean energy is cheaper than ever. Global solar module prices fell 35 percent to less than 9 cents/kWh. EV batteries saw their best ...', \"Renewable energy sources run at a lower cost because they don't require fuel to run. Fossil fuel plants, however, require various fuels like coal, oil and ...\"]\u001b[0m\u001b[32;1m\u001b[1;3mThe latest developments in renewable energy in 2024 include significant growth and innovation:\n", + "\n", + "1. Global renewable energy capacity grew by a record-breaking 15.1% in 2024, reaching 4,448 gigawatts (GW).\n", + "2. The US added 42 GW of new renewable power-generating capacity, mainly driven by solar energy.\n", + "3. Innovations such as next-generation solar panels, floating wind farms, and green hydrogen production are shaping the future of renewables.\n", + "4. Battery storage capacity is also expanding, with significant grid-scale battery storage projects underway, for example in India.\n", + "5. Renewable energy consumption in power, heat, and transport sectors is expected to increase by nearly 60% from 2024 to 2030.\n", + "\n", + "In terms of cost and efficiency compared to fossil fuels:\n", + "\n", + "1. Renewable energy is becoming increasingly cost-competitive, with four-fifths of new renewable capacity producing power more cheaply than fossil fuels.\n", + "2. The cost of solar PV with storage has seen some fluctuations but remains competitive.\n", + "3. Global solar module prices have fallen by 35% to less than 9 cents per kWh.\n", + "4. Renewable energy sources have lower operational costs since they do not require fuel, unlike fossil fuel plants which need coal, oil, or gas.\n", + "5. The investment ratio in clean power compared to unabated fossil fuel power has increased from 2:1 in 2015 to an expected 10:1 in 2024, indicating a strong shift towards renewables.\n", + "\n", + "Overall, renewable energy is advancing rapidly in capacity, technology, and cost-effectiveness, increasingly outperforming fossil fuels in both cost and efficiency.\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n", + "\n", + "Final Result: The latest developments in renewable energy in 2024 include significant growth and innovation:\n", + "\n", + "1. Global renewable energy capacity grew by a record-breaking 15.1% in 2024, reaching 4,448 gigawatts (GW).\n", + "2. The US added 42 GW of new renewable power-generating capacity, mainly driven by solar energy.\n", + "3. Innovations such as next-generation solar panels, floating wind farms, and green hydrogen production are shaping the future of renewables.\n", + "4. Battery storage capacity is also expanding, with significant grid-scale battery storage projects underway, for example in India.\n", + "5. Renewable energy consumption in power, heat, and transport sectors is expected to increase by nearly 60% from 2024 to 2030.\n", + "\n", + "In terms of cost and efficiency compared to fossil fuels:\n", + "\n", + "1. Renewable energy is becoming increasingly cost-competitive, with four-fifths of new renewable capacity producing power more cheaply than fossil fuels.\n", + "2. The cost of solar PV with storage has seen some fluctuations but remains competitive.\n", + "3. Global solar module prices have fallen by 35% to less than 9 cents per kWh.\n", + "4. Renewable energy sources have lower operational costs since they do not require fuel, unlike fossil fuel plants which need coal, oil, or gas.\n", + "5. The investment ratio in clean power compared to unabated fossil fuel power has increased from 2:1 in 2015 to an expected 10:1 in 2024, indicating a strong shift towards renewables.\n", + "\n", + "Overall, renewable energy is advancing rapidly in capacity, technology, and cost-effectiveness, increasingly outperforming fossil fuels in both cost and efficiency.\n" + ] + } + ], "source": [ "from langchain.agents import load_tools\n", "\n", @@ -1212,6 +1269,8 @@ "os.environ[\"SERPAPI_API_KEY\"] = os.getenv(\"SERPAPI_API_KEY\") \\\n", " or getpass(\"Enter your SerpAPI API key: \")\n", "\n", + "\n", + "# Now load the tools - this will use the environment variable we just set\n", "toolbox = load_tools(tool_names=['serpapi'], llm=llm)\n", "\n", "# Use modern agent creation pattern\n", @@ -1219,7 +1278,7 @@ "from langchain.agents import create_tool_calling_agent, AgentExecutor\n", "\n", "prompt = ChatPromptTemplate.from_messages([\n", - " (\"system\", \"you're a helpful assistant\"),\n", + " (\"system\", \"You are a helpful assistant that can search the web for information. When faced with complex questions, break them down into smaller searchable parts and ask follow-up questions to gather the information needed. This is the 'Self Ask with Search' approach - you should decompose complex queries into simpler searchable components.\"),\n", " (\"human\", \"{input}\"),\n", " (\"placeholder\", \"{agent_scratchpad}\")\n", "])\n", @@ -1232,7 +1291,12 @@ " agent=agent,\n", " tools=toolbox,\n", " verbose=True\n", - ")" + ")\n", + "\n", + "# Now let's actually test it with a complex question that demonstrates the self-ask approach\n", + "print(\"Testing Self Ask with Search agent...\")\n", + "result = self_ask_with_search.invoke({\"input\": \"What are the latest developments in renewable energy and how do they compare to fossil fuels in terms of cost and efficiency?\"})\n", + "print(f\"\\nFinal Result: {result['output']}\")" ] }, { @@ -1242,12 +1306,12 @@ "id": "0ec1c4c8" }, "source": [ - "We will not interact with this agent because for that we would need a serpapi key. However, by checking out the prompt we can see a few examples of how it works:" + "We can also check the prompt:" ] }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 28, "id": "e4134930", "metadata": { "colab": { @@ -1267,10 +1331,20 @@ "==================================================\n", "Prompt Template Structure:\n", "==================================================\n", - "System message: you're a helpful assistant\n", - "Human message template: {input}\n", - "Required variables: ['input']\n", - "Optional variables: ['agent_scratchpad']\n", + "Messages in prompt:\n", + "\n", + "Message 1:\n", + " Type: SystemMessagePromptTemplate\n", + " Content: You are a helpful assistant that can search the web for information. When faced with complex questions, break them down into smaller searchable parts and ask follow-up questions to gather the information needed. This is the 'Self Ask with Search' approach - you should decompose complex queries into simpler searchable components.\n", + "\n", + "Message 2:\n", + " Type: HumanMessagePromptTemplate\n", + " Content: {input}\n", + "\n", + "Message 3:\n", + " Type: MessagesPlaceholder\n", + "\n", + "Input variables: ['input']\n", "==================================================\n" ] } @@ -1282,17 +1356,24 @@ "print(\"Agent type:\", type(self_ask_with_search.agent).__name__)\n", "print(\"=\" * 50)\n", "\n", - "# To see the prompt template, we need to access it differently\n", - "agent_runnable = self_ask_with_search.agent.runnable\n", - "prompt_template = agent_runnable.steps[1]\n", - "\n", + "# Cleaner way to inspect the prompt template\n", "print(\"Prompt Template Structure:\")\n", "print(\"=\" * 50)\n", - "print(\"System message:\", prompt_template.messages[0].prompt.template)\n", - "print(\"Human message template:\", prompt_template.messages[1].prompt.template)\n", - "print(\"Required variables:\", prompt_template.input_variables)\n", - "print(\"Optional variables:\", prompt_template.optional_variables)\n", - "print(\"=\" * 50)\n" + "\n", + "# Get the prompt template directly from the agent\n", + "agent_prompt = self_ask_with_search.agent.runnable.steps[1]\n", + "\n", + "print(\"Messages in prompt:\")\n", + "for i, message in enumerate(agent_prompt.messages):\n", + " print(f\"\\nMessage {i+1}:\")\n", + " print(f\" Type: {message.__class__.__name__}\")\n", + " if hasattr(message, 'prompt') and hasattr(message.prompt, 'template'):\n", + " print(f\" Content: {message.prompt.template}\")\n", + " elif hasattr(message, 'content'):\n", + " print(f\" Content: {message.content}\")\n", + "\n", + "print(f\"\\nInput variables: {agent_prompt.input_variables}\")\n", + "print(\"=\" * 50)" ] }, { From d91394f03f5519a4c7010cddcdf5bd6d081ce739 Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Mon, 7 Jul 2025 16:32:41 +0400 Subject: [PATCH 5/6] fix: Fixing Broken URL Links --- .../langchain/handbook/06-langchain-agents.ipynb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index f5bcb13c..8caf01dd 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -17,7 +17,7 @@ "id": "1571b632" }, "source": [ - "#### [LangChain Handbook](https://pinecone.io/learn/langchain)\n", + "#### [LangChain Handbook](https://www.pinecone.io/learn/series/langchain/)\n", "\n", "# Agents 🤖\n", "\n", @@ -401,7 +401,7 @@ "source": [ "In this first example we will use slightly different type of agent - SQL Agent which can be instantiated with it's own method `create_sql_agent`. Other agents will be instantiated in more generic way as we will see below in other examples.\n", "

\n", - "This method uses *toolkit* instead of simple list of `tools`. You can read more about them in the [documentation](https://python.langchain.com/docs/modules/agents/toolkits/). For this use case, we will use `SQLDatabaseToolkit`." + "This method uses *toolkit* instead of simple list of `tools`. You can read more about them in the [documentation](https://python.langchain.com/docs/integrations/tools/). For this use case, we will use `SQLDatabaseToolkit`." ] }, { @@ -630,7 +630,7 @@ "id": "3a698f78" }, "source": [ - "Suffice it to say for now that **the LLM now has the ability to 'reason' on how to best use tools** to solve our query and can combine them in intelligent ways with just a brief description of each of them. If you want to learn more about this paradigm (MRKL) in detail, please refer to [this](https://arxiv.org/pdf/2205.00445.pdf) paper." + "Suffice it to say for now that **the LLM now has the ability to 'reason' on how to best use tools** to solve our query and can combine them in intelligent ways with just a brief description of each of them. If you want to learn more about this paradigm (MRKL) in detail, please refer to [this](https://arxiv.org/pdf/2205.00445) paper." ] }, { @@ -1173,7 +1173,7 @@ "\n", "In short, it contains several examples of the `Question` > `Thought` > `Action` > `Observation` loop, that include the `Search` and `Lookup` tools.\n", "\n", - "If you want to learn more about this approach [this](https://arxiv.org/pdf/2210.03629.pdf) is the paper for ReAct" + "If you want to learn more about this approach [this](https://arxiv.org/pdf/2210.03629) is the paper for ReAct" ] }, { @@ -1195,7 +1195,7 @@ "source": [ "This is the first-choice agent to use when using LLM's to extract information with a search engine. The agent will ask follow-up questions and use the search functionality to get intermediate answers that help it get to a final answer.\n", "\n", - "> You will need an API key from here: https://serpapi.com/manage-api-key" + "> You will need an API key from here: https://serpapi.com/" ] }, { @@ -1396,7 +1396,7 @@ "id": "5df324ba" }, "source": [ - "And.. again [here](https://arxiv.org/pdf/2210.03350.pdf) you have the paper to dive deeper!" + "And.. again [here](https://arxiv.org/pdf/2210.03350) you have the paper to dive deeper!" ] }, { @@ -1429,7 +1429,7 @@ "id": "e33dd90a" }, "source": [ - "Check the how-to [guides](https://langchain.readthedocs.io/en/latest/modules/agents/how_to_guides.html) for more!" + "Check the how-to [guides](https://python.langchain.com/docs/how_to) for more!" ] } ], From b062b02b988f64143e3ed496916f8022147a41d8 Mon Sep 17 00:00:00 2001 From: Siraj R Aizlewood Date: Mon, 7 Jul 2025 16:39:32 +0400 Subject: [PATCH 6/6] fix: Link Redirects to Version with Forward Slash at End. Flagged by PR Checks. --- learn/generation/langchain/handbook/06-langchain-agents.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn/generation/langchain/handbook/06-langchain-agents.ipynb b/learn/generation/langchain/handbook/06-langchain-agents.ipynb index 8caf01dd..ba8b82d1 100644 --- a/learn/generation/langchain/handbook/06-langchain-agents.ipynb +++ b/learn/generation/langchain/handbook/06-langchain-agents.ipynb @@ -1429,7 +1429,7 @@ "id": "e33dd90a" }, "source": [ - "Check the how-to [guides](https://python.langchain.com/docs/how_to) for more!" + "Check the how-to [guides](https://python.langchain.com/docs/how_to/) for more!" ] } ],