ryo2 commited on
Commit
f477594
·
verified ·
1 Parent(s): 2641453

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +81 -7
  2. app.py +292 -0
  3. requirements.txt +1 -0
README.md CHANGED
@@ -1,13 +1,87 @@
1
  ---
2
- title: Prompt Engineering Tool
3
- emoji: 🦀
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 5.31.0
8
  app_file: app.py
9
  pinned: false
10
- license: apache-2.0
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: プロンプトエンジニアリングツール
3
+ emoji: 🔧
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: "4.0.0"
8
  app_file: app.py
9
  pinned: false
10
+ tags:
11
+ - prompt-engineering
12
+ - japanese
13
+ - markdown
14
+ - gradio
15
+ - nlp
16
  ---
17
 
18
+ # 🔧 プロンプトエンジニアリングツール
19
+
20
+ プロンプトエンジニアリングを効率化するためのGradioウェブアプリケーションです。
21
+
22
+ ## ✨ 機能
23
+
24
+ - **📁 アコーディオン式UI**: 各ショットが折りたたみ可能で、スクロールが最小限
25
+ - **🔧 基本4ショット**: デフォルトで命令書、制約条件、入力、出力が用意
26
+ - **➕ 動的ショット追加**: 最大8ショットまで追加可能(合計12ショット)
27
+ - **🗑️ 個別削除**: 不要なショットを個別に削除可能
28
+ - **⬆️⬇️ 順序変更**: ショットの順序を自由に変更
29
+ - **🔄 リアルタイムプレビュー**: 入力内容が即座にMarkdown形式で表示
30
+ - **📥 ファイルダウンロード**: 生成されたプロンプトをMarkdownファイルでダウンロード
31
+ - **📋 コピー機能**: テキストエリアから直接コピー可能
32
+ - **📊 統計表示**: ショット数と入力済み数をリアルタイム表示
33
+
34
+ ## 🚀 使用方法
35
+
36
+ ### このSpaceでの使用
37
+
38
+ 1. **ショット入力**: 各アコーディオンを開いてショットのタイトルと内容を入力
39
+ 2. **ショット追加**: "➕ ショットを追加"ボタンで新しいショットを1つずつ追加
40
+ 3. **ショット管理**:
41
+ - ⬆️⬇️ ボタンでショットの順序を変更
42
+ - 🗑️ 削除ボタンで不要なショットを削除
43
+ 4. **プレビュー**: 右側パネルで生成されたMarkdownプロンプトをリアルタイム確認
44
+ 5. **出力**: ダウンロードまたはコピーでプロンプトを活用
45
+
46
+ ### ローカル実行
47
+
48
+ ```bash
49
+ # 依存関係のインストール
50
+ pip install gradio>=4.0.0
51
+
52
+ # アプリケーションの実行
53
+ python app.py
54
+ ```
55
+
56
+ ## 🎨 UI改善点
57
+
58
+ - **スクロール負担軽減**: アコーディオン式で必要なショットのみ展開
59
+ - **直感的操作**: 絵文字とアイコンでわかりやすいUI
60
+ - **効率的編集**: プレースホルダーでガイダンス付き入力
61
+ - **一括操作**: 全てクリアボタンで素早いリセット
62
+ - **クラス設計**: 保守性の高いクラスベースアーキテクチャ
63
+
64
+ ## 🛠️ 技術スタック
65
+
66
+ - **Python**: >=3.10
67
+ - **Gradio**: >=4.0.0
68
+ - **アーキテクチャ**: クラスベース設計(MarkdownGenerator, ShotManager, PromptEngineering)
69
+
70
+ ## 📝 プロンプト例
71
+
72
+ 生成されるMarkdownは以下のような形式になります:
73
+
74
+ ```markdown
75
+ # 命令書
76
+ あなたは専門的なAIアシスタントです...
77
+
78
+ # 制約条件
79
+ - 日本語で回答してください
80
+ - 簡潔で分かりやすい表現を心がけてください
81
+
82
+ # 入力
83
+ {{ユーザーからの質問}}
84
+
85
+ # 出力
86
+ {{期待される回答形式}}
87
+ ```
app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+
4
+
5
+ class MarkdownGenerator:
6
+ @staticmethod
7
+ def generate_from_shot_data(shot_data):
8
+ markdown = ""
9
+ for i in range(0, len(shot_data), 3):
10
+ if i + 2 < len(shot_data):
11
+ title, content, visible = shot_data[i], shot_data[i + 1], shot_data[i + 2]
12
+ if visible and content.strip():
13
+ markdown += f"# {title}\n{content}\n\n"
14
+ return markdown.strip()
15
+
16
+ @staticmethod
17
+ def create_temporary_file(markdown_content):
18
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as f:
19
+ f.write(markdown_content)
20
+ return f.name
21
+
22
+
23
+ class ShotManager:
24
+ def __init__(self):
25
+ self.default_titles = ["命令書", "制約条件", "入力", "出力"]
26
+ self.max_shots = 12
27
+
28
+ def add_shot(self, *shot_data):
29
+ shot_data = list(shot_data)
30
+ for i in range(12, len(shot_data), 3):
31
+ if i + 2 < len(shot_data) and not shot_data[i + 2]:
32
+ shot_data[i] = f"新しいショット {(i // 3) - 3}"
33
+ shot_data[i + 2] = True
34
+ break
35
+ return shot_data + self._create_accordion_updates(shot_data)
36
+
37
+ def delete_shot(self, shot_index, *shot_data):
38
+ shot_data = list(shot_data)
39
+ visible_count = sum(1 for i in range(2, len(shot_data), 3) if i < len(shot_data) and shot_data[i])
40
+
41
+ if visible_count > 1:
42
+ data_index = shot_index * 3
43
+ if data_index + 2 < len(shot_data):
44
+ shot_data[data_index:data_index + 3] = ["", "", False]
45
+
46
+ return shot_data + self._create_accordion_updates(shot_data)
47
+
48
+ def move_shot_up(self, shot_index, *shot_data):
49
+ shot_data = list(shot_data)
50
+ if shot_index > 0:
51
+ self._swap_shots(shot_data, shot_index, shot_index - 1)
52
+ return shot_data + self._create_accordion_updates(shot_data)
53
+
54
+ def move_shot_down(self, shot_index, *shot_data):
55
+ shot_data = list(shot_data)
56
+ next_index = (shot_index + 1) * 3
57
+ if next_index + 2 < len(shot_data) and shot_data[next_index + 2]:
58
+ self._swap_shots(shot_data, shot_index, shot_index + 1)
59
+ return shot_data + self._create_accordion_updates(shot_data)
60
+
61
+ def clear_all_shots(self, *shot_data):
62
+ shot_data = list(shot_data)
63
+
64
+ for i, title in enumerate(self.default_titles):
65
+ data_index = i * 3
66
+ if data_index + 2 < len(shot_data):
67
+ shot_data[data_index:data_index + 3] = [title, "", True]
68
+
69
+ for i in range(4, len(shot_data) // 3):
70
+ data_index = i * 3
71
+ if data_index + 2 < len(shot_data):
72
+ shot_data[data_index:data_index + 3] = ["", "", False]
73
+
74
+ return shot_data + self._create_accordion_updates(shot_data)
75
+
76
+ def _swap_shots(self, shot_data, index1, index2):
77
+ start1, start2 = index1 * 3, index2 * 3
78
+ if start1 + 2 < len(shot_data) and start2 + 2 < len(shot_data):
79
+ for offset in range(3):
80
+ shot_data[start1 + offset], shot_data[start2 + offset] = \
81
+ shot_data[start2 + offset], shot_data[start1 + offset]
82
+
83
+ def _create_accordion_updates(self, shot_data):
84
+ updates = []
85
+ for i in range(self.max_shots):
86
+ data_index = i * 3
87
+ if data_index + 2 < len(shot_data):
88
+ updates.append(gr.update(visible=shot_data[data_index + 2]))
89
+ else:
90
+ updates.append(gr.update())
91
+ return updates
92
+
93
+
94
+ class PromptEngineering:
95
+ def __init__(self):
96
+ self.shot_manager = ShotManager()
97
+ self.markdown_generator = MarkdownGenerator()
98
+
99
+ def update_preview_and_stats(self, *shot_data):
100
+ markdown = self.markdown_generator.generate_from_shot_data(shot_data)
101
+ total_shots, filled_shots = self._calculate_stats(shot_data)
102
+ stats = f"ショット数: {total_shots} | 入力済み: {filled_shots}"
103
+
104
+ if markdown:
105
+ temp_file = self.markdown_generator.create_temporary_file(markdown)
106
+ return markdown, stats, markdown, gr.update(visible=True, value=temp_file)
107
+ else:
108
+ return "ショットに内容を入力すると、プレビューが表示されます。", stats, "", gr.update(visible=False)
109
+
110
+ def _calculate_stats(self, shot_data):
111
+ total_shots = filled_shots = 0
112
+ for i in range(0, len(shot_data), 3):
113
+ if i + 2 < len(shot_data):
114
+ title, content, visible = shot_data[i], shot_data[i + 1], shot_data[i + 2]
115
+ if visible:
116
+ total_shots += 1
117
+ if content.strip():
118
+ filled_shots += 1
119
+ return total_shots, filled_shots
120
+
121
+ def create_interface(self):
122
+ with gr.Blocks(title="🔧 プロンプトエンジニアリングツール", theme=gr.themes.Soft()) as demo:
123
+ gr.Markdown("# 🔧 プロンプトエンジニアリングツール")
124
+ gr.Markdown("各ショットを入力して、統合されたプロンプトを生成しましょう!")
125
+
126
+ with gr.Row():
127
+ shot_components, shot_data_components = self._create_shot_input_section()
128
+ preview_components = self._create_preview_section()
129
+
130
+ self._setup_event_handlers(shot_components, shot_data_components, preview_components)
131
+
132
+ return demo
133
+
134
+ def _create_shot_input_section(self):
135
+ with gr.Column(scale=2):
136
+ gr.Markdown("## 📝 ショット入力")
137
+
138
+ with gr.Row():
139
+ add_shot_btn = gr.Button("➕ ショットを追加", variant="primary")
140
+ clear_all_btn = gr.Button("🗑️ 全てクリア", variant="stop")
141
+
142
+ shot_components = []
143
+ shot_data_components = []
144
+
145
+ for i in range(12):
146
+ component_data = self._create_single_shot(i)
147
+ shot_components.append(component_data['components'])
148
+ shot_data_components.extend(component_data['data'])
149
+
150
+ return {'components': shot_components, 'add_btn': add_shot_btn, 'clear_btn': clear_all_btn}, shot_data_components
151
+
152
+ def _create_single_shot(self, index):
153
+ default_values = self._get_default_shot_values(index)
154
+
155
+ with gr.Accordion(default_values['accordion_title'],
156
+ open=(index == 0),
157
+ visible=default_values['visible']) as accordion:
158
+
159
+ with gr.Row():
160
+ with gr.Column(scale=3):
161
+ title_input = gr.Textbox(
162
+ label="ショットタイトル",
163
+ value=default_values['title'],
164
+ placeholder="ショットのタイトルを入力してください..."
165
+ )
166
+ content_input = gr.Textbox(
167
+ label="内容",
168
+ value="",
169
+ lines=5,
170
+ placeholder="ショットの内容を入力してください..."
171
+ )
172
+
173
+ with gr.Column(scale=1):
174
+ gr.Markdown("**操作**")
175
+ with gr.Row():
176
+ up_btn = gr.Button("⬆️", size="sm", variant="secondary")
177
+ down_btn = gr.Button("⬇️", size="sm", variant="secondary")
178
+ with gr.Row():
179
+ delete_btn = gr.Button("🗑️ 削除", size="sm", variant="stop")
180
+
181
+ visible_state = gr.Checkbox(value=default_values['visible'], visible=False)
182
+
183
+ return {
184
+ 'components': {
185
+ 'accordion': accordion,
186
+ 'title': title_input,
187
+ 'content': content_input,
188
+ 'visible': visible_state,
189
+ 'up': up_btn,
190
+ 'down': down_btn,
191
+ 'delete': delete_btn
192
+ },
193
+ 'data': [title_input, content_input, visible_state]
194
+ }
195
+
196
+ def _get_default_shot_values(self, index):
197
+ if index < 4:
198
+ titles = ["命令書", "制約条件", "入力", "出力"]
199
+ return {
200
+ 'title': titles[index],
201
+ 'visible': True,
202
+ 'accordion_title': f"📋 {titles[index]}"
203
+ }
204
+ else:
205
+ return {
206
+ 'title': "",
207
+ 'visible': False,
208
+ 'accordion_title': f"📋 追加ショット {index - 3}"
209
+ }
210
+
211
+ def _create_preview_section(self):
212
+ with gr.Column(scale=1):
213
+ gr.Markdown("## 📄 生成されたプロンプト")
214
+
215
+ stats_display = gr.Textbox(
216
+ label="📊 統計",
217
+ value="ショット数: 4 | 入力済み: 0",
218
+ interactive=False
219
+ )
220
+
221
+ gr.Markdown("### 👀 プレビュー")
222
+ preview_area = gr.Textbox(
223
+ label="生成されたMarkdown",
224
+ value="ショットに内容を入力すると、プレビューが表示されます。",
225
+ lines=15,
226
+ max_lines=20,
227
+ interactive=False
228
+ )
229
+
230
+ gr.Markdown("### 📥 ダウンロード")
231
+ download_file = gr.File(label="Markdownファイル", visible=False)
232
+
233
+ gr.Markdown("### 📋 コピー用")
234
+ copy_area = gr.Textbox(
235
+ label="テキスト形式",
236
+ lines=8,
237
+ interactive=False,
238
+ info="このテキストをコピーして使用できます"
239
+ )
240
+
241
+ return [preview_area, stats_display, copy_area, download_file]
242
+
243
+ def _setup_event_handlers(self, shot_components, shot_data_components, preview_components):
244
+ accordion_components = [comp['accordion'] for comp in shot_components['components']]
245
+ all_outputs = shot_data_components + accordion_components
246
+
247
+ shot_components['add_btn'].click(
248
+ fn=self.shot_manager.add_shot,
249
+ inputs=shot_data_components,
250
+ outputs=all_outputs
251
+ )
252
+
253
+ shot_components['clear_btn'].click(
254
+ fn=self.shot_manager.clear_all_shots,
255
+ inputs=shot_data_components,
256
+ outputs=all_outputs
257
+ )
258
+
259
+ for i, component in enumerate(shot_components['components']):
260
+ component['up'].click(
261
+ fn=lambda *args, idx=i: self.shot_manager.move_shot_up(idx, *args),
262
+ inputs=shot_data_components,
263
+ outputs=all_outputs
264
+ )
265
+
266
+ component['down'].click(
267
+ fn=lambda *args, idx=i: self.shot_manager.move_shot_down(idx, *args),
268
+ inputs=shot_data_components,
269
+ outputs=all_outputs
270
+ )
271
+
272
+ component['delete'].click(
273
+ fn=lambda *args, idx=i: self.shot_manager.delete_shot(idx, *args),
274
+ inputs=shot_data_components,
275
+ outputs=all_outputs
276
+ )
277
+
278
+ for component in shot_data_components:
279
+ component.change(
280
+ fn=self.update_preview_and_stats,
281
+ inputs=shot_data_components,
282
+ outputs=preview_components
283
+ )
284
+
285
+ def launch(self):
286
+ demo = self.create_interface()
287
+ demo.launch()
288
+
289
+
290
+ if __name__ == "__main__":
291
+ app = PromptEngineering()
292
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio>=4.0.0