[TOC] # Langchain-代理03 https://python.langchain.com/docs/get_started/quickstart#agent ## **Tavily Search API** https://tavily.com/ 註冊Tavily取得API KEY,以搜尋網路資料 ``` import os os.environ["TAVILY_API_KEY"] = "tvly-YOUR API KEY" ``` ## **基本流程** `pip install langchainhub` `pip install langchain-openai` ```python= from langchain.tools.retriever import create_retriever_tool from langchain_core.messages import HumanMessage, AIMessage from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings from langchain_openai import ChatOpenAI from langchain import hub from langchain.agents import create_openai_functions_agent from langchain.agents import AgentExecutor from langchain_community.tools.tavily_search import TavilySearchResults from langchain_community.document_loaders import WebBaseLoader from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings from langchain_text_splitters import RecursiveCharacterTextSplitter import os OPENAI_API_KEY="sk-YOUR API KEY" TYLY_API_KEY="tvly-YOUR API KEY" os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY os.environ["TAVILY_API_KEY"] = TYLY_API_KEY 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_tool = create_retriever_tool( retriever, "langsmith_search", "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!", ) search = TavilySearchResults() tools = [retriever_tool, search] # Get the prompt to use - you can modify this! prompt = hub.pull("hwchase17/openai-functions-agent") # You need to set OPENAI_API_KEY environment variable or pass it as argument `openai_api_key`. llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) agent = create_openai_functions_agent(llm, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=False) #verbose=True 詳細模式,將日誌列印出來。 print(agent_executor.invoke({"input": "how can langsmith help with testing?"})['output']) print(agent_executor.invoke({"input": "台灣台北大安區的明天天氣如何?"})['output']) print(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?", } )['output']) ``` ``` LangSmith can help with testing by providing capabilities for debugging, testing, evaluating, and monitoring chains and intelligent agents built on any LLM framework. It seamlessly integrates with LangChain, an open-source framework for building with LLMs. LangSmith offers tracing and evaluation features to assist in the testing process. Additionally, LangSmith supports various workflows at each stage of the LLM application lifecycle, making it a valuable tool for testing and development. 根據資料顯示,台灣台北大安區明天的天氣為舒適,白天氣溫介於 21 到 26 度之間,晚上氣溫介於 21 到 24 度之間。預計會有短暫雨,建議出門 時攜帶雨具。請注意溫度的變化,適時增添衣物。 Your name is Bob. How can I assist you today, Bob? ``` * langchain.agents.agent.AgentExecutor:[連結](https://api.python.langchain.com/en/latest/agents/langchain.agents.agent.AgentExecutor.html#langchain-agents-agent-agentexecutor) * agent_executor.invoke:[連結](https://python.langchain.com/docs/modules/agents/agent_types/openai_assistants#using-agentexecutor) ## **Serving with LangServe** `pip install "langserve[all]"` `pip install langchainhub` ```python= from typing import List from fastapi import FastAPI from langchain_openai import ChatOpenAI from langchain_community.document_loaders import WebBaseLoader from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain.tools.retriever import create_retriever_tool from langchain_community.tools.tavily_search import TavilySearchResults from langchain import hub from langchain.agents import create_openai_functions_agent from langchain.agents import AgentExecutor from langchain.pydantic_v1 import BaseModel, Field from langchain_core.messages import BaseMessage from langserve import add_routes import os OPENAI_API_KEY="sk-YOUR API KEY" TYLY_API_KEY="tvly-YOUR API KEY" os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY os.environ["TAVILY_API_KEY"] = TYLY_API_KEY # 1. Load Retriever loader = WebBaseLoader("https://docs.smith.langchain.com/user_guide") docs = loader.load() text_splitter = RecursiveCharacterTextSplitter() documents = text_splitter.split_documents(docs) embeddings = OpenAIEmbeddings() vector = FAISS.from_documents(documents, embeddings) retriever = vector.as_retriever() # 2. Create Tools retriever_tool = create_retriever_tool( retriever, "langsmith_search", "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!", ) search = TavilySearchResults() tools = [retriever_tool, search] # 3. Create Agent prompt = hub.pull("hwchase17/openai-functions-agent") llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) agent = create_openai_functions_agent(llm, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) # 4. App definition app = FastAPI( title="LangChain Server", version="1.0", description="A simple API server using LangChain's Runnable interfaces", ) # 5. Adding chain route # We need to add these input/output schemas because the current AgentExecutor # is lacking in schemas. class Input(BaseModel): input: str chat_history: List[BaseMessage] = Field( ..., extra={"widget": {"type": "chat", "input": "location"}}, ) class Output(BaseModel): output: str add_routes( app, agent_executor.with_types(input_type=Input, output_type=Output), path="/agent", ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000) ``` * 測試執行結果: http://localhost:8000/agent/playground/ ## 範例:agent_with_history https://github.com/langchain-ai/langserve/tree/main/examples/agent_with_history ### [sever.py](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/server.py) ```python= from typing import Any, List, Union from fastapi import FastAPI from langchain.agents import AgentExecutor, tool from langchain.agents.format_scratchpad.openai_tools import ( format_to_openai_tool_messages, ) from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser from langchain.prompts import MessagesPlaceholder from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool from langchain_core.messages import AIMessage, FunctionMessage, HumanMessage from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langserve import add_routes from langserve.pydantic_v1 import BaseModel, Field import os OPENAI_API_KEY="sk-YOUR API KEY" os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are very powerful assistant, but bad at calculating lengths of words. " "Talk with the user as normal. " "If they ask you to calculate the length of a word, use a tool", ), # Please note the ordering of the fields in the prompt! # The correct ordering is: # 1. history - the past messages between the user and the agent # 2. user - the user's current input # 3. agent_scratchpad - the agent's working space for thinking and # invoking tools to respond to the user's input. # If you change the ordering, the agent will not work correctly since # the messages will be shown to the underlying LLM in the wrong order. MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ] ) @tool def word_length(word: str) -> int: """Returns a counter word""" return len(word) # We need to set streaming=True on the LLM to support streaming individual tokens. # Tokens will be available when using the stream_log / stream events endpoints, # but not when using the stream endpoint since the stream implementation for agent # streams action observation pairs not individual tokens. # See the client notebook that shows how to use the stream events endpoint. llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, streaming=True) tools = [word_length] llm_with_tools = llm.bind(tools=[format_tool_to_openai_tool(tool) for tool in tools]) # ATTENTION: For production use case, it's a good idea to trim the prompt to avoid # exceeding the context window length used by the model. # # To fix that simply adjust the chain to trim the prompt in whatever way # is appropriate for your use case. # For example, you may want to keep the system message and the last 10 messages. # Or you may want to trim based on the number of tokens. # Or you may want to also summarize the messages to keep information about things # that were learned about the user. # # def prompt_trimmer(messages: List[Union[HumanMessage, AIMessage, FunctionMessage]]): # '''Trims the prompt to a reasonable length.''' # # Keep in mind that when trimming you may want to keep the system message! # return messages[-10:] # Keep last 10 messages. 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 # | prompt_trimmer # See comment above. | llm_with_tools | OpenAIToolsAgentOutputParser() ) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) app = FastAPI( title="LangChain Server", version="1.0", description="Spin up a simple api server using LangChain's Runnable interfaces", ) # We need to add these input/output schemas because the current AgentExecutor # is lacking in schemas. class Input(BaseModel): input: str # The field extra defines a chat widget. # Please see documentation about widgets in the main README. # The widget is used in the playground. # Keep in mind that playground support for agents is not great at the moment. # To get a better experience, you'll need to customize the streaming output # for now. chat_history: List[Union[HumanMessage, AIMessage, FunctionMessage]] = Field( ..., extra={"widget": {"type": "chat", "input": "input", "output": "output"}}, ) class Output(BaseModel): output: Any # Adds routes to the app for using the chain under: # /invoke # /batch # /stream # /stream_events add_routes( app, agent_executor.with_types(input_type=Input, output_type=Output).with_config( {"run_name": "agent"} ), ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000) ``` ### [client.py](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/client.ipynb) ```python= import asyncio from langserve import RemoteRunnable from langchain_core.messages import HumanMessage, AIMessage # 定義異步函數來處理聊天邏輯 async def chat_with_ai(): remote_runnable = RemoteRunnable("http://localhost:8000/") chat_history = [] while True: human = input("Human (Q/q to quit): ") if human in {"q", "Q"}: print('AI: Bye bye human') break ai = await remote_runnable.ainvoke({"input": human, "chat_history": chat_history}) print(f"AI: {ai['output']}") chat_history.extend([HumanMessage(content=human), AIMessage(content=ai['output'])]) # 使用 asyncio 運行異步函數 asyncio.run(chat_with_ai()) ``` ``` Human (Q/q to quit): 1+1等於? AI: 1 + 1 等於 2。有什麼其他問題我可以幫忙解答嗎? Human (Q/q to quit): what is the length of the word audioee? AI: The length of the word "audioee" is 7. Is there anything else I can assist you with? Human (Q/q to quit): 我第一個問題問什麼? AI: 你問了「1+1等於?」。有其他問題需要我的幫助嗎? Human (Q/q to quit): 1+5 AI: 1 + 5 等於 6。需要我為你做些什麼嗎? ``` ## 其他範例 https://python.langchain.com/docs/langserve ## 參考文獻 https://python.langchain.com/docs/modules/agents/quick_start#retriever