콘텐츠로 이동

POST_MARKET UNIT별 에이전트 파이프라인 설계

문제

claude -p 한 번 호출로 UNIT 1~9 전체를 작성하면: - 명세를 부분적으로만 따름 (포맷 불일치, 축약, 불필요 단락 추가) - 같은 데이터로 매번 다른 결과 (품질 들쭉날쭉) - 성공조건 검증 없음 (실패해도 그대로 출력)

목표

각 UNIT을 독립 에이전트로 실행. 각 에이전트는: 1. 고정 질문: 무엇을 답해야 하는가 2. 입력 데이터: 어떤 파일/필드를 읽는가 3. 출력 포맷: 마크다운 템플릿 고정 4. 성공조건: 출력에 반드시 포함되어야 할 요소 5. 실패 시 재실행: 성공조건 미달 → 최대 2회 재시도

결과를 조립해서 최종 리포트 생성.

아키텍처

run_routine.py Phase 1: 데이터 수집 (23개 스크립트 + 6A)
    ↓
run_report_pipeline.py: UNIT별 에이전트 순차/병렬 호출
    ↓
    UNIT 1 → daily_narrative_agent → unit1.md (검증 → pass/retry)
    UNIT 2 → derivative_agent → unit2.md (검증 → pass/retry)
    UNIT 3 → limit_up_agent → unit3.md (검증 → pass/retry)
    UNIT 4 → theme_landscape_agent → unit4.md (검증 → pass/retry)
    UNIT 5 → leading_6axis_agent → unit5.md (검증 → pass/retry)
    UNIT 6 → go_wait_agent → unit6.md (검증 → pass/retry)
    UNIT 7 → cooling_6axis_agent → unit7.md (검증 → pass/retry)
    UNIT 7.5 → q3_verify_agent → unit7_5.md (검증 → pass/retry)
    UNIT 8 → entry_board_agent → unit8.md (조립만, 검증 간소)
    UNIT 9 → carry_forward_agent → unit9.md (검증 → pass/retry)
    ↓
assemble: unit1~9.md → docs/daily_reports/YYYYMMDD_post_market.md

UNIT별 에이전트 명세


UNIT 1. daily_narrative_agent

질문: 오늘 시장이 어떻게 흘러갔는가? 아침 예측은 맞았는가?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | daily_view/{date}.json | global_signals, checkpoint_1, checkpoint_2 | 시간대별 시장 상태 | | morning_open/{date}_morning_open.md | 전문 | 아침 예측/FP/WATCH | | routine_data/{date}_post_market_collected.json | step_B.raw → KOSPI/KOSDAQ 종가 | 종가 수치 |

출력 포맷 (고정, 변경 불가):

> **KOSPI {종가}({등락}%) {1줄 요약}**

## 1. 하루 흐름
**장 전**: {1줄}
**오전 (09:00~10:30)**: {주도종목+등락+거래대금. 역행종목.}
**오후 (10:30~15:20)**: {순환매/반전. 종목+수치.}
**종가**: {종가+상한가수+거래대금합산.}

## 2. 아침 시그널 vs 종가 결과
| # | 아침 시그널 | 예상 전이 | 종가 실제 | 판정 |

## 3. 놓친 시그널
### MISS-1: {제목}
{원인 1줄 + 교훈 1줄}

## 4. 잘한 점
{1~2줄}

## 5. 내일 이월 과제
{carry_forward와 중복 금지}

성공조건: - [ ] > **KOSPI 볼드 인용 1줄 존재 - [ ] 하루 흐름 4단계(장전/오전/오후/종가) 전부 존재 - [ ] 시그널 테이블 행 5개 (daily_view global_signals 수) - [ ] MISS 최소 1건 - [ ] TL;DR 섹션 부재


UNIT 2. derivative_agent

질문: 파생 포지셔닝이 어떻게 변했는가? 무엇을 시사하는가?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | routine_data/{date}_post_market_collected.json | step_D.raw | 선물 주체별, VIX, COT | | routine_data/{date}_post_market_collected.json | step_MS.raw | 매크로 시계열 (외인선물 누적, 베이시스, 프로그램) |

출력 포맷:

## 1. KOSPI200 선물 주체별 포지셔닝
| 날짜 | 외인 | 기관 | 개인 | 베이시스 |
{5d 시계열}

## 2. VIX / 옵션
| 지표 | 값 | 신호 |

## 3. COT/CFTC (주간)
| 계약 | 자산운용 | 레버리지 | 주간 변화 |

## 4. 글로벌 선물 야간
| 지수 | 가격 | 등락 |

## 5. 종합 해석
{숫자 나열 금지. 교차 해석 2~3줄.}

성공조건: - [ ] 선물 시계열 5행 존재 (최근 5거래일) - [ ] VIX 수치 + 콘탱고/백워데이션 판정 - [ ] 종합 해석 2줄 이상 (숫자만 나열 아님) - [ ] source_ref (step_D, step_MS) 명시


UNIT 3. limit_up_agent

질문: 거래대금은 어디로 이동했는가? 상한가 종목의 6축 판정은?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | routine_data/{date}_post_market_collected.json | step_B.raw | TOP TRADE AMOUNT, LIMIT-UP, TOP GAINERS | | routine_data/{date}_post_market_collected.json | step_C.raw | 전일 대비 변화 감지 | | limit_up_analysis/{date}.json | stocks[].d1_verdict | 6축 scores + reasons |

출력 포맷:

## 1. 거래대금 변화
{상시 상위 생략. 신규진입/급변/가격괴리만.}
| # | 종목 | 종가 | 등락 | 거래대금 | 전일비 | 방향 | 비고 |

## 2. 상한가 종목 WHY
### {테마 A} ({N}종목)
| 종목 | 종가 | 수급 주도 | 재료 | 6축 |
**WHY**: {그룹 공통 원인}

### {테마 B} ...

## 3. 6축 상세
| 종목 | 테마리더 | 구조변화 | 정책 | 독점 | 수급 | 재료직접 | 합계 | 판정 |
{limit_up_analysis/{date}.json → d1_verdict.scores}

## 4. 상한가 종합 판정
TRACK: {N} / WATCH: {N} / NOISE: {N}

## 5. 과거 상한가 D+N 추적
| 종목 | 상한가일 | D+1 | D+3 | 현재 |

성공조건: - [ ] 거래대금에 상시 상위(삼성전자/SK하이닉스 등) 미포함 (변화 없으면) - [ ] 상한가 전 종목 6축 판정 (누락 0) - [ ] 6축 상세 테이블에 O/X 전부 기재 - [ ] 테마 그룹핑 검증: BIZ_MATCH와 불일치 시 교정 주석


UNIT 4. theme_landscape_agent

질문: 테마별 자금 흐름과 생명주기 상태는?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | routine_data/{date}_post_market_collected.json | step_RS, step_TL, step_TT, step_SS | RS 시계열, 리더, 추적, 섹터 | | theme_tracker/dashboard.json | leading/cooling/emerging_themes | 60d 추적 |

출력 포맷:

## 1. [G] 그룹 단위 테마 지형도
| [G] | Comp | Grade | Amt% | Accel | RS | 대장주 | 오늘 | 비고 |
{10개+}

## 2. 60일 추적 맥락
{dashboard leading/cooling/emerging 요약}

## 3. 해석
{자금 흐름 방향 1~2줄}

성공조건: - [ ] 테마 10개+ 포함 - [ ] 각 테마에 대장주 명시 - [ ] LEADING/STRONG/ACTIVE/COOLING 분류 전부 존재 - [ ] 오늘 전환 이벤트 (grade 변경) 있으면 명시


UNIT 5. leading_6axis_agent

질문: LEADING 대장주 각각의 6축 상태와 판정은?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | stock_6axis/{date}_leading.json | stocks[] | 6축 자동 판정 결과 | | theme_leaders/{date}.json | pool | 대장주 목록 + leader_axes | | flow_series/batch_{date}_*.json | stocks.{code} | 수급 패턴/누적 | | routine_data/{date}_post_market_collected.json | step_WC | WAIT 조건 |

출력 포맷 (종목당, leader_stock_status.md 6축 표준):

### {종목명} ({코드}) — [G] {테마} | [{판정}]
**현재가**: {X}원 | **고점**: {X}원 | **하락폭**: {-X%}

#### 1) 차트 [{긍정/중립/부정}]
- {2~3줄. RRG/RS/차트 상태.}

#### 2) 수급 [{긍정/중립/부정}]
- **패턴**: {X}, **dual_buy**: {Y}
- **f5d**: {X}주, **f20d**: {X}주 (출처: collect_stock_flow_series.py → 필드명)
- 해석: {가격방향+수급방향 결합 1줄}

#### 3) 재료 [{긍정/중립/부정} / {Fresh/Aging/Stale}]
- {핵심 촉매+날짜. 다음 촉매.}

#### 4) 구조적 경쟁력 [{긍정/중립/부정}]
- {독점/가격결정권/병목/테마독립성. 각 1줄.}

#### 5) 펀더멘탈 [{긍정/중립/부정}]
- {매출/이익/성장률.}

#### 6) 시장 환경 [{긍정/중립/부정}]
- {섹터 RS + 테마 grade.}

#### 판정: [{verdict}] ({N}/6 긍정)
- **조건**: {진입가/SL/Kill}

성공조건: - [ ] is_leader=true 전 종목 포함 (누락 0) - [ ] 종목당 6축 전부 [긍정/중립/부정] 태그 존재 - [ ] 수급에 f5d/f20d 수치 + 패턴 + 출처 존재 - [ ] 판정에 N/6 합산 + 구체적 행동(가격대 포함) - [ ] 전 종목 동일 포맷 (한 종목만 상세, 나머지 1줄 축약 금지)


UNIT 6. go_wait_agent

질문: GO 종목은 유지/청산? WAIT 종목은 진입 조건 충족?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | tracking/verdict_list.json | stocks | GO 종목 목록 | | tracking/wait_watchlist.json | stocks | WAIT 종목 목록 | | routine_data/{date}_post_market_collected.json | step_V, step_FS, step_WC | verdict D+N, 수급, WAIT 조건 | | daily_view/{date}.json | candidate_scenarios | Stage 판정 |

출력 포맷:

## Part A — GO 종목 현황
| 종목 | D+N | 가격 | ret | 오늘 | 수급 | 판정 |
### {종목} 상세
| 조건 | 충족 | 근거 |
{4조건: 차트/재료/수급/R:R}

## Part B — WAIT 종목 현황
### B-1. 활성 WAIT
| 종목 | 테마 | 가격 | 수급 패턴 | 미충족 | 판정 |
### B-2. 오늘 확인 사항
### B-3. PASS 전환 종목
### B-4. WAIT 체류 만료 판정

## Part C — Stage 판정
| 후보 | 이전 Stage | 현재 Stage | 근거 | 다음 trigger or kill |

성공조건: - [ ] GO 전 종목 4조건 테이블 존재 - [ ] WAIT 전 종목 수급 패턴 + 미충족 조건 명시 - [ ] Stage 변경 종목 있으면 근거 필수 - [ ] step_FS 수급 패턴(accumulation/exit 등) 인용


UNIT 7. cooling_6axis_agent

질문: COOLING 대장주 중 재진입 후보가 있는가?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | stock_6axis/{date}_cooling.json | stocks[] | 6축 자동 판정 결과 | | theme_tracker/dashboard.json | falling_leaders | COOLING 대상 목록 | | flow_series/batch_{date}_*.json | stocks.{code} | 수급 패턴 |

출력 포맷:

## 판정 요약
| 종목 | 테마 | 고점대비 | 판정 | 핵심 근거 |

## 개별 6축 분석
{재진입 후보 / 관찰 → UNIT 5와 동일 6축 포맷 (종목당)}
{추적 중단 → 1줄 요약}

## 종합 정리
### 재진입 후보 ({N}종목)
### 관찰 ({N}종목)
### 추적 중단 ({N}종목)

성공조건: - [ ] falling_leaders 전 종목 판정 (누락 0) - [ ] 재진입 후보/관찰 종목은 UNIT 5와 동일 6축 포맷 (축약 금지) - [ ] 추적 중단 종목은 사유 1줄 - [ ] 전 종목 동일 깊이 (한 종목만 상세, 나머지 1줄 금지)


UNIT 7.5. q3_verify_agent

질문: 전일 EVENING Q3 판단이 맞았는가?

입력 데이터: | 파일 | 필드 | 용도 | |------|------|------| | evening_reports/{prev_date}_evening.md | Q3 판단 | 빠질곳/갈곳 예측 | | routine_data/{date}_post_market_collected.json | step_B | 오늘 실제 결과 |

출력 포맷:

| 판단 | 예측 | 실제 | 결과 |
### 틀렸으면 왜? {1줄}

성공조건: - [ ] 전일 Q3 있으면 검증 테이블 존재 - [ ] 없으면 [SKIP] 1줄


UNIT 8. entry_board_agent

질문: 내일 진입 가능한 종목이 있는가?

입력: UNIT 5~7 결과 참조만. 새 분석 금지.

출력 포맷:

#### 즉시 진입 가능
| 종목 | 테마 | 근거 | 진입가 | SL | R:R | 비중 |
{없으면 "해당 없음" 1줄}

#### 조건부 대기
| 종목 | 테마 | 현재 상태 | 미충족 | 내일 확인 |

#### COOLING 재진입 후보
{UNIT 7 판정 참조 1줄씩. 재분석 금지.}

#### 추적 중단 후보
| 종목 | 사유 |

성공조건: - [ ] 4구분(즉시/조건부/COOLING/중단) 전부 존재 - [ ] UNIT 5~7과 정보 중복 없음 (참조만)


UNIT 9. carry_forward_agent

질문: EVENING에 넘길 판단은?

입력: UNIT 1~8 결과 전체.

출력 포맷:

### 오늘 결론 3줄
### 내일 확인 포인트
| 항목 | 내용 |
### 테마 순환 예상
{자금 이동 방향 1~2줄}

성공조건: - [ ] 결론 3줄 이내 - [ ] 확인 포인트 3개+ - [ ] GO verdict 변경 사항 반영


검증 엔진

def validate_unit(unit_id, content, criteria):
    """성공조건 체크리스트 검증"""
    results = []
    for criterion in criteria:
        passed = criterion.check(content)
        results.append((criterion.name, passed))

    all_passed = all(r[1] for r in results)
    if not all_passed:
        failed = [r[0] for r in results if not r[1]]
        return False, failed
    return True, []

검증 예시: | 조건 | 검증 방법 | |------|----------| | 볼드 인용 존재 | > ** 패턴 매칭 | | 하루 흐름 4단계 | **장 전**, **오전**, **오후**, **종가** 4개 존재 | | 시그널 테이블 5행 | \| 구분 행 카운트 | | 6축 태그 전부 존재 | [긍정], [중립], [부정] 중 하나가 6번 | | N/6 합산 존재 | \d/6 패턴 매칭 | | 상시 상위 미포함 | 삼성전자.*거래대금.*#1 패턴 부재 (변화 없을 때) |

실행 흐름

# run_report_pipeline.py

MAX_RETRY = 2

for unit_id, agent_config in UNIT_AGENTS.items():
    for attempt in range(MAX_RETRY + 1):
        # 1. 에이전트 호출
        result = call_agent(
            prompt=agent_config['prompt'],
            data_files=agent_config['data_files'],
            template=agent_config['template'],
        )

        # 2. 성공조건 검증
        passed, failed = validate_unit(unit_id, result, agent_config['criteria'])

        if passed:
            save_unit(unit_id, result)
            break
        elif attempt < MAX_RETRY:
            # 실패 항목 명시하여 재시도
            result = call_agent(
                prompt=f"이전 출력의 문제: {failed}. 수정하여 재작성.",
                data_files=agent_config['data_files'],
                previous_output=result,
            )
        else:
            save_unit(unit_id, result, status='PARTIAL')
            log_failure(unit_id, failed)

# 3. 조립
assemble_report(date)

파일 구조

scripts/
  run_routine.py          # Phase 1: 데이터 수집 (기존 유지)
  run_report_pipeline.py  # Phase 2: UNIT별 에이전트 호출 + 검증 + 조립 (신규)
  analyze_stock_6axis.py  # 6축 분석 스크립트 (기존)

data/
  unit_outputs/{date}/    # UNIT별 개별 출력 (신규)
    unit1.md
    unit2.md
    ...
    unit9.md
  stock_6axis/{date}_leading.json  # 6축 결과 (기존)
  stock_6axis/{date}_cooling.json

docs/
  daily_reports/{date}_post_market.md  # 최종 조립 결과

단계별 구현 계획

단계 내용 선행 조건
1 run_report_pipeline.py 프레임워크 (UNIT 순차 호출 + 검증 + 조립) 없음
2 UNIT 1/2/4 구현 (데이터 정리형, 비교적 단순) 단계 1
3 UNIT 3 구현 (상한가 6축 = analyze_limit_up.py 결과 활용) 단계 1
4 UNIT 5/7 구현 (대장주 6축 = analyze_stock_6axis.py 결과 활용) 단계 1
5 UNIT 6 구현 (GO/WAIT = verdict_list + wait_watchlist) 단계 1
6 UNIT 8/9 구현 (조립형) 단계 2~5
7 검증 엔진 구현 + 재시도 로직 단계 1~6
8 5/12 데이터로 전체 파이프라인 검증 전체

MORNING_OPEN / EVENING 확장

동일 구조를 MORNING_OPEN(7 UNIT)과 EVENING(3 STEP)에도 적용. run_report_pipeline.py--slot morning_open|post_market|evening 인자를 받아서 각 시간대의 UNIT 설정을 로드.

SLOT_CONFIGS = {
    'post_market': POST_MARKET_UNITS,  # 위 9개
    'morning_open': MORNING_OPEN_UNITS,  # 별도 정의
    'evening': EVENING_STEPS,  # 별도 정의
}