Skip to content

프롬프트 커스터마이징 (Customizing Prompts)

왜 프롬프트를 직접 커스터마이징할까요?

CrewAI의 기본 프롬프트는 많은 시나리오에서 잘 작동해요. 그런데 에이전트가 "그냥 잘 돌아가는" 수준을 넘어서, 특정 모델이나 도메인에 맞춰 정밀하게 동작하게 만들고 싶을 때가 있어요. 이때 저수준(low-level) 커스터마이징이 열리는 문이 됩니다. 기본 프롬프트를 그대로 쓰면 감당하기 어려운 일들을, 직접 손보면 훨씬 유연하고 강력하게 제어할 수 있어요.

구체적으로 이런 이유들이 있어요.

  1. 특정 LLM에 최적화 – GPT-4, Claude, Llama 같은 모델들은 각자 구조에 잘 맞는 프롬프트 형식이 있어요. 같은 설정이라도 모델마다 결과가 달라질 수 있죠.
  2. 언어 변경 – 영어가 아닌 다른 언어로만 동작하는 에이전트를 만들 수 있어요. 뉘앙스까지 정확하게 처리하도록 만들 수 있죠.
  3. 복잡한 도메인 특화 – 헬스케어, 금융, 법률처럼 전문성이 아주 중요한 분야에 맞춰 프롬프트를 조정할 수 있어요.
  4. 톤과 스타일 조정 – 에이전트를 더 격식 있게, 더 캐주얼하게, 더 창의적으로, 더 분석적으로 만들 수 있어요.
  5. 초맞춤(super custom) 사용 사례 지원 – 프로젝트별 복잡한 요구사항을 충족하도록 고급 프롬프트 구조와 포맷을 활용할 수 있어요.

이 가이드에서는 CrewAI의 프롬프트를 더 낮은 수준에서 다루는 방법을 살펴봐요. 에이전트가 어떻게 생각하고 상호작용하는지 세밀하게 제어할 수 있게 되는 거죠.

CrewAI의 프롬프트 시스템 이해하기

내부적으로 CrewAI는 광범위하게 커스터마이징할 수 있는 모듈식 프롬프트 시스템을 써요.

  • 에이전트 템플릿(Agent templates) – 각 에이전트가 맡은 역할에 어떻게 접근할지를 정해요.
  • 프롬프트 슬라이스(Prompt slices) – 태스크, 도구 사용, 출력 구조 같은 특수한 동작을 제어해요.
  • 오류 처리(Error handling) – 실패, 예외, 타임아웃에 에이전트가 어떻게 대응할지를 정해요.
  • 도구별 프롬프트(Tool-specific prompts) – 도구가 어떻게 호출되고 활용되는지에 대한 상세 지침을 정의해요.

이 요소들이 어떻게 구성돼 있는지 보고 싶다면, CrewAI 저장소의 원본 프롬프트 템플릿을 확인해보세요. 여기서 필요에 따라 override하거나 조정하면 고급 동작을 풀 수 있어요.

기본 시스템 지시(설정) 이해하기

운영 투명성 문제: CrewAI는 사용자가 인지하지 못하는 사이에 기본 지시를 프롬프트에 자동으로 주입해요. 이 섹션은 그 내부 동작과 완전한 제어를 얻는 방법을 설명해요.

role, goal, backstory로 에이전트를 정의하면, CrewAI는 포맷과 동작을 제어하는 추가 시스템 지시를 자동으로 덧붙여요. 운영 환경에서는 프롬프트의 완전한 투명성이 필요하기 때문에, 이런 기본 주입을 이해하는 게 중요해요.

CrewAI가 자동으로 주입하는 것

에이전트 구성에 따라 추가되는 기본 지시가 달라져요.

도구가 없는 에이전트

"I MUST use these formats, my job depends on it!"

도구가 있는 에이전트

"IMPORTANT: Use the following format in your response:

Thought: you should always think about what to do
Action: the action to take, only one name of [tool_names]
Action Input: the input to the action, just a simple JSON object..."

구조화된 출력(JSON/Pydantic)

"Ensure your final answer contains only the content in the following format: {output_format}
Ensure the final output does not include any code block markers like ```json or ```python."

완전한 시스템 프롬프트 확인하기

LLM에 실제로 어떤 프롬프트가 전송되는지 정확히 보고 싶다면, 생성된 프롬프트를 검사할 수 있어요.

from crewai import Agent, Crew, Task
from crewai.utilities.prompts import Prompts

# 에이전트 생성
agent = Agent(
    role="Data Analyst",
    goal="Analyze data and provide insights",
    backstory="You are an expert data analyst with 10 years of experience.",
    verbose=True
)

# 샘플 태스크 생성
task = Task(
    description="Analyze the sales data and identify trends",
    expected_output="A detailed analysis with key insights and trends",
    agent=agent
)

# 프롬프트 생성기 생성
prompt_generator = Prompts(
    agent=agent,
    has_tools=len(agent.tools) > 0,
    use_system_prompt=agent.use_system_prompt
)

# 실제 프롬프트 생성 및 검사
generated_prompt = prompt_generator.task_execution()

# LLM에 전송될 완전한 시스템 프롬프트 출력
if "system" in generated_prompt:
    print("=== SYSTEM PROMPT ===")
    print(generated_prompt["system"])
    print("\n=== USER PROMPT ===")
    print(generated_prompt["user"])
else:
    print("=== COMPLETE PROMPT ===")
    print(generated_prompt["prompt"])

# 태스크 설명이 어떻게 포맷되는지도 확인 가능
print("\n=== TASK CONTEXT ===")
print(f"Task Description: {task.description}")
print(f"Expected Output: {task.expected_output}")

system 키가 있으면 시스템과 사용자 프롬프트가 분리돼 있고, 없으면 prompt 하나로 합쳐져 있다는 뜻이에요. 실제 전송되는 내용을 그대로 들여다볼 수 있는 유용한 디버깅 방법이에요.

기본 지시 override하기

프롬프트를 완전히 제어할 수 있는 방법이 여러 가지 있어요.

옵션 1: 커스텀 템플릿 (권장)

from crewai import Agent

# 기본 지시 없이 자신만의 시스템 템플릿 정의
custom_system_template = """You are {role}. {backstory}
Your goal is: {goal}

Respond naturally and conversationally. Focus on providing helpful, accurate information."""

custom_prompt_template = """Task: {input}

Please complete this task thoughtfully."""

agent = Agent(
    role="Research Assistant",
    goal="Help users find accurate information",
    backstory="You are a helpful research assistant.",
    system_template=custom_system_template,
    prompt_template=custom_prompt_template,
    use_system_prompt=True  # 시스템/사용자 메시지를 분리해 사용
)

옵션 2: 커스텀 프롬프트 파일

특정 프롬프트 슬라이스를 override하는 custom_prompts.json 파일을 만들 수 있어요.

{
  "slices": {
    "no_tools": "\nProvide your best answer in a natural, conversational way.",
    "tools": "\nYou have access to these tools: {tools}\n\nUse them when helpful, but respond naturally.",
    "formatted_task_instructions": "Format your response as: {output_format}"
  }
}

그리고 crew에서 이렇게 사용해요.

crew = Crew(
    agents=[agent],
    tasks=[task],
    prompt_file="custom_prompts.json",
    verbose=True
)

참고로 기존의 agent.i18n역호환성 목적으로만 유지되고 있으며 deprecated 상태예요. 런타임에 프롬프트를 커스터마이징하려면 Crewprompt_file을 넘기면 되고, 프롬프트 슬라이스에 프로그래매틱하게 접근해야 한다면 i18n 유틸리티를 직접 쓰면 돼요.

from crewai.utilities.i18n import get_i18n

i18n = get_i18n("custom_prompts.json")
format_slice = i18n.slice("format")
tool_prompt = i18n.tools("ask_question")

옵션 3: o1 모델용 시스템 프롬프트 비활성화

agent = Agent(
    role="Analyst",
    goal="Analyze data",
    backstory="Expert analyst",
    use_system_prompt=False  # 시스템 프롬프트 분리 비활성화
)

관측(Observability) 도구로 디버깅하기

운영 투명성을 높이려면 관측 플랫폼을 연동해서 모든 프롬프트와 LLM 상호작용을 모니터링할 수 있어요. 이렇게 하면 기본 지시를 포함해 LLM에 전송되는 프롬프트를 정확히 볼 수 있어요. Langfuse, MLflow, Weights & Biases, 커스텀 로깅 솔루션 등 다양한 플랫폼 연동 가이드는 Observability 문서에서 확인할 수 있어요.

운영 환경을 위한 모범 사례

  1. 배포 전에 항상 생성된 프롬프트를 검사한다
  2. 커스텀 템플릿을 사용해 프롬프트 내용을 완전히 제어한다
  3. 관측 도구를 연동해 지속적으로 프롬프트를 모니터링한다 (Observability 문서 참고)
  4. 서로 다른 LLM으로 테스트한다 – 기본 지시가 모델마다 다르게 작동할 수 있으므로
  5. 프롬프트 커스터마이징을 문서화해 팀 투명성을 확보한다

기본 지시는 일관된 에이전트 동작을 보장하려는 목적이지만, 도메인별 요구사항과 충돌할 수 있어요. 위의 커스터마이징 옵션을 활용하면 운영 환경에서 에이전트 동작을 완전히 제어할 수 있어요.

프롬프트 파일 관리의 모범 사례

저수준 프롬프트 커스터마이징을 할 때는 정리되고 유지보수 가능한 상태로 두기 위해 이런 지침을 따르는 게 좋아요.

  1. 파일을 분리한다 – 커스터마이징한 프롬프트를 메인 코드베이스 밖의 전용 JSON 파일에 저장한다.
  2. 버전 관리를 한다 – 저장소에서 변경 사항을 추적해, 시간에 따른 프롬프트 조정을 명확히 문서화한다.
  3. 모델이나 언어별로 정리한다prompts_llama.json, prompts_es.json 같은 이름 규칙으로 특수 구성을 빠르게 식별한다.
  4. 변경 사항을 문서화한다 – 커스터마이징의 목적과 범위를 설명하는 주석이나 README를 유지한다.
  5. 변경을 최소화한다 – 진짜 필요한 슬라이스만 override하고, 나머지는 기본 기능을 그대로 유지한다.

프롬프트를 커스터마이징하는 가장 간단한 방법

가장 간단한 접근은 override하고 싶은 프롬프트만 담은 JSON 파일을 만들고, 그 파일을 Crew에 지정하는 거예요.

  1. 업데이트할 프롬프트 슬라이스가 담긴 JSON 파일을 만든다.
  2. Crewprompt_file 파라미터로 그 파일을 참조한다.

그러면 CrewAI가 커스터마이징과 기본값을 병합해줘서, 모든 프롬프트를 다시 정의하지 않아도 돼요. 참고로 프롬프트 슬라이스를 직접 읽어야 하는 코드에서는 agent.i18n을 읽는 대신 crewai.utilities.i18n.get_i18n()을 같은 프롬프트 파일로 사용하세요.

예시: 기본 프롬프트 커스터마이징

수정하고 싶은 프롬프트가 담긴 custom_prompts.json 파일을 만드세요. 변경 사항만이 아니라, 포함시킬 모든 최상위 프롬프트를 나열해야 한다는 점을 꼭 기억하세요.

{
  "slices": {
    "format": "When responding, follow this structure:\n\nTHOUGHTS: Your step-by-step thinking\nACTION: Any tool you're using\nRESULT: Your final answer or conclusion"
  }
}

그리고 이렇게 통합해요.

from crewai import Agent, Crew, Task, Process

# 평소처럼 에이전트와 태스크 생성
researcher = Agent(
    role="Research Specialist",
    goal="Find information on quantum computing",
    backstory="You are a quantum physics expert",
    verbose=True
)

research_task = Task(
    description="Research quantum computing applications",
    expected_output="A summary of practical applications",
    agent=researcher
)

# 커스텀 프롬프트 파일로 crew 생성
crew = Crew(
    agents=[researcher],
    tasks=[research_task],
    prompt_file="path/to/custom_prompts.json",
    verbose=True
)

# crew 실행
result = crew.kickoff()

몇 가지 수정만으로도 에이전트가 소통하고 태스크를 푸는 방식을 저수준에서 제어할 수 있게 돼요.

특정 모델에 최적화하기

모델마다 잘 동작하는 프롬프트 구조가 달라요. 프롬프트를 모델의 특성에 맞추면 성능이 유의미하게 향상될 수 있어요.

예시: Llama 3.3 프롬프팅 템플릿

예를 들어 Meta의 Llama 3.3을 다룰 때, 더 깊은 수준의 커스터마이징이 여기에 문서화된 권장 구조를 반영할 수 있어요: https://www.llama.com/docs/model-cards-and-prompt-formats/llama3_1/#prompt-template

코드에서 Llama 3.3을 활용하도록 에이전트를 미세 조정하는 예시를 볼게요.

from crewai import Agent, Crew, Task, Process
from crewai_tools import DirectoryReadTool, FileReadTool

# 시스템, 사용자(prompt), 어시스턴트(response) 메시지용 템플릿 정의
system_template = """<|begin_of_text|><|start_header_id|>system<|end_header_id|>{{ .System }}<|eot_id|>"""
prompt_template = """<|start_header_id|>user<|end_header_id|>{{ .Prompt }}<|eot_id|>"""
response_template = """<|start_header_id|>assistant<|end_header_id|>{{ .Response }}<|eot_id|>"""

# Llama 특유의 레이아웃을 쓰는 에이전트 생성
principal_engineer = Agent(
    role="Principal Engineer",
    goal="Oversee AI architecture and make high-level decisions",
    backstory="You are the lead engineer responsible for critical AI systems",
    verbose=True,
    llm="groq/llama-3.3-70b-versatile",  # Llama 3 모델 사용
    system_template=system_template,
    prompt_template=prompt_template,
    response_template=response_template,
    tools=[DirectoryReadTool(), FileReadTool()]
)

# 샘플 태스크 정의
engineering_task = Task(
    description="Review AI implementation files for potential improvements",
    expected_output="A summary of key findings and recommendations",
    agent=principal_engineer
)

# 태스크용 Crew 생성
llama_crew = Crew(
    agents=[principal_engineer],
    tasks=[engineering_task],
    process=Process.sequential,
    verbose=True
)

# crew 실행
result = llama_crew.kickoff()
print(result.raw)

별도의 JSON 파일 없이도 Llama 기반 워크플로우를 포괄적으로, 저수준에서 완전히 제어할 수 있게 돼요.

정리

CrewAI에서 저수준 프롬프트 커스터마이징은 초맞춤(super custom)의 복잡한 사용 사례를 여는 문이에요. 잘 정리된 프롬프트 파일(또는 인라인 템플릿)을 만들면, 다양한 모델과 언어, 전문화된 도메인을 수용할 수 있어요. 이 유연성 덕분에 정확히 원하는 AI 동작을 만들 수 있고, override하지 않은 부분에서는 CrewAI가 여전히 믿을 수 있는 기본값을 제공해준다는 점도 그대로예요.

지금까지 프롬프트 커스터마이징의 기반을 다뤘어요. 모델별 구조든 도메인별 제약이든, 이 저수준 접근으로 에이전트 상호작용을 아주 특수화된 방식으로 형태를 만들 수 있어요.

더 알아보기