lu sun commited on
Commit
2826971
·
0 Parent(s):
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +15 -0
  2. app.py +299 -0
  3. blog_class.py +452 -0
  4. requirements.txt +6 -0
  5. resource/2024-10-09-プロジェクト計画で重要視すること.mp3 +3 -0
  6. resource/2024-10-10-プロジェクト計画時点でユーザーと握ること.mp3 +3 -0
  7. resource/2024-10-11-全員が同じベクトルを向くために.mp3 +3 -0
  8. resource/2024-10-12-PMはバランスを取る仕事.mp3 +3 -0
  9. resource/2024-10-15-PMはコミュニケーションが大事です.mp3 +3 -0
  10. resource/2024-10-17-進捗報告は定量的に.mp3 +3 -0
  11. resource/2024-10-18-進捗報告から真実を掴むには.mp3 +3 -0
  12. resource/2024-10-19-報告は多少疑うべし.mp3 +3 -0
  13. resource/2024-10-21-心配事がリスクです.mp3 +3 -0
  14. resource/2024-10-22-適切なリスク計上のコツ.mp3 +3 -0
  15. resource/2024-10-23-確実にリスク対策するために.mp3 +3 -0
  16. resource/2024-10-24-リスクが顕在化した時の為にやっておくこと.mp3 +3 -0
  17. resource/2024-10-25-前提をリスクで管理する.mp3 +3 -0
  18. resource/2024-10-26-言い訳をリスクで管理する.mp3 +3 -0
  19. resource/2024-10-27-リスクを棚卸しする.mp3 +3 -0
  20. resource/2024-10-28-課題の対策を考える.mp3 +3 -0
  21. resource/2024-10-29-課題の早期発見に向けて心がけること.mp3 +3 -0
  22. resource/2024-10-30-現実的な対策を考える.mp3 +3 -0
  23. resource/2024-10-31-報告の目的って何?.mp3 +3 -0
  24. resource/2024-11-01-原因と真因.mp3 +3 -0
  25. resource/2024-11-02-なぜを5回繰り返してもダメな理由.mp3 +3 -0
  26. resource/2024-11-03-真因分析の極意.mp3 +3 -0
  27. resource/2024-11-05-対策検討の極意.mp3 +3 -0
  28. resource/2024-11-06-プロジェクトは性悪説がちょうどいい.mp3 +3 -0
  29. resource/2024-11-07-コスト管理でよくある話.mp3 +3 -0
  30. resource/2024-11-08-手戻りはもったいない.mp3 +3 -0
  31. resource/2024-11-09-責任って何?.mp3 +3 -0
  32. resource/2024-11-10-早く開発するためには.mp3 +3 -0
  33. resource/2024-11-11-変更管理の要諦.mp3 +3 -0
  34. resource/2024-11-12-立ち止まる勇気.mp3 +3 -0
  35. resource/2024-11-13-テストの目的.mp3 +3 -0
  36. resource/2024-11-14-テスト計画のコツ.mp3 +3 -0
  37. resource/2024-11-15-雛形の必要性と弊害.mp3 +3 -0
  38. resource/2024-11-16-個人を主語にするステークホルダーマネジメント.mp3 +3 -0
  39. resource/2024-11-17-議論する組織を目指す.mp3 +3 -0
  40. resource/2024-11-18-問題を問題と言える風土.mp3 +3 -0
  41. resource/2024-11-19-保守工程のことを意識する.mp3 +3 -0
  42. resource/2024-11-20-本番実施計画も性悪説が基本です.mp3 +3 -0
  43. resource/2024-11-21-本番確認のレベル感を決める観点.mp3 +3 -0
  44. resource/2024-11-22-失敗のパターンを持っておく.mp3 +3 -0
  45. resource/2024-11-23-規模の小さいお仕事で基礎・基本を身につける.mp3 +3 -0
  46. resource/2024-11-24-極力マルチタスクを作らない計画.mp3 +3 -0
  47. resource/2024-11-25-「頑張ります」を適切に使い、正しく解釈する.mp3 +3 -0
  48. resource/2024-11-26-基本設計の品質がプロジェクト成功の鍵.mp3 +3 -0
  49. resource/2024-11-27-文章を使ってプロジェクトを成功に導く.mp3 +3 -0
  50. resource/2024-11-28-作戦を伝えることがプロジェクトをスムーズに進める鍵.mp3 +3 -0
README.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FAQ Workshop
3
+ emoji: 💻
4
+ colorFrom: gray
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 5.16.0
8
+ python_version: 3.11.11
9
+ app_file: app.py
10
+ pinned: false
11
+ license: mit
12
+ short_description: FAQ CHAT by プロジェクトマネジメント勉強会
13
+ ---
14
+
15
+ Check out the configuration reference at <https://huggingface.co/docs/hub/spaces-config-reference>
app.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ import shutil
4
+ import threading
5
+ import time
6
+
7
+ import gradio as gr
8
+
9
+ from blog_class import audio_text, knowledge_class, save_feedback
10
+
11
+ knowledge_cls = knowledge_class()
12
+
13
+
14
+ def update_knowledge():
15
+ knowledge_cls.get_new_knowledge()
16
+ choices_list = list(knowledge_cls.reference_dict.keys())
17
+
18
+ return gr.update(choices=choices_list)
19
+
20
+
21
+ def user_message_fn(user_message, history):
22
+ if history is None:
23
+ history = []
24
+ history.append({"role": "user", "content": user_message})
25
+ return "", history
26
+
27
+
28
+ def answer_question(history):
29
+ print("chat Start")
30
+ answer_text, info_title, audio_file_path = knowledge_cls.get_chat_answer(history)
31
+ if not answer_text:
32
+ yield history
33
+ return
34
+ audio_answer_container = {"result": None}
35
+
36
+ def run_audio_answer():
37
+ audio_answer_container["result"] = audio_text(answer_text, audio_file_path)
38
+
39
+ audio_thread = threading.Thread(target=run_audio_answer)
40
+ audio_thread.start()
41
+
42
+ history.append({"role": "assistant", "content": ""})
43
+ #
44
+ history.append(
45
+ {
46
+ "role": "assistant",
47
+ "content": "音声作成中...",
48
+ "metadata": {"title": "🎧 音声返信", "status": "pending"},
49
+ }
50
+ )
51
+
52
+ history.append(
53
+ {
54
+ "role": "assistant",
55
+ "content": info_title,
56
+ "metadata": {"title": "📚 参照ナレッジ", "status": "done"},
57
+ }
58
+ )
59
+
60
+ for char in answer_text:
61
+ history[-3]["content"] += char
62
+ if (
63
+ audio_answer_container["result"] is not None
64
+ and history[-2]["metadata"]["status"] != "done"
65
+ ):
66
+
67
+ audio_str = base64.b64encode(
68
+ open(audio_answer_container["result"], "rb").read()
69
+ ).decode("utf-8")
70
+
71
+ history[-2]["content"] = gr.HTML(
72
+ f"""
73
+ <audio controls>
74
+ <source src="data:audio/mp3;base64,{audio_str}" type="audio/mp3">
75
+ </audio>
76
+ """
77
+ )
78
+
79
+ history[-2]["metadata"] = {"title": "🎧 音声返信", "status": "done"}
80
+
81
+ yield history
82
+ time.sleep(0.03)
83
+ while audio_thread.is_alive():
84
+ yield history
85
+ time.sleep(0.2)
86
+ audio_thread.join()
87
+ print("chat end")
88
+ if history[-2]["metadata"]["status"] != "done":
89
+ audio_str = base64.b64encode(open(audio_file_path, "rb").read()).decode("utf-8")
90
+ history[-2]["content"] = gr.HTML(
91
+ f"""
92
+ <audio controls>
93
+ <source src="data:audio/mp3;base64,{audio_str}" type="audio/mp3">
94
+ </audio>
95
+ """
96
+ )
97
+ history[-2]["metadata"] = {"title": "🎧 音声返信", "status": "done"}
98
+ yield history
99
+
100
+
101
+ def handle_feedback(like_data: gr.LikeData):
102
+ save_feedback(like_data.value[0], like_data.liked)
103
+
104
+
105
+ def get_reference_info(selected_reference):
106
+ reference_dict = knowledge_cls.reference_dict.get(selected_reference, None)
107
+ if reference_dict:
108
+ yield (
109
+ reference_dict["audio"],
110
+ reference_dict["original_text"],
111
+ reference_dict["summary"],
112
+ )
113
+ else:
114
+ yield (None, None, None)
115
+
116
+
117
+ def zip_directory():
118
+ zip_path = "resource.zip"
119
+ if os.path.exists(zip_path):
120
+ os.remove(zip_path)
121
+ shutil.make_archive(zip_path.replace(".zip", ""), "zip", "./resource")
122
+ return zip_path
123
+
124
+
125
+ custom_css = """
126
+ body {
127
+ background-color: #eef2f7;
128
+ margin: 0;
129
+ padding: 0;
130
+ font-family: "Microsoft YaHei", sans-serif;
131
+ }
132
+
133
+ #title-area {
134
+ text-align: center;
135
+ margin-top: 30px;
136
+ margin-bottom: 15px;
137
+ color: #333;
138
+ }
139
+
140
+ #chatbot-container, #references-container, #download-container {
141
+ max-width: 1100px;
142
+ margin: auto;
143
+ background: #f0f7ff;
144
+ }
145
+
146
+ #chatbot-container {
147
+ border-radius: 12px;
148
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
149
+ padding: 10px;
150
+ }
151
+
152
+ #references-container {
153
+ border-radius: 12px;
154
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
155
+ padding: 15px;
156
+ }
157
+
158
+ #submit-btn button {
159
+ background-color: #007bff;
160
+ color: white;
161
+ border-radius: 8px;
162
+ padding: 8px 12px;
163
+ }
164
+
165
+ #update-btn button {
166
+ background-color: #28a745;
167
+ color: white;
168
+ border-radius: 8px;
169
+ padding: 8px 12px;
170
+ }
171
+
172
+ h3 {
173
+ color: #444;
174
+ border-bottom: 2px solid #eee;
175
+ padding-bottom: 8px;
176
+ margin-bottom: 20px;
177
+ }
178
+ """
179
+ custom_head = """
180
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
181
+ <script src="https://unpkg.com/@dotlottie/player-component@2.7.12/dist/dotlottie-player.mjs" type="module"></script>
182
+ """
183
+
184
+ js_func = """
185
+ function refresh() {
186
+ const url = new URL(window.location);
187
+
188
+ if (url.searchParams.get('__theme') !== 'light') {
189
+ url.searchParams.set('__theme', 'light');
190
+ window.location.href = url.href;
191
+ }
192
+ }
193
+ """
194
+ with gr.Blocks(title="勉強会", css=custom_css, head=custom_head, js=js_func) as demo:
195
+
196
+ with gr.Column(elem_id="title-area"):
197
+ gr.HTML(
198
+ """
199
+ <div style="display: flex; align-items: center; justify-content: center;">
200
+ <img src="https://cdn.profile-image.st-hatena.com/users/saratoga623/profile.png?1728512391" style="width:50px; height:50px; margin-right:10px;">
201
+ <h1 style="margin: 0;">
202
+ <a href="https://saratoga623.hatenablog.com/" target="_blank" style="text-decoration: none; color: inherit;">
203
+ プロジェクトマネジメント勉強会 <i class='fa fa-lightbulb-o'></i>
204
+ </a>
205
+ </h1>
206
+ </div>
207
+ """
208
+ )
209
+ gr.HTML(
210
+ """
211
+ <div style="display: flex; align-items: center; justify-content: center;">
212
+ <div>
213
+ <p>経験上、プロジェクトマネジメントで大切だと思っている考え方で</p>
214
+ <p>疑問に答え、音声、参考資料が提供されます</p>
215
+ </div>
216
+ <dotlottie-player src="https://lottie.host/e2d675e6-73f4-4e92-a217-89bf1a4e49d3/cyif3Z9bwS.lottie"
217
+ background="transparent" speed="1" style="width: 150px; height: 150px" loop autoplay></dotlottie-player>
218
+ </div>
219
+ """
220
+ )
221
+
222
+ with gr.Column(elem_id="chatbot-container"):
223
+ gr.HTML("<h3><i class='fa fa-question-circle'></i> 何かお困りですか?</h3>")
224
+ chatbot = gr.Chatbot(
225
+ show_label=False,
226
+ type="messages",
227
+ bubble_full_width=False,
228
+ avatar_images=(
229
+ None,
230
+ "https://cdn.profile-image.st-hatena.com/users/saratoga623/profile.png?1728512391",
231
+ ),
232
+ )
233
+
234
+ with gr.Row(elem_id="submit-btn"):
235
+ msg = gr.Textbox(
236
+ placeholder="質問を入力してEnter...",
237
+ show_label=False,
238
+ scale=5,
239
+ container=False,
240
+ )
241
+ submit_btn = gr.Button(value="送信 🚀", scale=1)
242
+
243
+ clear_btn = gr.ClearButton([msg, chatbot], value="クリア 🧹", scale=1)
244
+
245
+ with gr.Accordion("ナレッジ一覧", elem_id="references-container", open=False):
246
+ with gr.Column():
247
+ with gr.Row():
248
+ gr.HTML("<h3><i class='fa fa-book'></i> ナレッジ一覧</h3>")
249
+ update_btn = gr.Button(value="🔄 更新", elem_id="update-btn")
250
+ references_dropdown = gr.Dropdown(
251
+ label="ナレッジを選ぶ",
252
+ choices=list(knowledge_cls.reference_dict.keys()),
253
+ value=None,
254
+ )
255
+ reference_audio = gr.Audio(
256
+ label="ナレッジ要約を聞く",
257
+ type="filepath",
258
+ interactive=False,
259
+ waveform_options=gr.WaveformOptions(
260
+ show_recording_waveform=False,
261
+ show_controls=False,
262
+ ),
263
+ )
264
+ sum_references_box = gr.Textbox(
265
+ label="ナレッジ要約",
266
+ lines=4,
267
+ interactive=False,
268
+ )
269
+ with gr.Accordion("ナレッジ详情", open=False):
270
+ all_references_box = gr.Markdown(max_height=250)
271
+
272
+ with gr.Accordion("資源", open=False, elem_id="download-container"):
273
+ download_btn = gr.Button("ダウンロード", elem_id="update-btn")
274
+ file_output = gr.File(show_label=False, container=False)
275
+
276
+ msg.submit(
277
+ fn=user_message_fn, inputs=[msg, chatbot], outputs=[msg, chatbot], queue=False
278
+ ).then(fn=answer_question, inputs=chatbot, outputs=chatbot)
279
+
280
+ submit_btn.click(
281
+ fn=user_message_fn, inputs=[msg, chatbot], outputs=[msg, chatbot], queue=False
282
+ ).then(fn=answer_question, inputs=chatbot, outputs=chatbot)
283
+ chatbot.like(handle_feedback, None, None, like_user_message=True)
284
+ update_btn.click(
285
+ fn=update_knowledge,
286
+ outputs=references_dropdown,
287
+ )
288
+
289
+ references_dropdown.change(
290
+ fn=get_reference_info,
291
+ inputs=references_dropdown,
292
+ outputs=[reference_audio, all_references_box, sum_references_box],
293
+ )
294
+
295
+ download_btn.click(fn=zip_directory, outputs=file_output)
296
+
297
+ # app
298
+ if __name__ == "__main__":
299
+ demo.launch(inline=False, share=False, debug=True)
blog_class.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import re
4
+ import time
5
+ from datetime import datetime
6
+ from urllib.parse import urljoin
7
+
8
+ import numpy as np
9
+ import requests
10
+ from bs4 import BeautifulSoup
11
+ from fish_audio_sdk import Session, TTSRequest
12
+ from openai import OpenAI
13
+ from tenacity import retry, stop_after_attempt, wait_exponential
14
+
15
+ # params
16
+ PKL_FILE = "./resource/knowledge_data.pkl"
17
+ INFO_audio_ID = os.getenv("INFO_audio_ID")
18
+ BASE_URL = "https://saratoga623.hatenablog.com/"
19
+
20
+ client = OpenAI(api_key=os.getenv("gpt"))
21
+
22
+ audio_client = Session(os.getenv("audio"))
23
+
24
+ SYS_Prompt = """
25
+ あなたは言語の専門家であり、さまざまな話し方や思考パターンを分析し、模倣し、適応することに長けています。
26
+ """
27
+
28
+ SUMMARY_Prompt = """
29
+ あなたは文章の内容を分析する専門家です。
30
+ 以下の文章の要約を作成してください。要約は100字以内で簡潔にまとめてください。
31
+ """
32
+
33
+ STYLE_Prompt = """
34
+ あなたは文章の内容を分析する専門家です。
35
+ 以下の文章内容を分析して、
36
+ 1. 原文の作者の文体を抽出してください。例:敬語、語気、構造、用語など代表的なもの
37
+ 2. 原文の作者の思考パターンを抽出してください。例:論理構成、推論、態度など代表的なもの
38
+ """
39
+ SEGMENT_Prompt = """
40
+ あなたは文章の内容を分析する専門家です。
41
+ 以下の文章を内容に基づいて意味的に段落に分割してください。各段落は独立した文章で、段落ごとに改行を入れてください。
42
+ """
43
+
44
+ QA_Prompt = """
45
+ ユーザーの質問に応じて表現を的確に調整し、自然でスムーズな対話を実現し、\
46
+ 検索された情報の内容、文脈、コミュニケーションスタイルに適した応答を簡潔に提供してください。
47
+
48
+ 【ユーザーの質問】
49
+ {q_text}
50
+ 【検索された情報】
51
+ {r_text}
52
+ 【タスク要求】
53
+ - 検索された情報を参考に、質問に会話の形で回答すること。
54
+ - 原文の作者の文体を模倣すること。
55
+ - 原文の思考パターンを模倣すること。
56
+ - 簡潔な回答を心がけること。3-5文程度で要点をまとめることを意識してください。
57
+ - 文章風ではなく、対話形式で答えること。
58
+ """
59
+ REWRITE_SYS_Prompt = """
60
+ Given a conversation (between Human and Assistant) and a follow up message from Human, \
61
+ rewrite the message to be a standalone question that captures all relevant context \
62
+ from the conversation.
63
+ """
64
+ REWRITE_Prompt = """
65
+ <Chat History>
66
+ {chat_history}
67
+
68
+ <Follow Up Message>
69
+ {question}
70
+
71
+ <Standalone question>
72
+ """
73
+ QA_chat_Prompt = """
74
+ ユーザーの最新質問に応じて表現を的確に調整し、自然でスムーズな対話を実現し、\
75
+ 検索された情報の内容、文脈、コミュニケーションスタイルに適した応答を簡潔に提供してください。
76
+ 【ユーザーの会話履歴】
77
+ {h_text}
78
+ 【ユーザーの最新質問】
79
+ {q_text}
80
+ 【検索された情報】
81
+ {r_text}
82
+ 【タスク要求】
83
+ - 検索された情報内容を参考に、質問に会話の形で回答すること。
84
+ - 原文の作者の文体を模倣すること。
85
+ - 原文の作者の思考パターンを模倣すること。
86
+ - 簡潔な回答を心がけること。3-5文程度で要点をまとめることを意識してください。
87
+ - 文章風ではなく、対話形式で答えること。
88
+ """
89
+
90
+
91
+ def load_pkl(file_path):
92
+ if os.path.exists(file_path):
93
+ with open(file_path, "rb") as f:
94
+ return pickle.load(f)
95
+ else:
96
+ return {}
97
+
98
+
99
+ def save_pkl(file_name, data):
100
+ with open(file_name, "wb") as f:
101
+ pickle.dump(data, f)
102
+
103
+
104
+ def get_page_content(page_url):
105
+ response = requests.get(page_url)
106
+ response.encoding = response.apparent_encoding
107
+ return response.text
108
+
109
+
110
+ def parse_homepage(html):
111
+
112
+ soup = BeautifulSoup(html, "html.parser")
113
+ articles = soup.find_all("article", class_="entry")
114
+ blog_infos = []
115
+ for article in articles:
116
+ title_tag = article.find("h1", class_="entry-title")
117
+ if title_tag and title_tag.find("a"):
118
+ link = urljoin(BASE_URL, title_tag.find("a")["href"])
119
+ else:
120
+ continue
121
+ # <time> タグ内から公開日を取得する
122
+ time_tag = article.find("time")
123
+ if time_tag:
124
+ year_tag = time_tag.find("span", class_="date-year")
125
+ month_tag = time_tag.find("span", class_="date-month")
126
+ day_tag = time_tag.find("span", class_="date-day")
127
+ if year_tag and month_tag and day_tag:
128
+ pub_date = f"{year_tag.get_text(strip=True)}-{month_tag.get_text(strip=True)}-{day_tag.get_text(strip=True)}"
129
+ else:
130
+ pub_date = "unknown_date"
131
+ else:
132
+ pub_date = "unknown_date"
133
+ blog_infos.append({"date": pub_date, "link": link})
134
+ return blog_infos, soup
135
+
136
+
137
+ def get_next_page_url(soup):
138
+ next_page_tag = soup.find("a", string="次のページ")
139
+ if next_page_tag and next_page_tag.has_attr("href"):
140
+ return urljoin(BASE_URL, next_page_tag["href"])
141
+ return None
142
+
143
+
144
+ def sort_key(info):
145
+ try:
146
+ return datetime.strptime(info["date"], "%Y-%m-%d")
147
+ except Exception:
148
+ return datetime.max
149
+
150
+
151
+ def fetch_blog_text(url):
152
+ response = requests.get(url)
153
+ response.encoding = response.apparent_encoding
154
+ soup = BeautifulSoup(response.text, "html.parser")
155
+
156
+ title_tag = soup.find("h1", class_="entry-title")
157
+ title = title_tag.get_text(strip=True) if title_tag else "no_title"
158
+
159
+ content_tag = soup.find("div", class_="entry-content")
160
+ if not content_tag:
161
+ content_tag = soup.find("div", class_="hatenablog-entry")
162
+ if content_tag:
163
+
164
+ for a in content_tag.find_all("a", class_="keyword"):
165
+ a.unwrap()
166
+ content = content_tag.get_text(strip=False).strip()
167
+ else:
168
+ content = ""
169
+ return title, content
170
+
171
+
172
+ def sanitize_filename(filename):
173
+ return re.sub(r'[\\/*?:"<>|]', "", filename)
174
+
175
+
176
+ def max_cosine_similarity(v1, v2_list):
177
+ """ """
178
+ v1 = np.array(v1)
179
+ v2_list = np.array(v2_list)
180
+
181
+ norm_v1 = np.linalg.norm(v1)
182
+ norm_v2 = np.linalg.norm(v2_list, axis=1)
183
+
184
+ if norm_v1 == 0:
185
+ return 1.0 if np.any(norm_v2 == 0) else 0.0
186
+
187
+ valid_indices = norm_v2 > 0
188
+ similarities = np.full(v2_list.shape[0], -1.0)
189
+ if np.any(valid_indices):
190
+ similarities[valid_indices] = np.dot(v2_list[valid_indices], v1) / (
191
+ norm_v1 * norm_v2[valid_indices]
192
+ )
193
+ return np.max(similarities)
194
+
195
+
196
+ @retry(
197
+ stop=stop_after_attempt(5),
198
+ wait=wait_exponential(multiplier=1, min=2, max=10),
199
+ )
200
+ def generate_item(prompt, sys_prompt, model="gpt-4o-mini"):
201
+ response = client.chat.completions.create(
202
+ model=model,
203
+ messages=[
204
+ {
205
+ "role": "system",
206
+ "content": sys_prompt,
207
+ },
208
+ {"role": "user", "content": prompt},
209
+ ],
210
+ )
211
+ return response.choices[0].message.content.strip()
212
+
213
+
214
+ @retry(
215
+ stop=stop_after_attempt(5),
216
+ wait=wait_exponential(multiplier=1, min=2, max=10),
217
+ )
218
+ def get_embedding(text):
219
+ response = client.embeddings.create(input=text, model="text-embedding-3-small")
220
+ return [emb.embedding for emb in response.data]
221
+
222
+
223
+ @retry(
224
+ stop=stop_after_attempt(5),
225
+ wait=wait_exponential(multiplier=1, min=2, max=10),
226
+ )
227
+ def audio_text(text, audio_path):
228
+ with open(audio_path, "wb") as f:
229
+ for chunk in audio_client.tts(
230
+ TTSRequest(reference_id=INFO_audio_ID, text=text)
231
+ ):
232
+ f.write(chunk)
233
+ return audio_path
234
+
235
+
236
+ def save_feedback(value, liked):
237
+ if liked:
238
+ md_text = "text:\n" + value + "\n" + "liked"
239
+ else:
240
+ md_text = "text:\n" + value + "\n" + "disliked"
241
+ timestamp = int(time.time() * 1000)
242
+ md_filename = f"./resource/feedback_Text_{timestamp}.md"
243
+ with open(md_filename, "w", encoding="utf-8") as file:
244
+ file.write(md_text)
245
+
246
+
247
+ class knowledge_class:
248
+ def __init__(self):
249
+ self.knowledge_data = load_pkl(PKL_FILE)
250
+ self.reference_dict = self.get_reference_dict()
251
+ # q_v = self.knowledge_data["2024-10-09-プロジェクト計画で重要視すること"][
252
+ # "vector"
253
+ # ][0]
254
+ # t_v = self.knowledge_data["2024-10-09-プロジェクト計画で重要視すること"][
255
+ # "vector"
256
+ # ]
257
+
258
+ # cos_sim = max_cosine_similarity(q_v, t_v)
259
+ # print(cos_sim)
260
+
261
+ def get_reference_dict(self):
262
+ reference_dict = {}
263
+ for ref_name, ref_dict in self.knowledge_data.items():
264
+ title = ref_dict["title"]
265
+ if ref_name not in reference_dict:
266
+ reference_dict[ref_name] = {}
267
+ reference_dict[ref_name]["original_text"] = (
268
+ f"### {title}\n"
269
+ + ref_dict["text"]
270
+ + f"\n\n[URL]({ref_dict['url']})"
271
+ )
272
+ reference_dict[ref_name]["summary"] = ref_dict["summary"]
273
+ reference_dict[ref_name]["audio"] = ref_dict["audio"]
274
+ else:
275
+ print(f"overlap ref_name: {ref_name}")
276
+ raise
277
+ return reference_dict
278
+
279
+ def get_new_knowledge(self):
280
+ print(f"[{datetime.now()}] 記事の検出を開始します...")
281
+ processed_urls = set()
282
+ for _, v in self.knowledge_data.items():
283
+ processed_urls.add(v.get("url", ""))
284
+ new_blog_infos = []
285
+ page_url = BASE_URL
286
+ while page_url:
287
+ try:
288
+ html = get_page_content(page_url)
289
+ except Exception as e:
290
+ print(f"{page_url} の取得時にエラーが発生しました: {e}")
291
+ break
292
+ infos, soup = parse_homepage(html)
293
+ new_infos = [info for info in infos if info["link"] not in processed_urls]
294
+ new_blog_infos.extend(new_infos)
295
+ page_url = get_next_page_url(soup)
296
+
297
+ if not new_blog_infos:
298
+ print("新しい記事は見つかりませんでした。")
299
+ else:
300
+ print(
301
+ f"新規記事 {len(new_blog_infos)} 件を検出しました。処理を開始します..."
302
+ )
303
+ new_blog_infos.sort(key=sort_key)
304
+
305
+ for info in new_blog_infos:
306
+ blog_url = info["link"]
307
+ pub_date = info["date"]
308
+ print(f"記事を処理中: {blog_url}")
309
+ try:
310
+ title, content = fetch_blog_text(blog_url)
311
+ key_name = f"{pub_date}-{sanitize_filename(title)}"
312
+ print("記事:", key_name)
313
+ summary_text = generate_item(content, SUMMARY_Prompt)
314
+ style_text = generate_item(content, STYLE_Prompt)
315
+ segment_texts = [
316
+ seg.strip()
317
+ for seg in generate_item(content, SEGMENT_Prompt).split("\n\n")
318
+ if seg.strip()
319
+ ]
320
+ texts_vector = get_embedding([title, summary_text] + segment_texts)
321
+ audio_path = f"./resource/{key_name}.mp3"
322
+ audio_path = audio_text(summary_text, audio_path)
323
+ dict_item = {
324
+ key_name: {
325
+ "title": title,
326
+ "text": content,
327
+ "url": blog_url,
328
+ "style": style_text,
329
+ "summary": summary_text,
330
+ "audio": audio_path,
331
+ "segments": segment_texts,
332
+ "vector": texts_vector,
333
+ }
334
+ }
335
+
336
+ except Exception as e:
337
+ print(f"{blog_url} の処理中にエラーが発生しました: {e}")
338
+ raise
339
+
340
+ self.knowledge_data.update(dict_item)
341
+ time.sleep(1)
342
+ save_pkl(PKL_FILE, self.knowledge_data)
343
+ time.sleep(1)
344
+ self.knowledge_data = load_pkl(PKL_FILE)
345
+ self.reference_dict = self.get_reference_dict()
346
+ time.sleep(1)
347
+
348
+ print(f"PKLファイルの更新が完了しました。新規記事数: {len(new_blog_infos)}")
349
+
350
+ def find_top_info(self, question_vector):
351
+ results = []
352
+ for idx, k_dict in self.knowledge_data.items():
353
+ idx_vector = k_dict.get("vector", [0])
354
+ cos_sim = max_cosine_similarity(question_vector, idx_vector)
355
+ results.append((idx, cos_sim))
356
+
357
+ results_sorted = sorted(results, key=lambda x: x[1], reverse=True)
358
+ top2 = []
359
+ retrieve_text = "\n"
360
+ retrieve_title = ""
361
+ for res_sort in results_sorted:
362
+ if res_sort[0] not in top2:
363
+ top2.append(res_sort[0])
364
+ retrieve_text += f"情報 {len(top2)} : \n"
365
+ retrieve_text += (
366
+ f"- タイトル:{self.knowledge_data[res_sort[0]]['title']} \n"
367
+ )
368
+ retrieve_title += f"{len(top2)}. {res_sort[0]} \n"
369
+ retrieve_text += (
370
+ f"- コンテンツ:\n {self.knowledge_data[res_sort[0]]['text']} \n"
371
+ )
372
+ retrieve_text += f"- 作者の文体と思考パターン:\n {self.knowledge_data[res_sort[0]]['style']} \n"
373
+ retrieve_text += f"- 質問と類似度:{res_sort[1]} \n"
374
+ retrieve_text += "\n"
375
+ if len(top2) > 1:
376
+ break
377
+ return retrieve_text, retrieve_title
378
+
379
+ def get_answer(self, question_text):
380
+
381
+ # get similar info
382
+ question_vector = get_embedding(question_text)[0]
383
+ info_text, info_title = self.find_top_info(question_vector)
384
+ user_prompt = QA_Prompt.format(q_text=question_text, r_text=info_text)
385
+ answer_text = generate_item(user_prompt, SYS_Prompt, model="gpt-4o")
386
+ md_text = user_prompt + "\n 応答: \n" + answer_text
387
+ timestamp = int(time.time() * 1000)
388
+ md_filename = f"./resource/QA_Text_{timestamp}.md"
389
+ audio_filename = f"./resource/QA_Audio_{timestamp}.mp3"
390
+ with open(md_filename, "w", encoding="utf-8") as file:
391
+ file.write(md_text)
392
+ return answer_text, info_title, audio_filename
393
+
394
+ def get_chat_answer(self, chat_list):
395
+ print("all chat list:", chat_list)
396
+ chat_history = []
397
+ for turn in chat_list:
398
+ if turn["role"] == "assistant" and turn["metadata"]:
399
+ continue
400
+ else:
401
+ chat_history.append(turn)
402
+ chat_history = chat_history[-5:]
403
+ if chat_history[-1]["role"] != "user" or (not chat_history[-1]["content"]):
404
+ return None, None, None
405
+ if len(chat_history) == 1:
406
+ answer_text, info_title, audio_filename = self.get_answer(
407
+ chat_history[-1]["content"]
408
+ )
409
+ return answer_text, info_title, audio_filename
410
+ # get history
411
+ chat_history_str = ""
412
+ for msg in chat_history[:-1]:
413
+ chat_history_str += (
414
+ "\n" if chat_history_str else ""
415
+ ) + f"{msg['role']}: {msg['content']}"
416
+ print("chat_history_str:", chat_history_str)
417
+ new_query = chat_history[-1]["content"]
418
+ print("new_query:", new_query)
419
+
420
+ rw_prompt = REWRITE_Prompt.format(
421
+ chat_history=chat_history_str, question=new_query
422
+ )
423
+
424
+ if "test" in chat_history[-1]["content"]:
425
+ return (
426
+ f"test_ui:{chat_history[-1]['content']}",
427
+ "1. info_text1 \n 2. info_text2 \n",
428
+ "./resource/2025-03-03-機嫌良く働くと仕事は上手く進む.mp3",
429
+ )
430
+
431
+ rewirte_question = generate_item(rw_prompt, REWRITE_SYS_Prompt, model="gpt-4o")
432
+ print("rewirte_question:", rewirte_question)
433
+ # prompt = DEFAULT_TEMPLATE.format(chat_history=chat_history, question=query)
434
+ # get rewrite question
435
+ # get similar info
436
+ question_vector = get_embedding(rewirte_question)[0]
437
+ info_text, info_title = self.find_top_info(question_vector)
438
+ user_prompt = QA_chat_Prompt.format(
439
+ h_text=chat_history_str, q_text=rewirte_question, r_text=info_text
440
+ )
441
+ answer_text = generate_item(user_prompt, SYS_Prompt, model="gpt-4o")
442
+ md_text = user_prompt + "\n 応答: \n" + answer_text
443
+ timestamp = int(time.time() * 1000)
444
+ md_filename = f"./resource/QA_Text_{timestamp}.md"
445
+ audio_filename = f"./resource/QA_Audio_{timestamp}.mp3"
446
+ with open(md_filename, "w", encoding="utf-8") as file:
447
+ file.write(md_text)
448
+ return answer_text, info_title, audio_filename
449
+
450
+
451
+ # kc_class = knowledge_class()
452
+ # print(kc_class.get_new_knowledge())
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ # zyphra
4
+ tenacity
5
+ beautifulsoup4
6
+ fish-audio-sdk
resource/2024-10-09-プロジェクト計画で重要視すること.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b7683d2a43cbeac7888aa3c0e7ffde46be1c358a5570e67835ed54b7431c35f7
3
+ size 387866
resource/2024-10-10-プロジェクト計画時点でユーザーと握ること.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:877e099d232c1b65ccdc5bad4f341ae2c8106cec8d62d063caccab123f44c02d
3
+ size 459755
resource/2024-10-11-全員が同じベクトルを向くために.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:26b683d71496c8bfd82df704f1573acd1bb4d65c85896d9484d64ae41bbbbd18
3
+ size 427990
resource/2024-10-12-PMはバランスを取る仕事.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a0f41c346ab19723446bf782c4c49729b17993a460d6c74aeab94cde260c761
3
+ size 478981
resource/2024-10-15-PMはコミュニケーションが大事です.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0df2921d026d1a359762a5066c65b2767068ef34858c0055cad1d77872cdaf92
3
+ size 451813
resource/2024-10-17-進捗報告は定量的に.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:60b350f070de3f590c296e3ff6080c138936ef53a3226fc20cd6df77401b7216
3
+ size 450142
resource/2024-10-18-進捗報告から真実を掴むには.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:689b1a8971c2323e9dcc9623c4424203b3558917a0ef6b343f85e1b6cbb6faf7
3
+ size 469368
resource/2024-10-19-報告は多少疑うべし.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b6338670de9426f6c652ad97156b92cc58af712a3dfc2ead2375991d187f32f4
3
+ size 462680
resource/2024-10-21-心配事がリスクです.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e7df6358c906ff9feddae99f2c082401c7705ab96bb6d30d15393577e7c0d106
3
+ size 351921
resource/2024-10-22-適切なリスク計上のコツ.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fa7113ac3ab1528cab5ea2f3f485dd13c641a77b4bf3ad7b82238c45a4d9ca3c
3
+ size 363206
resource/2024-10-23-確実にリスク対策するために.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:07bf318bd874b4a1ce3391b1a5acc71ba69ecef4546016648b07397be7a32901
3
+ size 340218
resource/2024-10-24-リスクが顕在化した時の為にやっておくこと.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a9929731d716d857f588f7ead9d94260329b0749026002dd9a5fe220a228455d
3
+ size 418377
resource/2024-10-25-前提をリスクで管理する.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:021a07cc4025677192403f415f88c50317baa30d4e0539f364e98cba7e1f2b63
3
+ size 447216
resource/2024-10-26-言い訳をリスクで管理する.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1ac122cd688d79ad7e3cf6569b8fd156e2ad2352fed1ac7871efe0fc8fa7ef4c
3
+ size 332695
resource/2024-10-27-リスクを棚卸しする.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e0380b69bb7802ff46f02f243c0474a5c13623246074a5cfb6ec3f0e22299e68
3
+ size 359444
resource/2024-10-28-課題の対策を考える.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d56a6f13943fdccbb953427737f9d7da4cfced3f1b782259dcdefb111a70ed0a
3
+ size 393717
resource/2024-10-29-課題の早期発見に向けて心がけること.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0a55eaf799452cd8eeb9d4432495f978ed794b18a4836a75a4ebeb03b2d30f23
3
+ size 443454
resource/2024-10-30-現実的な対策を考える.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:02e32622eec4b6e8aa96ffe7d1a0a810594b6f5fcd573d0ed89ce7b7fd0cbc47
3
+ size 532062
resource/2024-10-31-報告の目的って何?.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:413fb10503a6ee67db413a0f9524c2c325794ecda741d15e5b47338915a099dc
3
+ size 566752
resource/2024-11-01-原因と真因.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d600f5bc08799134326da48bc54bb9641779bec47e529d9aa8bdf1517b6ed57e
3
+ size 354847
resource/2024-11-02-なぜを5回繰り返してもダメな理由.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c47bbc5846a701eb303b8839cb228d31a9485512a8e92f996fed12294cee8be
3
+ size 341472
resource/2024-11-03-真因分析の極意.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ceea59afe99cb1d233f2cc5a35da9d5e98c0b70b82b14d217217559f7056e5a
3
+ size 341054
resource/2024-11-05-対策検討の極意.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7aa29e7c6c92e693f679a84db7be50e318774cf65c0c920a52fb440d1f2bde90
3
+ size 371565
resource/2024-11-06-プロジェクトは性悪説がちょうどいい.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4866f65c0fc02893caad25e540789b76e3d3c43793a4c0ea969c401132cdddb5
3
+ size 432169
resource/2024-11-07-コスト管理でよくある話.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2b2aeb9e6206267a90b8c4b3c65d3d5a3ccd42b31c24f41bb3df50e2390a2400
3
+ size 417541
resource/2024-11-08-手戻りはもったいない.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3319adaac8419d57b1ea92675c7443bfaec10ffb250483a73ea05bc1d23268b9
3
+ size 363206
resource/2024-11-09-責任って何?.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:591c5ddf4ccda6df6274a1d61279b99f9967ffa51714a37ddf16d5c32cb7cbdd
3
+ size 419631
resource/2024-11-10-早く開発するためには.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:751232225cf687bd6643c27d37f059606c8ba03c6ea810eb7a3a39cdbb9deac3
3
+ size 383268
resource/2024-11-11-変更管理の要諦.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:814aa9ef7503e9397aafcd796a607093c5c47fb4a1412b165920614e34435974
3
+ size 419631
resource/2024-11-12-立ち止まる勇気.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:35f27fb560a5a68361baac4bd8a6edb86dc457443193b058c876403cc4fbd652
3
+ size 429244
resource/2024-11-13-テストの目的.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:378667439071712bdbc6311e1e1733239b903990a4c46d3116ef0e28f5c763df
3
+ size 413779
resource/2024-11-14-テスト計画のコツ.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d87dfecca48dddfd14770cca39e79375056381a06fb1d2006c3309b205a92886
3
+ size 349831
resource/2024-11-15-雛形の必要性と弊害.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7a267e3b27399e305fa59823ef1fa3e1ba6c66e3cb6a3d3893484c5ed5b8ab59
3
+ size 533315
resource/2024-11-16-個人を主語にするステークホルダーマネジメント.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c5e283edea25048840152854628bf9df614b4c4d2785cbdb63be2ce018bfd42e
3
+ size 381596
resource/2024-11-17-議論する組織を目指す.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e778c62045eccf8a523d32368830c06f425a3ec1cac67aecaf2fa38be0f6061
3
+ size 398733
resource/2024-11-18-問題を問題と言える風土.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6cc92081b4d3fd42c206781043bb31d2a42afdba01c656c42fb0d1c217d8f05f
3
+ size 345234
resource/2024-11-19-保守工程のことを意識する.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d44f8eb264c287704c2058b9328846b058aa033e5e0bae9c71b9ae17c67eec56
3
+ size 401658
resource/2024-11-20-本番実施計画も性悪説が基本です.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b541fbaf1989b79d7b0b674a4b9ecde49e5d254968e02d9b6ff0bbca40ee612c
3
+ size 406256
resource/2024-11-21-本番確認のレベル感を決める観点.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1ebef11621b3b80e06912022d42b9f45355cccb11670bb9d9b715a9fa782f3a0
3
+ size 476055
resource/2024-11-22-失敗のパターンを持っておく.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:52f2e0a8c464cc7a2d35c765c08352c8e7cbee964b14cf8187fb0938fd45e741
3
+ size 337293
resource/2024-11-23-規模の小さいお仕事で基礎・基本を身につける.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:925c2514fffcde5b64c553dbf992d6b36a8f4457bfbf77499912999f877abfd4
3
+ size 361116
resource/2024-11-24-極力マルチタスクを作らない計画.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4da8741bf47da797a160af970d67b5c4a8177f900884cf81ffbd2b3836395bf9
3
+ size 473965
resource/2024-11-25-「頑張ります」を適切に使い、正しく解釈する.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:67940e9700a5bb82618b9def888d724da18aefa1eb4c58a113f3b03aba425ca2
3
+ size 478981
resource/2024-11-26-基本設計の品質がプロジェクト成功の鍵.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6e6cf764ff4449a6be3a33e6e0c92f3f2dfaa069c068e0f71ad8f11adef60935
3
+ size 380342
resource/2024-11-27-文章を使ってプロジェクトを成功に導く.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9a0eb4054fbfc33baee0ea523e94aaaba5560a93d7e1c19d613983f3bc83bdd7
3
+ size 489430
resource/2024-11-28-作戦を伝えることがプロジェクトをスムーズに進める鍵.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c628fd8f82c01506f61e86d36d6c2a3c4a4d300a320038076da5856f8ababb12
3
+ size 438439