masapon05 commited on
Commit
cc86110
·
verified ·
1 Parent(s): 5e20798

Upload BBS2ChatGPT.ipynb

Browse files
Files changed (1) hide show
  1. BBS2ChatGPT.ipynb +268 -0
BBS2ChatGPT.ipynb ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "metadata": {
7
+ "id": "USVNvArSTCIw"
8
+ },
9
+ "outputs": [],
10
+ "source": [
11
+ "# ChatGPT APIを用いた掲示板ログ解析スクリプト\n",
12
+ "\n",
13
+ "# 必要なライブラリのインストール\n",
14
+ "!pip install --upgrade requests sentence-transformers tiktoken tenacity\n",
15
+ "\n",
16
+ "# 必要なライブラリのインポート\n",
17
+ "import os\n",
18
+ "import glob\n",
19
+ "import json\n",
20
+ "import requests\n",
21
+ "from requests.exceptions import HTTPError\n",
22
+ "from sentence_transformers import SentenceTransformer, util\n",
23
+ "from typing import List, Optional\n",
24
+ "from tenacity import retry, stop_after_attempt, wait_exponential\n",
25
+ "\n",
26
+ "# クラス定義: OpenAIClient\n",
27
+ "class OpenAIClient:\n",
28
+ " \"\"\"OpenAI APIとの通信を管理するクラス\"\"\"\n",
29
+ "\n",
30
+ " def __init__(self, api_key: str):\n",
31
+ " self.api_key = api_key\n",
32
+ " self.endpoint = \"https://api.openai.com/v1/chat/completions\"\n",
33
+ "\n",
34
+ " def count_tokens(self, text: str) -> int:\n",
35
+ " \"\"\"テキストのトークン数を計算\"\"\"\n",
36
+ " import tiktoken # トークン計算用ライブラリ\n",
37
+ " encoding = tiktoken.get_encoding(\"cl100k_base\")\n",
38
+ " return len(encoding.encode(text))\n",
39
+ "\n",
40
+ " @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))\n",
41
+ " def generate_response(self, messages: List[dict], max_tokens: int = 1000) -> str:\n",
42
+ " \"\"\"ChatGPT APIを使用して応答を生成(エラー時は自動リトライ)\"\"\"\n",
43
+ " headers = {\n",
44
+ " \"Authorization\": f\"Bearer {self.api_key}\",\n",
45
+ " \"Content-Type\": \"application/json\"\n",
46
+ " }\n",
47
+ "\n",
48
+ " payload = {\n",
49
+ " \"model\": \"gpt-4\",\n",
50
+ " \"messages\": messages,\n",
51
+ " \"max_tokens\": max_tokens,\n",
52
+ " \"temperature\": 0.7\n",
53
+ " }\n",
54
+ "\n",
55
+ " try:\n",
56
+ " response = requests.post(self.endpoint, headers=headers, json=payload)\n",
57
+ " response.raise_for_status()\n",
58
+ " return response.json()[\"choices\"][0][\"message\"][\"content\"]\n",
59
+ " except HTTPError as http_err:\n",
60
+ " print(f\"HTTPエラーが発生しました: {http_err}\")\n",
61
+ " raise\n",
62
+ " except Exception as e:\n",
63
+ " print(f\"予期せぬエラーが発生しました: {e}\")\n",
64
+ " raise\n",
65
+ "\n",
66
+ "# クラス定義: FileHandler\n",
67
+ "class FileHandler:\n",
68
+ " \"\"\"ファイル操作を管理するクラス\"\"\"\n",
69
+ "\n",
70
+ " @staticmethod\n",
71
+ " def delete_existing_files():\n",
72
+ " \"\"\"既存のJSONファイルとマークダウンファイルを削除\"\"\"\n",
73
+ " file_patterns = ['./*.json', './*.md']\n",
74
+ " for pattern in file_patterns:\n",
75
+ " files_to_delete = glob.glob(pattern)\n",
76
+ " for file_path in files_to_delete:\n",
77
+ " try:\n",
78
+ " os.remove(file_path)\n",
79
+ " print(f\"削除しました: {file_path}\")\n",
80
+ " except Exception as e:\n",
81
+ " print(f\"{file_path} の削除中にエラーが発生しました: {e}\")\n",
82
+ "\n",
83
+ " @staticmethod\n",
84
+ " def upload_files(file_type: str) -> List[str]:\n",
85
+ " \"\"\"ファイルをアップロードし、パスのリストを返す\"\"\"\n",
86
+ " print(f\"{file_type}ファイルをアップロードしてください(複数選択可)\")\n",
87
+ " try:\n",
88
+ " from google.colab import files # Google Colab環境用\n",
89
+ " uploaded = files.upload()\n",
90
+ " file_paths = []\n",
91
+ " for filename in uploaded.keys():\n",
92
+ " file_path = os.path.abspath(filename)\n",
93
+ " file_paths.append(file_path)\n",
94
+ " print(f\"ファイル {filename} をアップロードしました(パス: {file_path})\")\n",
95
+ " return file_paths\n",
96
+ " except Exception as e:\n",
97
+ " print(f\"ファイルアップロード中にエラーが発生しました: {e}\")\n",
98
+ " return []\n",
99
+ "\n",
100
+ " @staticmethod\n",
101
+ " def read_markdown_file(file_path: str) -> Optional[List[str]]:\n",
102
+ " \"\"\"マークダウンファイルを読み込み、行のリストを返す\"\"\"\n",
103
+ " try:\n",
104
+ " with open(file_path, 'r', encoding='utf-8') as file:\n",
105
+ " return file.readlines()\n",
106
+ " except Exception as e:\n",
107
+ " print(f\"マークダウンファイル読み取り中にエラーが発生しました: {e}\")\n",
108
+ " return None\n",
109
+ "\n",
110
+ "# クラス定義: PostProcessor\n",
111
+ "class PostProcessor:\n",
112
+ " \"\"\"投稿データの処理を管理するクラス\"\"\"\n",
113
+ "\n",
114
+ " def __init__(self, model_name: str = \"multi-qa-mpnet-base-dot-v1\"):\n",
115
+ " self.model = SentenceTransformer(model_name)\n",
116
+ "\n",
117
+ " def extract_theme_line(self, markdown_lines: List[str]) -> Optional[str]:\n",
118
+ " \"\"\"マークダウンから「テーマ」行を抽出\"\"\"\n",
119
+ " for line in markdown_lines:\n",
120
+ " if \"**テーマ**\" in line:\n",
121
+ " parts = line.split(\":\")\n",
122
+ " if len(parts) > 1:\n",
123
+ " return parts[1].strip()\n",
124
+ " print(\"「**テーマ**」行が見つかりませんでした。\")\n",
125
+ " return None\n",
126
+ "\n",
127
+ " def split_json_by_posts(self, file_paths: List[str]) -> List[str]:\n",
128
+ " \"\"\"JSONファイルから投稿データを抽出し、個別の投稿に分割\"\"\"\n",
129
+ " all_posts = []\n",
130
+ " for file_path in file_paths:\n",
131
+ " try:\n",
132
+ " with open(file_path, 'r', encoding='utf-8') as file:\n",
133
+ " data = json.load(file)\n",
134
+ " posts = data.get('posts', [])\n",
135
+ " split_posts = [json.dumps(post, ensure_ascii=False) for post in posts]\n",
136
+ " all_posts.extend(split_posts)\n",
137
+ " except Exception as e:\n",
138
+ " print(f\"JSONファイル処理中にエラーが発生しました: {e}\")\n",
139
+ " return all_posts\n",
140
+ "\n",
141
+ " def filter_relevant_posts(self, user_prompt: str, posts: List[str], threshold: float = 0.8) -> List[str]:\n",
142
+ " \"\"\"投稿データから関連性の高いものをフィルタリング(閾値を動的に調整)\"\"\"\n",
143
+ " while threshold >= 0.1:\n",
144
+ " try:\n",
145
+ " prompt_embedding = self.model.encode(user_prompt, convert_to_tensor=True)\n",
146
+ " post_embeddings = self.model.encode(posts, convert_to_tensor=True)\n",
147
+ " cosine_scores = util.pytorch_cos_sim(prompt_embedding, post_embeddings)\n",
148
+ " relevant_posts = [posts[i] for i, score in enumerate(cosine_scores[0]) if score >= threshold]\n",
149
+ "\n",
150
+ " if relevant_posts:\n",
151
+ " print(f\"関連性のある投稿が見つかりました。使用された閾値: {threshold}\")\n",
152
+ " return relevant_posts\n",
153
+ "\n",
154
+ " threshold -= 0.1\n",
155
+ " print(f\"閾値を下げて再試行します。現在の閾値: {threshold:.1f}\")\n",
156
+ " except Exception as e:\n",
157
+ " print(f\"関連性判定中にエラーが発生しました: {e}\")\n",
158
+ " return []\n",
159
+ "\n",
160
+ " print(\"関連性のある投稿が見つかりませんでした。\")\n",
161
+ " return []\n",
162
+ "\n",
163
+ "def main():\n",
164
+ " \"\"\"メイン処理関数。ユーザー入力やアップロードされたファイルの処理。\"\"\"\n",
165
+ " try:\n",
166
+ " api_key = input(\"OpenAI APIキーを入力してください: \")\n",
167
+ "\n",
168
+ " # 各クラスの初期化\n",
169
+ " openai_client = OpenAIClient(api_key)\n",
170
+ " file_handler = FileHandler()\n",
171
+ " post_processor = PostProcessor()\n",
172
+ "\n",
173
+ " print(\"\\n既存JSONおよびMarkdownファイル削除中...\")\n",
174
+ " file_handler.delete_existing_files()\n",
175
+ "\n",
176
+ " print(\"\\nプロンプトとして使用するマークダウンファイルをアップロードしてください。\")\n",
177
+ " markdown_files = file_handler.upload_files(\"マークダウン\")\n",
178
+ " if not markdown_files or len(markdown_files) != 1:\n",
179
+ " raise ValueError(\"マークダウンファイルが正しくアップロードされませんでした。\")\n",
180
+ "\n",
181
+ " markdown_content = file_handler.read_markdown_file(markdown_files[0])\n",
182
+ " if not markdown_content:\n",
183
+ " raise ValueError(\"マークダウンファイルが空です。\")\n",
184
+ "\n",
185
+ " user_prompt_theme = post_processor.extract_theme_line(markdown_content)\n",
186
+ " if not user_prompt_theme:\n",
187
+ " raise ValueError(\"テーマ行が見つかりませんでした。\")\n",
188
+ "\n",
189
+ " user_prompt_full = \"\".join(markdown_content)\n",
190
+ "\n",
191
+ " print(\"\\n解析対象となるJSONファイルをアップロードしてください。\")\n",
192
+ " json_files = file_handler.upload_files(\"JSON\")\n",
193
+ " if not json_files:\n",
194
+ " raise ValueError(\"JSONファイルが正しくアップロードされませんでした。\")\n",
195
+ "\n",
196
+ " posts = post_processor.split_json_by_posts(json_files)\n",
197
+ " if not posts:\n",
198
+ " raise ValueError(\"有効な投稿データが見つかりませんでした。\")\n",
199
+ "\n",
200
+ " relevant_posts = post_processor.filter_relevant_posts(user_prompt_theme.strip(), posts)\n",
201
+ " if not relevant_posts:\n",
202
+ " raise ValueError(\"関連性のある投稿データが見つかりませんでした。\")\n",
203
+ "\n",
204
+ " # 関連性の高い投稿をJSONファイルとして保存\n",
205
+ " with open(\"relevant_posts.json\", \"w\", encoding=\"utf-8\") as json_file:\n",
206
+ " json.dump({\"relevant_posts\": [json.loads(post) for post in relevant_posts]},\n",
207
+ " json_file,\n",
208
+ " ensure_ascii=False,\n",
209
+ " indent=2)\n",
210
+ " print(\"\\n関連性の高い投稿を 'relevant_posts.json' に保存しました。\")\n",
211
+ "\n",
212
+ " combined_posts = \"\\n\".join(relevant_posts)\n",
213
+ "\n",
214
+ " # トークン数計算と調整\n",
215
+ " total_tokens = openai_client.count_tokens(user_prompt_full + combined_posts)\n",
216
+ "\n",
217
+ " # トークン数制限対応\n",
218
+ " if total_tokens > 4096:\n",
219
+ " print(f\"トークン数が上限(4096)を超えています。投稿データを切り詰めます。\")\n",
220
+ " truncation_point = int(4096 * len(combined_posts) / total_tokens)\n",
221
+ " combined_posts = combined_posts[:truncation_point]\n",
222
+ "\n",
223
+ " # プロンプトと投稿データを結合\n",
224
+ " prompt = f\"{user_prompt_full}\\n\\n関連性が高い投稿データ:\\n{combined_posts}\"\n",
225
+ "\n",
226
+ " # ChatGPT API呼び出しと結果表示\n",
227
+ " try:\n",
228
+ " response = openai_client.generate_response(\n",
229
+ " messages=[{\"role\": \"user\", \"content\": prompt}],\n",
230
+ " max_tokens=1000\n",
231
+ " )\n",
232
+ "\n",
233
+ " print(\"\\n### ChatGPT解析結果 (マークダウン形式):\\n\")\n",
234
+ " print(response)\n",
235
+ "\n",
236
+ " # 結果をファイルに保存\n",
237
+ " with open(\"chatgpt_analysis_result.md\", \"w\", encoding=\"utf-8\") as result_file:\n",
238
+ " result_file.write(response)\n",
239
+ " print(\"\\n解析結果が 'chatgpt_analysis_result.md' に保存されました。\")\n",
240
+ "\n",
241
+ " except Exception as e:\n",
242
+ " print(f\"ChatGPT API呼び出し中にエラーが発生しました: {e}\")\n",
243
+ "\n",
244
+ " except Exception as e:\n",
245
+ " print(f\"エラーが発生しました: {e}\")\n",
246
+ "\n",
247
+ "if __name__ == \"__main__\":\n",
248
+ " main()\n"
249
+ ]
250
+ }
251
+ ],
252
+ "metadata": {
253
+ "accelerator": "GPU",
254
+ "colab": {
255
+ "gpuType": "T4",
256
+ "provenance": []
257
+ },
258
+ "kernelspec": {
259
+ "display_name": "Python 3",
260
+ "name": "python3"
261
+ },
262
+ "language_info": {
263
+ "name": "python"
264
+ }
265
+ },
266
+ "nbformat": 4,
267
+ "nbformat_minor": 0
268
+ }