Fix: [3.0.2] 요약 응답 구조화 + Discord 임베드 디자인 개선
All checks were successful
news-summary-bot-cicd / build_push_deploy (push) Successful in 9m14s

- 요약을 JSON으로 구조화: oneliner, main_points, conclusion 분리
- Claude에게 JSON 형식으로만 응답하도록 프롬프트 변경
- n8n Discord 임베드: 섹션별 필드 분리, 이모지, 타임스탬프 추가
- JSON.stringify Expression으로 특수문자 이스케이프 처리
- 전체 문서 API 응답 형식 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sm4640
2026-03-25 17:18:31 +09:00
parent f4532840cf
commit 9c2bf7c1ce
6 changed files with 86 additions and 40 deletions

View File

@@ -41,7 +41,7 @@ async def summarize_video(
"error_message": str(e),
}
return {**base, "status": "ok", "summary": summary}
return {**base, "status": "ok", **summary}
@app.get("/health")

View File

@@ -1,3 +1,6 @@
import json
import re
import anthropic
from app.config import settings
@@ -8,20 +11,26 @@ SYSTEM_PROMPT = """너는 뉴스/경제 유튜브 영상을 시청하고 핵심
영상을 직접 다 본 사람처럼, 구체적인 수치·사례·맥락을 포함해서 요약해줘.
읽는 사람이 영상을 안 봐도 내용을 충분히 파악할 수 있어야 해.
## 형식
- **한줄 요약**: 영상의 핵심 메시지를 구체적으로 한 문장
- **주요 내용**: 영상에서 다룬 핵심 포인트를 3~7개 불릿으로 정리. 각 항목에 구체적인 수치, 종목명, 인물, 사건 등을 반드시 포함
- **결론/시사점**: 영상이 전달하려는 메시지와 시청자가 취할 수 있는 액션
## 출력 형식 (반드시 이 JSON 형식으로만 응답)
```json
{
"oneliner": "영상의 핵심 메시지를 구체적으로 한 문장",
"main_points": "• 핵심 포인트 1\\n• 핵심 포인트 2\\n• 핵심 포인트 3",
"conclusion": "영상이 전달하려는 메시지와 시청자가 취할 수 있는 액션"
}
```
## 규칙
- 반드시 위 JSON 형식으로만 응답. 다른 텍스트 없이 JSON만 출력
- 한국어로 작성
- main_points는 3~7개 불릿(•)으로 정리. 각 항목에 구체적인 수치, 종목명, 인물, 사건 등을 반드시 포함
- "~에 대해 이야기했다" 같은 메타 서술 금지. 내용 자체를 직접 전달
- 자막의 오타나 말더듬은 무시하고 의미 중심으로 정리
- 영상에서 언급된 구체적 수치(%, 금액, 날짜 등)가 있으면 반드시 포함
"""
def summarize(transcript: str, title: str) -> str:
def summarize(transcript: str, title: str) -> dict:
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2048,
@@ -33,4 +42,20 @@ def summarize(transcript: str, title: str) -> str:
}
],
)
return message.content[0].text
raw = message.content[0].text
# JSON 블록 추출 (```json ... ``` 또는 순수 JSON)
json_match = re.search(r"```json\s*(.*?)\s*```", raw, re.DOTALL)
json_str = json_match.group(1) if json_match else raw.strip()
try:
parsed = json.loads(json_str)
except json.JSONDecodeError:
# 파싱 실패 시 전체 텍스트를 oneliner로
parsed = {"oneliner": raw, "main_points": "", "conclusion": ""}
return {
"oneliner": parsed.get("oneliner", ""),
"main_points": parsed.get("main_points", ""),
"conclusion": parsed.get("conclusion", ""),
}