magictreee commited on
Commit
80409d6
·
verified ·
1 Parent(s): 349d8e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -129
app.py CHANGED
@@ -20,8 +20,8 @@ logging.basicConfig(level=logging.INFO)
20
  # -------------------------------
21
  # 상수 정의 (향후 조정 및 유지보수 용이하도록)
22
  # -------------------------------
23
- TARGET_CHAR_LENGTH = 3500 # 정보성 블로그 글자수
24
- MIN_SECTION_LENGTH = 500 # 각 소제목 아래 최소 글자수
25
  MAX_TOKENS = 15000 # Gemini API 최대 토큰 수
26
  TEMPERATURE = 0.85 # Gemini API 온도 값
27
  TOP_P = 0.9 # Gemini API top_p 값
@@ -86,112 +86,109 @@ def fetch_crawl_results(query):
86
  def get_style_prompt(style="친근한"):
87
  prompts = {
88
  "친근한": """
89
- [친근한 정보성 블로그 스타일 가이드]
90
  1. 톤과 어조
91
  - 대화하듯 편안하고 친근한 말투 사용
92
- - 주제에 대한 관심을 담은 표현 사용
93
  2. 문장 및 어투
94
  - 반드시 '해요체'로 작성, 절대 '습니다'체를 사용하지 말 것
95
  - '~요'로 끝나도록 작성, '~다'로 끝나지 않게 하라
96
  - 구어체 표현 사용 (예: "~했어요", "~인 것 같아요")
97
  - 이모티콘은 사용하지 마세요
98
  3. 용어 및 설명 방식
99
- - 전문 용어 쉬운 단어로 풀어서 설명
100
  - 비유나 은유를 활용하여 복잡한 개념 설명
101
- - 수사의문문 활용하여 독자와 소통하는 느낌 주기 (예: "여러분도 이런 생각 해보셨나요?")
102
- - 구체적례와시를 개념 설명
103
- 4. 정보 전달 방식
104
- - 개인적인 관점에 녹여 자연스럽게 정보 전달
105
- - 복잡개념을 단계적으로 설명
106
- - 독자가 실제로 활용할 는 실용적 정보 제공
107
  5. 독자와의 상호작용
108
- - 독자의 의견을 물어보는 질문 포함
109
- - 실생활에 적용할 수 있는 팁이나 조언 제공
110
- 주의사항: 너무 가벼운 톤은 지양고, 주제의 전성과 신뢰성을 해치지 않는 선에서 친근함 유지
 
111
  """,
112
  "일반적인": """
113
- #일반적인 정보성 블로그 스타일 가이드
114
  1. 톤과 어조
115
  - 중립적이고 객관적인 톤 유지
116
  - 적절한 존댓말 사용 (예: "~합니다", "~입니다")
117
- - 정보 전달 중심명확한 어투
118
  2. 내용 구조 및 전개
119
- - 명확한 주제 소개로 시작
120
- - 논리적인 순서로 정보 전개 (배경 → 주요 개념분석적용 등)
121
- - 핵심 포인트를 강조하는 소제목 활용
122
  - 적절한 길이의 단락으로 구성
123
  3. 용어 및 설명 방식
124
  - 일반적으로 이해하기 쉬운 용어 선택
125
- - 필요시 전문 용어에 간단한 설명 추가
126
  - 객관적인 정보 제공에 중점
127
- - 균형 잡힌 시각에서 다양관점 제시
128
- 4. 정보 전달 방식
129
- - 주제의 기본 개념과 원리 명확하게 제공
130
- - 구체적인 사례 포함
131
- - 시각적 요소(그래프, 표 등) 방식
132
- - 최신 연구 동향
133
  5. 독자 상호작용
134
  - 적절히 독자의 생각을 묻는 질문 포함
135
  - 추가 정보를 찾을 수 있는 키워드 제시
136
- - 실용적인 적용 방안 제공
137
  6. 마무리
138
  - 주요 내용 간단히 요약
139
- - 주제중요성이나 의의 강조
140
- - 향후 전망이나 추가 고려사항
141
- 주의사항: 너무 딱딱하거나 지루하지 않도록 다양한 예시와 객관적 정보 사이의 균형 유지
142
  """,
143
  "전문적인": """
144
- #전문적인 정보성 블로그 스타일 가이드
145
  1. 톤과 구조
146
  - 공식적이고 전문적인 톤 사용
147
  - 객관적이고 분석적인 접근 유지
148
- - 명확한 서론(개요), 본론(상세 분석), 결론(종합 평가) 구조
149
  - 체계적인 정보 전개
150
  - 세부 섹션을 위한 명확한 소제목 사용
151
  2. 내용 구성 및 전개
152
- - 주제의 역사적 배경, 이론기반, 현재 동향 등 심층적 정보 포함
153
  - 논리적 연결을 위한 전환어 활용
154
- - 전문 용어 적절히 활용 (필요시 간략한 설명 제공)
155
  - 심층적인 분석과 비판적 평가 제공
156
- - 다양한 관점 이론적 프레임워크 제시
157
  3. 데이터 및 근거 활용
158
- - 통계, 연구 결과, 사 연구 등 객관적 데이터 활용
159
- - 주제 분석을 위한 체계적인 프레임워크 제시
160
- - 수치 데이터는 명확히 설명 (추세, 상관관계, 인과관계 등)
161
- - 학술근거와 현실 적용의 균형
162
- 4. 전문정보 제공
163
- - 최신 연구 동향 발전 방향 분석
164
- - 이론과 실제 적용 사이의 간극 분석
165
- - 주제 관련 쟁점과 논쟁점 소개
166
- - 체계적인 문제 해결 접근법 제시
167
  5. 마무리
168
  - 핵심 정보 요약 및 종합 평가
169
- - 주제학문적, 실용적 의의 분석
170
- - 향후 연구 방향이나 발전 가능성
171
- 주의사항: 전문성을 유지하되, 불필요 어려운 용어는 지양하고 명확한 설명 중심으로 구성
172
  """
173
  }
174
  return prompts.get(style, prompts["친근한"])
175
 
176
- def get_informational_blog_prompt():
177
  prompts = [
178
  """
179
- [중요: 정보성 블로그 글 작성 필수 규칙]
180
  이 규칙을 반드시 따르세요. 어떤 상황에서도 예외는 없습니다:
181
  1. 마크다운 문법(**, *, #, -, 1., 2., 3.)을 절대 사용하지 마세요.
182
  2. 모든 소제목은 번호 없이 일반 문장으로 작성하세요.
183
  3. 모든 목록은 불릿이나 번호 없이 자연스러운 문장으로 서술하세요.
184
  4. "참고글", "참고글에 따르면" 등의 표현을 절대 사용하지 마세요.
185
- 5. 주제에 관한 정확하고 신뢰할 수 있는 정보만 작성하세요.
186
- 6. 글의 주제는 반드시 주어진 참고글 내용을 바탕으로 작성하세요.
187
- 7. 글의 구성은 다음을 포함해야 합니다:
188
- - 주 대한 명확한 소개와 배경
189
- - 역사적/맥락
190
- - 현동향 최신 정보
191
- - 개념이나 원리
192
- - 실용적인 적용 방법이나 사례
193
- - 다양한 관점이나 비교 분석
194
- - 향후 전망이나 결론
195
  """
196
  ]
197
  return random.choice(prompts)
@@ -200,10 +197,10 @@ def remove_unwanted_phrases(text):
200
  unwanted_phrases = [
201
  '여러분', '최근', '마지막으로', '결론적으로', '결국',
202
  '종합적으로', '따라서', '마무리', '끝으로', '요약',
203
- '한 줄 요약', '정리하자면', '총정리', '을 마치며',
204
- '이상으로', '추천드립니다', '참고하세요', '도움이 되셨길',
205
- '좋은 하루 되세요', '다음 에서', '도움이 되었길',
206
- '즐거운 하루 되세요', '감사합니다'
207
  ]
208
  words = re.findall(r'\S+|\n', text)
209
  result_words = [word for word in words if not any(phrase in word for phrase in unwanted_phrases)]
@@ -227,32 +224,43 @@ def post_process_blog(blog_content, style="친근한"):
227
  blog_content = re.sub(r'됩니다', '돼요', blog_content)
228
  blog_content = re.sub(r'입니다', '이에요', blog_content)
229
 
230
- # 과장된 표현 정리
231
  exaggerated_expressions = [
232
- (r'필수적인', r'중요한'),
233
- (r'혁명적인', r'중요한'),
234
- (r'놀라운', r'주목할 만한'),
235
- (r'기적의', r'효과적인'),
236
- (r'최고의', r'좋은'),
237
- (r'세계적인', r'유명한'),
238
- (r'완벽한', r'우수한'),
239
- (r'극적인', r'상당한'),
240
- (r'무한한', r'많은'),
241
- (r'절대적인', r'상당한'),
242
- (r'혁신적인', r'새로운'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  (r'환상적인', r'좋은'),
244
- (r'근본적인', r'기본적인'),
245
- (r'획기적인', r'중요한'),
246
- (r'전례없는', r'특별한'),
247
- (r'압도적인', r'주목할 만한'),
248
- (r'황홀한', r'좋은'),
249
- (r'천상의', r'우수한'),
250
- (r'기가 막힌', r'효과적인'),
251
- (r'끝판왕', r'최상위'),
252
  (r'그 자체', r''),
 
253
  (r'이 .{1,10} 그 자체였어요', r'이 \1였어요'),
254
  (r'가 .{1,10} 그 자체였어요', r'가 \1였어요'),
255
- (r'압도적인', r'중요한'),
 
 
256
  (r'천국', r'좋은 곳'),
257
  (r'황홀했어요', r'좋았어요'),
258
  (r'환상의', r'좋은')
@@ -261,12 +269,12 @@ def post_process_blog(blog_content, style="친근한"):
261
  for pattern, replacement in exaggerated_expressions:
262
  blog_content = re.sub(pattern, replacement, blog_content, flags=re.IGNORECASE)
263
 
264
- blog_content = re.sub(r'참고글에 따르면', r'알려진 바로는', blog_content)
265
  blog_content = re.sub(r'참고글', r'관련 정보', blog_content)
266
 
267
  # Gemini API를 활용한 추가 후처리 프롬프트 (텍스트 보완 목적)
268
  prompt = f"""
269
- 다음 정보성 블로그 글을 더 자연스러운 형태로 변경해주세요:
270
 
271
  원본 글:
272
  {blog_content}
@@ -275,7 +283,7 @@ def post_process_blog(blog_content, style="친근한"):
275
  1. 마크다운 형식 및 번호 목록, 불릿 표현 제거
276
  2. 소제목은 5개 이하로, 각 소제목 아래 내용은 최소 {MIN_SECTION_LENGTH}자 이상으로 상세하게 서술
277
  3. "참고글" 관련 표현 제거
278
- 4. 구체적인 예시와 실용적인 정보 포함
279
  5. 스타일: {style} (자연스러운 문체 사용)
280
  """
281
 
@@ -347,7 +355,7 @@ def format_blog_post(blog_post, query=""):
347
  if title.endswith(f" {ending}"):
348
  title = title[:-len(ending)-1]
349
  if len(title) < 20 and query:
350
- title = f"{query} 정보 가이드"
351
  return title
352
 
353
  for i, line in enumerate(lines):
@@ -389,7 +397,7 @@ def format_blog_post(blog_post, query=""):
389
  if in_paragraph:
390
  formatted_lines.append("</p>")
391
  if not title_found:
392
- default_title = f"{query} 정보 가이드" if query else "정보성 블로그"
393
  if first_non_empty_line:
394
  default_title = optimize_title(first_non_empty_line)
395
  formatted_lines.insert(0, f'<h1 style="font-size: 1.8em; margin-bottom: 20px; font-weight: bold; color: #222;">{html.escape(default_title)}</h1>')
@@ -413,7 +421,7 @@ def generate_blog_post(query, style="친근한"):
413
  # 참고글 수집
414
  ref1, ref2, ref3 = fetch_crawl_results(query)
415
  style_prompt = get_style_prompt(style)
416
- format_prompt = get_informational_blog_prompt()
417
 
418
  # 스타일 세부 지시사항 (원본 내용의 특성을 유지)
419
  style_specific_instructions = ""
@@ -424,9 +432,9 @@ def generate_blog_post(query, style="친근한"):
424
  - 격식체(예: "~합니다", "~입니다") 사용 금지
425
  - 대화하듯 편안하게 작성
426
  - 짧고 간결한 문장 사용
427
- - 복잡개념은 일상적인 비유로 설명
428
- - 실제 사례와 예시를 통설명
429
- - 수사문문을 활용한 독자와의 소통
430
  """
431
  elif style == "일반적인":
432
  style_specific_instructions = """
@@ -434,22 +442,20 @@ def generate_blog_post(query, style="친근한"):
434
  - '습니다체' 사용: "~했습니다", "~입니다", "~하였습니다"
435
  - 격식적이지 않은 명확한 표현 사용
436
  - 간결하고 명료한 문장 사용
437
- - 객관적인 사실과 분석 중심으로 서술
438
- - 균형 잡힌 시각에서 다양한 관점 제시
439
  """
440
  elif style == "전문적인":
441
  style_specific_instructions = """
442
  이 블로그는 반드시 전문적이고 분석적인 어투로 작성해야 합니다.
443
  - '습니다체' 사용: "~했습니다", "~입니다", "~하였습니다"
444
- - 역사, 이론, 연구 결과 등 심층 정보 포함
445
  - 객관적이고 논리적인 분석 중심 작성
446
  - 구체적인 데이터 및 수치 포함하여 설명
447
- - 다양한 관점과 이론적 프레임워크 제시
448
  """
449
 
450
  # Phase 1: 초기 생성
451
  initial_prompt = f"""
452
- 주제: {query} 정보성 블로그
453
  참고글 1: {ref1}
454
  참고글 2: {ref2}
455
  참고글 3: {ref3}
@@ -460,25 +466,15 @@ def generate_blog_post(query, style="친근한"):
460
  스타일 세부 지시사항:
461
  {style_specific_instructions}
462
  특별 지시사항:
463
- 1. 반드 글의 처음에 매력적이고 명확한 제목 포함 (주제와 핵심 포인트를 담은 제목).
464
  2. 마크다운 문법(#, *, -, 1., 2., 등) 사용 금지.
465
  3. 소제목은 번호 없이 작성하고, 5개 이하로 제한.
466
  4. 모든 목록은 불릿이나 번호 없이 자연스럽게 서술.
467
  5. "참고글" 관련 표현 사용 금지.
468
- 6. 주제에 관한 정확하고 신뢰할 수 있는 정보만 작성.
469
- 7. 구체적인 정보(역사적 배경, 주요 개념, 이론, 실용적 적용, 최신 동향 등) 자연스럽게 포함.
470
  8. 글자수가 최소 {TARGET_CHAR_LENGTH}자 이상이어야 함.
471
- 9. 각 소제목 아래 내용은 최소 {MIN_SECTION_LENGTH}자 이상 서술.
472
- 10. 반드시 다음 구성 요소를 포함할 것:
473
- - 주제에 대한 명확한 소개와 배경
474
- - 주제의 역사적/이론적 맥락
475
- - 현재 동향이나 최신 정보
476
- - 주요 개념이나 원리에 대한 설명
477
- - 실용적인 적용 방법이나 사례
478
- - 다양한 관점이나 비교 분석
479
- - 향후 전망이나 결론
480
  """
481
-
482
  first_attempt = call_gemini_api(initial_prompt)
483
  first_attempt_cleaned = remove_unwanted_phrases(first_attempt)
484
  first_attempt_length = len(first_attempt_cleaned)
@@ -502,21 +498,20 @@ def generate_blog_post(query, style="친근한"):
502
  이전에 작성된 글은 목표 글자수인 {TARGET_CHAR_LENGTH}자에 미치지 못합니다. 현재 글자수는 약 {first_attempt_length}자입니다.
503
  또한, 각 소제목 아래 내용이 너무 짧고 부실합니다.
504
  중요 요구사항:
505
- 1. 글의 처음에 매력적인 제목(주제와 핵심 포인트 포함) 추가.
506
  2. 글자수를 최소 {TARGET_CHAR_LENGTH}자 이상으로 늘리고, 각 섹션을 상세히 서술.
507
  3. 마크다운 형식(#, *, -, 1., 2., 등) 사용 금지.
508
  4. 소제목은 번호 없이 작성.
509
  5. 모든 목록은 불릿이나 번호 없이 서술.
510
  6. "참고글" 관련 표현 사용 금지.
511
- 7. 주제에 관한 정확하고 신뢰할 수 있는 정보작성.
512
  8. 각 소제목 아래 내용을 최소 {MIN_SECTION_LENGTH}자 이상 서술.
513
  9. 소제목 수는 5개 이하로 제한.
514
  상세 보완:
515
- - 주제의 배경맥락에 대한 깊은 설명 추가.
516
- - 구체적인 사례, 연구 결과, 통계실증 추가.
517
- - 역사적 발전 과정, 주요 이론프레임워크심층 정보 추가.
518
- - 실용적인 적용 방법, , 권장사항 등 실 가능한 정보 추가.
519
- - 다양한 관점, 쟁점, 논쟁점 등 균형 잡힌 시각 제공.
520
  """
521
  revised_attempt = call_gemini_api(revision_prompt)
522
  revised_cleaned = remove_unwanted_phrases(revised_attempt)
@@ -529,7 +524,7 @@ def generate_blog_post(query, style="친근한"):
529
  # Phase 3: 확장 (Expansion) 시도 (글자수가 부족할 경우)
530
  if actual_char_length < TARGET_CHAR_LENGTH * 0.8:
531
  expansion_prompt = f"""
532
- 다음 정보성 블로그 글의 내용을 크게 확장해주세요:
533
  원본 글:
534
  {final_post}
535
  문제점:
@@ -540,17 +535,17 @@ def generate_blog_post(query, style="친근한"):
540
  {style_specific_instructions}
541
  요구사항:
542
  1. 각 소제목 아래의 내용을 최소 {MIN_SECTION_LENGTH}자 이상 대폭 확장.
543
- 2. 구체적인 사례, 연구 결과, 통계, 예시 추가.
544
  3. 마크다운 형식(#, *, -, 1., 2., 등) 사용 금지.
545
  4. {style} 스타일에 맞춰 일관되게 작성.
546
  5. 소제목 구조는 유지하되, 각 섹션 내용을 3배 이상 풍부하게 확장.
547
- 6. 주제역사적 발전, 이론적 배경, 주요 개념 등을 상세히 서술.
548
  7. 전체 글자수를 최소 {TARGET_CHAR_LENGTH}자 이상 달성.
549
  8. 소제목 수는 최대 5개로 제한.
550
- 9. 실용적 정보(방법, , 주의사항 등) 추가.
551
- 10. 다양한 관점과 각에서의 분석 추가.
552
- 11. 주제와련된 최신 동향이발전 방향 추가.
553
- 12. 결론 부분에서 주제의 의의와 중요성 강조.
554
  """
555
  expanded_attempt = call_gemini_api(expansion_prompt)
556
  final_post = post_process_blog(expanded_attempt, style)
@@ -677,10 +672,10 @@ def save_content_to_pdf(blog_post, user_topic):
677
  # Gradio 인터페이스 구성 (기존 함수 이름, 인터페이스 유지)
678
  # -------------------------------
679
  with gr.Blocks() as iface:
680
- gr.Markdown("# 정보성 블로그 생성기")
681
- gr.Markdown("주제 키워드를 입력해 주세요.")
682
 
683
- query_input = gr.Textbox(lines=1, placeholder="예: 블록체인 기술, 지속가능한 농업, 인공지능 윤리, 심리학 이론, 경제학 개념 ", label="키워드")
684
  style_input = gr.Radio(["친근한", "일반적인", "전문적인"], label="포스팅 스타일", value="친근한")
685
 
686
  generate_button = gr.Button("블로그 글 생성")
@@ -707,4 +702,4 @@ with gr.Blocks() as iface:
707
  )
708
 
709
  if __name__ == "__main__":
710
- iface.launch()
 
20
  # -------------------------------
21
  # 상수 정의 (향후 조정 및 유지보수 용이하도록)
22
  # -------------------------------
23
+ TARGET_CHAR_LENGTH = 4000 # 최 글자수 목표
24
+ MIN_SECTION_LENGTH = 600 # 각 소제목 아래 최소 글자수
25
  MAX_TOKENS = 15000 # Gemini API 최대 토큰 수
26
  TEMPERATURE = 0.85 # Gemini API 온도 값
27
  TOP_P = 0.9 # Gemini API top_p 값
 
86
  def get_style_prompt(style="친근한"):
87
  prompts = {
88
  "친근한": """
89
+ [친근한 여행 블로그 스타일 가이드]
90
  1. 톤과 어조
91
  - 대화하듯 편안하고 친근한 말투 사용
92
+ - 여행에 대한 설렘과 기대감을 담은 표현 사용
93
  2. 문장 및 어투
94
  - 반드시 '해요체'로 작성, 절대 '습니다'체를 사용하지 말 것
95
  - '~요'로 끝나도록 작성, '~다'로 끝나지 않게 하라
96
  - 구어체 표현 사용 (예: "~했어요", "~인 것 같아요")
97
  - 이모티콘은 사용하지 마세요
98
  3. 용어 및 설명 방식
99
+ - 전문 용어 대신 쉬운 단어로 풀어서 설명
100
  - 비유나 은유를 활용하여 복잡한 개념 설명
101
+ - 수사의문문 활용하여 독자와 소통하는 느낌 주기 (예: "여러분도 이런 경험 있으시죠?")
102
+ - 오감을 활용한 생생한 묘(: "바다에서 느껴지는 짭조름향기", "귓가를 간지럽히는 파도 소리")
103
+ 4. 여행 정보 전달 방식
104
+ - 개인 경험에 녹여 자연스럽게 정보 전달 (예: "주차장이 생각보다 좁아서 오전에 도착하는 게 좋더라구요")
105
+ - 구체적인 에피소드를 통생생한 정보 전달 (예: "점심 피크타임에 도착했더니 1시간 넘게 기다렸어요")
106
+ - 솔직한 장단점 평가 (예: "경치정말 예뻤지만, 화장 아쉬웠어요")
107
  5. 독자와의 상호작용
108
+ - 독자의 의견을 물어보는 질문 포함 (예: "여러분은 어떤 코스를 선호하시나요?")
109
+ - 여행 팁이나 조언 제공 (예: "아침 일찍 방문하시면 한적하게 즐길 수 있어요")
110
+ - 댓글 달기를 독려 사용
111
+ 주의사항: 너무 가벼운 톤은 지양하고, 여행지의 특성과 매력을 해치지 않는 선에서 친근함 유지
112
  """,
113
  "일반적인": """
114
+ #일반적인 여행 블로그 팅 스타일 가이드
115
  1. 톤과 어조
116
  - 중립적이고 객관적인 톤 유지
117
  - 적절한 존댓말 사용 (예: "~합니다", "~입니다")
118
+ - 정보 전달 개인 경험균형 유지
119
  2. 내용 구조 및 전개
120
+ - 명확한 여행지 소개로 시작
121
+ - 논리적인 순서로 정보 전개 (위치교통 → 주요 볼거리음식숙소 등)
122
+ - 주요 포인트를 강조하는 소제목 활용
123
  - 적절한 길이의 단락으로 구성
124
  3. 용어 및 설명 방식
125
  - 일반적으로 이해하기 쉬운 용어 선택
126
+ - 필요시 여행 관련 용어에 간단한 설명 추가
127
  - 객관적인 정보 제공에 중점
128
+ - 개인 경험을 바탕으로실용적인 조언 포함
129
+ 4. 여행 정보 전달 방식
130
+ - 위치, 교통편, 운영시간, 입장료 등 핵심 정보를 명확하게 제공
131
+ - 계절별,간대별 특징 소개
132
+ - 주변 편의설이나 부대시안내
133
+ - 다양한 여행자 유형(가족, 인, 친, 혼자)을려한 정보 제공
134
  5. 독자 상호작용
135
  - 적절히 독자의 생각을 묻는 질문 포함
136
  - 추가 정보를 찾을 수 있는 키워드 제시
137
+ - 여행 계획에 도움이 되는 실용적인 제공
138
  6. 마무리
139
  - 주요 내용 간단히 요약
140
+ - 해당 여행지특별한 매력 강조
141
+ - 방문 참고할 만한 추가 조언
142
+ 주의사항: 너무 딱딱하거나 지루하지 않도록 개인 경험과 객관적 정보 사이의 균형 유지
143
  """,
144
  "전문적인": """
145
+ #전문적인 여행 블로그 팅 스타일 가이드
146
  1. 톤과 구조
147
  - 공식적이고 전문적인 톤 사용
148
  - 객관적이고 분석적인 접근 유지
149
+ - 명확한 서론(여행지 개요), 본론(상세 분석), 결론(종합 평가) 구조
150
  - 체계적인 정보 전개
151
  - 세부 섹션을 위한 명확한 소제목 사용
152
  2. 내용 구성 및 전개
153
+ - 여행지의 역사, 문화적 배경, 지리특성 등 심층적 정보 포함
154
  - 논리적 연결을 위한 전환어 활용
155
+ - 여행 관련 전문 용어 적절히 활용 (필요시 간략한 설명 제공)
156
  - 심층적인 분석과 비판적 평가 제공
157
+ - 다양한 관점 제시 및 비교 (예: 성수기 vs 비수기, 평일 vs 주말)
158
  3. 데이터 및 근거 활용
159
+ - 방문객 통계, 여행 트렌드, 중요성 등 객관적 데이터 활용
160
+ - 여행지 평가를 위한 체계적인 기준 제시 (접근성, 시설, 가성비, 볼거리 등)
161
+ - 수치 데이터는 명확히 설명 (예: 이동 거리, 소요 시간, 비용 등)
162
+ 4. 전문여행 정보 제공
163
+ - 방문 시기, 계절별 특징 상세 분석
164
+ - 교통편 옵션 비교 분석 (소요 시간, 비용, 편의성 등)
165
+ - 숙박 옵션 상세 분석 (위치, 가격대, 시설, 서비스 등)
166
+ - 여행 계획 수립을 위한 체계적인 정보 제공
 
167
  5. 마무리
168
  - 핵심 정보 요약 및 종합 평가
169
+ - 여행지차별화된 가치 분석
170
+ - 여행자 유형별 추천
171
+ 주의사항: 전문성을 유지하되, 완전히 이해 어려운 수준은 지양하고 실용적인 정보 중심으로 구성
172
  """
173
  }
174
  return prompts.get(style, prompts["친근한"])
175
 
176
+ def get_travel_blog_prompt():
177
  prompts = [
178
  """
179
+ [중요: 여행 블로그 글 작성 필수 규칙]
180
  이 규칙을 반드시 따르세요. 어떤 상황에서도 예외는 없습니다:
181
  1. 마크다운 문법(**, *, #, -, 1., 2., 3.)을 절대 사용하지 마세요.
182
  2. 모든 소제목은 번호 없이 일반 문장으로 작성하세요.
183
  3. 모든 목록은 불릿이나 번호 없이 자연스러운 문장으로 서술하세요.
184
  4. "참고글", "참고글에 따르면" 등의 표현을 절대 사용하지 마세요.
185
+ 5. 블로그 작성자가 직접 경험하고 느낀 내용만 작성하세요.
186
+ 6. 글의 주제는 반드시 주어진 참고글에서 1개 여행지를 선정하여 작성하세요.
187
+ 예시:
188
+ 주도 서쪽 해안 드라이브 코스, 협재해변에서 차귀도까지
189
+ 오랜만에 휴가를 내서 주��로 여행을 다녀왔어요. 번에는 특별히 서쪽 해안가를 따라 드라이브 코스를 계획했답니다. 협재해변부터 시작해서 한림공원, 금능해수욕장을 거쳐 차귀도까지 이어지는 해안 길은 정말 환상이었어요. 푸른 바다와 검은 화산암이 만들어내는 풍경이 어찌나 아름답던지, 지금도 눈을 감으면 그 광경이 떠오르네요.
190
+ 해변, 토록 사랑받는 곳일까?
191
+ 서쪽위치협재해변은 에메랄드빛 바다로 유한 곳이더라고요. 모래가 고와서 맨발로 걷기에도 정말 좋았어요. 비양도가 바로 앞에 보이는 풍경은 그야말로 그림 같았답니다.
 
 
 
192
  """
193
  ]
194
  return random.choice(prompts)
 
197
  unwanted_phrases = [
198
  '여러분', '최근', '마지막으로', '결론적으로', '결국',
199
  '종합적으로', '따라서', '마무리', '끝으로', '요약',
200
+ '한 줄 요약', '정리하자면', '총정리', '여행을 마치며',
201
+ '이상으로', '추천드립니다', '방문해보세요', '도움이 되셨길',
202
+ '즐거운 여행 되세요', '다음 여행기에서', '도움이 되었길',
203
+ '좋은 하루 되세요', '즐거운 여행을 기원합니다'
204
  ]
205
  words = re.findall(r'\S+|\n', text)
206
  result_words = [word for word in words if not any(phrase in word for phrase in unwanted_phrases)]
 
224
  blog_content = re.sub(r'됩니다', '돼요', blog_content)
225
  blog_content = re.sub(r'입니다', '이에요', blog_content)
226
 
 
227
  exaggerated_expressions = [
228
+ (r'미식의 향연', r'맛있는 음식들'),
229
+ (r'입맛을 사로잡는', r'맛있는'),
230
+ (r'황홀한 맛', r'맛있는'),
231
+ (r'비할 데 없는 맛', r'특별한 맛'),
232
+ (r'천상', r'맛있는'),
233
+ (r'혀가 기억하는', r'기억에 남는'),
234
+ (r'절대 잊을 수 없는 맛', r'기억에 남는 맛'),
235
+ (r'미식가의 천국', r'맛집이 많은 곳'),
236
+ (r'기가 막힌 맛', r'맛있는'),
237
+ (r'장관이었어요', r'예뻤어요'),
238
+ (r'숨이 멎을 듯한', r'예쁜'),
239
+ (r'압도적인 경치', r'인상적인 경치'),
240
+ (r'절경', r'예쁜 풍경'),
241
+ (r'눈부신', r'밝은'),
242
+ (r'환상적인 전망', r'좋은 전망'),
243
+ (r'천혜의 자연', r'아름다운 자연'),
244
+ (r'그림 같은', r'예쁜'),
245
+ (r'파노라마 같은', r'넓게 보이는'),
246
+ (r'황홀경', r'좋은 경험'),
247
+ (r'가슴이 벅차오르는', r'기분 좋은'),
248
+ (r'꿈같은 시간', r'좋은 시간'),
249
+ (r'잊을 수 없는 경험', r'기억에 남는 경험'),
250
+ (r'감동적인', r'인상적인'),
251
+ (r'마음을 사로잡는', r'인상적인'),
252
+ (r'영혼을 치유하는', r'편안한'),
253
+ (r'최고의 순간', r'좋은 시간'),
254
  (r'환상적인', r'좋은'),
255
+ (r'경이로운', r'놀라운'),
256
+ (r'매혹적인', r'예쁜'),
 
 
 
 
 
 
257
  (r'그 자체', r''),
258
+ (r'상상을 초월하는', r'예상 밖의'),
259
  (r'이 .{1,10} 그 자체였어요', r'이 \1였어요'),
260
  (r'가 .{1,10} 그 자체였어요', r'가 \1였어요'),
261
+ (r'향연', r'즐거움'),
262
+ (r'미식', r'맛있는 음식'),
263
+ (r'압도적인', r'인상적인'),
264
  (r'천국', r'좋은 곳'),
265
  (r'황홀했어요', r'좋았어요'),
266
  (r'환상의', r'좋은')
 
269
  for pattern, replacement in exaggerated_expressions:
270
  blog_content = re.sub(pattern, replacement, blog_content, flags=re.IGNORECASE)
271
 
272
+ blog_content = re.sub(r'참고글에 따르면', r'제가 바로는', blog_content)
273
  blog_content = re.sub(r'참고글', r'관련 정보', blog_content)
274
 
275
  # Gemini API를 활용한 추가 후처리 프롬프트 (텍스트 보완 목적)
276
  prompt = f"""
277
+ 다음 여행 블로그 글을 더 자연스러운 형태로 변경해주세요:
278
 
279
  원본 글:
280
  {blog_content}
 
283
  1. 마크다운 형식 및 번호 목록, 불릿 표현 제거
284
  2. 소제목은 5개 이하로, 각 소제목 아래 내용은 최소 {MIN_SECTION_LENGTH}자 이상으로 상세하게 서술
285
  3. "참고글" 관련 표현 제거
286
+ 4. 오감에 기반한 생생한 묘사 및 실용 정보 포함
287
  5. 스타일: {style} (자연스러운 문체 사용)
288
  """
289
 
 
355
  if title.endswith(f" {ending}"):
356
  title = title[:-len(ending)-1]
357
  if len(title) < 20 and query:
358
+ title = f"{query} 여행 후기"
359
  return title
360
 
361
  for i, line in enumerate(lines):
 
397
  if in_paragraph:
398
  formatted_lines.append("</p>")
399
  if not title_found:
400
+ default_title = f"{query} 여행 후기" if query else "여행 후기"
401
  if first_non_empty_line:
402
  default_title = optimize_title(first_non_empty_line)
403
  formatted_lines.insert(0, f'<h1 style="font-size: 1.8em; margin-bottom: 20px; font-weight: bold; color: #222;">{html.escape(default_title)}</h1>')
 
421
  # 참고글 수집
422
  ref1, ref2, ref3 = fetch_crawl_results(query)
423
  style_prompt = get_style_prompt(style)
424
+ format_prompt = get_travel_blog_prompt()
425
 
426
  # 스타일 세부 지시사항 (원본 내용의 특성을 유지)
427
  style_specific_instructions = ""
 
432
  - 격식체(예: "~합니다", "~입니다") 사용 금지
433
  - 대화하듯 편안하게 작성
434
  - 짧고 간결한 문장 사용
435
+ - 여행에 대설렘과 기대감 표현
436
+ - 오감을 활용한 생생묘사 포함
437
+ - 시간흐름에 따른 여행 경험 서술
438
  """
439
  elif style == "일반적인":
440
  style_specific_instructions = """
 
442
  - '습니다체' 사용: "~했습니다", "~입니다", "~하였습니다"
443
  - 격식적이지 않은 명확한 표현 사용
444
  - 간결하고 명료한 문장 사용
445
+ - 객관적인 사실과 개인 경험 균형 있게 서술
 
446
  """
447
  elif style == "전문적인":
448
  style_specific_instructions = """
449
  이 블로그는 반드시 전문적이고 분석적인 어투로 작성해야 합니다.
450
  - '습니다체' 사용: "~했습니다", "~입니다", "~하였습니다"
451
+ - 역사, 문화, 지리적 정보 등 심층 정보 포함
452
  - 객관적이고 논리적인 분석 중심 작성
453
  - 구체적인 데이터 및 수치 포함하여 설명
 
454
  """
455
 
456
  # Phase 1: 초기 생성
457
  initial_prompt = f"""
458
+ 주제: {query} 여행
459
  참고글 1: {ref1}
460
  참고글 2: {ref2}
461
  참고글 3: {ref3}
 
466
  스타일 세부 지시사항:
467
  {style_specific_instructions}
468
  특별 지시사항:
469
+ 1. 반드�� 글의 처음에 매력적 제목 포함 (여행지명과 핵심 특징).
470
  2. 마크다운 문법(#, *, -, 1., 2., 등) 사용 금지.
471
  3. 소제목은 번호 없이 작성하고, 5개 이하로 제한.
472
  4. 모든 목록은 불릿이나 번호 없이 자연스럽게 서술.
473
  5. "참고글" 관련 표현 사용 금지.
474
+ 6. 블로그 작성자가 직접 경험하고 느낀 내용만 작성.
475
+ 7. 구체적인 정보(위치, 교통, 시간, 가격, 주차, 추천코스, 주변 맛집 등) 자연스럽게 포함.
476
  8. 글자수가 최소 {TARGET_CHAR_LENGTH}자 이상이어야 함.
 
 
 
 
 
 
 
 
 
477
  """
 
478
  first_attempt = call_gemini_api(initial_prompt)
479
  first_attempt_cleaned = remove_unwanted_phrases(first_attempt)
480
  first_attempt_length = len(first_attempt_cleaned)
 
498
  이전에 작성된 글은 목표 글자수인 {TARGET_CHAR_LENGTH}자에 미치지 못합니다. 현재 글자수는 약 {first_attempt_length}자입니다.
499
  또한, 각 소제목 아래 내용이 너무 짧고 부실합니다.
500
  중요 요구사항:
501
+ 1. 글의 처음에 매력적인 제목(여행지명({query})와 핵심 특징 포함) 추가.
502
  2. 글자수를 최소 {TARGET_CHAR_LENGTH}자 이상으로 늘리고, 각 섹션을 상세히 서술.
503
  3. 마크다운 형식(#, *, -, 1., 2., 등) 사용 금지.
504
  4. 소제목은 번호 없이 작성.
505
  5. 모든 목록은 불릿이나 번호 없이 서술.
506
  6. "참고글" 관련 표현 사용 금지.
507
+ 7. 블로그 작성자의 실제 경험서술.
508
  8. 각 소제목 아래 내용을 최소 {MIN_SECTION_LENGTH}자 이상 서술.
509
  9. 소제목 수는 5개 이하로 제한.
510
  상세 보완:
511
+ - 여행지 외관분위기, 오감 묘사 추가.
512
+ - 구체적인 에피소드, 상황, 대화구체적 이야기 추가.
513
+ - 역사, 문화배경흥미로운 정보 추가.
514
+ - 다양한 체험 활동, 맛집, 교통, 운영시간 등 실 정보 추가.
 
515
  """
516
  revised_attempt = call_gemini_api(revision_prompt)
517
  revised_cleaned = remove_unwanted_phrases(revised_attempt)
 
524
  # Phase 3: 확장 (Expansion) 시도 (글자수가 부족할 경우)
525
  if actual_char_length < TARGET_CHAR_LENGTH * 0.8:
526
  expansion_prompt = f"""
527
+ 다음 여행 블로그 글의 내용을 크게 확장해주세요:
528
  원본 글:
529
  {final_post}
530
  문제점:
 
535
  {style_specific_instructions}
536
  요구사항:
537
  1. 각 소제목 아래의 내용을 최소 {MIN_SECTION_LENGTH}자 이상 대폭 확장.
538
+ 2. 구체적인 에피소드, 상황, 대화, 감정상세 추가.
539
  3. 마크다운 형식(#, *, -, 1., 2., 등) 사용 금지.
540
  4. {style} 스타일에 맞춰 일관되게 작성.
541
  5. 소제목 구조는 유지하되, 각 섹션 내용을 3배 이상 풍부하게 확장.
542
+ 6. 여행지모습, 분위기, 음식, 사람들, 날씨, 느낌 등을 오감으로 생생하게 묘사.
543
  7. 전체 글자수를 최소 {TARGET_CHAR_LENGTH}자 이상 달성.
544
  8. 소제목 수는 최대 5개로 제한.
545
+ 9. 실용적 정보(교통, 비, 운영시간, 숙박, 맛집 등) 추가.
546
+ 10. 계절별,간대별 여행 추가.
547
+ 11. 주광지나 추가 코스 정보도 포함.
548
+ 12. 역사적, 문화적 배경을 흥미롭게 추가.
549
  """
550
  expanded_attempt = call_gemini_api(expansion_prompt)
551
  final_post = post_process_blog(expanded_attempt, style)
 
672
  # Gradio 인터페이스 구성 (기존 함수 이름, 인터페이스 유지)
673
  # -------------------------------
674
  with gr.Blocks() as iface:
675
+ gr.Markdown("# 여행 후기 블로그")
676
+ gr.Markdown("여행지 키워드를 입력해 주세요.")
677
 
678
+ query_input = gr.Textbox(lines=1, placeholder="제주도 여행, 부산 해운대, 남산타워, 통영, 경 불국사, 강원도 속초", label="키워드")
679
  style_input = gr.Radio(["친근한", "일반적인", "전문적인"], label="포스팅 스타일", value="친근한")
680
 
681
  generate_button = gr.Button("블로그 글 생성")
 
702
  )
703
 
704
  if __name__ == "__main__":
705
+ iface.launch()