Tiny Universe, часть 4: TinyAgent — минимальный агент ReAct
Полный перевод раздела TinyAgent из tiny-universe: модель InternLM2, описание инструмента «поиск Google», системная подсказка в формате ReAct, разбор вызова инструмента и пример диалога — плюс пояснения, чем этот учебный агент отличается от настоящего цикла агента.
TinyAgent
Заголовок раздела «TinyAgent»Эфир «Агент своими руками»: записи эфира пока нет — найдите в видеоканале Datawhale (WeChat) ролик «Практический разбор „большая модель с нуля“. Tiny-Agent: строим минимальную систему агента».
После того как ChatGPT появился как гром среди ясного неба и отобрал корону у Bert, большие модели становятся всё
популярнее; в Китае одна модель появляется за другой — это время назвали «войной ста моделей». Способности
больших моделей бесспорны, но в вопросах о текущих событиях или в узких профессиональных областях им может не
хватать сил. Поэтому моделям нужны инструменты, которые дадут им «рычаг» и позволят сверять детали с тем, что
происходит в реальном мире, — так получается модель, которой удобнее пользоваться.
Здесь, следуя подходу ReAct, мы вручную сделали минимальную структуру Agent (на деле это скорее вызов
инструментов). Летом попробуем переделать структуру ReAct в структуру SOP.
Если писать Agent шаг за шагом своими руками, лучше понимаешь, из чего он состоит и как работает. Ниже — несколько
небольших примеров из статьи о ReAct.
Статья: ReAct: Synergizing Reasoning and Acting in Language Models
Рисунок в оригинале (images/React.png): примеры из статьи ReAct — модель чередует «мысли» (Thought), «действия» (Act: поиск в Википедии и т. п.) и «наблюдения» (Obs) и приходит к верному ответу там, где только рассуждения или только действия ошибаются.
Детали реализации
Заголовок раздела «Детали реализации»Шаг 1. Готовим большую модель
Заголовок раздела «Шаг 1. Готовим большую модель»Сначала нужна большая модель; в качестве модели агента возьмём InternLM2. InternLM2 — универсальная диалоговая
модель архитектуры Decoder-Only, её можно загрузить библиотекой transformers.
Как и раньше, начнём с класса BaseModel и определим в нём базовые методы, например chat и load_model, — чтобы
потом легко подключать другие модели.
class BaseModel: def __init__(self, path: str = '') -> None: self.path = path
def chat(self, prompt: str, history: List[dict]): pass
def load_model(self): passЗатем создаём класс InternLM2, наследник BaseModel, и реализуем в нём методы chat и load_model. Загружаем
InternLM2 так же, как обычно, и просто возвращаем ответ.
class InternLM2Chat(BaseModel): def __init__(self, path: str = '') -> None: super().__init__(path) self.load_model()
def load_model(self): print('================ Loading model ================') self.tokenizer = AutoTokenizer.from_pretrained(self.path, trust_remote_code=True) self.model = AutoModelForCausalLM.from_pretrained(self.path, torch_dtype=torch.float16, trust_remote_code=True).cuda().eval() print('================ Model loaded ================')
def chat(self, prompt: str, history: List[dict], meta_instruction:str ='') -> str: response, history = self.model.chat(self.tokenizer, prompt, history, temperature=0.1, meta_instruction=meta_instruction) return response, historyШаг 2. Готовим инструменты
Заголовок раздела «Шаг 2. Готовим инструменты»В файле tools.py создадим инструменты, например «поиск Google». В этом файле делаем класс Tools и добавляем в
него описание каждого инструмента и его реализацию.
Описание инструмента нужно, чтобы при сборке system_prompt модель знала, какие инструменты можно вызвать, что они
делают и какие у них параметры.
- Сначала добавляем в
toolsописание инструмента. - Затем добавляем в
toolsего реализацию.
Для поиска Google нужно получить
tokenна сайтеserper: https://serper.dev/dashboard, а потом вписать ключ в файл tools.py. Каждый может бесплатно получить один ключ с 2500 бесплатными вызовами — для экспериментов хватит.
class Tools: def __init__(self) -> None: self.toolConfig = self._tools()
def _tools(self): tools = [ { 'name_for_human': 'Поиск Google', 'name_for_model': 'google_search', 'description_for_model': 'Поиск Google — универсальная поисковая система: доступ в интернет, справочные знания, новости.', 'parameters': [ { 'name': 'search_query', 'description': 'ключевое слово или фраза для поиска', 'required': True, 'schema': {'type': 'string'}, } ], } ] return tools
def google_search(self, search_query: str): passШаг 3. Собираем агента
Заголовок раздела «Шаг 3. Собираем агента»В файле Agent.py создаём класс Agent — агента в парадигме ReAct. В нём реализован метод text_completion —
метод диалога: он вызывает модель InternLM2 и по логике агента ReAct вызывает инструменты из Tools.
Сначала собираем system_prompt — системную подсказку. В неё можно добавить системные указания, например подсказку
в формате ReAct.
def build_system_input(self): tool_descs, tool_names = [], [] for tool in self.tool.toolConfig: tool_descs.append(TOOL_DESC.format(**tool)) tool_names.append(tool['name_for_model']) tool_descs = '\n\n'.join(tool_descs) tool_names = ','.join(tool_names) sys_prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names) return sys_promptЕсли всё прошло гладко, результат будет таким:
Answer the following questions as best you can. You have access to the following tools:
google_search: Call this tool to interact with the Поиск Google API. What is the Поиск Google API useful for? Поиск Google — универсальная поисковая система: доступ в интернет, справочные знания, новости. Parameters: [{'name': 'search_query', 'description': 'ключевое слово или фраза для поиска', 'required': True, 'schema': {'type': 'string'}}] Format the arguments as a JSON object.
Use the following format:
Question: the input question you must answerThought: you should always think about what to doAction: the action to take, should be one of [google_search]Action Input: the input to the actionObservation: the result of the action... (this Thought/Action/Action Input/Observation can be repeated zero or more times)Thought: I now know the final answerFinal Answer: the final answer to the original input question
Begin!Эта system_prompt сообщает модели, какие инструменты она может вызывать, в каком формате отвечать, что делает
каждый инструмент и какие параметры он принимает.
Пока реализован только простой инструмент «поиск Google»; потом добавятся инструменты для анализа в геоинформационных системах — да, я студент-геоинформатик.
Полная структура агента — в tinyAgent/Agent.py. Скажем коротко: структура Agent — это структура ReAct:
system_prompt сообщает модели, какие инструменты она может вызвать и в каком формате отвечать.
На каждый вопрос пользователя, если нужен инструмент, модель вызывается два раза: первый раз — чтобы разобрать вопрос
и выбрать инструмент и параметры, второй — чтобы объединить результат инструмента с вопросом пользователя. Так и
получается структура ReAct.
Ниже — упрощённый код Agent; полная реализация каждой функции — в tinyAgent/Agent.py.
class Agent: def __init__(self, path: str = '') -> None: pass
def build_system_input(self): # собрать системную подсказку, описанную выше pass
def parse_latest_plugin_call(self, text): # разобрать первый ответ модели: какой инструмент выбран и с какими параметрами pass
def call_plugin(self, plugin_name, plugin_args): # вызвать выбранный инструмент pass
def text_completion(self, text, history=[]): # объединить два вызова модели passРисунок в оригинале (images/Agent.png):
схема работы агента — ниже она перерисована по коду Agent.py.
flowchart TB U["Вопрос пользователя"] --> P1["Вызов 1: модель + system_prompt<br/>ответ: Thought / Action / Action Input"] P1 --> PARSE["parse_latest_plugin_call:<br/>ищем последние Action и Action Input"] PARSE -->|"инструмент выбран"| TOOL["call_plugin → google_search(...)<br/>добавляем Observation"] PARSE -->|"инструмента нет"| P2 TOOL --> P2["Вызов 2: модель продолжает текст<br/>с Observation"] P2 --> A["Final Answer"]
Шаг 4. Запускаем агента
Заголовок раздела «Шаг 4. Запускаем агента»В этом примере использована модель InternLM2-chat-7B. Если хотите, чтобы Agent работал стабильнее, возьмите её
«большую чашку» — InternLM2-20b-chat: это повышает стабильность агента.
from Agent import Agent
agent = Agent('/root/share/model_repos/internlm2-chat-20b')
response, _ = agent.text_completion(text='Привет', history=[])print(response)
# Thought: Здравствуйте, чем могу помочь?# Action: google_search# Action Input: {'search_query': 'Привет'}# Observation: (сниппет первого результата поиска — страница англо-китайского онлайн-словаря с примерами перевода)# Final Answer: Здравствуйте, чем могу помочь?
response, _ = agent.text_completion(text='В каком году родился Джей Чоу?', history=_)print(response)
# Final Answer: Джей Чоу родился в 1979 году.
response, _ = agent.text_completion(text='Кто такой Джей Чоу?', history=_)print(response)
# Thought: По результатам поиска, Джей Чоу — тайваньский певец-автор, пианист и автор песен. Его первый альбом# «Джей» вышел в 2000 году, его музыку слушают в Азиатско-Тихоокеанском регионе и на Западе.# Final Answer: Джей Чоу — тайваньский певец-автор, пианист, автор песен и музыкальный продюсер. В 2000 году# он выпустил первый альбом «Джей», его музыку слушают в Азиатско-Тихоокеанском регионе и на Западе. У него# своеобразный стиль, в котором смешаны поп, рок, хип-хоп, электроника и другие жанры, и множество поклонников# по всему миру. Самые известные его песни — «Аромат риса», «Бело-синий фарфор», «Слушай маму» и другие.
response, _ = agent.text_completion(text='Какой у него первый альбом?', history=_)print(response)
# Final Answer: Первый альбом Джея Чоу — «Jay».Не забудьте поставить репозиторию маленькую звёздочку!
Литература
Заголовок раздела «Литература»От переводчика: как запустить на своей машине
Заголовок раздела «От переводчика: как запустить на своей машине»Что сделать руками:
- Добавьте второй инструмент —
calculator(expression: str)— в_tools()и вcall_plugin. Спросите агента «Сколько будет 17,5% от 2340?» и проверьте, что он выбрал калькулятор. - Перепишите
text_completionв цикл: до 5 шагов, выход поFinal Answer. Задайте вопрос, требующий двух поисков подряд («Сколько лет было автору песни „Аромат риса“, когда она вышла?»). - Сравните с версией агента из Happy-LLM (раздел 7.3, вызов функций через API): что в ней делает модель, а что — библиотека?
Словарик
Заголовок раздела «Словарик»| English | Русский | Пояснение |
|---|---|---|
| agent | агент | LLM + инструменты + цикл решений |
| tool / plugin | инструмент, плагин | функция, которую может вызвать модель |
| system prompt | системная подсказка | инструкции модели «сверху» диалога |
| ReAct (Reason + Act) | ReAct, «рассуждай и действуй» | чередование Thought / Action / Observation |
| Observation | наблюдение | результат вызова инструмента |
| Final Answer | окончательный ответ | сигнал остановки цикла |
| SOP (standard operating procedure) | стандартная процедура | агент по заранее заданному сценарию |
| war of a hundred models | «война ста моделей» | бум китайских LLM 2023 года |