mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-22 23:52:23 +02:00
20 KiB
20 KiB
In [1]:
from langchain import hub
from langchain.agents import create_openai_functions_agent
from langchain.chat_models import ChatOpenAI
from langchain_community.chat_models import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.runnables import RunnablePassthrough
from langgraph.graph import END, Graph
tools = [TavilySearchResults(max_results=1)]
# Get the prompt to use - you can modify this!
prompt = hub.pull("hwchase17/openai-functions-agent")
# Choose the LLM that will drive the agent
llm = ChatOpenAI(model="gpt-3.5-turbo-1106")
# Construct the OpenAI Functions agent
agent_runnable = create_openai_functions_agent(llm, tools, prompt)
from langchain_core.agents import AgentFinish
# Define decision-making logic
def should_continue(data):
# Logic to decide whether to continue in the loop or exit
if isinstance(data['agent_outcome'], AgentFinish):
return "exit"
else:
return "continue"
def execute_tools(data):
agent_action = data.pop('agent_outcome')
observation = {t.name: t for t in tools}[agent_action.tool].invoke(agent_action.tool_input)
data['intermediate_steps'].append((agent_action, observation))
return data
# Define agents
agent = RunnablePassthrough.assign(
agent_outcome = agent_runnable
)
# Define a new graph
workflow = Graph()
workflow.add_node("agent", agent)
workflow.add_node("tools", execute_tools)
workflow.set_entry_point("agent")
workflow.add_conditional_edges(
"agent",
should_continue,
{
"continue": "tools",
"exit": END
}
)
workflow.add_edge('tools', 'agent')
chain = workflow.compile()In [2]:
chain.invoke({"input": "what is the weather in sf", "intermediate_steps": []})Out [2]:
{'input': 'what is the weather in sf',
'intermediate_steps': [(AgentActionMessageLog(tool='tavily_search_results_json', tool_input={'query': 'weather in San Francisco'}, log="\nInvoking: `tavily_search_results_json` with `{'query': 'weather in San Francisco'}`\n\n\n", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'name': 'tavily_search_results_json', 'arguments': '{"query":"weather in San Francisco"}'}})]),
[{'url': 'https://www.weather25.com/north-america/usa/california/san-francisco',
'content': 'will give you an idea of weather trends in San Francisco. For example, the weather in San Francisco in January 2024. San Francisco 14 day weather The weather today in San Francisco San Francisco weather report The weather in San Francisco, United States San Francisco weather by months San Francisco weather the weather in San Francisco including humidity, wind, chance of rain and more on the San Francisco current weather01 January 02 February 03 March 04 April 05 May 06 June 07 July 08 August 09 September 10 October 11 November 12 December. ... For example, the weather in San Francisco in January 2024. These trends can be helpful when planning trips to San Francisco or preparing for the weather in advance. There are many factors to consider when looking at the ...'}])],
'agent_outcome': AgentFinish(return_values={'output': 'For the current weather in San Francisco, you can visit the following website: [San Francisco Weather](https://www.weather25.com/north-america/usa/california/san-francisco). This will provide you with the latest weather updates including humidity, wind, chance of rain, and more.'}, log='For the current weather in San Francisco, you can visit the following website: [San Francisco Weather](https://www.weather25.com/north-america/usa/california/san-francisco). This will provide you with the latest weather updates including humidity, wind, chance of rain, and more.')}In [3]:
from langchain.agents import AgentExecutor, BaseMultiActionAgent, Tool
from langchain.schema import AgentAction, AgentFinish
from langchain_core.language_models.chat_models import BaseChatModel
from langchain.chains import LLMChain
from langchain.globals import set_llm_cache
from dotenv import load_dotenv
from pydantic import BaseModel
from langchain.chat_models import ChatOpenAI
from langchain.cache import SQLiteCache
from langchain_core.output_parsers import BaseOutputParser
from langchain.prompts.chat import ChatPromptTemplate
from langchain.callbacks import get_openai_callback
from langchain.tools.tavily_search import TavilySearchResults
from langchain.utilities.tavily_search import TavilySearchAPIWrapper
from langchain.pydantic_v1 import BaseModel
import os
from langchain.agents import AgentType, initialize_agent, load_tools
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
llm = ChatOpenAI(
temperature=0.0,
max_tokens=2000,
max_retries=100,
model="gpt-4-1106-preview",
)
search = TavilySearchAPIWrapper()
tavily_tool = TavilySearchResults(api_wrapper=search, max_results=5)
NEXT_STEP_TEMPLATE = """You are expert researcher trying answer a question ~250 words. You are asked to answer the following question: {question}
The way you are going to answer the question is as follows:
1. Revise your previous answer using the new information.
- You should use the previous critique to add important information to your answer.
_ You MUST include numerical citations in your revised answer to ensure it can be verified.
- Add a "References" section to the bottom of your answer (which does not count towards the word limit). In form of:
- [1] https://example.com
- [2] https://example.com
- You should use the previous critique to remove superfluous information from your answer and make SURE it is not more than 250 words.
2. Reflect and critique your answer. Specifically, you should:
- Think about what is missing from your answer.
- Think about what is superfluous in your answer.
- Think about what search query you should use next to improve your answer.
Give your answer in exactly 2 parts. The first should address what is missing from your answer. The second should address what could be removed from your answer. Your should be VERY harsh as we really want to improve the answer.
3. Give the search query you came up with to improve your answer.
Previous steps:
{previous_steps}
===
Format your answer as follows:
Revised answer: [give your revised answer based on the previous critique and new information from the search engine then the "References" section]
Critique: [give your harsh critique of your revised answer in 2 parts: what is missing and what is superfluous]
Search query: [give the new search query you came up with to enter into the search engine to improve your answer. If you have more than one, make sure they are comma separated and in quotes]
SAY NOTHING else please."""
INITIAL_ANSWER_TEMPLATE = """You are expert researcher trying answer a question ~250 words. You are asked to answer the following question: {question}
The way you are going to answer the question is as follows:
1. Give a detailed in ~250 words.
2. Reflect and critique your answer. Specifically, you should:
- Think about what is missing from your answer.
- Think about what is superfluous in your answer.
- Think about what search query you should use next to improve your answer.
Give your answer in exactly 2 parts. The first should address what is missing from your answer. The second should address what could be removed from your answer. Your should be VERY harsh as we really want to improve the answer.
3. Give the search query you came up with to improve your answer.
===
Format your answer as follows:
Answer: [give your initial answer]
Critique: [give your harsh critique of your answer in 2 parts: what is missing and what is superfluous]
Search query: [give the search query you came up with to improve your answer. If you have more than one, make sure they are comma separated and in quotes]
SAY NOTHING else please."""
class ReflexionStep(BaseModel):
"""A single step in the reflexion process."""
answer: str
critique: str
search_query: str
def __str__(self):
return f"Answer: {self.answer}\nCritique: {self.critique}\nSearch query: {self.search_query}"
def _parse_reflexion_step(output: str) -> tuple[str, str, str]:
# find answer using .split()
if ("Answer:" not in output and "Revised answer:" not in output) or not "Critique:" in output or not "Search query:" in output:
raise ValueError(f"The output is not formatted correctly. Output: {output}")
if "Answer:" in output:
answer = output.split("Answer:")[1].split("Critique:")[0].strip()
else:
answer = output.split("Revised answer:")[1].split("Critique:")[0].strip()
critique = output.split("Critique:")[1].split("Search query:")[0].strip()
search_query = output.split("Search query:")[1].strip()
return answer, critique, search_query
class ReflexionStepParser(BaseOutputParser[ReflexionStep]):
"""Parser for the reflexion step."""
def parse(self, output: str) -> ReflexionStep:
"""Parse the output."""
# try to find answer or initial answer
answer, critique, search_query = _parse_reflexion_step(output)
return ReflexionStep(
answer=answer, critique=critique, search_query=search_query
)In [4]:
initial_chain = RunnablePassthrough.assign(
agent_outcome = ChatPromptTemplate.from_template(INITIAL_ANSWER_TEMPLATE) | llm | ReflexionStepParser() | (lambda x: AgentAction(
tool="tavily_search_results_json",
tool_input=x.search_query,
log=str(x),
))
)
def prep_next(inputs):
intermediate_steps = inputs["intermediate_steps"]
previous_steps = list[str]()
for i, (action, observation) in enumerate(intermediate_steps, start=1):
last_step_str = f"""Step {i}:
{action.log}
Search output for "{action.tool_input}":
{observation}"""
previous_steps.append(last_step_str)
previous_steps_str = "\n\n".join(previous_steps)
inputs["previous_steps"] = previous_steps_str
return inputs
next_chain = RunnablePassthrough.assign(
agent_outcome = prep_next | ChatPromptTemplate.from_template(NEXT_STEP_TEMPLATE) | llm | ReflexionStepParser() | (lambda x: AgentAction(
tool="tavily_search_results_json",
tool_input=x.search_query,
log=str(x),
))
)
def finish(inputs):
intermediate_steps = inputs["intermediate_steps"]
last_action, _ = intermediate_steps[-1]
last_step_str = last_action.log
# extract answer
answer, _, _ = _parse_reflexion_step(last_step_str)
first_action, _ = intermediate_steps[0]
first_step_str = first_action.log
# extract answer
initial_answer, _, _ = _parse_reflexion_step(first_step_str)
return AgentFinish(
log="Reached max steps.",
return_values={"output": answer, "initial_answer": initial_answer},
)
def execute_tools(data):
agent_action = data.pop('agent_outcome')
observation = {t.name: t for t in tools}[agent_action.tool].invoke(agent_action.tool_input)
data['intermediate_steps'].append((agent_action, observation))
return data
In [5]:
workflow = Graph()
# add actors
workflow.add_node("initial", initial_chain)
workflow.add_node("next", next_chain)
workflow.add_node("finish", finish)
workflow.add_node("tools", execute_tools)
# Enter with initial actor, then loop through tools -> next steps until finished
workflow.set_entry_point('initial')
workflow.add_edge('initial', 'tools')
workflow.add_conditional_edges(
'tools',
lambda x: "exit" if len(x['intermediate_steps']) >= 2 else "continue",
{
"continue": 'next',
"exit": 'finish'
}
)
workflow.add_edge('next', 'tools')
workflow.set_finish_point('finish')
chain = workflow.compile()
chain.invoke({"question": "what is the weather in sf", "intermediate_steps": []})Out [5]:
AgentFinish(return_values={'output': "The current weather in San Francisco can be accessed through various weather reporting services, which provide real-time temperature, humidity, wind, and chances of precipitation [1]. Historically, San Francisco experiences a mild, Mediterranean climate with average temperatures ranging from the low 50s to the mid-60s Fahrenheit. The city's unique topography creates microclimates, leading to significant weather variations across different neighborhoods. San Francisco's summers are notably cooler compared to other Californian cities, largely due to the cold California Current and persistent fog, especially in June and July. Winters are mild and the wettest months span from November to March, with an annual rainfall average of approximately 23 inches. Wind is a prominent feature, with spring being particularly windy. For historical weather extremes and average wind speeds, additional specific data can be sought from climatological records.\n\nReferences:\n[1] https://www.weather25.com/north-america/usa/california/san-francisco", 'initial_answer': "The weather in San Francisco (SF) is characterized by a mild, Mediterranean-like climate with wet winters and dry summers. The city's unique topography and coastal location result in microclimates, where weather conditions can vary significantly from one neighborhood to another. Average temperatures typically range from the low 50s to the mid-60s Fahrenheit throughout the year. Summers in San Francisco are often cooler than in other parts of California due to the cold California Current offshore and the presence of fog, particularly in June and July. The fog usually burns off by the afternoon, leading to clearer skies and slightly warmer temperatures. Winters are mild and moist, with the majority of the city's rainfall occurring between November and March. Rainfall averages around 23 inches annually. Wind is also a notable feature of San Francisco's weather, with spring being the windiest season. Despite the general patterns, it's always advisable to dress in layers due to the potential for rapid weather changes."}, log='Reached max steps.')In [ ]:
In [ ]: