본문 바로가기

Software

프롬프트 엔지니어링 툴: PromptLayer로 프롬프트 및 LLM 요청 관리하기

프롬프트 및 OpenAI API 요청의 로그를 관리 및 공유할 수 있는 플랫폼인 PromptLayer의 사용법에 대해 정리한 글입니다.

 

  Prompt Layer

PromptLayer는 GPT 프롬프트 엔지니어링을 추적, 관리 및 공유할 수 있는 개발 도구입니다. 코드와 OpenAI의 파이썬 라이브러리 사이의 미들웨어 역할을 하며, 모든 API 요청을 기록하고 관련 메타데이터를 저장하여 PromptLayer 대시보드에서 쉽게 탐색하고 검색할 수 있도록 합니다.

프롬프트 레이어에서는 크게 두 가지를 관리하는 것을 돕습니다.

  1. prompt template
  2. LLM request

UI상에서 프롬프트 템플릿은 Registry에서 관리가 되고, LLM에 대한 요청은 History 탭에 로그가 쌓이게 됩니다.

 

Tag

템플릿과 요청에 대해 공통적으로 tag 라는 개념이 있는데, 이 둘의 쓰임이 약간 혼동될 수 있어서 먼저 정리를 하고 가겠습니다.

  • 프롬프트 템플릿 생성 시 tag 를 지정할 수 있습니다.
    • 인자명: tags
  • LLM 요청을 생성할 때에도 tag 를 추가할 수 있습니다.
    • 인자명: pl_tags

그러나 이 둘은 각각에 대한 tag이기 때문에 History 에서 요청을 조회할 때에는 LLM 요청에 대한 tag로 필터링이 가능하고, Registry 에서 프롬프트를 조회할 때에는 프롬프트 템플릿에 대한 tag로 필터링이 가능합니다.

History  에서 태그로 요청 필터링
Registry  에서 태그로 프롬프트 필터링

 

  Registry

프롬프트 레지스트리는 LLM 앱을 프로덕션에 적용하는 과정에서 프롬프트 템플릿을 쉽게 관리하는 데 도움이 됩니다. 개발 시 프롬프트를 코드 곳곳에 하드코딩 하지 않고, 지정된 형식으로 프롬프트를 저장소에 등록하고 LLM 요청시에는 저장소로부터 프롬프트를 내려받는 형식을 취하여 프롬프트와 코드를 분리할 수 있습니다.

이로 인해 프롬프트에 대한 A/B 테스트를 쉽게 수행할 수 있으며 가장 좋은 결과를 보인 프롬프트에 대해 추적 및 공유가 가능해집니다.

현재 저는 사내에서 마케팅 솔루션 서비스에 GPT 분석 기능 추가 및 유지보수에 대한 업무를 진행중입니다. 문제는 프롬프트 엔지니어링 과정에서 더 나은 프롬프트를 위해서 잦은 수정이 이루어지면서 버전 관리에 대한 필요성이 생겨났다는 것입니다.

PromptLayer는 파이썬으로 구현되어있어 저희 서비스의 백엔드에 통합하기 용이하고, 웹 UI를 통해 개발 팀 뿐만이 아닌 다른 부서에서도 더 나은 프롬프트를 위한 테스트 진행 및 공유가 수월해질 것 같아 채택하게 되었습니다.

 

Prompt Template이란?

구체적으로 프롬프트 템플릿은 중괄호 안에 변수가 표시된 프롬프트 문자열입니다(예: This is a prompt by {author_name}). 프롬프트 템플릿은 태그를 가질 수 있으며 고유한 이름으로 지정해야합니다.

PromptLayer는 LangChain PromptTemplate 스키마와 호환되도록 만들어졌으며, 일반 템플릿과 few-shot 템플릿이 모두 지원됩니다.

 

 

Python Library를 사용하기 위한 준비사항

Python에서 promptlayer를 사용하려면 라이브러리를 설치하고 promptlayer와 OpenAI의 API 키를 환경변수로 등록해야합니다.

1. 라이브러리 설치

pip install promptlayer
  1. 프롬프트 레이어 계정 생성 시 발급받은 API 키 등록

3. OpenAI API 키 등록

import os
import promptlayer

# 두 api key 환경변수로 등록
os.environ['OPENAI_API_KEY'] = '<YOUR OpenAI API KEY xxxxxx>'
os.environ['PROMPTLAYER_API_KEY'] = '<YOUR PromptLayer API KEY pl_xxxxxx>'

# 환경변수로부터 키 획득
promptlayer.api_key = os.environ.get("PROMPTLAYER_API_KEY")

# openai를 래핑한 promptlayer의 객체를 생성
openai = promptlayer.openai

# open ai 키를 전달
openai.api_key = os.environ.get("OPENAI_API_KEY")

# LLM 요청
openai.Completion.create(
                    engine="text-ada-001", 
                    prompt="My name is", 
                    pl_tags=["name-guessing", "pipeline-2"]

이 때 위의 용어 정리에서 언급한 것처럼 pl_tags 인자는 프롬프트에 대한 태그가 아니라 향후 원하는 내용의 LLM 요청 기록을 찾기 위해 추가해주는 LLM 요청에 대한 태그 입니다. (참고)

 

 

  템플릿 등록하기

UI 또는 Python API를 사용하여 템플릿을 만들 수 있습니다. 템플릿은 이름별로 고유하므로 같은 이름의 템플릿을 게시하면 이전 템플릿을 덮어쓰게 된다는 점을 유의해야 합니다.

 

Web UI

웹에 접속하여 계정을 생성하면 기본적으로 프리티어에서 사용 가능합니다. 프리티어에서는 한달 동안 지난 1,000건의 요청에 액세스할 수 있습니다.

메인 화면에서는 다음과 같이 python 라이브러리의 사용법을 안내하고 있으며, 현재 호환이 되고 있는 LLM은 OpenAIAnthropic 입니다.

Registry > Create Template 에서 프롬프트를 등록할 수 있습니다. 프롬프트의 형식은 위에서 언급한대로 LangChainPromptTemplate 을 따른 텍스트 혹은 JSON 파일이어야하며 템플릿명은 소문자로 유지하는 것이 좋습니다.

JSON 또는 YAML 파일로 LangChain의 프롬프트를 저장하고 LangChain이 이를 직렬화하는 자세한 과정에 대해서는 이곳을 참고해주세요.

 

Python API

파이썬에서는 promptlayer.prompts.publish 로 템플릿을 등록할 수 있습니다. 또한 Langchain을 사용하여 LangchainHub에서 템플릿을 가져오거나, 사용자 정의 템플릿을 만들거나, Python 사전을 직접 제공하여 템플릿을 만들 수도 있습니다.

1. langchain hub에서 템플릿을 가져와서 등록

from langchain.prompts import load_prompt
prompt = load_prompt('lc://prompts/llm_bash/prompt.json')

promptlayer.prompts.publish(
    "llmbash",
    tags=["langchain_hub"],
    prompt_template=prompt)

2. 커스텀 langchain prompt template 을 생성하여 등록

from langchain import PromptTemplate

template = "5 example GPT prompt templates for {subject}:"
prompt = PromptTemplate(input_variables=["subject"], template=template)

promptlayer.prompts.publish("prompt_examples", prompt_template=prompt)

3. json 파일로부터 프롬프트 템플릿을 가져와서 등록

sample.json

{
    "_type": "prompt",
    "input_variables": ["num"],
    "template": "한글을 이용하여 남자 아이 이름을 지어주세요.\n이름은 {num}자가 적당합니다."
}
from langchain.prompts import load_prompt

prompt = load_prompt('sample.json')
promptlayer.prompts.publish("name_a_boy", prompt_template=prompt)

REST API

REST API endpoint를 이용하여 프롬프트를 등록할 수도 있습니다. 해당 과정에 대한 설명은 이곳을 참고해주세요.

 

 

 

  템플릿 가져오기

템플릿 획득은 등록과 마찬가지로 Python API 또는 REST API를 이용할 수 있습니다.

Python API

1. 딕셔너리 형태로 획득

획득시 version은 기본적으로 가장 최신에 등록된 프롬프트를 반환합니다. 이 때 특정 버전을 지정하고 싶은 경우 아래와 같이 버전 번호를 인자로 주면 됩니다. (등록된 범위 내에 없는 버전을 획득하려고 할 경우는 에러가 납니다.)

template_dict = promptlayer.prompts.get("name_a_boy", version=1)
template_dict

>>>
{'input_variables': ['num'],
 'output_parser': None,
 'template': '한글을 이용하여 남자 아이 이름을 지어주세요.\n이름은 {num}자가 적당합니다.',
 'template_format': 'f-string'}
  1. Langchain PromptTemplate 객체로 획득

Langchain의 PromptTemplate 객체로 템플릿을 반환받으려면 langchain=True 로 인자를 주면 됩니다.

prompt_template_obj = promptlayer.prompts.get("name_a_boy", langchain=True)
prompt_template_obj

>>>
PromptTemplate(
input_variables=['num'], 
output_parser=None, partial_variables={}, 
template='한글을 이용하여 남자 아이 이름을 지어주세요.\n이름은 {num}자가 적당합니다.', 
template_format='f-string', 
validate_template=True
)

REST API

REST API endpoint를 이용하여 프롬프트를 가져오는 방법에 대해서는 문서를 참고해주세요.

 

 

 

  LLM 요청

GPT에 요청을 생성하는 동시에 해당 요청에 대한 정보와 응답을 promptlayer에 저장하는 데에는 여러가지 방법이 있습니다.

LangChain 내에서 PromptLayer 사용

LangChain 내에서도 PromptLayer의 OpenAI LLM 래퍼를 사용하여 요청을 수행할 수 있습니다.

예를 들어 ChatGPT를 사용하고 싶은경우 아래와 같이 요청을 수행하면 해당 요청에 대한 로그를 UI에서 확인 가능합니다.

  1. PromptLayerOpenAIChat 을 사용
from langchain.llms import PromptLayerOpenAIChat
llm = PromptLayerOpenAIChat()
resp = llm("tell me a joke")

2. PromptLayerChatOpenAI 를 사용

from langchain.chat_models import PromptLayerChatOpenAI
from langchain.schema import (
    SystemMessage,
    HumanMessage,
)
chat = PromptLayerChatOpenAI()
chat([
    SystemMessage(content="You are a helpful assistant that translates English to French."),
    HumanMessage(content="Translate this sentence from English to French. I love programming.")
])

 

PromptLayer library를 사용하여 바로 요청하고 싶은 경우

또는 Langchain을 사용하지 않고 promptlayer 내의 래핑된 openai 객체를 사용하여 요청할 수도 있습니다.

from promptlayer import openai
response = openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

 

 

 

   LLM 요청에 대한 Medata 추가

그러나 위와 같이 요청한 경우는 Metadata 에 아무 정보가 없게 됩니다.

요청에 대한 metadata를 추가하고 UI에서 확인하고 싶은 경우는 아래와 같이 2단계로 진행합니다.

1. return_pl_id=True 로 설정하고 LLM 요청

from promptlayer import openai
response, pl_request_id = openai.Completion.create(
                engine="text-davinci-003", 
                prompt="hi my name is ", 
                return_pl_id=True
)

2. promptlayer.track 으로 메타데이터 등록

연결할 수 있는 데이터 타입은 3가지가 있습니다.

  1. prompt template
  2. metadata
  3. score

 

주의 사항

여기서 제가 혼동을 겪은 부분이 있습니다. track은 예상과는 다르게 프롬프트 및 요청에 대한 추적 기능이 아니라, LLM 요청에 대한 점수와 메타데이터를 “추가”하고 향후 prompt layer의 대시보드에서 해당 메타 정보로 필터링 및 조회가 용이하도록 하려는 의도인 것 같습니다.

참고 사항에 따르면 metadata는 string타입의 key와 value로 등록해야 하며, 이미 메타데이터가 있는 요청에 대해 또 메타데이터를 track할 경우 기존의 값을 덮어쓰게 됩니다.

 

프롬프트 템플릿 연결

만약 registry에 name-guessing 이라는 템플릿이 있고, 요청에 해당 템플릿을 연결하고자 할 경우 아래와 같이 등록하면 됩니다.

promptlayer.track.prompt(
    request_id=pl_request_id, 
    prompt_name='name-guessing', 
    prompt_input_variables={}
)

그러면 UI상에서 아래와 같이 요청의 왼쪽 상단에 No prompt template linked 상태에서 _Template이 연결된 상태_로 바뀐 것을 확인할 수 있습니다.

“주의할 점은 해당 요청에 대한 UI 상의 Prompt가 실제 요청시의 프롬프트가 아닌 연결된 템플릿의 프롬프트로 덮어씌워진다는 점입니다.”

실제 요청시의 prompt는 "hi my name is " 였음

 

메타데이터 등록

요청에 대한 메타 데이터는 promptlayer.track.metadata로 등록할 수 있습니다.

promptlayer.track.metadata(
    request_id=pl_request_id,
    metadata={
        "user_id":"1abf2345f",
        "post_id": "2cef2345f"
  }
)

 

 

점수 등록

마찬가지로 해당 요청에 대한 점수도 track으로 등록할 수 있습니다.

promptlayer.track.score(
    request_id=pl_request_id,
    score=100
)

그 결과 Web UI에서 아래와 같이 확인 가능합니다.

그 후 아래와 같이 Analytics > Metadata > Add filter 를 통해 등록한 메타데이터로 필터링 및 필터링된 분석 결과를 볼 수 있습니다.

 

 

  개선이 필요한 부분

그러나 현재 시점(2023.06)을 기준으로 prompt layer는 프로덕트에 적용시키기 위해 API 단에서 개선 사항이 많아보였습니다.

  1. Registry에서 전체 프롬프트에 대한 목록을 가져오는 기능이 없음
  2. 프롬프트 등록 시 user id, 등록된 시간 등의 정보를 추가하고 해당 정보로 필터링하여 가져오는 api가 없음
  3. 현재는 버전이 지정되지 않으면 가장 최신의 프롬프트를 반환하지만, 반환된 프롬프트가 몇 버전인지 정보가없음

위의 사항 외에도 아직 추가되어야할 기능이 많고 github issuefeature-request 페이지에 요구 사항을 등록하여 계속해서 발전시켜나가도록 해야할 것 같습니다.