jonghhhh commited on
Commit
09fac6b
·
verified ·
1 Parent(s): 151ad85

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +196 -0
  2. readme.md +24 -0
  3. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from bs4 import BeautifulSoup
4
+ import google.generativeai as genai
5
+
6
+ # Page 설정
7
+ st.set_page_config(page_title="한겨레 AI 논설위원", page_icon="✍️", layout="wide")
8
+
9
+ # 한겨레 사설 실시간 수집 함수
10
+ def get_hankyoreh_editorials(keyword):
11
+ url = "https://search.naver.com/search.naver"
12
+ query = f"사설 {keyword}"
13
+ params = {
14
+ "where": "news",
15
+ "ssc": "tab.news.all",
16
+ "query": query,
17
+ "mynews": "1", # 언론사 선택 활성화
18
+ "news_office_checked": "1028", # 한겨레 언론사 ID
19
+ "office_type": "1",
20
+ "office_section_code": "1",
21
+ }
22
+ headers = {
23
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
24
+ "Referer": "https://search.naver.com"
25
+ }
26
+
27
+ try:
28
+ response = requests.get(url, params=params, headers=headers, timeout=10)
29
+ response.raise_for_status()
30
+ except Exception:
31
+ return []
32
+
33
+ soup = BeautifulSoup(response.text, "html.parser")
34
+ news_items = soup.select(".fds-news-item-list-tab > div")
35
+ if not news_items:
36
+ news_items = soup.select("ul.list_news > li")
37
+
38
+ results = []
39
+ for item in news_items:
40
+ # 제목 수집
41
+ title_el = item.find('a', attrs={'data-heatmap-target': '.tit'})
42
+ if not title_el:
43
+ title_el = item.select_one(".news_tit")
44
+ if not title_el:
45
+ continue
46
+ title = title_el.get_text(strip=True)
47
+
48
+ # 본문 요약 수집
49
+ body_el = item.find('a', attrs={'data-heatmap-target': '.body'})
50
+ if not body_el:
51
+ body_el = item.select_one(".news_dsc")
52
+ body = body_el.get_text(strip=True) if body_el else ""
53
+
54
+ # 네이버 뉴스 URL 수집
55
+ nav_link_el = item.find('a', attrs={'data-heatmap-target': '.nav'})
56
+ if nav_link_el and nav_link_el.has_attr('href'):
57
+ naver_url = nav_link_el['href']
58
+ else:
59
+ naver_url = title_el['href'] if title_el.has_attr('href') else ""
60
+
61
+ # 날짜 정보 필터링 수집
62
+ subtext_elements = item.select(".sds-comps-profile-info-subtext")
63
+ date = ""
64
+ for sub_el in subtext_elements:
65
+ text = sub_el.get_text(strip=True)
66
+ if "네이버뉴스" in text:
67
+ continue
68
+ if any(ind in text for ind in ["초", "분", "시간", "일", "주", "달", "년", "어제"]) or "." in text:
69
+ if "면" not in text and "단" not in text:
70
+ date = text
71
+ break
72
+ if not date:
73
+ info_el = item.select_one(".info")
74
+ if info_el:
75
+ date = info_el.get_text(strip=True)
76
+
77
+ results.append({
78
+ "title": title,
79
+ "date": date,
80
+ "body": body,
81
+ "naver_url": naver_url
82
+ })
83
+ if len(results) == 10:
84
+ break
85
+
86
+ return results
87
+
88
+ # 세션 상태 변수 초기화
89
+ if "editorials" not in st.session_state:
90
+ st.session_state.editorials = []
91
+ if "messages" not in st.session_state:
92
+ st.session_state.messages = []
93
+
94
+ # UI 레이아웃 구성
95
+ st.title("✍️ 한겨레 AI 논설위원")
96
+ st.caption("실시간 수집된 한겨레 사설의 진보적이고 정교한 시각을 대화에 반영합니다.")
97
+
98
+ # 사이드바 설정 영역
99
+ with st.sidebar:
100
+ st.header("🔑 API 설정 및 공지")
101
+ st.markdown(
102
+ """
103
+ 이 서비스는 **Google Gemini API**를 사용합니다. 개인 API Key를 입력하여 안전하게 대화해보세요.
104
+
105
+ 👉 [Google AI Studio에서 무료 API Key 발급받기](https://aistudio.google.com/)
106
+ """
107
+ )
108
+ api_key_input = st.text_input("Gemini API Key 입력", type="password", help="발급받으신 API Key를 입력해 주세요.")
109
+
110
+ st.divider()
111
+ st.header("🔍 사설 기반 지식 탐색")
112
+ keyword = st.text_input("검색어 입력 (예: 인공지능, 반도체)", value="인공지능")
113
+ search_button = st.button("사설 수집 및 AI 반영")
114
+
115
+ # 사설 검색 및 지식 갱신 동작
116
+ if search_button:
117
+ if not keyword.strip():
118
+ st.warning("검색어를 입력해 주세요.")
119
+ else:
120
+ with st.spinner(f"네이버에서 '{keyword}' 관련 한겨레 사설을 수집 중입니다..."):
121
+ editorials = get_hankyoreh_editorials(keyword)
122
+ if editorials:
123
+ st.session_state.editorials = editorials
124
+ st.session_state.messages = [] # 새로운 지식을 위해 이전 대화 리셋
125
+ st.success(f"'{keyword}' 관련 사설 {len(editorials)}개를 학습해 논조를 재조정했습니다!")
126
+ else:
127
+ st.error("사설을 수집하지 못했습니다. 검색어를 변경하거나 다시 시도해 주세요.")
128
+
129
+ # 수집된 사설 ��스 표시
130
+ if st.session_state.editorials:
131
+ with st.expander("📚 참조 중인 사설 데이터 원본 보기", expanded=False):
132
+ for idx, ed in enumerate(st.session_state.editorials, 1):
133
+ st.markdown(f"**{idx}. [{ed['title']}]({ed['naver_url']})** \n"
134
+ f"*날짜: {ed['date']}* \n"
135
+ f"> {ed['body']}")
136
+
137
+ # API Key 입력 검증 및 모델 인스턴스 생성
138
+ if not api_key_input:
139
+ st.info("👈 대화를 시작하려면 왼쪽 사이드바에 Gemini API Key를 입력해 주세요.")
140
+ else:
141
+ # 3. AI 논설위원의 시스템 프롬프트 및 수집된 컨텍스트 결합
142
+ system_instruction = (
143
+ "당신은 대한민국 대표 일간지 '한겨레'의 'AI 논설위원'입니다. "
144
+ "정의, 인권, 복지, 민주주의, 평화, 상생, 기득권 견제, 소수자 보호, 균형 잡힌 경제 성장 등의 한겨레 고유 논조를 깊이 내면화하고 있습니다.\n\n"
145
+ "반드시 다음 지침을 준수하십시오:\n"
146
+ "1. 제공되는 사설 정보([한겨레 사설 지식 정보])의 내용을 대화 내용에 적극 활용하고, 그 주장의 핵심적인 관점을 근거로 드십시오.\n"
147
+ "2. 논리적이고 친절하되, 엄정하고 품격 있는 논설위원 특유의 서술 서체(예: '~해야 할 것이다', '~가 필요한 시점이다', '~라고 보지 않을 수 없다')를 정교하게 활용해 주십시오.\n"
148
+ "3. 만약 제공된 사설 외에 일반 상식에 관해 질문하더라도 진보 언론의 관점에서 일관성 있게 논리를 펴 나가십시오.\n"
149
+ )
150
+
151
+ if st.session_state.editorials:
152
+ context_str = "\n\n[한겨레 사설 지식 정보]\n"
153
+ for idx, ed in enumerate(st.session_state.editorials, 1):
154
+ context_str += f"- 사설 {idx}: {ed['title']} (날짜: {ed['date']})\n내용: {ed['body']}\n"
155
+ system_instruction += context_str
156
+
157
+ try:
158
+ # API 설정 및 모델 선언
159
+ genai.configure(api_key=api_key_input)
160
+ model = genai.GenerativeModel(
161
+ model_name="gemini-2.5-flash-lite", # 지목된 gemini-2.5-flash-lite 사용
162
+ system_instruction=system_instruction
163
+ )
164
+ except Exception as e:
165
+ st.error(f"AI 모델 초기화 실패: {e}")
166
+ st.stop()
167
+
168
+ # 채팅 기록 출력
169
+ for message in st.session_state.messages:
170
+ with st.chat_message(message["role"]):
171
+ st.markdown(message["content"])
172
+
173
+ # 사용자 채팅 입력 대기 및 멀티턴 전송
174
+ if prompt := st.chat_input("AI 논설위원에게 질문이나 의견을 입력하세요."):
175
+ with st.chat_message("user"):
176
+ st.markdown(prompt)
177
+ st.session_state.messages.append({"role": "user", "content": prompt})
178
+
179
+ # Gemini 멀티턴 형식(contents 구조)에 맞춰 세션 기록 전달
180
+ contents = []
181
+ for msg in st.session_state.messages:
182
+ contents.append({
183
+ "role": "user" if msg["role"] == "user" else "model",
184
+ "parts": [msg["content"]]
185
+ })
186
+
187
+ with st.chat_message("assistant"):
188
+ message_placeholder = st.empty()
189
+ with st.spinner("AI 논설위원이 의견을 정리 중입니다..."):
190
+ try:
191
+ response = model.generate_content(contents)
192
+ full_response = response.text
193
+ message_placeholder.markdown(full_response)
194
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
195
+ except Exception as e:
196
+ st.error(f"응답 생성 중 오류가 발생했습니다: {e}")
readme.md ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 한겨레 AI 논설위원
3
+ emoji: ✍️
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: streamlit
7
+ sdk_version: 1.35.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # 한겨레 AI 논설위원
13
+
14
+ 실시간 네이버 뉴스 검색을 바탕으로 한겨레의 최근 사설 지식을 습득하고, 그 논조를 반영하여 사용자와 진보적 시각의 대화를 나눌 수 있는 인공지능 논설위원 챗봇입니다.
15
+
16
+ ## 주요 기능
17
+ - **실시간 사설 수집**: 검색어를 입력하면 한겨레 신문사가 발행한 최근 사설 10개를 실시간 수집하여 학습에 활용합니다.
18
+ - **Gemini 2.5 Flash Lite**: 빠르고 경제적인 추론 모델을 사용해 원활한 멀티턴 대화가 가능합니다.
19
+ - **논설위원 페르소나**: 일관성 있게 논리적이고 격조 있는 사설 서술체를 구사합니다.
20
+
21
+ ## 사용법
22
+ 1. 사이드바에 본인의 **Gemini API Key**를 입력합니다.
23
+ 2. 분석하고 싶은 사회 이슈 검색어(예: *반도체*, *인공지능*)를 입력하고 **[사설 수집 및 AI 반영]** 버튼을 누릅니다.
24
+ 3. 수집된 사설이 표시되면, 하단 채팅 입력창을 통해 멀티턴 대화를 시작할 수 있습니다.
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit
2
+ requests
3
+ beautifulsoup4
4
+ google-generativeai