Główna idea agenta LangChain Agent polega na wykorzystaniu LLM jako mózgu do automatycznego myślenia, podejmowania decyzji i wykonywania różnych działań, aby ostatecznie zrealizować nasze celowe zadania.
Wskazówka: Z perspektywy rozwoju oznacza to, że wcześniej rozwijamy różne interfejsy API, a następnie dajemy Agentowi zadanie, pozwalając LLMowi na analizę, które API wywołać, aby wykonać zadanie.
Aby lepiej zrozumieć problem, który ma na celu rozwiązanie Agent LangChain, rozważmy przykład.
Na przykład:
Jeśli chcemy zbadać, czy "Docker można używać jako rozwiązanie wdrożenia produkcyjnego", najpierw wyszukamy "wprowadzenie do Docker" na Baidu, przejrzymy wyniki wyszukiwania, a następnie dalej wyszukamy "zalety i wady wdrożenia Docker" i przejrzymy wyniki, i tak dalej, aż do osiągnięcia wniosku.
Agent LangChain ma na celu zasymulowanie tego procesu. Mogę wcześniej zapakować serię narzędzi (np. wyszukiwanie Baidu, narzędzia do wyodrębniania zawartości URL), a następnie dać Agentowi zadanie docelowe "Czy Docker można używać jako rozwiązanie wdrożenia produkcyjnego?" Agent następnie skonstruuje polecenia, aby wywołać LLM. Aby osiągnąć to zadanie, następnym krokiem jest wykonanie jakiej akcji (czyli wywołanie której funkcji). Sztuczna inteligencja zwróci narzędzie do wywołania, kod wykonuje to narzędzie, a następnie przekazuje wyniki wykonania narzędzia z powrotem do sztucznej inteligencji, pytając o następny krok w wykonaniu którego narzędzia. Powtarzając ten proces, ukończymy wcześniej wspomniane zadanie.
Wskazówka: Od wydania modelu GPT, jest to wybuchowa możliwość, pozwalająca LLMowi działać jako mózg, aktywnie myśleć, a następnie wywoływać różne interfejsy API, które opracowaliśmy. To znacznie zwiększa możliwości LLM. Obecnie ta funkcja jest wciąż w fazie eksperymentalnej i będzie wielokrotnie wywoływać LLMa, więc zużywa dość dużo tokenów. Ukończenie zadania może kosztować dziesiątki tysięcy tokenów w ciągu kilku minut. Jeśli chcesz oszczędzać pieniądze, zaleca się najpierw pozwolenie LLMowi wykonać proste zadania logiczne.
Podstawowe pojęcia
Teraz przedstawimy powiązane komponenty i pojęcia.
Agent
Agent można rozumieć jako naszego asystenta, działającego w naszym imieniu, podejmującego decyzje. W podstawowej implementacji agenta LangChain działa to poprzez LLM, aby określić następne działanie (lub wywołanie interfejsu API). Tryb ReAct opisuje proces podejmowania decyzji przez sztuczną inteligencję przez zainteresowanych.
LangChain oferuje kilka typów agentów do różnych scenariuszy.
Narzędzia
Uważam, że narzędzia lepiej jest rozumiane jako interfejsy API, wcześniej zapakowane z różnymi funkcjonalnymi interfejsami API zaprojektowanymi do rozszerzenia możliwości LLM. LLM określa, który konkretny interfejs API ma wywołać, aby wykonać zadanie.
Zestawy narzędzi
Zestawy narzędzi zazwyczaj dostarczają LLMowi nie tylko jedno lub dwa narzędzia, ale zestaw narzędzi, aby dać LLMowi więcej opcji podczas wykonywania zadań.
AgentExecutor
Pełnomocnik wykonawczy jest odpowiedzialny za wykonanie narzędzia (API) wybranego przez LLM. Pseudokod tego procesu wykonywania jest następujący:
next_action = agent.get_action(...)
while next_action != AgentFinish:
observation = run(next_action)
next_action = agent.get_action(..., next_action, observation)
return next_action
O ile proces wykonania nie jest skomplikowany, wykonawca zajmuje się wieloma szczegółowymi kwestiami, głównie:
- Obsługa sytuacji, w której agent wybiera nieistniejące narzędzie
- Obsługa sytuacji błędu narzędzia
- Obsługa sytuacji, w której agent generuje wynik, który nie może być rozwiązany jako wywołanie narzędzia
- Kwestie związane z debugowaniem.
Szybki start
Ta sekcja przedstawia podstawowe użycie agenta LangChain.
1. Załaduj LLM
Najpierw załadujmy model językowy (LLM), który będziemy używać do kontrolowania agenta.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
2. Zdefiniuj narzędzia
Następnie definiujemy kilka narzędzi, które Agent ma wywołać. Napiszemy bardzo prostą funkcję w języku Python, aby obliczyć długość wprowadzonego słowa.
from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
"""Zwraca długość słowa."""
return len(word)
get_word_length.invoke("abc")
Uwaga: Komentarze funkcji są bardzo ważne. Informują one LLM, jaki problem może rozwiązać poprzez ich wywołanie. Funkcja
get_word_length
mówi LLMowi, że może obliczyć długość słowa.
3
Zdefiniuj zbiór narzędzi
narzędzia = [get_word_length]
3. Utwórz polecenie
Teraz utwórzmy polecenie. Ponieważ funkcja OpenAI Function Calling została zoptymalizowana pod kątem użyteczności narzędzi, prawie nie potrzebujemy żadnych instrukcji dotyczących rozumowania ani formatu wyjściowego. Mamy tylko dwie zmienne wejściowe: input
i agent_scratchpad
. input
reprezentuje pytanie użytkownika, a agent_scratchpad
jest miejscem na instrukcje wywołania agenta, które zostaną wstawione do szablonu polecenia podczas wywoływania polecenia narzędzi.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Jesteś bardzo potężnym asystentem, ale nie rozumiesz aktualnej sytuacji.",
),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
4. Podłącz narzędzia do LLM
Jak agent wie, które narzędzia może użyć?
To zależy od funkcji wywoływania narzędzi OpenAI (wiele modeli wspiera podobne funkcje). Musimy tylko powiedzieć modelowi, jaki jest zdefiniowany format wywołania narzędzia.
llm_with_tools = llm.bind_tools(tools)
5. Tworzenie agenta
Teraz, gdy zintegrowaliśmy poprzednią zawartość, możemy przejść do utworzenia programu pośredniczącego. Zaimportujemy ostatnie dwie praktyczne funkcje narzędzi: jedną do formatowania kroków pośrednich (działania proxy, wyniki narzędzi) na wiadomości wejściowe, które można wysłać do modelu, oraz drugą do zamiany wiadomości wyjściowych na działania proxy/zakończenia proxy.
from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
Definiowanie wykonawcy agenta
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
Pokażmy działanie programu pośredniczącego na przykładzie:
list(agent_executor.stream({"input": "Ile liter jest w słowie 'eudca'?"}))
Przykład dziennika wyjścia agenta
> Rozpoczynanie nowego łańcucha wykonania agenta...
Wywołanie: Uruchamianie `get_word_length` z parametrem `{'word': 'educa'}`
W wyrazie "educa" jest 5 liter.
> Zakończenie łańcucha wykonania.
Przez ten przykład zademonstrowaliśmy cały proces programu pośredniczącego.
Dodanie funkcjonalności pamięci do agenta
Jeśli chcemy, aby agent zapamiętywał poprzednie rozmowy, jest to dość proste: musimy tylko wstawić zawartość zwróconą przez SI do szablonu polecenia i przesłać ją razem z nim do SI.
Modyfikacja szablonu polecenia
Poniżej modyfikujemy szablon polecenia, aby zawierał zmienną szablonu historii rozmowy.
from langchain.prompts import MessagesPlaceholder
MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Jesteś świetnym asystentem, ale nie radzisz sobie z obliczaniem długości słów.",
),
MessagesPlaceholder(variable_name=MEMORY_KEY),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
Modyfikacja definicji procesu agenta
Zmodyfikuj definicję procesu agenta, aby dostarczyć dane historii rozmowy do szablonu polecenia, jak pokazano w poniższym kodzie, dodając obsługę parametru 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)
Udostępnianie historii rozmowy podczas wywoływania agenta
chat_history = []
input1 = "Ile liter jest w słowie '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": "Czy to słowo naprawdę istnieje?", "chat_history": chat_history})
W rzeczywistych scenariuszach biznesowych można zapisać historię rozmowy do bazy danych i wstawiać dane do komunikatu w miarę potrzeb zgodnie z wymaganiami biznesowymi.
Wskazówka: Funkcja pamięci dużych modeli (LLM) jest obecnie głównie osiągana poprzez wstawianie historycznej treści rozmowy do komunikatu i przekazywanie jej do LLM. LangChain po prostu dostarcza pewne kapsułkowanie. Możesz zdecydować się nie używać go i ręcznie łączyć historię rozmowy w szablonie komunikatu.