整个工作流分为以下几个核心模块:
- 工具集成: 利用 TavilySearchResults 提供实时搜索结果。
- LLM模型: 通过 ChatOpenAI 调用 GLM-4-plus 模型进行语言生成。
- 工作流管理: 使用 langgraph 创建一个状态图,实现任务的动态规划与执行。
- 动态任务规划与执行: 根据用户问题生成任务计划,并动态调整未完成的任务。
1. 搜索工具
from langchain_community.tools.tavily_search import TavilySearchResults
import os
os.environ["TAVILY_API_KEY"] = "your api key"
tools = [TavilySearchResults(max_results=3)]
2. executor 执行节点
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
temperature=0,
model="GLM-4-plus",
openai_api_key="your api key",
openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)
from langchain import hub
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
# Get the prompt to use - you can modify this!
prompt = hub.pull("ih/ih-react-agent-executor")
prompt.pretty_print()
agent_executor = create_react_agent(llm, tools, state_modifier=prompt)
d:\soft\anaconda\envs\langchain\Lib\site-packages\langsmith\client.py:354: LangSmithMissingAPIKeyWarning: API key must be provided when using hosted LangSmith API
warnings.warn(
================================[1m System Message [0m================================
You are a helpful assistant.
=============================[1m Messages Placeholder [0m=============================
[33;1m[1;3m{messages}[0m
agent_executor.invoke({"messages": [("user", "who is the winnner of the us open")]})
{'messages': [HumanMessage(content='who is the winnner of the us open', additional_kwargs={}, response_metadata={}, id='79a6dac2-034d-4a10-b4a4-cbb9e70f0ba6'),
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_-9176234598398291859', 'function': {'arguments': '{"query": "US Open 2023 winner"}', 'name': 'tavily_search_results_json'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 161, 'total_tokens': 180, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'GLM-4-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-fb86ce02-c1d5-4052-b812-b5a17ed4506f-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'US Open 2023 winner'}, 'id': 'call_-9176234598398291859', 'type': 'tool_call'}], usage_metadata={'input_tokens': 161, 'output_tokens': 19, 'total_tokens': 180, 'input_token_details': {}, 'output_token_details': {}}),
ToolMessage(content='[{"url": "https://currentaffairs.adda247.com/us-open-2023-winners-complete-list/", "content": "US Open 2023 Winners Complete list Novak Djokovic won his fourth title at the US Open and 24th Grand Slam title by beating Daniil Medvedev 6-3, 7-6(5), 6-3 in the final. Coco Gauff capped off her sensational American summer by winning her maiden Grand Slam title at the US Open by beating Aryna Sabalenka 2-6, 6-3, 6-2 in the final. This tournament is the 143rd edition. Here is the Complete List of US Open 2023 Winners in Different Categories Other champions at US Open 2023 If you are preparing for Government Job Exams, then it is very important for you to read the Daily Current Affairs. All the important updates based on current affairs are included in this Daily Current Affairs 2024 article."}, {"url": "https://www.usopen.org/en_US/news/articles/2023-09-10/novak_djokovic_wins_24th_grand_slam_singles_title_at_2023_us_open.html", "content": "Novak Djokovic wins 24th Grand Slam singles title at 2023 US Open - Official Site of the 2024 US Open Tennis Championships - A USTA Event Novak Djokovic wins 24th Grand Slam singles title at 2023 US Open WHAT HAPPENED:\xa0Novak Djokovic\xa0handled the weight of history to defeat\xa0Daniil Medvedev\xa0on Sunday in the 2023 US Open men\'s singles final. Djokovic is now\xa04-6 across his record-tying 10 US Open men\'s singles finals. MATCH POINT:\xa0Now 10-5 in his head-to-head against Medvedev, Djokovic\xa0earned a measure of US Open revenge two years after Medvedev\xa0denied him\xa0a Grand Slam at the\xa0last hurdle in the 2021 New York final. These cookies may be set through our site by our advertising partners."}, {"url": "https://en.wikipedia.org/wiki/2023_US_Open_–_Men\'s_singles", "content": "Djokovic became the oldest US Open men\'s singles champion in the Open Era, at 36 years and 111 days, as well as the first man to capture the Australian Open, the French Open, and the US Open in a season since Mats Wilander in 1988.[1] Contents\\n2023 US Open – Men\'s singles\\nNovak Djokovic defeated Daniil Medvedev in the final, 6–3, 7–6(7–5), 6–3 to win the men\'s singles tennis title at the 2023 US Open. Alcaraz\'s loss marked the 15th consecutive year where the reigning US Open champion failed to defend the title, with Federer being the last man to do so in 2008.\\n Other entry information[edit]\\nWild cards[edit]\\nProtected ranking[edit]\\nQualifiers[edit]\\nLucky losers[edit]\\nWithdrawals[edit]\\nReferences[edit]\\nExternal links[edit] By reaching a 47th men\'s singles major semifinal, Djokovic surpassed Roger Federer\'s all-time record,[2] and, by reaching the final, he equaled Federer\'s record of reaching all major finals in a season three times.\\n"}]', name='tavily_search_results_json', id='6548718f-0fc6-4298-99c5-1a97e9816310', tool_call_id='call_-9176234598398291859', artifact={'query': 'US Open 2023 winner', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'title': 'US Open 2023 Winners Complete list - adda247', 'url': 'https://currentaffairs.adda247.com/us-open-2023-winners-complete-list/', 'content': 'US Open 2023 Winners Complete list Novak Djokovic won his fourth title at the US Open and 24th Grand Slam title by beating Daniil Medvedev 6-3, 7-6(5), 6-3 in the final. Coco Gauff capped off her sensational American summer by winning her maiden Grand Slam title at the US Open by beating Aryna Sabalenka 2-6, 6-3, 6-2 in the final. This tournament is the 143rd edition. Here is the Complete List of US Open 2023 Winners in Different Categories Other champions at US Open 2023 If you are preparing for Government Job Exams, then it is very important for you to read the Daily Current Affairs. All the important updates based on current affairs are included in this Daily Current Affairs 2024 article.', 'score': 0.99984527, 'raw_content': None}, {'title': 'Novak Djokovic wins 24th Grand Slam singles title at 2023 US Open', 'url': 'https://www.usopen.org/en_US/news/articles/2023-09-10/novak_djokovic_wins_24th_grand_slam_singles_title_at_2023_us_open.html', 'content': "Novak Djokovic wins 24th Grand Slam singles title at 2023 US Open - Official Site of the 2024 US Open Tennis Championships - A USTA Event Novak Djokovic wins 24th Grand Slam singles title at 2023 US Open WHAT HAPPENED:\xa0Novak Djokovic\xa0handled the weight of history to defeat\xa0Daniil Medvedev\xa0on Sunday in the 2023 US Open men's singles final. Djokovic is now\xa04-6 across his record-tying 10 US Open men's singles finals. MATCH POINT:\xa0Now 10-5 in his head-to-head against Medvedev, Djokovic\xa0earned a measure of US Open revenge two years after Medvedev\xa0denied him\xa0a Grand Slam at the\xa0last hurdle in the 2021 New York final. These cookies may be set through our site by our advertising partners.", 'score': 0.99940693, 'raw_content': None}, {'title': "2023 US Open - Men's singles - Wikipedia", 'url': "https://en.wikipedia.org/wiki/2023_US_Open_–_Men's_singles", 'content': "Djokovic became the oldest US Open men's singles champion in the Open Era, at 36 years and 111 days, as well as the first man to capture the Australian Open, the French Open, and the US Open in a season since Mats Wilander in 1988.[1] Contents\n2023 US Open – Men's singles\nNovak Djokovic defeated Daniil Medvedev in the final, 6–3, 7–6(7–5), 6–3 to win the men's singles tennis title at the 2023 US Open. Alcaraz's loss marked the 15th consecutive year where the reigning US Open champion failed to defend the title, with Federer being the last man to do so in 2008.\n Other entry information[edit]\nWild cards[edit]\nProtected ranking[edit]\nQualifiers[edit]\nLucky losers[edit]\nWithdrawals[edit]\nReferences[edit]\nExternal links[edit] By reaching a 47th men's singles major semifinal, Djokovic surpassed Roger Federer's all-time record,[2] and, by reaching the final, he equaled Federer's record of reaching all major finals in a season three times.\n", 'score': 0.9993358, 'raw_content': None}], 'response_time': 1.42}),
AIMessage(content="The winner of the 2023 US Open in men's singles is Novak Djokovic, who secured his fourth US Open title and 24th Grand Slam title by defeating Daniil Medvedev in the final. In the women's singles, Coco Gauff won her maiden Grand Slam title by overcoming Aryna Sabalenka in the final.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 73, 'prompt_tokens': 922, 'total_tokens': 995, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'GLM-4-plus', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-ea7dd37c-596e-41a8-8272-4ca47caf463c-0', usage_metadata={'input_tokens': 922, 'output_tokens': 73, 'total_tokens': 995, 'input_token_details': {}, 'output_token_details': {}})]}
3. Plan 计划节点
import operator
from typing import Annotated, List, Tuple
from typing_extensions import TypedDict
class PlanExecute(TypedDict):
input: str
plan: List[str]
past_steps: Annotated[List[Tuple], operator.add]
response: str
from pydantic import BaseModel, Field
class Plan(BaseModel):
"""Plan to follow in future"""
steps: List[str] = Field(
description="different steps to follow, should be in sorted order"
)
from langchain_core.prompts import ChatPromptTemplate
planner_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"""For the given objective, come up with a simple step by step plan. \
This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \
The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.""",
),
("placeholder", "{messages}"),
]
)
planner = planner_prompt | ChatOpenAI(
temperature=0,
model="GLM-4-plus",
openai_api_key="your api key",
openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
).with_structured_output(Plan)
planner.invoke(
{
"messages": [
("user", "what is the hometown of the current Australia open winner?")
]
}
)
Plan(steps=['Find out who is the current Australia Open winner.', 'Determine the hometown of the current Australia Open winner.'])
4. replan 计划检查节点
from typing import Union
class Response(BaseModel):
"""Response to user."""
response: str
class Act(BaseModel):
"""Action to perform."""
action: Union[Response, Plan] = Field(
description="Action to perform. If you want to respond to user, use Response. "
"If you need to further use tools to get the answer, use Plan."
)
replanner_prompt = ChatPromptTemplate.from_template(
"""For the given objective, come up with a simple step by step plan. \
This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \
The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.
Your objective was this:
{input}
Your original plan was this:
{plan}
You have currently done the follow steps:
{past_steps}
Update your plan accordingly. If no more steps are needed and you can return to the user, then respond with that. Otherwise, fill out the plan. Only add steps to the plan that still NEED to be done. Do not return previously done steps as part of the plan."""
)
replanner = replanner_prompt | ChatOpenAI(
temperature=0,
model="GLM-4-plus",
openai_api_key="your api key",
openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
).with_structured_output(Act)
5. graph 各个节点连接
from typing import Literal
from langgraph.graph import END
async def execute_step(state: PlanExecute):
plan = state["plan"]
plan_str = "\n".join(f"{i+1}. {step}" for i, step in enumerate(plan))
task = plan[0]
task_formatted = f"""For the following plan:
{plan_str}\n\nYou are tasked with executing step {1}, {task}."""
agent_response = await agent_executor.ainvoke(
{"messages": [("user", task_formatted)]}
)
return {
"past_steps": [(task, agent_response["messages"][-1].content)],
}
async def plan_step(state: PlanExecute):
plan = await planner.ainvoke({"messages": [("user", state["input"])]})
return {"plan": plan.steps}
async def replan_step(state: PlanExecute):
output = await replanner.ainvoke(state)
if isinstance(output.action, Response):
return {"response": output.action.response}
else:
return {"plan": output.action.steps}
def should_end(state: PlanExecute):
if "response" in state and state["response"]:
return END
else:
return "agent"
from langgraph.graph import StateGraph, START
workflow = StateGraph(PlanExecute)
# Add the plan node
workflow.add_node("planner", plan_step)
# Add the execution step
workflow.add_node("agent", execute_step)
# Add a replan node
workflow.add_node("replan", replan_step)
workflow.add_edge(START, "planner")
# From plan we go to agent
workflow.add_edge("planner", "agent")
# From agent, we replan
workflow.add_edge("agent", "replan")
workflow.add_conditional_edges(
"replan",
# Next, we pass in the function that will determine which node is called next.
should_end,
["agent", END],
)
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable
app = workflow.compile()
graph 可视化
from IPython.display import Image, display
display(Image(app.get_graph(xray=True).draw_mermaid_png()))
6. 示例
config = {"recursion_limit": 50}
inputs = {"input": "what is the hometown of the mens 2024 Australia open winner?"}
async for event in app.astream(inputs, config=config):
for k, v in event.items():
if k != "__end__":
print(v)
{'plan': ["Find out who won the men's 2024 Australian Open.", 'Research the hometown of the winner.']}
{'past_steps': [("Find out who won the men's 2024 Australian Open.", "The winner of the men's singles at the 2024 Australian Open was Jannik Sinner. He made an epic comeback to defeat Daniil Medvedev in the final, with a scoreline of 3-6, 3-6, 6-4, 6-4, 6-3. This victory marked Sinner's first ever Grand Slam title, and he became the first Italian man to win the Australian Open since 1976, as well as the youngest player to win at Melbourne Park since Novak Djokovic in 2008.")]}
{'plan': ['Research the hometown of Jannik Sinner.']}
{'past_steps': [('Research the hometown of Jannik Sinner.', "Jannik Sinner's hometown is Sexten, located in northern Italy. This information is confirmed by multiple sources, including ATP Tour and TennisTonic, which mention his return to Sexten and his connection to the region. Additionally, Tennis World USA describes Sexten as being in the county of Trentino Alto Adige, nestled in the Alps. This northern Italian town is where Sinner was born and raised, and it holds a special place in his heart.")]}
{'response': "The hometown of the men's 2024 Australia Open winner, Jannik Sinner, is Sexten, located in northern Italy."}
参考链接: https://langchain-ai.github.io/langgraph/tutorials/plan-and-execute/plan-and-execute/
如果有任何问题,欢迎在评论区提问。