Fix: [3.0.3] JSON 파싱 강화 + Discord 전송 안정화
Some checks failed
news-summary-bot-cicd / build_push_deploy (push) Has been cancelled
Some checks failed
news-summary-bot-cicd / build_push_deploy (push) Has been cancelled
- summarizer: 코드펜스/잡텍스트 포함된 Claude 응답도 안정적으로 파싱 - summarizer: 프롬프트에 코드펜스 금지 명시 - main: Discord embed용 summary 통합 필드 추가 - docs: n8n Discord 노드를 JSON.stringify() 방식으로 변경 (줄바꿈/따옴표 이스케이프) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
10
app/main.py
10
app/main.py
@@ -41,6 +41,16 @@ async def summarize_video(
|
||||
"error_message": str(e),
|
||||
}
|
||||
|
||||
# Discord embed description용 통합 요약
|
||||
parts = []
|
||||
if summary.get("oneliner"):
|
||||
parts.append(f"**{summary['oneliner']}**")
|
||||
if summary.get("main_points"):
|
||||
parts.append(f"\n{summary['main_points']}")
|
||||
if summary.get("conclusion"):
|
||||
parts.append(f"\n> {summary['conclusion']}")
|
||||
summary["summary"] = "\n".join(parts)
|
||||
|
||||
return {**base, "status": "ok", **summary}
|
||||
|
||||
|
||||
|
||||
@@ -11,17 +11,12 @@ SYSTEM_PROMPT = """너는 뉴스/경제 유튜브 영상을 시청하고 핵심
|
||||
영상을 직접 다 본 사람처럼, 구체적인 수치·사례·맥락을 포함해서 요약해줘.
|
||||
읽는 사람이 영상을 안 봐도 내용을 충분히 파악할 수 있어야 해.
|
||||
|
||||
## 출력 형식 (반드시 이 JSON 형식으로만 응답)
|
||||
```json
|
||||
{
|
||||
"oneliner": "영상의 핵심 메시지를 구체적으로 한 문장",
|
||||
"main_points": "• 핵심 포인트 1\\n• 핵심 포인트 2\\n• 핵심 포인트 3",
|
||||
"conclusion": "영상이 전달하려는 메시지와 시청자가 취할 수 있는 액션"
|
||||
}
|
||||
```
|
||||
## 출력 형식
|
||||
반드시 아래 JSON만 출력해. 코드펜스(```)로 감싸지 마.
|
||||
{"oneliner": "영상의 핵심 메시지를 구체적으로 한 문장", "main_points": "• 핵심 포인트 1\\n• 핵심 포인트 2\\n• 핵심 포인트 3", "conclusion": "영상이 전달하려는 메시지와 시청자가 취할 수 있는 액션"}
|
||||
|
||||
## 규칙
|
||||
- 반드시 위 JSON 형식으로만 응답. 다른 텍스트 없이 JSON만 출력
|
||||
- 반드시 위 JSON 형식으로만 응답. 코드펜스, 설명 등 다른 텍스트 절대 금지
|
||||
- 한국어로 작성
|
||||
- main_points는 3~7개 불릿(•)으로 정리. 각 항목에 구체적인 수치, 종목명, 인물, 사건 등을 반드시 포함
|
||||
- "~에 대해 이야기했다" 같은 메타 서술 금지. 내용 자체를 직접 전달
|
||||
@@ -44,18 +39,28 @@ def summarize(transcript: str, title: str) -> dict:
|
||||
)
|
||||
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": ""}
|
||||
parsed = _parse_json_response(raw)
|
||||
|
||||
return {
|
||||
"oneliner": parsed.get("oneliner", ""),
|
||||
"main_points": parsed.get("main_points", ""),
|
||||
"conclusion": parsed.get("conclusion", ""),
|
||||
}
|
||||
|
||||
|
||||
def _parse_json_response(raw: str) -> dict:
|
||||
"""Claude 응답에서 JSON을 추출하여 파싱한다."""
|
||||
# 1) 코드펜스 제거 (```json ... ``` 또는 ``` ... ```)
|
||||
json_match = re.search(r"```(?:json)?\s*(.*?)\s*```", raw, re.DOTALL)
|
||||
json_str = json_match.group(1) if json_match else raw.strip()
|
||||
|
||||
# 2) 첫 번째 { ~ 마지막 } 만 추출 (앞뒤 잡텍스트 제거)
|
||||
brace_match = re.search(r"\{.*\}", json_str, re.DOTALL)
|
||||
if brace_match:
|
||||
json_str = brace_match.group(0)
|
||||
|
||||
try:
|
||||
return json.loads(json_str)
|
||||
except json.JSONDecodeError:
|
||||
# 파싱 실패 시 전체 텍스트를 oneliner로
|
||||
return {"oneliner": raw, "main_points": "", "conclusion": ""}
|
||||
|
||||
Reference in New Issue
Block a user