Skip to main content
Glama
README.md
# crosscheck

> Reddit 팔로우 글의 **주장(claim)을 자동 교차검증**해 매일 아침 Discord로 다이제스트를 보내는 AI 자동화 시스템

[![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white)](https://python.org)
[![Claude](https://img.shields.io/badge/Anthropic-Claude_Sonnet-D4A843?logo=anthropic&logoColor=white)](https://anthropic.com)
[![MCP](https://img.shields.io/badge/MCP-FastMCP-6C52A0)](https://modelcontextprotocol.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## 어떤 프로젝트인가요?

SNS에 쏟아지는 정보 중 **사실 주장**만 골라내, 신뢰할 만한 뉴스 소스와 자동으로 대조한 뒤 매일 아침 Discord에 요약을 보내줍니다.

> ⚠️ **중요한 설계 원칙**: 이 시스템은 글의 **진위(진짜/가짜)를 판정하지 않습니다.**
> 각 주장에 대해 확인됨 / 반박됨 / 미보도 / 불충분 교차검증 결과와 **근거 링크**만 제시합니다.
> 최종 판단은 사용자가 합니다.

---

## 주요 기능

| 기능 | 설명 |
|------|------|
| **Reddit 수집** | 지정한 서브레딧의 최신 글을 자동 수집 (PRAW + RSS 폴백) |
| **주장 추출** | LLM으로 글에서 검증 가능한 사실 주장만 선별 |
| **뉴스 교차검증** | Anthropic 웹 검색으로 신뢰 소스(Reuters, Bloomberg 등) 기사 탐색 |
| **판정 + 근거** | 교차검증 결과를 4종 verdict와 근거 기사 링크로 구조화 |
| **Discord 다이제스트** | 결과를 임베드 형식으로 Discord 채널에 자동 전송 |
| **MCP 서버** | Claude Desktop에서 "오늘 새 글 정리해줘"로 대화형 사용 가능 |
| **중복 방지** | SQLite 캐시로 같은 글/주장 재처리 차단 |
| **Mock 모드** | API 키 없이도 --mock 플래그로 전체 파이프라인 테스트 가능 |

---

## 아키텍처

`
Reddit (PRAW / RSS)
        │
        ▼
 core/reddit.py          ← 새 글 수집, seen_posts 캐시 필터
        │
        ▼
 core/claims.py          ← LLM: 검증 가능한 주장 추출
        │
        ▼
 core/news.py            ← Anthropic web_search: 신뢰 소스 기사 탐색
        │
        ▼
 core/assess.py          ← LLM: 교차검증 판정 (grounding 규칙 강제)
        │
        ▼
 core/discord.py         ← Discord 웹훅으로 다이제스트 전송
`

`
core/          ← MCP에 의존하지 않는 순수 함수 (테스트·재사용 가능)
server.py      ← core를 @mcp.tool()로 감싼 FastMCP 서버 (Claude Desktop용)
daily_run.py   ← cron/launchd로 매일 1회 실행하는 자동화 진입점
`

![파이프라인 흐름도](crosscheck_flowchart.svg)

---

## 기술 스택

| 분류 | 기술 |
|------|------|
| 언어 | Python 3.11+ |
| AI | Anthropic Claude Sonnet (주장 추출 · 교차검증 판정) |
| AI 도구 | Anthropic Web Search (web_search_20260209) |
| MCP | 공식 Python SDK 내장 FastMCP (mcp[cli]>=1.27,<2) |
| Reddit | PRAW 7+ (OAuth) / 공개 RSS 폴백 |
| 전송 | Discord Incoming Webhook (httpx) |
| 캐시 | SQLite (state/cache.db) |
| 스케줄 | macOS launchd / cron |

---

## 설치

`ash
# 1. 저장소 클론
git clone https://github.com/YOUR_USERNAME/crosscheck.git
cd crosscheck

# 2. 가상환경 생성 및 의존성 설치
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

# 3. 환경변수 설정
cp .env.example .env
# .env 파일을 열어 아래 키 값을 채워주세요 (아래 "환경변수" 참고)
`

### 환경변수 (.env)

`env
# Discord (채널 설정 → 연동 → 웹훅 생성)
DISCORD_WEBHOOK_URL=

# Reddit (https://www.reddit.com/prefs/apps 에서 script 앱 생성)
REDDIT_CLIENT_ID=
REDDIT_CLIENT_SECRET=
REDDIT_USER_AGENT=crosscheck by u/your_username

# Anthropic
ANTHROPIC_API_KEY=
`

Reddit 키 없이도 --mock 플래그로 가짜 데이터를 사용해 테스트할 수 있습니다.

---

## 사용 방법

### 단계별 수동 테스트

`ash
# 1. Discord 전송 확인
.venv/bin/python scripts/test_discord.py

# 2. Reddit 글 수집 (--mock: 가짜 데이터 사용)
.venv/bin/python scripts/fetch_and_send.py --mock

# 3. 주장 추출
.venv/bin/python scripts/test_claims.py

# 4. 뉴스 검색
.venv/bin/python scripts/test_news.py

# 5. 교차검증 판정
.venv/bin/python scripts/test_assess.py

# 6. 전체 파이프라인 (실제 Discord 전송 포함)
.venv/bin/python scripts/test_pipeline.py --send

# 7. 자동 실행 진입점
.venv/bin/python daily_run.py --mock --dry-run
`

### Claude Desktop에서 대화형 사용 (MCP 서버)

`ash
# MCP Inspector로 도구 확인
.venv/bin/mcp dev server.py
`

Claude Desktop claude_desktop_config.json에 등록:
`json
{
  "mcpServers": {
    "crosscheck": {
      "command": "/절대경로/.venv/bin/python",
      "args": ["/절대경로/server.py"]
    }
  }
}
`

등록 후 Claude Desktop에서 "오늘 r/stocks 새 글 교차검증해줘" 라고 입력하면 5개 도구를 자동으로 호출합니다.

### 매일 자동 실행 (macOS launchd)

`ash
cp deploy/com.crosscheck.daily.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.crosscheck.daily.plist
launchctl start com.crosscheck.daily   # 즉시 한 번 테스트 실행
`

기본 매일 **08:00** 실행. 시간 변경은 plist의 Hour/Minute 수정 후 unload→load.
로그는 state/daily_run.log / .err에 기록됩니다.

---

## MCP 도구 목록

| 도구 | 설명 |
|------|------|
| get_reddit_posts | 서브레딧에서 새 글 수집 (키 없으면 목업 반환) |
| extract_claims | 글에서 검증 가능한 사실 주장 추출 |
| search_news | 신뢰 소스 뉴스에서 주장 관련 기사 탐색 |
| ssess_claim | 뉴스 근거와 대조해 교차검증 판정 |
| send_discord | Discord 웹훅으로 다이제스트 전송 |

---

## 교차검증 판정 기준

| Verdict | 설명 | Discord 표시 |
|---------|------|--------------|
| corroborated | 신뢰 소스가 같은 내용 보도 | ✅ 확인됨 |
| contradicted | 신뢰 소스가 반대 내용 보도 | ❌ 반박됨 |
| uncovered | 관련 보도를 찾지 못함 | ⚪ 미보도 |
| insufficient | 관련은 있으나 판단하기엔 근거 부족 | 🟡 불충분 |

**Grounding 규칙**: corroborated / contradicted 판정에는 반드시 근거 기사 번호(source_idxs)가 있어야 합니다. 근거 없이 단정하면 코드 레벨에서 uncovered로 강등됩니다.

**신뢰 소스 allowlist**: Reuters, AP News, Bloomberg, WSJ, FT, CNBC, BBC, NPR, The Guardian, NYT, WaPo, Axios, MarketWatch, Barron's, Forbes, Seeking Alpha 등

---

## 디렉토리 구조

`
crosscheck/
├── core/
│   ├── reddit.py        # get_reddit_posts (PRAW + RSS 폴백)
│   ├── claims.py        # extract_claims (LLM 구조화 출력)
│   ├── news.py          # search_news (웹 검색 + 신뢰 소스 필터)
│   ├── assess.py        # assess_claim (grounding 강제)
│   ├── discord.py       # send_discord (웹훅 전송)
│   ├── pipeline.py      # crosscheck_post (전체 체인)
│   └── cache.py         # SQLite 중복 방지 캐시
├── server.py            # FastMCP 래퍼 (Claude Desktop용)
├── daily_run.py         # cron 자동 실행 진입점
├── scripts/             # 단계별 수동 테스트 스크립트
├── deploy/              # launchd plist
├── state/               # 런타임 캐시 · 로그 (gitignore)
├── .env.example
├── requirements.txt
└── pyproject.toml
`

---

## 개발 배경

소셜미디어에서 접하는 주장들이 실제 뉴스와 얼마나 일치하는지 매번 직접 확인하기 번거로워 만든 개인 자동화 도구입니다.

핵심 고민은 **"AI가 사실 판정을 내리면 또 다른 허위정보가 될 수 있다"** 는 점이었습니다. 그래서 판정을 내리는 대신, 신뢰할 수 있는 뉴스 소스의 내용을 근거로 가져와 연결해주는 역할만 하도록 설계했습니다. LLM 프롬프트와 코드 양쪽에서 이 원칙을 강제합니다.

또한 Claude MCP(Model Context Protocol)를 처음 적용해보면서, **"자동 파이프라인"과 "대화형 도구"를 같은 코드 베이스에서 제공하는 구조**를 실험한 프로젝트이기도 합니다.

---

## License

MIT