コンテンツにスキップ

LLM

wippy/llm モジュールは、複数のプロバイダー(OpenAI、Anthropic、Google、ローカルモデル)の大規模言語モデルを操作するための統一インターフェースを提供します。テキスト生成、ツール呼び出し、構造化出力、エンベディング、ストリーミングに対応しています。

プロジェクトにモジュールを追加します:

Terminal window
wippy add wippy/llm
wippy install

_index.yaml で依存関係を宣言します。LLM モジュールには環境ストレージ(APIキー用)とプロセスホストが必要です:

version: "1.0"
namespace: app
entries:
- name: os_env
kind: env.storage.os
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: dep.llm
kind: ns.dependency
component: wippy/llm
version: "*"
parameters:
- name: env_storage
value: app:os_env
- name: process_host
value: app:processes

env.storage.os エントリは、OS 環境変数を LLM プロバイダーに公開します。APIキーを環境変数として設定してください(例: OPENAI_API_KEYANTHROPIC_API_KEY)。

エントリに llm ライブラリをインポートし、generate() を呼び出します:

entries:
- name: ask
kind: function.lua
source: file://ask.lua
method: handler
imports:
llm: wippy.llm:llm
local llm = require("llm")
local function handler()
local response, err = llm.generate("What are the three laws of robotics?", {
model = "gpt-4o"
})
if err then
return nil, err
end
return response.result
end
return { handler = handler }

generate() の第1引数には、文字列プロンプト、プロンプトビルダー、またはメッセージのテーブルを指定できます。第2引数はオプションテーブルです。

オプション説明
modelstringモデル名またはクラス(必須)
temperaturenumberランダム性の制御、0-1
max_tokensnumber生成する最大トークン数
top_pnumberNucleus サンプリングパラメータ
top_knumberTop-k フィルタリング
thinking_effortnumber思考の深さ 0-100(思考機能を持つモデル)
toolstableツール定義の配列
tool_choicestring"auto""none""any"、またはツール名
streamtableストリーミング設定: { reply_to, topic, buffer_size }
timeoutnumberリクエストタイムアウト(秒単位、デフォルト 600)
フィールド説明
resultstring生成されたテキストコンテンツ
tokenstableトークン使用量: prompt_tokenscompletion_tokensthinking_tokenstotal_tokens
finish_reasonstring生成が停止した理由: "stop""length""tool_call""filtered""error"
tool_callstable?ツール呼び出しの配列(モデルがツールを呼び出した場合)
metadatatableプロバイダー固有のメタデータ
usage_recordtable?使用量追跡レコード

マルチターン会話や複雑なプロンプトには、プロンプトビルダーを使用します:

imports:
llm: wippy.llm:llm
prompt: wippy.llm:prompt
local llm = require("llm")
local prompt = require("prompt")
local conversation = prompt.new()
conversation:add_system("You are a helpful assistant.")
conversation:add_user("What is the capital of France?")
local response, err = llm.generate(conversation, {
model = "gpt-4o",
temperature = 0.7,
max_tokens = 500
})
メソッド説明
prompt.new()空のビルダーを作成
prompt.with_system(content)システムメッセージ付きでビルダーを作成
:add_system(content, meta?)システムメッセージを追加
:add_user(content, meta?)ユーザーメッセージを追加
:add_assistant(content, meta?)アシスタントメッセージを追加
:add_developer(content, meta?)開発者メッセージを追加
:add_message(role, content_parts, name?, meta?)ロールとコンテンツパーツを指定してメッセージを追加
:add_function_call(name, args, id?)アシスタントからのツール呼び出しを追加
:add_function_result(name, result, id?)ツール実行結果を追加
:add_cache_marker(id?)キャッシュ境界をマーク(Claude モデル)
:get_messages()メッセージ配列を取得
:build()llm.generate() 用の { messages = ... } テーブルを取得
:clone()ビルダーのディープコピーを作成
:clear()すべてのメッセージを削除

すべての add_* メソッドはチェーン呼び出しのためにビルダーを返します。

メッセージを追加してターン間のコンテキストを構築します:

local conversation = prompt.new()
conversation:add_system("You are a helpful assistant.")
-- first turn
conversation:add_user("What is Lua?")
local r1 = llm.generate(conversation, { model = "gpt-4o" })
conversation:add_assistant(r1.result)
-- second turn with full context
conversation:add_user("What makes it different from Python?")
local r2 = llm.generate(conversation, { model = "gpt-4o" })

テキストと画像を1つのメッセージに組み合わせます:

local conversation = prompt.new()
conversation:add_message(prompt.ROLE.USER, {
prompt.text("What's in this image?"),
prompt.image("https://example.com/photo.jpg")
})
関数説明
prompt.text(content)テキストコンテンツパーツ
prompt.image(url, mime_type?)URL からの画像
prompt.image_base64(mime_type, data)Base64 エンコードされた画像
定数
prompt.ROLE.SYSTEM"system"
prompt.ROLE.USER"user"
prompt.ROLE.ASSISTANT"assistant"
prompt.ROLE.DEVELOPER"developer"
prompt.ROLE.FUNCTION_CALL"function_call"
prompt.ROLE.FUNCTION_RESULT"function_result"
prompt.ROLE.CACHE_MARKER"cache_marker"

オリジナルを変更せずにバリエーションを作成するには、ビルダーをクローンします:

local base = prompt.new()
base:add_system("You are a helpful assistant.")
local conv1 = base:clone()
conv1:add_user("What is AI?")
local conv2 = base:clone()
conv2:add_user("What is ML?")

プロセス通信を使用してリアルタイムでレスポンスをストリーミングします。これには process.lua エントリが必要です:

local llm = require("llm")
local TOPIC = "llm_stream"
local function main()
local stream_ch = process.listen(TOPIC)
local response = llm.generate("Write a short story", {
model = "gpt-4o",
stream = {
reply_to = process.pid(),
topic = TOPIC,
},
})
while true do
local chunk, ok = stream_ch:receive()
if not ok then break end
if chunk.type == "chunk" then
io.write(chunk.content)
elseif chunk.type == "thinking" then
io.write(chunk.content)
elseif chunk.type == "error" then
io.print("Error: " .. chunk.error.message)
break
elseif chunk.type == "done" then
break
end
end
process.unlisten(stream_ch)
end
タイプフィールド説明
"chunk"contentテキストコンテンツの断片
"thinking"contentモデルの思考プロセス
"tool_call"name, arguments, idツール呼び出し
"error"error.message, error.typeストリームエラー
"done"metaストリーム完了
ストリーミングには process.lua エントリが必要です。Wippy のプロセス通信システム(process.pid()process.listen())を使用するためです。

インラインスキーマとしてツールを定義し、generate() に渡します:

local llm = require("llm")
local prompt = require("prompt")
local json = require("json")
local tools = {
{
name = "get_weather",
description = "Get current weather for a location",
schema = {
type = "object",
properties = {
location = { type = "string", description = "City name" },
},
required = { "location" },
},
},
}
local conversation = prompt.new()
conversation:add_user("What's the weather in Tokyo?")
local response = llm.generate(conversation, {
model = "gpt-4o",
tools = tools,
tool_choice = "auto",
})
if response.tool_calls and #response.tool_calls > 0 then
for _, tc in ipairs(response.tool_calls) do
-- execute the tool and get a result
local result = { temperature = 22, condition = "sunny" }
-- add the exchange to the conversation
conversation:add_function_call(tc.name, tc.arguments, tc.id)
conversation:add_function_result(tc.name, json.encode(result), tc.id)
end
-- continue generation with tool results
local final = llm.generate(conversation, { model = "gpt-4o" })
print(final.result)
end
フィールド説明
idstring一意の呼び出し識別子
namestringツール名
argumentstableスキーマに一致するパース済み引数
動作
"auto"モデルがツールを使用するかどうかを決定(デフォルト)
"none"ツールを使用しない
"any"少なくとも1つのツールを使用する必要がある
"tool_name"指定されたツールを使用する必要がある

スキーマに一致する検証済み JSON を生成します:

local llm = require("llm")
local schema = {
type = "object",
properties = {
name = { type = "string" },
age = { type = "number" },
hobbies = {
type = "array",
items = { type = "string" },
},
},
required = { "name", "age", "hobbies" },
additionalProperties = false,
}
local response, err = llm.structured_output(schema, "Describe a fictional character", {
model = "gpt-4o",
})
if not err then
print(response.result.name)
print(response.result.age)
end
OpenAI モデルの場合、すべてのプロパティを required 配列に含める必要があります。オプションフィールドにはユニオン型を使用してください: type = {"string", "null"}additionalProperties = false を設定してください。

モデルは meta.type: llm.model を持つレジストリエントリとして定義されます:

entries:
- name: gpt-4o
kind: registry.entry
meta:
name: gpt-4o
type: llm.model
title: GPT-4o
comment: OpenAI's flagship model
capabilities:
- generate
- tool_use
- structured_output
- vision
class:
- balanced
priority: 100
max_tokens: 128000
output_tokens: 16384
pricing:
input: 2.5
output: 10
providers:
- id: wippy.llm.openai:provider
provider_model: gpt-4o
フィールド説明
meta.nameAPI 呼び出しで使用されるモデル識別子
meta.typellm.model である必要があります
meta.capabilities機能リスト: generatetool_usestructured_outputembedthinkingvisioncaching
meta.classクラスメンバーシップ: fastbalancedreasoning など
meta.priorityクラスベース解決の数値優先度(高い方が優先)
max_tokens最大コンテキストウィンドウ
output_tokens最大出力トークン数
pricing100万トークンあたりのコスト: inputoutput
providersid(プロバイダーエントリ)と provider_model(プロバイダー固有のモデル名)を含む配列

ローカルでホストされたモデル(LM Studio、Ollama)の場合、カスタム base_url を持つ別のプロバイダーエントリを定義します:

- name: local_provider
kind: registry.entry
meta:
name: ollama
type: llm.provider
title: Ollama Local
driver:
id: wippy.llm.openai:driver
options:
api_key_env: none
base_url: http://127.0.0.1:11434/v1
- name: local-llama
kind: registry.entry
meta:
name: local-llama
type: llm.model
title: Local Llama
capabilities:
- generate
max_tokens: 4096
output_tokens: 4096
pricing:
input: 0
output: 0
providers:
- id: app:local_provider
provider_model: llama-3.2

モデルは正確な名前、クラス、または明示的なクラスプレフィックスで参照できます:

-- exact model name
llm.generate("Hello", { model = "gpt-4o" })
-- model class (picks highest priority in that class)
llm.generate("Hello", { model = "fast" })
-- explicit class syntax
llm.generate("Hello", { model = "class:reasoning" })

解決順序:

  1. 正確な meta.name で一致
  2. クラス名で一致(meta.priority が最も高いものが優先)
  3. class: プレフィックス付きの場合、そのクラス内のみを検索

実行時に利用可能なモデルとその機能を照会します:

local llm = require("llm")
-- all models
local models = llm.available_models()
-- filter by capability
local tool_models = llm.available_models("tool_use")
local embed_models = llm.available_models("embed")
-- list model classes
local classes = llm.get_classes()
for _, c in ipairs(classes) do
print(c.name .. ": " .. c.title)
end

セマンティック検索用のベクトルエンベディングを生成します:

local llm = require("llm")
-- single text
local response = llm.embed("The quick brown fox", {
model = "text-embedding-3-small",
dimensions = 512,
})
-- response.result is a float array
-- multiple texts
local response = llm.embed({
"First document",
"Second document",
}, { model = "text-embedding-3-small" })
-- response.result is an array of float arrays

作業を送信する前にプロバイダーを確認します。準備状態の確認や軽量なヘルスモニタリングに有用です:

local status, err = llm.status({
model = "gpt-4o",
})
オプション説明
model必須。確認するモデル。
provider_id任意。モデル解決をスキップし、特定のプロバイダーを対象にします。

プロバイダーの StatusResponse を返します(内容はプロバイダーによって異なります)。

エラーは第2戻り値として返されます。エラー時、第1戻り値は nil です:

local response, err = llm.generate("Hello", { model = "gpt-4o" })
if err then
io.print("Error: " .. tostring(err))
return
end
io.print(response.result)
定数説明
llm.ERROR_TYPE.INVALID_REQUEST不正なリクエスト
llm.ERROR_TYPE.AUTHENTICATION無効な API キー
llm.ERROR_TYPE.RATE_LIMITプロバイダーのレート制限超過
llm.ERROR_TYPE.SERVER_ERRORプロバイダーのサーバーエラー
llm.ERROR_TYPE.CONTEXT_LENGTH入力がコンテキストウィンドウを超過
llm.ERROR_TYPE.CONTENT_FILTER安全システムによるコンテンツフィルタリング
llm.ERROR_TYPE.TIMEOUTリクエストタイムアウト
llm.ERROR_TYPE.MODEL_ERROR無効または利用不可のモデル
定数説明
llm.FINISH_REASON.STOP正常完了
llm.FINISH_REASON.LENGTH最大トークン数に到達
llm.FINISH_REASON.CONTENT_FILTERコンテンツがフィルタリングされた
llm.FINISH_REASON.TOOL_CALLモデルがツール呼び出しを行った
llm.FINISH_REASON.ERROR生成中にエラーが発生
定数説明
llm.CAPABILITY.GENERATEテキスト生成
llm.CAPABILITY.TOOL_USEツール/関数呼び出し
llm.CAPABILITY.STRUCTURED_OUTPUTJSON 構造化出力
llm.CAPABILITY.EMBEDベクトルエンベディング
llm.CAPABILITY.THINKING拡張思考
llm.CAPABILITY.VISION画像理解
llm.CAPABILITY.CACHINGプロンプトキャッシュ