使用 AgentExecutor (傳統版) 建構 Agent
本節將介紹如何使用傳統 LangChain AgentExecutor 進行建構。這些對於入門來說很好,但超過一定程度,您可能需要它們不提供的彈性和控制。為了使用更進階的 Agent,我們建議查看 LangGraph Agent 或遷移指南
語言模型本身無法採取行動,它們只會輸出文字。LangChain 的一個重要用例是建立Agent。Agent 是使用 LLM 作為推理引擎的系統,以確定要採取的行動以及這些行動的輸入應該是什麼。然後可以將這些行動的結果回饋給 Agent,它會判斷是否需要更多行動,或者是否可以完成。
在本教學中,我們將建構一個可以與多種不同工具互動的 Agent:一種是本機資料庫,另一種是搜尋引擎。您將能夠向此 Agent 提問、觀看它調用工具,並與其進行對話。
概念
我們將涵蓋的概念是
- 使用語言模型,特別是它們的工具調用能力
- 建立檢索器以向我們的 Agent 公開特定資訊
- 使用搜尋工具在網路上查找事物
聊天歷史記錄
,這允許聊天機器人「記住」過去的互動,並在回覆後續問題時將其納入考量。- 使用 LangSmith 偵錯和追蹤您的應用程式
設定
Jupyter Notebook
本指南(以及文件中大多數其他指南)使用 Jupyter Notebook,並假設讀者也是如此。Jupyter Notebook 非常適合學習如何使用 LLM 系統,因為通常情況下事情可能會出錯(意外輸出、API 關閉等),並且在互動式環境中瀏覽指南是更好地理解它們的好方法。
本教學和其他教學在 Jupyter Notebook 中運行可能最方便。請參閱此處以取得有關如何安裝的說明。
安裝
若要安裝 LangChain,請執行
- Pip
- Conda
pip install langchain
conda install langchain -c conda-forge
如需更多詳細資訊,請參閱我們的安裝指南。
LangSmith
您使用 LangChain 建構的許多應用程式將包含多個步驟,其中包含多次 LLM 調用。隨著這些應用程式變得越來越複雜,能夠檢查鏈或 Agent 內部到底發生了什麼變得至關重要。執行此操作的最佳方法是使用 LangSmith。
在上面的連結註冊後,請確保設定您的環境變數以開始記錄追蹤
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
或者,如果在 Notebook 中,您可以使用以下程式碼設定它們
import getpass
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = getpass.getpass()
定義工具
我們首先需要建立我們要使用的工具。我們將使用兩種工具:Tavily(在線上搜尋),然後是一個檢索器,用於檢索我們將建立的本機索引
Tavily
我們在 LangChain 中有一個內建工具,可以輕鬆地將 Tavily 搜尋引擎用作工具。請注意,這需要 API 金鑰,它們有免費層級,但如果您沒有金鑰或不想建立金鑰,您可以隨時忽略此步驟。
建立 API 金鑰後,您需要將其匯出為
export TAVILY_API_KEY="..."
from langchain_community.tools.tavily_search import TavilySearchResults
search = TavilySearchResults(max_results=2)
search.invoke("what is the weather in SF")
[{'url': 'https://www.weatherapi.com/',
'content': "{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1714000492, 'localtime': '2024-04-24 16:14'}, 'current': {'last_updated_epoch': 1713999600, 'last_updated': '2024-04-24 16:00', 'temp_c': 15.6, 'temp_f': 60.1, 'is_day': 1, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/day/122.png', 'code': 1009}, 'wind_mph': 10.5, 'wind_kph': 16.9, 'wind_degree': 330, 'wind_dir': 'NNW', 'pressure_mb': 1018.0, 'pressure_in': 30.06, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 72, 'cloud': 100, 'feelslike_c': 15.6, 'feelslike_f': 60.1, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 14.8, 'gust_kph': 23.8}}"},
{'url': 'https://www.weathertab.com/en/c/e/04/united-states/california/san-francisco/',
'content': 'San Francisco Weather Forecast for Apr 2024 - Risk of Rain Graph. Rain Risk Graph: Monthly Overview. Bar heights indicate rain risk percentages. Yellow bars mark low-risk days, while black and grey bars signal higher risks. Grey-yellow bars act as buffers, advising to keep at least one day clear from the riskier grey and black days, guiding ...'}]
檢索器
我們還將建立一個檢索器,用於檢索我們自己的一些資料。如需此處每個步驟的更深入說明,請參閱本教學。
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
docs = loader.load()
documents = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200
).split_documents(docs)
vector = FAISS.from_documents(documents, OpenAIEmbeddings())
retriever = vector.as_retriever()
retriever.invoke("how to upload a dataset")[0]
Document(page_content='# The data to predict and grade over evaluators=[exact_match], # The evaluators to score the results experiment_prefix="sample-experiment", # The name of the experiment metadata={ "version": "1.0.0", "revision_id": "beta" },)import { Client, Run, Example } from \'langsmith\';import { runOnDataset } from \'langchain/smith\';import { EvaluationResult } from \'langsmith/evaluation\';const client = new Client();// Define dataset: these are your test casesconst datasetName = "Sample Dataset";const dataset = await client.createDataset(datasetName, { description: "A sample dataset in LangSmith."});await client.createExamples({ inputs: [ { postfix: "to LangSmith" }, { postfix: "to Evaluations in LangSmith" }, ], outputs: [ { output: "Welcome to LangSmith" }, { output: "Welcome to Evaluations in LangSmith" }, ], datasetId: dataset.id,});// Define your evaluatorconst exactMatch = async ({ run, example }: { run: Run; example?:', metadata={'source': 'https://docs.smith.langchain.com/overview', 'title': 'Getting started with LangSmith | \uf8ffü¶úÔ∏è\uf8ffüõ†Ô∏è LangSmith', 'description': 'Introduction', 'language': 'en'})
現在我們已經填入索引,我們將在該索引上進行檢索,我們可以輕鬆地將其轉換為工具(Agent 正確使用它所需的格式)
from langchain.tools.retriever import create_retriever_tool
retriever_tool = create_retriever_tool(
retriever,
"langsmith_search",
"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
)
工具
現在我們已經建立了這兩種工具,我們可以建立一個工具列表,我們將在下游使用它們。
tools = [search, retriever_tool]
使用語言模型
接下來,讓我們學習如何使用語言模型來調用工具。LangChain 支援許多不同的語言模型,您可以互換使用它們,請在下方選擇您想要使用的模型!
pip install -qU "langchain[openai]"
import getpass
import os
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4", model_provider="openai")
您可以透過傳遞訊息列表來調用語言模型。預設情況下,回應是 content
字串。
from langchain_core.messages import HumanMessage
response = model.invoke([HumanMessage(content="hi!")])
response.content
'Hello! How can I assist you today?'
我們現在可以看到啟用此模型進行工具調用是什麼樣的。為了啟用該功能,我們使用 .bind_tools
為語言模型提供這些工具的知識
model_with_tools = model.bind_tools(tools)
我們現在可以調用模型。讓我們首先使用一般訊息調用它,看看它的回應方式。我們可以查看 content
欄位以及 tool_calls
欄位。
response = model_with_tools.invoke([HumanMessage(content="Hi!")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
ContentString: Hello! How can I assist you today?
ToolCalls: []
現在,讓我們嘗試使用一些預期會調用工具的輸入來調用它。
response = model_with_tools.invoke([HumanMessage(content="What's the weather in SF?")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
ContentString:
ToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_4HteVahXkRAkWjp6dGXryKZX'}]
我們可以看到現在沒有內容,但有一個工具調用!它希望我們調用 Tavily 搜尋工具。
這還沒有調用該工具,它只是告訴我們調用。為了實際調用它,我們需要建立我們的 Agent。
建立 Agent
現在我們已經定義了工具和 LLM,我們可以建立 Agent。我們將使用工具調用 Agent,有關此類型 Agent 以及其他選項的更多資訊,請參閱本指南。
我們可以先選擇要用來引導 Agent 的提示。
如果您想查看此提示的內容並存取 LangSmith,您可以前往
https://smith.langchain.com/hub/hwchase17/openai-functions-agent
from langchain import hub
# Get the prompt to use - you can modify this!
prompt = hub.pull("hwchase17/openai-functions-agent")
prompt.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')]
現在,我們可以使用 LLM、提示和工具初始化 Agent。Agent 負責接收輸入並決定要採取的行動。至關重要的是,Agent 不會執行這些操作,而是由 AgentExecutor(下一步)完成。如需有關如何考慮這些組件的更多資訊,請參閱我們的概念指南。
請注意,我們傳遞的是 model
,而不是 model_with_tools
。這是因為 create_tool_calling_agent
會在幕後為我們調用 .bind_tools
。
from langchain.agents import create_tool_calling_agent
agent = create_tool_calling_agent(model, tools, prompt)
最後,我們將 Agent(大腦)與 AgentExecutor 內的工具結合起來(它將重複調用 Agent 並執行工具)。
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools)
執行 Agent
我們現在可以在一些查詢上執行 Agent!請注意,目前,這些都是無狀態查詢(它不會記住先前的互動)。
首先,讓我們看看當不需要調用工具時,它是如何回應的
agent_executor.invoke({"input": "hi!"})
{'input': 'hi!', 'output': 'Hello! How can I assist you today?'}
為了準確查看幕後發生的情況(並確保它沒有調用工具),我們可以查看 LangSmith 追蹤
現在讓我們在一個應該調用檢索器的範例中試用它
agent_executor.invoke({"input": "how can langsmith help with testing?"})
{'input': 'how can langsmith help with testing?',
'output': 'LangSmith is a platform that aids in building production-grade Language Learning Model (LLM) applications. It can assist with testing in several ways:\n\n1. **Monitoring and Evaluation**: LangSmith allows close monitoring and evaluation of your application. This helps you to ensure the quality of your application and deploy it with confidence.\n\n2. **Tracing**: LangSmith has tracing capabilities that can be beneficial for debugging and understanding the behavior of your application.\n\n3. **Evaluation Capabilities**: LangSmith has built-in tools for evaluating the performance of your LLM. \n\n4. **Prompt Hub**: This is a prompt management tool built into LangSmith that can help in testing different prompts and their responses.\n\nPlease note that to use LangSmith, you would need to install it and create an API key. The platform offers Python and Typescript SDKs for utilization. It works independently and does not require the use of LangChain.'}
讓我們查看 LangSmith 追蹤,以確保它實際上正在調用它。
現在讓我們嘗試一個需要調用搜尋工具的範例
agent_executor.invoke({"input": "whats the weather in sf?"})
{'input': 'whats the weather in sf?',
'output': 'The current weather in San Francisco is partly cloudy with a temperature of 16.1°C (61.0°F). The wind is coming from the WNW at a speed of 10.5 mph. The humidity is at 67%. [source](https://www.weatherapi.com/)'}
我們可以查看 LangSmith 追蹤,以確保它正在有效地調用搜尋工具。
新增記憶體
如前所述,此 Agent 是無狀態的。這表示它不記得先前的互動。為了給它記憶體,我們需要傳遞先前的 chat_history
。注意:由於我們使用的提示,它需要稱為 chat_history
。如果我們使用不同的提示,我們可以更改變數名稱
# Here we pass in an empty list of messages for chat_history because it is the first message in the chat
agent_executor.invoke({"input": "hi! my name is bob", "chat_history": []})
{'input': 'hi! my name is bob',
'chat_history': [],
'output': 'Hello Bob! How can I assist you today?'}
from langchain_core.messages import AIMessage, HumanMessage
agent_executor.invoke(
{
"chat_history": [
HumanMessage(content="hi! my name is bob"),
AIMessage(content="Hello Bob! How can I assist you today?"),
],
"input": "what's my name?",
}
)
{'chat_history': [HumanMessage(content='hi! my name is bob'),
AIMessage(content='Hello Bob! How can I assist you today?')],
'input': "what's my name?",
'output': 'Your name is Bob. How can I assist you further?'}
如果我們想要自動追蹤這些訊息,我們可以將其包裝在 RunnableWithMessageHistory 中。如需有關如何使用此功能的更多資訊,請參閱本指南。
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
由於我們有多個輸入,因此我們需要指定兩件事
input_messages_key
:用於新增至對話歷史記錄的輸入鍵。history_messages_key
:用於將載入的訊息新增至其中的鍵。
agent_with_chat_history = RunnableWithMessageHistory(
agent_executor,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
)
agent_with_chat_history.invoke(
{"input": "hi! I'm bob"},
config={"configurable": {"session_id": "<foo>"}},
)
{'input': "hi! I'm bob",
'chat_history': [],
'output': 'Hello Bob! How can I assist you today?'}
agent_with_chat_history.invoke(
{"input": "what's my name?"},
config={"configurable": {"session_id": "<foo>"}},
)
{'input': "what's my name?",
'chat_history': [HumanMessage(content="hi! I'm bob"),
AIMessage(content='Hello Bob! How can I assist you today?')],
'output': 'Your name is Bob.'}
範例 LangSmith 追蹤:https://smith.langchain.com/public/98c8d162-60ae-4493-aa9f-992d87bd0429/r
結論
結束了!在這個快速入門中,我們介紹了如何建立一個簡單的 Agent。Agent 是一個複雜的主題,有很多東西要學習!
本節介紹了使用 LangChain Agent 進行建構。它們對於入門來說很好,但超過一定程度,您可能需要它們不提供的彈性和控制。為了開發更進階的 Agent,我們建議查看 LangGraph
如果您想繼續使用 LangChain Agent,一些不錯的進階指南是