mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-20 06:35:46 +02:00
22 KiB
22 KiB
In [1]:
# %pip install -U --quiet langchain langgraph langchain_openai
# %pip install -U --quiet tavily-pythonIn [2]:
import getpass
import os
def _set_if_undefined(var: str) -> None:
if os.environ.get(var):
return
os.environ[var] = getpass.getpass(var)
# Optional: Configure tracing to visualize and debug the agent
_set_if_undefined("LANGCHAIN_API_KEY")
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "Reflexion"
_set_if_undefined("OPENAI_API_KEY")
_set_if_undefined("TAVILY_API_KEY")In [3]:
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.utilities.tavily_search import TavilySearchAPIWrapper
search = TavilySearchAPIWrapper()
tavily_tool = TavilySearchResults(api_wrapper=search, max_results=5)In [28]:
from collections import defaultdict
from typing import List
from langchain.output_parsers.openai_tools import (
JsonOutputToolsParser,
PydanticToolsParser,
)
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage
from langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation
# This a helper class we have that is useful for running tools
# It takes in an agent action and calls that tool and returns the result
tool_executor = ToolExecutor([tavily_tool])
# Parse the tool messages for the execution / invocation
parser = JsonOutputToolsParser(return_id=True)
def execute_tools(state: List[BaseMessage]) -> List[BaseMessage]:
tool_invocation: AIMessage = state[-1]
parsed_tool_calls = parser.invoke(tool_invocation)
ids = []
tool_invocations = []
for parsed_call in parsed_tool_calls:
for query in parsed_call["args"]["search_queries"]:
tool_invocations.append(
ToolInvocation(
# We only have this one for now. Would want to map it
# if we change
tool="tavily_search_results_json",
tool_input=query,
)
)
ids.append(parsed_call["id"])
outputs = tool_executor.batch(tool_invocations)
outputs_map = defaultdict(dict)
for id_, output, invocation in zip(ids, outputs, tool_invocations):
outputs_map[id_][invocation.tool_input] = output
return [
ToolMessage(content=json.dumps(query_outputs), tool_call_id=id_)
for id_, query_outputs in outputs_map.items()
]In [63]:
import datetime
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.pydantic_v1 import BaseModel, Field, ValidationError
from langchain_openai import ChatOpenAI
from langsmith import traceable
actor_prompt_template = ChatPromptTemplate.from_messages(
[
(
"system",
"""You are expert researcher.
Current time: {time}
1. {first_instruction}
2. Reflect and critique your answer. Be severe to maximize improvement.
3. Recommend search queries to research information and improve your answer.""",
),
MessagesPlaceholder(variable_name="messages"),
("system", "Answer the user's question above using the required format."),
]
).partial(
time=lambda: datetime.datetime.now().isoformat(),
)
class Reflection(BaseModel):
missing: str = Field(description="Critique of what is missing.")
superfluous: str = Field(description="Critique of what is superfluous")
class AnswerQuestion(BaseModel):
"""Answer the question."""
answer: str = Field(description="~250 word detailed answer to the question.")
reflection: Reflection = Field(description="Your reflection on the initial answer.")
search_queries: List[str] = Field(
description="1-3 search queries for researching improvements to address the critique of your current answer."
)
llm = ChatOpenAI(model="gpt-4-turbo-preview")
initial_answer_chain = actor_prompt_template.partial(
first_instruction="Provide a detailed ~250 word answer."
) | llm.bind_tools(tools=[AnswerQuestion], tool_choice="AnswerQuestion")
validator = PydanticToolsParser(tools=[AnswerQuestion])
class ResponderWithRetries:
def __init__(self, runnable, validator):
self.runnable = runnable
self.validator = validator
@traceable
def respond(self, state: List[BaseMessage]):
response = []
for attempt in range(3):
try:
response = self.runnable.invoke({"messages": state})
self.validator.invoke(response)
return response
except ValidationError as e:
state = state + [HumanMessage(content=repr(e))]
return responseIn [64]:
first_responder = ResponderWithRetries(
runnable=initial_answer_chain, validator=validator
)In [65]:
example_question = "Why is reflection useful in AI?"
initial = first_responder.respond([HumanMessage(content=example_question)])In [ ]:
parsed = parser.invoke(initial)
parsedIn [67]:
revise_instructions = """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.
"""
# Extend the initial answer schema to include references.
# Forcing citation in the model encourages grounded responses
class ReviseAnswer(AnswerQuestion):
"""Revise your original answer to your question."""
references: List[str] = Field(
description="Citations motivating your updated answer."
)
revision_chain = actor_prompt_template.partial(
first_instruction=revise_instructions
) | llm.bind_tools(tools=[ReviseAnswer], tool_choice="ReviseAnswer")
revision_validator = PydanticToolsParser(tools=[ReviseAnswer])
revisor = ResponderWithRetries(runnable=revision_chain, validator=revision_validator)In [68]:
import json
revised = revisor.respond(
[
HumanMessage(content=""),
initial,
ToolMessage(
tool_call_id=initial.additional_kwargs["tool_calls"][0]["id"],
content=json.dumps(
tavily_tool.invoke(str(parsed[0]["args"]["search_queries"]))
),
),
]
)In [69]:
parsed = parser.invoke(revised)
parsedOut [69]:
[{'type': 'ReviseAnswer',
'args': {'answer': "Reflection in AI refers to the ability of AI systems to analyze and adapt their behavior and algorithms autonomously. This introspective capability enhances AI's performance and adaptability, making it crucial for learning, transparency, and optimization. \n\nReflection enables AI to learn from experiences, adjusting strategies for better decision-making. For example, Google DeepMind's AI has shown significant advancements in learning and adapting strategies in various environments [1]. Moreover, AI systems can explain their decisions, supporting the development of explainable AI (XAI), vital in sensitive sectors like healthcare and autonomous driving. This increases user trust and acceptance by providing insights into AI's decision-making processes. \n\nAdditionally, reflection aids in debugging and improving AI models by identifying weaknesses and suggesting enhancements. For instance, AI in healthcare, like the Mayo Clinic's use of medical data analytics, demonstrates how reflective AI can optimize algorithms to provide better patient care [2]. \n\nIn summary, reflection in AI fosters learning and adaptation, enhances transparency and trust, and facilitates model optimization, contributing to the development of sophisticated, reliable AI systems.",
'reflection': {'missing': 'The previous answer lacked specific examples and case studies to illustrate the benefits of reflection in AI. Including such examples would provide a more concrete understanding of the concept and its applications.',
'superfluous': 'The initial answer was comprehensive but could benefit from direct examples to demonstrate the practical applications and benefits of reflection in AI, rather than a broad overview without concrete cases.'},
'search_queries': ['Google DeepMind reflective AI examples',
'Mayo Clinic AI case study',
'Reflective AI benefits in healthcare'],
'references': ['https://casestudybuddy.com/blog/best-ai-case-study-examples/',
'https://indatalabs.com/blog/artificial-intelligence-case-studies']},
'id': 'call_0kZkZgn5DP2Z8VhRtxGkXDp5'}]In [71]:
from langgraph.graph import END, MessageGraph
MAX_ITERATIONS = 5
builder = MessageGraph()
builder.add_node("draft", first_responder.respond)
builder.add_node("execute_tools", execute_tools)
builder.add_node("revise", revisor.respond)
# draft -> execute_tools
builder.add_edge("draft", "execute_tools")
# execute_tools -> revise
builder.add_edge("execute_tools", "revise")
# Define looping logic:
def _get_num_iterations(state: List[BaseMessage]):
i = 0
for m in state[::-1]:
if not isinstance(m, (ToolMessage, AIMessage)):
break
i += 1
return i
def event_loop(state: List[BaseMessage]) -> str:
# in our case, we'll just stop after N plans
num_iterations = _get_num_iterations(state)
if num_iterations > MAX_ITERATIONS:
return END
return "execute_tools"
# revise -> execute_tools OR end
builder.add_conditional_edges("revise", event_loop)
builder.set_entry_point("draft")
graph = builder.compile()In [72]:
events = graph.stream(
[HumanMessage(content="How should we handle the climate crisis?")]
)
for i, step in enumerate(events):
node, output = next(iter(step.items()))
print(f"## {i+1}. {node}")
print(str(output)[:100] + " ...")
print("---")## 1. draft
content='' additional_kwargs={'tool_calls': [{'id': 'call_GOmUTyAeA8kLLm4G9sXZ4jGV', 'function': {'a ...
---
## 2. execute_tools
[ToolMessage(content='{"successful climate policies examples": [{"url": "https://www.washingtonpost. ...
---
## 3. revise
content='' additional_kwargs={'tool_calls': [{'id': 'call_Z0dky70zr74bLi6dBfQTCj6j', 'function': {'a ...
---
## 4. execute_tools
[ToolMessage(content='{"successful climate policies examples": [{"url": "https://www.washingtonpost. ...
---
## 5. revise
content='' additional_kwargs={'tool_calls': [{'id': 'call_tM6DgVvQoux8IDIkRlrUKwOj', 'function': {'a ...
---
## 6. execute_tools
[ToolMessage(content='{"successful climate policies examples": [{"url": "https://www.washingtonpost. ...
---
## 7. revise
content='' additional_kwargs={'tool_calls': [{'id': 'call_XkarDDuEf43cOBPn9zNXN8vM', 'function': {'a ...
---
## 8. __end__
[HumanMessage(content='How should we handle the climate crisis?'), AIMessage(content='', additional_ ...
---
In [77]:
print(parser.invoke(step[END][-1])[0]["args"]["answer"])Addressing the climate crisis requires a comprehensive approach, combining policy, technology, and finance. Successful policy initiatives include the U.S. Army's carbon footprint reduction, federal funding to plug methane-leaking wells, and Ithaca, NY's building decarbonization [1]. Renewable energy, particularly solar and wind, is forecasted to surpass coal by 2025, demonstrating the critical role of transitioning to sustainable energy sources [2]. Technological innovations are essential, with significant advancements in solar cell efficiency, data-driven climate adaptation technologies, and efforts to replace or mitigate major emission sources [3][4][5]. Financial mechanisms are pivotal, with climate finance needing a substantial increase to meet global warming limits. The U.S. has made progress by significantly enhancing its international public climate finance, exemplifying financial commitment to supporting global climate action [6]. This holistic strategy, integrating policy, technological innovation, and finance, represents the most effective way to tackle the climate crisis, emphasizing sustainability, innovation, and global cooperation. References: [1] https://www.washingtonpost.com/climate-solutions/2022/04/21/climate-change-policy-examples-list/ [2] https://www.weforum.org/agenda/2024/01/climate-transition-tipping-point/ [3] https://www.technologyreview.com/2024/01/11/1086412/three-climate-technologies-breaking-through-in-2024/ [4] https://unfccc.int/news/how-climate-technology-is-being-ramped-up [5] https://www.weforum.org/agenda/2024/02/ai-climate-adaptation-technologies/ [6] https://www.state.gov/progress-report-on-president-bidens-climate-finance-pledge/
In [ ]:
