3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLM・LLM活用Advent Calendar 2024

Day 2

ローカル環境でカスタムツールを使うLLMの実装

Last updated at Posted at 2024-12-01

このページではlangchainを通じて、ローカルのLLMモデルに自作ツールを使うエージェントを構築し、対話する試みを紹介します。
ローカルモデルはOllamaを通じて実行します。

前提

  • TypeScriptを使用しています。
  • denoを使用しています。
    • npmを使う場合は事前にlangchainをインストールする必要があります。
  • Ollamaがインストールされており、正常に動作していること
    • meta製のllamaモデルなど、Ollamaで実行できるモデルがダウンロード済みであること
    • ollama pull llama3.2:1bなどでダウンロードできます

環境構築の手順

  1. denoのインストール
# Linux, macOSの場合
curl -fsSL https://deno.land/install.sh | sh
# windowの場合
irm https://deno.land/install.ps1 | iex
  1. Node.js + npmの場合は以下のパッケージをインストールする必要があります:
npm install langchain
npm install @langchain/community
npm install @langchain/core
npm install zod
  1. Ollamaのインストール
# macOSの場合
brew install ollama

# Linuxの場合
curl -fsSL https://ollama.com/install.sh | sh
  1. Ollamaの起動
ollama serve
  1. モデルのダウンロード
ollama pull llama2
# もしくは
ollama pull llama3.2:1b
  1. モデルの動作確認
ollama run llama3.2:1b "Hello!"

注意事項

  • Ollamaは常時起動している必要があります
  • 初回実行時はモデルのロードに時間がかかる場合があります
  • Silicon Mac以外の場合、実行速度が遅くなる可能性があります
  • Windows環境の場合はWSL2経由での実行を推奨します

計算を実行するエージェントの構築

ディレクトリ構成は以下のようになります。

.
├── calculator.ts
├── llm.ts
└── main.ts

計算ツール

計算ツールを自作します。
例えば、以下のようにして、質問を自然言語で理解して、どの四則演算を使うか判断し、計算を実行する「計算機tool」を自作します。

参考:

calculator.ts
/** 計算ツールの定義 */

import { DynamicStructuredTool } from "npm:@langchain/core/tools";
import { z } from "npm:zod";

const calculatorSchema = z.object({
  operation: z
    .enum(["add", "subtract", "multiply", "divide"])
    .describe("実行する操作の種類"),
  number1: z.number().describe("操作する最初の数値"),
  number2: z.number().describe("操作する2番目の数値"),
});

// tools
export const calculatorTool = new DynamicStructuredTool({
  name: "calculator",
  description: "計算機として足し算、引き算、掛け算、割り算を行います。",
  schema: calculatorSchema,
  func: async ({ operation, number1, number2 }) => {
    if (operation === "add") {
      return `${number1 + number2}`;
    } else if (operation === "subtract") {
      return `${number1 - number2}`;
    } else if (operation === "multiply") {
      return `${number1 * number2}`;
    } else if (operation === "divide") {
      return `${number1 / number2}`;
    } else {
      throw new Error("Invalid operation.");
    }
  },
});

カスタムクラス

ChatOpenAIでは実行できましたが、ChatOllamaではツール実行時にエラーが出てしまいます。

error: Uncaught (in promise) TypeError: llm.bindTools is not a function
export const llmWithTools = llm.bindTools([calculatorTool]);

下記ページで一時的な対策を見つけました。
自作クラスでChatOllamaを拡張しています。

Bedrock Agent with Tools: This agent requires that the "bind_tools()" method be implemented on the input model #5610

llm.ts
/** ChatOllamaにbindTools()メソッドを追加するクラス拡張 */
import { ChatOllama } from "npm:@langchain/community/chat_models/ollama";

interface ModelInterface {
  model: string;
  baseUrl: string;
  temperature: number;
}

export class ChatOllamaWithTools extends ChatOllama {
  constructor({ model, baseUrl, temperature }: ModelInterface) {
    super({ model, baseUrl, temperature });
  }

  bindTools(tools) {
    // Implement the logic to bind tools to your model
    this.tools = tools;
    return this;
  }

  // Implement other necessary methods from BaseChatModel
}

実際に使う

queryに質問を入力して実行します。

const query = "123456と654321の和は?";
const result = await askAgent(query);
console.log(result);
// => { input: "123456と654321の和は?", output: "123456 + 654321 = 777777" }

質問の形式は

  • 計算の文章題(例: 3と2の和は?, 56かける12は?, 4+6=)
  • 数字を2ついれる
main.ts
/** agentを使うメイン */

import { AgentExecutor, createToolCallingAgent } from "npm:langchain/agents";

import { ChatOllamaWithTools } from "./llm.ts";
import { calculatorTool } from "./calculator.ts";
import { ChatPromptTemplate } from "npm:@langchain/core/prompts";

interface AgentInterface {
  input: string;
  output: string;
}

const askAgent = async (input: string): Promise<AgentInterface> => {
  // Ollamaの代わりに OpenAIを使う場合
  //
  // import { ChatOpenAI } from "npm:@langchain/openai";
  // const llm = new ChatOpenAI({
  //   model: "gpt-4o-mini",
  //   temperature: 0,
  // });
  const llm = new ChatOllamaWithTools({
    model: "llama3.2:3b",
    baseUrl: "http://192.168.100.123:11434",
    temperature: 0,
  });

  const tools = [calculatorTool];

  const prompt = ChatPromptTemplate.fromMessages([
    ["system", "You are a helpful assistant"],
    ["placeholder", "{chat_history}"],
    ["human", "{input}"],
    ["placeholder", "{agent_scratchpad}"],
  ]);

  // エージェントの作成
  const agent = await createToolCallingAgent({ llm, tools, prompt });
  const agentExecutor = new AgentExecutor({ agent, tools });
  // エージェントに問い合わせ
  const result = await agentExecutor.invoke({ input });
  return result;
};

const main = async () => {
  const query = "123456と654321の和は?";
  const result = await askAgent(query);
  console.log(result);
  // => { input: "123456と654321の和は?", output: "123456 + 654321 = 777777" }
};

await main();

llm.tsからモデルをインポートします。
calculator.tsから計算用のツールをインポートします。

llmにチャットモデルを定義します。
ここではOllamaを使うので、モデル名とURLを適宜変更してください。
モデルはmeta製llamaの最新版を使いました。
URLは外部のホストでなければhttp://localhost:11434で良いです。
localhostの場合はChatOllama内部でデフォルトhttp://localhost:11434を設定してくれるそうです? 未確認。localhostであればbaseUrlは設定しなくても良さそうです。
誤った結果を出さないために、ツールを使うときにはtemperatureを0にしておくのが定石のようです。

OpenAIモデルなどを使うときはコメントアウト内を参照してください。

toolsにインポートしたcalculatorToolを入れます。

プロンプトはChatPromptTemplateを使って適宜変更します。
プロンプトの解説は公式 How to use legacy LangChain Agents (AgentExecutor) を参照してください。

agentを上記で定義したllm, tools, promptにより組み立て、 AgentExecuter.invoke() で質問に答えてもらいます。

まとめ

この記事では、以下の内容を解説しました:

  1. LangChainを使用して四則演算を行うカスタムツールの作成方法
  2. ChatOllamaをツール機能ありで使用するための拡張クラスの実装
  3. エージェントを構築して自然言語での計算問題を解決する方法

主なポイント:

  • zodによる型安全な計算ツールの定義
  • ChatOllamaの制限を回避するためのカスタムクラス実装
  • ローカルでLLMを動かすためのOllama環境の準備

これにより、OpenAIのAPIに依存せず、ローカル環境でカスタムツールを扱うLLMを実現できます。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?