Gemma3でtoolsを使用する(Ollama, ローカルLLM)

AI

Gemma3は画像入力に対応している上にテキストでの返答もとても優秀なのですが、tools に対応していないところが不便です。
今回はモデルファイルを編集することでGemma3にtoolsを呼び出させてみたいと思います。

toolsに対応することで、Function Calling や MCP(Model Context Protocol) が使用できるかと思います。

前置き

実行環境

  • Windows11
  • RTX4080 16GB
  • Python3.11
  • Ollama

Ollama等のインストール解説は省略します。

Gemma3のPull

Ollamaコマンドでpullできます。サイズは27bを選択しました。

PowerShell
ollama pull gemma3:27b

Gemma3でtoolsを使うための設定

Gemma3はデフォルトでtoolsには対応していないので、設定を書き換えます。

まず、下記コマンドでmodelfileをファイルに出力します。

PowerShell
ollama show gemma3:27b --modelfile > gemma3_27b.modelfile

次に、gemma3_27b.modelfile をエディタで開き編集します。
TEMPLATEにToolsの記述を追加することで、toolsを使えるようにします。

Plaintext
# Modelfile generated by "ollama show"
# To build a new Modelfile based on this, replace FROM with:
FROM gemma3:27b
TEMPLATE """{{- if or .System .Tools }}<start_of_turn>user
{{- if .System }}
{{ .System }}
{{- end }}
{{- if .Tools }}

# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
{{- range .Tools }}
{"type": "function", "function": {{ .Function }}}
{{- end }}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>

You may receive a response from the tool you called. The response will be within <tool_response></tool_response> XML tags.
And you can use the tool response to generate a response to the original user question.
<tool_response>
Tool response content
</tool_response>

{{- end }}<end_of_turn>
{{ end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 }}
{{- if eq .Role "user" }}<start_of_turn>user
{{ .Content }}<end_of_turn>
{{- else if eq .Role "assistant" }}<start_of_turn>model
{{- if .Content }}{{ .Content }}
{{- else if .ToolCalls }}<tool_call>
{{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
{{- end }}</tool_call>
{{- end }}
{{- if not $last }}<end_of_turn>
{{- end }}
{{- else if eq .Role "tool" }}<start_of_turn>user
<tool_response>
{{ .Content }}
</tool_response><end_of_turn>
{{- end }}  
{{ if and (ne .Role "assistant") $last }}<start_of_turn>model
{{ end }}
{{- end }}"""

PARAMETER stop <end_of_turn>
PARAMETER temperature 0.1
LICENSE """Gemma Terms of Use 
... 以下略 ...

変更したmodelfileから新しいモデル gemma3-tools:27b を作成します。

PowerShell
ollama create gemma3-tools:27b -f .\gemma3_27b.modelfile  

テスト

テストするために簡単なPythonスクリプトを作成しました。
簡単な計算を行うツールを3つ作成し、Gemma3に呼び出させます。

Python
import openai
from openai.types.chat import ChatCompletionToolMessageParam
import base64
import json

# Set up the client with your configuration
client = openai.OpenAI(
    base_url="http://localhost:11434/v1/",
    api_key="ollama"
)
model = "gemma3-tools:27b"

# Define the tools
tools = [
    {
        "type": "function",
        "function": {
            "name": "add_numbers",
            "description": "Add two numbers",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {"type": "number", "description": "The first number"},
                    "b": {"type": "number", "description": "The second number"}
                },
                "required": ["a", "b"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "multiply_numbers",
            "description": "Multiply two numbers",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {"type": "number", "description": "The first number"},
                    "b": {"type": "number", "description": "The second number"}
                },
                "required": ["a", "b"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "power_numbers",
            "description": "Raise a number to the power of another",
            "parameters": {
                "type": "object",
                "properties": {
                    "base": {"type": "number", "description": "The base number"},
                    "exponent": {"type": "number", "description": "The exponent"}
                },
                "required": ["base", "exponent"]
            }
        }
    },
]

# Example function to handle the tool
def add_numbers(a, b):
    return a + b

# Example function to handle the tool
def multiply_numbers(a, b):
    return a * b

# Example function to handle the tool
def power_numbers(base, exponent):
    return base ** exponent

def completion_with_tool(messages):
    # Call the model with the message string and tools
    completion = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=tools,
        tool_choice = 'auto',
    )

    # Check if the model called a tool
    if completion.choices[0].message.tool_calls:
        tool_call = completion.choices[0].message.tool_calls[0]
        tool_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        print("Tool called:", tool_name, "with parameters:", arguments)

        # Call the actual function with the parameters
        if tool_name == "add_numbers":
            result = add_numbers(**arguments)
            print("Result:", result)
        elif tool_name == "multiply_numbers":
            result = multiply_numbers(**arguments)
            print("Result:", result)
        elif tool_name == "power_numbers":
            result = power_numbers(**arguments)
            print("Result:", result)
        else:
            print("Unknown tool:", tool_name)
            raise ValueError(f"Unknown tool: {tool_name}")

        # Send the result back to the model
        toolMessage: ChatCompletionToolMessageParam = {
            'role': "tool",
            'content': f"{result}",
            'tool_call_id': tool_call.id,
        }
        completion = client.chat.completions.create(
            model=model,
            messages=[
                *messages,
                toolMessage
            ],
            tools=tools,
            tool_choice = 'none',
        )
    return completion.choices[0].message.content

# Main function to demonstrate tool usage
if __name__ == "__main__":
    # Example message string to trigger the tool
    response = completion_with_tool([{"role": "user", "content": "What is 5 + 3?"}])
    print("Response:", response)

    # Example message string to trigger another tool
    response = completion_with_tool([{"role": "user", "content": "What is 4 * 7?"}])
    print("Response:", response)

    # Example message string to trigger another tool
    response = completion_with_tool([{"role": "user", "content": "What is 2 ^ 3?"}])
    print("Response:", response)

    # Example message string to trigger another tool with Image
    with open("2^10.png", "rb") as f:
        response = completion_with_tool([
            {
                "role": "user", 
                "content": [
                    {"type": "text", "text": "What is answer of this image?" },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/jpeg;base64,{base64.b64encode(f.read()).decode('utf-8')}"
                        },
                    }
                ]
            }
        ])
        print("Response:", response)

4番目のテストの 2^10.png の内容は下記の画像です。

実行結果は下記のようになりました。すべてtoolsを使用したうえで正解しています。

PowerShell
Tool called: add_numbers with parameters: {'a': 5, 'b': 3}
Result: 8
Response: 
The answer is 8.

Tool called: multiply_numbers with parameters: {'a': 4, 'b': 7}
Result: 28
Response: 
4 multiplied by 7 is 28.

Tool called: power_numbers with parameters: {'base': 2, 'exponent': 3}
Result: 8
Response: 
2 raised to the power of 3 is 8.

Tool called: power_numbers with parameters: {'base': 2, 'exponent': 10}
Result: 1024
Response: 
The answer to the image, which represents 2 raised to the power of 10, is 1024.

画像で入力した 2^10 まで正解してくれました。

まとめ

Gemma3をtoolsに対応させる方法を紹介しました。
結構素直にツールを呼び出してくれました。

画像入力にも問題なさそうなので普通に使えそうですね。

コメント

タイトルとURLをコピーしました