在 [1]
已复制!
%%capture --no-stderr
%pip install -U langgraph langchain langchain_openai langchain_experimental langsmith pandas
%%capture --no-stderr %pip install -U langgraph langchain langchain_openai langchain_experimental langsmith pandas
在 [1]
已复制!
import getpass
import os
def _set_if_undefined(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"Please provide your {var}")
_set_if_undefined("OPENAI_API_KEY")
_set_if_undefined("TAVILY_API_KEY")
import getpass import os def _set_if_undefined(var: str): if not os.environ.get(var): os.environ[var] = getpass.getpass(f"请提供你的 {var}") _set_if_undefined("OPENAI_API_KEY") _set_if_undefined("TAVILY_API_KEY")
创建工具¶
在这个示例中,你将创建一个智能体来使用搜索引擎进行网络研究,以及一个智能体来创建图表。在下面定义他们将使用的工具
在 [1]
已复制!
from typing import Annotated
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_experimental.tools import PythonREPLTool
tavily_tool = TavilySearchResults(max_results=5)
# This executes code locally, which can be unsafe
python_repl_tool = PythonREPLTool()
from typing import Annotated from langchain_community.tools.tavily_search import TavilySearchResults from langchain_experimental.tools import PythonREPLTool tavily_tool = TavilySearchResults(max_results=5) # 这在本地执行代码,这可能不安全 python_repl_tool = PythonREPLTool()
辅助工具¶
定义一个辅助函数,我们将使用它来创建图中的节点——它负责将智能体响应转换为人类消息。这很重要,因为这是我们将它添加到图的全局状态的方式
在 [2]
已复制!
from langchain_core.messages import HumanMessage
def agent_node(state, agent, name):
result = agent.invoke(state)
return {
"messages": [HumanMessage(content=result["messages"][-1].content, name=name)]
}
from langchain_core.messages import HumanMessage def agent_node(state, agent, name): result = agent.invoke(state) return { "messages": [HumanMessage(content=result["messages"][-1].content, name=name)] }
创建智能体主管¶
它将使用函数调用来选择下一个工作节点或完成处理。
在 [3]
已复制!
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from typing import Literal
members = ["Researcher", "Coder"]
system_prompt = (
"You are a supervisor tasked with managing a conversation between the"
" following workers: {members}. Given the following user request,"
" respond with the worker to act next. Each worker will perform a"
" task and respond with their results and status. When finished,"
" respond with FINISH."
)
# Our team supervisor is an LLM node. It just picks the next agent to process
# and decides when the work is completed
options = ["FINISH"] + members
class routeResponse(BaseModel):
next: Literal[*options]
prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder(variable_name="messages"),
(
"system",
"Given the conversation above, who should act next?"
" Or should we FINISH? Select one of: {options}",
),
]
).partial(options=str(options), members=", ".join(members))
llm = ChatOpenAI(model="gpt-4o")
def supervisor_agent(state):
supervisor_chain = prompt | llm.with_structured_output(routeResponse)
return supervisor_chain.invoke(state)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_openai import ChatOpenAI from pydantic import BaseModel from typing import Literal members = ["Researcher", "Coder"] system_prompt = ( "你是一个主管,负责管理以下工作者之间的对话:{members}。给定以下用户请求," " 回答下一个要执行的任务的工作者。每个工作者将执行一项任务并用其结果和状态进行响应。完成后," " 回答 FINISH。" ) # 我们的团队主管是一个 LLM 节点。它只是选择下一个要处理的智能体, # 并在工作完成时做出决定 options = ["FINISH"] + members class routeResponse(BaseModel): next: Literal[*options] prompt = ChatPromptTemplate.from_messages( [ ("system", system_prompt), MessagesPlaceholder(variable_name="messages"), ( "system", "给定上面的对话,谁应该接下来行动?" " 或者我们应该 FINISH? 选择以下一项:{options}", ), ] ).partial(options=str(options), members=", ".join(members)) llm = ChatOpenAI(model="gpt-4o") def supervisor_agent(state): supervisor_chain = prompt | llm.with_structured_output(routeResponse) return supervisor_chain.invoke(state)
构建图¶
我们已准备好开始构建图。在下面,使用我们刚刚定义的函数来定义状态和工作节点。
在 [4]
已复制!
import functools
import operator
from typing import Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph import END, StateGraph, START
from langgraph.prebuilt import create_react_agent
# The agent state is the input to each node in the graph
class AgentState(TypedDict):
# The annotation tells the graph that new messages will always
# be added to the current states
messages: Annotated[Sequence[BaseMessage], operator.add]
# The 'next' field indicates where to route to next
next: str
research_agent = create_react_agent(llm, tools=[tavily_tool])
research_node = functools.partial(agent_node, agent=research_agent, name="Researcher")
# NOTE: THIS PERFORMS ARBITRARY CODE EXECUTION. PROCEED WITH CAUTION
code_agent = create_react_agent(llm, tools=[python_repl_tool])
code_node = functools.partial(agent_node, agent=code_agent, name="Coder")
workflow = StateGraph(AgentState)
workflow.add_node("Researcher", research_node)
workflow.add_node("Coder", code_node)
workflow.add_node("supervisor", supervisor_agent)
import functools import operator from typing import Sequence from typing_extensions import TypedDict from langchain_core.messages import BaseMessage from langgraph.graph import END, StateGraph, START from langgraph.prebuilt import create_react_agent # 智能体状态是图中每个节点的输入 class AgentState(TypedDict): # 注释告诉图,新消息将始终 # 添加到当前状态的消息 messages: Annotated[Sequence[BaseMessage], operator.add] # 'next' 字段指示下一个路由位置 next: str research_agent = create_react_agent(llm, tools=[tavily_tool]) research_node = functools.partial(agent_node, agent=research_agent, name="Researcher") # 注意:这执行任意代码执行。请谨慎操作 code_agent = create_react_agent(llm, tools=[python_repl_tool]) code_node = functools.partial(agent_node, agent=code_agent, name="Coder") workflow = StateGraph(AgentState) workflow.add_node("Researcher", research_node) workflow.add_node("Coder", code_node) workflow.add_node("supervisor", supervisor_agent)
现在连接图中的所有边。
在 [5]
已复制!
for member in members:
# We want our workers to ALWAYS "report back" to the supervisor when done
workflow.add_edge(member, "supervisor")
# The supervisor populates the "next" field in the graph state
# which routes to a node or finishes
conditional_map = {k: k for k in members}
conditional_map["FINISH"] = END
workflow.add_conditional_edges("supervisor", lambda x: x["next"], conditional_map)
# Finally, add entrypoint
workflow.add_edge(START, "supervisor")
graph = workflow.compile()
for member in members: # 我们希望我们的工作者在完成时始终“报告给”主管 workflow.add_edge(member, "supervisor") # 主管填充图状态中的“next”字段 # 这将路由到一个节点或完成 conditional_map = {k: k for k in members} conditional_map["FINISH"] = END workflow.add_conditional_edges("supervisor", lambda x: x["next"], conditional_map) # 最后,添加入口点 workflow.add_edge(START, "supervisor") graph = workflow.compile()
调用团队¶
图创建完成后,我们现在可以调用它并查看它的执行情况!
在 [6]
已复制!
for s in graph.stream(
{
"messages": [
HumanMessage(content="Code hello world and print it to the terminal")
]
}
):
if "__end__" not in s:
print(s)
print("----")
for s in graph.stream( { "messages": [ HumanMessage(content="编写 hello world 代码并将其打印到终端") ] } ): if "__end__" not in s: print(s) print("----")
{'supervisor': {'next': 'Coder'}} ----
Python REPL can execute arbitrary code. Use with caution.
{'Coder': {'messages': [HumanMessage(content='The code to print "Hello, World!" in the terminal has been executed successfully. Here is the output:\n\n```\nHello, World!\n```', additional_kwargs={}, response_metadata={}, name='Coder')]}} ---- {'supervisor': {'next': 'FINISH'}} ----
在 [7]
已复制!
for s in graph.stream(
{"messages": [HumanMessage(content="Write a brief research report on pikas.")]},
{"recursion_limit": 100},
):
if "__end__" not in s:
print(s)
print("----")
for s in graph.stream( {"messages": [HumanMessage(content="编写关于鼠兔的简要研究报告。")]}, {"recursion_limit": 100}, ): if "__end__" not in s: print(s) print("----")
{'supervisor': {'next': 'Researcher'}} ---- {'Researcher': {'messages': [HumanMessage(content='### Research Report on Pikas\n\n#### Introduction\nPikas are small, herbivorous mammals belonging to the family Ochotonidae, closely related to rabbits and hares. These animals are known for their distinctive high-pitched calls and are often found in cold, mountainous regions across Asia, North America, and parts of Europe.\n\n#### Habitat and Behavior\nPikas primarily inhabit talus slopes and alpine meadows, often at elevations ranging from 2,500 to over 13,000 feet. These environments provide the necessary rock crevices and vegetation required for their survival. Pikas are diurnal and exhibit two main foraging behaviors: direct consumption of plants and the collection of vegetation into "haypiles" for winter storage. Unlike many small mammals, pikas do not hibernate and remain active throughout the winter, relying on these haypiles for sustenance.\n\n#### Diet and Feeding Habits\nPikas are generalist herbivores, feeding on a variety of grasses, forbs, and small shrubs. They have a highly developed behavior known as "haying," where they collect and store plant material during the summer months to ensure a food supply during the harsh winter. This behavior is crucial for their survival, as the stored hay provides the necessary nutrients when fresh vegetation is scarce.\n\n#### Reproduction and Lifecycle\nPikas have a relatively short lifespan, averaging around three years. They typically breed once or twice a year, with a gestation period of roughly 30 days. Females usually give birth to litters of two to six young. The young are weaned and become independent within a month, reaching sexual maturity by the following spring.\n\n#### Conservation Status\nThe conservation status of pikas varies by region and species. The American pika (Ochotona princeps), found in the mountains of western North America, is particularly vulnerable to climate change. Rising temperatures and reduced snowpack threaten their habitat, forcing pikas to move to higher elevations or face local extirpation. Despite these challenges, the American pika is not currently listed under the US Endangered Species Act, although several studies indicate localized population declines.\n\n#### Conclusion\nPikas are fascinating creatures that play a vital role in their alpine ecosystems. Their unique behaviors, such as haying, and their sensitivity to climate change make them important indicators of environmental health. Continued research and conservation efforts are essential to ensure the survival of these small but significant mammals in the face of global climatic shifts.\n\n#### References\n1. Wikipedia - Pika: [Link](https://en.wikipedia.org/wiki/Pika)\n2. Wikipedia - American Pika: [Link](https://en.wikipedia.org/wiki/American_pika)\n3. Animal Spot - American Pika: [Link](https://www.animalspot.net/american-pika.html)\n4. Animalia - American Pika: [Link](https://animalia.bio/index.php/american-pika)\n5. National Park Service - Pikas Resource Brief: [Link](https://www.nps.gov/articles/pikas-brief.htm)\n6. Alaska Department of Fish and Game - Pikas: [Link](https://www.adfg.alaska.gov/static/education/wns/pikas.pdf)\n7. NatureMapping Foundation - American Pika: [Link](http://naturemappingfoundation.org/natmap/facts/american_pika_712.html)\n8. USDA Forest Service - Conservation Status of Pikas: [Link](https://www.fs.usda.gov/psw/publications/millar/psw_2022_millar002.pdf)', additional_kwargs={}, response_metadata={}, name='Researcher')]}} ---- {'supervisor': {'next': 'Coder'}} ---- {'Coder': {'messages': [HumanMessage(content='### Research Report on Pikas\n\n#### Introduction\nPikas are small, herbivorous mammals belonging to the family Ochotonidae, closely related to rabbits and hares. These animals are known for their distinctive high-pitched calls and are often found in cold, mountainous regions across Asia, North America, and parts of Europe.\n\n#### Habitat and Behavior\nPikas primarily inhabit talus slopes and alpine meadows, often at elevations ranging from 2,500 to over 13,000 feet. These environments provide the necessary rock crevices and vegetation required for their survival. Pikas are diurnal and exhibit two main foraging behaviors: direct consumption of plants and the collection of vegetation into "haypiles" for winter storage. Unlike many small mammals, pikas do not hibernate and remain active throughout the winter, relying on these haypiles for sustenance.\n\n#### Diet and Feeding Habits\nPikas are generalist herbivores, feeding on a variety of grasses, forbs, and small shrubs. They have a highly developed behavior known as "haying," where they collect and store plant material during the summer months to ensure a food supply during the harsh winter. This behavior is crucial for their survival, as the stored hay provides the necessary nutrients when fresh vegetation is scarce.\n\n#### Reproduction and Lifecycle\nPikas have a relatively short lifespan, averaging around three years. They typically breed once or twice a year, with a gestation period of roughly 30 days. Females usually give birth to litters of two to six young. The young are weaned and become independent within a month, reaching sexual maturity by the following spring.\n\n#### Conservation Status\nThe conservation status of pikas varies by region and species. The American pika (Ochotona princeps), found in the mountains of western North America, is particularly vulnerable to climate change. Rising temperatures and reduced snowpack threaten their habitat, forcing pikas to move to higher elevations or face local extirpation. Despite these challenges, the American pika is not currently listed under the US Endangered Species Act, although several studies indicate localized population declines.\n\n#### Conclusion\nPikas are fascinating creatures that play a vital role in their alpine ecosystems. Their unique behaviors, such as haying, and their sensitivity to climate change make them important indicators of environmental health. Continued research and conservation efforts are essential to ensure the survival of these small but significant mammals in the face of global climatic shifts.\n\n#### References\n1. Wikipedia - Pika: [Link](https://en.wikipedia.org/wiki/Pika)\n2. Wikipedia - American Pika: [Link](https://en.wikipedia.org/wiki/American_pika)\n3. Animal Spot - American Pika: [Link](https://www.animalspot.net/american-pika.html)\n4. Animalia - American Pika: [Link](https://animalia.bio/index.php/american-pika)\n5. National Park Service - Pikas Resource Brief: [Link](https://www.nps.gov/articles/pikas-brief.htm)\n6. Alaska Department of Fish and Game - Pikas: [Link](https://www.adfg.alaska.gov/static/education/wns/pikas.pdf)\n7. NatureMapping Foundation - American Pika: [Link](http://naturemappingfoundation.org/natmap/facts/american_pika_712.html)\n8. USDA Forest Service - Conservation Status of Pikas: [Link](https://www.fs.usda.gov/psw/publications/millar/psw_2022_millar002.pdf)', additional_kwargs={}, response_metadata={}, name='Coder')]}} ---- {'supervisor': {'next': 'FINISH'}} ----