Дозвольте Близнюкам вибрати джерело: FAISS, Tavily та прямі відповіді у LangGraph
Створіть невеликий робочий процес LangGraph, у якому Gemini направляє кожне запитання до бази знань FAISS, веб-пошуку Tavily або на пряму відповідь, з використанням механізму направлення структурованих результатів.
Більшість прототипів систем відповідей на запитання проходять кожен запит через одну фіксовану схему обробки, хоча запити відрізняються за своїми потребами: деякі залежать від приватних корпоративних документів, інші — від фактів, які змінюються щодня, а ще інші — лише від загальних знань. У цьому посібнику створюється компактний робочий процес у Python, у якому мовна модель спочатку аналізує кожне запитання та направляє його у відповідне місце: до бази даних векторів FAISS для внутрішніх знань, до Tavily для актуальних результатів з Інтернету або безпосередньо до Gemini. До кінця ви зрозумієте, як стани, вузли та умовні краї поєднуються в LangGraph, чому структурований результат робить маршрутизатор LLM надійним, та де ця конструкція потребує удосконалення перед тим, як використовуватися з реальними користувачами. Якщо ви хочете отримати більш широкий каталог форм графів, перегляньте статтю про маршрутизацію, розгалуження.
«Шаблони критики та схвалення в LangGraph» доповнює цей практичний приклад реалізації.Три запитання, три різні джерела
User Question
|
v
+----------------+
| AI Router |
| (Gemini) |
+----------------+
/ | \
/ | \
v v v
FAISS Tavily Gemini
Internal DB Web Direct
\ | /
\ | /
v v v
+----------------+
| Generate Answer|
| Gemini |
+----------------+
|
v
Answer
if "weather" in question:
use_tavily()
elif "leave" in question:
use_faiss()
else:
use_gemini()
Тут це рішення делегується моделі Gemini. Оскільки модель інтерпретує запит, а не шукає у ньому ключові слова, такий підхід отримує назву «агентний».
Генеративний ШІ, агенти та агентні робочі процеси
Ці терміни часто використовуються як синоніми, але вони описують різні рівні залучення моделі.
Простий генеративний ШІ
У найпростішому варіанті запит передається моделі, і вона повертає все, що створює:
User Question
|
v
LLM
|
v
Answer
Він може пояснити принцип RAG на основі навчальних даних, але не знає нічого про вашу політику відпусток.
Агенти, які використовують інструменти
Агент розширює можливості моделі за допомогою інструментів, таких як база даних, пошуковий двигун чи зовнішня API, і дозволяє їй вирішувати, чи викликати один з них перед відповіддю:
User Question
|
v
LLM
|
+------> Database
|
+------> Web Search
|
+------> API
|
v
Answer
Наприклад, на запитання про погоду можна відповісти, звернувшись до сервісу погоди, замість того щоб модель сама створювала правдоподібний прогноз.
Робочі процеси з агентами
Робочий процес з агентом надає моделі можливість впливати на шлях виконання під час роботи програми. У цьому проекті логіка виконання виглядає так:
Question
|
v
Router
|
+----> FAISS
|
+----> Tavily
|
+----> Gemini
Розробник визначає три можливі шляхи; модель лише обирає один з них для кожного надходження запитання. Їй не надається повний контроль над програмою, а лише меню дозволених дій. „Робочий процес маршрутизації з агентами“ — це найточніша назва для такої схеми.
Налаштування проекту
Вам знадобиться Python 3.10 або новіша версія, API-ключ для Google Gemini та API-ключ для Tavily. Встановіть бібліотеки одночасно:
pip install -U langchain langchain-google-genai langchain-community langgraph faiss-cpu tavily-python python-dotenv pydantic
Зберігайте обидва ключі у файлі .env, а не у вихідному коді:
GOOGLE_API_KEY=your_google_api_key
TAVILY_API_KEY=your_tavily_api_key
Імпорти включають інструменти для роботи з типами, Pydantic для схем маршрутизації, обгортки документів від LangChain та FAISS, класи для чату та створення ембеддингів від Gemini, примітиви графів від LangGraph та клієнт Tavily:
import os
from typing import List, Literal
from typing_extensions import TypedDict
from dotenv import load_dotenv
from pydantic import BaseModel
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_google_genai import (
ChatGoogleGenerativeAI,
GoogleGenerativeAIEmbeddings,
)
from langgraph.graph import StateGraph, START, END
from tavily import TavilyClient
Наступний крок, показаний у вигляді однорядкового фрагмента, — це просто завантаження змінних середовища:
Load the environment variables:
З параметром override=True значення з файлу .env мають пріоритет над змінними, вже встановленими у вашому шеллі:
load_dotenv(override=True)
Налаштування Gemini для маршрутизації, відповідей та ембеддингів
Модель чату виконує дві функції: спочатку вона вибирає, яке джерело має обробити запитання, а потім формує кінцеву відповідь на основі зібраного контексту. Наведена нижче конфігурація дозволяє двом автоматичним спробам та залишає ліміти на кількість токенів та час виконання незмінними:
model = ChatGoogleGenerativeAI(
model="gemini-3.6-flash",
max_tokens=None,
timeout=None,
max_retries=2,
api_key=os.getenv("GOOGLE_API_KEY"),
)
Ідентифікатори моделей часто змінюються, тому перед їх використанням перевірте назву у фрагменті коду з поточним списком моделей Gemini.
Ембеддинги — це окрема проблема, якою керує окрема модель. Gemini Embedding перетворює кожен документ на числовий вектор:
embeddings = GoogleGenerativeAIEmbeddings(
model="models/gemini-embedding-001",
google_api_key=os.getenv("GOOGLE_API_KEY"),
)
Саме вектори уможливлюють семантичний пошук: тексти з схожим значенням розташовуються близько один до одного у векторному просторі, тож запит може знаходити відповідні уривки навіть тоді, коли між ними мало точних слів.
Крихітна внутрішня база знань у FAISS
Щоб зосередити увагу на робочому процесі, база знань містить лише три короткі документи: політику повернення грошей протягом 30 днів, надання 20 оплачуваних днів відпустки з вимогою попередження за сім днів та години підтримки у робочі дні. У справжній системі ці тексти походитимуть з посібників, PDF-файлів, сторінок Notion, заявок на підтримку, внутрішніх документів або рядків бази даних.
documents = [
Document(
page_content="""
Our company provides a 30-day refund policy.
Customers can request a refund within 30 days of purchase.
"""
),
Document(
page_content="""
Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.
"""
),
Document(
page_content="""
The company provides technical support from Monday to Friday,
9 AM to 6 PM IST.
"""
),
]
Створення магазину та його оформлення як засобу пошуку вимагає двох викликів. Встановлення значення k у 3 означає пошук трьох найближчих документів, що в цьому примітивному наборі даних означає, що кожен документ повертається в порядку спорідненості:
vector_store = FAISS.from_documents(
documents,
embeddings,
)
retriever = vector_store.as_retriever(
search_kwargs={"k": 3}
)
FAISS не розуміє мови; це індекс для пошуку найближчих сусідів. Модель ембеддингу перетворює надіслане запитання на вектор, а FAISS повертає збережені документи, вектори яких знаходяться найближче до цього вектора.
Додавання клієнта Tavily для отримання актуальної інформації
Для пошуку в Інтернеті потрібен лише клієнтський екземпляр, створений за допомогою другого API-ключа:
tavily_client = TavilyClient(
api_key=os.getenv("TAVILY_API_KEY")
)
Проектування спільного стану
Стан є центральною ідеєю в LangGraph: це типізований словник, який передається по графу, з якого кожен вузол читає дані та до якого сам додає інформацію. Цей процес вимагає запиту, будь-яких отриманих документів, результатів від Tavily, обраного джерела та кінцевої відповіді:
class AgentState(TypedDict):
question: str
documents: List[Document]
tavily_response: str
source: str
answer: str
Концептуально це схоже на спільний запис, який може бачити кожен етап:
AgentState
|
+-----------+-----------+
| | |
question documents source
|
tavily_response
|
answer
Вузол отримує поточний стан, використовує ті поля, які його цікавлять, та повертає оновлення; LangGraph об’єднує їх перед тим, як передати стан наступному вузлу.
Вузол пошуку FAISS
Цей вузол бере запит зі стану, обробляє його за допомогою механізму пошуку та зберігає відповідні документи:
def retrieve_from_faiss(state : AgentState) -> AgentState:
question = state['question']
""" Fetch the details from the FAISS vector database
"""
result = retriever.invoke(question)
return {**state, "documents": result}
Він виконується лише тоді, коли маршрутизатор обрав внутрішню базу знань. Таке запитання, як наведено нижче, є типовим тригером:
"What is our company leave policy?"
Для такого вхідного даних пошуковий механізм повинен вивести документ про відпустку:
Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.
Вузол пошуку Tavily
Вузол веб-пошуку надсилає запит до Tavily з параметром search_depth, встановленим на advanced, а потім отримує поле content з кожного результату:
def search_with_tavily(state: AgentState) -> AgentState:
question = state['question']
""" Using the Tavily to search the web and
fetch the latest information about user query
"""
response = tavilyClient.search(
query=question,
search_depth='advanced'
)
contents = [result["content"] for result in response["results"]]
return {**state, "tavilyResponse":contents}
Ці уривки стають контекстом, який Gemini буде використовувати під час формування відповіді. Зауважте, що вузол повертає список рядків; якщо ви бажаєте отримати один блок тексту, об’єднайте елементи перед зберіганням, щоб поле відповідало типу str, визначеному у стані.
Забезпечення надійності маршрутизатора за допомогою структурованого виводу
Примітивний маршрутизатор просить модель відповісти одним із трьох простих слів:
Return only:
faiss
tavily
gemini
Моделі мов не завжди дотримуються інструкцій щодо форматування. Можливо, ви отримаєте повне речення у відповідь:
I would choose tavily.
Або трохи іншу формулювання:
The best option is: tavily
Будь-яка з цих відповідей порушує структуру графа, якій потрібна точна рядок для вибору краю. Структурований вихід усуває цю проблему. Спочатку описайте дозволені варіанти рішень як модель Pydantic, чия єдина поля обмежена трьома літеральними значеннями:
class RouteDecision(BaseModel):
source: Literal["faiss", "tavily", "gemini"]
Потім створіть модель маршрутизатора, яка повинна повертати екземпляр цієї схеми. Метод json_schema змушує постачальника обмежувати генерацію схемою, замість того щоб покладатися лише на формулювання запиту:
router_model = model.with_structured_output(
RouteDecision,
method="json_schema",
)
Вихід перевіряється за типом Literal, тому недійсне значення виявляється як помилка, замість того щоб спрямувати граф у невизначене місце.
Створення вузла рішення
Інструкція з маршрутизації описує кожен варіант: faiss — для запитань щодо корпоративної політики та інших внутрішніх знань, tavily — для будь-чого, що потребує актуальної чи веб-орієнтованої інформації, а gemini — для загальних знань, які не вимагають жодного з цих підходів:
def decide_source(state:AgentState)-> AgentState:
question = state["question"]
prompt = f"""
Decide the best source for answering this question.
Choose exactly one:
faiss:
Use when the question can be answered using our internal
knowledge base.related to company policy and all
tavily:
Use when the question requires current, recent, or web-based
information.
gemini:
Use when the question is general knowledge and does not
require our internal documents or current web information.
Question:
{question}
Return only one word:
faiss, tavily, or gemini
"""
response = model.invoke(prompt)
# return response
return {**state,"source":response.text}
Уважно подивіться на останні рядки. У нинішньому вигляді вузол все ще викликає звичайний model та зберігає response.text, що є саме тим крихким підходом з вільним текстом, про який йшлося вище. Щоб скористатися перевагами схеми, замість цього використовуйте router_model.invoke(prompt) та зберігайте атрибут source результату. Після цієї зміни маршрутизатор повертатиме об’єкт з визначеним типом, а не довільний прозовий текст:
RouteDecision(source="faiss")
Вказування LangGraph, куди рухатися далі
Умовні краї потребують функції, яка визначає, яку гілку слід обрати. Ця функція не приймає жодних рішень самостійно; вона читає вибір, який маршрутизатор вже зберіг у стані, та передає його назад до LangGraph:
def route_source(
state: AgentState,
) -> Literal["faiss", "tavily", "gemini"]:
return state["source"]
Один вузол відповіді для кожного маршруту
Усі три гілки завершуються на одному кроці генерації. Вона аналізує source та створює відповідний запит: отримані документи об’єднуються у контекст для FAISS, уривки пошуку використовуються як матеріал для порівняння у Tavily, або ж саме запит використовується для прямих відповідей:
# Generate the answer for the user
def generateAnswer(state:AgentState) -> AgentState:
source = state["source"]
question = state['question']
documents = state['documents']
tavilyResponse = state['tavilyResponse']
if source == "faiss":
context = "\n\n".join([doc.page_content for doc in documents])
prompt = f"""Based on the following context answer the question below
Context:
{context}
Question:
{question}
"""
elif source == "tavily":
prompt = f""" Based on the following search result , use this as an reference and provdie the
answer to the below question
Context:
{tavilyResponse}
Question:
{question}
"""
else:
prompt = f" Answer the following question : {question}"
response = model.invoke(prompt)
answer = response.content
return {**state, "answer":answer}
Кожну відповідь пише той самий модель Gemini. Єдине, що змінюється, — це контекст, який подається перед нею; саме це є основною ідеєю RAG: кращі вхідні дані, а не інший модель, забезпечують більш обґрунтовані відповіді.
Збереження послідовності назв під час складання уривків
У цих фрагментах поєднуються два стилі найменувань, і їхні неузгодженості спричинять помилки, якщо склеїти їх разом без змін. Стан оголошується як tavily_response, тоді як вузли читають та записують tavilyResponse; клієнт створюється як tavily_client, але викликається як tavilyClient; функція відповіді визначається як generateAnswer, але реєструється як generate_answer. Виберіть одну конвенцію та застосовуйте її скрізь перед запуском графа.
Складання графа
На цьому етапі елементи — це вузол прийняття рішень та три можливі продовження:
decide_source
|
+----> faiss
|
+----> tavily
|
+----> gemini
Тут є нюанс. Шлях gemini зовсім не є кроком отримання даних; він означає „пропустити отримання даних та відповісти безпосередньо“. Тож замість створення порожнього вузла для нього ця гілка може безпосередньо вказувати на спільний вузол генерації.
Почніть з створення графа за типом стану:
workflow = StateGraph(AgentState)
Зареєструйте чотири вузли:
workflow.add_node("decide", decide_source)
workflow.add_node("faiss", retrieve_from_faiss)
workflow.add_node("tavily", search_with_tavily)
workflow.add_node("generate", generate_answer)
Зробіть вузол прийняття рішень вхідною точкою:
workflow.add_edge(START, "decide")
Під’єднайте умовні ребра. Мапування перетворює кожне значення, яке може повернути функція маршрутизації, на назву вузла — саме туди gemini напряму надсилається до функції generate:
workflow.add_conditional_edges(
"decide",
route_source,
{
"faiss": "faiss",
"tavily": "tavily",
"gemini": "generate",
},
)
Потім гілки отримання даних та пошуку мають перейти до генерації відповіді:
workflow.add_edge("faiss", "generate")
workflow.add_edge("tavily", "generate")
Генерація є останнім кроком перед завершенням графа:
workflow.add_edge("generate", END)
Компіляція перетворює визначення на запускову програму:
app = workflow.compile()
Готовий граф
Повний процес від початку до кінця:
START
|
v
+--------------+
| decide |
| source |
+--------------+
/ | \
/ | \
v v v
+------+ +--------+ +---------+
|FAISS | | Tavily | | Generate|
| | | | | directly|
+------+ +--------+ +---------+
\ | /
\ | /
v v v
+----------------+
| generate |
| answer |
+----------------+
|
v
END
Пам’ятайте про основний принцип: граф визначає набір можливих шляхів, а модель обирає один з них під час виконання. Саме ця комбінація робить робочий процес агентним, не роблячи його непередбачуваним.
Спроба трьох шляхів
Невеликий допоміжний скрипт створює початковий стан та запускає скомпільований додаток:
def ask_question(question: str):
initial_state = {
"question":question,
"documents":[],
"tavilyResponse":"",
"source":""
}
result = app.invoke(initial_state)
return result
Питання, яке потребує використання Інтернету
Питання про погоду слід надсилати до Tavily:
result = ask_question(
"What is the current weather in Uttarakhand?"
)
Виведіть як обраний джерело, так і генеровану відповідь:
print("Source:", result["source"])
print("Answer:", result["answer"])
Очікуваний шлях:
Source: tavily
Текущі умови існують лише в Інтернеті.
Питання загальних знань
Далі поставте запитання про Retrieval Augmented Generation:
result = ask_question(
"What is Retrieval Augmented Generation?"
)
Виведіть результат так само:
print("Source:", result["source"])
print("Answer:", result["answer"])
Рутер повинен повністю пропустити процес отримання даних:
Source: gemini
Модель може самостійно пояснити цей концепт.
Питання щодо внутрішньої політики
Нарешті, питання про політику відпусток:
result = ask_question(
"What is our company leave policy?"
)
І ті самі оператори виведення:
print("Source:", result["source"])
print("Answer:", result["answer"])
Цього разу внутрішня база знань має дати правильну відповідь:
Source: faiss
Маршрутизація за допомогою LLM є ймовірнісною, тому сприймайте це як очікувані результати, а не гарантії.
Чому семантична маршрутизація краща за правила на основі ключових слів
Ось знову ж таки альтернатива у вигляді жорстко закодованих правил:
if "weather" in question:
use_tavily()
elif "leave" in question:
use_faiss()
else:
use_gemini()
Подібні правила швидко втрачають свою ефективність. Уявіть користувача, який запитує, чи працює офіс у суботу. У цьому реченні не згадується жодна політика, проте відповідь може знаходитися у внутрішній документації. Маршрутизатор, заснований на моделі, може зрозуміти намір користувача; список ключових слів цього не здатний, якщо тільки хтось не передбачить кожну можливу формулювання.
Цей підхід також краще масштабується. Коли з’являються нові бекенди, такі як ті, що наведені нижче, ви розширюєте схему та запит, замість того щоб створювати складну мережу умовних операторів:
SQL Database
Internal API
CRM
Customer Support System
Documentation
Web Search
Єдиний недолік — додатковий виклик моделі з відповідними витратами та затримками перед початком реальної роботи.
Чи це справді штучний інтелект?
Більш точно буде назвати це невеликою агентською робочою процедурою, ніж автономним агентом. Доступні дії визначаються заздалегідь:
FAISS
Tavily
Direct Gemini
Модель не може самостійно вирішити виконати щось подібне, оскільки їй ніколи не надавалися такі можливості:
delete a database
send an email
call an arbitrary API
Архітектура представляє собою ланцюг відповідальностей:
Developer defines possible actions
|
v
LLM chooses action
|
v
LangGraph executes
|
v
Result
Ця обмеженість є перевагою: обмежений вибір забезпечує прогнозовану та контрольовану поведінку в умовах реального використання.
Роль кожного компонента
LangGraph: двигун робочих процедур
LangGraph контролює структуру додатку: стан, вузли, ребра, умовне маршрутизування та порядок виконання. У абстрактному сенсі кожен крок дотримується однакової схеми:
State
|
v
Node
|
v
Updated State
|
v
Conditional Edge
|
+----> Node A
|
+----> Node B
|
+----> Node C
Граф з невеликою кількістю кроків легше тестувати та спостерігати, ніж одна велика функція.
FAISS: шар пошуку
FAISS забезпечує частину системи, пов’язану з RAG. У спрощеній формі:
Company Documents
|
v
Embeddings
|
v
FAISS
|
v
Similar Documents
|
v
Gemini
|
v
Answer
Під час реального впровадження перед нею додається конвейер для отримання даних:
Documents
|
v
Load
|
v
Split into chunks
|
v
Generate embeddings
|
v
Store vectors
|
v
Retrieve relevant chunks
|
v
Generate answer
У прикладі пропускається завантаження та розділення на частини, щоб зосередитися на робочому процесі; справжні посібники вимагають обох етапів.
Tavily: пошук у Інтернеті в реальному часі
Tavily обробляє інформацію, яка змінюється з часом:
User Question
|
v
Router
|
v
Tavily
|
v
Search Results
|
v
Gemini
|
v
Answer
Типовими прикладами є поточна погода, актуальні новини, нещодавні випуски продуктів, оновлена документація, свіжі події та дані ринку в реальному часі. У продакшені також необхідно вирішити, як будуть цитуватися, фільтруватися, перевірятися та відображатися результати пошуку, адже веб-контент не гарантовано є точним чи безпечним для передачі до моделі без перевірки.
Шаблон, який варто запам’ятати
Компоненти є взаємозамінними. Суттєвою ідеєю є маршрутизація: підбір кожного запиту до найкращої здатності, яка підходить для його відповіді.
Internal Knowledge
|
+------ FAISS
Current Information
|
+------ Tavily
General Knowledge
|
+------ Gemini
Цей вибір здатності для кожного запиту зустрічається майже в кожному серйозному агентському додатку.
Куди рухатися далі
Додати більше інструментів
Маршрутизатор може обирати серед значно більшої кількості серверних частин:
SQL Database
REST APIs
CRM
Email
Calendar
Internal Documentation
Посилити процес пошуку
Три документи є лише прикладом, а не базою знань. Справжній RAG потребує механізмів завантаження документів, їх розділення на частини, метаданих, кращих стратегій пошуку, переранжування результатів, посилань на джерела та контролю доступу, щоб користувачі отримували лише те, що їм дозволено бачити.
Перевірка рішень щодо маршрутизації
Крок перевірки між маршрутизатором та інструментами може відхилити нелогічні варіанти та забезпечити безпечний перехід на альтернативу:
Router
|
v
Validator
|
+---- valid ----> Tool
|
+---- invalid --> Fallback
Це стає ще важливішим зі зростанням кількості інструментів.
Керування несправностями
Структурований вихід гарантує наявність коректного маршруту, а не успішний виклик інструменту. API пошуку може завершитися через тайм-аут або не повернути жодних даних:
Router
|
v
Tavily
|
X
Search failed
|
v
Fallback
У продакшн-системах необхідні повторні спроби та запасний варіант, наприклад пряма відповідь із чітким застереженням. Стаття на про проектування стійких графів агентів із повторними спробами та запасними варіантами детальніше розглядає це питання.
Зробіть це спостережуваним
Коли відповідь є неправильною, потрібно знати, на якому етапі виникла проблема:
Wrong route?
|
v
Bad retrieval?
|
v
Bad search results?
|
v
Bad generation?
Фіксування обраних вихідних даних та проміжних результатів дозволяє отримати відповідь на це запитання.
Введіть цикли
Текущий граф приймає рішення лише один раз:
Question
|
v
Router
|
v
Tool
|
v
Answer
Більш здатний агент оцінює отриману інформацію та вирішує, чи потрібно діяти знову:
Question
|
v
Reason
|
v
Tool
|
v
Evaluate Result
|
+---- Need more information?
| |
| v
| Tool
| |
+------------+
|
v
Final Answer
Саме тут агентні системи набувають справжньої потужності: модель вирішує, чи є наявна інформація достатньою, чи потрібна інша дія. Тут також необхідні обмеження щодо кількості ітерацій, адже цикл не може працювати нескінченно.
Ключові висновки
Увесь системний підхід відповідає на одне запитання: як програма на основі ШІ має вирішувати, звідки брати відповідь? Готовий алгоритм роботи виглядає так:
User Question
|
v
Gemini
Router
|
+----------+----------+
| | |
v v v
FAISS Tavily Gemini
Internal DB Web Direct
| | |
+----------+----------+
|
v
Gemini
Final Answer
- Визначте можливості та межі в графі; дозвольте моделі обирати серед них під час виконання.
- Використовуйте структурований вихід для прийняття рішень щодо маршрутизації та переконайтеся, що вузол прийняття рішень дійсно викликає структуровану модель.
- Зберігайте один вузол генерації відповідей та змінюйте лише контекст, який ви йому надаєте.
- Розглядайте маршрутизацію як компонент, який можна тестувати: фіксуйте рішення та перевіряйте їх на прикладних запитаннях.
Відсилаючись до цих принципів, їх можна легко застосувати до SQL-баз даних, API, оперативної пам’яті, людського схвалення, вузлів оцінки, повторних спроб та багатоагентних конфігурацій. Граф розширюється, але принцип залишається незмінним: надайте моделі корисні функції, визначте, як їх можна використовувати, та дозвольте їй самій вирішити, яка з них підходить для завдання.
Пов’язана література
- Вбудовування агента LangChain у FastAPI: інструменти, ручний пошук, стрімінг — Створення вбудованого асистента за допомогою FastAPI та LangChain: PDF-довідник у ChromaDB, який використовується як інструмент, контекст для кожного користувача, історія з позначками стану та стрімовані відповіді.
- Проектування чотирирівневої пам’яті агента з використанням LangGraph та Amazon Bedrock — Дізнайтеся, як надати агентам на основі LLM функції епізодичної, семантичної та процедурної пам’яті на платформах Bedrock та LangGraph, а також як захистити її від отруєння даними, витоку конфіденційної інформації та несанкціонованого доступу.