[User] Provide the documentation for 'browser_qwen.md' in the Qwen-Agent framework.

[Assistant]
# Example Application: BrowserQwen

We have also developed an example application based on Qwen-Agent: a **Chrome browser extension** called BrowserQwen,
which has key features such as:

- You can discuss with Qwen regarding the current webpage or PDF document.
- It records the web pages and PDF/Word/PowerPoint materials that you have browsed. It helps you understand multiple
  pages, summarize your browsing content, and automate writing tasks.
- It comes with plugin integration, including **Code Interpreter** for math problem solving and data visualization.

## BrowserQwen Demonstration

You can watch the following showcase videos to learn about the basic operations of BrowserQwen:

- Long-form writing based on visited webpages and
  PDFs. [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_write_article_based_on_webpages_and_pdfs.mp4)
- Drawing a plot using code interpreter based on the given
  information. [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_chat_with_docs_and_code_interpreter.mp4)
- Uploading files, multi-turn conversation, and data analysis using code
  interpreter. [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_code_interpreter_multi_turn_chat.mp4)

### Workstation - Editor Mode

**This mode is designed for creating long articles based on browsed web pages and PDFs.**

**It allows you to call plugins to assist in rich text creation.**

### Workstation - Chat Mode

**In this mode, you can engage in multi-webpage QA.**

**Create data charts using the code interpreter.**

### Browser Assistant

**Web page QA**

**PDF document QA**

## BrowserQwen User Guide

### Step 1. Deploy Local Database Service

On your local machine (the machine where you can open the Chrome browser), you will need to deploy a database service to
manage your browsing history and conversation history.

If you are using DashScope's model service, then please execute the following command:

```bash
# Start the database service, specifying the model on DashScope by using the --llm flag.
# The value of --llm can be one of the following, in increasing order of resource consumption:
#   - qwen1.5-7b/14b/72b-chat (the same as the open-sourced Qwen1.5 7B/14B/72B Chat model)
#   - qwen-turbo, qwen-plus, qwen-max (qwen-max is recommended)
# "YOUR_DASHSCOPE_API_KEY" is a placeholder. The user should replace it with their actual key.
python run_server.py --llm qwen-max --model_server dashscope --workstation_port 7864 --api_key YOUR_DASHSCOPE_API_KEY
```

If you are using your own model service instead of DashScope, then please execute the following command:

```bash
# Specify the model service, and start the database service.
# Example: Assuming Qwen1.5-72B-Chat is deployed at http://localhost:8000/v1 using vLLM, you can specify the model service as:
#   --llm Qwen1.5-72B-Chat --model_server http://localhost:8000/v1 --api_key EMPTY
python run_server.py --llm {MODEL} --model_server {API_BASE} --workstation_port 7864 --api_key {API_KEY}
```

Now you can access [http://127.0.0.1:7864/](http://127.0.0.1:7864/) to use the Workstation's Editor mode and Chat mode.

### Step 2. Install Browser Assistant

Install the BrowserQwen Chrome extension:

- Open the Chrome browser and enter `chrome://extensions/` in the address bar, then press Enter.
- Make sure that the `Developer mode` in the top right corner is turned on, then click on `Load unpacked` to upload
  the `browser_qwen` directory from this project and enable it.
- Click the extension icon in the top right corner of the Chrome browser to pin BrowserQwen to the toolbar.

Note that after installing the Chrome extension, you need to refresh the page for the extension to take effect.

When you want Qwen to read the content of the current webpage:

- Click the `Add to Qwen's Reading List` button on the screen to authorize Qwen to analyze the page in the background.
- Click the Qwen icon in the browser's top right corner to start interacting with Qwen about the current page's content.


[User] Provide the documentation for 'browser_qwen_cn.md' in the Qwen-Agent framework.

[Assistant]
# 示例应用：BrowserQwen

我们在Qwen-Agent的基础上开发了一个较为复杂的Agent应用，名为BrowserQwen的**Chrome浏览器扩展**，它具有以下主要功能：

- 与Qwen讨论当前网页或PDF文档的内容。
- BrowserQwen会记录您浏览过的网页和PDF/Word/PPT材料，帮助您了解多个页面的内容、总结浏览过的内容、自动化繁琐的文字工作。
- 集成各种插件，包括可用于数学问题求解、数据分析与可视化、处理文件等的**代码解释器**（**Code Interpreter**）。

## BrowserQwen 功能演示

可查看以下几个演示视频，了解BrowserQwen的核心功能和基本操作：

- 根据浏览过的网页、PDFs进行长文创作 [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_write_article_based_on_webpages_and_pdfs.mp4)
- 提取浏览内容使用代码解释器画图 [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_chat_with_docs_and_code_interpreter.mp4)
- 上传文件、多轮对话利用代码解释器分析数据 [video](https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/showcase_code_interpreter_multi_turn_chat.mp4)

### 工作台 - 创作模式

**根据浏览过的网页、PDFs素材进行长文创作**

**调用插件辅助富文本创作**

### 工作台 - 对话模式

**多网页问答**

**使用代码解释器绘制数据图表**

### 浏览器助手

**网页问答**

**PDF文档问答**

## BrowserQwen 使用说明

### 第一步 - 部署本地数据库服务

在这一步，您需要在您的本地机器上（即您可以打开Chrome浏览器的那台机器），部署维护个人浏览历史、对话历史的数据库服务。

如果您使用DashScope提供的模型服务的话，请执行以下命令启动数据库服务：

```bash
# 启动数据库服务，通过 --llm 参数指定您希望通过DashScope使用的具体模型
# 参数 --llm 可以是如下之一，按资源消耗从小到大排序：
#   - qwen1.5-7b/14b/72b-chat （与开源的Qwen1.5-7B/14B/72B-Chat相同模型）
#   - qwen-turbo, qwen-plus, qwen-max （推荐使用qwen-max）
# 您需要将YOUR_DASHSCOPE_API_KEY替换为您的真实API-KEY。
python run_server.py --llm qwen-max --model_server dashscope --workstation_port 7864 --api_key YOUR_DASHSCOPE_API_KEY
```

如果您没有在使用DashScope、而是部署了自己的模型服务的话，请执行以下命令：

```bash
# 指定模型服务，并启动数据库服务。
# 示例: 假设Qwen1.5-72B-Chat已经通过vLLM部署于http://localhost:8000/v1，则可用以下参数指定模型服务：
#   --llm Qwen1.5-72B-Chat --model_server http://localhost:8000/v1 --api_key EMPTY
python run_server.py --llm {MODEL} --model_server {API_BASE} --workstation_port 7864 --api_key {API_KEY}
```

现在您可以访问 [http://127.0.0.1:7864/](http://127.0.0.1:7864/) 来使用工作台（Workstation）的创作模式（Editor模式）和对话模式（Chat模式）了。

### 第二步 - 安装浏览器助手

安装BrowserQwen的Chrome插件（又称Chrome扩展程序）：

1. 打开Chrome浏览器，在浏览器的地址栏中输入 `chrome://extensions/` 并按下回车键；
2. 确保右上角的 `开发者模式` 处于打开状态，之后点击 `加载已解压的扩展程序` 上传本项目下的 `browser_qwen` 目录并启用；
3. 单击谷歌浏览器右上角```扩展程序```图标，将BrowserQwen固定在工具栏。

注意，安装Chrome插件后，需要刷新页面，插件才能生效。

当您想让Qwen阅读当前网页的内容时：

1. 请先点击屏幕上的 `Add to Qwen's Reading List` 按钮，以授权Qwen在后台分析本页面。
2. 再单击浏览器右上角扩展程序栏的Qwen图标，便可以和Qwen交流当前页面的内容了。


[User] What is Qwen-Agent and how do I use it? Provide the official README.

[Assistant]
[中文](https://github.com/QwenLM/Qwen-Agent/blob/main/README_CN.md) ｜ English

          💜 Qwen Chat&nbsp&nbsp | &nbsp&nbsp🤗 Hugging Face&nbsp&nbsp | &nbsp&nbsp🤖 ModelScope&nbsp&nbsp | &nbsp&nbsp 📑 Blog &nbsp&nbsp ｜ &nbsp&nbsp📖 Documentation

💬 WeChat (微信)&nbsp&nbsp | &nbsp&nbsp🫨 Discord&nbsp&nbsp

Qwen-Agent is a framework for developing LLM applications based on the instruction following, tool usage, planning, and
memory capabilities of Qwen.
It also comes with example applications such as Browser Assistant, Code Interpreter, and Custom Assistant.
Now Qwen-Agent plays as the backend of [Qwen Chat](https://chat.qwen.ai/).

# News
* 🔥🔥🔥 Sep 23, 2025: Added [Qwen3-VL Tool-call Demo](./examples/cookbook_think_with_images.ipynb), supporting tools such as zoom in, image search, and web search.
* Jul 23, 2025: Add [Qwen3-Coder Tool-call Demo](./examples/assistant_qwen3_coder.py); Added native API tool call interface support, such as using vLLM's built-in tool call parsing.
* May 1, 2025: Add [Qwen3 Tool-call Demo](./examples/assistant_qwen3.py), and add [MCP Cookbooks](./examples/).
* Mar 18, 2025: Support for the `reasoning_content` field; adjust the default [Function Call template](./qwen_agent/llm/fncall_prompts/nous_fncall_prompt.py), which is applicable to the Qwen2.5 series general models and QwQ-32B. If you need to use the old version of the template, please refer to the [example](./examples/function_calling.py) for passing parameters.
* Mar 7, 2025: Added [QwQ-32B Tool-call Demo](./examples/assistant_qwq.py). It supports parallel, multi-step, and multi-turn tool calls.
* Dec 3, 2024: Upgrade GUI to Gradio 5 based. Note: GUI requires Python 3.10 or higher.
* Sep 18, 2024: Added [Qwen2.5-Math Demo](./examples/tir_math.py) to showcase the Tool-Integrated Reasoning capabilities of Qwen2.5-Math. Note: The python executor is not sandboxed and is intended for local testing only, not for production use.

# Getting Started

## Installation

- Install the stable version from PyPI:
```bash
pip install -U "qwen-agent[gui,rag,code_interpreter,mcp]"
# Or use `pip install -U qwen-agent` for the minimal requirements.
# The optional requirements, specified in double brackets, are:
#   [gui] for Gradio-based GUI support;
#   [rag] for RAG support;
#   [code_interpreter] for Code Interpreter support;
#   [mcp] for MCP support.
```

- Alternatively, you can install the latest development version from the source:
```bash
git clone https://github.com/QwenLM/Qwen-Agent.git
cd Qwen-Agent
pip install -e ./"[gui,rag,code_interpreter,mcp]"
# Or `pip install -e ./` for minimal requirements.
```

## Preparation: Model Service

You can either use the model service provided by Alibaba
Cloud's [DashScope](https://help.aliyun.com/zh/dashscope/developer-reference/quick-start), or deploy and use your own
model service using the open-source Qwen models.

- If you choose to use the model service offered by DashScope, please ensure that you set the environment
variable `DASHSCOPE_API_KEY` to your unique DashScope API key.

- Alternatively, if you prefer to deploy and use your own model service, please follow the instructions provided in the README of Qwen2 for deploying an OpenAI-compatible API service.
Specifically, consult the [vLLM](https://github.com/QwenLM/Qwen2?tab=readme-ov-file#vllm) section for high-throughput GPU deployment or the [Ollama](https://github.com/QwenLM/Qwen2?tab=readme-ov-file#ollama) section for local CPU (+GPU) deployment.
For the QwQ and Qwen3 model, it is recommended to **do not** add the `--enable-auto-tool-choice` and `--tool-call-parser hermes` parameters, as Qwen-Agent will parse the tool outputs from vLLM on its own.
For Qwen3-Coder, it is recommended to enable both of the above parameters, use vLLM's built-in tool parsing, and combine with the `use_raw_api` parameter [usage](#how-to-pass-llm-parameters-to-the-agent).

## Developing Your Own Agent

Qwen-Agent offers atomic components, such as LLMs (which inherit from `class BaseChatModel` and come with [function calling](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py)) and Tools (which inherit
from `class BaseTool`), along with high-level components like Agents (derived from `class Agent`).

The following example illustrates the process of creating an agent capable of reading PDF files and utilizing tools, as
well as incorporating a custom tool:

```py
import pprint
import urllib.parse
import json5
from qwen_agent.agents import Assistant
from qwen_agent.tools.base import BaseTool, register_tool
from qwen_agent.utils.output_beautify import typewriter_print

# Step 1 (Optional): Add a custom tool named `my_image_gen`.
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    # The `description` tells the agent the functionality of this tool.
    description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.'
    # The `parameters` tell the agent what input parameters the tool has.
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description': 'Detailed description of the desired image content, in English',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        # `params` are the arguments generated by the LLM agent.
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json5.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)

# Step 2: Configure the LLM you are using.
llm_cfg = {
    # Use the model service provided by DashScope:
    'model': 'qwen-max-latest',
    'model_type': 'qwen_dashscope',
    # 'api_key': 'YOUR_DASHSCOPE_API_KEY',
    # It will use the `DASHSCOPE_API_KEY' environment variable if 'api_key' is not set here.

    # Use a model service compatible with the OpenAI API, such as vLLM or Ollama:
    # 'model': 'Qwen2.5-7B-Instruct',
    # 'model_server': 'http://localhost:8000/v1',  # base_url, also known as api_base
    # 'api_key': 'EMPTY',

    # (Optional) LLM hyperparameters for generation:
    'generate_cfg': {
        'top_p': 0.8
    }
}

# Step 3: Create an agent. Here we use the `Assistant` agent as an example, which is capable of using tools and reading files.
system_instruction = '''After receiving the user's request, you should:
- first draw an image and obtain the image url,
- then run code `request.get(image_url)` to download the image,
- and finally select an image operation from the given document to process the image.
Please show the image using `plt.show()`.'''
tools = ['my_image_gen', 'code_interpreter']  # `code_interpreter` is a built-in tool for executing code.
files = ['./examples/resource/doc.pdf']  # Give the bot a PDF file to read.
bot = Assistant(llm=llm_cfg,
                system_message=system_instruction,
                function_list=tools,
                files=files)

# Step 4: Run the agent as a chatbot.
messages = []  # This stores the chat history.
while True:
    # For example, enter the query "draw a dog and rotate it 90 degrees".
    query = input('\nuser query: ')
    # Append the user query to the chat history.
    messages.append({'role': 'user', 'content': query})
    response = []
    response_plain_text = ''
    print('bot response:')
    for response in bot.run(messages=messages):
        # Streaming output.
        response_plain_text = typewriter_print(response, response_plain_text)
    # Append the bot responses to the chat history.
    messages.extend(response)
```

In addition to using built-in agent implementations such as `class Assistant`, you can also develop your own agent implemetation by inheriting from `class Agent`.

The framework also provides a convenient GUI interface, supporting the rapid deployment of Gradio Demos for Agents.
For example, in the case above, you can quickly launch a Gradio Demo using the following code:

```py
from qwen_agent.gui import WebUI
WebUI(bot).run()  # bot is the agent defined in the above code, we do not repeat the definition here for saving space.
```
Now you can chat with the Agent in the web UI. Please refer to the [examples](https://github.com/QwenLM/Qwen-Agent/blob/main/examples) directory for more usage examples.

# FAQ

## How to Use MCP?

You can select the required tools on the open-source [MCP server website](https://github.com/modelcontextprotocol/servers) and configure the relevant environment.

Example of MCP invocation format:
```
{
    "mcpServers": {
        "memory": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-memory"]
        },
        "filesystem": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
        },
        "sqlite" : {
            "command": "uvx",
            "args": [
                "mcp-server-sqlite",
                "--db-path",
                "test.db"
            ]
        }
    }
}
```
For more details, you can refer to the [MCP usage example](./examples/assistant_mcp_sqlite_bot.py)

The dependencies required to run this example are as follows:
```
# Node.js (Download and install the latest version from the Node.js official website)
# uv 0.4.18 or higher (Check with uv --version)
# Git (Check with git --version)
# SQLite (Check with sqlite3 --version)

# For macOS users, you can install these components using Homebrew:
brew install uv git sqlite3

# For Windows users, you can install these components using winget:
winget install --id=astral-sh.uv -e
winget install git.git sqlite.sqlite
```
## Do you have function calling (aka tool calling)?

Yes. The LLM classes provide [function calling](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py). Additionally, some Agent classes also are built upon the function calling capability, e.g., FnCallAgent and ReActChat.

The current default tool calling template natively supports **Parallel Function Calls**.

## How to pass LLM parameters to the Agent?
```py
llm_cfg = {
    # The model name being used:
    'model': 'qwen3-32b',
    # The model service being used:
    'model_type': 'qwen_dashscope',
    # If 'api_key' is not set here, it will default to reading the `DASHSCOPE_API_KEY` environment variable:
    'api_key': 'YOUR_DASHSCOPE_API_KEY',

    # Using an OpenAI API compatible model service, such as vLLM or Ollama:
    # 'model': 'qwen3-32b',
    # 'model_server': 'http://localhost:8000/v1',  # base_url, also known as api_base
    # 'api_key': 'EMPTY',

    # (Optional) LLM hyperparameters:
    'generate_cfg': {
        # This parameter will affect the tool-call parsing logic. Default is False:
          # Set to True: when content is `<think>this is the thought</think>this is the answer`
          # Set to False: when response consists of reasoning_content and content
        # 'thought_in_content': True,

        # tool-call template: default is nous (recommended for qwen3):
        # 'fncall_prompt_type': 'nous'

        # Maximum input length, messages will be truncated if they exceed this length, please adjust according to model API:
        # 'max_input_tokens': 58000

        # Parameters that will be passed directly to the model API, such as top_p, enable_thinking, etc., according to the API specifications:
        # 'top_p': 0.8

        # Using the API's native tool call interface
        # 'use_raw_api': True,
    }
}
```

## How to do question-answering over super-long documents involving 1M tokens?

We have released [a fast RAG solution](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/assistant_rag.py), as well as [an expensive but competitive agent](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/parallel_doc_qa.py), for doing question-answering over super-long documents. They have managed to outperform native long-context models on two challenging benchmarks while being more efficient, and perform perfectly in the single-needle "needle-in-the-haystack" pressure test involving 1M-token contexts. See the [blog](https://qwenlm.github.io/blog/qwen-agent-2405/) for technical details.

# Application: BrowserQwen

BrowserQwen is a browser assistant built upon Qwen-Agent. Please refer to its [documentation](https://github.com/QwenLM/Qwen-Agent/blob/main/browser_qwen.md) for details.

# Disclaimer

The code interpreter is not sandboxed, and it executes code in your own environment. Please do not ask Qwen to perform dangerous tasks, and do not directly use the code interpreter for production purposes.


[User] What is Qwen-Agent and how do I use it? Provide the official README.

[Assistant]
中文 ｜ [English](./README.md)

          💜 Qwen Chat&nbsp&nbsp | &nbsp&nbsp🤗 Hugging Face&nbsp&nbsp | &nbsp&nbsp🤖 ModelScope&nbsp&nbsp | &nbsp&nbsp 📑 Blog &nbsp&nbsp ｜ &nbsp&nbsp📖 Documentation

💬 WeChat (微信)&nbsp&nbsp | &nbsp&nbsp🫨 Discord&nbsp&nbsp

Qwen-Agent是一个开发框架。开发者可基于本框架开发Agent应用，充分利用基于通义千问模型（Qwen）的指令遵循、工具使用、规划、记忆能力。本项目也提供了浏览器助手、代码解释器、自定义助手等示例应用。
现在，Qwen-Agent 作为 [Qwen Chat](https://chat.qwen.ai/) 的后端运行。

# 更新
* 🔥🔥🔥Sep 23, 2025: 新增 [Qwen3-VL Tool-call Demo](./examples/cookbook_think_with_images.ipynb)，支持使用抠图、图搜、文搜等工具。
* Jul 23, 2025: 新增 [Qwen3-Coder Tool-call Demo](./examples/assistant_qwen3_coder.py)；新增原生API工具调用接口支持，例如可使用vLLM自带的工具调用解析。
* May 1, 2025: 新增 [Qwen3 Tool-call Demo](./examples/assistant_qwen3.py)；新增 [MCP cookbooks](./examples/)。
* Mar 18, 2025: 支持`reasoning_content`字段；调整默认的[Function Call模版](./qwen_agent/llm/fncall_prompts/nous_fncall_prompt.py)（适用于Qwen2.5系列通用模型、QwQ-32B）。如果需要使用旧版模版：请参考[样例](./examples/function_calling.py)传递参数。
* Mar 7, 2025: 新增[QwQ-32B Tool-call Demo](./examples/assistant_qwq.py)，支持并行、多步、多轮工具调用。
* Dec 3, 2024: GUI 升级为基于 Gradio 5。注意：如果需要使用GUI，Python版本需要3.10及以上。
* Sep 18, 2024: 新增[Qwen2.5-Math Demo](./examples/tir_math.py)以展示Qwen2.5-Math基于工具的推理能力。注意：代码执行工具未进行沙箱保护，仅适用于本地测试，不可用于生产。

# 开始上手

## 安装

- 从 PyPI 安装稳定版本：
```bash
pip install -U "qwen-agent[rag,code_interpreter,gui,mcp]"
# 或者，使用 `pip install -U qwen-agent` 来安装最小依赖。
# 可使用双括号指定如下的可选依赖：
#   [gui] 用于提供基于 Gradio 的 GUI 支持；
#   [rag] 用于支持 RAG；
#   [code_interpreter] 用于提供代码解释器相关支持；
#   [mcp] 用于支持 MCP。
```

- 或者，你可以从源码安装最新的开发版本：
```bash
git clone https://github.com/QwenLM/Qwen-Agent.git
cd Qwen-Agent
pip install -e ./"[gui,rag,code_interpreter,mcp]"
# 或者，使用 `pip install -e ./` 安装最小依赖。
```

## 准备：模型服务

Qwen-Agent支持接入阿里云[DashScope](https://help.aliyun.com/zh/dashscope/developer-reference/quick-start)服务提供的Qwen模型服务，也支持通过OpenAI API方式接入开源的Qwen模型服务。

- 如果希望接入DashScope提供的模型服务，只需配置相应的环境变量`DASHSCOPE_API_KEY`为您的DashScope API Key。

- 或者，如果您希望部署并使用您自己的模型服务，请按照Qwen2的README中提供的指导进行操作，以部署一个兼容OpenAI接口协议的API服务。
具体来说，请参阅[vLLM](https://github.com/QwenLM/Qwen2?tab=readme-ov-file#vllm)一节了解高并发的GPU部署方式，或者查看[Ollama](https://github.com/QwenLM/Qwen2?tab=readme-ov-file#ollama)一节了解本地CPU（+GPU）部署。

注意对于QwQ和Qwen3模型，建议启动服务时**不加**`--enable-auto-tool-choice`和`--tool-call-parser hermes`两个参数，因为Qwen-Agent会自行解析vLLM的工具输出。
对于Qwen3-Coder，则建议开启以上两个参数，使用vLLM自带的工具解析，并搭配`use_raw_api`参数[使用](#如何传递llm参数给agent)。

## 快速开发

框架提供了大模型（LLM，继承自`class BaseChatModel`，并提供了[Function Calling](./examples/function_calling.py)功能）和工具（Tool，继承自`class BaseTool`）等原子组件，也提供了智能体（Agent）等高级抽象组件（继承自`class Agent`）。

以下示例演示了如何增加自定义工具，并快速开发一个带有设定、知识库和工具使用能力的智能体：

```py
import pprint
import urllib.parse
import json5
from qwen_agent.agents import Assistant
from qwen_agent.tools.base import BaseTool, register_tool
from qwen_agent.utils.output_beautify import typewriter_print

# 步骤 1（可选）：添加一个名为 `my_image_gen` 的自定义工具。
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    # `description` 用于告诉智能体该工具的功能。
    description = 'AI 绘画（图像生成）服务，输入文本描述，返回基于文本信息绘制的图像 URL。'
    # `parameters` 告诉智能体该工具有哪些输入参数。
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description': '期望的图像内容的详细描述',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        # `params` 是由 LLM 智能体生成的参数。
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json5.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)

# 步骤 2：配置您所使用的 LLM。
llm_cfg = {
    # 使用 DashScope 提供的模型服务：
    'model': 'qwen-max-latest',
    'model_type': 'qwen_dashscope',
    # 'api_key': 'YOUR_DASHSCOPE_API_KEY',
    # 如果这里没有设置 'api_key'，它将读取 `DASHSCOPE_API_KEY` 环境变量。

    # 使用与 OpenAI API 兼容的模型服务，例如 vLLM 或 Ollama：
    # 'model': 'Qwen2.5-7B-Instruct',
    # 'model_server': 'http://localhost:8000/v1',  # base_url，也称为 api_base
    # 'api_key': 'EMPTY',

    # （可选） LLM 的超参数：
    'generate_cfg': {
        'top_p': 0.8
    }
}

# 步骤 3：创建一个智能体。这里我们以 `Assistant` 智能体为例，它能够使用工具并读取文件。
system_instruction = '''在收到用户的请求后，你应该：
- 首先绘制一幅图像，得到图像的url，
- 然后运行代码`request.get`以下载该图像的url，
- 最后从给定的文档中选择一个图像操作进行图像处理。
用 `plt.show()` 展示图像。
你总是用中文回复用户。'''
tools = ['my_image_gen', 'code_interpreter']  # `code_interpreter` 是框架自带的工具，用于执行代码。
files = ['./examples/resource/doc.pdf']  # 给智能体一个 PDF 文件阅读。
bot = Assistant(llm=llm_cfg,
                system_message=system_instruction,
                function_list=tools,
                files=files)

# 步骤 4：作为聊天机器人运行智能体。
messages = []  # 这里储存聊天历史。
while True:
    # 例如，输入请求 "绘制一只狗并将其旋转 90 度"。
    query = input('\n用户请求: ')
    # 将用户请求添加到聊天历史。
    messages.append({'role': 'user', 'content': query})
    response = []
    response_plain_text = ''
    print('机器人回应:')
    for response in bot.run(messages=messages):
        # 流式输出。
        response_plain_text = typewriter_print(response, response_plain_text)
    # 将机器人的回应添加到聊天历史。
    messages.extend(response)
```

除了使用框架自带的智能体实现（如`class Assistant`），您也可以通过继承`class Agent`来自行开发您的智能体实现。

框架还提供了便捷的GUI接口，支持为Agent快速部署Gradio Demo。
例如上面的例子中，可以使用以下代码快速启动Gradio Demo：

```py
from qwen_agent.gui import WebUI
WebUI(bot).run()  # bot is the agent defined in the above code, we do not repeat the definition here for saving space.
```

现在您可以在Web UI中和Agent对话了。更多使用示例，请参阅[examples](./examples)目录。

# FAQ
## 如何使用MCP？
可以在开源的[MCP Sever网站](https://github.com/modelcontextprotocol/servers)上选择需要的工具，并配置相关环境。

Qwen-Agent中MCP调用格式：
```
{
    "mcpServers": {
        "memory": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-memory"]
        },
        "filesystem": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
        },
        "sqlite" : {
            "command": "uvx",
            "args": [
                "mcp-server-sqlite",
                "--db-path",
                "test.db"
            ]
        }
    }
}
```
具体可参考[MCP使用例子](./examples/assistant_mcp_sqlite_bot.py)

运行该例子需要额外安装的依赖有：
```
# Node.js（访问 Node.js 官网下载并安装最新版本, https://nodejs.org/）
# uv 0.4.18 或更高版本 (使用 uv --version 检查)
# Git (git --version 检查)
# SQLite (sqlite3 --version 检查)

# 对于 macOS 用户，可以使用 Homebrew 安装这些组件：
brew install uv git sqlite3

# 对于 Windows 用户，可以使用 winget 安装这些组件：
winget install --id=astral-sh.uv -e
winget install git.git sqlite.sqlite
```

## 支持函数调用（也称为工具调用）吗？

支持，LLM类提供了[函数调用](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py)的支持。此外，一些Agent类如FnCallAgent和ReActChat也是基于函数调用功能构建的。

目前的默认工具调用模版原生支持 **并行工具调用**（Parallel Function call）。

## 如何传递LLM参数给Agent？
```py
llm_cfg = {
    # 使用的模型名：
    'model': 'qwen3-32b',
    # 使用的模型服务：
    'model_type': 'qwen_dashscope',
    # 如果这里没有设置 'api_key'，它将默认读取 `DASHSCOPE_API_KEY` 环境变量：
    'api_key': 'YOUR_DASHSCOPE_API_KEY',

    # 使用与 OpenAI API 兼容的模型服务，例如 vLLM 或 Ollama：
    # 'model': 'qwen3-32b',
    # 'model_server': 'http://localhost:8000/v1',  # base_url，也称为 api_base
    # 'api_key': 'EMPTY',

    # （可选） LLM 的超参数：
    'generate_cfg': {
        # 这个参数将影响tool-call解析逻辑。默认为False：
          # 设置为True：当content为 `<think>this is the thought</think>this is the answer`
          # 设置为False: 当回复为 reasoning_content 和 content
        # 'thought_in_content': True,

        # tool-call template：默认为nous（qwen3 推荐）
        # 'fncall_prompt_type': 'nous'

        # 最大输入长度，超过该长度会对messages截断，请根据模型API调整
        # 'max_input_tokens': 58000

        # 将直接输入模型API的参数，例如top_p, enable_thinking等，根据API规范传入：
        # 'top_p': 0.8

        # Using the API's native tool call interface
        # 'use_raw_api': True,
    }
}
```

## 如何让AI基于超长文档进行问答？

我们已发布了一个[快速的RAG解决方案](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/assistant_rag.py)，以及一个虽运行成本较高但[准确度较高的智能体](https://github.com/QwenLM/Qwen-Agent/blob/main/examples/parallel_doc_qa.py)，用于在超长文档中进行问答。它们在两个具有挑战性的基准测试中表现出色，超越了原生的长上下文模型，同时更加高效，并在涉及100万字词上下文的“大海捞针”式单针查询压力测试中表现完美。欲了解技术细节，请参阅[博客](https://qwenlm.github.io/blog/qwen-agent-2405/)。

# 应用：BrowserQwen

BrowserQwen 是一款基于 Qwen-Agent 构建的浏览器助手。如需了解详情，请参阅其[文档](browser_qwen_cn.md)。

# 免责声明

代码解释器未进行沙盒隔离，会在部署环境中执行代码。请避免向Qwen发出危险指令，切勿将该代码解释器直接用于生产目的。


[User] What is Qwen-Agent and how do I use it? Provide the official README.

[Assistant]
# Code Interpreter Benchmark

## Introduction
To assess LLM's ability to use the Python Code Interpreter for tasks such as mathematical problem solving, data visualization, and other general-purpose tasks such as file handling and web scraping, we have created and open-sourced a benchmark specifically designed for evaluating these capabilities.

### Metrics
The metrics are divided into two parts: code executability and code correctness.
- Code executability: evaluating the ability of the LLM-generated code to be executed.
- Code correctness: evaluating whether the LLM-generated code runs correctly.

### Domain
When evaluating the accuracy of the code execution results for code correctness, we further divide it into two specific domains: `Math`, `Visualization`.
In terms of code executability, we calculate executable rate of the generated code for `General problem-solving`.

## Results
- Qwen-7B-Chat refers to the version updated after September 25, 2023.
- The code correctness judger model for `Visualization` has changed from `Qwen-vl-chat` to `gpt-4-vision-preview` in the version 20231206.

        In-house Code Interpreter Benchmark (Version 20231206)

        Model
        Accuracy of Code Execution Results (%)
        Executable Rate of Code (%)

        Math↑	Visualization-Hard↑	Visualization-Easy↑	General↑

        GPT-4
        82.8
        66.7
        60.8
        82.8

        GPT-3.5
        47.3
        33.3
        55.7
        74.1

        LLaMA2-13B-Chat
        8.3
        1.2
        15.2
        48.3

        CodeLLaMA-13B-Instruct
        28.2
        15.5
        21.5
        74.1

        InternLM-20B-Chat
        34.6
        10.7
        24.1
        65.5

        ChatGLM3-6B
        54.2
        4.8
        15.2
        62.1

        Qwen-1.8B-Chat
        25.6
        21.4
        22.8
        65.5

        Qwen-7B-Chat
        41.9
        23.8
        38.0
        67.2

        Qwen-14B-Chat
        58.4
        31.0
        45.6
        65.5

        Qwen-72B-Chat
        72.7
        41.7
        43.0
        82.8

Furthermore, we also provide the results of `Qwen-vl-plus` as the code correctness judger model for `Visualization` task to serve as a reference.

        Code Correctness Judger Model = Qwen-vl-plus

        Model
        Accuracy of Code Execution Results (%)

        Visualization-Hard↑
        Visualization-Easy↑

        LLaMA2-13B-Chat
        2.4
        17.7

        CodeLLaMA-13B-Instruct
        17.9
        34.2

        InternLM-20B-Chat
        9.5
        31.7

        ChatGLM3-6B
        10.7
        29.1

        Qwen-1.8B-Chat
        32.1
        32.9

        Qwen-7B-Chat
        26.2
        39.2

        Qwen-14B-Chat
        36.9
        41.8

        Qwen-72B-Chat
        38.1
        38.0

## Usage

### Installation

```shell
git clone https://github.com/QwenLM/Qwen-Agent.git
cd benchmark
pip install -r requirements.txt
```

### Dataset Download
```shell
cd benchmark
wget https://qianwen-res.oss-cn-beijing.aliyuncs.com/assets/qwen_agent/benchmark_code_interpreter_data.zip
unzip benchmark_code_interpreter_data.zip
mkdir eval_data
mv eval_code_interpreter_v1.jsonl eval_data/
```

### Evaluation
To reproduce the comprehensive results of benchmark, you can run the following script:

```Shell
python inference_and_execute.py --model {model_name}
```

{model_name}:
- qwen-1.8b-chat
- qwen-7b-chat
- qwen-14b-chat
- qwen-72b-chat
- llama-2-7b-chat
- llama-2-13b-chat
- codellama-7b-instruct
- codellama-13b-instruct
- internlm-7b-chat-1.1
- internlm-20b-chat

The benchmark will run the test cases and generate the performance results. The results will be saved in the `output_data` directory.

**Notes**:
Please install `simhei.ttf` font for proper display in matplotlib when evaluating visualization task. You can do this by preparing `simhei.ttf` (which can be found on any Windows PC) and then running the following code snippet:
```python
import os
import matplotlib
target_font_path = os.path.join(
    os.path.abspath(
        os.path.join(matplotlib.matplotlib_fname(), os.path.pardir)),
        'fonts', 'ttf', 'simhei.ttf')
os.system(f'cp simhei.ttf {target_font_path}')
font_list_cache = os.path.join(matplotlib.get_cachedir(), 'fontlist-*.json')
os.system(f'rm -f {font_list_cache}')
```

#### Code Executable Rate
```Shell
python inference_and_execute.py --task {task_name} --model {model_name}
```

{task_name}:
- `general`: General problem-solving task

#### Code Correctness Rate
```Shell
python inference_and_execute.py --task {task_name} --model {model_name}
```

{task_name}:
- `visualization`: Visualization task
- `gsm8k`: Math task

## Configuration
The inference_and_exec.py file contains the following configurable options:

- `--model`: The model to test which can be one of `qwen-72b-chat`, `qwen-14b-chat`, `qwen-7b-chat`, `qwen-1.8b-chat`, `qwen-7b-chat`, `llama-2-7b-chat`, `llama-2-13b-chat`, `codellama-7b-instruct`, `codellama-13b-instruct`, `internlm-7b-chat-1.1`, `internlm-20b-chat`.
- `--task`: The test task which can be one of `all`, `visualization`, `general`, `gsm8k`.
- `--output-path`: The path for saving evaluation result.
- `--input-path`: The path for placing evaluation data.
- `--output-fname`: The file name for evaluation result.
- `--input-fname`: The file name for evaluation data.
- `--force`: Force generation and will overwrite the cached results.
- `--eval-only`: Only calculate evaluation metrics without re-inference.
- `--eval-code-exec-only`: Only evaluate code executable rate
- `--gen-exec-only`: Only generate and execuate code without calculating evaluation metrics.
- `--gen-only`: Only generate without execuating code and calculating evaluation metrics.
- `--vis-judger`: The model to judge the result correctness for `Visualization` task which can be one of `gpt-4-vision-preview`, `qwen-vl-chat`, `qwen-vl-plus`. It is set to `gpt-4-vision-preview` by default in the version 20231206, and `Qwen-vl-chat` has been deprecated.


[User] Provide the documentation for 'docs\agent.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Agent Introduction

This document introduces the usage and development process of the Agent class.

## 1. Agent Usage
The Agent class serves as a higher-level interface for Qwen-Agent, where an Agent object integrates the interfaces for tool calls and LLM (Large Language Model).
The Agent receives a list of messages as input and produces a generator that yields a list of messages, effectively providing a stream of output messages.

Different Agent classes have various workflows. In the [agents](../qwen_agent/agents) directory, we provide several different fundamental Agent subclasses.
For instance, the [ArticleAgent](../qwen_agent/agents/article_agent.py) returns a message that includes an article;
the [BasicDocQA](../qwen_agent/agents/doc_qa/basic_doc_qa.py) returns a message that contains the results of a document Q&A Results.

These types of Agents have relatively fixed response patterns and are suited for fairly specific use cases.

### 1.1. Assistant Class
We offer a generic Agent class: the [Assistant](../qwen_agent/agents/assistant.py) class,
which, when directly instantiated, can handle the majority of Single-Agent tasks.
Features:
- It supports role-playing;
- It provides automatic planning and tool calls abilities;
- RAG (Retrieval-Augmented Generation): It accepts documents input, and can use an integrated RAG strategy to parse the documents.

For example, in the following scenario, we instantiate a assistant Agent by specifying the LLM, a list of tools, and the role instruction.
Then we can interact with the Agent.

From the responses, we can see that after the user requests 'a cute cat',
the Agent, in keeping with the role-play instruction, automatically plans and executes the necessary tools for drawing the cat, downloading it and flipping it.

```py
import os
from qwen_agent.agents import Assistant
llm_cfg = {'model': 'qwen-max'}
tools = ['image_gen', 'amap_weather']  # image_gen and code_interpreter is a built-in tool in Qwen-Agent
system = 'According to the user\'s request, you first draw a picture and then automatically run code to download the picture ' + \
          'and select an image operation from the given document to process the image'

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)

"""Running results：
[
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'my_image_gen', 'arguments': '{"prompt": "a cute cat"}'}},
    {'role': 'function', 'content': '{"image_url": "https://image.pollinations.ai/prompt/a%20cute%20cat"}', 'name': 'my_image_gen'},
    {'role': 'assistant', 'content': "Here is the image of a cute cat based on your description:\n\n![](https://image.pollinations.ai/prompt/a%20cute%20cat)\n\nNow, let's proceed with downloading this image and performing an operation such as flipping it."},
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'code_interpreter', 'arguments': '```py\nfrom PIL import Image\nimport requests\n\n# Downloading the cute cat image\nurl = "https://image.pollinations.ai/prompt/a%20cute%20cat"\nresponse = requests.get(url)\nwith open("cute_cat.jpg", \'wb\') as file:\n    file.write(response.content)\n\n# Flipping the image horizontally\ndef flip_image_horizontally(filename):\n    img = Image.open(filename)\n    flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)\n    flipped_img.save("flipped_cute_cat.jpg")\n    return flipped_img\n\n# Performing the horizontal flip\nflipped_cat = flip_image_horizontally("cute_cat.jpg")\n```'}},
    {'role': 'function', 'content': 'Finished execution.', 'name': 'code_interpreter'},
    {'role': 'assistant', 'content': 'The image of the cute cat has been downloaded and flipped horizontally. The flipped image has been saved as "flipped_cute_cat.jpg". Since we\'re in a text-based environment, I can\'t display the actual image here, but you can check it out at the location where the script was executed.'}
]
"""
```

In the [examples](../examples) directory,
we provide more Single-Agent use cases developed based on the Assistant class.

### 1.2. GroupChat Class
We also provide a generic Multi-Agent class: the [GroupChat](../qwen_agent/agents/group_chat.py) class. This class manages a list of Agents and automatically maintains their speech orders.
The features of this class include:
- Upon receiving external input, it automatically coordinates the speaking order of the built-in Agents and sequentially returns their responses to the user;
- Human-in-the-loop: The user is also defined as an Agent, and the group chat may request feedback from the user when necessary;
- The user can interrupt the group chat at any time.

In the [examples](../examples) directory, we provide a Gradio [Demo](../examples/group_chat_demo.py) for creating and experiencing group chats,
where you can further explore the group chat functionality.
For development using GroupChat, you can refer to the [Gomoku](../examples/group_chat_chess.py) use case.

## 2. Agent Development

As our Agent class is defined as a workflow for processing messages, we can flexibly develop specific Agent classes.
By examining the [Agent](../qwen_agent/agent.py) base class, it becomes apparent that when we develop an Agent subclass, we only need to implement the function
`._run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]`,
which receives a list of messages as input and returns an iterator over lists of messages.

During the development process, the functions `_call_llm(...)` and `_call_tool(...)` can be used to call LLMs or Tools.
It is also possible to nest other Agents, such as using `Assistant.run(...)` to directly utilize the Assistant's tool/LLM planning capabilities.

### 2.1. Nested Development
For example, in the scenario below, I want to create a custom Agent for visual story telling.
This Agent only needs to receive an image URL to automatically generate a composition:
In this Agent, I nest an image_agent that uses the Qwen-VL model to help me understand the content of the image,
and then I also nest a writing_agent that is responsible for learning writing techniques and helping me compose a piece of writing.

Note: This is just one way to implement an visual story telling Agent; other methods could achieve the same goal, such as using only an image_agent to complete both image comprehension and writing tasks.
However, the advantage of nesting multiple Agents to collaborate is that each Agent can use independent prompts, tools, and LLMs to achieve the best result at each stage.

```py
import copy
from typing import Dict, Iterator, List, Optional, Union

from qwen_agent import Agent
from qwen_agent.agents import Assistant
from qwen_agent.llm import BaseChatModel
from qwen_agent.llm.schema import ContentItem, Message
from qwen_agent.tools import BaseTool

class VisualStorytelling(Agent):
    """Customize an agent for writing story from pictures"""

    def __init__(self,
                 function_list: Optional[List[Union[str, Dict,
                                                    BaseTool]]] = None,
                 llm: Optional[Union[Dict, BaseChatModel]] = None):
        super().__init__(llm=llm)

        # Nest one vl assistant for image understanding
        self.image_agent = Assistant(llm={'model': 'qwen-vl-max'})

        # Nest one assistant for article writing
        self.writing_agent = Assistant(
            llm=self.llm,
            function_list=function_list,
            system_message='You are a student, first, you need to understand the content of the picture,' +
            'then you should refer to the knowledge base and write a narrative essay of 800 words based on the picture.',
            files=['https://www.jianshu.com/p/cdf82ff33ef8'])

    def _run(self,
             messages: List[Message],
             lang: str = 'zh',
             max_ref_token: int = 4000,
             **kwargs) -> Iterator[List[Message]]:
        """Define the workflow"""

        assert isinstance(messages[-1]['content'], list) and any([
            item.image for item in messages[-1]['content']
        ]), 'This agent requires input of images'

        # Image understanding
        new_messages = copy.deepcopy(messages)
        new_messages[-1]['content'].append(
            ContentItem(text='Please provide a detailed description of all the details of this image'))
        response = []
        for rsp in self.image_agent.run(new_messages):
            yield response + rsp
        response.extend(rsp)
        new_messages.extend(rsp)

        # Writing article
        new_messages.append(Message('user', 'Start writing your narrative essay based on the above image content!'))
        for rsp in self.writing_agent.run(new_messages,
                                          lang=lang,
                                          max_ref_token=max_ref_token,
                                          **kwargs):
            yield response + rsp
```

### 2.2. Non nested development

In this example, we utilize the fundamental `_call_llm(...)` function to invoke an LLM or Tool.
The workflow implemented by this DocQA involves concatenating the provided reference material into the built-in Prompt as a System Message,
and then calling the LLM to generate the return results.

```py
import copy
from typing import Iterator, List

from qwen_agent import Agent
from qwen_agent.llm.schema import CONTENT, ROLE, SYSTEM, Message

PROMPT_TEMPLATE_ZH = """
请充分理解以下参考资料内容，组织出满足用户提问的条理清晰的回复。
#参考资料：
{ref_doc}

"""

PROMPT_TEMPLATE_EN = """
Please fully understand the content of the following reference materials and organize a clear response that meets the user's questions.
# Reference materials:
{ref_doc}

"""

PROMPT_TEMPLATE = {
    'zh': PROMPT_TEMPLATE_ZH,
    'en': PROMPT_TEMPLATE_EN,
}

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_TEMPLATE[lang].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)

```

Examples of using the `_call_llm(...)` and `_call_tool(...)` functions in combination to call LLMs and Tools can be found by examining the implementation of [ReActChat](../qwen_agent/agents/react_chat.py) and [Assistant](../qwen_agent/agents/assistant.py).


[User] Provide the documentation for 'docs\agent_cn.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Agent 介绍

本文档介绍了Agent类的使用和开发流程。

## 1. Agent 使用
Agent类是Qwen-Agent的一个上层接口，一个Agent对象集成了工具调用和LLM调用接口。
Agent接收一个消息列表输入，并返回一个消息列表的生成器，即流式输出的消息列表。

不同Agent类具有不同的工作流程，我们在[agents](../qwen_agent/agents)目录提供了多个不同的基础的Agent子类，
例如[ArticleAgent](../qwen_agent/agents/article_agent.py)接收消息后，返回消息包含一篇文章；
[BasicDocQA](../qwen_agent/agents/doc_qa/basic_doc_qa.py)返回消息包含文档问答的结果。
可以看出，这类Agent回复模式相对固定，使用场景也比较固定。

### 1.1. Assistant 类
我们提供了一个通用的Agent类：[Assistant](../qwen_agent/agents/assistant.py) 类，直接实例化这个类，可以解决大部分Single-Agent任务。
这个类的特点是：
- 支持角色扮演；
- 可以自动规划并调用工具；
- 接受输入文档，并采用内置的RAG策略解析文档。

例如在下面的例子中，我们通过指定LLM、工具列表、和角色指令来实例化了一个图片生成Agent，
即可开始和这个Agent交互。
从回复内容可以看出，在用户询问“a cute cat”后，这个Agent按照角色扮演的需求，自动规划并执行了绘图和代码工具，并参考给定文档对图片进行了翻转。

```py
import os
from qwen_agent.agents import Assistant
llm_cfg = {'model': 'qwen-max'}
tools = ['image_gen', 'amap_weather']  # image_gen and code_interpreter is a built-in tool in Qwen-Agent
system = 'According to the user\'s request, you first draw a picture and then automatically run code to download the picture ' + \
          'and select an image operation from the given document to process the image'

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)

"""Running results：
[
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'my_image_gen', 'arguments': '{"prompt": "a cute cat"}'}},
    {'role': 'function', 'content': '{"image_url": "https://image.pollinations.ai/prompt/a%20cute%20cat"}', 'name': 'my_image_gen'},
    {'role': 'assistant', 'content': "Here is the image of a cute cat based on your description:\n\n![](https://image.pollinations.ai/prompt/a%20cute%20cat)\n\nNow, let's proceed with downloading this image and performing an operation such as flipping it."},
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'code_interpreter', 'arguments': '```py\nfrom PIL import Image\nimport requests\n\n# Downloading the cute cat image\nurl = "https://image.pollinations.ai/prompt/a%20cute%20cat"\nresponse = requests.get(url)\nwith open("cute_cat.jpg", \'wb\') as file:\n    file.write(response.content)\n\n# Flipping the image horizontally\ndef flip_image_horizontally(filename):\n    img = Image.open(filename)\n    flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)\n    flipped_img.save("flipped_cute_cat.jpg")\n    return flipped_img\n\n# Performing the horizontal flip\nflipped_cat = flip_image_horizontally("cute_cat.jpg")\n```'}},
    {'role': 'function', 'content': 'Finished execution.', 'name': 'code_interpreter'},
    {'role': 'assistant', 'content': 'The image of the cute cat has been downloaded and flipped horizontally. The flipped image has been saved as "flipped_cute_cat.jpg". Since we\'re in a text-based environment, I can\'t display the actual image here, but you can check it out at the location where the script was executed.'}
]
"""
```

我们在[examples](../examples)目录中提供了更多基于Assistant类开发的Single-Agent用例。

### 1.2. GroupChat 类

我们也提供了一个通用的Multi-Agent类：[GroupChat](../qwen_agent/agents/group_chat.py) 类。
这个类管理一个Agent列表，并自动维护Agent的发言顺序。这个类的特点是：
- 接收外部输入后，自动协调内置Agent的发言顺序，并依次将发言内容返回给用户；
- Human-in-the-loop: 将用户也定义为一个Agent，群聊会在必要时要求用户给予反馈；
- 用户可以随时打断群聊；

例如在下面的例子中，我们实例化一个五子棋群聊，其中真人用户下黑棋，并实例化白棋玩家和棋盘两个Agent作为NPC：
从结果可以看出，在用户小塘输入下棋位置<1,1>后，群聊先自动选择棋盘来更新；然后选择NPC小明发言；小明发言结束后棋盘再次更新；然后继续选择用户小塘发言，并等待用户输入。
然后用户小塘继续输入下棋位置<1,2>后，群聊又继续管理NPC发言......
```py
"""A chess play game implemented by group chat"""
from qwen_agent.agents import GroupChat
from qwen_agent.llm.schema import Message

# Define a configuration file for a multi-agent:
# one real player, one NPC player, and one chessboard
NPC_NAME = '小明'
USER_NAME = '小塘'
CFGS = {
    'background':
    f'一个五子棋群组，棋盘为5*5，黑棋玩家和白棋玩家交替下棋，每次玩家下棋后，棋盘进行更新并展示。{NPC_NAME}下白棋，{USER_NAME}下黑棋。',
    'agents': [{
        'name': '棋盘',
        'description': '负责更新棋盘',
        'instructions':
        '你扮演一个五子棋棋盘，你可以根据原始棋盘和玩家下棋的位置坐标，把新的棋盘用矩阵展示出来。棋盘中用0代表无棋子、用1表示黑棋、用-1表示白棋。用坐标<i,j>表示位置，i代表行，j代表列，棋盘左上角位置为<0,0>。',
        'selected_tools': ['code_interpreter']
    }, {
        'name':
        NPC_NAME,
        'description':
        '白棋玩家',
        'instructions':
        '你扮演一个玩五子棋的高手，你下白棋。棋盘中用0代表无棋子、用1黑棋、用-1白棋。用坐标<i,j>表示位置，i代表行，j代表列，棋盘左上角位置为<0,0>，请决定你要下在哪里，你可以随意下到一个位置，不要说你是AI助手不会下！返回格式为坐标：\n<i,j>\n除了这个坐标，不要返回其他任何内容',
    }, {
        'name': USER_NAME,
        'description': '黑棋玩家',
        'is_human': True
    }]
}

def app():
    # Define a group chat agent from the CFGS
    bot = GroupChat(agents=CFGS, llm={'model': 'qwen-max'})
    # Chat
    messages = []
    while True:
        query = input('user question: ')
        messages.append(Message('user', query, name=USER_NAME))
        response = []
        for response in bot.run(messages=messages):
            print('bot response:', response)
        messages.extend(response)

if __name__ == '__main__':
    app()

"""运行结果示例：
user question:  <1,1>
bot response:
[
    Message({'role': 'assistant', 'content': '根据小塘的指令，他在棋盘的坐标 <1,1> 下了一枚黑棋。当前5x5棋盘的状态更新如下：\n\n```\n0 0 0 0 0\n0 1 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n```\n\n其中 (1, 1) 位置标记为1，表示此处是小塘下的黑棋。接下来轮到小明下白棋了。', 'name': '棋盘'}),
    Message({'role': 'assistant', 'content': '<0,1>', 'name': '小明'}),
    Message({'role': 'assistant', 'content': '根据小明的指令，他在棋盘的坐标 <0,1> 下了一枚白棋。当前5x5棋盘的状态更新如下：\n\n```\n0 -1 0 0 0\n0 1 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n```\n\n其中 (0, 1) 位置标记为-1，表示此处是小明下的白棋。现在轮到小塘下黑棋了。', 'name': '棋盘'})
]
user question:  <1,2>
bot response:
[
    Message({'role': 'assistant', 'content': '根据小塘的指令，他在棋盘的坐标 <1,2> 下了一枚黑棋。当前5x5棋盘的状态更新如下：\n\n```\n0 -1 0 0 0\n0 1 1 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n```\n\n其中 (1, 2) 位置标记为1，表示此处是小塘下的黑棋。现在轮到小明下白棋了。', 'name': '棋盘'}),
    Message({'role': 'assistant', 'content': '<2,0>', 'name': '小明'}),
    Message({'role': 'assistant', 'content': '根据小明的指令，他在棋盘的坐标 <2,0> 下了一枚白棋。当前5x5棋盘的状态更新如下：\n\n```\n0 -1 0 0 0\n0 1 1 0 0\n-1 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n```\n\n其中 (2, 0) 位置标记为-1，表示此处是小明下的白棋。现在轮到小塘下黑棋了。', 'name': '棋盘'})
]
"""
```

我们在[examples](../examples)目录中提供了一个创造群聊和体验群聊的Gradio [Demo](../examples/group_chat_demo.py)，可以进一步体验群聊功能。
利用GroupChat开发可以参考[五子棋](../examples/group_chat_chess.py)用例。

## 2. Agent 开发

由于我们的一个Agent类定义为一种处理消息的工作流，因此，我们可以较灵活的开发特定的Agent类。
通过查看[Agent](../qwen_agent/agent.py)基类，可以看出，我们开发一个Agent子类时，只需要实现
`._run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]函数`,
它接收一个消息列表输入，并返回一个消息列表迭代器。

在开发过程中，可以使用`_call_llm(...)`和`_call_tool(...)`函数来调用LLM或Tool，也可以嵌套使用其他Agent，例如使用`Assistant.run(...)`来直接利用Assistant的Tool/LLM规划能力。

### 2.1. 嵌套开发
例如，在下面的例子中，我希望可以定制一个看图写作Agent，这个Agent只需要接收一个图片URL，就可以自动写出一篇作文：
在这个Agent中，我嵌套了一个image_agent，它使用Qwen-VL模型帮我理解图片内容，
然后也嵌套了一个writing_agent，它负责学习写作技巧并帮我写一篇作文。

注意：这只是看图写作Agent的其中一种实现方式，也可以使用其他方式来达成，
例如直接只使用一个image_agent来完成图片理解和写作。
但这种嵌套多个Agent合作的好处是，每个Agent可以使用独立的Prompt、工具和LLM，实现每个环节的最佳效果。

```py
import copy
from typing import Dict, Iterator, List, Optional, Union

from qwen_agent import Agent
from qwen_agent.agents import Assistant
from qwen_agent.llm import BaseChatModel
from qwen_agent.llm.schema import ContentItem, Message
from qwen_agent.tools import BaseTool

class VisualStorytelling(Agent):
    """Customize an agent for writing story from pictures"""

    def __init__(self,
                 function_list: Optional[List[Union[str, Dict,
                                                    BaseTool]]] = None,
                 llm: Optional[Union[Dict, BaseChatModel]] = None):
        super().__init__(llm=llm)

        # Nest one vl assistant for image understanding
        self.image_agent = Assistant(llm={'model': 'qwen-vl-max'})

        # Nest one assistant for article writing
        self.writing_agent = Assistant(
            llm=self.llm,
            function_list=function_list,
            system_message='你扮演一个想象力丰富的学生，你需要先理解图片内容，根据描述图片信息以后，' +
            '参考知识库中教你的写作技巧，发挥你的想象力，写一篇800字的记叙文',
            files=['https://www.jianshu.com/p/cdf82ff33ef8'])

    def _run(self,
             messages: List[Message],
             lang: str = 'zh',
             max_ref_token: int = 4000,
             **kwargs) -> Iterator[List[Message]]:
        """Define the workflow"""

        assert isinstance(messages[-1]['content'], list) and any([
            item.image for item in messages[-1]['content']
        ]), 'This agent requires input of images'

        # Image understanding
        new_messages = copy.deepcopy(messages)
        new_messages[-1]['content'].append(
            ContentItem(text='请详细描述这张图片的所有细节内容'))
        response = []
        for rsp in self.image_agent.run(new_messages):
            yield response + rsp
        response.extend(rsp)
        new_messages.extend(rsp)

        # Writing article
        new_messages.append(Message('user', '开始根据以上图片内容编写你的记叙文吧！'))
        for rsp in self.writing_agent.run(new_messages,
                                          lang=lang,
                                          max_ref_token=max_ref_token,
                                          **kwargs):
            yield response + rsp
```

### 2.2. 非嵌套开发
在这个例子中，我们使用基础的`_call_llm(...)`函数来调用LLM或Tool。

这个DocQA实现的一个工作流为：将给定的参考资料拼接到内置的Prompt中，并作为System Message，然后调用LLM生成返回结果。

```py
import copy
from typing import Iterator, List

from qwen_agent import Agent
from qwen_agent.llm.schema import CONTENT, ROLE, SYSTEM, Message

PROMPT_TEMPLATE_ZH = """
请充分理解以下参考资料内容，组织出满足用户提问的条理清晰的回复。
#参考资料：
{ref_doc}

"""

PROMPT_TEMPLATE_EN = """
Please fully understand the content of the following reference materials and organize a clear response that meets the user's questions.
# Reference materials:
{ref_doc}

"""

PROMPT_TEMPLATE = {
    'zh': PROMPT_TEMPLATE_ZH,
    'en': PROMPT_TEMPLATE_EN,
}

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_TEMPLATE[lang].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)

```

使用`_call_llm(...)`和`_call_tool(...)`函数组合来调用LLM和Tool的例子，可以参考[ReActChat](../qwen_agent/agents/react_chat.py)和[Assistant](../qwen_agent/agents/assistant.py)的实现。


[User] Provide the documentation for 'docs\llm.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# LLM Introduction

This document introduces the usage and development process of LLM classes.

## 1. LLM Usage

Currently, Qwen-Agent provides access interfaces to Qwen's DashScope API and OpenAI API, and Qwen-VL's DashScope API. Both already support streaming Function Calling.

### 1.1. Direct External Call
The LLM is uniformly calling using the interface `get_chat_model(cfg: Optional[Dict] = None) -> BaseChatModel`, with parameters passed in being the configuration file for the LLM. The configuration file format is as follows:
- model_type: Corresponds to a specific LLM class, which is the registered name of the LLM class, i.e., the unique ID. This parameter can be omitted when using the built-in DashScope and OpenAI API. For externally registered LLM classes, this parameter must be provided to specify the class.
- model: The specific model name
- model_server: The model service address
- generate_cfg: The parameters for model generation

LLM classes uniformly use the `llm.chat(...)` interface to generate responses, supporting input of message lists, functions, and other parameters. For a detailed parameter list, see [BaseChatModel](../qwen_agent/llm/base.py).

See a complete example of LLM usage at [Function Calling](../examples/function_calling.py).

```py

from qwen_agent.llm import get_chat_model

llm_cfg = {
            # Use the model service provided by DashScope:
            # 'model_type': 'qwen_dashscope',
            'model': 'qwen-max',
            'model_server': 'dashscope',
            # Use your own model service compatible with OpenAI API:
            # 'model': 'Qwen',
            # 'model_server': 'http://127.0.0.1:7905/v1',
            # (Optional) LLM hyper-parameters:
            'generate_cfg': {
                'top_p': 0.8
            }
          }
llm = get_chat_model(llm_cfg)
messages = [{
    'role': 'user',
    'content': "What's the weather like in San Francisco?"
}]
functions = [{
    'name': 'get_current_weather',
    'description': 'Get the current weather in a given location',
    'parameters': {
        'type': 'object',
        'properties': {
            'location': {
                'type': 'string',
                'description':
                'The city and state, e.g. San Francisco, CA',
            },
            'unit': {
                'type': 'string',
                'enum': ['celsius', 'fahrenheit']
            },
        },
        'required': ['location'],
    },
}]

# The streaming output responses
responses = []
for responses in llm.chat(messages=messages,
                          functions=functions,
                          stream=True):
    print(responses)
```

### 1.2. Internal call by Agent

In the Agent, the `_call_llm(...)` function is used to call the LLM, and each Agent instance can call the LLM that was initialized for it,`llm: Optional[Union[Dict, BaseChatModel]] = None`.
The supported types include: LLM configuration file or LLM object.

Note that in order to maintain the consistency of output type in the Agent,
the Agent’s `_call_llm(...)` interface by default accesses the LLM using a streaming generation method.

## 2. LLM Development
Qwen-Agent provides a mechanism for registering LLMs. In the [LLM Base Class](../qwen_agent/llm/base.py), a uniform `llm.chat(...)` interface is implemented.
Newly registered LLMs only need to implement three specific functions:
- A non-streaming generation interface;
- A streaming generation interface (If the LLM itself does not support streaming generation, the non-streaming results can be wrapped into a generator for return);
- A function call interface.

If the newly registered LLM does not support function calls, it can inherit from the [BaseFnCallModel](../qwen_agent/llm/function_calling.py) class implemented in Qwen-Agent.
This class has implemented Function Calling based on the general conversational interface by wrapping a tool call Prompt similar to ReAct.


[User] Provide the documentation for 'docs\llm_cn.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# LLM 介绍

本文档介绍了LLM类的使用和开发流程。

## 1. LLM 使用

目前，Qwen-Agent提供了Qwen的DashScope API和OpenAI API访问接口；Qwen-VL的DashScope API访问接口。均已经支持流式Function Calling。

### 1.1. 外部直接调用
LLM统一使用`get_chat_model(cfg: Optional[Dict] = None) -> BaseChatModel`接口来调用，参数传入LLM的配置文件，
配置文件格式如下：
- model_type: 对应某个具体的llm类，是llm类注册的名字，即唯一ID。使用内置的dashscope和OpenAI API时，可以省略这个参数。外部注册的LLM类，需要传入这个参数来指定。
- model：具体的模型名称
- model_server：模型服务地址
- generate_cfg：模型生成时候的参数

LLM类统一使用`llm.chat(...)`接口生成回复，支持输入消息列表、函数等参数，具体参数列表详见[BaseChatModel](../qwen_agent/llm/base.py)。
LLM完整使用样例见[Function Calling](../examples/function_calling.py)。
```py

from qwen_agent.llm import get_chat_model

llm_cfg = {
            # Use the model service provided by DashScope:
            # 'model_type': 'qwen_dashscope',
            'model': 'qwen-max',
            'model_server': 'dashscope',
            # Use your own model service compatible with OpenAI API:
            # 'model': 'Qwen',
            # 'model_server': 'http://127.0.0.1:7905/v1',
            # (Optional) LLM hyper-parameters:
            'generate_cfg': {
                'top_p': 0.8
            }
          }
llm = get_chat_model(llm_cfg)
messages = [{
    'role': 'user',
    'content': "What's the weather like in San Francisco?"
}]
functions = [{
    'name': 'get_current_weather',
    'description': 'Get the current weather in a given location',
    'parameters': {
        'type': 'object',
        'properties': {
            'location': {
                'type': 'string',
                'description':
                'The city and state, e.g. San Francisco, CA',
            },
            'unit': {
                'type': 'string',
                'enum': ['celsius', 'fahrenheit']
            },
        },
        'required': ['location'],
    },
}]

# 此处演示流式输出效果
responses = []
for responses in llm.chat(messages=messages,
                          functions=functions,
                          stream=True):
    print(responses)
```

### 1.2. Agent内部调用

在Agent中，使用`_call_llm(...)`函数调用LLM，每个Agent实例可以调用初始化给它的LLM，
`llm: Optional[Union[Dict, BaseChatModel]] = None`。支持的传入类型包括：：LLM配置文件或LLM对象。

注意，为了统一Agent的输出类型，Agent的`_call_llm(...)`接口默认使用流式生成方式访问LLM。

## 2. LLM 开发

Qwen-Agent提供了注册LLM的机制，在[LLM 基类](../qwen_agent/llm/base.py)中，实现了统一的`llm.chat(...)`接口，
新注册的LLM仅需要实现特有的三个函数：
- 非流式生成接口
- 流式生成接口（如果LLM本身不支持流式生成，可以将非流式结果封装成生成器返回）
- 工具调用接口

其中，如果新注册的LLM不支持工具调用，可以继承Qwen-Agent中实现的[BaseFnCallModel](../qwen_agent/llm/function_calling.py)类，
这个类通过封装一个类似ReAct的工具调用Prompt，已经基于普通对话接口实现了Function Calling。


[User] What is Qwen-Agent and how do I use it? Provide the official README.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

[中文](./README_CN.md) ｜ English

The usage and development process of Qwen-Agent.

# Contents

- [Agent](agent.md)
- [Tool](tool.md)
- [LLM](llm.md)


[User] What is Qwen-Agent and how do I use it? Provide the official README.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

中文 ｜ [English](./README.md)

此处介绍Qwen-Agent的使用与开发流程。

# 列表

- [Agent](agent_cn.md)
- [Tool](tool_cn.md)
- [LLM](llm_cn.md)


[User] Provide the documentation for 'docs\tool.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Tool Introduction

This document introduces the usage and development process of the Tool class.

## 1. Tool Usage

### 1.1. Direct External Call
Tools uniformly utilize the `.call(params)` interface for calling, where you pass in the necessary parameters for the tool.
The tool then returns the results after execution, with the return type being `Union[str, list, dict]`.

For example, to directly call a image generation tool:

```py
from qwen_agent.tools import ImageGen

tool = ImageGen()
res = tool.call(params = {'prompt': 'a cute cat'})
print(res)
```

### 1.2. Internal call by Agent

In the Agent, the `_call_tool(...)` function is used to call tools, with each Agent instance capable of calling the tools that were initialized and assigned to it.
The tools can be passed in through `function_list: Optional[List[Union[str, Dict, BaseTool]]] = None` parameter.
The supported input types include the tool name, tool configuration file, or the tool object itself.
For instance, you can use `code_interpreter`, `{'name': 'code_interpreter', 'timeout': 10}`, or `CodeInterpreter()`.

Note that for the convenience of inputting tool results into LLMs, the Agent’s `_call_tool(...)` interface converts all returned results from the tools into string type. For more details, see the [Agent class](../qwen_agent/agent.py).

## 2. Tool Development

Qwen-Agent provides a mechanism for registering tools. For example, to register your own image generation tool:
- Specify the tool’s name, description, and parameters. Note that the string passed to `@register_tool('my_image_gen')` is automatically added as the `.name` attribute of the class and will serve as the unique identifier for the tool.
- Implement the `call(...)` function.

```py
import urllib.parse
import json5
import json
from qwen_agent.tools.base import BaseTool, register_tool
# Add a custom tool named my_image_gen：
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.'
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description':
        'Detailed description of the desired image content, in English',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)
```

Once the tools are registered, they can be used as mentioned above.

If you prefer not to use the registration method, you can also directly define a tool class and then pass the tool object to the Agent (unregistered tools do not support passing the tool name or configuration file).

```py
import urllib.parse
import json5
import json
from qwen_agent.tools.base import BaseTool

class MyImageGen(BaseTool):
    name = 'my_image_gen'
    description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.'
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description':
        'Detailed description of the desired image content, in English',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)
```


[User] Provide the documentation for 'docs\tool_cn.md' in the Qwen-Agent framework.

[Assistant]
<!---
Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Tool 介绍

本文档介绍了Tool类的使用和开发流程。

## 1. Tool 使用

### 1.1. 外部直接调用
Tool统一使用`.call(params)`接口来调用，传入工具需要的参数，返回工具执行后的结果，返回的类型为`Union[str, list, dict]`。

例如，直接调用天气查询工具：
```py
from qwen_agent.tools import AmapWeather

tool = AmapWeather()
res = tool.call(params = {'location': '海淀区'})
print(res)
```

### 1.2. Agent内部调用

在Agent中，使用`_call_tool(...)`函数调用工具，每个Agent实例可以调用初始化给它的工具列表
`function_list: Optional[List[Union[str, Dict, BaseTool]]] = None`中的工具。
支持的传入类型包括：工具名、工具配置文件、或工具对象。例如`code_interpreter`、 `{'name': 'code_interpreter', 'timeout': 10}`、 或`CodeInterpreter()`。

注意，为了方便将工具结果输入LLM，Agent的`_call_tool(...)`接口会将所有工具返回的结果转为str类型，具体见[Agent类](../qwen_agent/agent.py)。

## 2. Tool 开发
Qwen-Agent提供了注册工具的机制，例如，下面我们注册一个自己的图片生成工具：
- 指定工具的name、description、和parameters，注意@register_tool('my_image_gen')中的'my_image_gen'会被自动添加为这个类的.name属性，将作为工具的唯一标识。
- 实现`call(...)`函数

```py
import urllib.parse
import json5
import json
from qwen_agent.tools.base import BaseTool, register_tool
# Add a custom tool named my_image_gen：
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.'
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description':
        'Detailed description of the desired image content, in English',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)
```

注册好的工具，就可以按照上文直接使用或添加到Agent里使用了。

如果不想使用注册方式，也可以直接定义工具类，然后传工具对象给Agent（没有注册的工具，不支持传工具名或配置文件）。
```py
import urllib.parse
import json5
import json
from qwen_agent.tools.base import BaseTool

class MyImageGen(BaseTool):
    name = 'my_image_gen'
    description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.'
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description':
        'Detailed description of the desired image content, in English',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)
```


[User] Show me the code for 'run_server.py' in Qwen-Agent.

[Assistant]
```python
# Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import json
import os
import signal
import subprocess
import sys
from pathlib import Path

from qwen_server.schema import GlobalConfig

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-m',
        '--model_server',
        type=str,
        default='dashscope',
        help='Set it to `dashscope` if you are using the model service provided by DashScope.'
        ' Set it to the base_url (aka api_base) if using an OpenAI API-compatible service such as vLLM or Ollama.'
        ' Default: dashscope',
    )
    parser.add_argument(
        '-k',
        '--api_key',
        type=str,
        default='',
        help='You API key to DashScope or the OpenAI API-compatible model service.',
    )
    parser.add_argument(
        '-l',
        '--llm',
        type=str,
        default='qwen-plus',
        help='Set it to one of {"qwen-max", "qwen-plus", "qwen-turbo"} if using DashScope.'
        ' Set it to the model name using an OpenAI API-compatible model service.'
        ' Default: qwen-plus',
    )
    parser.add_argument(
        '-s',
        '--server_host',
        type=str,
        default='127.0.0.1',
        choices=['127.0.0.1', '0.0.0.0'],
        help='Set to 0.0.0.0 if you want to allow other machines to access the server. Default: 127.0.0.1',
    )
    parser.add_argument(
        '-t',
        '--max_ref_token',
        type=int,
        default=4000,
        help='Tokens reserved for the reference materials of retrieval-augmanted generation (RAG). Default: 4000',
    )
    parser.add_argument(
        '-w',
        '--workstation_port',
        type=int,
        default=7864,
        help='The port of the creative writing workstation. Default: 7864',
    )
    args = parser.parse_args()
    args.model_server = args.model_server.replace('0.0.0.0', '127.0.0.1')
    return args

def update_config(server_config, args, server_config_path):
    server_config.server.model_server = args.model_server
    server_config.server.api_key = args.api_key
    server_config.server.llm = args.llm
    server_config.server.server_host = args.server_host
    server_config.server.max_ref_token = args.max_ref_token
    server_config.server.workstation_port = args.workstation_port

    with open(server_config_path, 'w') as f:
        try:
            cfg = server_config.model_dump_json()
        except AttributeError:  # for pydantic v1
            cfg = server_config.json()
        json.dump(json.loads(cfg), f, ensure_ascii=False, indent=4)
    return server_config

def main():
    args = parse_args()
    server_config_path = Path(__file__).resolve().parent / 'qwen_server/server_config.json'
    with open(server_config_path, 'r') as f:
        server_config = json.load(f)
        server_config = GlobalConfig(**server_config)
    server_config = update_config(server_config, args, server_config_path)

    os.makedirs(server_config.path.work_space_root, exist_ok=True)
    os.makedirs(server_config.path.download_root, exist_ok=True)

    os.makedirs(server_config.path.code_interpreter_ws, exist_ok=True)
    code_interpreter_work_dir = str(Path(__file__).resolve().parent / server_config.path.code_interpreter_ws)

    # TODO: Remove these two hacky code interpreter env vars.
    os.environ['M6_CODE_INTERPRETER_WORK_DIR'] = code_interpreter_work_dir

    from qwen_agent.utils.utils import append_signal_handler, get_local_ip, logger
    logger.info(server_config)

    if args.server_host == '0.0.0.0':
        static_url = get_local_ip()
    else:
        static_url = args.server_host
    static_url = f'http://{static_url}:{server_config.server.fast_api_port}/static'
    os.environ['M6_CODE_INTERPRETER_STATIC_URL'] = static_url

    servers = {
        'database':
            subprocess.Popen([
                sys.executable,
                os.path.join(os.getcwd(), 'qwen_server/database_server.py'),
            ]),
        'workstation':
            subprocess.Popen([
                sys.executable,
                os.path.join(os.getcwd(), 'qwen_server/workstation_server.py'),
            ]),
        'assistant':
            subprocess.Popen([
                sys.executable,
                os.path.join(os.getcwd(), 'qwen_server/assistant_server.py'),
            ]),
    }

    def signal_handler(sig_num, _frame):
        for v in servers.values():
            v.terminate()
        for k in list(servers.keys()):
            del servers[k]
        if sig_num == signal.SIGINT:
            raise KeyboardInterrupt()

    append_signal_handler(signal.SIGINT, signal_handler)
    append_signal_handler(signal.SIGTERM, signal_handler)

    for p in list(servers.values()):
        p.wait()

if __name__ == '__main__':
    main()

```


[User] Show me the code for 'setup.py' in Qwen-Agent.

[Assistant]
```python
# Copyright 2023 The Qwen team, Alibaba Group. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re

from setuptools import find_packages, setup

def get_version() -> str:
    with open('qwen_agent/__init__.py', encoding='utf-8') as f:
        version = re.search(
            r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
            f.read(),
            re.MULTILINE,
        ).group(1)
    return version

def read_description() -> str:
    with open('README.md', 'r', encoding='UTF-8') as f:
        long_description = f.read()
    return long_description

# To update the package at PyPI:
# ```bash
# python setup.py sdist bdist_wheel
# twine upload dist/*
# ```
setup(
    name='qwen-agent',
    version=get_version(),
    author='Qwen Team',
    author_email='tujianhong.tjh@alibaba-inc.com',
    description='Qwen-Agent: Enhancing LLMs with Agent Workflows, RAG, Function Calling, and Code Interpreter.',
    long_description=read_description(),
    long_description_content_type='text/markdown',
    keywords=['LLM', 'Agent', 'Function Calling', 'RAG', 'Code Interpreter'],
    packages=find_packages(exclude=['examples', 'examples.*', 'qwen_server', 'qwen_server.*']),
    package_data={
        'qwen_agent': [
            'utils/qwen.tiktoken', 'tools/resource/*.ttf', 'tools/resource/*.py', 'gui/assets/*.css',
            'gui/assets/*.jpeg'
        ],
    },

    # Minimal dependencies for Function Calling:
    install_requires=[
        'dashscope>=1.11.0',
        'eval_type_backport',
        'json5',
        'jsonlines',
        'jsonschema',
        'openai',
        'pydantic>=2.3.0',
        'requests',
        'tiktoken',
        'pillow',
        'dotenv',
    ],
    extras_require={
        # Extra dependencies for RAG:
        'rag': [
            'charset-normalizer',
            'rank_bm25',
            'jieba',
            'snowballstemmer',
            'beautifulsoup4',
            'pdfminer.six',
            'pdfplumber',
            'python-docx',
            'python-pptx',
            'pandas',
            'tabulate',
        ],

        # Extra dependencies for MCP:
        'mcp': ['mcp'],

        # Extra dependencies for Python Executor, which is primarily for solving math problems:
        'python_executor': [
            'pebble',
            'multiprocess',
            'timeout_decorator',
            'python-dateutil',
            'sympy',
            'numpy',
            'scipy',
        ],

        # Extra dependencies for Code Interpreter:
        'code_interpreter': [
            'anyio>=3.7.1',
            'fastapi>=0.103.1',
            'jupyter>=1.0.0',
            'matplotlib',
            'numpy',
            'pandas',
            'pillow',
            'seaborn',
            'sympy',
            'uvicorn>=0.23.2',
        ],

        # Extra dependencies for Gradio-based GUI:
        'gui': [
            # Gradio has bad version compatibility. Therefore, we use `==` instead of `>=`.
            'pydantic==2.9.2',
            'pydantic-core==2.23.4',
            'gradio==5.23.1',
            'gradio-client==1.8.0',
            'modelscope_studio==1.1.7',
        ],
    },
    url='https://github.com/QwenLM/Qwen-Agent',
)

```


[User] Show me the code for 'benchmark\code_interpreter\code_interpreter.py' in Qwen-Agent.

[Assistant]
```python
import base64
import io
import json
import logging
import os
import queue
import re
import subprocess
import sys
import time
import traceback
import uuid

import matplotlib
import PIL.Image
from jupyter_client import BlockingKernelClient
from utils.code_utils import extract_code

WORK_DIR = os.getenv('CODE_INTERPRETER_WORK_DIR', '/tmp/workspace')

LAUNCH_KERNEL_PY = """
from ipykernel import kernelapp as app
app.launch_new_instance()
"""

_KERNEL_CLIENTS = {}

# Run this fix before jupyter starts if matplotlib cannot render CJK fonts.
# And we need to additionally run the following lines in the jupyter notebook.
#   ```python
#   import matplotlib.pyplot as plt
#   plt.rcParams['font.sans-serif'] = ['SimHei']
#   plt.rcParams['axes.unicode_minus'] = False
#   ````
def fix_matplotlib_cjk_font_issue():
    local_ttf = os.path.join(os.path.abspath(os.path.join(matplotlib.matplotlib_fname(), os.path.pardir)), 'fonts',
                             'ttf', 'simhei.ttf')
    if not os.path.exists(local_ttf):
        logging.warning(
            f'Missing font file `{local_ttf}` for matplotlib. It may cause some error when using matplotlib.')

def start_kernel(pid):
    fix_matplotlib_cjk_font_issue()

    connection_file = os.path.join(WORK_DIR, f'kernel_connection_file_{pid}.json')
    launch_kernel_script = os.path.join(WORK_DIR, f'launch_kernel_{pid}.py')
    for f in [connection_file, launch_kernel_script]:
        if os.path.exists(f):
            logging.warning(f'{f} already exists')
            os.remove(f)

    os.makedirs(WORK_DIR, exist_ok=True)

    with open(launch_kernel_script, 'w') as fout:
        fout.write(LAUNCH_KERNEL_PY)

    kernel_process = subprocess.Popen([
        sys.executable,
        launch_kernel_script,
        '--IPKernelApp.connection_file',
        connection_file,
        '--matplotlib=inline',
        '--quiet',
    ],
                                      cwd=WORK_DIR)
    logging.info(f"INFO: kernel process's PID = {kernel_process.pid}")

    # Wait for kernel connection file to be written
    while True:
        if not os.path.isfile(connection_file):
            time.sleep(0.1)
        else:
            # Keep looping if JSON parsing fails, file may be partially written
            try:
                with open(connection_file, 'r') as fp:
                    json.load(fp)
                break
            except json.JSONDecodeError:
                pass

    # Client
    kc = BlockingKernelClient(connection_file=connection_file)
    kc.load_connection_file()
    kc.start_channels()
    kc.wait_for_ready()
    return kc

def escape_ansi(line):
    ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
    return ansi_escape.sub('', line)

def publish_image_to_local(image_base64: str):
    image_file = str(uuid.uuid4()) + '.png'
    local_image_file = os.path.join(WORK_DIR, image_file)

    png_bytes = base64.b64decode(image_base64)
    assert isinstance(png_bytes, bytes)
    bytes_io = io.BytesIO(png_bytes)
    PIL.Image.open(bytes_io).save(local_image_file, 'png')

    return local_image_file

START_CODE = """
import signal
def _m6_code_interpreter_timeout_handler(signum, frame):
    raise TimeoutError("M6_CODE_INTERPRETER_TIMEOUT")
signal.signal(signal.SIGALRM, _m6_code_interpreter_timeout_handler)

def input(*args, **kwargs):
    raise NotImplementedError('Python input() function is disabled.')

import os
if 'upload_file' not in os.getcwd():
    os.chdir("./upload_file/")

import math
import re
import json

import seaborn as sns
sns.set_theme()

import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

import numpy as np
import pandas as pd

from sympy import Eq, symbols, solve
"""

def code_interpreter(action_input_list: list, timeout=30, clear=False):
    code = ''
    for action_input in action_input_list:
        code += (extract_code(action_input) + '\n')
    fixed_code = []
    for line in code.split('\n'):
        fixed_code.append(line)
        if line.startswith('sns.set_theme('):
            fixed_code.append('plt.rcParams["font.sans-serif"] = ["SimHei"]')
            fixed_code.append('plt.rcParams["axes.unicode_minus"] = False')
    fixed_code = '\n'.join(fixed_code)
    if 'def solution()' in fixed_code:
        fixed_code += '\nsolution()'

    return _code_interpreter(fixed_code, timeout, clear)

def _code_interpreter(code: str, timeout, clear=False):
    if not code.strip():
        return ''
    if timeout:
        code = f'signal.alarm({timeout})\n{code}'
    if clear:
        code = "get_ipython().run_line_magic('reset', '-f')\n" + START_CODE + code

    pid = os.getpid()
    if pid not in _KERNEL_CLIENTS:
        _KERNEL_CLIENTS[pid] = start_kernel(pid)
        _code_interpreter(START_CODE, timeout=None)
    kc = _KERNEL_CLIENTS[pid]
    kc.wait_for_ready()
    kc.execute(code)
    result = ''
    image_idx = 0
    while True:
        text = ''
        image = ''
        finished = False
        msg_type = 'error'
        try:
            msg = kc.get_iopub_msg()
            msg_type = msg['msg_type']
            if msg_type == 'status':
                if msg['content'].get('execution_state') == 'idle':
                    finished = True
            elif msg_type == 'execute_result':
                text = msg['content']['data'].get('text/plain', '')
                if 'image/png' in msg['content']['data']:
                    image_b64 = msg['content']['data']['image/png']
                    image_url = publish_image_to_local(image_b64)
                    image_idx += 1
                    image = '![fig-%03d](%s)' % (image_idx, image_url)
            elif msg_type == 'display_data':
                if 'image/png' in msg['content']['data']:
                    image_b64 = msg['content']['data']['image/png']
                    image_url = publish_image_to_local(image_b64)
                    image_idx += 1
                    image = '![fig-%03d](%s)' % (image_idx, image_url)
                else:
                    text = msg['content']['data'].get('text/plain', '')
            elif msg_type == 'stream':
                msg_type = msg['content']['name']  # stdout, stderr
                text = msg['content']['text']
            elif msg_type == 'error':
                text = escape_ansi('\n'.join(msg['content']['traceback']))
                if 'M6_CODE_INTERPRETER_TIMEOUT' in text:
                    text = f'Timeout. No response after {timeout} seconds.'
        except queue.Empty:
            text = f'Timeout. No response after {timeout} seconds.'
            finished = True
        except Exception:
            text = 'The code interpreter encountered an unexpected error.'
            logging.warning(''.join(traceback.format_exception(*sys.exc_info())))
            finished = True
        if text:
            result += f'\n\n{msg_type}:\n\n```\n{text}\n```'
        if image:
            result += f'\n\n{image}'
        if finished:
            break
    result = result.lstrip('\n')
    if timeout:
        _code_interpreter('signal.alarm(0)', timeout=None)
    return result

def get_multiline_input(hint):
    print(hint)
    print('// Press ENTER to make a new line. Press CTRL-D to end input.')
    lines = []
    while True:
        try:
            line = input()
        except EOFError:  # CTRL-D
            break
        lines.append(line)
    print('// Input received.')
    if lines:
        return '\n'.join(lines)
    else:
        return ''

if __name__ == '__main__':
    while True:
        print(code_interpreter([get_multiline_input('Enter python code:')]))

```


[User] Show me the code for 'benchmark\code_interpreter\config.py' in Qwen-Agent.

[Assistant]
```python
from parser import InternLMReActParser, ReActParser

from models import LLM, Qwen, QwenDashscopeVLModel, QwenVL
from prompt import InternLMReAct, LlamaReAct, QwenReAct

react_prompt_map = {
    'qwen': QwenReAct,
    'llama': LlamaReAct,
    'internlm': InternLMReAct,
}

react_parser_map = {
    'qwen': ReActParser,
    'llama': ReActParser,
    'internlm': InternLMReActParser,
}

model_map = {'qwen': Qwen, 'llama': LLM, 'internlm': LLM, 'qwen-vl-chat': QwenVL}

model_type_map = {
    'qwen-72b-chat': 'qwen',
    'qwen-14b-chat': 'qwen',
    'qwen-1.8b-chat': 'qwen',
    'qwen-7b-chat': 'qwen',
    'llama-2-7b-chat': 'llama',
    'llama-2-13b-chat': 'llama',
    'codellama-7b-instruct': 'llama',
    'codellama-13b-instruct': 'llama',
    'internlm-7b-chat-1.1': 'internlm',
    'internlm-20b-chat': 'internlm',
    'qwen-vl-chat': 'qwen-vl-chat',
}

model_path_map = {
    'qwen-72b-chat': 'Qwen/Qwen-72B-Chat',
    'qwen-14b-chat': 'Qwen/Qwen-14B-Chat',
    'qwen-7b-chat': 'Qwen/Qwen-7B-Chat',
    'qwen-1.8b-chat': 'Qwen/Qwen-1_8B-Chat',
    'llama-2-7b-chat': 'meta-llama/Llama-2-7b-chat-hf',
    'llama-2-13b-chat': 'meta-llama/Llama-2-13b-chat-hf',
    'codellama-7b-instruct': 'codellama/CodeLlama-7b-Instruct-hf',
    'codellama-13b-instruct': 'codellama/CodeLlama-13b-Instruct-hf',
    'internlm-7b-chat-1.1': 'internlm/internlm-chat-7b-v1_1',
    'internlm-20b-chat': 'internlm/internlm-chat-20b',
    'qwen-vl-chat': 'Qwen/Qwen-VL-Chat',
}

def get_react_prompt(model_name, query, lang, upload_fname_list):
    react_prompt_cls = react_prompt_map.get(model_type_map[model_name], QwenReAct)
    return react_prompt_cls(query, lang, upload_fname_list)

def get_react_parser(model_name):
    react_parser_cls = react_parser_map.get(model_type_map[model_name], ReActParser)
    return react_parser_cls()

def get_model(model_name):
    if model_name in ['qwen-vl-plus']:
        return QwenDashscopeVLModel(model=model_name)
    model_path = model_path_map.get(model_name, None)
    model_cls = model_map.get(model_type_map[model_name], LLM)
    return model_cls(model_path)

```