AI 보조 개발12 분 읽기

내 LLM 요약봇은 3주 내내 '개입 불필요'라고 말했다

매일 봇 상태를 LLM에 넣어 5줄 요약을 받았습니다. 3주 동안 '규칙대로 진행 중'이었습니다. 같은 기간 같은 예산을 그냥 들고 있었으면 20%p 더 벌었습니다. 모델은 거짓말하지 않았습니다. 제가 준 JSON에 비교 대상이 없었을 뿐입니다.

#llm#automation#dashboard#methodology#reality-check
모델에 넘긴 상태 JSON에 절대값만 있어서 요약이 낙관적일 수밖에 없었던 2패널 도식
왼쪽: 입력 JSON의 모든 필드가 절대값. 오른쪽: 같은 예산 단순보유와 나란히 놓으면 -20.38%p.

페이퍼(모의) 운영입니다. 수익률과 비율은 실제 원장 값이고, 예산은 실제 설정값입니다.

봇 대시보드에 LLM 코멘터리 패널을 붙여뒀습니다. 매일 상태를 JSON으로 만들어 로컬 CLI에 넘기면 한국어 5줄 요약이 돌아옵니다. 마지막 줄은 항상 총평입니다.

3주 동안 그 총평은 이랬습니다.

총평: 원장 정합성 정상, 규칙대로 진행 중이며 개입 불필요합니다.

같은 3주 동안 실제로 무슨 일이 있었냐면, 같은 예산을 그냥 해당 종목에 넣고 가만히 있었을 경우 대비 -20.38%p였습니다. 다른 종목 봇은 -35.47%p.

모델이 잘못 요약한 게 아닙니다. 제가 준 데이터로는 저 문장 말고 나올 게 없었습니다.

당신의 AI 요약 패널은, 나쁜 소식을 말할 수 있는 입력을 받고 있습니까?

내가 모델에게 준 것

상태 JSON은 이랬습니다.

{
  "symbol": "...",
  "cycle_no": 2,
  "shares": 3,
  "avg_price_usd": 73.23,
  "invested_usd": 219.69,
  "carryover_usd": 122.78,
  "realized_pnl_usd": 16.61,
  "commission_usd": 0.0,
  "cumulative_loss_usd": 0.0,
  "ledger_ok": true
}

한 줄씩 보면 전부 정상적인 운영 지표입니다. 그런데 성격을 분류해 보면 이렇게 갈립니다.

  • 규칙 준수 지표: cycle_no, shares, invested_usd, ledger_ok, cumulative_loss_usd
  • 성과 지표: realized_pnl_usd 하나
  • 비교 지표: 없음

성과 지표가 하나뿐이고, 그게 +16.61이었습니다. 양수입니다. 그리고 손실 한도 지표는 0이었습니다. 위반 없음.

이 입력을 받은 모델이 낼 수 있는 결론은 구조적으로 하나뿐입니다. "규칙 위반 없음 + 실현손익 양수 = 잘 되고 있음."

절대값만 주면 낙관밖에 안 나옵니다. 모델이 낙관적이어서가 아니라, 비관할 근거를 안 줬기 때문입니다.

이 전략은 원래 벤치에 진다

여기서 중요한 반전이 있습니다. 이 봇이 단순보유에 지는 건 버그가 아닙니다.

분할매수 전략은 예산을 40등분해 나눠 담습니다. 3주 시점에 투입된 건 예산의 6%였고, 94%는 현금으로 대기 중이었습니다. 상승장에서 현금은 아무것도 벌지 않습니다. 뒤지는 게 당연합니다.

더 결정적인 건, 저는 이걸 이미 알고 있었다는 겁니다. 배포 전에 쓴 백테스트 문서에 이런 섹션이 있습니다.

가장 중요한 발견: 기대수익은 단순보유보다 훨씬 작다

전체 히스토리에서 전략 실현수익 +499%는 같은 기간 단순보유 +29,214%에 비하면 1.7%도 안 된다.

알고 있었고, 문서에 적었고, 커밋했습니다. 그리고 운영 화면에는 넣지 않았습니다.

이게 진짜 문제입니다. 아는 것과 화면에 있는 것은 다릅니다. 매일 아침 보는 건 문서가 아니라 대시보드고, 대시보드에는 "+$16.61"과 "개입 불필요"만 있었습니다.

당신이라면 프롬프트를 고치겠습니까

여기서 두 갈래가 있습니다.

  1. 프롬프트를 고친다 — "낙관적으로 쓰지 마라", "비판적으로 보라"를 시스템 프롬프트에 추가
  2. 입력을 고친다 — 비교 대상을 JSON에 넣는다

1번은 유혹적입니다. 한 줄이면 되니까요. 그리고 실제로 톤은 바뀝니다. 모델이 조심스러운 표현을 쓰기 시작합니다.

하지만 데이터가 그대로면 모델은 없는 사실을 조심스럽게 말할 뿐입니다. "실현손익은 양수이나 신중한 접근이 필요합니다" 같은 문장이 나옵니다. 정보량은 0인데 겉보기엔 균형 잡혀 보여서 오히려 더 나쁩니다.

2번을 택했습니다. 프롬프트 튜닝은 데이터 결손을 못 메웁니다.

사과 대 사과로 맞추기

비교값 계산에서 걸린 함정이 하나 있었습니다. 처음엔 이렇게 짰습니다.

전략 수익률 = 실현손익 / 예산

이러면 전략이 실제보다 나빠 보입니다. 보유 중인 주식의 평가손익이 빠져 있으니까요. 벤치마크는 미실현을 다 반영하는데 전략만 실현분으로 재면 비교가 성립하지 않습니다.

고친 정의는 이렇습니다.

unrealized  = shares * last_price - cost_basis
strat_value = budget + realized_pnl + unrealized
strat_ret   = strat_value / budget - 1
hold_ret    = last_price / first_fill_price - 1

기준점 세 가지를 맞춰야 합니다. 같은 예산, 같은 시작 시점(원장의 최초 체결일·체결가), 같은 평가 방식(미실현 포함). 하나라도 어긋나면 숫자가 나와도 근거로 못 씁니다.

여기에 cash_idle_pct(미투입 현금 비중)를 같이 노출했습니다. 이게 있어야 열위의 원인이 읽힙니다 — 신호가 나빠서가 아니라 자본을 안 넣어서라는 것.

바뀐 출력

benchmark_vs_buyhold 필드를 JSON에 추가하고, 시스템 프롬프트에 한 줄을 박았습니다.

benchmark_vs_buyhold가 있으면 반드시 한 줄을 할애해 동일예산 단순보유 대비 초과수익을 인용한다. 절대 실현손익만으로 '잘 되고 있다'고 쓰지 않는다. 이 전략은 상승장 열위가 설계이며 실패가 아니라고 명시한다.

같은 봇, 같은 날, 바뀐 출력입니다.

동일예산 단순보유 대비 초과수익 -20.38%p (전략 0.59% vs 보유 20.97%). 예산 3,424달러 중 93.6%가 현금 대기 상태, 상승장 열위는 이 전략 설계상 당연한 결과이며 실패가 아니다. 판정 기준은 낙폭 방어 성능이나, 하락 구간이 없어 아직 검증되지 않았다. 실현손익 16.61달러이나 이 숫자만으로 성과를 판단할 수 없다. 누적손실 0달러, 원장 정상 — 규칙대로 진행 중, 개입 불필요.

마지막 줄은 그대로입니다. 그리고 그건 맞습니다. 규칙대로 돌고 있는 건 사실이고, 지금 개입할 이유도 없습니다. 달라진 건 그 문장이 이제 네 줄의 맥락 위에 놓여 있다는 것입니다.

같은 결론이라도, 비교값을 보고 내린 "개입 불필요"와 절대값만 보고 내린 "개입 불필요"는 다른 문장입니다.

자가진단 3줄

LLM에 요약·판정·리포트를 맡기고 있다면:

  1. 입력 JSON에서 "비교" 성격의 필드가 몇 개입니까? 0개라면 그 요약은 구조적으로 낙관 편향입니다. 톤과 무관합니다.
  2. 모델이 "나쁘다"고 말하려면 어떤 필드를 봐야 합니까? 그 필드가 입력에 없으면, 모델은 나쁘다고 말할 수 없습니다.
  3. 당신이 문서에 적어둔 한계가 운영 화면에도 있습니까? 아는 것과 매일 보는 것은 다릅니다. 매일 보는 쪽이 이깁니다.

솔직한 부분

이 봇은 여전히 벤치마크에 집니다. 고친 뒤에도 -20.38%p입니다. 그건 안 바뀝니다 — 예산의 94%가 현금인 전략이니까요.

바뀐 건 그 사실이 화면에 있다는 것뿐입니다. 그리고 그게 판정을 가능하게 만듭니다. 이 전략의 주장은 "더 많이 번다"가 아니라 "같은 예산으로 낙폭을 줄인다"이고, 그건 하락 구간이 와야 시험됩니다. 그때까지는 무판정이고, 실계좌 전환도 없습니다.

한 가지 더. 이 결함을 3주나 못 본 이유는 코멘터리가 틀린 말을 한 적이 없기 때문입니다. "원장 정합성 정상"도 사실이고 "누적손실 0"도 사실입니다. 전부 참인 문장으로 조립된 잘못된 인상이었습니다. 모니터가 거짓말하는 다른 방식이나 수집 상한이 결론을 대신 써준 경우와 같은 계열인데, 이번 건은 코드에 버그조차 없었습니다. 빠진 필드 하나가 전부였습니다.

지금 AI에게 상태 요약을 시키고 있다면, 그 입력 JSON을 열어서 필드를 하나씩 읽어보세요. 그중 몇 개가 "무엇과 비교해서"에 답합니까?

관련 글