
Strands Agents의 Harness Optimizer로 에이전트를 육성해 보았다
요약
Strands Agents에서 공개한 Harness Optimizer는 에이전트의 시스템 프롬프트, 도구 정의 등을 자동으로 최적화하는 오픈 소스 라이브러리입니다. 모델 재학습 대신 실행 결과와 보상 함수를 기반으로 에이전트의 환경(Harness)을 개선하는 루프를 제공합니다.
핵심 포인트
- 모델 재학습 없이 시스템 프롬프트와 도구 정의를 최적화
- 실행-평가-업데이트의 머신러닝 루프를 에이전트 환경에 적용
- 성공과 실패 사례의 판단 패턴을 비교하여 프롬프트 자동 업데이트
- SystemPromptFormula를 통해 특정 구성 요소만 선택적 최적화 가능
여러분은 Strands Agents의 Blog를 알고 계신가요?
저는 평소 Strands Agents의 사이트를 열 때, 항상 보는 것은 문서나 샘플 코드였습니다. 알고 싶은 기능을 찾아보고, 동작하는 코드를 발견하면 페이지를 닫는. 그런 방식으로 사용해 왔기에 Blog는 거의 열어본 적이 없었습니다.
어느 날 문득 Blog를 들여다보니, "Introducing Harness Optimizer: Automatically optimize your agent harness"라는 기사가 눈에 들어왔습니다. 에이전트의 하네스(Harness)를 자동으로 최적화한다? 재미있을 것 같아 읽어 내려가 보았습니다.
생성형 AI 에이전트를 구동하기까지의 허들은 최근 들어 상당히 낮아졌습니다.
예를 들어 Amazon Bedrock AgentCore Harness에서는 모델, 시스템 프롬프트 (System Prompt), 도구 (Tool), Skill 등을 설정으로서 선언할 수 있습니다. 에이전트 루프나 실행 환경은 매니지드 (Managed) 형태로 제공되기 때문에, 오케스트레이션 (Orchestration) 코드나 컨테이너를 처음부터 만들지 않아도 에이전트를 구성할 수 있습니다.
출처: AgentCore harness - AWS 공식 문서
Strands Agents에서도 모델과 도구, 시스템 프롬프트를 조합하는 것만으로 에이전트를 만들 수 있습니다.
agent = Agent(
model=model,
tools=[list_transport_options],
...
이러한 메커니즘 덕분에 개발자뿐만 아니라 업무를 잘 아는 BIZ 측 담당자도 에이전트 작성에 참여하기 쉬워졌습니다.
다만, 에이전트를 만들 수 있는 것과 업무에서 기대한 대로 계속 동작하는 것은 별개의 문제입니다.
업무에는 원칙과 예외가 있습니다. "문의에는 FAQ에 기반하여 답변한다"가 원칙이라 하더라도, 제품 결함 보고에는 FAQ를 건너뛰고 에스컬레이션 (Escalation)한다. 이러한 예외는 운용해 보고 나서야 처음 발견되는 경우도 적지 않습니다.
예외나 업무 지식, Skill이나 도구의 사용법을 올바르게 반영하여 업무를 수행할 수 있는 에이전트로 튜닝 (Tuning)해 나가는 것은 쉽지 않습니다. 모델이나 도구를 변경하면, 그때까지의 지시대로 똑같이 움직인다는 보장도 없습니다.
Harness Optimizer는 에이전트의 시스템 프롬프트, 도구 정의, Skill 등을 "Agent Harness"로 파악하고, 실행 결과로부터 개선해 나가는 오픈 소스 라이브러리입니다. 모델 자체를 재학습하는 것이 아니라, 모델을 둘러싼 하네스를 육성한다는 발상입니다.
에이전트를 실행하고, 결과를 Reward (보상 함수)로 평가하여, 성공과 실패의 차이로부터 시스템 프롬프트 등을 업데이트합니다. 머신러닝 (Machine Learning)의 "실행 → 평가 → 파라미터 업데이트"와 유사한 루프를 에이전트의 행동 개선에 적용합니다.
출처: Introducing Harness Optimizer
그림 중의 Formula는 하네스 안에서 최적화할 대상을 나타냅니다. 에이전트를 실행하고, Reward로 평가하며, 그 결과로부터 시스템 프롬프트를 업데이트하는 흐름을 반복합니다.
이번에는 SystemPromptFormula를 사용하여, 도구 정의 등은 고정한 채 시스템 프롬프트만을 최적화 대상으로 삼았습니다. 공식 블로그에서는 Skill, Memory, 도구를 대상으로 하는 Formula도 향후 추가될 예정이라고 합니다.
업데이트 방법도 심플합니다. 성공한 실행과 실패한 실행을 비교하여, "성공 측에는 있고 실패 측에는 없는 판단 패턴"을 찾아내어 그것을 시스템 프롬프트에 추가합니다.
출처: Introducing Harness Optimizer
실행 결과를 성공과 실패로 나누고, 양자의 행동이나 판단을 비교합니다. 이번 사례로 치면, 케이스 A의 성공과 케이스 B의 실패를 비교하여 확인해야 할 규칙을 추출합니다.
시나리오의 무대는 제가 살고 있는 도야마현 다카오카시로 설정했습니다. 여행자가 10:00에 신다카오카역을 출발하여 다카오카시 미술관으로 향합니다.
이용 가능한 교통수단은 버스와 택시 두 가지입니다.
| 교통수단 | 요금 | 소요 시간 | 도착 시각 |
|---|---|---|---|
| 버스 | 300엔 | 25분 | 10:25 |
| 택시 | 1,200엔 | 10분 | 10:10 |
회사의 출장 규정은 다음과 같습니다.
원칙적으로 최저가의 대중교통을 이용한다. 단, 대중교통으로는 방문 시각에 맞출 수 없는 경우 택시 이용을 허가한다.
이 규정에 대해 확인해 볼 케이스를 두 가지 준비했습니다.
| 케이스 | 방문 시각 | 기대하는 선택 | 이유 |
|---|---|---|---|
| A | 10:40 | 버스 | 버스로도 충분함 |
| B | 10:15 | 택시 | 버스로는 10분 지연 |
케이스 A는 원칙대로 가장 저렴한 버스를 선택하면 되는 케이스입니다. 케이스 B는 버스로는 시간에 맞출 수 없기 때문에 택시를 선택해야 하는 예외 케이스입니다.
이번 질문은 다음과 같습니다.
Harness Optimizer는 가장 저렴한 버스를 선택하여 방문 시각에 늦는 실패를 바탕으로, 택시를 이용하는 예외 규칙을 시스템 프롬프트 (System Prompt)에 추가할 수 있는가.
참고로, 요금이나 소요 시간은 실험용 고정 데이터입니다. 실제 교통 정보가 아닙니다.
에이전트가 사용하는 도구 (Tool)는 이번 실험을 위해 직접 제작한 list_transport_options뿐입니다. 외부 API에는 접속하지 않고, 고정 데이터에서 버스와 택시를 반환합니다.
@tool
def list_transport_options(
origin: str,
...
최종 결과는 자연문이 아니라, 구조화된 형식으로 받습니다.
class TransportDecision(BaseModel):
selected_transport_id: Literal["bus", "taxi"]
arrival_at: datetime
...
"택시도 검토했습니다"와 같은 문장 표현이 아니라, 실제로 무엇을 선택했는지를 기계적으로 평가할 수 있도록 구성했습니다.
보상 (Reward)은 출장 규정을 그대로 판정 조건으로 삼았습니다. 다음 조건을 모두 만족하면 1, 하나라도 실패하면 0입니다.
도구가 found를 반환하여 버스와 택시 2가지 후보를 획득했다
선택한 교통수단의 도착 시각과 요금이 고정 데이터와 일치한다
버스로 시간에 맞출 수 있는 케이스 A에서는 버스를 선택했다
버스로는 시간에 맞출 수 없는 케이스 B에서는 택시를 선택했다
failures = []
if not transport_options_checked:
failures.append("transport_options_not_checked")
...
0점이라는 점수뿐만 아니라, 무엇이 원인이 되어 실패했는지도 metadata에 남기는 것이 포인트입니다.
최적화 전의 시스템 프롬프트에는 출장 규정의 원칙만을 작성했습니다.
출장 시에는 이용 가능한 대중교통 중에서 가장 저렴한 수단을 선택해 주세요.
이 프롬프트로 2가지 케이스를 실행한 결과입니다.
| 케이스 | 방문 시각 | 선택 | 도착 시각 | Reward |
|---|---|---|---|---|
| A | 10:40 | 버스 | 10:25 | 1 |
| B | 10:15 | 버스 | 10:25 | 0 |
케이스 A는 기대한 대로입니다. 버스로 방문 시각에 맞출 수 있고 택시보다 저렴하므로 Reward는 1이 되었습니다.
케이스 B에서 에이전트는 다음 사실을 올바르게 인식하고 있었습니다.
버스는 10:25에 도착하므로, 10:15 방문 시각에 10분 늦는다
택시라면 10:10에 도착할 수 있다
그럼에도 불구하고 최종적으로 선택한 것은 버스였습니다. "가장 저렴한 수단을 선택한다"라는 시스템 프롬프트를 그대로 우선시했기 때문입니다.
Reward에는 다음과 같은 실패 이유가 기록되었습니다.
selected_option_misses_visit_time
taxi_exception_not_applied
모델은 필요한 정보를 획득했고 지각 사실도 인식했습니다. 부족했던 것은 모델의 지식이 아니라, "시간에 맞출 수 없는 경우에는 원칙을 덮어쓴다"라는 판단 규칙이었습니다.
케이스 A의 성공 트레이스 (Trace)와 케이스 B의 실패 트레이스를 Harness Optimizer에 전달했습니다.
formula = SystemPromptFormula(
system_prompt=INITIAL_SYSTEM_PROMPT,
)
...
step()을 1회 실행한 결과, Optimizer는 원래의 일본어 프롬프트를 유지한 채 Learned Behaviors를 추가했습니다. 생성된 규칙의 핵심 부분입니다.
대중교통 옵션이 예정된 방문 시간 '이후'에 도착할 경우, 비용에 관계없이 이를 선택하지 마십시오.
모든 대중교통 옵션이 방문 시간을 맞추지 못할 경우,
...
일본어로 정리하면, 다음과 같은 판단 순서입니다.
1. 교통수단의 도착 시각과 방문 시각을 비교한다
2. 시간에 맞춰 도착할 수 있는 대중교통이 있다면, 그중에서 가장 저렴한 것을 선택한다
3. 대중교통으로는 시간을 맞출 수 없는 경우, 택시를 선택한다
학습시키고자 했던 예외 규칙이 그대로 추가되었습니다. 이번에는 생성된 후보를 편집하지 않고, 그대로 After의 시스템 프롬프트 (System Prompt)에 반영했습니다.
업데이트된 시스템 프롬프트로 동일한 두 가지 케이스를 재실행했습니다.
| 케이스 | Before | After | 변화 |
|---|---|---|---|
| A: 10:40까지 | 버스, Reward 1 | 버스, Reward 1 | 최저가 원칙 유지 |
| B: 10:15까지 | 버스, Reward 0 | 택시, Reward 1 | 시간 예외 적용 |
케이스 A에서는 버스로 시간에 맞출 수 있기 때문에, 업데이트 후에도 300엔인 버스를 선택했습니다. 케이스 B에서는 버스의 10:25 도착과 방문 시각 10:15를 비교하여, 10:10에 도착할 수 있는 택시를 선택했습니다.
케이스 B의 구조화된 출력 (Structured Output)입니다.
{
"selected_transport_id": "taxi",
"arrival_at": "2026-08-08T10:10:00+09:00",
...
최저가 대중교통을 선택하는 원칙은 유지하면서, 시간에 맞지 않는 경우에만 택시로 전환되었습니다.
한편, 프롬프트가 길어진 만큼 사용된 토큰 (Tokens)은 증가했습니다.
| 페이즈 | 총 사이클 (Cycles) | 총 토큰 (Tokens) |
|---|---|---|
| Before | 4 | 6,987 |
| After | 4 | 7,694 |
성공 건수는 1/2에서 2/2로 바뀌었으며, 총 토큰은 707 증가했습니다.
이번 실험에서 가장 인상 깊었던 점은, "모델이 실수를 인지하고 있다는 것"과 "올바른 행동을 선택할 수 있다는 것"은 별개라는 점입니다.
Before의 케이스 B에서 모델은 버스로는 늦는다는 것을 알고 있었습니다. 택시라면 시간에 맞출 수 있다는 것도 알고 있었습니다. 그럼에도 시스템 프롬프트에 예외 규칙이 없었기 때문에 버스를 선택했습니다. 최종 답변만 보면 "교통수단 선택을 틀렸다"고 할 수 있지만, 실행 트레이스 (Execution Trace)를 보면 문제는 규칙의 우선순위에 있다는 것을 알 수 있습니다.
Harness Optimizer는 이러한 성공과 실패의 차이로부터 "시간 제약을 먼저 확인한다"라는 규칙을 추출해냈습니다.
버스나 택시 선택이라는 작은 실험이었지만, Harness Optimizer의 "실행 → 평가 → 프롬프트 업데이트" 루프를 한 차례 경험할 수 있었습니다.
인상 깊었던 것은 모델이 지각할 것을 알고 있었음에도 버스를 선택했다는 점입니다. 부족했던 것은 모델의 지식이 아니라, 시스템 프롬프트에 있는 판단 규칙이었습니다. Harness Optimizer는 이러한 "규칙의 부족"을 성공과 실패의 비교를 통해 찾아내어 프롬프트에 추가해 주었습니다.
이번 실험에서는 툴 (Tool)이나 리워드 (Reward) 구현에 코드가 필요했습니다. 반면, 개선의 기점이 된 것은 "무엇을 올바른 행동으로 볼 것인가"라는 업무 규칙입니다. 업무 담당자가 올바른 행동을 정의하고, 개발자가 이를 리워드 (Reward)로 구현하며, 생성된 개선 후보를 함께 리뷰하는 방식. 그러한 역할 분담을 통해 에이전트를 육성해 나갈 수 있는 가능성을 느꼈습니다.
이번에는 시스템 프롬프트만을 업데이트했지만, 향후 Memory나 Skill 같은 하네스 (Harness)의 다른 요소들로 대응 범위가 넓어진다면, 에이전트를 육성하는 선택지가 더욱 늘어날 것으로 보입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기