2025년 1월 9일 목요일

최근 SLM(Small Language Model) 모델 성능 테스트 사용기

이 글은 최근 SLM(Small Language Model) 모델 성능 테스트 사용기를 간략히 정리한 것이다.

테스트를 위해, ollama를 설치한 후, 터미널에서 다음 명령을 입력해 각각 적합한 SLM 모델을 다운로드 받는다.
ollama run gemma2

SLM 실행 모습
정보 생성 결과 예시

사용한 SLM 모델 중 유명한 gemma-2, llama-3, phi4를 테스트해보았다. 
  • gemma-2: 실행 속도가 매우 빠르다. 맥락에 따른 정보 생성 성능이 뛰어나다.
  • llama-3: 실행 속도는 중간이다. 생성 품질은 뛰어나다.
  • phi4: 실행 속도는 중상이다. 생성 품질도 뛰어나다.
  • llama-3.2:vision: 멀티모달 기능이 뛰어나다. 다른 것은 보통 수준이다.
다만, 이런 SLM은 4K 이내 프롬프트 크기 입력 등의 한계가 있다. 

레퍼런스

2025년 1월 4일 토요일

Gradio 와 LangChain 을 이용한 간단한 챗봇 개발하기

이 글은 손쉽게 웹 어플리케이션을 개발할 수 있는 Gradio 와 LangChain 을 이용해 간단한 챗봇을 개발해 본다. 
Gradio 적용 예시(YOLO. CLIP. AEC 건설 분야)

환경 설치
터미널에서 다음 라이브러리를 설치한다.
pip install gradio langchain

코딩
다음을 코딩하고 실행한다. 
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage, SystemMessage
import os
import gradio as gr

os.environ["OPENAI_API_KEY"] = "<input your openai key"  # API 키 설정

llm = ChatOpenAI(temperature=1.0, model='gpt-4o-mini')  

# LLM 응답 처리
def response(message, history, additional_input_info):
    history_langchain_format = []
    history_langchain_format.append(SystemMessage(content= additional_input_info))
    for human, ai in history:
            history_langchain_format.append(HumanMessage(content=human))
            history_langchain_format.append(AIMessage(content=ai))
    history_langchain_format.append(HumanMessage(content=message))
    gpt_response = llm(history_langchain_format)
    return gpt_response.content

# 인터페이스 생성
gr.ChatInterface(
    fn=response,   # LLM 응답처리 콜백함수 설정
    textbox=gr.Textbox(placeholder="Talk", container=False, scale=7),
    chatbot=gr.Chatbot(height=1000),
    title="ChatBot",
    description="I'm a chatbot that can chat with you. I'm lovely chatbot.",
    theme="soft",
    examples=[["Hi"], ["I'm good"], ["What's your name?"]],
    retry_btn="resend",
    undo_btn="delete❌",
    clear_btn="delete all💫",
    additional_inputs=[
        gr.Textbox("", label="Input System Prompt", placeholder="I'm chatbot.")
    ]
).launch()

실행 결과
성공한 결과는 다음과 같다. 

마무리
Gradio는 2019년에 Abubakar Abid에 의해 개발된 오픈소스 파이썬 라이브러리로, 머신러닝 모델을 손쉽게 웹 애플리케이션 형태로 배포하고 공유할 수 있도록 설계되었다.021년, Gradio는 AI 커뮤니티에서 널리 알려진 Hugging Face에 인수되었으며, 이를 통해 더욱 발전하고 있다.근에는 Gradio 5.0 버전이 출시되어, 월 사용자 수 200만 명과 47만 개 이상의 애플리케이션을 보유한 AI 개발 생태계의 핵심 도구로 자리 잡았다.
레퍼런스

2025년 1월 3일 금요일

LangChain 작동 메커니즘 분석기

이 글은 LangChain(랭체인) 작동 메커니즘을 분석한 것이다. 

랭체인은 제한된 LLM 입력 프롬프트 크기 한계 내에서 LLM의 추론 기능을 잘 활용하기 위해 개발된 라이브러리이다. LLM 환각 현상 등을 개선하기 위해 RAG 기법을 적극 개발하고 있는데, 이로인해, 다음과 같이 다양한 벤더사의 LLM, vector database, chunking 등을 지원해야 했다. 그러므로, 객체지향적으로 설계되어 추상화 레이어가 매우 깊고 복잡한 면이 있다.
다음은 주요 핵심 기능의 동작 방식을 설명한다.

도구(Tools)
LangChain에서 도구는 에이전트가 자연어 처리 이상의 작업을 수행할 수 있도록 제공되는 기능이나 API이다. 이 코드는 이를 명확히 정의하고 에이전트가 사용할 수 있도록 등록하고 있다.

코드에서의 예시:
functions = {
    "getWeather": {
        "name": "getWeather",
        "description": "Realtime info about the weather.",
        "parameters": CITY_PARAM,
        "function": partial(get_request, "/api/weather")
    },
    "getPopulation": {
        "name": "getPopulation",
        "description": "Realtime info about the population.",
        "parameters": CITY_PARAM,
        "function": partial(get_request, "/api/population")
    },
}
이름(name): 각 도구를 고유하게 식별할 수 있는 이름이다.
설명(description): 도구가 수행하는 작업을 설명한다.
매개변수(parameters): 도구가 받는 입력의 JSON 스키마를 정의한다.
함수(function): 도구가 실제로 실행할 작업이다. 여기서는 partial을 사용해 API 엔드포인트를 바인딩하였다.
LangChain과의 대응: LangChain에서 Tool 클래스 또는 tool 데코레이터로 도구를 정의하며, 위 코드와 유사한 방식으로 도구를 등록한다.

메모리(Memory)
메모리는 에이전트가 대화의 문맥을 유지하도록 돕는다. 제공된 코드에서는 messages 리스트가 이러한 역할을 한다.
코드에서의 예시:
messages = []
messages.append({"role": "system", "content": "You are an AI assistant with multiple tools..."})
messages 리스트는 대화 이력을 저장하며, 시스템 프롬프트, 사용자 입력, 모델 응답을 순차적으로 기록한다.
LangChain과의 대응: LangChain에서는 ConversationBufferMemory, ConversationSummaryMemory와 같은 메모리 모듈을 사용해 대화 이력을 관리한다.

에이전트 루프(Agent Loop)
에이전트 루프는 모델의 응답을 처리하고, 필요한 경우 도구를 호출하며, 사용자가 원하는 결과를 도출할 때까지 반복한다.
코드에서의 예시:
iter = 0
while iter < MAX_ITERATIONS:
    response = openai.ChatCompletion.create(...)
    message from response
    if message.get("function_call"):
        function_name = message["function_call"]["name"]
        results = call_function(function_name, function_input)
        messages.append({"role": "function", "name": function_name, "content": results})
        iter += 1
    else:
        return message['content']

모델이 도구 호출 여부를 판단하며, 호출한 결과를 메시지 이력에 추가한 후 루프를 반복한다.
LangChain과의 대응: LangChain의 AgentExecutor는 에이전트의 도구 호출 및 반복 작업을 자동으로 관리한다.

확장된 구성 요소
동적 도구 호출
도구를 동적으로 호출하기 위해 도구 이름과 매개변수를 사용하여 실행하는 로직이 포함되어 있다.
코드에서의 예시:
def call_function(name, params):
    f = functions[name]
    return f['function'](json.loads(params))
도구 이름과 입력 데이터를 기반으로 적절한 함수를 실행한다.
LangChain과의 대응: LangChain은 등록된 도구의 run 메서드를 사용해 자동으로 호출하며, 이를 에이전트 실행 플로우에 통합한다.

반복적 추론
에이전트는 도구 결과를 기반으로 추가적인 작업을 수행하며, 원하는 결과를 얻기 위해 여러 차례 도구를 호출한다.
코드에서의 예시:
while 루프에서 반복적으로 도구를 호출하거나 모델의 응답을 처리한다.
LangChain과의 대응: LangChain은 이러한 작업을 지원하는 에이전트 프레임워크를 제공하며, react 기반 에이전트가 이러한 반복적 추론을 처리한다.

LangChain의 주요 기능과 비교
a. 도구 통합
제공된 코드에서 functions와 get_functions_for_model은 LangChain의 도구 등록 및 사양 정의와 유사하다.
b. 메모리 관리
대화 이력을 messages 리스트로 관리하는 방식은 LangChain의 메모리 모듈과 직접 대응된다.
c. 에이전트 루프
제공된 코드의 루프는 LangChain의 AgentExecutor와 구조적으로 유사하며, 사용자 정의 작업과 도구 호출을 통합한다.
d. 프롬프트 설계
시스템 메시지와 도구 사양이 포함된 구조는 LangChain의 프롬프트 엔지니어링 방식과 맞닿아 있다.

마무리
앞의 설명은 랭체인 도구 통합, 메모리 관리, 에이전트 루프의 구현 방식을 보여준다. LangChain은 이와 같은 구조를 확장 가능하고 재사용 가능한 방식으로 제공하며, 코드 작성과 관리의 복잡성을 줄이는 데 도움을 준다. LangChain을 사용하면 이러한 구성 요소를 더 간결하게 관리할 수 있으며, 다양한 에이전트 유형과 도구를 손쉽게 통합할 수 있다. 다만, 매우 복잡한 추상화와 불명확한 에이전트의 동작 메커니즘 등은 개선이 필요해 보인다. 최근 랭체인보다 간략화된 RAG 라이브러리가 오픈되고 있어, 프로젝트 목적에 따라 이를 고려해 볼 필요도 있다.

레퍼런스

2025년 1월 2일 목요일

Polygonjs 기반 인터랙티브 3D 앱 만들기

요즘 디지털 환경에서 인터랙티브 3D 경험은 다양한 산업에서 필수 요소가 되고 있다. 과학 시각화, 제품 쇼케이스, 비디오 게임, 디지털 트윈, 설정 도구(Configurators), 그리고 예술 작품까지, 3D 콘텐츠는 어디에서나 볼 수 있으며 그 활용 범위는 점점 넓어지고 있다.
그러나 이러한 몰입형 경험을 만드는 일은 쉽지 않다. 3D 디자인과 웹 기술 모두에 대한 전문 지식이 필요하며, 단순히 기능적으로 만드는 것조차 어려울 수 있다. 여기에 더해 텍스처 아티스트, 모델러, 리거, 애니메이터, 룩데브(look dev), 쉐이더 개발자 등 3D 분야의 다양한 전문성을 요구하기 때문에 작업은 금세 복잡해질 수밖에 없다.

머리말
Polygonjs는 이러한 과정을 단순화하고 쉽게 만들어주는 도구이다. Polygonjs는 다음과 같은 특징으로 웹용 3D 제작을 지원한다.
  • 사용하기 쉬움: 누구나 쉽게 접근할 수 있는 직관적인 도구이다.
  • 확장 가능: 특정 요구 사항과 워크플로에 맞게 확장할 수 있다.
  • 강력함: 아름답고 성능이 뛰어난 3D 경험을 만들 수 있다.
Polygonjs는 복잡함을 줄이고 창의성에 집중할 수 있도록 돕는다. 이제 Polygonjs를 사용하여 더 간단하고 빠르게, 그리고 멋진 3D 경험을 만들어 본다.


설치 방법
다음 명령을 실행해 본다. 
npm create polygonjs@latest
npm add polygonjs-editor
npm run polygon

사용 방법
사용 방법은 다음과 같다.

영상을 보면 알겠지만, 비쥬얼 프로그래밍 방식으로, 노드로 구성되어 있어, 작업이 직관적이
고 편리하다. 

레퍼런스

2024년 12월 7일 토요일

벡터DB, 도구, 메모리 기반 간단한 PDF, Web 검색 에이전트 개발과 랭체인 라이브러리 설명

이 글은 벡터DB, 도구, 메모리 기반 간단한 PDF, Web 검색 에이전트 개발과 랭체인 라이브러리를 설명한 것이다. 참고로, Langchain은 급속히 버전과 함수가 개선되고 있어 API가 자주 변경되는 경향이 있다(Deprecated error). 이런 이유로, 작업 시 가상환경에서 개발하고, 관련 버전을 기록해 두는 것이 좋다. 
에이전트 예시(Build Ask Data app - Streamlit)
에이전트 예시(Local_RAG_Agents_Intel_cpu)

LangChain 설명 및 사용법은 앞의 블로그 내용(검색)을 참고한다. 다음 같은 순서로 LLM이 사용된다는 것만 이해하고 있으면 코드 사용에 큰 어려움은 없을 것이다. 

사용자 입력 > 컨텐츠 검색 입력 > 에이전트 도구에서 데이터 입력 > 과거 메모리 내용 입력 > 프롬프트 생성 > LLM 전달 > 추론 내용 출력

설치 
파이썬, 아나콘다가 준비되어 있다는 가정에서, 라이브러리 설치는 다음과 같다. 
pip install --upgrade openai langchain-openai langchain langchain-experimental

이 글은 OpenAI API Key, Tavily API Key를 신청해 가지고 있다고 가정한다. 실행 시 로그나 성능을 확인하고자 한다면, Langsmith에 가입한다. 각 API는 해당 웹사이트에서 가입하여 얻을 수 있다. 각 사이트에서 생성한 API키는 .env 파일을 생성해 다음과 같이 입력해 놓는다.
OPENAI_API_KEY=<OpenAI API Key>
LANGCHAIN_TRACING_V2=false
LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
LANGCHAIN_API_KEY=<Langsmith API Key>
LANGCHAIN_PROJECT=AGENT TUTORIAL

코딩
다음과 같이 코딩한다. 우선 필요한 라이브러리를 임포트한다.
import os, getpass
from openai import OpenAI  
from dotenv import load_dotenv
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.agents import create_openai_functions_agent
from langchain_community.document_loaders import PyPDFLoader
from langchain.tools.retriever import create_retriever_tool
from langchain.agents import AgentExecutor
from langchain_openai import ChatOpenAI
from langchain import hub

os.environ["TAVILY_API_KEY"] = "input your key"
os.environ["LANGCHAIN_PROJECT"] = "AGENT TUTORIAL"
load_dotenv()
client = OpenAI(api_key="input your key")

# Travily의 웹 검색 객체 획득
web_search = TavilySearchResults(k=5) 

# PDF 데이터를 벡터DB에 청크로 저장하고, 문서 검색 객체를 획득
loader = PyPDFLoader("./202212_LiDAR.pdf")  # 적절한 PDF 입력
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
split_docs = loader.load_and_split(text_splitter)

embeddings = OpenAIEmbeddings(api_key=os.environ["OPENAI_API_KEY"])
vector = FAISS.from_documents(split_docs, embeddings)
vectordb_retriever = vector.as_retriever()
output = vectordb_retriever.get_relevant_documents(
    "PCL(Point Cloud Library) 라이브러리에 대해 설명해줘"
)[0]
print(output)  # PCL 검색 예시

pdf_retriever_tool = create_retriever_tool(
    vectordb_retriever,
    name="pdf_search",
    description="2023년 12월 PCL(Point Cloud Library) 정보를 PDF 문서에서 검색합니다. '2023년 12월 라이다 포인트 클라우드 처리 기술' 과 관련된 질문은 이 도구를 사용해야 합니다!",
) # 문서 검색 객체

# 에이전트 도구들 설정
tools = [web_search, pdf_retriever_tool]

# LLM 객체 설정
llm = ChatOpenAI(model="gpt-4-turbo-preview", temperature=0)

# 프롬프트 설정
prompt = hub.pull("hwchase17/openai-functions-agent")
print(prompt.messages)

# LLM 함수 호출 에이전트 설정
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 전문가 에이전트 질문 수행
response = agent_executor.invoke(
    {
        "input": "2010년부터 PCL 라이브러리 기술에 대한 대한 내용을 PDF 문서에서 알려줘"
    }
)
print(f'답변: {response["output"]}')

우선 PDF에서 검색하라고 에이전트에게 명령했으므로, tools에 등록된 pdf vector database를 검색하는 tool을 실행한다. 다음은 이 결과의 예이다. 


> Finished chain.
답변: PCL(Point Cloud Library)은 BSD 라이선스로 개발되어 상업 및 연구 목적에 서 무료로 사용할 수 있습니다. 이 라이브러리는 크로스 플랫폼 개발을 지원하여 리눅스, 맥, 윈도우, 안드로이드 등 다양한 운영 체제에서 사용할 수 있습니다. PCL은 잘 모듈화되어 있어 배포가 용이하며, ROS(Robot Operating System), PDAL 등 유명한 오픈소스 프로젝트에서 직접 사용됩니다....

이제, web 검색 도구를 실행해 관련 내용을 추론하고, 기존 추론된 내용은 메모리를 이용해 같이 이용해 보도록 한다. 다음 코드를 입력해 본다. 
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
message_history = ChatMessageHistory()  # 메모리 사용

agent_with_chat_history = RunnableWithMessageHistory(
    agent_executor,
    lambda session_id: message_history,
    input_messages_key="input",
    history_messages_key="chat_history",
)

response = agent_with_chat_history.invoke(
    {
        "input": "2024년부터 PCL에 대한 새로운 내용을 인터넷 웹 문서에서 알려줘"
    },
    config={"configurable": {"session_id": "MyTestSessionID"}},
)
print(f"답변: {response['output']}")

이 코드를 실행하면, 다음과 같이 travily web search 도구를 이용해, 인터넷에서 해당 질문에 대한 검색을 수행하여, 결과를 LLM이 추론한다.


추론 결과는 다음과 같다.
> Finished chain.
답변: 2024년에 대한 Point Cloud Library (PCL)의 새로운 업데이트와 관련된 정보는 다음과 같습니다:

- **GitHub Pull Requests**: 2024년에는 PCL 개발에 몇 가지 주목할 만한 기여가 있었습니다. 예를 들어, 2024년 2월 10일에 larshg에 의해 열린 [#5958 (https://github.com/PointCloudLibrary/pcl/pulls)는 기능을 두 개의 라이브러리로 분할하 
는 작업에 대한 초안입니다. 또한, 2024년 1월 15일에 larshg에 의해 열린 [#5932 (https://github.com/PointCloudLibrary/pcl/pulls)는 관련된 또 다른 기여입니다.

- **공식 문서 및 웹사이트**: PCL은 대규모 오픈 프로젝트로, 포인트 클라우드 처리를 위한 다양한 최신 알고리즘을 포함하고 있습니다. 이러한 알고리즘에는 노이즈 데이터에서 이상치를 필터링하는 것과 같은 작업을 위한 필터링, 특징 추정, 표면 재구성, 등록, 모델 피팅 및 세분화가 포함됩니다. [공식 문서](http://pointclouds.org/documentation/index.html)와 [공식 웹사이트](https://pointclouds.org/)에서는 PCL에 대한 자세한 정보와 리소스를 제공합니다.

결론
langchain의 tool 에이전트 기능을 이용하면, 파일 벡터 검색, 웹 검색, 계산, 추론, 텍스트 및 차트 생성 등과 같은 기능을 쉽게 개발할 수 있다.

참고로, langchain과 연동되는 langsmith 사이트를 방문하면, 다음과 같이 얼마나 토큰을 사용했는 지 확인할 수 있다. 
LangSmith 로그 결과

부록: langchain 라이브러리 설명
LCEL 문법
사용자 정의 체인을 위한 LCEL은 기본으로 Runnable 에서 파생되어 처리된다. 이는 다음 표준적인 인터페이스를 지원한다.
  • stream: 응답 청크를 스트리밍
  • invoke: 입력에 대한 체인을 실행 호출
  • batch: 입력 목록에 대해 체인들을 실행 호출
  • astream: 비동기적으로 응답 청크를 스트리밍
  • ainvoke: invoke의 비동기 버전
LCEL는 유닉스 파이프라인처럼 입력 | 처리 | 실행 | 출력 형태로 유연한 LLM 오케스트레이션을 지원한다.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

model = ChatOpenAI()
prompt = PromptTemplate.from_template("Explain about {topic} as one paragraph.")
chain = prompt | model | StrOutputParser()

다음 코드를 이용해 체인 그래프의 구조를 확인할 수 있다.
chain.get_graph().nodes
chain.get_graph().edges
chain.get_graph().print_ascii()

각 체인은 이터레이션(iteration)을 이용해 각 단계를 개별 관찰, 실행, 제어할 수 있도록 한다.

Runnable
runnable을 사용하면, 프롬프트 실행 중 동적으로 입력 단계에 참여할 수 있다. 이는 프롬프트에 데이터를 전달할 수 있다.
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough # RunnablePassthrough().invoke({"num": 10})
from langchain_core.runnables import RunnableLambda, RunnablePassthrough

prompt = PromptTemplate.from_template(
    "List top {n} famous people which have birthday {today}."
)
llm = ChatOpenAI(temperature=0, model_name="gpt-4o")

chain = (
    {"today": RunnableLambda(get_today), "n": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
print(chain.invoke({'n': 3}))

사용자 정의 람다를 지원하는 RunnableLambda, 입력에 따른 동적 라우팅 RunnableBranch, 병렬 처리 RunnableParall, 이전 내용 기억을 하는 RunnableWithMessageHistory 등이 있다. 

@chain을 사용하면, 알아서 RunnableLambda로 주어진 함수를 래핑한다.
@chain
def custom_chain(text):
    chain1 = prompt1 | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()
    output1 = chain1.invoke({"topic": text})
    chain2 = prompt2 | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()
    return chain2.invoke({"sentence": output1})

체인 직렬화
앞서 정의한 LLM 체인을 직렬화(langchain_core.load)하면, 개별로 파일 저장해, 필요 시 동적으로 로딩하여 사용할 수 있다. 

OutputFixingParser
출력 파싱 시 발생하는 오류를 자동으로 수정한다. 

Caching
이미 얻은 답변을 캐쉬처리 하여, LLM API 호출을 줄여주고, 실행 속도를 높여준다. InMemoryCache, SQLiteCache 등이 있다.

PydanticOutputParser
LLM 출력을 기계처리 가능한 구조화된 형태로 변환하는 파서이다. 다음은 그 예이다. 
class EmailSummary(BaseModel):
    person: str = Field(description="person who send email")
    email: str = Field(description="email address of sender")
    subject: str = Field(description="email title")
    summary: str = Field(description="email's summerized text")
    date: str = Field(description="meeting date and time in email")

parser = PydanticOutputParser(pydantic_object=EmailSummary)
print(parser.get_format_instructions())

이와 유사한, StructuredOutputParser, JsonOutputParser, PandasDataFrameOutputParser 등이 있다.

LLM 모델 종류
lanchain이 지원하는 주요 LLM 모델은 openai, anthropic claude, cohere aya, facebook llama, microsoft phi, google gemini (ChatGoogleGenerativeAI), GPT4All 등이 있다. 이 중 몇몇 모델은 멀티모달을 지원한다.
gemini = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest")

system_prompt = (
    "You're writer. From given image, write short novel."
)

user_prompt = "Write short novel from the follow image."

multimodal_gemini = MultiModal(
    llm, system_prompt=system_prompt, user_prompt=user_prompt
)
IMAGE_URL = "house.jpg"
answer = multimodal_gemini.stream(IMAGE_URL)

LLM 모델 로컬 실행
LLM 모델을 로컬 PC에서도 실행할 수 있다. 이 경우, 허깅페이스, Ollama를 이용한다. 
llm = HuggingFacePipeline.from_model_id(
    model_id="beomi/llama-2-ko-7b",  
    task="text-generation",  
    pipeline_kwargs={"max_new_tokens": 512},
)

llm = ChatOllama(model="EEVE-Korean-10.8B:latest")

문서 로더
LangChain은 매우 다양한 문서 종류를 지원한다. PDF, CSV, Excel, Word, Web, JSON, Arxiv 등을 지원한다.

문서 청크 분할 및 요약
문서를 LLM에 입력 가능하도록 청크로 분할하는 CharacterTextSplitter(/n 기준 분할), 재귀적 문서 분할, 토큰 제한 분할, 의미론적 분할, 코드 분할, HTML 분할, JSON 분할(RecursiveJsonSplitter) 등이 있다. 

문서를 요약하는 방법은 전체 문서 요약하는 Stuff, 분할 요약하는 Map reduce, 분할 요약 후 정리하는 Map refine 및 chain of density, clustering map refine 방법 등이 있다. 

임베딩 모델
문서의 청크를 검색 가능한 벡터 형태의 임베딩 벡터로 변환하는 모델을 말한다. 보통, OpenAIEmbedding, CacheBackedEmbeddings, HuggingFace Embeddings, OllamaEmbeddings, GPT4All 등이 사용된다. 

벡터 데이터베이스와 검색기
보통, Chroma, Vectordatabase, FAISS, Pinecone 등이 사용된다. 여기서 원하는 임베딩 벡터를 검색해 문서를 리턴하는 방법은 vectorstore 기반 similarity search, MMR, ContextualCompressionRetriever(압축 검색), ContextualCompressionRetriever(컨테텍스트 압축), LongContextReorder(긴 문맥 기록), ParentDocumentRetriever(계층적 문서 검색), MultiQueryRetriever(다중 질의 후 검색), SelfQueryRetriever 등이 있다.

이와 더불어, 재순위(Reranker)를 이용해, 원하는 답을 검색할 때까지 우선순위를 조정할 수 있다. Cross Encoder Reranker, Cohere reranker, Jina Reranker, FlashRank reranker 등이 있다.

메모리
채팅과 같이 앞의 입출력에 대한 맥락을 기억해야 할 때 사용한다. RunnableWithMessageHistory 등이 있다.

Fallback 오류 방지
만약 체인 실행 중 여러 이유로 에러가 발생할 경우, 다시 재 시도하는 방법이 폴백이다. with_fallbacks 함수를 사용한다.

도구와 에이전트
앞서 언급한 도구나 사전 정의된 에이전트를 이용하면, 멀티 에이전트를 만들 수 있다. 도구들 중에서는 파일관리 도구, 이미지 생성 도구, 보고서 작성 도구 등이 있다.
랭체인 무료 제공 도구 예

예를 들어, agent_toolkits에는 langchain 커뮤니티 생태계에서 업로드한 다양한 에이전트가 있다. 이 중에 엑셀 분석 에이전트, SQL agent인 create_sql_agent 등은 많이 사용된다.
랭체인 커뮤니티 제공 에이전트 예

이런 에이전트는 BaseToolkit, BaseTool, RunnableSerializable를 기본 클래스로 파생되어 개발할 수 있다. 이 클래스의 아키텍처는 컴포짓(Composite) 디자인 패턴으로 다음과 같다.
도구를 정의할 때 앞의 @chain 처럼 @tool 데코레이터를 사용할 수도 있다. description은 어떤 도구를 LLM이 선택할 지를 체인에서 결정하는 힌트로 동작한다. 예는 다음과 같다.
import re
import requests
from bs4 import BeautifulSoup
from langchain.agents import tool

@tool
def get_word_length(word: str) -> int:
    """Returns the length of a word."""
    return len(word)

@tool
def add_function(a: float, b: float) -> float:
    """Adds two numbers together."""
    return a + b

@tool
def naver_news_crawl(news_url: str) -> str:
    """Crawls a naver.com news article and returns the body content."""
    response = requests.get(news_url)
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, "html.parser")
        title = soup.find("h2", id="title_area").get_text()
        content = soup.find("div", id="contents").get_text()
        cleaned_title = re.sub(r"\n{2,}", "\n", title)
        cleaned_content = re.sub(r"\n{2,}", "\n", content)
    else:
        print(f"HTTP fail code: {response.status_code}")
    return f"{cleaned_title}\n{cleaned_content}"

tools = [get_word_length, add_function, naver_news_crawl]

from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
llm_with_tools = llm.bind_tools(tools)

LLM 평가
생성 결과에 대한 평가 방법은 자동 계산이 가능한 BLEU, ROUGE, METEOR, SemScore, 수동 평가, 작업 기반 평가, LLM 기반 평가 등이 있다. 평가용 라이브러리는 RAGAS, LangSmith, W&B 등이 있다.

평가를 위해, 테스트 데이터셋을 from ragas.testset.generator import TestsetGenerator 를 통해 자동 생성할 수 있다. 다만, 토큰 비용이 과대해 질 수 있는 문제가 발생할 수 있다. 이 경우, 오픈소스 LLM 모델을 사용할 수 있다.


2024년 11월 30일 토요일

Bespoke: 소리의 모든 것을 연결하는 모듈러 사운드 작업 공간

Bespoke는 오픈소스 기반 신디사이저 소프트웨어이다. 현존하는 대부분의 사운드를 개발, 합성할 수 있다.

화면 인터페이스

Bespoke(비스포크)는 전통적인 음악 제작 방식의 틀을 깨고, 사용자가 자신만의 소리 흐름을 자유롭게 설계할 수 있도록 지원하는 혁신적인 오픈소스 모듈러(Modular) 사운드 엔지니어링 도구이다.

개요

Bespoke는 소프트웨어 신디사이저이자 디지털 오디오 워크스테이션(DAW)이지만, 트랙과 타임라인 기반의 일반적인 DAW(Ableton Live, Logic Pro 등)와는 근본적으로 다른 작동 방식을 가진다. 이 소프트웨어의 핵심은 '모듈(Module)'과 '연결(Patching)'이다. 사용자는 빈 캔버스 같은 작업 공간에 오실레이터, 이펙터, 시퀀서, 샘플러 등 소리를 만들고 변조하는 수많은 모듈을 자유롭게 배치하고, 이들을 가상의 케이블로 연결하여 자신만의 독창적인 악기나 사운드 시스템을 구축한다.

마치 거대한 가상 벽에 수많은 음향 장비를 마음대로 부착하고 케이블을 꽂아 소리를 만들어내는 '유로랙(Eurorack)' 신디사이저처럼, Bespoke는 소리가 생성되고 변형되는 모든 과정을 시각적으로 제어할 수 있는 무한한 유연성을 제공한다. 이는 정해진 규칙을 따르기보다 소리를 탐험하고 실험하려는 뮤지션과 사운드 디자이너에게 최적의 놀이터가 된다.

더 자세한 정보는 Bespoke 웹사이트에서 확인할 수 있다.

개발 배경

Bespoke는 개발자 라이언 챌리너(Ryan Challinor)의 개인적인 필요에서 시작된 프로젝트이다. 그는 기존의 DAW들이 제공하는 선형적인 작곡 방식에 답답함을 느꼈고, 작곡가의 창의적인 흐름이 소프트웨어의 구조에 의해 제약받아서는 안 된다고 생각했다. 그는 머릿속에 떠오르는 복잡한 아이디어를 즉흥적으로 실현하고, 마치 살아있는 유기체처럼 사운드 시스템을 계속해서 변형하고 확장할 수 있는 도구를 원했다.

이러한 갈증을 해소하기 위해 그는 자신만을 위한 맞춤형 사운드 제작 도구를 만들기 시작했다. 'Bespoke'라는 이름 자체가 '맞춤 제작'을 의미하듯, 이 소프트웨어는 사용자의 작업 방식에 스스로를 맞추는 것을 핵심 철학으로 삼는다. 처음에는 개인적인 라이브 연주와 즉흥 작곡을 위한 도구였으나, 그 유연성과 가능성을 높이 평가한 주변의 반응에 힘입어 모든 사람이 사용할 수 있는 오픈소스 프로젝트로 공개되었다.

주요 특징 및 핵심 기술

Bespoke의 강력함은 다음과 같은 독창적인 특징들에서 비롯된다.

  • 완전한 모듈식 환경 (Completely Modular Environment): Bespoke 내의 모든 요소는 독립적인 모듈이다. 소리를 생성하는 오실레이터부터 외부 오디오 입력을 받는 모듈, MIDI 신호를 처리하는 모듈, 심지어 VST 플러그인을 불러오는 모듈까지, 모든 것이 개별적인 부품처럼 존재한다. 사용자는 이들을 무한정 조합하고 연결하여 상상할 수 있는 거의 모든 종류의 사운드 라우팅을 구현할 수 있다.

  • 방대한 내장 모듈 라이브러리: 기본적으로 175개 이상의 다채로운 모듈을 내장하고 있다. 간단한 신디사이저, 드럼머신, 웨이브테이블 합성, 그래뉼러 신디시스 등 다양한 사운드 소스는 물론, 리버브, 딜레이, 디스토션과 같은 이펙터, 그리고 복잡한 로직을 제어하는 유틸리티 모듈까지 풍부하게 제공하여 별도의 플러그인 없이도 깊이 있는 사운드 디자인이 가능하다.

  • VST/VST3 플러그인 지원: Bespoke의 생태계는 내장 모듈에만 국한되지 않는다. 사용자가 이미 보유하고 있는 서드파티 VST/VST3 가상악기 및 이펙트 플러그인을 하나의 모듈처럼 불러와 기존의 Bespoke 모듈들과 자유롭게 연결할 수 있다. 이는 Bespoke를 전체 음악 작업의 중심 허브로 사용할 수 있게 하는 매우 강력한 기능이다.

  • 파이썬 라이브 스크립팅 (Live Python Scripting): 고급 사용자를 위해 파이썬(Python) 코드를 실시간으로 작성하여 자신만의 커스텀 모듈을 만들거나 기존 모듈의 동작을 제어할 수 있는 기능을 제공한다. 이는 사실상 소프트웨어의 기능을 무한대로 확장할 수 있는 잠재력을 의미한다.

유사 도구

Bespoke와 같이 모듈러 방식을 채택한 사운드 엔지니어링 도구는 다음과 같다.

  • VCV Rack: 가상 유로랙 신디사이저의 표준으로 불리는 소프트웨어이다. 실제 하드웨어 모듈을 매우 정밀하게 복각하는 데 중점을 두고 있으며, 방대한 서드파티 모듈 생태계를 자랑한다. 사운드 합성에 매우 특화되어 있다. (VCV Rack 공식 웹사이트)

  • Bitwig Studio (The Grid): 전통적인 DAW와 모듈러 환경을 결합한 하이브리드 소프트웨어이다. Bitwig 내의 'The Grid'라는 환경에서 사용자가 자신만의 악기나 이펙터를 모듈러 방식으로 설계할 수 있어, 정형화된 작업과 자유로운 실험을 넘나들 수 있다. (Bitwig 공식 웹사이트)

  • Native Instruments Reaktor: 오랫동안 사운드 디자이너들에게 사랑받아온 강력한 모듈러 사운드 디자인 플랫폼이다. 매우 낮은 레벨의 신호 처리부터 복잡한 악기 제작까지 가능하며, 사용자들이 만든 수많은 악기와 이펙터를 공유하는 라이브러리가 강점이다. (Reaktor 공식 웹사이트)

  • Max/MSP: 음악과 멀티미디어를 위한 시각적 프로그래밍 언어이다. Bespoke보다 훨씬 더 근본적이고 자유도가 높은 개발 환경에 가까워, 단순한 음악 제작을 넘어 인터랙티브 아트나 커스텀 소프트웨어 개발에도 사용된다.

라이선스

Bespoke는 모든 사람이 창의적인 도구에 접근할 수 있어야 한다는 철학 아래 완전한 오픈소스 소프트웨어로 제공된다.

  • 가격 정책: 공식적으로 무료이며, 'Pay-What-You-Can(지불할 수 있는 만큼 지불)' 정책을 채택하고 있다. 누구나 비용 없이 다운로드하여 모든 기능을 사용할 수 있지만, 개발자의 지속적인 노력을 지원하기 위해 사용자가 자발적으로 기부하는 것을 권장한다.

  • 소스 코드: 모든 소스 코드는 GitHub 저장소에 공개되어 있어, 누구나 코드를 확인하고 개발에 기여할 수 있다.


데모
설치 방법은 다음 링크를 참고한다.

마무리 및 평가

Bespoke는 단순한 음악 제작 툴을 넘어, 소리를 탐구하는 새로운 방식을 제안하는 하나의 창의적인 철학이다. 정해진 길을 따라가는 대신, 수많은 가능성을 직접 연결하고 실험하며 예기치 못한 소리를 발견하는 즐거움을 선사한다.

이러한 극단적인 자유도는 실험적인 전자음악가, 사운드 아티스트, 그리고 기존 DAW에 한계를 느끼는 모든 창작자에게 강력한 영감을 제공한다. Bespoke는 표준화된 워크플로우에서 벗어나 자신만의 소리를 '맞춤 제작'하고 싶은 이들에게 가장 완벽한 디지털 놀이터가 되어줄 것이다.


레퍼런스 

2024년 11월 23일 토요일

RAG LLM 기반 멀티 에이전트 시스템 개발 경험기

이 글은 LLM 기반 멀티 에이전트 시스템을 개발해 본 후 개인적으로 느낌 경험을 남긴다.

멀티 에이전트를 개발하는 방법은 다양하다. 지금까지 경험한 바로는 랭체인과 같은 라이브러리의 기능들을 많이 이용하기 보다는 LLM과 에이전트의 핵심 기능에 집중해 구현하는 것이 효과적이었다.

예를 들어, 다음 그림과 같이, 뉴스나 차트를 읽어 해석하여, 사용자 질문에 대해 LLM이 추론하는 방식의 에이전트를 개발한다고 하자.
신문 차트 기반 에이전트 개념

이 경우, 랭체인과 같은 라이브러리의 복잡한 기능을 사용하는 것 보다 LLM의 기본적인 기능에 집중하는 것이 오히려 나을 때가 있다. 처음에는 라이브러리의 관련 함수를 이용해 에이전트를 개발하려 하였으나, 1) 확률적?으로 에이전트가 동작되는 문제, 2) 복잡한 라이브러리 의존성 문제, 3) 복잡한 호출 경로로 인한 디버깅 문제, 4) 불명확한 함수 호출 문제 등이 발생하였다. 

결론적으로, 랭체인 예제, 튜터리얼, 블로그 등에서 알려주는 것을 해본 결과, LLM 조작에 핵심적인 부분만 구현하고, 나머지 확률적이고 불명확한 함수들을 사용하지 않는 편이 좋은 성능을 얻을 수 있다.

다음은 단순히 Tool과 LLM을 이용해 개발된 결과를 보여준다.

레퍼런스