From 4aed9c61731e76a6e6f05ca53b77ecd515be70c3 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Tue, 12 May 2026 13:02:16 +0900 Subject: [PATCH] fix(briefing): simplify migration SQL (remove unicode, ::jsonb cast) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit asyncpg 'cannot insert multiple commands into a prepared statement' 회피. 가설: 한국어 코멘트의 special char (lambda/arrow) + '::jsonb' cast 가 asyncpg prepare 에서 multi-statement 오인. Phase 4 101 SQL 패턴과 정확히 맞춤 — JSONB column 이라 default literal 은 자동 cast. --- migrations/255_morning_briefings.sql | 59 ++++++++++------------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/migrations/255_morning_briefings.sql b/migrations/255_morning_briefings.sql index 67d4999..cd57253 100644 --- a/migrations/255_morning_briefings.sql +++ b/migrations/255_morning_briefings.sql @@ -1,66 +1,49 @@ -- 야간 수집 뉴스 브리핑 (Morning Briefing) --- 매일 KST 자정~05:00 사이 수집된 뉴스를 topic×country 비교 분석 1페이지 카드. +-- 매일 KST 자정~05:00 사이 수집된 뉴스를 topic-country 비교 분석 1페이지 카드. -- 트리거: 05:10 KST APScheduler cron (PR-3 에서 등록). Phase 4 와 axis 반대 (topic-first). -- 코드/로직/테이블 모두 Phase 4 와 분리. 공통 알고리즘만 services/clustering_common 공유. --- 부모: 일일 1행 CREATE TABLE morning_briefings ( id BIGSERIAL PRIMARY KEY, - briefing_date DATE NOT NULL, -- KST 기준 (윈도우 자정 시작일) - window_start TIMESTAMPTZ NOT NULL, -- UTC 환산 자정 - window_end TIMESTAMPTZ NOT NULL, -- UTC 환산 05:00 KST - decay_lambda DOUBLE PRECISION NOT NULL, -- 실제 사용된 time-decay λ (briefing = ln(2)/2h) - + briefing_date DATE NOT NULL, + window_start TIMESTAMPTZ NOT NULL, + window_end TIMESTAMPTZ NOT NULL, + decay_lambda DOUBLE PRECISION NOT NULL, total_articles INTEGER NOT NULL DEFAULT 0, total_countries INTEGER NOT NULL DEFAULT 0, total_topics INTEGER NOT NULL DEFAULT 0, - generation_ms INTEGER, llm_calls INTEGER NOT NULL DEFAULT 0, llm_failures INTEGER NOT NULL DEFAULT 0, - status VARCHAR(20) NOT NULL DEFAULT 'success', -- success | partial | failed | empty - - headline_oneliner TEXT, -- 향후 별 단계 (지금은 NULL) - + status VARCHAR(20) NOT NULL DEFAULT 'success', + headline_oneliner TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - UNIQUE (briefing_date) -- idempotency: regenerate 시 DELETE+INSERT + UNIQUE (briefing_date) ); CREATE INDEX idx_morning_briefings_date ON morning_briefings (briefing_date DESC); --- 자식: 1 briefing 안 topic_rank 순 (importance_score 내림차순) CREATE TABLE briefing_topics ( id BIGSERIAL PRIMARY KEY, briefing_id BIGINT NOT NULL REFERENCES morning_briefings(id) ON DELETE CASCADE, - - topic_rank INTEGER NOT NULL, -- 1..N - topic_label VARCHAR(120) NOT NULL, -- "이란-이스라엘 충돌" 등 5~10 단어 한국어 - headline TEXT NOT NULL, -- 1줄 요약 - - -- LLM 비교 분석 결과 (JSONB cap: perspectives ≤10, divergences ≤3, convergences ≤2, quotes ≤5) - country_perspectives JSONB NOT NULL DEFAULT '[]'::jsonb, -- [{country, summary, article_ids}, ...] - divergences JSONB NOT NULL DEFAULT '[]'::jsonb, -- ["A국 X / B국 Y", ...] - convergences JSONB NOT NULL DEFAULT '[]'::jsonb, -- ["모두 Z 일치", ...] - key_quotes JSONB NOT NULL DEFAULT '[]'::jsonb, -- [{country, source, quote}, ...] - - -- Historical context (BRIEFING_HISTORICAL_ENABLED=false default off, PR-1b 에서 on) - historical_article_ids JSONB, -- [doc_id, ...] top-K 과거 참고 (페이지 노출 X) - historical_context TEXT, -- LLM 생성 "지난 흐름" 1~2문장 - historical_window_days INTEGER, -- 사용된 N (기본 30) - - -- Cluster 메타 (LLM 성공/실패와 무관, UI 폴백 link 용) - cluster_members JSONB NOT NULL DEFAULT '[]'::jsonb, -- [doc_id, ...] cluster 전체 article + topic_rank INTEGER NOT NULL, + topic_label VARCHAR(120) NOT NULL, + headline TEXT NOT NULL, + country_perspectives JSONB NOT NULL DEFAULT '[]', + divergences JSONB NOT NULL DEFAULT '[]', + convergences JSONB NOT NULL DEFAULT '[]', + key_quotes JSONB NOT NULL DEFAULT '[]', + historical_article_ids JSONB, + historical_context TEXT, + historical_window_days INTEGER, + cluster_members JSONB NOT NULL DEFAULT '[]', article_count INTEGER NOT NULL, - country_count INTEGER NOT NULL, -- cluster 안 distinct country 수 (MIN_COUNTRIES_PER_TOPIC=2 필터 통과) - importance_score DOUBLE PRECISION NOT NULL, -- briefing 내 0~1 normalized + country_count INTEGER NOT NULL, + importance_score DOUBLE PRECISION NOT NULL, raw_weight_sum DOUBLE PRECISION NOT NULL, - llm_model VARCHAR(100), llm_fallback_used BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - UNIQUE (briefing_id, topic_rank) );