プログラミング
記事内に商品プロモーションを含む場合があります

LMStudio APIを試す|Go言語でローカルLLMを呼び出す方法

Aru

LMStudioは、ローカルLLM(大規模言語モデル)を手軽に利用できる便利ツールで、OpenAI互換のAPI経由でのアクセスが可能です。この機能を活用することで、Visual Studio Codeなどのエディタや、他のアプリケーションからもローカルLLMを呼び出すことができます。この記事では、LMStudioのAPI機能をGo言語から利用する方法を詳しく解説します。

はじめに

LMStudioは、ローカル環境で大規模言語モデル(LLM)を手軽に試すことができるツールです。LMStudioを使うことで、最新のローカルLLMを簡単に検索してダウンロードし、すぐに試すことができます。

私も新しいモデルが登場するたびに、すぐにダウンロードして試しています。バージョンアップも頻繁に行われ、新しいモデルへの対応も非常に早いです。

最近では、ローカルLLMでも十分に会話や応答が可能になり、実用性が高まっています。クラウドベースのLLMを利用するのも選択肢としてはありますが、会話内容が外部に送信されない点は、特にプライバシーを重視する場面では大きな魅力です。

さらに、LMStudioは、OpenAIのAPIに似た言語モデルサーバーの機能も備えており、これを利用することで、外部のプログラムからLMStudioで読み込んだローカルLLMにAPI経由でアクセスすることができます。

今回は、LMStudioにロードしたgemma-3-4b-itを、Go言語から呼び出して使ってみましたので、その手順を解説します。

プログラム

LMStudioが用意しているサーバーは、OpenAI互換のAPIでアクセスすることが可能です。今回は、「ChatGPT API(gpt-3.5-turbo)を GoLang から使ってみる」という記事を参考にして、LMStudioへのアクセスを実装してみました。

主な変更はapiKeyが必要ないので削除したこと、Markdown形式の出力に対応させたことです

LMStudio側の設定

LMStudio側では、モデルをロードして、右側の開発者タブで、Status:をRunningに設定するだけです。これでサーバーが起動します。

この画面にアクセスのURLが書かれています。デフォルトでは以下になります。

http://127.0.0.1:1234

パッケージのインポート

Markdown表示を可能にするために、github.com/charmbracelet/glamourをインポートしています。Go言語のパッケージインポートについては以下の記事を参考にしてください。

あわせて読みたい
Go言語で自作パッケージをGithubに公開・利用する手順を解説
Go言語で自作パッケージをGithubに公開・利用する手順を解説

基本的には、以下のコマンドでモジュールを取得すればOKです。

go get github.com/charmbracelet/glamour

うまくいかない場合は、go mod init xxx後に、go mod tidyを試してみてください。

プログラムリスト

以下は、プログラムリストになります。

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"

	"github.com/charmbracelet/glamour"
)

// JSONフォーマットを定義
type OpenaiRequest struct {
	Model    string    `json:"model"`
	Messages []Message `json:"messages"`
}

type OpenaiResponse struct {
	ID      string   `json:"id"`
	Object  string   `json:"object"`
	Created int      `json:"created"`
	Choices []Choice `json:"choices"`
	Usages  Usage    `json:"usage"`
}

type Choice struct {
	Index        int     `json:"index"`
	Messages     Message `json:"message"`
	FinishReason string  `json:"finish_reason"`
}

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type Usage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	TotalTokens      int `json:"total_tokens"`
}

// LMStudioのURLを設定
const openaiURL = "http://localhost:1234/v1/chat/completions"

// APIによるやり取りのためのスライス
var messages []Message

// APIを使ってアクセスする関数
func getOpenAIResponse(apiKey string) OpenaiResponse {
	requestBody := OpenaiRequest{
		Model:    "gemma-3-4b-it",
		Messages: messages,
	}

	requestJSON, _ := json.Marshal(requestBody)

	req, err := http.NewRequest("POST", openaiURL, bytes.NewBuffer(requestJSON))
	if err != nil {
		panic(err)
	}

	req.Header.Set("Content-Type", "application/json")
	// req.Header.Set("Authorization", "Bearer "+apiKey)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			panic(err)
		}
	}(resp.Body)

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	// fmt.Println(string(body))
	var response OpenaiResponse
	err = json.Unmarshal(body, &response)
	if err != nil {
		println("Error: ", err.Error())
		return OpenaiResponse{}
	}

	messages = append(messages, Message{
		Role:    "assistant",
		Content: response.Choices[0].Messages.Content,
	})

	return response
}

// main関数
func main() {
	apiKey := "YOUR_API_KEY" // openAIに繋ぐときはAPI_KEYを入れる。

	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("Ask a question: ")
		question, _ := reader.ReadString('\n')
		question = strings.TrimSpace(question)

		if question == "exit" {
			break
		}

		messages = append(messages, Message{
			Role:    "user",
			Content: question,
		})

		// fmt.Println(messages)

		response := getOpenAIResponse(apiKey)

		out, err := glamour.Render(response.Choices[0].Messages.Content, "dark")
		if err != nil {
			panic(err.Error())
		}

		fmt.Println(out)
	}
}

以下、主要な部分の解説です

JSONフォーマットを定義

サーバーとの通信はJSON形式で行うため、そのフォーマットを定義しています。Goでは、構造体のフィールド名の先頭が大文字でなければエラーが発生しますので、注意が必要です。

OpenaiRequest:

  • Model: 使用する言語モデルを指定します(例:gemma-3-4b-it)。
  • Messages: 送信するメッセージの配列。ユーザーとアシスタントの対話履歴を格納します。

OpenaiResponse:

  • APIから返されるレスポンスの形式。レスポンスには、選択肢やメッセージの内容が含まれます

Choice:

  • レスポンス内での選択肢の詳細。メッセージ内容と終了理由を格納します

Message:

  • 会話の1回分のメッセージ。Role(役割)とContent(内容)を含んでいます

Usage:

  • APIのトークン使用状況を管理します。PromptTokensCompletionTokensが含まれます

この部分は、基本的にOpenAIのAPIに準拠したフォーマットを設定してているだけです。

getOpenAIResponse

OpenAI互換のAPIを使ってLMStudioと通信する部分です。ユーザーとの対話ログからメッセージを作成し、サーバーに送信しています

リクエストの作成:

  • OpenaiRequest構造体に、使用するモデル名と送信するメッセージを設定します
  • その後、json.MarshalでJSON形式に変換し、POSTリクエストとして送信します

HTTPリクエストの処理:

  • http.NewRequestを使用して、POSTリクエストを作成します
  • ヘッダーにはContent-Typeapplication/jsonに設定し、ボディにJSONデータを含めます

レスポンスの受信と処理:

  • サーバーからのレスポンスを読み取り、JSONとしてデコードします
  • 取得した応答内容を次のメッセージに追加します

応答した内容を追加する点がポイントです。APIを経由して会話を行う場合、それまでの会話は記録されていないので、これまでの会話を全て含めて送信する必要があります

main

この部分は、実際の会話の処理を行う部分です。無限ループを使用してユーザーからの入力を受け取り、その都度APIにリクエストを送信して応答を得ます。得られた応答は、glamourライブラリを使ってMarkdown形式で整形し、表示します。

その他

apiKeyの部分は、コメントアウトで残しています。apiKeyの部分を追加し、URLを変更すればOpenAIのモデルへアクセスさせることも可能です。ローカルLLMを使って、初期のデバッグを行い、ある程度完成してからOpenAIなどに接続といった開発フローを行うことを想定して残しました。

実行方法

実行は以下のコマンドでバイナリを作成してからchatを実行します。

go build -o chat main.go 
./chat

または、go runを使って実行します。

go run main.go

実行結果

実行すると、Ask a questionと聞いてきますので、ここに質問を入力します。すると、しばらく待って回答が返ってきます。

終了する場合は、CTRL-Cでプログラムを中断させます。

まとめ

LMStudioをサーバーとして起動して、Go言語でアクセスする方法について解説しました。やってみると案外簡単です。自分のプログラムでLLMを利用したサービスをちょっと作る場合などに便利かもと思いました。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました