HCL 구성

HCL 구성 (HCL Configuration)

YAML 대신 HCL로 Docker Agent 구성을 작성해요. 같은 Docker Agent 스키마와 검증 규칙에 매핑돼요.

출처: 문서

본문

docker-agent 는 .yaml 또는 .yml 파일을 지원하는 어디서든 .hcl 구성 파일을 지원해요. HCL은 라벨 블록, 더 적은 구두점, 긴 프롬프트용 heredoc을 선호한다면 유용해요.

Tip 같은 구성 모델, 다른 구문 (Same config model, different syntax) YAML과 HCL은 같은 Docker Agent 구성 모델의 두 구문일 뿐이에요. Docker Agent는 HCL을 내부적으로 동등한 YAML 구조로 변환한 뒤 정상 스키마 검증과 로딩 파이프라인을 실행해요.

최소 예제 (Minimal Example)

#!/usr/bin/env docker agent run

agent "root" {
  model = "openai/gpt-5"
  description = "A helpful assistant"
  instruction = <<-EOT
    You are a helpful assistant.
  EOT

  toolset "think" {}
}

YAML 구성처럼 정확히 실행해요:

$ docker agent run agent.hcl
$ docker agent run --exec agent.hcl "Summarize this repository"
$ docker agent serve api ./agents/ # directories may mix .yaml, .yml, and .hcl files

Tip 참고 (See also) HCL은 구문만 바꾸지 필드의 의미는 바꾸지 않아요. 각 필드가 무엇을 하는지 보려면 Agent Config, Model Config, Tool Config 참고.

YAML vs HCL

이 두 구성은 동등해요:

models:
  claude:
    provider: anthropic
    model: claude-sonnet-4-5

agents:
  root:
    model: claude
    description: Coding assistant
    instruction: You help with software development.
    toolsets:
      - type: filesystem
      - type: shell
model "claude" {
  provider = "anthropic"
  model = "claude-sonnet-4-5"
}

agent "root" {
  model = "claude"
  description = "Coding assistant"
  instruction = "You help with software development."

  toolset "filesystem" {}
  toolset "shell" {}
}

핵심 규칙 (Core Conventions)

HCL은 몇 가지 간단한 매핑 규칙을 따릅니다:

HCL syntax YAML shape
agent "root" { ... } agents.root
model "claude" { ... } models.claude
provider "team" { ... } providers.team
mcp "github" { ... } mcps.github
rag "docs" { ... } rag.docs
agent 안의 command "fix" { ... } commands.fix
toolset "shell" {} type: shell 이 있는 toolsets의 목록 항목
metadata { ... }, permissions { ... } 같은 최상위 이름을 가진 singleton 블록

최상위 키가 매핑된 것은 라벨 블록이 됨 (Top-level keyed maps become labeled blocks)

YAML에서 여러 섹션은 이름으로 키가 매겨진 맵이에요. HCL에서는 그것들이 라벨 블록이 돼요:

model "claude" {
  provider = "anthropic"
  model = "claude-sonnet-4-5"
}

agent "root" {
  model = "claude"
  description = "Primary assistant"
  instruction = "You are helpful."
}

지원되는 최상위 라벨 블록:

  • agent
  • model
  • provider
  • mcp
  • rag

지원되는 최상위 singleton 블록:

  • metadata
  • permissions

Toolset은 블록 라벨을 type으로 사용 (Toolsets use the block label as type)

type: ... 이 있는 목록 항목을 쓰는 대신, HCL은 라벨이 도구 유형이 되는 toolset 블록을 사용해요:

agent "root" {
  model = "openai/gpt-5"
  description = "Dev assistant"
  instruction = "You can inspect and modify code."

  toolset "filesystem" {}

  toolset "mcp" {
    ref = "docker:github-official"
  }
}

명령도 라벨 블록 사용 (Commands use labeled blocks too)

에이전트 명령은 각 명령이 자체 블록을 갖기 때문에 HCL로 작성하기에 종종 더 좋아요:

agent "root" {
  model = "openai/gpt-5"
  description = "Build helper"
  instruction = "You help with builds."

  command "fix-lint" {
    description = "Fix lint issues"
    instruction = "Run the linter, then fix any problems."
  }
}

문자열과 Heredoc (Strings and Heredocs)

짧은 값에는 따옴표가 붙은 문자열을, 긴 프롬프트, 환영 메시지, 임베디드 JSON에는 heredoc을 사용해요.

agent "root" {
  model = "openai/gpt-5"
  description = "Friendly assistant"

  instruction = <<-EOT
    You are a helpful assistant.

    Keep answers concise and practical.
  EOT
}

리터럴 ${...} 이스케이프 (Escaping literal ${...})

HCL은 문자열과 heredoc 안의 ${...} 를 템플릿 보간으로 취급해요. 프롬프트에 리터럴 텍스트 ${...} 가 필요하면 $${...} 로 이스케이프해요.

이것은 Docker Agent 템플릿 스니펫을 의도적으로 보여주는 명령 프롬프트에 중요해요:

command "fix-lint" {
  instruction = <<-EOT
    Run the linter and inspect the result:

    $${shell({cmd: "task lint"})}
  EOT
}

모델은 리터럴 ${shell({cmd: "task lint"})} 텍스트를 받아요.

file()로 파일 로드 (Loading Files with file())

file() 함수는 UTF-8 텍스트 파일을 읽고 내용을 문자열로 반환해요. 상대 경로는 HCL 구성 파일의 디렉터리에서 해석되고, 읽기는 그 디렉터리로 제한돼요.

긴 프롬프트를 구성 밖에 두는 데 도움이 돼요:

agent "root" {
  model = "openai/gpt-5"
  description = "Coding assistant"
  instruction = file("prompts/coding.md")
}

단일 인자로 파일 내용은 작성된 그대로 반환돼요 — 파일의 어떤 ${...} 도 리터럴로 유지되므로, ${shell({cmd: "..."})} 같은 런타임 스니펫이 그대로 통과해요.

파일을 템플릿으로 렌더링 (Rendering files as templates)

두 번째 인자로 객체를 전달해 파일을 HCL 템플릿으로 렌더링해요. 각 키가 파일 안에서 사용 가능한 변수가 돼요:

agent "reviewer" {
  model = "openai/gpt-5"
  description = "Go reviewer"
  instruction = file("prompts/reviewer.md", {
    language = "Go"
    strictness = "high"
  })
}

agent "py_reviewer" {
  model = "openai/gpt-5"
  description = "Python reviewer"
  instruction = file("prompts/reviewer.md", {
    language = "Python"
    strictness = "relaxed"
  })
}

prompts/reviewer.md 가 다음을 포함한다면:

You review ${language} code with ${strictness} strictness.

템플릿은 %{ for } 와 %{ if } 지시자를 포함한 전체 HCL 템플릿 구문을 지원해요:

Rules:
%{ for rule in rules ~}
- ${rule}
%{ endfor ~}

두 가지를 명심해야 해요:

  • 객체에 없는 변수를 참조하면 에러예요.
  • 템플릿 안에서는 함수를 사용할 수 없어서, 템플릿이 file() 을 다시 호출할 수 없어요. 파일이 템플릿으로 렌더링되는 동안 리터럴 ${...} 를 필요로 하면 파일 안에서 $${...} 로 이스케이프하세요.

반복 블록이 리스트가 됨 (Repeated Blocks Become Lists)

일부 YAML 섹션은 리스트예요. HCL에서는 반복 블록으로 작성돼요.

예를 들어 모델 라우팅 규칙은 반복 routing { ... } 블록이 돼요:

model "smart_router" {
  provider = "openai"
  model = "gpt-5"

  routing {
    model = "anthropic/claude-sonnet-4-5"
    examples = [
      "Write a detailed technical document",
      "Review this code for security issues",
    ]
  }

  routing {
    model = "openai/gpt-5"
    examples = [
      "Generate some creative ideas",
      "Help me brainstorm",
    ]
  }
}

같은 아이디어가 RAG 전략 블록, 훅 이벤트 항목 같은 다른 리스트 형태 섹션에도 적용돼요.

Terraform과의 중요한 차이점 (Important Differences from Terraform)

Docker Agent는 HCL을 구성 구문으로 사용하지, Terraform으로는 사용하지 않아요:

  • modules, locals, variable 블록이 없어요.
  • 표현식에서 사용 가능한 유일한 함수는 file() 이에요; Terraform의 함수 라이브러리(file() 을 vars 객체로 대체하는 templatefile() 포함)는 사용할 수 없어요.
  • 일반 리터럴 값: 문자열, 숫자, 불리언, 리스트, 객체, 중첩 블록을 선호해요.
  • 변환 후 결과는 동등한 YAML 구성과 정확히 똑같이 검증돼요.

이미 Terraform을 안다면, Docker Agent HCL을 기존 구성 스키마 위의 얇은 블록 기반 구문으로 생각하세요.

예제 (Examples)

저장소에서 이 실제 구성들을 참고하세요:

  • examples/pirate.hcl
  • examples/gopher.hcl
  • examples/instructions_from_file.hcl

더 알아보기 (Learn more)