콘텐츠로 이동

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_KEY, ANTHROPIC_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()의 첫 번째 인자는 문자열 프롬프트, 프롬프트 빌더, 또는 메시지 테이블이 될 수 있습니다. 두 번째 인자는 옵션 테이블입니다.

옵션타입설명
modelstring모델 이름 또는 클래스 (필수)
temperaturenumber무작위성 제어, 0-1
max_tokensnumber생성할 최대 토큰 수
top_pnumber핵 샘플링 파라미터
top_knumberTop-k 필터링
thinking_effortnumber사고 깊이 0-100 (사고 기능이 있는 모델)
toolstable도구 정의 배열
tool_choicestring"auto", "none", "any", 또는 도구 이름
streamtable스트리밍 설정: { reply_to, topic, buffer_size }
timeoutnumber요청 타임아웃(초 단위, 기본값 600)
필드타입설명
resultstring생성된 텍스트 콘텐츠
tokenstable토큰 사용량: prompt_tokens, completion_tokens, thinking_tokens, total_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" })

단일 메시지에서 텍스트와 이미지를 결합합니다:

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스트림 완료
스트리밍은 Wippy의 프로세스 통신 시스템(process.pid(), process.listen())을 사용하므로 process.lua 엔트리가 필요합니다.

인라인 스키마로 도구를 정의하고 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"최소 하나의 도구를 사용해야 합니다
"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기능 목록: generate, tool_use, structured_output, embed, thinking, vision, caching
meta.class클래스 소속: fast, balanced, reasoning
meta.priority클래스 기반 해석을 위한 숫자 우선순위 (높을수록 우선)
max_tokens최대 컨텍스트 윈도우
output_tokens최대 출력 토큰 수
pricing백만 토큰당 비용: input, output
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를 반환합니다(내용은 프로바이더에 따라 다릅니다).

오류는 두 번째 반환 값으로 반환됩니다. 오류 발생 시 첫 번째 반환 값은 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프롬프트 캐싱