내 Pydantic AI 에이전트를 GitHub Agentic Workflow로 실행하기
내 Pydantic AI 에이전트를 GitHub Agentic Workflow로 실행하기 (Run your own Pydantic AI agent as a GitHub Agentic Workflow)
GitHub Agentic Workflows(gh-aw)는 .github/workflows/의 마크다운 파일에서 에이전트를 실행해요. 이슈·PR·스케줄에서 발동하고, egress 방화벽 뒤 컨테이너에서 에이전트를 시작하며, MCP 도구를 넘기고, 에이전트가 만든 것을 안전한 출력으로 GitHub에 다시 써요. pydantic-ai 엔진은 그 기계를 Pydantic AI 에이전트에 겨눠요. 기본 실행하는 Coder 구성, 하네스가 배송하는 다른 에이전트, 또는 당신 저장소가 정의하는 에이전트가 될 수 있어요. 셋은 Start from Coder, Researcher, or your own에 펼쳐져 있고, 이 페이지는 그중 마지막을 끝에서 끝까지 짚어요.
출처: 문서
이것은 헤드리스 로 실행되는 에이전트예요. GitHub 러너에서, 당신이 고른 이벤트에서, 프롬프트 앞에 아무도 없이. 출력은 화면의 답변이 아니라 코멘트, PR 또는 커밋이고, 나중에 무엇을 했는지 볼 수 있는 곳은 실행 로그와 당신이 구성한 텔레메트리뿐이에요. 그래서 Observability가 곁가지가 아니라 섹션이에요.
모든 모델 벤더가 이 형태를 위한 액션을 배송하는데, 각각 그 벤더의 에이전트를 그 벤더의 모델에서 실행해요. 여기서 다른 점은 에이전트가 당신 것이라는 거예요. 당신의 지시문, 당신의 도구, 당신의 capabilities, 그리고 어떤 모델이든 문자열 하나 교체로, 터미널에서도 실행하고 웹 UI를 통해 서빙하거나 자체 백엔드에서 부를 수 있는 저장소에서요. 그 모든 곳에서 같은 Agent 객체예요.
완성된 저장소는 dsfaccini/gh-aw-pydantic-ai-demo고, 아래 모든 파일과 명령이 그것에서 와요.
먼저 gh-aw 용어 두 개를 치워두세요. gh-aw custom agent(또는 "agent file")는 .github/agents/의 마크다운 프롬프트로, custom agents 아래 설명돼요. 이 페이지가 만드는 것이 아니에요. 여기서 에이전트는 저장소의 모듈에 있는 파이썬 Agent 객체로, PAI_AGENT 환경 변수가 이름붙여요. 그리고 engine: driver:는 gh-aw의 copilot 엔진(그리고 pi on Node)의 내부 드라이버를 바꾸는 메커니즘이고, 이 같은 import 기반 엔진에는 효과가 없어 아래 구성의 일부가 아니에요.
본문
전제 조건 (Prerequisites)
repo와workflow범위로 인증된ghCLI:gh auth login --scopes repo,workflow.- gh-aw 확장:
gh extension install github/gh-aw. 고정하려면@vX.Y.Z를 붙여요. - gh-aw 런타임 v0.86.3 이상. 엔진 정의가
deriveBaseUrlFromModelsURL을 필요로 하는데, v0.86.3이 처음 내보낸 릴리스예요. 이미 커밋된 lockfile의 더 오래된 고정은 재컴파일할 때까지 유지돼요. - Linux 러너. gh-aw 샌드박스는 Linux와 Docker가 필요해서
macos-*와windows-*러너 라벨은 지원되지 않아요. - 저장소에서 이슈 활성화. 아래 워크플로우가
issues이벤트에서 발동하고 그 결과를 이슈에 코멘트로 게시해요. gh-aw는 워크플로우가create-issue안전 출력을 선언하면 컴파일 시 그 설정도 검사하고 이슈가 꺼져 있으면 컴파일을 실패시켜요. - 저장소에서 Actions 활성화 (Repository settings 참고).
Coder, Researcher, 또는 당신 것에서 시작 (Start from Coder, Researcher, or your own)
기본적으로 Coder를 써요. PAI_AGENT를 생략하면 Coder를 써요. 샌드박스 안의 파일시스템 접근과 무제한 셸 명령으로요. 에이전트 모듈이 필요 없어요.
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
imports:
- pydantic/pydantic-ai-harness/gh-aw/pydantic.md@main
engine:
id: pydantic-ai
model: openai/gpt-5
safe-outputs:
add-comment:
---
다른 준비된 에이전트. 하네스가 조립된 에이전트를 import 가능한 변수로 내보내므로 PAI_AGENT가 하나를 직접 이름붙일 수 있고 저장소는 여전히 에이전트 코드를 포함하지 않아요.
engine:
id: pydantic-ai
model: openai/gpt-5
env:
PAI_AGENT: pydantic_ai_harness.researcher:researcher_agent
steps:
- name: Install the researcher extra
run: python3 -P -m pip install --quiet --user --disable-pip-version-check "pydantic-ai-harness[researcher]"
Researcher는 로컬 검색·가져오기 폴백을 위해 자체 extra가 필요하고, steps: 블록이 설치하는 것이에요. 그 검색은 network: 허용 목록의 호스트에 닿아야 해요. pydantic_ai_harness.coder:coder_agent는 coder 에이전트의 같은 모양이고, 워크플로우가 기본 구성을 원하면서 명확성을 위해 PAI_AGENT를 설정하고 싶을 때 명시적으로 이름붙일 가치가 있어요.
당신의 에이전트. 이 페이지의 나머지. 에이전트가 자체 도구, 자체 지시문, 또는 하네스가 배송하지 않는 구성을 필요로 할 때 손대세요.
에이전트 모듈 (The agent module)
저장소 루트의 my_agent.py. PAI_AGENT가 module:variable 형태로 그 안의 변수를 이름붙여요. pai CLI가 -a에 받는 것과 같은 형태예요.
"""The agent this repository's agentic workflow runs.
`PAI_AGENT: my_agent:agent` in `.github/workflows/triage.md` names the `agent`
variable below. The engine passes `-m` from the workflow's `engine.model`, so
the model is not set here.
"""
from pydantic_ai import Agent
LABELS = ('bug', 'documentation', 'enhancement', 'question')
agent = Agent(
name='triage',
instructions="""
You triage one GitHub issue. Read the issue in the prompt, then post exactly one
comment with the `safeoutputs_add_comment` tool. The comment has three parts, in order:
1. **Summary.** What the issue reports, in two sentences or fewer.
2. **Suggested label.** One label from `label_catalog()`, and one line saying why.
3. **Question.** What the reporter still needs to tell us before anyone can act.
Write "No follow-up needed." when the issue is already actionable.
Suggest a label; do not apply one. Do not edit files. Do not open issues.
""",
)
@agent.tool_plain
def label_catalog() -> list[str]:
"""The labels this repository triages with."""
return list(LABELS)
그 모듈에 대해 네 가지.
- 모델 없음. 엔진이 항상 워크플로우의
engine.model에서 만든-m을 넘기고, 명시적-m이 로드된 에이전트가 선언한 어떤 모델이든 교체해요.Agent에 모델을 설정하면 무시되므로 워크플로우가 모델이 구성되는 유일한 곳이에요. safeoutputs_add_comment는 안전 출력이지 평범한 도구가 아니에요. gh-aw가 구성하는 모든 MCP 서버를 게이트웨이 뒤에 세우고 호스트 러너의${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json에 써요. 에이전트 스텝이 읽기 전용으로 마운트하고 엔진이pai --mcp-config로 넘기죠.load_mcp_toolsets가 각 서버의 도구에 그 이름을 접두사 붙여,add-comment안전 출력이safeoutputs_add_comment로 도착해요. 그 파일은 의도적으로 체크아웃 밖이에요. 엔진이 읽는 경로에 커밋된 파일은 게이트웨이 자격 증명을 쥔 프로세스에 대한 저장소 제어 입력이 될 테니까요. 코멘트 자체는 에이전트가 끝난 후 별도 작업이 게시해요. 에이전트는 저장소에 쓸 수 있는 토큰을 결코 쥐지 않아요.label_catalog는 평범한 함수 도구예요. 저장소 코드가 import 가능하고 에이전트의 자체 도구가 gh-aw가 공급하는 MCP 도구 옆에서 작동함을 보여주려고 여기 있어요.- 타사 임포트 없음. 엔진이
pydantic-ai-harness[cli]와pydantic-ai-slim[anthropic,openai,mcp,spec]을 설치해pydantic_ai가 자체 설정 없이 import 가능해요. 에이전트가 import하는 다른 것은 워크플로우 수준steps:블록이 설치해요(Dependencies).
모듈은 같은 프로세스에서 CLI를 실행하는 인터프리터가 한 번 import하므로 모듈 수준 작업이 한 번 실행돼요. import에서 오류를 일으키는 에이전트는 한 줄 "could not load agent" 메시지가 아니라 파이썬 traceback으로 스텝을 실패시켜요.
에이전트를 스펙으로 (The agent as a spec instead)
PAI_AGENT는 import 경로를 받는 어디든 .yml, .yaml 또는 .json agent spec을 받아, 이미 YAML로 하나를 구성하는 저장소가 워크플로우 옆 파일에서 같은 방식으로 에이전트를 구성할 수 있어요.
engine:
id: pydantic-ai
model: openai/gpt-5
env:
PAI_AGENT: triage_agent.yml
name: triage
instructions: |
You triage one GitHub issue. Read the issue in the prompt, then post exactly one
comment with the `safeoutputs_add_comment` tool. Suggest a label; do not apply one.
Do not edit files. Do not open issues.
capabilities:
- Thinking:
effort: medium
엔진이 YAML 파싱용 spec extra를 설치해요.
스펙에 model: 없음, 모듈이 지니지 않는 것과 같은 이유로. 엔진이 항상 워크플로우의 engine.model에서 만든 -m을 넘기고, 명시적 -m이 로드된 에이전트가 선언한 어떤 것이든 교체해요. 워크플로우가 모델이 구성되는 유일한 곳이에요.
게이트웨이의 MCP 서버는 여전히 --mcp-config로 도착해, 스펙 에이전트가 모듈과 같은 조건으로 안전 출력과 GitHub 도구를 얻어요.
오늘 스펙이 할 수 없는 두 가지, 둘 다 모듈로 되돌려보내요.
- 하네스 capability 이름붙이기. 스펙이 폐쇄 레지스트리를 통해 capability 이름을 해석하는데 하네스 capability는 그 일부가 아니고, CLI가
custom_capability_types를 넘기지 않아 스펙이 내장 capability와 그 외는 닿지 못해요.Coder,Researcher등은 모듈 전용이에요. pydantic-ai#8334 참고. - 함수 도구 정의. 위
label_catalog도구는 파이썬이고, 그것을 위한 스펙 형태가 없어요.
지시문 + 내장 capability가 스펙이 잘 다루는 형태예요. 그 너머는 모듈이에요.
워크플로우 파일 (The workflow file)
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
imports:
- pydantic/pydantic-ai-harness/gh-aw/pydantic.md@main
engine:
id: pydantic-ai
model: openai/gpt-5
env:
PAI_AGENT: my_agent:agent
safe-outputs:
add-comment:
---
# Triage the new issue
The issue that triggered this run, as gh-aw sanitized it:
<issue>
${{ steps.sanitized.outputs.text }}
</issue>
Post your triage as a single comment on that issue.
키별로:
on: issues: types: [opened]가 유일한 발동이에요.add-comment는 발동한 이슈에 게시하고workflow_dispatch실행에는 그게 없어 워크플로우가 수동 발동을 선언하지 않아요.gh aw run과 Run workflow 버튼은 이 워크스루의 일부가 아니에요.permissions:는 워크플로우 수준 토큰 범위이고, gh-aw는 여기서 쓰기 범위를 거부해요. GitHub에 쓰는 작업은 컴파일된 파일에서 자체 더 좁은 범위를 얻어요.issues: read가 GitHub MCP 도구가 이슈를 읽게 하는 것이에요. gh-aw는tools:블록이 전혀 없어도 기본 GitHub 읽기 toolset(context,repos,issues,pull_requests,users)을 로드해 에이전트가 어느 쪽이든github_issue_read를 가지지만, 범위가 없으면 호출이403 Resource not accessible by integration으로 돌아와요.tools: github: toolsets:로 toolset을 좁히거나 넓혀요.imports:가 엔진 정의를 끌어와요. gh-aw의 엔진 카탈로그는pydantic-aiid를 알지만 당신을 위해 import하지 않아요. 이 줄 없이 엔진을 이름붙이면 컴파일에 실패해요.engine: id:가 import된 엔진을 선택하고,engine: model:은provider/model형태로 필수예요. provider 세그먼트가 gh-aw의 api-proxy의 어느 백엔드가 요청을 서빙하는지 선택해요.copilot,anthropic,openai,codex가 받아들여지는 값이에요. 유선 API도 결정해요.anthropic/은 Anthropic Messages API로 실행되는데 그 백엔드가 요청 경로를api.anthropic.com으로 그대로 앞으로 보내니까요. 나머지 셋은 OpenAI 모양이고 Chat Completions를 써요.PAI_BASE_URL아래에서는 모든 것이 Chat Completions로 유지돼요.engine: env: PAI_AGENT:가 엔진의 조합된Coder에이전트를 당신 것으로 교체하는 것이에요. 설정하면 체크아웃을PYTHONPATH에 두어my_agent가 import 가능하게 해요.safe-outputs: add-comment:가 이 워크플로우가 수행하는 하나의 쓰기를 선언해요.safe-outputs:섹션이 전혀 없으면 gh-aw가 최대 1로create-issue를 활성화해요. 섹션을 선언하면 그 기본을 교체해, triage가 코멘트하는 모든 이슈에 대해 이슈도 열지 않아요.- 프론트매터 뒤 본문이 프롬프트예요. gh-aw가 자체 컨텍스트 블록을 앞에 붙이는데, 그것은 이슈 번호를 담지만 이슈 텍스트는 아니에요. 그래서
${{ steps.sanitized.outputs.text }}가 제목·본문을 에이전트 앞에 두는 것이에요. gh-aw가 발동 항목의 콘텐츠를 샌니타이징해 그 값을 계산하고, 그것이 템플릿 참조가 프롬프트로 문서화하는 형태예요(.title과.body가 두 반쪽을 따로 노출).
누락된 imports: 줄에서 나오는 컴파일 오류가 github/gh-aw/.github/workflows/shared/pydantic.md@<version>을 이름붙이는 팁을 지녀요. 그것은 gh-aw 자체의 더 오래된 정의 사본이에요. 무시하고 위 pydantic/pydantic-ai-harness 줄을 쓰세요. 이 저장소의 정의가 유지되는 것이에요.
정의 고정. @main은 매 컴파일 다시 해석되어 정의의 변경이 다음 gh aw compile 실행에 닿아요. 고정 버전을 쥐려면 gh-aw/pydantic.md를 포함하는 커밋 SHA, 또는 그 정의가 main에 상륙한 뒤 자른 릴리스 태그를 import하세요. 파일보다 오래된 태그는 컴파일 시 404를 반환해요. 그 ref가 정의를 고정해요. 하네스 패키지 버전은 정의의 engine: version:으로 별도 고정되고, 워크플로우 자체의 engine: version:이 그것을 덮어써요.
컴파일하고 커밋 (Compile and commit)
gh aw compile
컴파일러가 써요:
.github/workflows/triage.lock.yml, 실제 실행되는 GitHub Actions 워크플로우..github/aw/actions-lock.json, 잠금이 쓰는 모든 액션의 SHA 고정..github/aw/imports/pydantic/pydantic-ai-harness/<sha>/gh-aw_pydantic.md, 해석된 SHA에서 import된 정의의 바이트 동일 캐시..github/aw/imports/.gitattributes와 생성 파일을 표시하는 최상위.gitattributes.
그 전부를 .md와 함께 커밋하세요. 컴파일된 워크플로우의 Check workflow lock file 스텝이 실행 시 프론트매터 해시를 다시 계산하고 잠금이 소스와 일치하지 않으면 실행을 실패시켜, 낡거나 없는 잠금이 옛 구성을 실행하는 대신 워크플로우를 멈춰요.
매 컴파일이 Using experimental engine: Pydantic AI를 출력해요. 엔진 정의가 gh-aw에 대해 스스로를 experimental로 선언하는데, 그것은 Pydantic AI가 아니라 이 정의의 gh-aw와의 인터페이스에 대한 진술이에요. 두 프로젝트가 움직이고 있고 정의가 그것들을 잇는 방식을 자유롭게 바꿀 수 있으니까요. 그게 중요하면 위처럼 imports: ref를 태그나 SHA로 고정하세요.
이 워크플로우를 처음 컴파일하면 CODEX_API_KEY와 OPENAI_API_KEY를 새 제한 비밀으로 나열하는 보안 검토 경고를 출력해요. gh-aw가 잠금이 쓰는 비밀·액션·컨테이너 이미지를 gh-aw-manifest 헤더에 기록하고 추가를 살펴보라고 요청해요. 목록을 읽고 --approve로 다시 실행하세요.
gh aw compile --approve
gh aw compile은 또한 원격 import를 커밋 SHA로 .github/aw/imports/ 아래 캐시해요. gh-aw는 잠금과 그 캐시를 함께 공유 워크플로우를 위한 재현성 보장으로 다뤄요. .md와 .lock.yml 옆에 커밋하세요.
의존성 (Dependencies)
에이전트가 pydantic_ai 너머 import하는 것은 워크플로우 수준 steps: 블록이 설치해요.
steps:
- name: Install the agent's dependencies
run: python3 -P -m pip install --quiet --user --disable-pip-version-check httpx
--user는 $HOME/.local에 설치하는데, 샌드박스가 노출하고 엔진 자체 설치가 이미 쓰는 디렉터리예요. 시스템 전체 설치면 에이전트가 못 읽는 곳에 떨어져요. -P는 설치 자체를 위해 체크아웃을 sys.path에서 빼서, 패키지 이름을 딴 저장소의 모듈이 실제 패키지 대신 import될 수 없게 해요.
순서가 중요하고, 컴파일된 잠금이 이 스텝들을 올바른 위치에 둬요. 생성된 워크플로우에서 순서는 Setup Python, 그다음 당신의 steps:, 그다음 엔진의 Preinstall Pydantic AI coder agent, 그다음 Execute Pydantic AI CLI예요. 따라서 당신의 설치는 엔진이 나중에 쓰는 같은 인터프리터를 대상으로 해요.
자격 증명 (Credentials)
engine.model의 provider 세그먼트가 에이전트 스텝이 읽는 저장소 비밀을 결정해요.
engine.model 접두사 |
비밀 |
|---|---|
copilot/... |
COPILOT_GITHUB_TOKEN |
anthropic/... |
ANTHROPIC_API_KEY |
openai/... |
CODEX_API_KEY, 또는 CODEX_API_KEY가 설정 안 되면 OPENAI_API_KEY |
codex/... |
CODEX_API_KEY, 또는 CODEX_API_KEY가 설정 안 되면 OPENAI_API_KEY |
사용하는 프로바이더에 대한 비밀만 설정하면 돼요. 위 워크플로우는 openai/gpt-5를 써서 CODEX_API_KEY 또는(그게 설정 안 되면) OPENAI_API_KEY가 필요해요. 아래 예는 OPENAI_API_KEY를 설정해요.
gh aw secrets set OPENAI_API_KEY --value "<key>"
값은 환경 변수(--value-from-env OPENAI_KEY)나 stdin에서도 올 수 있고, --repo owner/name이 현재 것 말고 다른 저장소를 겨눠요. gh aw secrets bootstrap은 컴파일된 워크플로우를 읽고 빠진 것을 안내해요. 평범한 gh secret set OPENAI_API_KEY도 작동해요. 값을 묻거나 stdin에서 읽어요.
키가 어디서 오는지, 프로바이더별:
- OpenAI와 Codex: https://platform.openai.com/api-keys.
- Anthropic: https://console.anthropic.com/settings/keys.
- Copilot: 자원 소유자를 당신 계정으로 설정하고 Account permissions -> Copilot Requests를 Read로 설정한 https://github.com/settings/personal-access-tokens/new의 fine-grained PAT.
gho_OAuth 토큰은 거부돼요. 조직 Copilot 구독이 중앙 집중 청구에 있으면 PAT를 건너뛰고 대신permissions: copilot-requests: write를 추가해 에이전트 스텝이 실행의github.token을 쓰게 해요.
PAI_API_KEY는 없어요. gh-aw가 저장소 비밀을 에이전트 샌드박스 밖에 둬요. ${{ secrets.* }}를 포함하는 어떤 engine.env 값이든 awf --exclude-env로 에이전트 환경에서 제거되고, 컴파일러가 그렇지 않다고 가정하게 두지 않고 워크플로우를 실패시켜요. 프로바이더 자격 증명은 샌드박스 경계 반대편에 있는 gh-aw의 api-proxy에 있고, 에이전트는 새지 못하는 플레이스홀더 bearer 토큰을 보내요. 당신의 키드 엔드포인트에 닿으려면 PAI_BASE_URL을 키를 쥔 당신이 운영하는 게이트웨이에 겨눠요. 엔진의 README가 그 경로를 다뤄요.
저장소 설정 (Repository settings)
Actions 활성화.
gh api repos/OWNER/REPO/actions/permissions
gh api -X PUT repos/OWNER/REPO/actions/permissions -F enabled=true -f allowed_actions=all
PUT이 전체 정책을 설정하므로 원하는 값으로 allowed_actions를 보내고 현재 것이 유지되길 믿지 마세요.
허용 액션. allowed_actions가 selected일 때만 관련돼요. 컴파일된 잠금은 두 주인으로부터 액션을 써요. actions/*(checkout, setup-python, setup-node, cache, github-script, upload-artifact, download-artifact)와 github/gh-aw-actions/*. 둘 다 허용 목록에 있어야 해요. gh-aw 자체 문제 해결 페이지는 여전히 github/gh-aw@*를 나열하는데, 현재 버전이 내는 것이 아니에요.
gh api repos/OWNER/REPO/actions/permissions/selected-actions
gh api -X PUT repos/OWNER/REPO/actions/permissions/selected-actions \
-F github_owned_allowed=true \
-f 'patterns_allowed[]=github/gh-aw-actions/*'
github_owned_allowed가 actions/* 반쪽을 다뤄요. selected-actions 엔드포인트는 allowed_actions가 selected일 때만 적용돼요.
기본 브랜치 배치. GitHub는 저장소의 기본 브랜치에 있는 워크플로우 파일에 대해서만 issues 이벤트를 발동해요. 워크플로우가 기능 브랜치에 있는 동안은 아무 일도 안 일어나요. 이것은 gh-aw 설정이 아니라 GitHub Actions 동작이고, gh-aw 자체 페이지는 언급하지 않아요.
누가 발동할 수 있나. 컴파일된 워크플로우는 발동 사용자의 저장소 역할에 게이팅하는데, 기본 admin, maintainer, write예요. 그 집합 밖의 누군가가 연 이슈는 에이전트를 시작하지 않아요. roles: 프론트매터 키로 넓히거나 좁혀요.
포크. gh-aw는 pull_request 트리거가 forks: 허용 목록에 이름붙이지 않으면 포크의 PR을 차단하고, agentic 워크플로우가 저장소의 포크된 사본 안에서 실행되지 않는다고 문서화해요. fork 지원 참고.
기본 GITHUB_TOKEN을 위한 저장소 수준 "Read and write permissions"는 필요 없어요. 잠금이 직업별로 범위를 선언하니까요. "Allow GitHub Actions to create and approve pull requests"는 create-pull-request 안전 출력에만 중요해요.
실행하고 관찰 (Run and observe)
워크플로우를 기본 브랜치에 병합하세요. 이슈를 열면 실행이 시작돼요.
gh aw status
gh aw status는 각 워크플로우를 그 엔진, 잠금이 최신인지, 워크플로우가 활성인지와 함께 나열해요.
실행 후:
gh aw logs triage --artifacts all
gh aw audit <run-id-or-url>
gh aw logs는 실행을 실행별 폴더로 다운로드하고, --artifacts all은 기본으로 가져오는 컴팩트 사용 아티팩트에 에이전트 로그, agent-stdio.log, aw.patch, summary.json을 더해요. gh aw audit은 실행 하나를 .github/aw/logs 아래 마크다운 보고서로 바꾸고, id를 둘 이상 주면 두 개 이상의 실행을 diff해요. 원시 스텝 출력(엔진 자체 줄 포함)을 위해 gh run view <run-id> --log이(이전 시도면 --attempt N 추가) 종종 더 빨라요.
엔진의 자체 스텝 요약은 잠금의 Parse agent logs for step summary 스텝이 써서 Actions 실행의 Summary 페이지에 나타나고, 턴·도구 호출·토큰 수를 실어 나라요. gh aw logs --parse와 gh aw audit --parse는 그것을 로컬에서 다시 렌더링하지 않아요. gh-aw 내장 레지스트리(claude, codex, copilot, gemini, pi)로 엔진을 해석하고 다른 것은 다 건너뛰니까요. 이 엔진 포함한 모든 import 기반 엔진이요.
작동하는 실행이 어떻게 보이나
Execute Pydantic AI CLI 스텝에서 엔진이 에이전트가 시작하기 전에 해석한 구성을 출력하고, 그다음 도구 호출당 한 줄을 출력해요.
[pydantic-ai] provider=openai model=gpt-5 baseUrl=http://api-proxy:10000/v1 agent=my_agent:agent
▌ Called tool label_catalog.
▌ Called tool safeoutputs_add_comment.
agent= 값이 PAI_AGENT가 해석된 것이고, baseUrl=은 샌드박스 안의 api-proxy예요. 이런 줄:
[pydantic-ai] awf-reflect: unable to persist reflect payload to /home/runner/work/_temp/awf-reflect.json: EACCES: permission denied
성공적인 실행에도 나타나요. gh-aw의 reflect 헬퍼가 샌드박스가 쓰게 두지 않는 디렉터리에 엔드포인트 페이로드를 캐시하려 하면서 나오는 것이고, 발견된 엔드포인트는 어쨌든 쓰여요.
코멘트는 워크플로우를 이름붙이고 실행을 연결하는 gh-aw 푸터와 함께 이슈에 도착하고, 뒤에 엔진·버전·모델을 기록하는 HTML 코멘트가 따라와요.
관측성 (Observability)
gh-aw는 워크플로우가 OpenTelemetry 백엔드를 구성하면 자체 설정·결론 스텝의 스팬을 내보내고, 이 엔진은 그것을 에이전트로 확장해요. 에이전트 실행, 모든 모델 요청, 모든 도구 호출이 당신의 파이썬 없이 같은 트레이스에서 Pydantic AI 스팬으로 도착해요.
observability:가 백엔드를 이름붙이고 network:가 egress 방화벽을 통해 내보내기를 통과시켜요.
network:
allowed:
- logfire-us.pydantic.dev
observability:
otlp:
endpoint:
- url: https://logfire-us.pydantic.dev
headers:
Authorization: *** secrets.LOGFIRE_TOKEN }}
두 반쪽 모두 필요해요. 허용 목록은 엔진이 이미 허용하는 도메인(PyPI, GitHub, 모델 프로바이더)과 병합하지 대체하지 않고, 항목이 없으면 방화벽이 내보내기를 떨어뜨리는데 실행 자체는 여전히 성공해서 백엔드에서는 결코 구성되지 않은 워크플로우와 같게 보여요.
토큰을 프로바이더 자격 증명과 같은 방식으로 저장소 비밀로 설정해요.
gh aw secrets set LOGFIRE_TOKEN --value "<write-token>"
EU 지역 프로젝트에는 logfire-eu.pydantic.dev를 쓰세요. 호스트가 토큰이 속한 지역과 일치해야 해요. Logfire OTLP 수집은 토큰을 Bearer 접두사 없이 베어로 받아, gh-aw가 Authorization 헤더에 일반적으로 문서화하는 형태예요.
텔레메트리 자격 증명은 에이전트에 닿아요. 모델 자격 증명은 닿지 않아요. 샌드박스 경계 반대편 gh-aw의 api-proxy에 머물러서 PAI_API_KEY가 없는 이유예요(Credentials). OTLP 헤더는 달라요. gh-aw가 그것을 OTEL_EXPORTER_OTLP_HEADERS로 에이전트 프로세스에 전달해요. 샌드박스 안에서 도는 SDK가 백엔드에 닿으려는 방식이니까요. 프로젝트에서 데이터를 다시 읽을 수 없는 쓰기 토큰을 쓰세요.
엔진이 그것으로 무엇을 하나
OTEL_EXPORTER_OTLP_ENDPOINT는 gh-aw에게 관측성이 구성됐다는 신호이고 엔진이 키로 삼는 것이에요. 설정됐을 때, 그리고 그때만:
logfire가 CLI 옆에 설치됩니다. 관측성 없는 워크플로우는 그것에 아무 비용을 내지 않아요.- 런처가 에이전트를 import하기 전에 구성하고 계측합니다. import 시 작업을 시작하는 에이전트가 이미 추적돼요.
send_to_logfire는'if-token-present'이고 콘솔 실행기는 꺼져요. 스팬은 워크플로우가 구성한 엔드포인트로 가고, 엔진이 고른 Logfire 프로젝트나 스텝 로그(거기로 가면 로그 파서에도 닿을 것)로 가지 않아요. - 에이전트 환경의
LOGFIRE_TOKEN은 대상 하나를 추가하지 교체하지 않습니다.'if-token-present'는 스팬이 위 OTLP 엔드포인트와 함께 그 프로젝트로도 간다는 뜻이고, 프롬프트·컴플리션 포함이에요. gh-aw는engine.env에서 올 때${{ secrets.* }}값을 에이전트 환경 밖에 두므로, 워크플로우가 자체env:나steps:블록에 토큰을 넣을 때만 일어나요. 그것이 명시적으로 요청하는 방식이에요. 토큰을 빼면 워크플로우 엔드포인트가 어떤 것이 가는 유일한 곳이에요. - 구성과 자격 증명은 체크아웃 밖입니다. 런처가
/tmp아래 mode-0700 임시 디렉터리를 가리키는 명시적config_dir과data_dir값을 넘겨요.TMPDIR이 체크아웃을 가리켜도요. 체크아웃pyproject.toml설정과.logfire/logfire_credentials.json이 텔레메트리 대상을 선택할 수 없어요. 이 인수들은 또한LOGFIRE_CONFIG_DIR과LOGFIRE_CREDENTIALS_DIR을 덮어쓰지만 환경LOGFIRE_TOKEN을 비활성화하지 않아요. 디렉터리는 프로세스가 사는 동안 존재하고, 실행기 종료 핸들러가 실행된 뒤 인터프리터 종료 시 제거돼요. - 트레이스 컨텍스트가 붙습니다. gh-aw가 실행의 W3C 트레이스 컨텍스트를
TRACEPARENT에 게시해 엔진이 그 스팬을 워크플로우 스팬 아래 중첩할 수 있게 하지만, Logfire도 OpenTelemetry SDK도 그 변수를 스스로 읽지 않아요. 붙이는 것이 에이전트 스팬을 두 번째 무관 트레이스가 아니라 워크플로우의 트레이스에 유지하게 해요. - 트레이스만 보내는 신호입니다. OTLP 엔드포인트는 메트릭과 로그도 다루고, 트레이스만 받는 백엔드는 그것들에 스텝 로그에서 내보내기당 한 번
404로 답해요. 그래서 엔진은 기본적으로OTEL_METRICS_EXPORTER와OTEL_LOGS_EXPORTER를none으로 둬요. 받는 백엔드를 위해 워크플로우에서 어느 것을 설정해 그 신호를 다시 켜요. 토큰 수는 어느 쪽이든 스팬에 있어요.
엔진이 출력하는 구성 줄이 otlp= 세그먼트를 얻고 엔드포인트의 origin만 실어 나라요. 엔드포인트의 userinfo·쿼리 매개변수는 자격 증명이고 실행 로그는 저장소를 읽을 수 있는 누구나 읽으니까요. 실행 로그가 텔레메트리가 켜졌는지, 대략 어디로 갔는지 말하게 하려고 있는 것이에요.
[pydantic-ai] provider=openai model=gpt-5 baseUrl=http://api-proxy:10000/v1 agent=my_agent:agent otlp=https://logfire-us.pydantic.dev
Pydantic AI 스팬은 백엔드로 곧장 내보내지고 gh aw logs --artifacts agent가 다운로드하는 otel.jsonl 파일로 미러되지 않아요. 그 파일은 gh-aw 자체 JavaScript 헬퍼가 내는 스팬을 실어 나라요.
실행 찾기
gh-aw가 실행의 정체성을 OTEL_RESOURCE_ATTRIBUTES에 두고 logfire.configure()가 그것을 리소스로 병합해, 엔진이 자체 속성을 추가하지 않고 모든 스팬이 그것을 실어 나라요.
| 속성 | 값 |
|---|---|
gh-aw.engine.id |
pydantic-ai |
gh-aw.workflow.name |
워크플로우 이름 |
gh-aw.repository |
owner/name |
gh-aw.run.id, github.run_id |
Actions 실행 id |
gh-aw.engine.id = 'pydantic-ai'가 하나의 백엔드에 보고하는 모든 저장소에 걸친 이 엔진의 모든 실행에 대한 필터이고, gh-aw.run.id가 트레이스를 그 Actions 실행에 다시 이어요.
어떤 OpenTelemetry 백엔드든
위의 어떤 것도 호스트와 헤더 너머 Logfire 특정이 아니에요. observability.otlp는 HTTP로 OTLP를 받는 어떤 엔드포인트든 받고, gh-aw의 OpenTelemetry 참조는 정적 헤더 대신 Workload Identity Federation을 통한 Sentry 설정과 Google Cloud Telemetry도 문서화해요. url, headers, 허용 목록의 호스트를 바꾸고, 이 섹션의 나머지는 그대로예요.
당신의 에이전트에서 구성하기
스스로 logfire.configure()를 부르는 PAI_AGENT 모듈은 엔진의 호출 후 실행되어 그것을 교체해요. 서비스 이름, 스크러빙 규칙, 추가 스팬 프로세서를 설정하는 방법이에요. 전체 구성을 교체하지, 다시 말하는 인수는 아니에요. 위에서 하중을 지는 것들을 이어받으세요.
import atexit
import tempfile
logfire_dir = tempfile.TemporaryDirectory(prefix='gh-aw-logfire-', dir='/tmp')
atexit.register(logfire_dir.cleanup)
import logfire
logfire.configure(
send_to_logfire='if-token-present',
console=False,
distributed_tracing=True,
config_dir=logfire_dir.name,
data_dir=logfire_dir.name,
)
logfire.instrument_pydantic_ai()
하기 전에 알아둘 것:
- 두 디렉터리를 프로세스 수명 동안 비공개로 유지하세요.
config_dir이나data_dir을 생략하면 체크아웃 구성이나 자격 증명을 다시 켜요. Logfire 구성 전에 정리를 등록해 실행기 종료 핸들러가 먼저 실행되게 하세요. console=False는 선택이 아니에요. 빼면 logfire 콘솔 실행기를 복원해서 모든 스팬을 stderr로 쓰는데, 엔진의 로그 파서가 읽는 스트림이에요.distributed_tracing=True는 실행의 트레이스에 유지해요. 모듈이 import될 때쯤 엔진이 이미TRACEPARENT의 컨텍스트를 붙였고 그 붙임은 살아남지만, 플래그가 없으면 logfire가 매 실행 경고해요.- 베어
logfire.configure()는 여기서 실패해요. 그send_to_logfire기본값은 환경의LOGFIRE_TOKEN을 요구하고, 위 프론트매터의 토큰은 환경 변수가 아니라 헤더 값이에요. 엔진처럼send_to_logfire='if-token-present'를 넘기세요. logfire는 엔드포인트가 구성됐을 때만 설치됩니다. 무조건 import하는 모듈은 워크플로우 자체steps:설치에도 필요하고, 아니면 관측성 꺼진 실행에서 import에 실패해요.
문제 해결 (Troubleshooting)
error: invalid engine: pydantic-ai. Valid engines are: claude, codex, copilot, gemini, pi. imports: 줄이 없어요. 동봉된 팁이 제안하는 경로가 아니라 pydantic/pydantic-ai-harness/gh-aw/pydantic.md@main을 추가하세요.
error: invalid engine.model for engine 'pydantic-ai': for universal consumer engines, engine.model must use provider/model format provider/ 접두사가 engine.model에서 빠졌어요. gh-aw가 엔진이 실행되기 전에 검사해요.
error: strict mode: secrets detected in 'engine.env' section are excluded from the agent sandbox via awf --exclude-env ${{ secrets.* }} 참조가 engine.env에 있어요. 어차피 에이전트 환경에서 제거됐을 것이에요. 프로바이더 비밀을 저장소 비밀로 설정하세요.
warning: safe update mode detected unapproved changes와 새 제한 비밀 목록. 첫 컴파일과, 잠금이 쓰는 비밀·액션·이미지를 바꾸는 어떤 컴파일에서도 예상돼요. 목록을 검토하고 gh aw compile --approve로 다시 실행하세요.
[INFO] API proxy enabled: OpenAI=false, Anthropic=false, ... 그다음 health-check Cannot connect to OpenAI API proxy at http://host.docker.internal:10000 프로바이더 비밀 누락 또는 비어 있어서 gh-aw가 프록시 백엔드를 시작하지 않고 엔진 자체 스크립트 전에 작업이 실패해요. 바로 앞 줄이 직접 말해요: [WARN] API proxy enabled but no API keys found in environment. 비밀을 설정하고 gh run rerun <run-id>로 실행을 다시 실행해 비밀을 다시 읽게 하세요. 비밀 존재 전에 시작한 실행은 평생 빈 값을 유지하므로, 같은 시도를 다시 실행하는 것이 고침이지 기다리는 게 아니에요.
Lock file '...' is outdated! The workflow file '...' frontmatter has changed. Run 'gh aw compile' to regenerate the lock file. 커밋된 .lock.yml이 .md와 일치하지 않아요. 다시 컴파일하고 결과를 커밋하세요.
이슈가 열려도 워크플로우가 시작하지 않아요. 워크플로우 파일이 기본 브랜치에 있고, 이슈를 연 계정이 roles:의 역할 중 하나를 가졌는지 확인하세요.
코딩 에이전트 사용 (Using a coding agent)
셸 접근이 있는 코딩 에이전트가 이 설정을 끝에서 끝까지 할 수 있어요. 두 파일을 쓰고, gh aw compile이 프론트매터가 틀리면 정확히 무엇이 틀렸는지 보고해요. 이 페이지와 실행하려는 에이전트의 module:variable을 주세요.
Read https://pydantic.dev/docs/ai/harness/gh-aw/ and set up a gh-aw agentic workflow in
this repository that runs my Pydantic AI agent at my_agent:agent on newly opened issues.
이 워크스루의 완성된 저장소는 dsfaccini/gh-aw-pydantic-ai-demo예요. 기본 coder 에이전트, 커스텀 엔드포인트, 자격 증명 모델을 다루는 엔진의 자체 참조 문서는 이 저장소의 gh-aw/README.md예요.