Skip to content

Commit

Permalink
[Docs] Force call tool first (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
hinthornw committed May 18, 2024
1 parent 4570b2b commit b04f583
Show file tree
Hide file tree
Showing 16 changed files with 1,812 additions and 753 deletions.
4 changes: 4 additions & 0 deletions docs/docs/how-tos/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ How to apply common design patterns in your workflows:
- [Subgraphs](subgraph.ipynb): How to compose subgraphs within a larger graph
- [Branching](branching.ipynb): How to create branching logic in your graphs for parallel node execution
- [Human-in-the-loop](human-in-the-loop.ipynb): How to incorporate human feedback and intervention

The following examples are useful especially if you are used to LangChain's AgentExecutor configurations.

- [Force calling a tool first](force-calling-a-tool-first.ipynb): Define a fixed workflow before ceding control to the ReAct agent
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ nav:
- "how-tos/branching.ipynb"
- "how-tos/subgraph.ipynb"
- "how-tos/human-in-the-loop.ipynb"
- "how-tos/force-calling-a-tool-first.ipynb"
- "Conceptual Guides":
- "concepts/index.md"
- "Reference":
Expand Down
87 changes: 51 additions & 36 deletions examples/agent_executor/base.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"source": [
"# Agent Executor From Scratch\n",
"\n",
"In this notebook we will go over how to build a basic agent executor from scratch.\n",
"In this notebook we will go over how to build a basic agent executor from\n",
"scratch.\n",
"\n",
"![diagram](./img/agent-executor-diagram.png)"
]
Expand All @@ -18,6 +19,7 @@
"metadata": {},
"source": [
"## Setup¶\n",
"\n",
"First we need to install the packages required\n",
"\n",
"```bash\n",
Expand All @@ -30,7 +32,8 @@
"id": "5f4179ce-48fa-4aaf-a5a1-027b5229be1a",
"metadata": {},
"source": [
"Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)\n",
"Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the\n",
"search tool we will use)\n",
"\n",
"```bash\n",
"export OPENAI_API_KEY=\n",
Expand All @@ -43,7 +46,9 @@
"id": "37943b1c-2b0a-4c09-bfbd-5dc24b839e3c",
"metadata": {},
"source": [
"Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability.\n",
"Optionally, we can set API key for\n",
"[LangSmith tracing](https://smith.langchain.com/), which will give us\n",
"best-in-class observability.\n",
"\n",
"```bash\n",
"export LANGCHAIN_TRACING_V2=true\n",
Expand All @@ -58,7 +63,8 @@
"source": [
"## Create the LangChain agent\n",
"\n",
"First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://js.langchain.com/docs/modules/agents/)."
"First, we will create the LangChain agent. For more information on LangChain\n",
"agents, see [this documentation](https://js.langchain.com/docs/modules/agents/)."
]
},
{
Expand Down Expand Up @@ -89,20 +95,20 @@
"\n",
"// Get the prompt to use - you can modify this!\n",
"const prompt = await pull<ChatPromptTemplate>(\n",
" \"hwchase17/openai-functions-agent\"\n",
" \"hwchase17/openai-functions-agent\",\n",
");\n",
"\n",
"// Choose the LLM that will drive the agent\n",
"const llm = new ChatOpenAI({\n",
" modelName: \"gpt-4-1106-preview\",\n",
" temperature: 0\n",
" temperature: 0,\n",
"});\n",
"\n",
"// Construct the OpenAI Functions agent\n",
"const agentRunnable = await createOpenAIFunctionsAgent({\n",
" llm,\n",
" tools,\n",
" prompt\n",
" prompt,\n",
"});"
]
},
Expand All @@ -113,11 +119,16 @@
"source": [
"## Define the graph schema\n",
"\n",
"We now define the graph state. The state for the traditional LangChain agent has a few attributes:\n",
"We now define the graph state. The state for the traditional LangChain agent has\n",
"a few attributes:\n",
"\n",
"1. `input`: This is the input string representing the main ask from the user, passed in as input.\n",
"3. `steps`: This is list of actions and corresponding observations that the agent takes over time. This is updated each iteration of the agent.\n",
"4. `agentOutcome`: This is the response from the agent, either an AgentAction or AgentFinish. The AgentExecutor should finish when this is an AgentFinish, otherwise it should call the requested tools.\n"
"1. `input`: This is the input string representing the main ask from the user,\n",
" passed in as input.\n",
"2. `steps`: This is list of actions and corresponding observations that the\n",
" agent takes over time. This is updated each iteration of the agent.\n",
"3. `agentOutcome`: This is the response from the agent, either an AgentAction or\n",
" AgentFinish. The AgentExecutor should finish when this is an AgentFinish,\n",
" otherwise it should call the requested tools."
]
},
{
Expand All @@ -129,16 +140,16 @@
"source": [
"const agentState = {\n",
" input: {\n",
" value: null\n",
" value: null,\n",
" },\n",
" steps: {\n",
" value: (x, y) => x.concat(y),\n",
" default: () => []\n",
" default: () => [],\n",
" },\n",
" agentOutcome: {\n",
" value: null\n",
" }\n",
"};\n"
" value: null,\n",
" },\n",
"};"
]
},
{
Expand All @@ -148,24 +159,28 @@
"source": [
"## Define the nodes\n",
"\n",
"We now need to define a few different nodes in our graph.\n",
"In `langgraph`, a node can be either a function or a [runnable](https://js.langchain.com/docs/expression_language/).\n",
"There are two main nodes we need for this:\n",
"We now need to define a few different nodes in our graph. In `langgraph`, a node\n",
"can be either a function or a\n",
"[runnable](https://js.langchain.com/docs/expression_language/). There are two\n",
"main nodes we need for this:\n",
"\n",
"1. The agent: responsible for deciding what (if any) actions to take.\n",
"2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
"2. A function to invoke tools: if the agent decides to take an action, this node\n",
" will then execute that action.\n",
"\n",
"We will also need to define some edges.\n",
"Some of these edges may be conditional.\n",
"The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
"The path that is taken is not known until that node is run (the LLM decides).\n",
"We will also need to define some edges. Some of these edges may be conditional.\n",
"The reason they are conditional is that based on the output of a node, one of\n",
"several paths may be taken. The path that is taken is not known until that node\n",
"is run (the LLM decides).\n",
"\n",
"1. Conditional Edge: after the agent is called, we should either:\n",
" a. If the agent said to take an action, then the function to invoke tools should be called\n",
" b. If the agent said that it was finished, then it should finish\n",
"2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
"1. Conditional Edge: after the agent is called, we should either: a. If the\n",
" agent said to take an action, then the function to invoke tools should be\n",
" called b. If the agent said that it was finished, then it should finish\n",
"2. Normal Edge: after the tools are invoked, it should always go back to the\n",
" agent to decide what to do next\n",
"\n",
"Let's define the nodes, as well as a function to decide how what conditional edge to take."
"Let's define the nodes, as well as a function to decide how what conditional\n",
"edge to take."
]
},
{
Expand Down Expand Up @@ -216,7 +231,7 @@
" }\n",
" const output = await toolExecutor.invoke(agentAction, config);\n",
" return {\n",
" steps: [{ action: agentAction, observation: JSON.stringify(output) }]\n",
" steps: [{ action: agentAction, observation: JSON.stringify(output) }],\n",
" };\n",
"};"
]
Expand All @@ -243,7 +258,7 @@
"\n",
"// Define a new graph\n",
"const workflow = new StateGraph({\n",
" channels: agentState\n",
" channels: agentState,\n",
"});\n",
"\n",
"// Define the two nodes we will cycle between\n",
Expand Down Expand Up @@ -271,8 +286,8 @@
" // If `tools`, then we call the tool node.\n",
" continue: \"action\",\n",
" // Otherwise we finish.\n",
" end: END\n",
" }\n",
" end: END,\n",
" },\n",
");\n",
"\n",
"// We now add a normal edge from `tools` to `agent`.\n",
Expand Down Expand Up @@ -370,10 +385,10 @@
}
],
"source": [
"const inputs = { input: \"what is the weather in sf\" }\n",
"const inputs = { input: \"what is the weather in sf\" };\n",
"for await (const s of await app.stream(inputs)) {\n",
" console.log(s)\n",
" console.log(\"----\\n\")\n",
" console.log(s);\n",
" console.log(\"----\\n\");\n",
"}"
]
}
Expand Down
Loading

0 comments on commit b04f583

Please sign in to comment.