作者:CSDN博客
文章目录
1 Langchain基础回顾、LECL、Tool Use、RAG 以及 LangSmith2 conversational agents,ReAct、agent_scratchpad 历史过程信息维护3 LangGraph 基本概念(AgentState、StateGraph,nodes,edges)
核心概念案例一:Agent Executor
案例二:Chat Executor
4 LangGraph 构建复杂 RAG workflow(Self-corrective)
(Self-Corrective) RAG on LangGraphmodel, retriever & toolsGraph
5 多轮对话 qlora SFT(Multi-turn Conversation)05 LangChain、LangGraph 结构化输出(Structured output),gpt-4o-2024-08-066 基于LangGraph 实现 Reflexion Agent(generator vs. critic)7 [OpenAI API] logprobs 与 top_logprobs 模型输出的置信度(confidence)与困惑度(PPL)
1 使用`logprobs`来衡量分类任务的置信度2 检索置信度评分以减少幻觉3 Highlighter and bytes parameter4 Autocomplete5 困惑度计算(perplexity, PPL)
https://www.bilibili.com/video/BV1ax4y1s7Sk
1 Langchain基础回顾、LECL、Tool Use、RAG 以及 LangSmith
参考资料:
https://github.com/langchain-ai/langchain/tree/master/cookbook
- https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel
https://python.langchain.com/v0.1/docs/expression_language/why/
https://ai.plainenglish.io/understanding-large-language-model-based-agents-27bee5c82cec
- import os
- from dotenv import load_dotenv
- # LANGCHAIN_TRACING_V2=true # 免费# LANGCHAIN_API_KEY= # 免费# OPENAI_API_KEY= # 付费,但可以用别的
- load_dotenv()
复制代码
- 所谓的 agent 开发,LLMs workflows,GenAI 时代的软件工程
丰富的生态,workflows 的复杂,手撸的效率非常低,而且不好维护,Input -> Processing -> Output
- 与 AutoGen 等相比,更多地面向开发者,面向软件工程
LangGraph:multi-agents workflowsLangSmith 也在更多地弥补中间过程显示的不足
- 推荐 《大模型应用开发 动手做AI Agent》(https://www.bilibili.com/opus/935785456083140628)
面向开发者,第一本系统而全面,可以做一个很好的入门
LCEL (LangChain Expression Language)
LangChain 重写了 |(__or__)运算符,Chain 之所在,即通过|将所有东西串成一个链RunnablePassthrough: RunnablePassthrough 允许你将输入数据直接传递而不做任何更改(identity),通常与 RunnableParallel 一起使用,将数据传递到新的键中。LangChain 的应用 RunnablePassthrough 作为一个占位符,可以在需要时填充数据,比如在公司名称尚未确定时先留空,后续再填入。
- 所谓的最佳实践
python:遍历,也可以用 list comprehension对于 matlab:也可以遍历,也可以整理成 matrix,直接矩阵矢量乘法;
- # 自带的|运算符,是二进制位运算# (2 | 3) > 32|3>2# True2|3# 3
复制代码- from langchain_core.runnables import(
- RunnablePassthrough,
- RunnableLambda,
- RunnableParallel
- )
- os.environ["LANGCHAIN_PROJECT"]='lcel_test'# 设置一个新的projectfrom langchain_openai import ChatOpenAI
- from langchain_core.prompts import ChatPromptTemplate
- from langchain_core.output_parsers import StrOutputParser
- prompt = ChatPromptTemplate.from_template("Tell me a short joke about {topic}")
- output_parser = StrOutputParser()
- llm = ChatOpenAI(model="gpt-3.5-turbo")# lcel
- chain =({"topic": RunnablePassthrough()}| prompt
- | llm
- | output_parser
- )
- chain.invoke("ice cream")# 'Why did the ice cream truck break down?\n\nBecause it had too many "scoops"!'
复制代码 这里prompt的输入就是{"topic": RunnablePassthrough()},这样上一步的输出是下一步的输入,如此成chain
比如直接写成常见的串行语法:- prompt.invoke({'topic':'ice cream'})# ChatPromptValue(messages=[HumanMessage(content='Tell me a short joke about ice cream')])
- llm.invoke(prompt.invoke({'topic':'ice cream'}))# AIMessage(content='Why did the ice cream truck break down?\n\nIt had too many "scoops" of ice cream!', response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 15, 'total_tokens': 37}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-fb46f4ce-665b-45f7-9fd0-9dd559465fcc-0', usage_metadata={'input_tokens': 15, 'output_tokens': 22, 'total_tokens': 37})
- output_parser.invoke(llm.invoke(prompt.invoke({'topic':'ice cream'})))# 'Why did the ice cream truck break down? It had too many "scoops" on board!'
复制代码 runnables
RunnablePassthrough(): identity
- # ex1
- chain = RunnablePassthrough()| RunnablePassthrough ()| RunnablePassthrough ()
- chain.invoke("hello")# 'hello'# ex2
- chain = RunnablePassthrough()| RunnableLambda(lambda x: x.upper())
- chain.invoke("hello")# 'HELLO'# ex3
- chain = RunnablePassthrough()| RunnableLambda(lambda x: x.upper())| RunnablePassthrough()
- chain.invoke("hello")# 'HELLO'
复制代码 2 conversational agents,ReAct、agent_scratchpad 历史过程信息维护
一些概念:
- 过程信息(所谓的对话式 agent,conversational agents,就是带记忆)
stateless LLMs => with memory (maintaining previous interactions)
- intermediate_steps: list of actions/tools (input, output)
agent_scratchpad(草稿本):list of tool messages这个具体而言就是一些定义好的函数
- 第一次 query-resp 之后,才会有这些过程信息
第一次:intermediate_steps: [], agent_scratchpad: ""
- chat history
messages (openai 的 api)system, user, assistant, user assistant …
langsmith: 监控所有的 messages (input, output)
前一种是隐式的,后者是显式的。
ReAct(Reasoning & Acting)
https://arxiv.org/abs/2210.03629
- ReAct(经典、且general、应用广泛的 prompt)
- User query => (Thought -> Action (Action Input) -> Observation(Action Ouptut)) * N
Action input => output, function decision & calling
multi-step/hops reasoning/interacting跟 function calling(LangChain 中的 tools)天然地适配;
- from langchain import hub
- prompt = hub.pull("hwchase17/react")type(prompt), prompt
- """
- (langchain_core.prompts.prompt.PromptTemplate,
- PromptTemplate(input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'], metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'react', 'lc_hub_commit_hash': 'd15fe3c426f1c4b3f37c9198853e4a86e20c425ca7f4752ec0c9b0e97ca7ea4d'}, template='Answer the following questions as best you can. You have access to the following tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\n\nQuestion: {input}\nThought:{agent_scratchpad}'))
- """
复制代码- # 一个完整的 prompt 模板,历史对话/函数调用(输入输出)信息不断地追加在这一个 prompt 中,而不是维护成 messages listprint(prompt.template)"""
- Answer the following questions as best you can. You have access to the following tools:
- {tools}
- Use the following format:
- Question: the input question you must answer
- Thought: you should always think about what to do
- Action: the action to take, should be one of [{tool_names}]
- Action Input: the input to the action
- Observation: the result of the action
- ... (this Thought/Action/Action Input/Observation can repeat N times)
- Thought: I now know the final answer
- Final Answer: the final answer to the original input question
- Begin!
- Question: {input}
- Thought:{agent_scratchpad}
- """
复制代码Thought: you should always think about what to doAction: the action to take, should be one of [{tool_names}]Action Input: the input to the actionObservation: the result of the action… (this Thought/Action/Action Input/Observation can repeat N times)
LangChain Projects
AgentExecutor:agent that is using tools
- next_action = agent.get_action(...)while next_action != AgentFinish:
- observation = run(next_action)
- next_action = agent.get_action(..., next_action, observation)return next_action
复制代码- os.environ["LANGCHAIN_PROJECT"]='conversational_agents'from langchain_openai import ChatOpenAI
- from langchain.tools import tool
- from langchain.agents import AgentExecutor, create_react_agent
- @tooldefget_employee_id(name):"""
- To get employee id, it takes employee name as arguments
- name(str): Name of the employee
- """
- fake_employees ={"Alice":"E001","Bob":"E002","Charlie":"E003","Diana":"E004","Evan":"E005","Fiona":"E006","George":"E007","Hannah":"E008","Ian":"E009","Jasmine":"E010"}return fake_employees.get(name,"Employee not found")# Custom tool for the Agent # ValueError: Function must have a docstring if description not provided.@tooldefget_employee_salary(employee_id):"""
- To get the salary of an employee, it takes employee_id as input and return salary
- """
- employee_salaries ={"E001":56000,"E002":47000,"E003":52000,"E004":61000,"E005":45000,"E006":58000,"E007":49000,"E008":53000,"E009":50000,"E010":55000}return employee_salaries.get(employee_id,"Employee not found")
- prompt = hub.pull("hwchase17/react")
- llm = ChatOpenAI(model='gpt-4o')
- tools =[get_employee_salary, get_employee_id]
- agent = create_react_agent(llm, tools, prompt)
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
- agent_executor.invoke({"input":"What is the Salary of Evan?"})
复制代码 输出结果:- > Entering new AgentExecutor chain...
- To determine Evan's salary, I first need to obtain his employee ID.
- Action: get_employee_id
- Action Input:"Evan"E005Now that I have Evan's employee ID, I can retrieve his salary using this ID.
- Action: get_employee_salary
- Action Input: E00545000I now know the final answer.
- Final Answer: The salary of Evan is45000.> Finished chain.{'input':'What is the Salary of Evan?','output':'The salary of Evan is 45000.'}
复制代码 first query-resp
LangSmith: https://smith.langchain.com/
LangSmith是评估大模型能力好坏的评估工具,能够量化评估基于大模型的系统的效果。LangSmith通过记录langchain构建的大模型应用的中间过程,从而能够更好的调整提示词等中间过程做优化。
使用的Prompt:- Answer the following questions as best you can. You have access to the following tools:
- get_employee_salary(employee_id)- To get the salary of an employee, it takes employee_id asinputandreturn salary
- get_employee_id(name)- To get employee id, it takes employee name as arguments
- name(str): Name of the employee
- Use the following format:
- Question: the input question you must answer
- Thought: you should always think about what to do
- Action: the action to take, should be one of [get_employee_salary, get_employee_id]
- Action Input: the input to the action
- Observation: the result of the action
- ...(this Thought/Action/Action Input/Observation can repeat N times)
- Thought: I now know the final answer
- Final Answer: the final answer to the original input question
- Begin!
- Question: What is the Salary of Evan?
- Thought:
复制代码- To determine Evan's salary, I first need to obtain his employee ID.
- Action: get_employee_id
- Action Input:"Evan"
复制代码 second query-resp- Answer the following questions as best you can. You have access to the following tools:
- get_employee_salary(employee_id)- To get the salary of an employee, it takes employee_id asinputandreturn salary
- get_employee_id(name)- To get employee id, it takes employee name as arguments
- name(str): Name of the employee
- Use the following format:
- Question: the input question you must answer
- Thought: you should always think about what to do
- Action: the action to take, should be one of [get_employee_salary, get_employee_id]
- Action Input: the input to the action
- Observation: the result of the action
- ...(this Thought/Action/Action Input/Observation can repeat N times)
- Thought: I now know the final answer
- Final Answer: the final answer to the original input question
- Begin!
- Question: What is the Salary of Evan?
- Thought:To determine Evan's salary, I first need to obtain his employee ID.
- Action: get_employee_id
- Action Input:"Evan"
- Observation: E005
- Thought:
复制代码- Now that I have Evan's employee ID, I can retrieve his salary using this ID.
- Action: get_employee_salary
- Action Input: E005
复制代码 get_employee_salary
finally- Answer the following questions as best you can. You have access to the following tools:
- get_employee_salary(employee_id)- To get the salary of an employee, it takes employee_id asinputandreturn salary
- get_employee_id(name)- To get employee id, it takes employee name as arguments
- name(str): Name of the employee
- Use the following format:
- Question: the input question you must answer
- Thought: you should always think about what to do
- Action: the action to take, should be one of [get_employee_salary, get_employee_id]
- Action Input: the input to the action
- Observation: the result of the action
- ...(this Thought/Action/Action Input/Observation can repeat N times)
- Thought: I now know the final answer
- Final Answer: the final answer to the original input question
- Begin!
- Question: What is the Salary of Evan?
- Thought:To determine Evan's salary, I first need to obtain his employee ID.
- Action: get_employee_id
- Action Input:"Evan"
- Observation: E005
- Thought: Now that I have Evan's employee ID, I can retrieve his salary using this ID.
- Action: get_employee_salary
- Action Input: E005
- Observation:45000
- Thought:
复制代码- I now know the final answer.
- Final Answer: The salary of Evan is45000.
复制代码 显式地操作agent_scratchpad与intermediate_steps
https://python.langchain.com/v0.1/docs/modules/agents/how_to/custom_agent/
format_to_openai_tool_messages
https://api.python.langchain.com/en/latest/_modules/langchain/agents/format_scratchpad/openai_tools.html#format_to_openai_tool_messages
- os.environ["LANGCHAIN_PROJECT"]='memory_agents'@tooldefget_word_length(word:str)->int:"""Returns the length of a word."""returnlen(word)
- get_word_length.invoke("abc")# 3
- tools =[get_word_length]from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
- prompt = ChatPromptTemplate.from_messages([("system","You are very powerful assistant, but don't know current events",),("user","{input}"),
- MessagesPlaceholder(variable_name="agent_scratchpad"),])
- llm = ChatOpenAI(model="gpt-4o", temperature=0)
- llm_with_tools = llm.bind_tools(tools)from langchain.agents.format_scratchpad.openai_tools import(
- format_to_openai_tool_messages,)from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
- # runnable Sequence,根据需要循环执行这 4 步;
- agent =({"input":lambda x: x["input"],"agent_scratchpad":lambda x: format_to_openai_tool_messages(
- x["intermediate_steps"]),}| prompt
- | llm_with_tools
- | OpenAIToolsAgentOutputParser())
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
- process_list =list(agent_executor.stream({"input":"How many letters in the word eudca"}))"""
- > Entering new AgentExecutor chain...
- Invoking: `get_word_length` with `{'word': 'eudca'}`
- 5The word "eudca" has 5 letters.
- > Finished chain.
- """
复制代码 上面的process_list变量形如:- [{'actions':[ToolAgentAction(tool='get_word_length', tool_input={'word':'eudca'}, log="\nInvoking: `get_word_length` with `{'word': 'eudca'}`\n\n\n", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls':[{'index':0,'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','function':{'arguments':'{"word":"eudca"}','name':'get_word_length'},'type':'function'}]}, response_metadata={'finish_reason':'tool_calls','model_name':'gpt-4o-2024-05-13','system_fingerprint':'fp_3aa7262c27'},id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name':'get_word_length','args':{'word':'eudca'},'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','type':'tool_call'}], tool_call_chunks=[{'name':'get_word_length','args':'{"word":"eudca"}','id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','index':0,'type':'tool_call_chunk'}])], tool_call_id='call_P3LQGFQ50JvKMtLpLXJ2SSsZ')],'messages':[AIMessageChunk(content='', additional_kwargs={'tool_calls':[{'index':0,'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','function':{'arguments':'{"word":"eudca"}','name':'get_word_length'},'type':'function'}]}, response_metadata={'finish_reason':'tool_calls','model_name':'gpt-4o-2024-05-13','system_fingerprint':'fp_3aa7262c27'},id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name':'get_word_length','args':{'word':'eudca'},'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','type':'tool_call'}], tool_call_chunks=[{'name':'get_word_length','args':'{"word":"eudca"}','id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','index':0,'type':'tool_call_chunk'}])]},{'steps':[AgentStep(action=ToolAgentAction(tool='get_word_length', tool_input={'word':'eudca'}, log="\nInvoking: `get_word_length` with `{'word': 'eudca'}`\n\n\n", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls':[{'index':0,'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','function':{'arguments':'{"word":"eudca"}','name':'get_word_length'},'type':'function'}]}, response_metadata={'finish_reason':'tool_calls','model_name':'gpt-4o-2024-05-13','system_fingerprint':'fp_3aa7262c27'},id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name':'get_word_length','args':{'word':'eudca'},'id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','type':'tool_call'}], tool_call_chunks=[{'name':'get_word_length','args':'{"word":"eudca"}','id':'call_P3LQGFQ50JvKMtLpLXJ2SSsZ','index':0,'type':'tool_call_chunk'}])], tool_call_id='call_P3LQGFQ50JvKMtLpLXJ2SSsZ'), observation=5)],'messages':[FunctionMessage(content='5', name='get_word_length')]},{'output':'The word "eudca" has 5 letters.','messages':[AIMessage(content='The word "eudca" has 5 letters.')]}]
复制代码 换一个例子:- from langchain_core.prompts import MessagesPlaceholder
- MEMORY_KEY ="chat_history"
- prompt = ChatPromptTemplate.from_messages([("system","You are very powerful assistant, but bad at calculating lengths of words.",),
- MessagesPlaceholder(variable_name=MEMORY_KEY),("user","{input}"),
- MessagesPlaceholder(variable_name="agent_scratchpad"),])from langchain_core.messages import AIMessage, HumanMessage
- chat_history =[]
- agent =({"input":lambda x: x["input"],"agent_scratchpad":lambda x: format_to_openai_tool_messages(
- x["intermediate_steps"]),"chat_history":lambda x: x["chat_history"],}| prompt
- | llm_with_tools
- | OpenAIToolsAgentOutputParser())
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
- input1 ="how many letters in the word educa?"
- result = agent_executor.invoke({"input": input1,"chat_history": chat_history})
- chat_history.extend([
- HumanMessage(content=input1),
- AIMessage(content=result["output"]),])
- agent_executor.invoke({"input":"is that a real word?","chat_history": chat_history})"""
- > Entering new AgentExecutor chain...
- Invoking: `get_word_length` with `{'word': 'educa'}`
- 5The word "educa" has 5 letters.
- > Finished chain.
- > Entering new AgentExecutor chain...
- "Educa" is not a standard English word. It might be a truncated form of "education" or a name, but it is not commonly recognized as a standalone word in English.
- > Finished chain.
- {'input': 'is that a real word?',
- 'chat_history': [HumanMessage(content='how many letters in the word educa?'),
- AIMessage(content='The word "educa" has 5 letters.')],
- 'output': '"Educa" is not a standard English word. It might be a truncated form of "education" or a name, but it is not commonly recognized as a standalone word in English.'}
- """
复制代码
- messages
systemhuman (user)ai (assistant)human (user)ai (assistant)…
- chat_history =[]# https://smith.langchain.com/hub/hwchase17/openai-functions-agent# input_variables=['agent_scratchpad', 'input'], optional_variables=['chat_history']
- prompt = hub.pull('hwchase17/openai-functions-agent')
- agent =({"input":lambda x: x["input"],"agent_scratchpad":lambda x: format_to_openai_tool_messages(
- x["intermediate_steps"]),"chat_history":lambda x: x["chat_history"],}| prompt
- | llm_with_tools
- | OpenAIToolsAgentOutputParser())
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
- input1 ="how many letters in the word dadebfdr?"
- result = agent_executor.invoke({"input": input1,"chat_history": chat_history})
- chat_history.extend([
- HumanMessage(content=input1),
- AIMessage(content=result["output"]),])
- agent_executor.invoke({"input":"is that a real word?","chat_history": chat_history})
复制代码 输出结果:- > Entering new AgentExecutor chain...
- Invoking: `get_word_length` with `{'word':'dadebfdr'}`
- 8The word "dadebfdr" has 8 letters.> Finished chain.> Entering new AgentExecutor chain...
- No,"dadebfdr" does not appear to be a real word in the English language. It seems to be a random string of letters.> Finished chain.{'input':'is that a real word?','chat_history':[HumanMessage(content='how many letters in the word dadebfdr?'),
- AIMessage(content='The word "dadebfdr" has 8 letters.')],'output':'No, "dadebfdr" does not appear to be a real word in the English language. It seems to be a random string of letters.'}
复制代码 3 LangGraph 基本概念(AgentState、StateGraph,nodes,edges)
- langgraph studio
https://www.youtube.com/watch?v=pLPJoFvq4_M
https://github.com/langchain-ai/langgraph/blob/main/examples/reflexion/reflexion.ipynbhttps://github.com/langchain-ai/langgraph/blob/main/examples/agent_executor/force-calling-a-tool-first.ipynb
所有用到的包- import os
- from typing import TypedDict, Annotated, List, Union, Sequence
- import operator
- import json
- from dotenv import load_dotenv
- from langchain import hub
- from langchain.tools import BaseTool, StructuredTool, Tool, tool
- from langchain.tools.render import format_tool_to_openai_function
- from langchain_core.utils.function_calling import convert_to_openai_function
- from langchain_openai.chat_models import ChatOpenAI
- from langchain.agents import create_openai_functions_agent
- from langchain_core.utils.function_calling import convert_to_openai_function
- from langchain_core.agents import AgentAction, AgentFinish
- from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage, FunctionMessage
- from langgraph.graph import END, StateGraph, MessagesState
- from langgraph.prebuilt import ToolNode, ToolInvocation
- from langgraph.prebuilt.tool_executor import ToolExecutor
- os.environ['http_proxy']='http://127.0.0.1:7890'
- os.environ['https_proxy']='http://127.0.0.1:7890'
复制代码 核心概念
langchain => langgraph
- chain: 更多是 linear chain,链式顺序执行;
LCEL:langchain expression language,典型的线性执行
- graph:可以有分支/判断(是否到达 AgentFinish),有循环
ReAct(Reasoning & Acting)其实本质上也不是 linear chain 了;
Graph
- nodes, edges
每个节点都是一个函数(即tools)所有节点的输入都是 state,输出会附加一些信息到新的 state 里
state 沿着 edge 从一个 node 到另一个 node 时,state 会发生变化(携带上一个 node 的执行输出信息)
LangGraph的执行中间过程同样可以在LangSmith里查看
案例一:Agent Executor
- classAgentState(TypedDict):input:str
- chat_history:list[BaseMessage]
- agent_outcome: Union[AgentAction, AgentFinish,None]# 不断追加和传递
- intermediate_steps: Annotated[list[tuple[AgentAction,str]], operator.add]
复制代码TypedDict: 静态类型,可以约定 value 类型的字典
tools
第一个tool的回答作为第二个tool的输入
Tools are interfaces that an agent can use to interact with the world. They combine a few things:
The name of the toolA description of what the tool isJSON schema of what the inputs to the tool areThe function to call
- import random
- @tool("upper_case", return_direct=True)defto_upper_case(input:str)->str:"""Returns the input as all upper case."""returninput.upper()@tool("random_number", return_direct=True)defrandom_number_maker(input:str)->str:"""Returns a random number between 0-100."""return random.randint(0,100)
- tools =[to_upper_case,random_number_maker]
- random_number_maker.run('random')
- to_upper_case.run('hello WORLD')
复制代码 Agent
- # https://smith.langchain.com/hub/hwchase17/openai-functions-agent
- prompt = hub.pull('hwchase17/openai-functions-agent')
- llm = ChatOpenAI(model="gpt-4o", streaming=True)
- prompt
- # ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], optional_variables=['chat_history'], input_types={'chat_history': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]], 'agent_scratchpad': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, partial_variables={'chat_history': []}, metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'openai-functions-agent', 'lc_hub_commit_hash': 'a1655024b06afbd95d17449f21316291e0726f13dcfaf990cc0d18087ad689a5'}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')), MessagesPlaceholder(variable_name='chat_history', optional=True), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')), MessagesPlaceholder(variable_name='agent_scratchpad')])
复制代码 上面的输出表明模型其实并没有使用tools,因为这只是一个step,不是整个pipeline,下面我们看如何执行整个graph- agent = create_openai_functions_agent(llm, tools, prompt)# langchain_core.runnables.base.RunnableSequence
- inputs ={"input":"give me a random number and then write in words and make it upper case.","chat_history":[],"intermediate_steps":[]}
- agent_outcome = agent.invoke(inputs)# AgentActionMessageLog(tool='random_number', tool_input={'input': 'Generate a random number'}, log="\nInvoking: `random_number` with `{'input': 'Generate a random number'}`\n\n\n", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{"input":"Generate a random number"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-e2652fc2-03db-4c6c-9b26-310d80e0f06a-0')])
复制代码 tools
- tool_executor = ToolExecutor(tools)# tool_executor = ToolNode(tools)defrun_agent(state):
- agent_outcome = agent.invoke(state)# AgentState.agent_outcome: 当前输出,决策动作return{"agent_outcome": agent_outcome}defexecute_tools(state):
- agent_action = state['agent_outcome']
- output = tool_executor.invoke(agent_action)print(f"The agent action is {agent_action}")print(f"The tool result is: {output}")# AgentState.intermediate_steps: 追加逻辑return{"intermediate_steps":[(agent_action,str(output))]}defshould_continue(state):ifisinstance(state['agent_outcome'], AgentFinish):return"end"return"continue"
复制代码 graphs
- workflow = StateGraph(AgentState)
- workflow.add_node("agent", run_agent)
- workflow.add_node("action", execute_tools)
- workflow.set_entry_point("agent")# 条件分支
- workflow.add_conditional_edges("agent",
- should_continue,{"continue":"action","end": END
- })
- workflow.add_edge('action','agent')
- graph = workflow.compile()from IPython.display import Image, display
- try:
- display(Image(graph.get_graph().draw_mermaid_png()))except Exception:# This requires some extra dependencies and is optionalpass
复制代码
这个过程跟networkx还有dgl是很像的,就是定义节点,定义边,然后可以可视化整个LangGraph |