Агенты ReAct в LangGraph: шаг за шагом — мышление, действие, наблюдение
Реализуйте цикл ReAct в виде явных узлов графа с типизированным состоянием, вызовами инструментов и условиями остановки, которые можно протестировать.
В этом руководстве воссоздаётся рабочий путь для проекта «ReAct Agents Explained: Пошаговая реализация с использованием LangGraph». Основное внимание уделяется контрактам, проверкам и коду, который можно просто добавить в репозиторий без необходимости угадывать его назначение. Для получения общего представления необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не пытаясь угадать скрытое состояние. Желательно использовать небольшие, тестируемые единицы вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной цепочкой операций.
Введение
В качестве введения необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия элементам, определите критерии успеха и не допускайте молчаливого частичного выполнения. Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает варианты; исполнитель вносит изменения; проверщик сравнивает результаты с поставленной целью.
Что такое агент ReAct?
В разделе «Что такое агент ReAct?» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и затраты. Раннее отображение информации предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Необходимо разделять планирование и выполнение с помощью инструментов: планировщик предлагает варианты, исполнитель вносит изменения, а проверщик сравнивает результаты с поставленной целью.
Почему ReAct лучше, чем чистая модель цепочки мыслей
Чтобы понять, почему ReAct лучше чистой модели Chain-of-Thought, необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь код. Необходимо разделить процесс планирования и выполнение с помощью инструментов. Планировщик предлагает действия; исполнитель их реализует; проверщик сравнивает результаты с поставленной целью. Чтобы понять, почему ReAct лучше чистой модели Chain-of-Thought, необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину.
а не сложной цепочкой операций.Thought: I don’t know the answer yet. I should search.
Action: Search("Paris weather this week")
Observation: It will rain on Thursday.
Thought: I should suggest indoor activities.
Final Answer: ...
ReAct Prompting
При использовании метода ReAct Prompting необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не пытаясь угадать скрытое состояние. Считайте этот этап договором между входными данными и проверенными результатами. Дайте названия результатам работы, определите критерии успеха и не допускайте молчаливого частичного выполнения задачи. Строго ограничьте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению вредоносного кода и затрудняют аудит.
Цель метода ReAct Prompting
Для целей использования подхода ReAct Prompting необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо фиксировать время выполнения и затраты рядом с функциональными результатами. Заранее видимая информация предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Схемы инструментов следует строго ограничивать; широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
Ключевые элементы подхода ReAct Prompting
Для ключевых элементов подсказок ReAct необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Строго ограничьте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению угроз и затрудняют аудит. Для ключевых элементов подсказок ReAct необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину, а не на запутанную структуру обработки данных.
1. Расчет с использованием цепочки мыслей
Для метода 1. Расчет с использованием цепочки мыслей необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывания скрытого состояния. Считайте этот этап договором между входными данными и проверенными результатами. Укажите названия создаваемых файлов, определите критерии успеха и не допускайте молчаливого частичного выполнения задачи. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
2. Явное пространство действий
2. Пространство явных действий: перед изменением кода необходимо определить входные данные, ответственного за выполнение шага и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и затраты. Раннее отображение информации предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. После дорогостоящих вызовов модели создается точка контроля, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
3. Интеграция наблюдений
Для пункта 3. Интеграция наблюдений необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторные попытки не влекли за собой повторной оплаты одних и тех же операций. Для пункта 3. Интеграция наблюдений необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода большим, запутанным скриптам. При сбое шага причина должна быть связана с конкретной функцией, а не с всей сложной цепочкой операций.
4. Итеративные циклы
Для 4. Итеративного цикла необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия результатам работы, определите критерии успеха и не допускайте молчаливого частичного завершения задачи. Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает варианты; исполнитель вносит изменения; проверщик сравнивает результаты с поставленной целью.
5. Формирование окончательного ответа
Для пятого этапа — генерации окончательного ответа — необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Регистрируйте время выполнения и затраты рядом с функциональными результатами. Раннее видимость данных предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. Разделяйте планирование и выполнение с помощью инструментов: планировщик предлагает варианты, исполнитель вносит изменения, а проверщик сравнивает результаты с поставленной целью.
Каноническая структура промпта ReAct
Для канонической структуры промптов ReAct необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь код. Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает действия; исполнитель их реализует; проверщик сравнивает результаты с целью. Для канонической структуры промптов ReAct необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину, а не на запутанную структуру обработки данных.
Ин.
Question: <user question>Thought: <reason about what to do next>
Action: <selected tool>
Action Input: <tool input>
Observation: <tool output>
... (repeat as needed)
Thought: I now know the final answer
Final Answer: <answer to the user>
Zero-Shot ReAct Prompting
При использовании метода Zero-Shot ReAct Prompting необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Считайте этот этап контрактом между входными данными и проверенными результатами. Укажите названия результатов работы, определите критерии успеха и не допускайте молчаливого частичного выполнения задачи. Строго ограничьте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению вредоносного кода и затрудняют аудит.
ReAct Prompting против ReAct Agents
При использовании подходов ReAct Prompting и ReAct Agents необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и затраты. Чем раньше становится видна ситуация, тем меньше вероятность неожиданных расходов при переходе с демо-среды в общедоступные среды. Схемы инструментов следует строго ограничивать; широкие параметры в виде свободного текста способствуют внедрению угроз и затрудняют аудит.
Почему LangGraph для ReAct Agents?
Чтобы понять, почему выбирается LangGraph для агентов ReAct, необходимо до изменения кода определить входные данные, ответственного за шаг и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Схемы инструментов следует строго ограничивать. Широкие параметры в виде свободного текста способствуют внедрению угроз и затрудняют аудит. Чтобы понять, почему выбирается LangGraph для агентов ReAct, необходимо до изменения кода определить входные данные, ответственного за шаг и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину, а не на запутанную цепочку операций.
В разделе «Основная проблема: ReAct — это машина состояний, а не промпт» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия элементов, определите критерии успеха и откажитесь от молчаливого частичного выполнения задачи. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
Для сценариев, которые не работают без LangGraph, определите входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения и затраты рядом с функциональными результатами. Раннее отображение информации предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
1. Косвенный поток управления
Для 1. Косвенного потока управления необходимо определить входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь граф обработки. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты за одну и ту же работу. Для 1. Косвенного потока управления необходимо определить входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной цепочкой операций.
while True:
llm_output = llm(prompt)
if "Action:" in llm_output:
tool_result = call_tool(...)
else:
break
2. Хрупкое управление состоянием
2. Управление хрупким состоянием: перед изменением кода необходимо определить входные данные, ответственного за выполнение шага и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия элементам, определите критерии успеха и не допускайте молчаливого частичного завершения работы. Разделяйте планирование и выполнение с помощью инструментов: планировщик предлагает варианты, исполнитель вносит изменения, а проверщик сравнивает результаты с поставленной целью.
3. Отсутствие семантики циклов первого класса
Пункт 3. В отсутствие семантики циклов первого класса необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и затраты. Заранее видимость данных предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Необходимо разделять планирование и выполнение с помощью инструментов: планировщик предлагает варианты, исполнитель вносит изменения, а проверщик сравнивает результаты с поставленной целью.
4. Недостаточная готовность к производственному использованию
Для пункта 4 «Низкая готовность к эксплуатации» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь код. Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает действия; исполнитель их реализует; проверщик сравнивает результаты с поставленной целью. Для пункта 4 «Низкая готовность к эксплуатации» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода большим скриптам. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной цепочкой операций.
Ключевые концепции в LangGraph (подход с упором на агента)
Для реализации ключевых концепций в LangGraph (подход с упором на агента) необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия результатам работы, определите критерии успеха и не допускайте молчаливого частичного завершения задачи. Строго ограничьте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению угроз и затрудняют аудит.
1. Состояние: память агента
Для пункта 1. «Память агента»: перед изменением кода необходимо определить входные данные, ответственного за выполнение шага и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Регистрируйте время выполнения и затраты рядом с функциональными результатами. Заранее видимость данных предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Жестко ограничьте схемы инструментов — широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
2. Узлы: когнитивные и операционные единицы
Для узлов типа 2: когнитивные и операционные единицы, определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Строго ограничьте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению угроз и затрудняют аудит. Для узлов типа 2: когнитивные и операционные единицы, определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы большим скриптам. Когда шаг терпит неудачу, причина должна указывать на конкретную ответственность, а не на запутанную цепочку операций.
3. Граничные условия: явный поток управления
При использовании подхода «3. Граничные условия: явный поток управления» необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия элементов, определите критерии успеха и не допускайте безответственного частичного выполнения задачи. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторные попытки не влекли за собой повторной оплаты той же работы.
4. Детерминистическое выполнение с гибкостью
4. Детерминистическое выполнение с гибкостью: определите входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения и затраты рядом с функциональными результатами. Раннее отображение информации предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. Устанавливайте точку контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
ReAct + LangGraph: идеальное сочетание
Для ReAct + LangGraph: это идеальное сочетание. Определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Создавайте точки контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной обработки той же работы. Для ReAct + LangGraph: это идеальное сочетание. Определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода большим скриптам. Когда шаг терпит неудачу, причина должна быть связана с одной конкретной функцией, а не с запутанной цепочкой операций.
Сценарий использования: Ассистент для отмены бронирования в отеле (правила + расчёт возврата средств)
Для сценария использования «Ассистент для отмены бронирования в отеле (правила + расчёт возврата средств)» необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия элементам, определите критерии успеха и не допускайте молчаливого частичного выполнения задачи. Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает решения; исполнитель вносит изменения; проверщик сравнивает результаты с поставленной целью.
Формулировка проблемы
Для описания проблемы необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо разделить процесс планирования и выполнение с помощью инструментов: планировщик предлагает действия, исполнитель их реализует, а проверщик сравнивает результаты с поставленной целью.
Шаг 1: Установка зависимостей
Необходимо разделить процесс планирования и выполнение с помощью инструментов: планировщик предлагает действия, исполнитель их реализует, а проверщик сравнивает результаты с поставленной целью.
pip install -U langgraph langchain langchain-openai
export OPENAI_API_KEY="..."
Шаг 2: Определение инструментов (ваших «Действий»)
Схемы инструментов должны быть строго ограничены. Широкие параметры в формате свободного текста способствуют внедрению угроз и затрудняют аудит.
from typing import TypedDict, Annotated
from datetime import datetime
import json
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
BaseMessage,
HumanMessage,
ToolMessage,
SystemMessage
)
from langchain_core.tools import tool
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import tools_condition
@tool
def get_cancellation_policy(rate_plan: str) -> str:
"""
Returns cancellation policy text for a given rate plan.
"""
policies = {
"flexible": "Free cancellation until 24 hours before check-in. After that, first night is charged.",
"semi-flex": "Free cancellation until 72 hours before check-in. After that, 50% of the stay is charged.",
"non-refundable": "No refund after booking. Full stay amount is charged on cancellation."
}
key = rate_plan.strip().lower()
return policies.get(key, "Policy not found. Supported: flexible, semi-flex, non-refundable.")
@tool
def calculate_refund(
rate_plan: str,
check_in: str,
cancel_date: str,
nightly_rate: float,
nights: int
) -> str:
"""
Calculates refund amount based on a simplified policy model.
Dates format: YYYY-MM-DD
"""
rp = rate_plan.strip().lower()
ci = datetime.strptime(check_in, "%Y-%m-%d").date()
cd = datetime.strptime(cancel_date, "%Y-%m-%d").date()
total = nightly_rate * nights
days_before = (ci - cd).days
if rp == "non-refundable":
refund = 0.0
charged = total
rule = "Non-refundable: no refund."
elif rp == "flexible":
if days_before >= 1:
refund = total
charged = 0.0
rule = "Flexible: cancelled >= 24h before check-in, full refund."
else:
charged = nightly_rate # 1 night penalty
refund = max(total - charged, 0.0)
rule = "Flexible: late cancel, 1 night charged."
elif rp == "semi-flex":
if days_before >= 3:
refund = total
charged = 0.0
rule = "Semi-flex: cancelled >= 72h before check-in, full refund."
else:
charged = 0.5 * total
refund = total - charged
rule = "Semi-flex: late cancel, 50% charged."
else:
return "Unsupported rate plan. Use: flexible, semi-flex, non-refundable."
return (
f"Rule: {rule}\n"
f"Days before check-in: {days_before}\n"
f"Total: ${total:.2f}\n"
f"Charged: ${charged:.2f}\n"
f"Refund: ${refund:.2f}"
)
Шаг 3: Создание цикла ReAct в LangGraph (Размышление → Инструмент → Размышление)
Схемы инструментов должны быть строго ограничены. Широкие параметры в формате свободного текста способствуют внедрению угроз и затрудняют аудит.
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
from langgraph.prebuilt import ToolNode, tools_condition
# 1) Define state
class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
booking_id: str
# 2) Define structured Output schema
class RefundDecision(BaseModel):
booking_id: str
rate_plan: str
total_amount: float
charged_amount: float
refund_amount: float
policy_summary: str
explanation: str
# 2) Choose model and System prompt
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
SYSTEM_PROMPT = SystemMessage(
content="""
You are a hotel cancellation assistant.
Rules:
- Use tools when needed.
- Never guess policy or refund.
- Final answer MUST be valid JSON with this schema:
{
"booking_id": "...",
"rate_plan": "...",
"total_amount": number,
"charged_amount": number,
"refund_amount": number,
"policy_summary": "...",
"explanation": "..."
}
"""
)
# 3) Register tools
tools = [get_cancellation_policy, calculate_refund]
def safe_tool_node(state):
last_msg = state["messages"][-1]
if not hasattr(last_msg, "tool_calls") or not last_msg.tool_calls:
return {}
tool_call = last_msg.tool_calls[0]
tool_name = tool_call["name"]
if tool_name not in ALLOWED_TOOLS:
return {
"messages": [
ToolMessage(
content=f"Tool '{tool_name}' is not allowed.",
tool_call_id=tool_call["id"]
)
]
}
for tool in tools:
if tool.name == tool_name:
result = tool.invoke(tool_call["args"])
return {
"messages": [
ToolMessage(
content=result,
tool_call_id=tool_call["id"]
)
]
}
# 4)Before reasoning, check if we already processed this booking.
REFUND_MEMORY = {}
def memory_lookup_node(state):
booking_id = state["booking_id"]
if booking_id in REFUND_MEMORY:
return {
"messages": [
HumanMessage(
content=f"Cached decision found:\n{REFUND_MEMORY[booking_id]}"
)
]
}
return {}
# 5) Reasoning node: LLM decides next action (tool call) or final answer
def agent_node(state: AgentState):
# Bind tools so the model can produce tool calls
llm_with_tools = llm.bind_tools(tools)
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
# 6) After final decision, store it.
def memory_write_node(state):
booking_id = state["booking_id"]
final_answer = state["messages"][-1].content
REFUND_MEMORY[booking_id] = final_answer
return {}
Создание и компиляция графа
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
# 7) Build the graph
builder = StateGraph(AgentState)
# Nodes
builder.add_node("memory_lookup", memory_lookup_node)
builder.add_node("agent", agent_node)
builder.add_node("tools", safe_tool_node)
builder.add_node("memory_write", memory_write_node)
# Flow
builder.add_edge(START, "memory_lookup")
builder.add_edge("memory_lookup", "agent")
builder.add_conditional_edges(
"agent",
tools_condition,
{
"tools": "tools", # model wants to act
END: "memory_write" # model finished reasoning
}
)
builder.add_edge("tools", "agent")
builder.add_edge("memory_write", END)
graph = builder.compile()
Шаг 4: Запуск агента в конкретном случае использования
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
query = """
Booking details:
Rate plan: Non-Refundable
Check-in: 2026-01-20
Nights: 2
Nightly rate: 120
Cancelled on: 2026-01-18
"""
result = graph.invoke({
"booking_id": "BKG-12345",
"messages": [
SYSTEM_PROMPT,
HumanMessage(content=query)
]
})
final_output = result["messages"][-1].content
print(final_output)
Результат
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
{
"booking_id": "BKG-12345",
"rate_plan": "Non-Refundable",
"total_amount": 240.0,
"charged_amount": 240.0,
"refund_amount": 0.0,
"policy_summary": "Non-refundable bookings do not allow refunds after confirmation.",
"explanation": "The booking was made under a non-refundable rate plan, which charges the full stay amount regardless of cancellation timing."
}
Что происходит внутри (поведение ReAct)
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
1) Мышление (Размышление)
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
2) Действие (запрос к инструменту)
Шаблоны инструментов должны быть строго ограничены. Широкие параметры в виде свободного текста способствуют внедрению угроз и делают аудиты дорогостоящими.
3) Наблюдение (результаты работы инструментов)
Пункт контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
4) Итоговый ответ
Пункт контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
Почему это «ReAct» (а не просто инструменты)
Пункт контроля после дорогостоящих вызовов модели, чтобы повторная попытка не влекла за собой повторной оплаты той же работы.
Заключение
Разделяйте планирование и выполнение с помощью инструментов. Планировщик предлагает варианты; исполнитель вносит изменения; проверщик сравнивает результаты с поставленной целью.
Чек-лист для работы
Строго ограничивайте схемы инструментов. Широкие параметры в виде свободного текста способствуют внедрению злоумышленнического кода и усложняют аудит.
Условные связи должны кодировать бизнес-правила в виде именованных функций, а не быть скрытыми в тексте запроса.
Размещайте типы рядом с компонентами и ограничивайте количество свойств. Большое количество свойств приводит к проблемам, которые TypeScript предназначен предотвращать.
Напишите краткое руководство: как обновлять ключи, как опустошать очередь, как откатывать последние изменения.