Spaces:
Running on Zero
Running on Zero
| import os | |
| import json | |
| import time | |
| import requests | |
| import wikipediaapi | |
| from tqdm import tqdm | |
| # LM Studioのローカルサーバー設定 | |
| LM_STUDIO_URL = "http://127.0.0.1:1234/v1/chat/completions" | |
| # 教師モデルへのプロンプト(システム命令) | |
| SYSTEM_PROMPT = """あなたは超優秀な情報整理AI(マインドマップ職人)です。 | |
| ユーザーから提供された長文テキストを深く読み解き、内容を論理的に構造化して、マインドマップ用のMarkdownを出力してください。 | |
| 【厳格なルール】 | |
| 1. 出力は必ずMarkdownの階層構造(#、##、###、および箇条書き - )のみとすること。 | |
| 2. 一番上の階層(#)は、そのテキストの主題(メインテーマ)を1つだけ記述すること。 | |
| 3. 下位の階層(##、###)で、大項目・中項目を整理すること。 | |
| 4. 具体的な要素や詳細な情報は、箇条書き(- )を用いて末端の枝として表現すること。 | |
| 5. Markdown以外の説明文、挨拶、感想、「以下に示します」などの前置きは一切出力してはならない(厳禁)。 | |
| 6. 「概要」や「利用状況」など、テキストにない見出しを適当にでっち上げないこと。テキストに沿った見出しを作ること。 | |
| """ | |
| USER_INSTRUCTION = "以下の長文から論理構造を抽出し、Markdown形式の目次(マインドマップ)を出力してください。" | |
| def generate_teacher_output(text): | |
| """LM StudioのAPIを叩いて、教師モデルにマインドマップを作らせる""" | |
| payload = { | |
| "messages": [ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": f"{USER_INSTRUCTION}\n\n【対象テキスト】\n{text}"} | |
| ], | |
| "temperature": 0.2, # 創造性よりも正確性を重視 | |
| "max_tokens": -1 | |
| } | |
| try: | |
| response = requests.post(LM_STUDIO_URL, json=payload, timeout=300) | |
| if response.status_code == 200: | |
| result = response.json() | |
| return result['choices'][0]['message']['content'].strip() | |
| else: | |
| print(f"API Error: {response.status_code}") | |
| return None | |
| except Exception as e: | |
| print(f"Connection Error: {e}") | |
| return None | |
| def fetch_random_wikipedia_text(count=10): | |
| """Wikipediaからランダムな記事のテキストを取得する(見出し構造は使わない)""" | |
| wiki_wiki = wikipediaapi.Wikipedia( | |
| user_agent='MindMapStudio_DistillationGen/1.0', | |
| language='ja', | |
| extract_format=wikipediaapi.ExtractFormat.WIKI | |
| ) | |
| print(f"Wikipediaからランダムに {count} 件の記事タイトルを取得中...") | |
| titles = set() | |
| while len(titles) < count: | |
| limit = min(50, count - len(titles)) | |
| url = f"https://ja.wikipedia.org/w/api.php?action=query&list=random&rnnamespace=0&rnlimit={limit}&format=json" | |
| headers = {'User-Agent': 'MindMapStudio_DistillationGen/1.0'} | |
| try: | |
| res = requests.get(url, headers=headers).json() | |
| for q in res.get("query", {}).get("random", []): | |
| titles.add(q["title"]) | |
| except Exception: | |
| time.sleep(1) | |
| print("記事のテキストをダウンロード中...") | |
| texts = [] | |
| for title in tqdm(list(titles)): | |
| try: | |
| page = wiki_wiki.page(title) | |
| if page.exists() and len(page.text) > 500: | |
| # 最初の2000文字程度を学習用テキストとする(長すぎるとAPIが遅くなるため) | |
| text = page.text[:2000] | |
| texts.append(text) | |
| except Exception: | |
| pass | |
| return texts | |
| def generate_distilled_dataset(num_samples=10, output_file="mindmap_dataset_distilled.jsonl"): | |
| print("=== 教師モデルによるデータセット蒸留(Knowledge Distillation)を開始 ===") | |
| texts = fetch_random_wikipedia_text(num_samples) | |
| print(f"合計 {len(texts)} 件の有効なテキストを取得しました。") | |
| print("LM Studio (Teacher) に推論させています...") | |
| with open(output_file, 'w', encoding='utf-8') as f: | |
| for text in tqdm(texts): | |
| teacher_md = generate_teacher_output(text) | |
| if teacher_md: | |
| # AIがMarkdownのコードブロック(```markdown)を含めてしまった場合は除去 | |
| teacher_md = teacher_md.replace("```markdown\n", "").replace("```\n", "").replace("```", "").strip() | |
| data = { | |
| "instruction": USER_INSTRUCTION, | |
| "input": text, | |
| "output": teacher_md | |
| } | |
| f.write(json.dumps(data, ensure_ascii=False) + '\n') | |
| f.flush() | |
| else: | |
| print("\n警告: APIは成功しましたが、出力(content)が空でした。") | |
| print(f"完了!蒸留データセットを {output_file} に保存しました。") | |
| if __name__ == "__main__": | |
| # 本番用データセットの生成(500件) | |
| generate_distilled_dataset(500) | |