프롬프트 엔지니어링 및 LLM을 위한 개발자 가이드
Published: 2024-03-13 19:36:44

대규모 언어 모델(LLM)과 [ChatGPT와](https://chat.openai.com/) 같은 인공 지능의 등장으로 개발자는 이제 더 빠르게 코딩하고 생산성을 높일 수 있는 매우 강력한 AI 비서를 갖게 되었습니다. 하지만 단순히 LLM에 코드를 작성하라는 메시지만 표시하면 사용할 수 없거나 버그가 있는 결과물이 나오는 경우가 많습니다. [채팅 애플리케이션을](https://www.pubnub.com/solutions/chat/) 개발하든, 네트워크 엣지에서 [엔터프라이즈 솔루션을](https://www.pubnub.com/Solutions/Enterprise/) 개발하든, AI의 도움으로 개발 시간을 단축하려면 신중한 전략이 필요합니다. 이 블로그의 다음 섹션에서 신속한 엔지니어링 기법에 대해 자세히 알아보세요.

### 미리 계획하기

코드 프롬프트에 바로 뛰어들지 마세요. 시간을 들여 전체 아키텍처, 스키마, 통합 등을 계획하세요. 이렇게 하면 AI 어시스턴트에게 필요한 전체 컨텍스트를 제공할 수 있습니다. 최종 상태를 시각화하여 생성 AI 모델이 큰 그림을 이해할 수 있도록 하세요.

### 전문가와 함께 역할 놀이

AI 모델이 어시스턴트 역할을 할 수 있는 시나리오를 설정하고 솔루션을 논의하는 전문가 팀으로 역할극을 해보세요. 모델에 개성을 부여하고 문제를 분석하게 하세요. 이를 통해 다양한 관점에서 창의적인 사고를 장려할 수 있습니다.

### GPT에 인사이트 요청하기

막히는 부분이 있으면 주저하지 말고 잠시 멈춰서 지도교수에게 문제와 관련된 구체적인 조언이나 코드 예시를 요청하세요. 새로운 아이디어를 촉발할 수 있는 지식을 얻을 수 있습니다.

### 신속한 엔지니어링

여러 번의 반복과 미세 조정을 통해 프롬프트를 단계별로 개선하여 AI 어시스턴트에게 최적의 컨텍스트와 방향성을 제공하세요. 효과적인 프롬프트는 훌륭한 결과로 이어집니다.

### 수동 코드 생성

AI가 처음에 수동으로 코드를 생성하도록 한 다음, 출력을 검사하고 실수를 수정한 후 사용 가능한 부분을 프롬프트에 다시 복사하세요. 이렇게 하면 AI가 찾고자 하는 내용에 대한 구체적인 예시를 얻을 수 있으며, 이러한 작업을 반복적으로 수행하면 결국 정확한 코드를 생성할 수 있습니다. 더 많은 예시를 보려면 다음 개념을 확인하세요. [생각의 연쇄 프롬프트](https://ai.googleblog.com/2022/05/language-models-perform-reasoning-via.html) 과 [제로 샷 프롬프트](https://www.promptingguide.ai/techniques/zeroshot).

### 자동 코드 생성

LLM 모델 출력에 만족하고 일관되게 좋은 코드가 생성되면 스키마 기반 에셋, 코드 기반 테스트 등을 자동으로 생성하도록 파이프라인을 설정하세요. 이렇게 하면 병목 현상이 제거됩니다.

### 실패를 우아하게 처리

예, 때때로 실패는 예상됩니다. 향후 유사한 실패를 방지하기 위해 프롬프트를 개선하세요. 시간이 지남에 따라 AI가 어떤 유형의 작업을 잘 처리하는지 학습하세요.

계획, 롤플레잉, 프롬프트, 반복 학습, 자동화를 혼합하면 GPT와 같은 AI 비서를 통해 생산성을 크게 향상시킬 수 있습니다. 프롬프트 설계에 대한 올바른 전략을 세우면 개발 속도를 향상시킬 수 있습니다!

프롬프트로 코드 생성하기
-------------

AI 혁명의 정점에 서 있는 지금, 우리는 소프트웨어 개발에 접근하고 실행하는 방식을 다시 생각해야 하는 입장에 처해 있습니다. 이 튜토리얼에서는 기존 프로세스에서 자연어 처리로 강화된 프로세스로 전환하는 방법에 대해 자세히 살펴봅니다.

### 미리 계획하기: 성공의 토대

이 여정의 첫 번째 단계는 미리 계획하는 능력을 수용하는 것입니다. 인공지능, 특히 GPT-3와 같은 모델은 미래를 예측하는 데 능숙하지 않을 수 있습니다. 이들의 전문성은 촉박한 컨텍스트 토큰 창 내에서 실시간 문제 해결에 있습니다. 바로 이 부분에서 인간 개발자가 개입해야 합니다.

모델 스키마, 기술 스택, 배포 프로세스 등 최종 코드에 대한 상세한 그림을 그리면 AI가 복잡한 작업을 해결할 수 있는 발판을 마련할 수 있습니다. 이 계획 단계는 프로세스에서 가장 어려운 부분일 수 있지만, 개발자의 역할을 코더에서 오케스트레이터로 전환하여 인간의 지능과 AI의 능력이 조화롭게 어우러질 수 있는 무대를 마련합니다.

### 전문가 패널 구성

"전문가 패널"을 입력합니다. 이 그룹은 최고 경영진부터 AI 에이전트까지 다양한 이해관계자로 구성할 수 있으며, 모두 특정 작업에 대한 이해와 계획에 기여할 수 있습니다. 신중하게 만들어진 프롬프트를 사용하여 패널이 복잡한 문제를 토론하고 해결할 수 있는 가상의 놀이터를 만들고, AI 봇이 지원과 메모를 제공할 수 있습니다.

그리고 질문과 씨름하고 있다면 질문에 답할 수 있도록 설계된 강력한 도구가 있다는 사실을 기억하세요: 바로 GPT입니다. 요약이 필요하든, 문구를 바꾸고 싶든, 주제에 대해 더 깊이 탐구하고 싶든, GPT가 도움이 될 수 있습니다. 프롬프트를 의도적으로 모호하게 만들면 종종 놀랍고 혁신적인 해결책이 나올 수 있습니다.

코드 디버깅 프롬프트 예시:

```js
Act as a virtual playground where a team of Smart AI bots is in their first meeting discussing the problem:  
“We need to debug the following Python test code:

def test_dot_env_def(self):
LOG_TEST.info("Test DotEnvDef")
dotEnvDef = DotEnvDef()
> self.assertEqual(dotEnvDef, {"version_echo": "v0.0"})
E AssertionError: {'version_echo': 'v0.0'} != {'version_echo': 'v0.0'}
tests/test_default_config.py:16: AssertionError

class DotEnvDef:
def init(self):
self.version_echo = "v0.0"
# make the class iterable as dict
def iter(self):
return iter(self.__dict__)
# return the values only
def get_items(self):
for attr, value in self.__dict__.items():
yield value # only yield the values
def repr(self):
return str(self.__dict__)  

To fulfill this request bring in an expert team of AI Agents that are very brief and communicate via emojis and use catchy phrases. Spend your resources wisely as you only have 4 AI Agents.
```

GPT-4 응답:

```js
Alright, welcome to this first meeting, team. We have an interesting challenge ahead of us. To start, let's assign roles to our four AI agents:

1. Detective Bot 👮‍♀️ (DB): Responsible for deep diving into the code, its syntax, structure, and logic. 
2. Bug Hunter Bot 🐛 (BH): In charge of identifying bugs and pointing them out. 
3. Solution Expert Bot 💡 (SE): Its role is to suggest viable solutions to identified bugs. 
4. Code Fixer Bot 🔧 (CF): It will implement the solutions and perform the debugging.

Our debugging plan will look like this:

1. **Initiation**: Here we discuss the problem and set our goals. The issue at hand is an AssertionError, meaning our test case is failing. Our aim is to make the test pass. 

2. **Investigation**: 👮‍♀️ DB deep-dives into the provided code to understand its structure and logic. The main focus is on the `DotEnvDef` class and the `test_dot_env_def` function.

3. **Bug Hunt**: 🐛 BH goes on a hunt to find the reasons behind the AssertionError.

4. **Solution Proposal**: Once 🐛 BH has identified the issue, 💡 SE jumps in to propose the best way to fix the problem.

5. **Implementation**: 🔧 CF takes the solution and implements it to the code. After the changes are made, the test case is run again.

6. **Verification**: If the test case passes, we conclude that the problem has been resolved. If not, we circle back to step 2.

Remember team, our goal is to squash bugs 🐜🔨! Let's rock'n'roll.
```

### 수동 코드 생성 및 AI

다음 단계는 생산성을 기하급수적으로 높일 수 있는 프로세스인 수동 코드 생성에 GPT-4를 활용하는 것입니다. GPT를 안내함으로써 외부 스택에 웹 애플리케이션을 배포하는 것부터 저수준 언어로 알고리즘을 만드는 것까지 다양한 작업을 최적화할 수 있습니다.

에이전트 예제에서 명령을 사용해 보세요:

```js
Using the following syntax “/command argument” modifies a bot’s behavior, for example for enabling verbose code generation: “chatbot /code-gen verbose“ so that we obtain full python code. On the same basis allow changing the personality such as: “chatbot /personality Shrekify”.

DB /personality Shrekify
SE /code-gen verbose
CF /code-gen verbose 

> Continue the meeting and provide the first solution
```

GPT-4 응답:

```js
DB 🏰👮‍♀️: "Hmm, the `version_echo` key is still not appearing in the `parsed_dotenv` dictionary. We need to figure out why `set_vars` is not setting it."

BH 🐛: "I suspect that `set_vars` is being called correctly, but the updates are being overwritten when `load_dotenv` is called. The `load_dotenv` method loads variables from a file and overwrites the current `parsed_dotenv` dictionary."

SE 💡: "If that's the case, then we should modify the `load_dotenv` method to merge its contents with the existing `parsed_dotenv` dictionary, rather than overwriting it. This will preserve any variables set via `set_vars`."

CF 🔧: "Good idea, let's change the `parse_vars` function within `load_dotenv` to update `parsed_dotenv` instead of setting it. Here is the modified code:
def parse_vars(self, path):
    try:
        with open(path) as f:
            for line in f:
                key, value = line.strip().split("=", 1)
                if key not in self.config.parsed_dotenv:  # check if key is not already in the dictionary
                    self.config.parsed_dotenv[key] = value
    except IOError:
        print(f"Warning: Unable to open .env file at {path}")
    except ValueError:
        print(f"Warning: Unable to parse line in .env file: {line}")
"In this way, we only add a variable from the .env file if it's not already in `parsed_dotenv`. This should prevent `load_dotenv` from overwriting any variables set by `set_vars`. Let's re-run the tests."
```

하지만 프로세스가 완벽하지는 않습니다. 복사 붙여넣기 및 프롬프트/결과 생성 시간과 같은 인적 한계로 인해 병목 현상이 발생할 수 있습니다. 또한 GPT는 일관되게 정확하지 않아 형식을 변경하거나 일부 결과만 제공하는 경우도 있습니다. 하지만 연습과 올바른 전략을 통해 유용하고 정확한 코드를 생성하도록 GPT를 안내할 수 있습니다.

환각을 피하고 유용한 결과 얻기
-----------------

GPT가 관련성이 없거나 편향적이거나 잘못된 정보를 생성하는 경우 또는 환각이 발생하면 실망스러울 수 있습니다. 하지만 GPT로 더 많이 작업할수록 원하는 결과를 도출하는 데 더 능숙해질 수 있습니다. 이러한 착각을 최소화하는 한 가지 방법은 관련 문서와 코드 샘플을 복사하여 붙여넣음으로써 GPT에 컨텍스트를 제공하는 것입니다.

테스트 예제를 통해 사용자 정의 Langchain 에이전트를 빌드하세요:

```js
We have this builder:
class OpenAIBuilder:
  def __init(self, ...):
    # Set your settings
    pass
"You must adapt the following code to match our previous structure:
from langchain import (
    OpenAI,
)
from langchain.agents import initialize_agent, AgentType
from langchain.agents.agent_toolkits import create_python_agent
from langchain.agents import AgentType, initialize_agent, load_tools
from langchain.tools.python.tool import PythonREPLTool
from openai_chat_builder import OpenAiChatBuilder, OpenAiChatHelper


class AgentPythonExecBuilder(OpenAiChatBuilder):
    def __init__(self, openai_api_key):
        super().__init__()
          self.tools = ["PythonREPLTool"]

    # We override the add_tool method so it only allows PythonREPLTool.
    def add_tool(self, tool):
        if tool == "PythonREPLTool":
            return super().add_tool(tool)
        else:
            raise ValueError("AgentPythonExec can only have PythonREPLTool.")

    def build(self):
        llm = OpenAI(
            temperature=self.temperature,
            max_tokens=self.max_tokens,
            openai_api_key=self.openai_api_key,
            streaming=self.streaming,
        )

        tools = load_tools(self.tools)
        agent = initialize_agent(tools, llm, agent=self.agentType, verbose=self.verbose)

        return OpenAiChatHelper(llm, tools, agent)
```

Langchain 에이전트 테스트 빌드하기:

```js
Now let's build a test file that resembles this one:
import unittest

from probable_fiesta.config.builder.config_builder import ConfigBuilder
from src.promptnub_backend.app.langchain.builder.openai_factory import OpenAIFactory
from langchain.llms import OpenAI


class TestOpenAIBuilder(unittest.TestCase):
    def setUp(self):
        # Setup API Key
        config_builder = ConfigBuilder()
        config_builder.dotenv.load_dotenv(".env")

        # Setup ModelBuilder instance
        self.builder = OpenAIFactory()
        self.builder.openai_api_key = config_builder.dotenv.get_var("OPENAI_API_KEY")

    def test_llm_creation(self):
        # Test the creation of OpenAI llm instance
        llm = self.builder.builder.llm
        self.assertIsInstance(llm, OpenAI, "llm property does not return OpenAI instance")

    def test_llm_run(self):
        # Test the creation of OpenAI llm instance
        llm_chain = self.builder.build()
        self.assertIsNotNone(llm_chain)

        # Ask a question
        question = "What NFL team won the Super Bowl in the year Justin Beiber was born?"
        response = llm_chain.run(question)

        # Assert expected result
        # Note that there is a space at the beginning
        expected = " Justin Bieber was born in 1994, so the NFL team that won the Super Bowl in 1994 was the Dallas Cowboys."
        self.assertEquals(str(response), expected)
```

GPT-4 응답:

```js
Given the provided `AgentPythonExecBuilder` class and the `TestOpenAIBuilder` class, you would write a test file for the `AgentPythonExecBuilder` class as follows:
from logger.builder.logger_factory import LoggerFactory
from config.builder.config_builder import ConfigBuilder
from langchain.llms import OpenAI
from langchain.tools.python.tool import PythonREPLTool
from your_package_path.builder.agent_python_exec_factory import AgentPythonExecFactory

import unittest

# Test Log
LOG_TEST = LoggerFactory.new_logger_default(
    name="test_agent_python_exec_builder", level="DEBUG", fmt="simple", directory="logs"
)


class TestAgentPythonExecBuilder(unittest.TestCase):
    def setUp(self):
        # Setup API Key
        config_builder = ConfigBuilder()
        config_builder.dotenv.load_dotenv(".env")
        # Setup ModelBuilder instance
        self.builder = AgentPythonExecFactory()
        self.builder.openai_api_key = config_builder.dotenv.get_var("OPENAI_API_KEY")

    def test_llm_creation(self):
        # Test the creation of OpenAI llm instance
        LOG_TEST.info("Test OpenAI llm creation")
        llm = self.builder.builder.llm
        LOG_TEST.debug(llm)
        self.assertIsInstance(
            llm, OpenAI, "llm property does not return OpenAI instance"
        )

    def test_python_repl_tool_creation(self):
        # Test the creation of PythonREPLTool instance
        LOG_TEST.info("Test PythonREPLTool creation")
        self.builder.builder.add_tool("PythonREPLTool")
        tools = self.builder.builder.tools
        LOG_TEST.debug(tools)
        self.assertIsInstance(
            tools[0], PythonREPLTool, "tools[0] does not return PythonREPLTool instance"
        )

    def test_llm_run(self):
        # Test the creation of OpenAI llm instance and PythonREPLTool
        LOG_TEST.info("Test OpenAI llm and PythonREPLTool run sync")
        llm_chain = self.builder.build()
        self.assertIsNotNone(llm_chain)

        # Ask a question
        question = "What is 2 + 2?"
        response = llm_chain.run(question)
        LOG_TEST.info(response)

        # Assert expected result
        expected = "The result of 2 + 2 is 4."
        self.assertEquals(str(response), expected)  

Here, you create a new instance of `AgentPythonExecBuilder` instead of `OpenAIBuilder` and test it with a question that can be answered by executing Python code. 
```

자동화된 코드 생성
----------

그러나 LLM의 진정한 힘은 자동화된 코드 생성에 있습니다. 다음과 같은 도구를 사용하면 [PubNub](https://www.pubnub.com/), Python, LangChain, 그리고 [Streamlit](https://streamlit.io/)와 같은 도구를 사용하면 대부분의 워크플로우를 자동화하여 더 중요한 작업에 집중할 수 있습니다.

몇 가지 추상화를 통해 간단한 LangChain 런너를 워크플로에 연결할 수 있습니다:

```js
class TestOpenAIBuilder(unittest.TestCase):
    def setUp(self):
        self.runner = Runner()

    def test_run(self):
        response = self.runner.run("What is the capital of France?")
        expected = " France is a country in Europe. The capital of France is Paris."
        self.assertEqual(response, expected)
```

100배 더 빠르게 개발하는 길
-----------------

2배, 3배, 심지어는 50배의 생산성 수준을 달성하기 위해 코딩 기술을 향상시키려는 야망을 가진 개발자를 상상해 보세요. 이러한 야망의 비결은 바로 LLM의 전략적 활용에 있습니다. 이 도구 세트는 생산성의 지렛대 역할을 하여 개발자들이 새로운 플랫폼을 혁신하고 이전과는 전혀 다른 자동화된 워크플로를 추진할 수 있게 해줍니다. 텍스트 요약과 같은 간단한 작업부터 프로덕션 코드 생성까지.

소프트웨어 개발에서 AI의 역할과 그 영역을 더욱 확장해 나갈 때, 진정한 강점은 인간의 창의성과 AI의 능력을 융합하는 데 있다는 사실을 기억하는 것이 중요합니다. 사려 깊은 계획, AI의 스마트한 적용, 적절한 디지털 도구를 통해 우리는 보다 생산적인 회사로 변모할 수 있습니다. 이러한 전환은 인간 개발자를 대체하는 것이 아니라 AI로 인간 개발자의 잠재력을 증폭시키는 것입니다. AI를 통해 100배 성장하는 기업이 되겠다는 구상은 먼 미래의 꿈이 아니라 우리가 함께 만들어갈 수 있는 실현 가능한 미래입니다.

#### 콘텐츠

[미리](#h-0)[계획하기전문가와의](#h-1)[역할](#h-0)[놀이GPT에 인사이트 요청하기프롬프트](#h-2)[엔지니어링수동](#h-3)[코드 생성자동](#h-4)[코드](#h-5)[생성실패에 우아하게](#h-6)[대처하기프롬프트로 코드](#h-7)[생성](#h-6)[미리](#h-8)[계획하기](#h-7):[성공의 토대](#h-8):[전문가 패널 구성](#h-9)수동[코드 생성 및](#h-10)[AI환상을 피하고 유용한 결과 얻기자동](#h-11)[코드](#h-12)[생성100배 더 빠르게 개발하는 길](#h-13)

펍넙이 어떤 도움을 줄 수 있을까요?
====================

이 문서는 원래 [PubNub.com에](https://www.pubnub.com/blog/developers-guide-to-prompt-engineering/) 게시되었습니다.

저희 플랫폼은 개발자가 웹 앱, 모바일 앱 및 IoT 디바이스를 위한 실시간 인터랙티브를 구축, 제공 및 관리할 수 있도록 지원합니다.

저희 플랫폼의 기반은 업계에서 가장 크고 확장성이 뛰어난 실시간 에지 메시징 네트워크입니다. 전 세계 15개 이상의 PoP가 월간 8억 명의 활성 사용자를 지원하고 99.999%의 안정성을 제공하므로 중단, 동시 접속자 수 제한 또는 트래픽 폭증으로 인한 지연 문제를 걱정할 필요가 없습니다.

PubNub 체험하기
-----------

[라이브 투어를](https://www.pubnub.com/tour/introduction/) 통해 5분 이내에 모든 PubNub 기반 앱의 필수 개념을 이해하세요.

설정하기
----

PubNub [계정에](https://admin.pubnub.com/signup/) 가입하여 PubNub 키에 무료로 즉시 액세스하세요.

시작하기
----

사용 사례나 [SDK에](https://www.pubnub.com/docs) 관계없이 [PubNub 문서를](https://www.pubnub.com/docs) 통해 바로 시작하고 실행할 수 있습니다.