ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 생성형 AI(LLM) 프롬프트 파일 관리 방법
    AI 2025. 3. 16. 00:38

    ChatGPT로 생성한 이미지 입니다.

     

    생성형 AI에 사용되는 프롬프트는 어떻게 관리하는 게 좋을까요? 프롬프트는 챗봇의 답변 품질에 굉장히 많은 영향을 미치는 만큼 반드시 체계적인 관리가 필요한데요. 정작, 인터넷에서 프롬프트 관리 방법에 대해 검색해 보면 공개된 자료를 거의 찾을 수 없어서 항상 아쉬움이 많이 남았습니다.

     

    최근 5개월짜리 짧은 기간 동안 AI 프로젝트를 리드하면서 어떻게 하면 팀의 성격에 맞게 프롬프트를 효율적으로 관리할 수 있을지 저도 직접 겪어보며 방법들을 조금씩 개선해 나갔는데요. 이번에 그 경험에 대해 공유드리고자 합니다.

     


     

    🤔 프롬프트는 어떻게 관리하는 게 좋을까?

    프롬프트를 파일로 관리 vs 데이터베이스로 관리

     

    우선, 프롬프트를 관리하기 위해 첫 번째로 생각해보아야 할 점은 프롬프트가 외부에 공개되어도 되는지? 아니면 외부로 유출되면 안 되는지 생각해 볼 필요가 있습니다. 만약 자사의 핵심 기술력이 되는 프롬프트이거나 보안에 주의해야 하는 프롬프트라면 안전하게 데이터베이스에 저장해 놓고 사용하는 것이 가장 좋습니다.

     

    만약, 비용과 관리포인트를 줄이고 싶다면 파일로 관리하는 방법도 존재합니다. 다만, 파일로 관리하게 될 경우 보안에 유의해야 한다면 프롬프트가 외부로 유출되지 않도록 조심해야 합니다. 예를 들어, github에서는 레포지토리를 private으로 관리하는 등의 주의가 필요합니다.

     

    파일로 관리하게 될 경우 프롬프트의 버전 관리도 힘들 수 있는데요. 파일의 경우 Git으로 버전 관리를 해도 되지만 작업자들의 커밋 권한과 규칙을 더욱 엄격하게 제한 해야하기 때문에 좋은 방법이 아닐 수 있습니다. 이 경우 lakefs 같은 오픈 소스 도구를 이용하여 스토리지를 통해 버전 관리를 하는 방법도 존재합니다.

     

    저의 경우에는 많은 고민 끝에 비영리단체를 위한 프로젝트인 만큼 비용을 가장 아낄 수 있고 기간이 짧은 프로젝트인 만큼 속도감 있는 애플리케이션 개발과 협업을 위해 github private 레포지토리에서 파일 기반으로 관리하는 방법을 선택하였습니다.

     


     

    🗂️ 프롬프트 파일의 확장자 종류

    만약, 프롬프트를 파일로 관리하기로 결정했다면 어떤 확장자로 관리해야될지도 고민포인트 중의 하나입니다. 프롬프트의 파일 확장자로는. txt,. py,. json,. yaml 등이 있을 것 같습니다. 제가 생각하는 각 확장자들의 장단점을 표로 정리해 보면 다음과 같습니다.

     

    확장자 장점 단점
    .txt 프롬프트를 공유하거나 소통하기가 편리하다. 변수 치환이 어렵다.
    .py 별도의 파일 Read 라이브러리를 필요로 하지 않는다. 자유도가 높아 불필요한 코드를 삽입하여 코드가 지저분해지거나 의존성이 높아져 추후 분리가 어려워질 수 있다.
    .json 랭체인에서 프롬프트를 불러올 때 해당 확장자를 지원한다. 중괄호({})로 인해 가독성이 떨어진다.
    .yaml 랭체인에서 프롬프트를 불러올 때 해당 확장자를 지원한다. 공백 문자 사용시 스페이스 대신 탭(\t)을 사용하면 오류가 발생한다.

    ※ TMI : yaml 대신 yml을 사용해도 됩니다. 옛날 OS (DOS 8.3) 파일명 규칙에는 확장자가 3자리까지만 허용되는 규칙이 있어서 yml로 사용했는데 공식적인 표기는 yaml입니다.

     

    저도 처음에는 프롬프트를 간단하게 파이썬 파일로 사용하다가 도중에 yaml 파일로 옮기게 되었는데요. 변경하게 된 이유는 시간이 지날수록 점점 프롬프트 코드에 불필요한 import 문과 랭체인 코드 등이 늘어나면서 복잡도가 증가하게 되는 문제가 발생하였습니다.

     

    특히 일을 하다 보면 비개발자인 기획, 데이터분석가 분들에게도 프롬프트를 공유드리는 경우가 많은데 그때마다 복잡한 파이썬 코드를 보여드리는 것이 죄송스러웠고 코드를 설명해야 하는 불필요한 의사소통이 발생했습니다.

     

    yaml 파일은 일정한 형식대로 작성해야 하기 때문에 위와 같은 문제들을 해결할 수 있습니다. 특히, 랭체인에서도 yaml로 작성된 프롬프트를 로드할 수 있는 함수를 지원하고 있어 코드 구현에 대한 부담 없이 사용할 수 있다는 점 역시 장점 중 하나입니다.

     


     

    🦜 랭체인 load_prompt 함수 사용해 보기

    앞서 살펴본 표에서 json, yaml 파일은 랭체인에서 지원이 된다고 말씀드렸는데요. 해당 확장자 파일로 저장된 프롬프트를 로드하는 함수는 load_prompt() 함수로 langchain_core 패키지에 포함되어 있습니다.

     

    함수의 위치는 langchain_core/prompts/loading.py 파일 내에 존재합니다. 아래 API DOCS를 통해 어떻게 사용하는지 알아보도록 하겠습니다.

     

    https://api.python.langchain.com/en/latest/core/prompts/langchain_core.prompts.loading.load_prompt.html

     

    위와 같이 load_prompt 함수의 파라미터로는 파일의 경로(path)와 encoding 타입을 받을 수 있도록 구성되어있습니다. 실제 비즈니스 로직에서 호출할 때도 마찬가지로 해당 인자값들을 채워주시면 됩니다. 아래 예시 코드를 참고하시면 좋을 것 같습니다.

     

    from pathlib import Path
    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import ChatPromptTemplate, load_prompt
    from langchain_openai import ChatOpenAI
    
    llm = ChatOpenAI()
    
    # 프롬프트 파일 경로 지정
    PROMPT_DIR = Path("app/utils/prompts")
    
    # YAML 파일로부터 프롬프트 템플릿 로드
    prompt_template = load_prompt(PROMPT_DIR / "template.yaml", encoding="utf-8")
    
    # 프롬프트에 사용될 메시지 구성
    chat_prompt = ChatPromptTemplate.from_messages([
        ("placeholder", "{messages}"),
        ("human", prompt_template.format(input="{input}")) # 프롬프트 템플릿에 input이라는 변수가 있을 경우
    ])
    
    # 체인 생성 및 실행
    chain = chat_prompt | llm | StrOutputParser()
    response = chain.invoke({
        "messages": messages[:-1],
        "input": messages[-1]
    })

     

    프롬프트 템플릿은 yaml 파일뿐만 아니라, json 형식도 가능합니다. 어떤 확장자들을 지원하는지는 아래와 같이 _load_prompt_from_file 함수에서 확인해 보실 수 있습니다. 해당 함수도 마찬가지로 langchain_core/prompts/loading.py 내에 존재합니다.

     

    https://api.python.langchain.com/en/latest/_modules/langchain_core/prompts/loading.html#load_prompt

     

    json, yaml 이외에도 만약 원하는 확장자를 추가하고 싶다면 직접 langchain 오픈소스 커밋터가 되어보시는 것도 추천드립니다.

     

     

    langchain/libs/core/langchain_core/prompts/loading.py at master · langchain-ai/langchain

    🦜🔗 Build context-aware reasoning applications. Contribute to langchain-ai/langchain development by creating an account on GitHub.

    github.com

     


     

    🟪 프롬프트 YAML 파일의 템플릿 구조

    다음은 랭체인의 load_prompt 함수를 사용하기 위한 YAML 파일 템플릿 구조를 살펴보도록 하겠습니다. 프롬프트 템플릿으로 사용하기 위한 YAML 파일 구조는 다음과 같습니다.

     

    # 예시1
    _type: prompt
    template: "다음 주제에 대해 설명해주세요: {topic}"
    input_variables: ["topic"]

     

    # 예시2
    _type: prompt
    template: |
      당신은 조선의 왕 입니다. 사용자 질문에 대해 조선의 왕 처럼 답변하시오.
       
      사용자 질문 : {input}
    input_variables: ["input"]

     

     

    옵션 설명
    _type prompt | few_shot | chat 중에서 선택하여 사용가능
    template 프롬프트 내용 작성 (변수가 있는 경우 중괄호 사용)
    input_variables template 변수에 매핑할 변수명 지정. 비즈니스 로직에서 아래와 같이 사용 가능

    template = load_prompt(”template.yaml", encoding="utf-8")
    template.format(topic="{topic}")

     

    위의 예제로 사용한 기본 prompt 타입을 이외에도 few_shot과 chat을 사용할 수도 있는데요. 각 프롬프트의 성격에 맞게 선택하여 사용하실 수 있습니다. 템플릿 구조는 다음과 같습니다.

     

    Few Shot 프롬프트(_type:few_shot)

    • 예제를 포함한 프롬프트
    • 학습 예제를 통해 모델에게 패턴을 보여줄 때 사용
    _type: few_shot
    prefix: "다음은 문장을 긍정/부정으로 분류하는 예제입니다:"
    examples:
      - input: "오늘은 정말 좋은 날이에요!"
        output: "긍정"
      - input: "날씨가 너무 안 좋네요."
        output: "부정"
    suffix: "입력: {input}\\n출력:"
    example_prompt:
      _type: prompt
      template: "입력: {input}\\n출력: {output}"
    input_variables: ["question"]

     

    채팅 프롬프트(_type: chat)

    • 대화형 상호작용을 위한 프롬프트
    • system/user/assistant 역할 구분 가능
    • 대화 맥락 유지 가능
    _type: chat
    messages:
      - role: system
        content: "당신은 수학 선생님입니다."
      - role: user
        content: "{question}"
      - role: assistant
        content: "이 문제를 단계별로 풀어보겠습니다."
    input_variables: ["question"]
    

     

    정리해 보자면 load_prompt 함수를 사용할 때 yaml 파일에서 사용할 수 있는 타입은 prompt, few_shot, chat 3가지 타입입니다. 주의하실 점은 채팅 프롬프트에는 사용자와 주고받은 채팅 히스토리를 집어넣는 게 아니라 위와 같이 일부 지정된 프롬프트를 기입하는 용도입니다.

     

    따라서, 실제 사용자와의 대화 히스토리 자체는 위의 프롬프트 앞뒤로 붙여서 사용해야 합니다. 즉, messages를 통째로 매핑할 수 있는 구조는 아니기 때문에 실제 멀티턴(사용자와의 대화 히스토리 맥락을 파악) 사용 시에는 few_shot과 chat 타입의 프롬프트를 적용하기 어려운 점들이 있어서 제가 프로젝트에 사용할 때는 prompt 템플릿 하나만을 사용하였습니다.

     

    만약, yaml 파일에 대화 히스토리를 넣고 싶다면 MS에서 개발한 프롬프트 관리 도구 및 라이브러리인 prompty를 사용하는 것도 방법입니다. 프롬프티는 yaml 파일 확장자 대신 .prompty라는 확장자를 사용하지만 yaml 파일과 비슷한 형식으로 사용 가능합니다.

     

    이 때는 프롬프티가 루프변수를 지원하기 때문에 대화 히스토리도 변수로 사용할 수 있는 장점이 있습니다.

     

    .prompty 예시 코드

    ---
    name: Basic Prompt
    description: A basic prompt that uses the GPT-3 chat API to answer questions
    authors:
      - author_1
      - author_2
    model:
      api: chat
      configuration:
        azure_deployment: gpt-35-turbo
    sample:
      firstName: Jane
      lastName: Doe
      question: What is the meaning of life?
      chat_history: []
    ---
    system:
    You are an AI assistant who helps people find information.
    As the assistant, you answer questions briefly, succinctly, 
    and in a personable manner using markdown and even add some personal flair with appropriate emojis.
    
    {% for item in chat_history %}
    {{item.role}}:
    {{item.content}}
    {% endfor %}
    
    
    user:
    {{input}}

     

    프롬프티 역시 랭체인에서 지원이 되기 때문에 필요에 따라서 사용하시면 됩니다. 프롬프티의 장점은 vscode에서 extension으로 플레이그라운드를 지원하기 때문에 개발자들의 테스트가 용이할 수 있다는 것이 장점 중에 하나 입니다.

     

    MS 프롬프티

     

    GitHub - microsoft/prompty: Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications.

    Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications. Prompty is an asset class and format for LLM prompts designed to enhance observability, understan...

    github.com

     

    MS 프롬프티 랭체인 연동 라이브러리

     

    langchain/libs/partners/prompty at master · langchain-ai/langchain

    🦜🔗 Build context-aware reasoning applications. Contribute to langchain-ai/langchain development by creating an account on GitHub.

    github.com

     


     

    🚀 프롬프트 버전 관리 방법

    앞서 프롬프트를 파일로 관리하는 경우 보안뿐만 아니라 버전 관리도 문제가 될 수 있다고 말씀드렸는데요. lakefs 또는 S3 등 별도 스토리지를 통해 관리하는 방법들이 있겠지만, 그 마저도 여력이 없다면 레포지토리 내 폴더로 버전관리를 해야 할 수도 있습니다.

     

    저의 경우에는 다음과 같이 prompts 폴더를 하나 만들어서 버전별로 프롬프트들을 관리하였습니다. 버전 폴더에는 yaml 파일들이 존재합니다. 그리고 팀원들과는 그라운드 룰을 설정하여 배포할 때는 폴더를 복사하여 v7, v8 이런 식으로 늘려나가는 방식을 택하였습니다.

     

    레포지토리 기반 프롬프트 버전 관리

     

    해당 폴더 안에는 버전별로 프롬프트가 10개 이상 존재하기 때문에 사실, 버전이 변하지 않은 프롬프트들도 많이 존재하는데요. 그럼에도 변하지 않는 프롬프트까지 복사하여 버전 업하는 이유는 직관적인 관리를 위해서입니다.

     

    만약, 프롬프트 별로 버전관리를 하게 된다면 10개나 되는 프롬프트의 버전을 전부 기억하고 있어야 하기 때문에 랩원 분들의 인지 부하가 발생할 수 있습니다. 따라서, 위와 같은 구조로 프롬프트를 관리하는 방법을 택하였습니다.

     

    추가로, 프롬프트 버전관리에 대한 커뮤니케이션 방법으로는 노션을 활용하였습니다. 프롬프트가 중요한 프로젝트였기 때문에 모든 직군이 전부 관심을 갖고 모니터링할 수 있어야 해서 가장 접근이 쉬운 노션을 활용하여 중앙 관리식으로 워크플로우를 설계하고 각 직군별 소통문제를 해결하였습니다.

     

    프롬프트 버전 관리 노트

     

    필드 설명
    버전 프롬프트 버전 기입(클릭시 각 프롬프트 내용 및 수정사항 확인 가능)
    날짜 프롬프트 배포 날짜
    설명 한 줄 요약 설명
    작업자 프롬프트 배포한 사람
    플레이그라운드 프롬프트 테스트 환경 링크 (Langserve 플레이그라운드 사용)

     

     

    🚀 버전 번호가 갖는 의미

    • 상호 간 의사소통에서 헷갈리지 않도록 버전은 다음과 같이 표기합니다.
      • v{메이저버전}.{마이너버전}
      • 한 주가 지나면 메이저버전을 올려서 배포하고 그 주에 여러 번 배포하는 경우 마이너 버전을 배포합니다.
        • ex) 02.03 ~ 02.09 🚀 v3, v3.1, v3.2 ….
        • ex) 02.10 ~ 02.16 🚀 v4 ….

     

    위와 같은 식으로 메이저버전과 마이너버전으로 나눠서 관리를 진행했던 이유는 hotfix 등 빠르게 변경사항 조치가 일어나거나 간단한 수정을 하는 경우에도 버전이 계속해서 늘려야 하는 상황을 방지하였고 메이저 버전이 수정될 때는 프롬프트 폴더를 통째로 복사해서 버전업을 하였습니다.

     

    이는 도미노를 쌓을 때 도미노 사이사이에 Safety Gap을 두는 것과 비슷합니다. 도미노 전체가 무너지는 것을 방지하기 위하여 사이사이에 도미노 하나를 눕혀 놓는데, 이처럼 메이저버전이 배포될 때는 기존 프롬프트를 업데이트하는 것이 아닌 폴더 채로 복사해서 버전업을 합니다.

     

    이미지 출처 : https://eranyona.com/the-pivotal-role-of-risk-assessment-in-medical-device-drug-and-medical-software-development-and-regulation/

     

    실제로 프로젝트 도중에 배포된 새 버전에서 일부 기능들이 동작 안 하는 이슈가 있었는데 다행히 이전 버전에서는 잘 동작하여서 새로 배포될 때까지 기다리지 않고도 다른 분들께서 이전 버전으로 테스트나 다른 작업들을 진행할 수가 있었습니다.

     

    테스트는 Langserve 프레임워크를 사용하여 메이저 버전별로 플레이그라운드에 접속하여 테스트가 가능하도록 링크를 노션에 공유해 놓았습니다. 실제로 메인 코드에는 다음과 같이 버전별로 라우트를 생성하여 관리합니다.

     

    from langserve import add_routes
    
    # v5
    add_routes(app, create_sql_chain("v5"), path="/api/v5/sql")
    add_routes(app, create_chat_chain("v5"), path="/api/v5/chat", playground_type="chat")
    
    # v6
    add_routes(app, create_sql_chain("v6"), path="/api/v6/sql")
    add_routes(app, create_chat_chain("v6"), path="/api/v6/chat", playground_type="chat")
    add_routes(app, trend_graph.compile("v6"), path="/api/v6/trend")

     

    Langserve를 사용할 경우 단점 중 하나가 PathVariable 지원이 안된다는 점이어서 버전이 많아질수록 메인 코드가 지저분해질 수 있기 때문에 고려해서 사용하시면 좋을 것 같습니다. Langserve는 지원이 더 이상 되지 않기 때문에 새로 프로젝트를 시작하는 경우라면 LangGraph 플랫폼을 사용해 주시면 좋습니다. 아래 블로그를 참고하시면 좋을 것 같습니다.

     

     

    초스피드 AI 프로토타입 개발을 위한 랭서브(Langserve) 도입기

    최근에 5개월짜리 짧은 기간의 AI 프로젝트 리드를 맡게 되면서 어떤 프레임워크와 환경 셋팅을 가져가야 구현 속도면에서 이점이 있을까 고민하던 찰나에 우아한형제들의 기술블로그 중 AI 데

    chucoding.tistory.com

     

    위와 같이 Langserve 라우트에서 프롬프트 버전을 파라미터로 받은 후에는 다음과 같이 사용가능합니다. 랭그래프에서 사용하는 방법을 예시로 들도록 하겠습니다.

     

    class TrendGraph():
        """
        추세 분석 워크플로우 정의
        """
        def __init__(self):
            self.version = "v6"
            self.llm = LLMFactory.create(provider="openai", model="gpt-4o-mini", temperature=0.0)
            self.prompt_dir = Path("app/utils/prompts") / f"{self.version}"
            self.workflow = StateGraph(GraphState)
            ...
    
        def compile(self, version: str = "v6") -> CompiledStateGraph:
        	""" 랭그래프 컴파일 """
        	self.version = version
        	self.prompt_dir = Path("app/utils/prompts") / f"{self.version}" # 버전
        	self.workflow.add_node("text_to_sql", self._text_to_sql)
        	...
    
        def _text_to_sql(self, state: GraphState) -> GraphState:
            """ 테이블 분류 + SQL 변환 """
        	trend_sql_prompt = load_prompt(f"{self.prompt_dir}/trend_sql.yaml", encoding="utf-8")
        	...

     


     

    🌠 정리하기

    지금까지 제가 적용했던 프롬프트 파일 관리 방법에 대해 정리해 보았습니다. 사실 프로젝트를 진행하면서 팀원들 중에 AI 프로젝트가 처음인 분들이 많았음에도 불구하고 5개월 동안 빠르게 성공시켜야 하는 단기간 AI 프로젝트였기 때문에 저는 무조건 진행속도 위주로 방법들을 채택하였는데요.

     

    그럼에도 많은 시행착오를 반복하며, 실제로 프로젝트 진행 속도를 증진시키는 데 있어 좋은 변화로 이어졌던 부분들 위주로 작성해 보았습니다. 물론, 모든 환경에서 적용되는 최적화된 관리방법이라는 생각은 안 들지만 AI 프로젝트를 운영하는 경우 해당 문서가 참고가 되어 조금이라도 도움이 된다면 좋을 것 같다는 생각이 듭니다.

    댓글

Designed by Tistory.