Show HN: Rust를 위한 사전 학습된 모델용 임베디드 프레임워크, Kalosm
요약
Rust 기반의 임베디드 프레임워크인 Kalosm v0.2.0이 출시되었습니다. 이 릴리스는 태스크(Tasks)와 에이전트(Agents)를 실행, 평가 및 개선하는 데 필요한 다양한 유틸리티를 제공합니다. 주요 기능으로는 프롬프트 자동 튜닝, 정규 표현식 검증, Surreal Database 통합, RAG 개선 등이 포함됩니다. 제공된 예제 코드는 Kalosm을 사용하여 LLM(Phi, Llama 등) 기반의 태스크를 정의하고, 제약 조건(constraints)과 예시(examples)를 통해 모델 출력을 구조화하고 신뢰성을 높이는 방법을 보여줍니다.
핵심 포인트
- Kalosm은 Rust로 작성된 임베디드 프레임워크로, LLM을 활용한 애플리케이션 개발에 초점을 맞춥니다.
- Tasks와 Agents 기능을 통해 복잡한 AI 워크플로우를 정의하고 실행할 수 있습니다.
- 정규 표현식(Regex) 기반의 제약 조건과 예시 제공 기능으로 모델 출력을 구조화하고 검증하여 신뢰성을 높입니다.
- 세션 재사용을 통해 반복적인 태스크 실행 시 프로세스 속도를 크게 향상시킬 수 있습니다.
Kalosm v0.2.0의 출시를 발표하게 되어 기쁩니다! 이번 릴리스에는 다음과 같은 다양한 새로운 기능, 개선 사항 및 버그 수정이 포함되어 있습니다:
- 태스크 (Tasks) 및 에이전트 (Agents)
- 태스크 평가 (Task Evaluation)
- 프롬프트 자동 튜닝 (Prompt Auto-Tuning)
- 정규 표현식 검증 (Regex Validation)
- Surreal Database 통합
- RAG 개선
- 성능 개선
- 새로운 모델들
Kalosm에는 이제 태스크와 에이전트를 실행, 평가 및 개선하기 위한 유틸리티가 포함되어 있습니다.
새로운 기능을 보여주기 위해 간단한 태스크와 에이전트를 만들어 보겠습니다.
use kalosm::language::*;
#[tokio::main]
async fn main() {
// llama, mistral 또는 phi 모델과 함께 태스크를 사용할 수 있습니다.
let llm = Phi::new_chat();
// 이제 태스크를 생성할 수 있습니다. 문장에서 키워드를 식별하는 간단한 어시스턴트를 만들 것입니다.
let task = Task::builder(&llm, "You are an assistant who identifies keywords in a sentence. When identifying keywords, you will output the keywords in a comma-separated list.")
// 선택적으로 태스크에 제약 조건 (constraints)을 추가할 수 있습니다.
// 각각 2-15자의 키워드를 2-5개 수락합니다.
.with_constraints(RegexParser::new(r#"The keywords are:([ a-z]{2,15},){2,5}"#).unwrap())
/// 또한 에이전트가 수학 문제를 해결하는 방법을 배우는 데 도움이 되도록 태스크에 예시를 추가할 수도 있습니다.
.with_example("What is the weather like in New York?", "The keywords are: weather, new york, ")
.with_example("What is the capital of France?", "The keywords are: capital, france, ")
.build();
// 태스크를 처음 사용할 때는 모델과 프롬프트를 로드하므로 시간이 약간 더 걸릴 수 있습니다.
task.run("What is the temperature in Chicago?", &llm)
.await
.unwrap()
.to_std_out()
.await
.unwrap();
}
또는 더 복잡한 제약 조건을 사용할 수도 있습니다.
// 먼저, Llama 인스턴스를 생성해야 합니다. 이 예제에서는 기본 채팅 모델 (open chat)을 사용하겠습니다. Task는 채팅 및 텍스트 생성 모델과 함께 작동합니다.
let mut llm = Llama::new_chat();
// 다음으로 (선택 사항), Task에 대한 제약 조건 (constraints)을 정의할 수 있습니다. 이 예제에서는 Task의 출력을 검증하기 위해 정규 표현식 (regex)을 사용하겠습니다.
let constraints = RegexParser::new(r"(Step \d: \d+ [+-*/] \d+ = \d+\n){1,3}Output: \d+").unwrap();
// 이제 Task를 생성할 수 있습니다. 간단한 수학 문제 풀이 Task를 생성하겠습니다.
let task = Task::builder(&llm, "You are an assistant who solves math problems. When solving problems, you will always solve problems step by step with one step per line. Once you have solved the problem, you will output the result in the format 'Output: <result>'.")
.with_constraints(constraints)
// 에이전트가 수학 문제를 푸는 방법을 학습할 수 있도록 Task에 예시를 추가할 수도 있습니다.
.with_example("What is 1 + 2?", "Step 1: 1 + 2 = 3\nOutput: 3")
.with_example("What is 3 + 4?", "Step 1: 3 + 4 = 7\nOutput: 7")
.with_example("What is (4 + 8) / 3?", "Step 1: 4 + 8 = 12\nStep 2: 12 / 3 = 4\nOutput: 4")
.build();
// Task를 처음 사용할 때는 모델과 프롬프트 (prompt)를 로드하므로 시간이 조금 더 걸립니다.
task.run("What is 2 + 2?", &llm)
.await
.unwrap()
.to_std_out()
.await
.unwrap();
Task는 실행 간의 세션 (session)을 효율적으로 재사용할 수 있으며, 이는 프로세스 속도를 크게 향상시킬 수 있습니다:
question 1 Step 1: 2 + 2 = 4 Output: 4. first question took: 18.371939709s
question 2 Step 1: 4 + 4 = 8 Output: 8 second question took: 5.723529959s
question 3 Step 1: 7 + 5 = 12 Step 2: 12 / 2 = 6 Output: 6 third question took: 9.303650625s
세 번째 질문은 더 복잡한 계산을 필요로 했고 문제를 풀기 위해 더 많은 토큰 (tokens)이 소모되었지만, 질문을 해결하는 데 걸린 시간은 여전히 첫 번째 질문보다 현저히 빨랐습니다. 첫 번째 질문의 세션이 두 번째와 세 번째 질문에 재사용되었기 때문에 두 번째와 세 번째 질문이 더 빠르게 실행되었습니다.
평가 추상화 (Evaluation Abstraction): 향상된 기능을 제공하는 평가 추상화 (evaluation abstraction)를 도입합니다. (#113)
+-----------------+-------+ | 통계치 (Statistic) | 값 (Value) | +=========================+ | 평균 (Mean) | 0.75 | |-----------------+-------| | 중앙값 (Median) | 0.77 | |-----------------+-------| | 최소값 (Min) | 0.49 | |-----------------+-------| | 최대값 (Max) | 0.94 | |-----------------+-------| | 25 백분위수 (25th Percentile) | 0.67 | |-----------------+-------| | 75 백분위수 (75th Percentile) | 0.81 | +-----------------+-------+ +------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------+ | 예상 출력 (Expected Output) | 실제 출력 (Actual Output) | 점수 (Score) | +===========================================================================================================================================================================================================+ | What are Floneum plugins? | What is designed to support an expanding ecosystem of plugins? | 0.49 (low outlier) | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | What is supervised learning in machine learning? | What is required for machine learning models in order to make accurate predictions? | 0.50 (low outlier) | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | What distinguishes open-source software from proprietary software? | What type of access does open source software provide for its users?
| 0.64 | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | 블록체인은 암호화폐의 보안에 어떻게 기여하나요? | 보안 측면에서 암호화폐가 전통적인 통화보다 갖는 장점은 무엇인가요? | 0.65 | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | ChatGPT를 사용하는 것의 트레이드오프 (tradeoffs)는 무엇인가요? | 특정 상황에서 ChatGPT를 사용하는 것이 어떻게 어려워질 수 있는지에 대한 예시는 무엇인가요? | 0.65 | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | DevOps, 지속적 통합 (CI) 및 지속적 제공 (CD) 사이의 관계는 무엇인가요? | DevOps의 주요 목표는 무엇인가요? | 0.67 | |------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------| | ... 18개 더 보기 | | 0.80 (평균) | +------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+--------------------+ | 점수 히스토그램 (Score Histogram) | | 0.00 - 0.10: | | 0.10 - 0.20: | | 0.20 - 0.30: | | 0.30 - 0.40: | | 0.40 - 0.50: * | | 0.50 - 0.60: * | | 0.60 - 0.70: ****** | | 0.70 - 0.80: ********* | | 0.80 - 0.90: ***** | | 0.90 - 1.00: ** |
프롬프트 자동 튜닝 (Prompt Auto-Tuning): 이제 더 나은 성능을 위해 프롬프트를 자동으로 튜닝할 수 있습니다. (#132)
예시와 함께 새로운 프롬프트 자동 튜닝 (Prompt Auto-Tuning) 기능을 살펴보겠습니다. RAG (Retrieval-Augmented Generation) 개선 사항의 일환으로, Kalosm에는 텍스트의 의미를 기반으로 유사한 문서를 찾을 수 있는 임베딩 모델 (Embedding Model)을 위해 해당 텍스트에 대한 가설 질문을 생성하는 태스크 (Task)가 포함되어 있습니다. 우리는 PromptAnnealer를 사용하여 해당 태스크에 가장 적합한 예시를 찾도록 이 태스크를 튜닝할 수 있습니다.
const EXAMPLES: &[(&str, &str)] = &[ ("An example input to the task", "An example output to the task"), // ...더 많은 예시들 ]; let llm = Phi::v2(); const PREFIX: &str = "Questions that are answered by the previous text: "; const QUESTION_STARTERS: [&str; 9] = [ "Who", "What", "When", "Where", "Why", "How", "Which", "Whom", "Whose", ]; let constraints = LiteralParser::new(PREFIX).then( IndexParser::new( QUESTION_STARTERS .iter() .copied() .map(LiteralParser::new) .collect::<Vec<_>>(), ) .then(StopOn::new("?").filter_characters( |c| matches!(c, ' ' | '?' | 'a'..='z' | 'A'..='Z' | '0'..='9' | ','), )) .repeat(1..=5), ); let task = Task::builder("You generate hypothetical questions that may be answered by the given text. The questions restate any information necessary to understand the question") .with_constraints(constraints); let mut annealing = kalosm::PromptAnnealer::builder(&llm, EXAMPLES, task) .with_initial_temperature(0.6) .with_initial_choice_range(1..4) .build() .await; let result = annealing.run().await; println!("Result: {:?}", result);
다음은 프롬프트 어닐러 (Prompt Annealer)가 해당 태스크를 위해 찾아낸 최적의 예시 세트입니다:
| 입력 (Input) | 출력 (Output) |
|---|---|
| 전통적인 데이터베이스가 고정된 스키마 (Schema)에 의존하는 반면, MongoDB와 같은 NoSQL 데이터베이스는 유연한 구조를 제공하여 데이터를 더 동적인 방식으로 저장하고 검색할 수 있게 해줍니다. 이러한 유연성은 데이터 요구 사항이 계속 진화하는 애플리케이션에 특히 유익합니다. | "MongoDB는 전통적인 데이터베이스와 어떻게 다른가요? |
| 블록체인 기술은 암호화폐를 넘어 스마트 계약 (Smart contracts)과 같은 애플리케이션을 위해 탐구되고 있습니다. 스마트 계약은 계약 조건이 코드에 직접 작성된 자기 실행형 계약입니다. | "블록체인 기술이 스마트 계약 개념에서 어떻게 활용되나요? |
이 두 예시를 태스크에 입력하면 다른 모든 예시들에 대해 0.71의 유사도 점수 (Similarity score)를 달성하는 반면, 태스크에서 무작위로 두 예시를 선택하면 다른 모든 예시들에 대해 0.62의 유사도 점수만을 달성합니다.
kalosm의 초기 출시 버전에서 받은 피드백 중 하나는 제약 조건이 있는 생성 (Constrained generation)을 위한 제약 조건 (Constraints)이 너무 복잡하다는 것이었습니다. Kalosm에서 제약 조건은 두 가지 목적을 수행합니다:
- 검증 (Validation): 제약 조건은 모델의 출력을 검증하는 데 사용될 수 있습니다. 모델은 제약 조건에 의해 파싱 (Parse)될 수 있는 텍스트만 출력합니다. 이를 통해 모델의 출력이 사용자가 기대하는 형식에 있는지 확인할 수 있습니다.
예를 들어, 모델의 응답이 항상 모델을 안내하는 접두사 (Prefix)로 시작하도록 강제하고 싶을 수 있습니다:
use kalosm::language::*;
#[tokio::main]
async fn main() {
let llm = Llama::default();
let prompt = "Generate a list of 10 words you use to describe yourself: ";
let validator = LiteralParser::new("(Responding as a pirate) ").then(OneLine);
let stream = llm.stream_structured_text(prompt, validator).await.unwrap();
stream.to_std_out().await.unwrap();
}
- 파싱 (Parsing): 제약 조건은 모델의 출력을 파싱하는 데 사용될 수 있습니다. 이는 검증과 파싱을 위한 별도의 로직을 작성하지 않고도 LLM으로부터 특정 구조를 생성하고 싶을 때 매우 유용할 수 있습니다.
예를 들어, 10개의 숫자 리스트를 생성하고 싶을 수 있습니다:
use kalosm::language::*;
#[tokio::main]
async fn main() {
let llm = Llama::default();
let prompt = "Prime numbers: ";
let validator = <[f32; 10] as HasParser>::new_parser();
let words = llm.stream_structured_text(prompt, validator).await.unwrap();
let result: [f32; 10] = words.result().await.unwrap();
println!("Prime numbers: {:?}", result);
}
만약 모델의 출력값만을 검증(validate)하면 되는 경우라면, 기존의 제약 조건(constraints)이 필요 이상으로 복잡할 수 있습니다. 이번 릴리스에서는 정규 표현식 검증 (regex validation) 지원을 추가했습니다. 이를 통해 파싱 (parsing)을 직접 처리하지 않고도 모델의 출력을 더 쉽게 검증할 수 있습니다:
use kalosm::language::*;
#[tokio::main]
async fn main() {
let llm = Llama::default();
let prompt = "Generate a list of 10 words you use to describe yourself: ";
let validator = RegexParser::new(r"\(Responding as a pirate\) ([a-z]{1,10}, ){10}").unwrap();
let stream = llm.stream_structured_text(prompt, validator).await.unwrap();
stream.to_std_out().await.unwrap();
}
벡터 데이터베이스 (Vector databases)는 LLM과 결합될 때 매우 유용할 수 있습니다. 벡터 데이터베이스는 단순히 사용된 단어가 아니라 텍스트의 의미를 기반으로 유사한 문서를 저장하고 검색하는 데 사용될 수 있습니다. 하지만 벡터 데이터베이스는 매우 제한된 수의 유스케이스 (use cases)만을 처리할 수 있습니다. 이번 릴리스에서는 더 전통적인 데이터베이스 유스케이스를 위해 Surreal DB 지원을 추가했습니다. Surreal DB는 애플리케이션에 임베드 (embedded)될 수 있으며, 로컬뿐만 아니라 네트워크를 통해 데이터를 저장하고 검색하는 데 사용될 수 있습니다.
Kalosm 0.2를 사용하면 벡터로 인덱싱 (indexed)되는 Surreal DB 내의 테이블을 생성할 수 있습니다. 그런 다음 테이블에 문서(또는 기타 임베딩 (embeddings))를 삽입하고, 텍스트의 의미를 기반으로 유사한 문서를 찾기 위해 테이블을 쿼리 (query)할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 HN AI Engineering의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기