# MiniMax-M2 工具调用指南 [英文版](./tool_calling_guide.md) | [中文版](./tool_calling_guide_cn.md) ## 简介 MiniMax-M2 模型支持工具调用功能,使模型能够识别何时需要调用外部工具,并以结构化格式输出工具调用参数。本文档提供了有关如何使用 MiniMax-M2 工具调用功能的详细说明。 ## 基础示例 以下 Python 脚本基于 OpenAI SDK 实现了一个天气查询工具调用示例: ```python from openai import OpenAI import json client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy") def get_weather(location: str, unit: str): return f"Getting the weather for {location} in {unit}..." tool_functions = {"get_weather": get_weather} tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City and state, e.g., 'San Francisco, CA'"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["location", "unit"] } } }] response = client.chat.completions.create( model=client.models.list().data[0].id, messages=[{"role": "user", "content": "What's the weather like in San Francisco? use celsius."}], tools=tools, tool_choice="auto" ) print(response) tool_call = response.choices[0].message.tool_calls[0].function print(f"Function called: {tool_call.name}") print(f"Arguments: {tool_call.arguments}") print(f"Result: {get_weather(**json.loads(tool_call.arguments))}") ``` **输出示例:** ``` Function called: get_weather Arguments: {"location": "San Francisco, CA", "unit": "celsius"} Result: Getting the weather for San Francisco, CA in celsius... ``` ## 手动解析模型输出 **我们强烈建议使用 vLLM 或 SGLnag 来解析工具调用。** 如果您无法使用支持 MiniMax-M2 的推理引擎(如 vLLM 和 SGLang)的内置解析器,或需要使用其他推理框架(如 transformers、TGI 等),您可以使用以下方法手动解析模型的原始输出。这种方法需要您自己解析模型输出的 XML 标签格式。 ### 使用 Transformers 的示例 这是一个使用 transformers 库的完整示例: ```python from transformers import AutoTokenizer def get_default_tools(): return [ { "name": "get_current_weather", "description": "Get the latest weather for a location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "A certain city, such as Beijing, Shanghai" } }, } "required": ["location"], "type": "object" } ] # Load model and tokenizer tokenizer = AutoTokenizer.from_pretrained(model_id) prompt = "What's the weather like in Shanghai today?" messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": prompt}, ] # Enable function calling tools tools = get_default_tools() # Apply chat template and include tool definitions text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, tools=tools ) # Send request (using any inference service) import requests payload = { "model": "MiniMaxAI/MiniMax-M2", "prompt": text, "max_tokens": 4096 } response = requests.post( "http://localhost:8000/v1/completions", headers={"Content-Type": "application/json"}, json=payload, stream=False, ) # Model output needs manual parsing raw_output = response.json()["choices"][0]["text"] print("Raw output:", raw_output) # Use the parsing function below to process the output tool_calls = parse_tool_calls(raw_output, tools) ``` ## 🛠️ 工具调用定义 ### 工具结构 工具调用需要在请求体中定义 `tools` 字段。每个工具由以下部分组成: ```json { "tools": [ { "name": "search_web", "description": "Search function.", "parameters": { "properties": { "query_list": { "description": "Keywords for search, list should contain 1 element.", "items": { "type": "string" }, "type": "array" }, "query_tag": { "description": "Category of query", "items": { "type": "string" }, "type": "array" } }, "required": [ "query_list", "query_tag" ], "type": "object" } } ] } ``` **字段说明:** - `name`:函数名称 - `description`:函数描述 - `parameters`:函数参数定义 - `properties`:参数属性定义,其中键是参数名称,值包含详细的参数描述 - `required`:必需参数列表 - `type`:参数类型(通常为 "object") ### 内部处理格式 在 MiniMax-M2 模型内部处理时,工具定义会被转换为特殊格式并连接到输入文本中。以下是一个完整示例: ``` ]~!b[]~b]system You are a helpful assistant. # Tools You may call one or more tools to assist with the user query. Here are the tools available in JSONSchema format: {"name": "search_web", "description": "Search function.", "parameters": {"type": "object", "properties": {"query_list": {"type": "array", "items": {"type": "string"}, "description": "Keywords for search, list should contain 1 element."}, "query_tag": {"type": "array", "items": {"type": "string"}, "description": "Category of query"}}, "required": ["query_list", "query_tag"]}} When making tool calls, use XML format to invoke tools and pass parameters: param-value-1 param-value-2 ... [e~[ ]~b]user When were the latest announcements from OpenAI and Gemini?[e~[ ]~b]ai ``` **格式说明:** - `]~!b[]~b]system`:系统消息开始标记 - `[e~[`:消息结束标记 - `]~b]user`:用户消息开始标记 - `]~b]ai`:助手消息开始标记 - `]~b]tool`:工具结果消息开始标记 - `...`:工具定义区域,每个工具都用 `` 标签包装,内容为 JSON Schema - `...`:工具调用区域 - `...`:生成过程中的思考过程标记 ### 模型输出格式 MiniMax-M2 使用结构化的 XML 标签格式: ```xml ["technology", "events"] ["\"OpenAI\" \"latest\" \"release\""] ["technology", "events"] ["\"Gemini\" \"latest\" \"release\""] ``` 每个工具调用使用 `` 标签,参数使用 `` 标签包装。 ## 手动解析工具调用结果 ### 解析工具调用 MiniMax-M2 使用结构化的 XML 标签,这需要一种不同的解析方法。核心函数如下: ```python import re import json from typing import Any, Optional, List, Dict def extract_name(name_str: str) -> str: """Extract name from quoted string""" name_str = name_str.strip() if name_str.startswith('"') and name_str.endswith('"'): return name_str[1:-1] elif name_str.startswith("'") and name_str.endswith("'"): return name_str[1:-1] return name_str def convert_param_value(value: str, param_type: str) -> Any: """Convert parameter value based on parameter type""" if value.lower() == "null": return None param_type = param_type.lower() if param_type in ["string", "str", "text"]: return value elif param_type in ["integer", "int"]: try: return int(value) except (ValueError, TypeError): return value elif param_type in ["number", "float"]: try: val = float(value) return val if val != int(val) else int(val) except (ValueError, TypeError): return value elif param_type in ["boolean", "bool"]: return value.lower() in ["true", "1"] elif param_type in ["object", "array"]: try: return json.loads(value) except json.JSONDecodeError: return value else: # Try JSON parsing, return string if failed try: return json.loads(value) except json.JSONDecodeError: return value def parse_tool_calls(model_output: str, tools: Optional[List[Dict]] = None) -> List[Dict]: """ Extract all tool calls from model output Args: model_output: Complete output text from the model tools: Tool definition list for getting parameter type information, format can be: - [{"name": "...", "parameters": {...}}] - [{"type": "function", "function": {"name": "...", "parameters": {...}}}] Returns: Parsed tool call list, each element contains name and arguments fields Example: >>> tools = [{ ... "name": "get_weather", ... "parameters": { ... "type": "object", ... "properties": { ... "location": {"type": "string"}, ... "unit": {"type": "string"} ... } ... } ... }] >>> output = ''' ... ... San Francisco ... celsius ... ... ''' >>> result = parse_tool_calls(output, tools) >>> print(result) [{'name': 'get_weather', 'arguments': {'location': 'San Francisco', 'unit': 'celsius'}}] """ # Quick check if tool call marker is present if "" not in model_output: return [] tool_calls = [] try: # Match all blocks tool_call_regex = re.compile(r"(.*?)", re.DOTALL) invoke_regex = re.compile(r"", re.DOTALL) parameter_regex = re.compile(r"", re.DOTALL) # Iterate through all tool_call blocks for tool_call_match in tool_call_regex.findall(model_output): # Iterate through all invokes in this block for invoke_match in invoke_regex.findall(tool_call_match): # Extract function name name_match = re.search(r'^([^>]+)', invoke_match) if not name_match: continue function_name = extract_name(name_match.group(1)) # Get parameter configuration param_config = {} if tools: for tool in tools: tool_name = tool.get("name") or tool.get("function", {}).get("name") if tool_name == function_name: params = tool.get("parameters") or tool.get("function", {}).get("parameters") if isinstance(params, dict) and "properties" in params: param_config = params["properties"] break # Extract parameters param_dict = {} for match in parameter_regex.findall(invoke_match): param_match = re.search(r'^([^>]+)>(.*)', match, re.DOTALL) if param_match: param_name = extract_name(param_match.group(1)) param_value = param_match.group(2).strip() # Remove leading and trailing newlines if param_value.startswith('\n'): param_value = param_value[1:] if param_value.endswith('\n'): param_value = param_value[:-1] # Get parameter type and convert param_type = "string" if param_name in param_config: if isinstance(param_config[param_name], dict) and "type" in param_config[param_name]: param_type = param_config[param_name]["type"] param_dict[param_name] = convert_param_value(param_value, param_type) tool_calls.append({ "name": function_name, "arguments": param_dict }) except Exception as e: print(f"Failed to parse tool calls: {e}") return [] return tool_calls ``` **使用示例:** ```python # Define tools tools = [ { "name": "get_weather", "parameters": { "type": "object", "properties": { "location": {"type": "string"}, "unit": {"type": "string"} }, "required": ["location", "unit"] } } ] # Model output model_output = """Let me help you query the weather. San Francisco celsius """ # Parse tool calls tool_calls = parse_tool_calls(model_output, tools) # Output results for call in tool_calls: print(f"Function called: {call['name']}") print(f"Arguments: {call['arguments']}") # Output: Function called: get_weather # Arguments: {'location': 'San Francisco', 'unit': 'celsius'} ``` ### 执行工具调用 完成解析后,您可以执行相应的工具并构造返回结果: ```python def execute_function_call(function_name: str, arguments: dict): """Execute function call and return result""" if function_name == "get_weather": location = arguments.get("location", "Unknown location") unit = arguments.get("unit", "celsius") # Build function execution result return { "role": "tool", "content": [ { "name": function_name, "type": "text", "text": json.dumps({ "location": location, "temperature": "25", "unit": unit, "weather": "Sunny" }, ensure_ascii=False) } ] } elif function_name == "search_web": query_list = arguments.get("query_list", []) query_tag = arguments.get("query_tag", []) # Simulate search results return { "role": "tool", "content": [ { "name": function_name, "type": "text", "text": f"Search keywords: {query_list}, Category: {query_tag}\nSearch results: Relevant information found" } ] } return None ``` ### 将工具执行结果返回给模型 在成功解析工具调用后,您应该将工具执行结果添加到对话历史中,以便模型在后续交互中可以访问和利用这些信息。请参考 [chat_template.jinja](https://huggingface.co/MiniMaxAI/MiniMax-M2/blob/main/chat_template.jinja) 了解连接格式。 ## 参考文献 - [MiniMax-M2 模型仓库](https://github.com/MiniMax-AI/MiniMax-M2) - [vLLM 项目主页](https://github.com/vllm-project/vllm) - [SGLang 项目主页](https://github.com/sgl-project/sglang) - [OpenAI Python SDK](https://github.com/openai/openai-python) ## 获取支持 如果遇到任何问题: - 通过邮箱 [model@minimax.io](mailto:model@minimax.io) 等官方渠道联系我们的技术支持团队 - 在我们的仓库提交 Issue - 通过我们的 [官方企业微信交流群](https://github.com/MiniMax-AI/MiniMax-AI.github.io/blob/main/images/wechat-qrcode.jpeg) 反馈 我们会持续优化模型的使用体验,欢迎反馈!