Перейти к содержимому
ПереводыQwen, InternLM и другие

Qwen-Agent, часть 1: путеводитель по фреймворку и его документации

Как устроен Qwen-Agent — фреймворк агентов от команды Qwen: карта кода и документации, перевод ключевых разделов (агенты, инструменты, управление контекстом, RAG, MCP) и как читать его код как пример агентного фреймворка.

Qwen-Agent — фреймворк для приложений на основе LLM, построенный вокруг возможностей Qwen: следование инструкциям, вызов инструментов, планирование и память. По словам авторов, он работает бэкендом чат-сервиса Qwen Chat.

Для курса у него две ценности:

  1. Готовый инструмент. Агент с вызовом функций, MCP, интерпретатором кода и RAG по своим PDF — за 20 строк кода, поверх вашего локального vLLM.
  2. Учебный образец. Код компактный и читаемый. Главный цикл агента — около 40 строк в qwen_agent/agents/fncall_agent.py; это ровно тот цикл, что вы писали руками в уроке 8.3.1. Документация Qwen называет Qwen-Agent «эталонной реализацией» вызова функций для Qwen3 (см. наш пересказ).
Папка / файлЧто тамУрок курса
qwen_agent/agent.pyбазовый класс Agent: run(), _call_llm(), _call_tool()8.3.1
qwen_agent/agents/готовые агенты: Assistant, FnCallAgent, ReActChat, GroupChat, Router, TIRMathAgent, агенты для документов8.3.1, 8.3.3
qwen_agent/llm/клиенты моделей: DashScope, OpenAI-совместимый (oai.py), transformers, OpenVINO8.1.1
qwen_agent/llm/fncall_prompts/шаблоны вызова функций: nous (по умолчанию, для Qwen3) и старый qwen8.3.2
qwen_agent/tools/инструменты: code_interpreter, web_search, doc_parser, retrieval, image_gen, mcp_manager и др.8.3.2
qwen_agent/memory/память агента: работа с файлами и поиском по ним8.2.1
qwen_agent/gui/веб-интерфейс на Gradio 5: WebUI(agent).run()—
qwen_agent/settings.pyконстанты, например MAX_LLM_CALL_PER_RUN = 20—
examples/≈35 примеров: от function_calling.py до мультиагентных шахматвсе уроки 8.3
qwen-agent-docs/исходники сайта документации (руководство и бенчмарк DeepPlanning)—
РазделО чёмВажность
Overviewпример агента с MCP-серверами времени и загрузки страниц + веб-интерфейспрочитать
Get Started → Featuresсписок возможностей (переведён ниже кратко)прочитать
Get Started → Install, Quickstart, Configurationустановка, первые шаги, все параметры llm_cfg и инструментовсправочник
Core Modules → Agentагенты и как писать своиглавное
Core Modules → LLM, Schemaинтерфейс моделей, формат сообщений Message, ContentItemсправочник
Core Modules → Toolинструменты и как писать своиглавное
Core Modules → MCPподключение MCP-серверовважно
Core Modules → RAG, Contextпоиск по документам и обрезка контекставажно
Benchmarks → DeepPlanningбенчмарк агентного планирования (январь 2026)по желанию
  • Единый интерфейс агентов: базовый класс Agent и готовые реализации (Assistant, FnCallAgent).
  • Вызов инструментов: параллельный, многошаговый и многоходовый, с автоматическим разбором и выполнением.
  • RAG по документам длиной от миллиона токенов: гибрид поиска и агентного разбиения вопроса.
  • Встроенные инструменты: code_interpreter (выполнение Python), web_search и web_extractor, image_search, image_zoom_in_tool (увеличить часть картинки по рамке).
  • MCP: подключение внешних серверов (GitHub, файловая система, SQLite…).
  • Свои инструменты через декоратор @register_tool и класс BaseTool.
  • Модели: Qwen3, Qwen3-VL, Qwen3-Omni, Qwen3-Coder, QwQ, Qwen2.5 — через облако DashScope или локальные OpenAI-совместимые серверы (vLLM, SGLang).
  • Свой парсер вызовов: если сервер не умеет разбирать вызовы функций, Qwen-Agent сделает это сам; умеет и пользоваться парсером сервера.
  • Управление контекстом: длинная история автоматически ужимается под лимит модели.
  • Веб-интерфейс одной строкой, стриминг ответа по токенам, модульность: модель, инструменты, память и стратегию планирования можно менять независимо.

Класс Agent — интерфейс верхнего уровня в Qwen-Agent: объект агента объединяет вызовы инструментов и модели. Агент получает список сообщений и возвращает генератор, выдающий списки сообщений — то есть поток выходных сообщений.

У разных классов агентов разные сценарии работы. В папке agents есть несколько базовых подклассов. Например, ArticleAgent возвращает сообщение со статьёй, BasicDocQA — с результатами ответа на вопрос по документу. У таких агентов довольно жёсткий шаблон ответа, и они подходят для узких задач.

Универсальный агент — класс Assistant; созданный напрямую, он справляется с большинством задач одного агента. Он умеет:

  • играть роль (по системному сообщению);
  • сам планировать и вызывать инструменты;
  • RAG: принимает документы и разбирает их встроенной стратегией поиска.

В примере ниже мы создаём агента, указав модель, список инструментов и инструкцию роли, и общаемся с ним. По ответам видно, что на просьбу «милый котик» агент, следуя инструкции, сам спланировал и выполнил нужные инструменты: нарисовал кота, скачал картинку и отразил её.

import os
from qwen_agent.agents import Assistant
llm_cfg = {'model': 'qwen-max'} # модель из облака DashScope (нужен ключ DASHSCOPE_API_KEY)
tools = ['image_gen', 'amap_weather'] # встроенные инструменты Qwen-Agent
system = ('По запросу пользователя сначала нарисуй картинку, затем сам запусти код, чтобы её скачать, '
'и выбери из данного документа операцию для обработки изображения')
bot = Assistant(llm=llm_cfg,
system_message=system,
function_list=tools,
files=[os.path.abspath('doc.pdf')])
messages = [{'role': 'user', 'content': 'a cute cat'}]
for response in bot.run(messages=messages):
print('bot response:', response)

Результат в оригинале — список сообщений: вызов my_image_gen → ответ функции с адресом картинки → текст ассистента → вызов code_interpreter с кодом на PIL, который скачивает и отражает картинку → «Finished execution.» → итоговый ответ. Обратите внимание на роли: вызов — сообщение assistant с полем function_call, результат — сообщение с ролью function.

В папке examples есть и другие сценарии одного агента на основе Assistant.

Есть и универсальный мультиагентный класс GroupChat. Он управляет списком агентов и сам определяет очерёдность их реплик:

  • получив внешний ввод, сам координирует, кто из агентов говорит, и по очереди возвращает их ответы;
  • человек в контуре (человек в контуречеловек в контуре (подтверждение человеком)EN human-in-the-loop): пользователь тоже описан как агент, и группа может при необходимости спросить его мнение;
  • пользователь может прервать групповой чат в любой момент.

В examples есть Gradio-демо group_chat_demo.py для создания групповых чатов и пример игры в гомоку group_chat_chess.py.

Класс агента — это сценарий обработки сообщений, поэтому свои агенты пишутся гибко. Из базового класса Agent видно: в подклассе достаточно реализовать одну функцию

def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]: ...

Она принимает список сообщений и возвращает итератор по спискам сообщений. Внутри можно вызывать модель и инструменты функциями _call_llm(...) и _call_tool(...), а также вкладывать других агентов — например, вызвать Assistant.run(...), чтобы воспользоваться его планированием.

Пример оригинала — агент «рассказ по картинке» (VisualStorytelling): получает адрес изображения и пишет сочинение. Внутри два вложенных агента: image_agent на мультимодальной модели Qwen-VL описывает картинку, writing_agent пишет текст по описанию, опираясь на базу знаний. Сценарий в _run: дописать к последнему сообщению просьбу «подробно опиши все детали изображения» → прогнать через image_agent, отдавая его ответы наружу по мере появления → добавить сообщение «начинай писать сочинение по содержанию картинки» → прогнать через writing_agent.

Документация оговаривает: это лишь один способ; можно было обойтись одним агентом. Преимущество вложенности в том, что у каждого агента свои промпт, инструменты и модель — и на каждом этапе можно добиться лучшего результата.

Второй пример — минимальный агент DocQA: склеивает переданные справочные материалы во встроенный промпт, кладёт его в системное сообщение и вызывает модель:

import copy
from typing import Iterator, List
from qwen_agent import Agent
from qwen_agent.llm.schema import CONTENT, ROLE, SYSTEM, Message
PROMPT = """
Внимательно изучи справочные материалы ниже и дай чёткий, хорошо организованный ответ на вопрос пользователя.
# Справочные материалы:
{ref_doc}
"""
class DocQA(Agent):
def _run(self, messages: List[Message], knowledge: str = '', lang: str = 'en', **kwargs) -> Iterator[List[Message]]:
messages = copy.deepcopy(messages) # не портим историю вызывающего
system_prompt = PROMPT.format(ref_doc=knowledge)
if messages and messages[0][ROLE] == SYSTEM: # системное сообщение уже есть — дописываем
messages[0][CONTENT] += system_prompt
else: # нет — вставляем первым
messages.insert(0, Message(SYSTEM, system_prompt))
return self._call_llm(messages=messages) # генератор ответов модели

(В оригинале промпт задан в двух вариантах — на китайском и английском — и выбирается по lang.) Как сочетать _call_llm и _call_tool, смотрите в реализациях ReActChat и Assistant.

Как устроен главный цикл (наш разбор кода)

Заголовок раздела «Как устроен главный цикл (наш разбор кода)»

Вот что делает FnCallAgent._run — родитель Assistant:

flowchart TD
A["messages"] --> B["_call_llm(messages, functions)"]
B --> C{"есть function_call?"}
C -- да --> D["_call_tool(name, args)"]
D --> E["добавить сообщение role=function"]
E --> F{"вызовов модели меньше 20?"}
F -- да --> B
C -- нет --> G["вернуть ответ"]
F -- нет --> G
FnCallAgent: модель → инструменты → модель… пока модель не ответит без вызова (не больше MAX_LLM_CALL_PER_RUN = 20 раз)

Три детали, которые стоит заметить в коде: лимит MAX_LLM_CALL_PER_RUN (по умолчанию 20, задаётся переменной окружения QWEN_AGENT_MAX_LLM_CALL_PER_RUN) защищает от бесконечного цикла; каждый промежуточный результат сразу отдаётся наружу через yield — отсюда стриминг; _call_tool переводит любой результат инструмента в строку, потому что модель читает только текст.

Инструменты (перевод раздела Tool Introduction, сокращённо)

Заголовок раздела «Инструменты (перевод раздела Tool Introduction, сокращённо)»

Напрямую. У всех инструментов единый интерфейс .call(params): передаёте параметры — получаете результат типа str, list или dict.

from qwen_agent.tools import ImageGen
tool = ImageGen()
res = tool.call(params={'prompt': 'a cute cat'})
print(res)

Из агента. Агент вызывает инструменты функцией _call_tool(...); каждый экземпляр агента может вызывать только те инструменты, которые ему передали при создании через параметр function_list. Передать можно имя инструмента, его конфигурацию или сам объект: 'code_interpreter', {'name': 'code_interpreter', 'timeout': 10} или CodeInterpreter(). Чтобы результат было удобно подать модели, _call_tool(...) приводит любой результат к строке.

В Qwen-Agent есть механизм регистрации инструментов. Чтобы зарегистрировать свой:

  • задайте имя, описание и параметры. Строка из @register_tool('my_image_gen') автоматически становится атрибутом .name класса и служит уникальным идентификатором инструмента;
  • реализуйте функцию call(...).
import json
import urllib.parse
import json5
from qwen_agent.tools.base import BaseTool, register_tool
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
# описание читает модель — от него зависит, когда она решит вызвать инструмент
description = 'Сервис ИИ-рисования: по текстовому описанию возвращает адрес нарисованной картинки.'
parameters = { # JSON Schema аргументов
'type': 'object',
'properties': {
'prompt': {'type': 'string',
'description': 'Подробное описание желаемой картинки на английском языке'},
},
'required': ['prompt'],
}
def call(self, params: str, **kwargs) -> str:
prompt = json5.loads(params)['prompt'] # params — строка с JSON от модели; json5 прощает мелкие ошибки
prompt = urllib.parse.quote(prompt)
return json.dumps({'image_url': f'https://image.pollinations.ai/prompt/{prompt}'}, ensure_ascii=False)

После регистрации инструментом пользуются, как описано выше. Можно и не регистрировать: объявить класс с атрибутом name и передать агенту объект. Но тогда нельзя передавать инструмент по имени или конфигурации.

Управление контекстом (перевод раздела Context Management)

Заголовок раздела «Управление контекстом (перевод раздела Context Management)»

Логика управления контекстом в Qwen-Agent динамически обрезает входные сообщения, сохраняя разумную структуру диалога, чтобы общее число токенов не превышало максимальную длину контекста модели.

  • Механизм срабатывает автоматически при вызове agent.run(...) или llm.call(...).
  • Контекст меняется только тогда, когда его длина достигает max_input_tokens.

Порядок действий:

  • Ш1. Начиная с самого старого хода диалога: если даже после удаления хода целиком контекст всё ещё длиннее лимита — удалить этот ход целиком. Иначе перейти к Ш2 и обрабатывать отдельные ходы.
  • Ш2. Начиная с самого старого ответа инструмента (кроме последнего шага), «сворачивать» (сжимать или пересказывать) ответы инструментов. Если длина стала допустимой — остановиться, иначе к Ш3.
  • Ш3. Начиная с самого старого шага вызова инструмента, удалять шаг целиком (кроме вопроса пользователя и итогового ответа). Если длина в пределах — остановиться, иначе к Ш4.
  • Ш4. Обработать последний шаг, постепенно сворачивая его ответ инструмента. Если хватило — остановиться, иначе к Ш5.
  • Ш5. Обрезать вопрос пользователя или итоговый ответ этого хода.

Стратегия в первую очередь выбрасывает старые воспоминания и сведения об окружении, постепенно уменьшая контекст, чтобы агент мог работать как бы с «бесконечным контекстом». Авторы признают, что запоминание при этом далеко от идеала, и обещают более продвинутый модуль памяти. Сравните с подходами к памяти агента в уроке 8.3.3.

Встроенный RAG ищет нужные фрагменты в переданных документах, чтобы модель опиралась в ответе на них.

  1. Разбор документов. Форматы: .pdf, .docx, .pptx, .txt, .csv, .tsv, .xlsx, .xls, .html. Документ режется на фрагменты по parser_page_size токенов (по умолчанию 500); разбором занимается инструмент DocParser.
  2. Поиск по ключевым словам BM25BM25EN BM25 (Okapi BM25)Функция ранжирования по ключевым словам — TF-IDF с насыщением по частоте (k1) и нормировкой на длину документа (b). (библиотека rank_bm25): запрос пользователя (или сгенерированные ключевые слова) сравнивается с фрагментами. Стратегии задаются в rag_searchers (keyword_search, front_page_search или их сочетания). Найденное обрезается до max_ref_token (по умолчанию 20 000 токенов).
  3. Генерация ключевых слов (по желанию). Модель разбивает вопрос на части и выдаёт ключевые слова на китайском и английском (стратегия по умолчанию SplitQueryThenGenKeyword). Без модели ищут по исходному вопросу.

В агенте Assistant RAG включается сам, если в сообщении есть файл:

from qwen_agent.agents import Assistant
from qwen_agent.llm.schema import Message, ContentItem
agent = Assistant(llm=llm_cfg)
for rsp in agent.run(messages=[Message(role='user', content=[
ContentItem(text='Какой срок гарантии на товар?'),
ContentItem(file='manual.pdf')])]):
pass
print(rsp[-1]['content'])

Нужны дополнительные пакеты: pip install "qwen-agent[rag]". Итог документации: лёгкий, но действенный поиск на фрагментах и BM25, без векторной базы. Почему авторы обходятся без эмбеддингов и как это сравнить с векторным поиском — тема урока 8.2.1.

Раздел MCP пошагово описывает: установить qwen-agent[mcp]; поставить Node.js, uv 0.4.18+, Git и (для примера) SQLite; описать серверы блоком mcpServers (как в Claude Desktop — команда и аргументы запуска каждого сервера); передать этот словарь агенту прямо в function_list рядом с обычными инструментами. Пример конфигурации — в README. Что такое MCP — урок 8.3.2.

ПримерЧто показывает
examples/function_calling.pyвызов функций на уровне модели, без агента
examples/function_calling_in_parallel.pyпараллельные вызовы
examples/assistant_qwen3.pyагент на Qwen3 с MCP и встроенными инструментами
examples/assistant_mcp_sqlite_bot.pyагент с SQLite через MCP
examples/assistant_rag.py, parallel_doc_qa.pyвопросы по очень длинным документам (быстрый и «дорогой» варианты)
examples/tir_math.pyрешение задач с вызовом Python (Tool-Integrated Reasoning)
examples/group_chat_demo.py, multi_agent_router.pyмультиагентность
  1. Поднимите vllm serve Qwen/Qwen3-8B и запустите пример Assistant с инструментом code_interpreter (нужен Docker): попросите «построй график y = x² от −3 до 3».
  2. Напишите свой инструмент @register_tool('currency') и проверьте, что агент вызывает его сам.
  3. Прочитайте qwen_agent/agents/fncall_agent.py и сравните со своим циклом из урока 8.3.1: что у них общего, чего не хватает вашему?
  4. Задайте маленький max_input_tokens (например, 2000) и посмотрите, как агент ужимает длинную историю.
EnglishРусский
agentагент
tool / functionинструмент / функция
multi-agent group chatгрупповой чат агентов
context managementуправление контекстом
code interpreterинтерпретатор кода
keyword retrieval (BM25)поиск по ключевым словам
human-in-the-loopчеловек в контуре