mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-20 14:42:28 +02:00
23 KiB
23 KiB
In [ ]:
import os
os.environ["OPENAI_API_KEY"] = "sk-xxx"
os.environ["TAVILY_API_KEY"] = "tvly-xxx"In [6]:
%pip install -qU langchain-ollama[1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m A new release of pip is available: [0m[31;49m24.0[0m[39;49m -> [0m[32;49m24.1.2[0m [1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m To update, run: [0m[32;49mpip install --upgrade pip[0m Note: you may need to restart the kernel to use updated packages.
In [8]:
# /// LLM ///
from langchain_ollama import ChatOllama
llm = ChatOllama(
# model="llama3-groq-tool-use",
model="llama3.1",
temperature=0,
)
# /// Retriever tool ///
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import SKLearnVectorStore
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
# List of URLs to load documents from
urls = [
"https://lilianweng.github.io/posts/2023-06-23-agent/",
"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/",
"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/",
]
# Load documents from the URLs
docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]
# Initialize a text splitter with specified chunk size and overlap
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=250, chunk_overlap=0
)
# Split the documents into chunks
doc_splits = text_splitter.split_documents(docs_list)
# Add the document chunks to the "vector store" using OpenAIEmbeddings
vectorstore = SKLearnVectorStore.from_documents(
documents=doc_splits,
embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever(k=4)
# Define a tool, which we will connect to our agent
def retrieve_documents(query: str) -> list:
"""Retrieve documents from the vector store based on the query."""
return retriever.invoke(query)
# /// Search Tool
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.schema import Document
web_search_tool = TavilySearchResults()
def web_search(query: str) -> str:
"""Run web search on the question."""
web_results = web_search_tool.invoke({"query": query})
return [
Document(page_content=d["content"], metadata={"url": d["url"]})
for d in web_results
]
# Tool list
tools = [retrieve_documents, web_search]In [9]:
from typing import Annotated, List
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import Runnable, RunnableConfig
from langgraph.graph.message import AnyMessage, add_messages
from typing_extensions import TypedDict
class State(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
class Assistant:
def __init__(self, runnable: Runnable):
"""
Initialize the Assistant with a runnable object.
Args:
runnable (Runnable): The runnable instance to invoke.
"""
self.runnable = runnable
def __call__(self, state: State, config: RunnableConfig):
"""
Call method to invoke the LLM and handle its responses.
Re-prompt the assistant if the response is not a tool call or meaningful text.
Args:
state (State): The current state containing messages.
config (RunnableConfig): The configuration for the runnable.
Returns:
dict: The final state containing the updated messages.
"""
while True:
result = self.runnable.invoke(state) # Invoke the LLM
if not result.tool_calls and (
not result.content
or isinstance(result.content, list)
and not result.content[0].get("text")
):
messages = state["messages"] + [("user", "Respond with a real output.")]
state = {**state, "messages": messages}
else:
break
return {"messages": result}
# Create the primary assistant prompt template
primary_assistant_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful assistant tasked with answering user questions. "
"You have access to two tools: retrieve_documents and web_search. "
"For any user questions about LLM agents, use the retrieve_documents tool to get information for a vectorstore. "
"For any other questions, such as questions about current events, use the web_search tool to get information from the web. ",
),
("placeholder", "{messages}"),
]
)
# Prompt our LLM and bind tools
assistant_runnable = primary_assistant_prompt | llm.bind_tools(tools)In [10]:
from langchain_core.messages import ToolMessage
from langchain_core.runnables import RunnableLambda
from langgraph.prebuilt import ToolNode
def create_tool_node_with_fallback(tools: list) -> dict:
return ToolNode(tools).with_fallbacks(
[RunnableLambda(handle_tool_error)], exception_key="error"
)
def handle_tool_error(state: State) -> dict:
error = state.get("error")
tool_calls = state["messages"][-1].tool_calls
return {
"messages": [
ToolMessage(
content=f"Error: {repr(error)}\n please fix your mistakes.",
tool_call_id=tc["id"],
)
for tc in tool_calls
]
}
from IPython.display import Image, display
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import END, START, StateGraph
from langgraph.prebuilt import tools_condition
# Graph
builder = StateGraph(State)
# Define nodes: these do the work
builder.add_node("assistant", Assistant(assistant_runnable))
builder.add_node("tools", create_tool_node_with_fallback(tools))
# Define edges: these determine how the control flow moves
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
"assistant",
# If the latest message (result) from assistant is a tool call -> tools_condition routes to tools
# If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END
tools_condition,
)
builder.add_edge("tools", "assistant")
# The checkpointer lets the graph persist its state
memory = SqliteSaver.from_conn_string(":memory:")
react_graph = builder.compile(checkpointer=memory)
# Show
display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))In [11]:
import uuid
def predict_react_agent_answer(example: dict):
"""Use this for answer evaluation"""
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
messages = react_graph.invoke({"messages": ("user", example["input"])}, config)
return {"response": messages["messages"][-1].content, "messages": messages}
example = {"input": "Get me information about the the types of LLM agent memory?"}
response = predict_react_agent_answer(example)In [13]:
example = {"input": "Get me information about the current weather in SF."}
response = predict_react_agent_answer(example)In [ ]: