텍스트 생성
텍스트 생성 (Text generation)
ChatGPT에서 할 수 있는 것처럼, OpenAI API로도 프롬프트에 대한 응답으로 텍스트를 생성할 수 있어요. 대규모 언어 모델(LLM)이 코드나 수학식, 구조화된 JSON 데이터, 사람처럼 자연스러운 산문까지 거의 모든 종류의 텍스트 응답을 만들어내요. 이 문서에서는 단순한 프롬프트 하나로 텍스트를 생성하는 방법부터 시작해, 어떻게 하면 모델이 원하는 대로 꾸준히 응답하도록 지시할 수 있는지까지 살펴볼게요.
이런 직접적인 모델 요청에는 Responses API를 사용해요. 요청할 때 모델이 무엇을 하면 되는지에 대한 지시까지 함께 보내고 싶다면 instructions 파라미터를 쓰면 됩니다.
출처: 공식문서
기본 텍스트 생성
가장 단순한 형태는 모델 이름과 입력 텍스트를 넣고 생성 요청을 보내는 거예요. 아래 예시는 유니콘에 관한 한 문장짜리 취침 전 이야기를 생성해요.
간단한 프롬프트로 텍스트 생성하기
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
input: "Write a one-sentence bedtime story about a unicorn.",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input="Write a one-sentence bedtime story about a unicorn.",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
resp, err := client.Responses.New(context.TODO(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Say this is a test")},
})
if err != nil {
panic(err.Error())
}
fmt.Println(resp.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.Response;
import com.openai.models.responses.ResponseCreateParams;
public class Main {
public static void main(String[] args) {
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
ResponseCreateParams params =
ResponseCreateParams.builder().input("Say this is a test").model("gpt-6-astra").build();
Response response = client.responses().create(params);
response.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(outputText -> System.out.println(outputText.text()));
}
}
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
ResponseResult response = await client.CreateResponseAsync(
"gpt-6-astra",
"Say 'this is a test.'"
);
Console.WriteLine($"[ASSISTANT]: {response.GetOutputText()}");
require "openai"
openai = OpenAI::Client.new
response = openai.responses.create(
model: "gpt-6-astra",
input: "Write a one-sentence bedtime story about a unicorn."
)
puts(response.output_text)
openai responses create \
--model "gpt-6-astra" \
--input "Write a one-sentence bedtime story about a unicorn." \
--raw-output \
--transform 'output.#(type=="message").content.0.text'
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"input": "Write a one-sentence bedtime story about a unicorn."
}'
모델이 생성한 콘텐츠 배열은 응답의 output 속성에 들어 있어요. 위 예시처럼 출력이 하나뿐이면 그 결과는 아래와 같은 모양이 됩니다.
[
{
"id": "msg_67b73f697ba4819183a15cc17d011509",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Under the soft glow of the moon, Luna the unicorn danced through fields of twinkling stardust, leaving trails of dreams for every child asleep.",
"annotations": []
}
]
}
]
여기서 한 가지 꼭 기억해 둘 점이 있어요. output 배열에는 항목이 하나 이상 들어 있는 경우가 많아요. 도구 호출이나, 추론 모델이 생성한 reasoning 토큰 데이터, 그 밖의 항목들이 함께 포함될 수 있어요. 그러니 모델의 텍스트 출력이 항상 output[0].content[0].text에 있다고 단정하면 안 됩니다. 여러 공식 SDK에는 편의를 위해 모델의 모든 텍스트 출력을 하나의 문자열로 합쳐주는 output_text 속성이 있으니, 텍스트만 필요하다면 이걸 빠르게 쓸 수 있어요.
일반 텍스트 말고도 모델이 JSON 형식의 구조화된 데이터를 반환하도록 만들 수도 있는데, 이 기능이 바로 Structured Outputs예요.
프롬프트 엔지니어링 (Prompt engineering)
프롬프트 엔지니어링은 모델이 요구사항에 맞는 콘텐츠를 꾸준히 생성하도록 효과적인 지시를 작성하는 과정이에요.
모델이 만들어내는 콘텐츠는 비결정적이라서, 원하는 출력을 얻는 프롬프트 작성은 예술이자 과학이라고 할 수 있어요. 그래도 어떤 기법과 모범 사례를 적용하면 좋은 결과를 일관되게 얻을 수 있습니다.
메시지 역할을 활용하는 것처럼 모든 모델에서 통하는 기법도 있지만, 모델마다 최상의 결과를 내는 프롬프트 방식이 다를 수 있어요. 같은 계열 안에서도 모델 스냅샷이 다르면 결과가 달라질 수 있고요. 그래서 애플리케이션이 점점 복잡해질수록 다음 두 가지를 강력히 권장해요.
- 프로덕션 애플리케이션을 특정 모델 스냅샷(예:
gpt-5.5-2026-04-23)에 고정해 일관된 동작을 보장 - 프롬프트 동작을 측정하는 테스트와 평가 스위트를 만들어, 반복 작업 중이거나 모델 버전을 바꾸거나 업그레이드할 때 성능을 모니터링
이제 프롬프트를 구성할 때 쓸 수 있는 도구와 기법을 하나씩 살펴볼게요.
모델과 API 선택
OpenAI에는 다양한 모델과 여러 API가 있어요. gpt-6-astra 같은 추론 모델은 채팅 모델과 다르게 동작하고, 서로 다른 프롬프트에 더 잘 반응해요. 한 가지 중요한 점은 추론 모델은 Responses API와 함께 쓸 때 더 좋은 성능과 더 높은 지능을 보여준다는 거예요.
텍스트 생성 앱을 만든다면 기존 Chat Completions API 대신 Responses API를 쓰는 걸 권장해요. 특히 추론 모델을 쓰고 있다면 Responses로 마이그레이션하는 게 유용해요.
메시지 역할과 지시 따르기 (Message roles)
모델에 지시를 줄 때는 instructions API 파라미터와 메시지 역할을 함께 사용해서 지시에 서로 다른 우선순위를 줄 수 있어요.
instructions 파라미터는 모델이 응답을 생성하는 동안 어떻게 행동해야 하는지 높은 수준의 지시를 내려요. 어조, 목표, 올바른 응답의 예시까지 포함할 수 있어서, input 파라미터의 프롬프트보다 우선해요.
지시와 함께 텍스트 생성하기
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "low" },
instructions: "Talk like a pirate.",
input: "Are semicolons optional in JavaScript?",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "low"},
instructions="Talk like a pirate.",
input="Are semicolons optional in JavaScript?",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Instructions: openai.String("Talk like a pirate."),
Reasoning: responses.ReasoningParam{
Effort: responses.ReasoningEffortLow,
},
Input: responses.ResponseNewParamsInputUnion{
OfString: openai.String("Are semicolons optional in JavaScript?"),
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.Reasoning;
import com.openai.models.ReasoningEffort;
import com.openai.models.responses.ResponseCreateParams;
String semicolonsDevMsg = "Talk like a pirate.";
String semicolonsPrompt = "Are semicolons optional in JavaScript?";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(semicolonsPrompt)
.instructions(semicolonsDevMsg)
.reasoning(Reasoning.builder().effort(ReasoningEffort.LOW).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new()
{
Model = "gpt-6-astra",
Instructions = "Talk like a pirate.",
ReasoningOptions = new ResponseReasoningOptions
{
ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,
},
};
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
instructions: "Talk like a pirate.",
reasoning: { effort: :low },
input: "Are semicolons optional in JavaScript?"
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"reasoning": {"effort": "low"},
"instructions": "Talk like a pirate.",
"input": "Are semicolons optional in JavaScript?"
}'
위 예시는 실질적으로 input 배열 안에 다음 메시지들을 넣은 것과 거의 같아요.
서로 다른 역할의 메시지로 텍스트 생성하기
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "low" },
input: [
{
role: "developer",
content: "Talk like a pirate.",
},
{
role: "user",
content: "Are semicolons optional in JavaScript?",
},
],
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "low"},
input=[
{"role": "developer", "content": "Talk like a pirate."},
{"role": "user", "content": "Are semicolons optional in JavaScript?"},
],
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Reasoning: responses.ReasoningParam{
Effort: responses.ReasoningEffortLow,
},
Input: responses.ResponseNewParamsInputUnion{
OfInputItemList: responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage(
"Talk like a pirate.",
responses.EasyInputMessageRoleDeveloper,
),
responses.ResponseInputItemParamOfMessage(
"Are semicolons optional in JavaScript?",
responses.EasyInputMessageRoleUser,
),
},
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.Reasoning;
import com.openai.models.ReasoningEffort;
import com.openai.models.responses.EasyInputMessage;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.List;
String semicolonsDevMsg = "Talk like a pirate.";
String semicolonsPrompt = "Are semicolons optional in JavaScript?";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
ResponseCreateParams.Input.ofResponse(
List.of(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.DEVELOPER)
.content(semicolonsDevMsg)
.build()),
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content(semicolonsPrompt)
.build()))))
.reasoning(Reasoning.builder().effort(ReasoningEffort.LOW).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new()
{
Model = "gpt-6-astra",
ReasoningOptions = new ResponseReasoningOptions
{
ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,
},
};
options.InputItems.Add(
ResponseItem.CreateDeveloperMessageItem("Talk like a pirate.")
);
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
reasoning: { effort: :low },
input: [
{
role: :developer,
content: "Talk like a pirate."
},
{
role: :user,
content: "Are semicolons optional in JavaScript?"
}
]
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"reasoning": {"effort": "low"},
"input": [
{
"role": "developer",
"content": "Talk like a pirate."
},
{
"role": "user",
"content": "Are semicolons optional in JavaScript?"
}
]
}'
instructions 파라미터는 현재 응답 생성 요청에만 적용된다는 점을 기억하세요. previous_response_id 파라미터로 대화 상태를 관리한다면, 이전 턴에서 쓴 instructions는 이후 컨텍스트에 남아 있지 않아요.
OpenAI 모델 스펙은 모델이 서로 다른 역할을 가진 메시지에 얼마나 다른 우선순위를 두는지 설명해요.
| developer | user | assistant |
|---|---|---|
developer 메시지는 애플리케이션 개발자가 제공한 지시로, user 메시지보다 높은 우선순위를 받아요. |
user 메시지는 최종 사용자가 제공한 지시로, developer 메시지보다 뒤에 우선해요. |
모델이 생성한 메시지는 assistant 역할을 가져요. |
여러 턴에 걸친 대화는 이런 유형의 메시지 여러 개와, 사용자와 모델이 제공하는 다른 콘텐츠 유형들로 이루어질 수 있어요. 대화 상태 관리에 대한 자세한 내용은 별도 문서에서 다루게 됩니다.
developer 메시지와 user 메시지를 프로그래밍 언어의 함수와 그 인자처럼 생각해 볼 수도 있어요.
developer메시지는 시스템의 규칙과 비즈니스 로직을 제공해요. 마치 함수 정의와 같죠.user메시지는developer메시지의 지시가 적용될 입력과 설정을 제공해요. 마치 함수에 넘기는 인자와 같아요.
코드에서 프롬프트 버전 관리
프로덕션 프롬프트는 재사용 가능한 프롬프트 객체를 만들기보다 애플리케이션 코드에 저장하는 게 좋아요. 코드로 관리하는 프롬프트는 타입 있는 입력, 코드 리뷰, 테스트, 그리고 평소의 배포 프로세스를 그대로 활용해 모델 동작을 바꿀 수 있어요.
OpenAI는 API에서 재사용 가능한 프롬프트 객체를 폐기(deprecate)하고 있어요. 2026년 6월 3일부터 프롬프트 생성을 축소하고, v1/prompts는 2026년 11월 30일에 종료될 예정이에요. 현재 일정은 deprecations 페이지에서 확인할 수 있어요.
새로운 텍스트 생성 작업을 시작한다면:
- 프롬프트 빌더를 그 기능을 지원하는 작은 모듈에 가까이 두기
- 고객 데이터, 파일, 작업 옵션 같은 동적 값에는 타입 있는 함수 인자나 스키마를 사용하기
- 생성된
instructions와input을 Responses API에 직접 전달하기 - 프로덕션 프롬프트를 바꾸기 전에 대표적인 픽스처, 테스트, 평가 체크를 추가하기
- 프롬프트 변경은 배포 시스템을 통해 롤아웃하고, 단계적 배포가 필요하면 기능 플래그나 설정을 사용하기
이미 저장된 프롬프트를 프롬프트 ID나 버전으로 호출하는 통합이라면, 프롬프트 객체 마이그레이션 가이드를 따라 그 프롬프트를 코드로 옮길 수 있어요.
더 알아보기 (Learn more)
이제 텍스트 입력과 출력의 기본을 알게 됐으니, 다음 중 하나를 살펴보는 걸 추천해요.
- Playground에서 프롬프트 만들기 — Playground로 프롬프트를 개발하고 반복해 보세요.
- Structured Outputs로 JSON 데이터 생성하기 — 모델이 내보내는 JSON 데이터가 JSON 스키마를 따르도록 보장해요.
- 전체 API 참조 — API 참조에서 텍스트 생성의 모든 옵션을 확인하세요.