OmniAICreator/Qiita-1.07M huggingface dataset:
Cursor × Claude 4：User Rulesの設定と活用チュートリアル

<h2>はじめに：AIとの開発をもっと賢く</h2>
<p>
Cursorは、AnthropicのClaude 4（OpusおよびSonnet）を統合したAIネイティブなコードエディタです。<strong>「User Rules」</strong>を活用することで、AIの出力をプロジェクトやチームのスタイルに合わせてカスタマイズできます。この記事では、User Rulesの設定方法と活用例を紹介し、AIとの協働をより効果的にする方法を解説します。
</p>

<h3>背景：CursorとClaude 4の進化</h3>
<p>
Claude 4は、前世代のモデルに比べて、コード生成やリファクタリング、テスト生成などのタスクで大幅な精度向上を実現しています。Cursorは、これらのモデルを統合し、開発者がエディタ内で直接AIと対話できる環境を提供しています。
</p>

<h3>課題：AIの出力をプロジェクトに合わせる</h3>
<p>
AIが生成するコードがプロジェクトのコーディングスタイルや設計方針と一致しないことがあります。例えば、TypeScriptプロジェクトでany型を避けたい場合や、特定のディレクトリ構造を維持したい場合など、AIの出力を制御する必要があります。
</p>

<h3>解決策：User Rulesの設定と活用</h3>
<p>
Cursorでは、<strong>User Rules</strong>を設定することで、AIの出力を制御できます。以下に、User Rulesの設定方法と活用例を紹介します。
</p>

<h4>1. User Rulesの設定方法</h4>
<ol>
  <li>Cursorの設定を開きます。</li>
  <li>「Rules for AI」セクションに移動します。</li>
  <li>グローバルなルールを記述します。</li>
</ol>

<h4>2. プロジェクト固有のルール（.cursorrulesファイル）の作成</h4>
<ol>
  <li>プロジェクトのルートディレクトリに<strong>.cursorrules</strong>ファイルを作成します。</li>
  <li>以下のようなルールを記述します。</li>
</ol>

<pre><code>
---
description: TypeScriptプロジェクトのコーディングガイドライン
globs: ["**/*.ts", "**/*.tsx"]
alwaysApply: true
---

- any型の使用を避け、明確な型定義を行う。
- 関数は可能な限り純粋関数として実装する。
- ファイル名はキャメルケースを使用する。
</code></pre>

<h4>3. ルールの活用例</h4>
<p>以下に、具体的なユースケースごとのUser Rulesの例を示します。</p>

<h5>ユースケース1：Reactコンポーネントのスタイル統一</h5>
<pre><code>
---
description: Reactコンポーネントのスタイルガイド
globs: ["src/components/**/*.tsx"]
alwaysApply: true
---

- スタイリングにはTailwind CSSを使用する。
- アニメーションにはFramer Motionを使用する。
- コンポーネント名はPascalCaseを使用する。
</code></pre>

<h5>ユースケース2：APIエンドポイントのバリデーション</h5>
<pre><code>
---
description: APIエンドポイントのバリデーションルール
globs: ["src/api/**/*.ts"]
alwaysApply: true
---

- リクエストボディのバリデーションにはZodを使用する。
- エラーハンドリングは共通のエラーハンドラを使用する。
- レスポンスには一貫した構造を持たせる。
</code></pre>

<h5>ユースケース3：ユニットテストの記述スタイル</h5>
<pre><code>
---
description: ユニットテストの記述スタイル
globs: ["tests/**/*.test.ts"]
alwaysApply: true
---

- テストフレームワークにはJestを使用する。
- 各テストケースは「should」で始まる説明的な名前を付ける。
- モックにはjest.mockを使用し、副作用を避ける。
</code></pre>

<h3>ベストプラクティスと注意点</h3>
<ul>
  <li><strong>ルールは具体的かつ簡潔に記述する</strong>：抽象的な表現は避け、明確な指示を与えましょう。</li>
  <li><strong>ルールのスコープを明確にする</strong>：特定のファイルやディレクトリに適用する場合、globsを適切に設定しましょう。</li>
  <li><strong>ルールの適用を確認する</strong>：AIがルールを正しく適用しているかを定期的に確認し、必要に応じて修正しましょう。</li>
  <li><strong><span style="color:red;">注意</span></strong>：AIがルールを無視する場合があります。特に、複雑なルールや曖昧な指示は避けましょう。</li>
</ul>

<h3>まとめと今後の展望</h3>
<p>
CursorのUser Rulesを活用することで、AIの出力をプロジェクトやチームのスタイルに合わせてカスタマイズできます。今後、より高度なルール設定や、プロジェクトごとのテンプレート化など、さらなる活用が期待されます。AIとの協働をより効果的にするために、User Rulesを積極的に活用していきましょう。
</p>
Pythonで別ファイルの関数を使う方法

## はじめに

非プログラマーでありながら、2024年にMicrosoft主催のCWBJの力と勢いを借りて、人生初のPythonアプリを完成させました。
 
あれから１年。非プログラマーなのは相変わらずですが、当時よりAIやクラウドといった言葉が身近な環境に身を置いています。忙しさにかまけてCWBJの進捗は芳しくありませんが、１年前に学んだことが実務でかなり生きています。今の状況を当時はまったく予測していませんでした。人生何があるか分かりません。やっておいて良かったCWBJ。

## 本記事の趣旨
プログラミング初心者である自分が頭に叩き込みたい「関数のインポート」について
 
## Pythonで別ファイルの関数を実行する方法
import関数をつかう。

例えば、circle.py　というファイルの中身が下記の場合
~~~circle.py
def calc_area(r):
    return r**2**3.14
~~~
circle.pyファイルの中にあるcalc_area関数を呼び出す
~~~関数を使う.py
import circle

#半径3の円の面積
area = circle.calc_area(3)

print(area)
~~~

または、fromキーワードでファイル名を指定しての記述も可能
~~~関数を使う2.py
from circle import calc_area

#半径3の円の面積
area = calc_area(3) #関数を呼び出す際の circleが不要になる

print(area)
~~~

## おわりに
初心者なので「どちらの書き方でもお好みで」ということなのかしら、と思っていますが、処理速度や安定性に違いがあるのかな？ご存知の方はコメントください。

ただ、ひとつのプログラム内で併記されるのは可読性を損なう気がします。自分はこっちでいく、というのを決めておく必要があると思います。
メトリック・フィールド [変数]は数値でなければなりません(SPSS Modeler データ加工逆引き12-12)

# メトリック・フィールド [変数]は数値でなければなりません（時系列モデル）

![スクリーンショット 2025-05-19 10.17.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/e558aeaf-5ae9-463b-bd0a-d01c2d6c1395.png)

エラーメッセージ：AEQTD0012E: メトリック・フィールド 大学講義 は数値でなければなりません

## 1.想定される利用目的

・時系列モデル作成時にエラーが起きる

*入力変数であるイベント有無を文字列の状態でモデリングした場合に生じるエラーのハンドリング

## 2.サンプルストリームのダウンロード

https://github.com/yoichiro0903n/blue/raw/refs/heads/main/trendmodel_event_ng.str

## 3.サンプルストリームの説明

![スクリーンショット 2025-05-19 10.27.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/3e538fb9-21fa-4c6e-a8d5-8e1fa5a45676.png)


a.入力するデータは以下のとおりです。予測対象は[実績]。時間間隔は[日付]それ以外のフィールドを入力変数にしますが、[大学講義]と[特売]の有無はセル左詰で文字列になっていることがわかります。

![スクリーンショット 2025-05-19 10.29.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/c9b06c6b-c3ac-4329-b708-e01371e2ee03.png)

#### エラーの現象を確認する

b.[データ型]ノードを編集します。予測に影響を与えるフラグデータを[イベントおよび干渉]に投入しています。Ver18.6のMac版の場合、この画面で投入そのものができないように矢印が無効になっています。

![スクリーンショット 2025-05-19 10.30.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/dba741cb-e64a-4d19-9abb-fc02fa057f1f.png)

[実行]します。エラーが以下のように表示されます。

![スクリーンショット 2025-05-19 10.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/c0b13227-e79c-45b1-90bd-ee1dcf07c9a9.png)

#### データのストレージを文字列から数値に変更する

c.[置換]ノードを編集します。@&#xFFF0;FIELDはワイルドカードです。複数のフィールドを同時に処理することが可能です。

![スクリーンショット 2025-05-19 10.31.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/dc2368be-16ea-4104-a7d4-126821b87375.png)

d.[時系列モデル]ノードを編集します。[大学講義]と[特売]がフラグ型から連続型へアイコンが変化しています。

![スクリーンショット 2025-05-19 10.31.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/1f497d8a-2e6c-4113-9f22-23e52ed6483f.png)


e.[時系列モデル]ナゲットを編集します。モデルが[大学講義]と[特売]を考慮していることが確認できます。

![スクリーンショット 2025-05-19 10.31.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/31479222-65b3-4992-a482-9c2387cc2c9f.png)

#### データのストレージを数値にしたまま[フラグ型]にしてもモデル作成ができる

f.[データ型]ノードを編集します。強制的に[大学講義]と[特売]をフラグ型にしてみます。

![スクリーンショット 2025-05-19 10.32.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/58bb0cad-c57d-4611-af7e-cf0253ae8e0e.png)

g.[時系列モデル]ノードを実行します。

h.[時系列モデル]ナゲットがeと同じ内容で生成されます。


#### 注意点

SPSS Modelerはデータタイプを[ストレージ]としてモデルに投入する場合の[尺度]とは別に
定義しています。今回は[ストレージ]が文字列であったことに起因してエラーが生じました。

![スクリーンショット 2025-05-19 10.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/1254c867-3f52-47af-bfee-88df208442e8.png)


## 4.参考情報

#### 時系列モデル

https://qiita.com/416nishimaki/items/f7842af5af1d6410526d

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/private/4bf813e6cba84b2a1008
Cloud Run JobsのエラーをSlackに通知する

## 1. Slack 通知チャネルを準備する

1. GCP コンソール → **Notification channels** を開く
2. **Slack** セクションで **Add new** をクリックし、ワークスペースとチャンネルを認可・登録する

## 2. Logs Explorer でクエリを作成

1. GCP コンソール → **Logs Explorer** を開く
2. クエリ欄に以下を入力し、Run で結果を確認

   ```plain
   resource.type="cloud_run_job"
   severity="ERROR"
   (
       resource.labels.job_name="your-cloud-run-jobs-name"
   )
   ```

   ORを使って、複数のCloud Run Jobsを選択することもできます
   ```plain
   resource.type="cloud_run_job"
   severity="ERROR"
   (
       resource.labels.job_name="your-cloud-run-jobs-name-1"
       OR resource.labels.job_name="your-cloud-run-jobs-name-2"
   )
   ```

   自動生成ログを除外したい場合は、以下のようにlogNameを使って絞り込めます
   ```plain
   resource.type="cloud_run_job"
   severity="ERROR"
   (
       resource.labels.job_name="your-cloud-run-jobs-name"
   )
   logName="projects/your-project-id/logs/run.googleapis.com%2Fstdout"
   ```

## 3. ログベースのアラートを作成

1. Logs Explorer の結果画面上部、**操作** メニュー → **ログアラートの作成** を選択

2. **Alert details** ペインで以下を設定：

   | 項目 | 設定例 |
   | ---- | ---- |
   | **Alert Policy Name** | `Your Alert Policy Name` |
   | **Policy severity** | `Error` |
   | **Documentation** | - **Markdown** を選択<br>- **Add documentation** に以下を記述： |

   ```markdown
   *Job*: `${resource.labels.job_name}`  
   *Error*: `${log.extracted_label.error_message}`  
   ```

   ※`${log.extracted_label.error_message}` は次のステップで抽出するログ本文です

3. **Choose logs to include in the alert** 画面で、先ほどのクエリがそのまま入っていることを確認

4. **Extract log labels** を使って、ログ本文を取り出すラベルを追加

   **ラベルを追加** ボタンをクリック

   | 項目 | 設定内容 |
   | ---- | ---- |
   | **Display name** | `error_message` |
   | **Log field name** | `textPayload` |
   | **正規表現** | `^(.*)$`（全文キャプチャ）|

5. **Set notification frequency and autoclose duration**画面で、通知の間隔とインシデントの自動クローズ期間を設定

   | 項目 | 設定例 |
   | ---- | ---- |
   | 通知の間隔 | 5分 |
   | インシデントの自動クローズ期間 | 7日 |

7. **Notification channels** で先ほど作成した Slack チャネルを選択 → **Save**
営業ができるバイブコーディング活用例 〜AI×営業現場の新常識〜

## あいさつ

おはようございます。私はSES営業とエンジニアの両方の経験を持つプロのSES営業です。AI技術の急速な進化により、営業現場でもAIを活用した新しい働き方が広がっています。特に、バイブコーディング（Vibe Coding）というAIと人間がリアルタイムで協働しながら開発を進めるスタイルは、営業の現場にも大きな変革をもたらしています。従来、営業とエンジニアの間には「伝達ミス」「認識齟齬」「情報共有の遅れ」といった壁があり、これがプロジェクトの品質やスピードに大きく影響していました。しかし、AIを活用したバイブコーディングを導入することで、これらの課題を根本から解決し、営業とエンジニアが一体となって顧客価値を最大化できる時代が到来しています。

本記事では、バイブコーディングの代表的なプラットフォームであるWindsurfを活用し、営業が現場でどのようにAIと協働し、業務効率や提案力を高めているのか、具体的な事例を1つピックアップしてご紹介します。エンジニア歴3年〜7年の方や、営業との連携に課題を感じている方、あるいはSES現場に馴染みのない方にも分かりやすく、実践的な内容を心がけて解説します。AI×営業の最前線にご興味のある方は、ぜひ最後までご覧ください。

---

## 本文

### 活用例：AI×営業による提案書の自動生成と顧客対応力の強化

#### 背景

営業現場では、顧客ごとに異なる課題や要望に合わせて提案書を作成する必要があります。しかし、現実には限られた時間やリソースの中で、毎回ゼロから提案書を作成することは難しく、どうしても既存のテンプレートを流用したり、過去の事例をコピー＆ペーストする対応になりがちです。その結果、顧客ごとのニーズに十分に応えきれなかったり、他社と差別化できない提案になってしまうことが多々あります。

また、ヒアリング内容を正確に記録し、要件を整理してエンジニアに伝えるプロセスでも、営業担当者の経験やスキルに依存しがちです。聞き漏れや認識のズレが発生すると、後工程での手戻りや顧客満足度の低下につながります。さらに、提案書作成にかかる工数が多く、営業担当者が本来注力すべき「顧客との関係構築」や「新規開拓」に十分な時間を割けないという悩みも根強く存在します。

こうした課題を解決するために、AIを活用したバイブコーディングの導入が注目されています。AIがヒアリング内容をリアルタイムで記録・要約し、要件整理や提案書作成までをサポートすることで、営業活動の質とスピードを飛躍的に向上させることが可能となります。Windsurfのようなプラットフォームを使えば、営業とエンジニアが同じ情報をもとに協働でき、SES現場における「伝達ミス」や「認識齟齬」といった従来の課題も大幅に軽減されます。

#### バイブコーディング（Windsurf）活用の流れ

```mermaid
flowchart TD
    A[顧客ヒアリング（営業主導）] --> B[WindsurfでAIがヒアリング内容を記録・要約]
    B --> C[AIが要件を整理し、提案書ドラフトを自動生成]
    C --> D[営業が内容を確認・カスタマイズ]
    D --> E[エンジニアが技術的観点でレビュー]
    E --> F[顧客へ提案・フィードバック取得]
```

#### ポイント解説
- **AIによる要約・ドラフト作成**：顧客ヒアリング内容をAIが即座に整理し、提案書のたたき台を自動生成。営業は本質的な提案内容に集中でき、作業効率が大幅に向上。
- **営業・エンジニア連携の強化**：AIが要件を構造化してくれるため、エンジニアへの伝達もスムーズ。技術的な観点でのレビューも容易に。
- **顧客対応力の向上**：提案書のスピード作成により、顧客への迅速な提案・フィードバック対応が可能。
- **ナレッジの蓄積**：AIが提案書やヒアリング内容をナレッジベース化し、次回以降の営業活動にも活用できる。

#### 営業・エンジニア両方の視点から
- **営業目線**：ヒアリングから提案書作成、顧客対応までの一連の流れが効率化され、差別化された提案がしやすくなる。
- **エンジニア目線**：要件の伝達ミスや認識齟齬が減り、開発に集中できる環境が整う。

---

## まとめ

バイブコーディング（Windsurf）の活用により、営業とエンジニアがAIを介して密接に連携し、提案活動の質とスピードを大きく向上させることができます。AIによる要約・自動生成・ナレッジ蓄積を活用することで、現場の課題解決に直結する新しい営業スタイルが実現します。

---

## 参考文献
- [Windsurf公式サイト](https://windsurf.ai/)
- [AI営業支援の最新動向まとめ](https://www.saleszine.jp/article/detail/3312)
KPTからStarfish振り返りに変更して生産性が劇的に向上した話

## はじめに

チーム開発における振り返り（レトロスペクティブ）は、継続的な改善を行う上で欠かせない活動です。多くのチームがKPT（Keep, Problem, Try）を採用していますが、実際に運用してみると「お腹が痛かった」「寝坊した」といった個人的な問題まで挙がってしまい、本質的な改善に繋がらないケースが頻発していました。

そこで私たちのチームでは、KPTからStarfish（スターフィッシュ）に振り返り手法を変更したところ、生産性が大幅に向上しました。本記事では、その経験と具体的な改善効果について詳しくお伝えします。

## KPTの問題点
なぜ生産性の低い振り返りになってしまうのかを考えてみました

### Problem（問題）の範囲が曖昧すぎる

KPTにおける「Problem」は非常に広範囲な概念です。これにより以下のような課題が発生していました

- **個人的な体調不良や私生活の問題**
  - 「お腹が痛くて集中できなかった」
  - 「寝坊してしまった」
  - 「家族の都合で早退した」

- **解決困難な環境的要因**
  - 「オフィスが寒かった」
  - 「隣の工事音がうるさかった」
  - 「エアコンの調子が悪かった」

これらの問題は確かに業務に影響を与えますが、チーム全体の生産性向上に直結する改善アクションを導き出すのは困難です

### Try（挑戦）が具体的なアクションに繋がらない

Problemが曖昧だと、必然的にTryも抽象的になりがちです

- 「体調管理に気をつける」
- 「早寝早起きを心がける」
- 「集中できる環境を作る」

これらのTryは個人の努力に依存する部分が大きく、チームとしての組織的な改善に繋がりにくいという課題がありました

## Starfish振り返り手法とは

Starfish（スターフィッシュ）は、星の形（5つの角）を模した振り返り手法で、以下の5つのカテゴリで構成されます

### 1. Keep Doing（継続すること）
チームが現在行っていて、今後も継続すべき良い取り組み

### 2. More of（もっと増やすこと）
現在も行っているが、より頻度や強度を増やすべき取り組み

### 3. Start Doing（新しく始めること）
まだ実施していないが、新たに取り組むべき活動

### 4. Less of（減らすこと）
現在行っているが、頻度や強度を減らすべき取り組み

### 5. Stop Doing（やめること）
現在行っているが、完全に停止すべき活動や習慣

## Starfish導入による具体的な改善効果

### 1. 問題の焦点が明確になった

KPTの「Problem」では「お腹が痛かった」といった個人的な問題も挙がっていましたが、Starfishでは以下のような建設的な議論に変化しました

**Less of（減らすこと）の例**
- 長時間の会議（3時間→1.5時間に短縮）
- 毎日の進捗確認メール（週2回に削減）
- 詳細すぎるドキュメント作成

**Stop Doing（やめること）の例**
- 毎朝の全員参加の定例会議
- 形式的なエクセル報告書の作成
- 重複したチェック工程

### 2. アクションがより戦略的になった

**Start Doing（新しく始めること）の具体例**
- ペアプログラミングの導入（週2回）
- 技術的負債の定期的な整理（月1回）
- 外部勉強会の参加推奨制度

**More of（もっと増やすこと）の具体例**
- コードレビューの頻度向上（1日1回→随時）
- 非同期コミュニケーションの活用
- 自動化ツールの導入拡大

### 3. 定量的な改善効果

Starfish導入から3ヶ月後の測定結果

- **開発速度**
    - 20%向上（ストーリーポイント/スプリント）
- **バグ発生率**
    - 35%削減
- **会議時間**
    - 40%短縮
- **チーム満足度**
    - 8.5/10（従来7.2/10）

## Starfish運用のベストプラクティス

### 1. 事前準備

- **時間設定**
    - 90分程度を確保
- **ファシリテーター**
    - 毎回異なるメンバーが担当
- **ツール**
    - MiroやFigJamなどのオンラインホワイトボード

### 2. 進行方法

1. **個人作業（15分）**
    - 各自で付箋に意見を記入
2. **共有・分類（30分）** 
    - 全員で付箋を共有し、類似項目をグループ化
3. **優先順位付け（20分）**
    - 投票により重要度をランク付け
4. **アクション決定（20分）**
    - 上位3-5項目について具体的なアクションを決定
5. **振り返り（5分）**
    - 今回の振り返り自体について短時間で振り返り

### 3. 継続のコツ

- **定期開催**
    - 2週間に1回の頻度で実施
- **アクション追跡**
    - 前回決めたアクションの進捗を必ず確認
- **可視化**
    - チーム全員が見える場所に結果を掲示

## 導入時の注意点とトラブルシューティング

### よくある課題と対処法

**課題1: 「Less of」と「Stop Doing」の区別が曖昧**
→ 判断基準を事前に明確化（完全廃止 vs 頻度削減）

**課題2: 個人攻撃になってしまう**
→ 「誰が」ではなく「何を」に焦点を当てるルールを徹底

**課題3: アクションが多すぎて実行できない**
→ 1回の振り返りで実行するアクションは最大3つまでに限定

## まとめ
### なぜStarfishが効果的なのか

Starfish振り返りが生産性向上に繋がる理由は以下の通りです

1. **具体性の向上**
    - 5つのカテゴリが明確で、それぞれに適した改善アクションを考えやすい
2. **バランスの取れた視点**
    - 追加と削減の両方を検討することで、リソース配分を最適化
3. **実行可能性**
    - 「やめる」「減らす」というオプションがあることで、現実的な改善が可能
4. **継続性**
    - 段階的な変化を促すため、チームの負担が少なく継続しやすい

KPTで生産性の低い振り返りに悩んでいるチームは、ぜひStarfish手法を試してみることをお勧めします。最初は慣れないかもしれませんが、3-4回実施すれば確実にその効果を実感できるはずです。


## 参考リンク

- [Starfish Retrospective Template](https://www.atlassian.com/team-playbook/plays/starfish-retrospective)
- [効果的な振り返りのガイドライン](https://speakerdeck.com/kawasima/retrospective-guide)
Windows11予期しない再起動の原因と対策調査

## 今回の動機
デスクトップPCでネットサーフィンをしていたところ、突然画面が暗転し再起動されてしまいました。原因を調査し、対策を打つことにしました。

---

## システム構成
以下は今回使用しているPCの構成です。

- **Motherboard**: MPG X870E EDGE TI WIFI
- **CPU**: AMD Ryzen 9 9950X
- **GPU**: ASUS TUF NVIDIA GeForce RTX 5070 Ti 16GB
- **OS**: Windows 11 Pro

---

## 調査の流れ

### 1. Windowsイベントログの調査
まずは **イベントビュアー** を利用して、どのような「エラーイベント」が記録されているかを確認しました。

- **確認ポイント**
  該当時間付近に以下のイベントIDが存在するかを調査します:
    - **ID:41、6008**
      - **存在しない場合**: イベントID `1074` が記録されていれば正常なシャットダウン／再起動です。
      - **存在する場合**: イベントID `1001` を確認します。
        - **存在しない場合**: リセットや電源断が原因の可能性があります。
        - **存在する場合**: BSoD（ブルースクリーン）が発生しダンプファイルが記録されています。

詳細は以下の記事も参考にしてください。
[Windowsのリブート原因を追う方法](https://jpwinsup.github.io/blog/2022/09/05/Performance/Hang_BSOD/UnintentionalReboot/)

#### 結果
今回のケースでは以下の現象が確認されました。
- イベントID:41 が記録
- イベントID:1001 も記録

![イベントログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3312479/99138958-4670-45ec-b569-0da1680868c0.png)

---

### 2. dumpファイルの分析

次に、Windows用デバッグツール **WinDbg** を使用し、dumpファイルを解析しました。

#### 手順
1. **管理者権限** で WinDbg を起動。
   > **注意**: 管理者権限で実行しないと、ダンプファイルを保存しているフォルダを閲覧できないためエラーになります。
 
2. dumpファイルをWinDbgで開く。
   開いた後に、コマンドプロンプト（`kd>`）で以下のコマンドを実行:
   ```cmd
   !analyze -v
   ```

#### 出力された情報
以下のような結果が得られました。

![dump解析結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3312479/d72ecb11-2d7d-4965-957a-25a4153eca5a.png)

---

### 3. コマンド実行結果から原因を推測

#### 出力結果の詳細と解説
1. **STOPコード**
   出力詳細から以下のSTOPコードが確認されました:
   ```cmd
   SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)
   ```
   → 「カーネルスレッド内で例外がハンドリングされずに停止」したことを示します。

2. **例外コード**
   ```cmd
   ExceptionCode: c0000005 (Access violation)
   Arg1: ffffffffc0000005
   ```
   → メモリアクセス違反（例: 不正な読み取り／書き込み）が原因と特定。

3. **例外発生モジュール**
   ```cmd
   ExceptionAddress: fffff8029e27bf83 (nvlddmkm+0x00000000000fbf83)
   Arg2: fffff8029e27bf83
   ```
   → 問題の発生原因が Nvidia ドライバー `nvlddmkm.sys` であることが明らかになりました。

4. **モジュール名／イメージ名**
   ```cmd
   MODULE_NAME: nvlddmkm
   IMAGE_NAME: nvlddmkm.sys
   ```
   → 問題を引き起こしたのは Nvidia GPU のドライバ `nvlddmkm.sys` であることを特定。

5. **FAILURE_BUCKET_ID**
   ```cmd
   FAILURE_BUCKET_ID: AV_nvlddmkm!unknown_function
   ```
   → Nvidia ドライバがアクセス違反を起こしたことでカーネルパニックを発生。

---

## 問題の概要
調査の結果、以下の状況が判明しました。

- **STOPコード**: SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (0x7E)
- **例外コード**: 0xC0000005（不正なメモリアクセス）
- **障害モジュール**: nvlddmkm.sys
- **原因要約**: Nvidia のグラフィックドライバーが不正なアクセスを引き起こし、システムがクラッシュ。

---

## 考えられる原因
以下の要因が今回の問題に関係している可能性があります。

1. **ドライバーのバグ／破損**
2. **ドライバ間の競合**
   例: AMD、サードパーティ製ドライバとの衝突
3. **GPUのハードウェア異常**
   VRAMの異常やGPUの過熱
4. **電源ユニットの容量不足・電圧異常**
5. **オーバークロック設定**
   過度な設定が原因の可能性も考えられる。

---

## 今回の対策

今回の予期しないさいきどうに対し、以下の対策を実施しました。

- Nvidia ドライバを **最新バージョン** に更新
- 最新版での動作を確認し、同様の事象が発生するか様子見

---

今後も発生する場合は、さらなるハードウェア検証や各コンポーネントの設定調整を行います。

---
過去に作ったラズパイとOpenCVを使った簡易監視カメラの記録を残します。

---
過去に作成した簡易監視カメラの記録を残します。（自分用）
今は使えないLineNotifyを後で変更します。

```
#include <iostream>
#include <stdio.h>
#include <time.h> //時間に関すライブラリ
#include <opencv4/opencv2/opencv.hpp> //opencvのいろんなライブラリをincludeしてくれる
#include <stdlib.h> //シェルスクリプト実行に必要
#include <wiringPi.h> //GPIOの使用

#define RED_BTN_PIN 0
#define GREEN_BTN_PIN 2
#define RED_LED_PIN 7
#define BLUE_LED_PIN 1

//名前空間の省略
using namespace std;
using namespace cv;

int main (int argc, char **argv){

  wiringPiSetup();
  pinMode (RED_LED_PIN, OUTPUT);
  pinMode (RED_BTN_PIN, INPUT);
  pinMode (GREEN_BTN_PIN, INPUT);


  Mat frame;
  VideoCapture cap(0); //映像オブジェクト作成、カメラ起動

  //カメラの起動確認
  if(!cap.isOpened()){
    cerr << "ERROR! カメラが見つからない\n";
    return 0;
  }
  
  VideoWriter writer; //書き込み用オブジェクト作成

  int fourcc = VideoWriter::fourcc('m','p','4','v'); //フォーマット指定
  double fps = cap.get(CAP_PROP_FPS); //映像フレームレート取得
  int width = cap.get(CAP_PROP_FRAME_WIDTH);
  int hight = cap.get(CAP_PROP_FRAME_HEIGHT);

  CascadeClassifier cascade;
  cascade.load("/home/pi/opencv/data/haarcascades/haarcascade_frontalface_alt_tree.xml"); //正面顔情報のカスケードファイル読み込み
  vector<Rect> faces; //輪郭情報の格納場所

  int nowhour = 0;
  int nowmin = 0;


  while(1){
    time_t t = time(NULL); //時刻情報の取得
    struct tm *local = localtime(&t); //時刻情報を現地に変換
    int year = local->tm_year + 1900;
    int mon = local->tm_mon + 1;
    int day = local->tm_mday;
    int hour = local->tm_hour;
    int min = local->tm_min;

    //１時間毎に録画ファイルの切り替え
    if(nowhour != hour){
      if(writer.isOpened()){
        writer.release();
        digitalWrite(RED_LED_PIN, LOW); //赤ランプ消灯
      }
      char videoname[100];
      sprintf(videoname, "video/%d,%d,%d_%d:%d.mp4", year, mon, day, hour, min);
      writer.open(videoname, fourcc, fps, Size(width, hight), 1);
      digitalWrite(RED_LED_PIN, HIGH); //赤ランプ点灯
      nowhour = hour;
    }

    cap >> frame;//映像を１フレーム取得
    if(frame.empty()){
      cerr << "映像取得エラー\n";
      break;
    }

    //画像からfacesの情報をもとに顔を検知
    cascade.detectMultiScale(frame, faces, 1.1, 3, 0, Size(20 ,20));
    for(int i = 0; i < faces.size(); i++) {

      //検知した顔を赤線で囲む
      rectangle(frame, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), Scalar(0, 0, 255), 3);
    }

    //顔を認識したか判別
    if(faces.size()){
      digitalWrite(BLUE_LED_PIN, HIGH); //青ランプ点灯
      //LINE送ったら1分未満LINE送信停止
      if(nowmin != min){
        imwrite("linephoto/image.jpg", frame); //検知した写真を保存
        system("bash line.sh"); //保存した写真をLINEに送信
        nowmin = min;
      } 
    }else{
      digitalWrite(BLUE_LED_PIN, LOW); //青ランプ消灯
    }
    
    //imshow("Live", frame); //ディスプレイ表示
    writer << frame; //書き込み

    //緑ボタンを押したらラインに写真を送る
    if(digitalRead(GREEN_BTN_PIN) == true){
      imwrite("linephoto/image.jpg", frame); //検知した写真を保存
      system("bash line.sh"); //保存した写真をLINEに送信
    }

    //赤ボタンを押したらループから抜ける
    if(digitalRead(RED_BTN_PIN) == true){
      break;
    }
  }

  //書き込みファイルを閉じる
  writer.release();
  digitalWrite(RED_LED_PIN, LOW);

  system("bash poweroff.sh");

  return 0;
}
```
【rails】use.ymlでencrypted_passwordをハッシュ化する方法


## はじめに

こんにちは。アメリカ在住で、独学でエンジニアを目指している Taira です。
現在 Rails アプリを devise を用いて作成しています。
`test/fixtures/user.yml` に user を設定してテストに使用することがよくあります。

```yml
user1:
  name: takeshi
  email: test1@example.com
  encrypted_password: '何を書けばいい？'
  confirmed_at: <%= Time.current %>
```

このとき、`encrypted_password`にパスワードを設定したいのですが、devise は内部でどのようにハッシュ化しているのかわかりにくい、という問題にぶつかりました。
そこで今回は、`encrypted_password`の値をどう設定すればよいか、devise のハッシュ化処理のソースコードを読み解くことで解説します。

## `encrypted_password`のハッシュ化方法

[https://github.com/heartcombo/devise](https://github.com/heartcombo/devise)

上記リポジトリの `devise/lib/devise/encryptor.rb` の 7 行目に該当の処理があります。

```ruby
module Devise
  module Encryptor
    def self.digest(klass, password)
      if klass.pepper.present?
        password = "#{password}#{klass.pepper}"
      end
      ::BCrypt::Password.create(password, cost: klass.stretches).to_s
    end
    # ...(省略)
  end
end
```

`digest`クラスメソッドは、第一引数にクラス（ここでは User）、第二引数にパスワードを渡して呼び出すことで、devise が期待する形式でハッシュ化できます。

つまり、fixture の `user.yml` で `encrypted_password` を指定するには、以下のように書くことができます。

```yml:user.yml
user1:
  name: takeshi
  email: test1@example.com
  encrypted_password: <%= Devise::Encryptor.digest(User, 'password') %>
  confirmed_at: <%= Time.current %>
```

## まとめ

ハッシュ化の方法をソースコードから探すアプローチは、Rails チュートリアルでも学習した大切な方法です。
今後も困ったときには積極的にソースコードを読む姿勢を大事にしたいと思います。

## 参考資料

https://github.com/heartcombo/devise
Chap1. cpaw Level1前編 

## はじめに

CTF学習のため、cpawというサイトの問題を解いていこうと思います。

https://ctf.cpaw.site/index.php


### この記事の対象者
- CTF超初心者
- cpawの問題を解いてみたい人

### 問題一覧

-  [Q6 [Crypto] Classical Cipher](#q6-crypto-classical-cipher)
- [Q7 [Reversing] Can you execute?](#q7-reversing-can-you-execute)
- [Q8 [Misc] Can you open this file?](#q8-misc-can-you-open-this-file)

***
### [Q6. [Crypto] Classical Cipher](#見出し1)
__シーザー暗号を解いてflagを入手する問題__
シーザー暗号：3文字分アルファベットをずらして解く

頑張れば自力でも解けますが、少し長いのでプログラムで解きます。
暗号文: fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}

<details><summary>求める用Cのソースコード</summary>
<div>

```C:level1-6.c
#include <stdio.h>

char shift(char c, int shift) {
    if (c >= 'A' && c <= 'Z')  // 大文字判定
        return (c - 'A' - shift + 26) % 26 + 'A';
    if (c >= 'a' && c <= 'z')  // 小文字判定
        return (c - 'a' - shift + 26) % 26 + 'a';
    return c;  // {}などの記号はそのまま
}

int main() {
    char str[] = "fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}";
    for (int i=0; str[i]; i++){
        str[i] = shift(str[i], 3);
    }
    printf("%s\n", str);
    return 0;
}
```

</div>
</details>

<details>
  <summary>flag</summary>
    cpaw{Caesar_cipher_is_classical_cipher}
</details>

***

### [Q7. [Reversing] Can you execute?](#見出し2)
__拡張子がないファイルをコマンドで実行する問題__

～やること～
- ファイルの種類をコマンドで調べる(Linux環境)
- 実行方法を考える
- 実行する

今回はWSLの環境を使って実行まで行います。
wsl起動->ダウンロードしたフォルダへの移動は割愛します。
<details><summary>実行するコマンド</summary>
<div>

```
file exec_me
```

>実行結果：
exec_me: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=663a3e0e5a079fddd0de92474688cd6812d3b550, not stripped

ELF 64-bit LSB executable ~ for GNU/Linux 2.6.24
と書いてあるのでUNIX系環境で動作するバイナリ実行ファイルであることがわかりました！
実行ファイルなのがわかったらあとは実行あるのみ！

```
./exec_me
```

</div>
</details>

<details>
  <summary>flag</summary>
    >実行結果(flag)：
    cpaw{Do_you_know_ELF_file?}
</details>

***

### [Q8. [Misc] Can you open this file?](#見出し3)
__拡張子がないファイルの種類を特定して開く問題__
ファイルの種類特定の流れはQ7と同じですが実行方法がQ7とは変わっているようです。
この問題も種類特定はWSLの環境下で行います。

<details><summary>実行するコマンド</summary>
<div>

```
file open_me
```

>実行結果：
open_me: Composite Document File V2 Document, Little Endian, Os: Windows, Version 10.0, Code page: 932, Author: v, Template: Normal.dotm, Last Saved By: v, Revision Number: 1, Name of Creating Application: Microsoft Office Word, Total Editing Time: 28:00, Create Time/Date: Mon Oct 12 04:27:00 2015, Last Saved Time/Date: Mon Oct 12 04:55:00 2015, Number of Pages: 1, Number of Words: 3, Number of Characters: 23, Security: 0

ここで注目すべきは`Creating Application: Microsoft Office Word`です。
ここからファイルがWordで作成されたとわかるのでWordで開くため拡張子を加えます。

__open_me.doc__ と拡張子を加えてWordで開くと、下のflagが入手できます！

</div>
</details>

<details>
  <summary>flag</summary>
    cpaw{Th1s_f1le_c0uld_be_0p3n3d}
</details>

## おわりに
第1回目ということでQ6~Q8の簡単な解き方備忘録的な感じで書き起こしてみました。
第2回目以降も順次上げて行く予定です！
【簡単Kubernetes】【livenessProbe】Kubernetesで livenessProbe（exec）を使ってPodの再起動を試してみた話


今回は Kubernetes の `livenessProbe` を使って、**意図的に Pod を「不健康」な状態にし、再起動をトリガーする方法**について紹介します。

---

## 🎯 目的

- `livenessProbe` の動作確認
- `exec` モードによるシンプルなヘルスチェックの使い方
- コンテナ内で一時的に「健康 → 不健康」をシミュレートし、Kubernetes の自動再起動を観察する

---

## 🛠️ 使用するYAML

以下のような Pod を定義しました：

```yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-test
  name: liveness-exec
spec:
  containers:
  - name: liveness-container
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5   # コンテナ起動後、5秒経過してから最初のプローブを実行します
      periodSeconds: 5　       # その後は5秒ごとにプローブを実行します
```

---

## 🧠 解説：このPodがやっていること

### 起動後に実行されるコマンド

```sh
touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
```

1. `/tmp/healthy` ファイルを作成（= 健康な状態）
2. 30秒間スリープ → この間は livenessProbe が成功
3. `/tmp/healthy` を削除（= 不健康になる）
4. さらに600秒スリープ（= プロセスがすぐ終了しないように）

### `livenessProbe` の動作

```yaml
livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
```

- `/tmp/healthy` ファイルが存在すれば `cat` 成功 → Healthy
- ファイルが消えると `cat` 失敗 → Unhealthy → Kubernetesが再起動

---

## 🔍 実行結果（確認方法）

```sh
kubectl apply -f liveness-test.yaml
kubectl describe pod liveness-exec
kubectl get pod liveness-exec -w
```

- 起動後30秒は`Running`
- 30秒経過すると`cat /tmp/healthy`が失敗し、`livenessProbe`が失敗判定
- Kubernetes により Pod が自動で再起動される（`RESTARTS`の値が増加）

---

## ✅ まとめ

- `livenessProbe` の `exec` モードは、シンプルなスクリプトやファイルの存在確認に便利。
- この方法で**自己診断・自動回復の仕組み**を簡単に試すことができる。
- 本番ではもっとしっかりしたヘルスチェック（例：HTTPリクエストなど）を使うのが一般的。

---

## 🔗 関連リンク

- [Kubernetes公式ドキュメント - Liveness Probes](https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
- [BusyBox イメージの紹介](https://hub.docker.com/_/busybox)

---

何かの参考になれば幸いです！  
質問や改善点があれば、コメントでぜひ教えてください 🙌
C#のフィールドとプロパティって何が違うの？初心者向けにわかりやすく解説！

# はじめに
C#を学習する上でつまずきやすいポイントの一つに「フィールド」と「プロパティ」の違いがあるかと思います。
この記事では、フィールドとプロパティの違いと使い方を、初心者の方にもわかりやすく丁寧に解説していきます。

# フィールドとは？
フィールドとは、クラスの中で定義する変数のことで、オブジェクトが持つデータを保存するために使います。
フィールドの定義は以下のように記述します。
```c#:Person.cs
public class Person
{
    private string name = ""; // フィールド
}
```
このコードでは、`Person`クラスに`name`というフィールドが定義されています。

*****

### フィールドの注意点
フィールドは`public`でも定義できますが、`private`にしておき、外部から直接アクセスさせないのが一般的です。
以下は、フィールドを`public`で定義した場合の一例です。
```c#:
class Person
{
    public int age;
}

class Program
{
    static void Main() {
        Person p = new Person();
        p.age = -10; // 不正な値も代入できてしまう
    }
}
```
:::note warn
publicにしておくと外部からの直接アクセスや変更が可能になり、予期せず値を変更されてしまうリスクがあります。
:::

# プロパティとは？
プロパティとは、フィールドへのアクセスを制御するための仕組みのことです。
`get`と`set`を記述し、値の取得や設定を行います。
プロパティの定義は以下のように記述します。

```c#:Person.cs
public class Person
{
    private string name = ""; // フィールド
    
    public string Name // プロパティ
        {
            get { return name; } 
            set { name = value; } 
        }
}
```
*****

### なぜプロパティを使うの？
<dl>
  <dt>カプセル化を実現できる</dt>
  <dd>getter/setterで値の取得・設定を制御し、カプセル化によってクラスの内部構造を隠すことで、安全にデータを扱えるようにしています。</dd><br>
  
  <dt>ロジックを追加できる</dt>
  <dd>プロパティにロジックを追加することで、変数に意図しない値が設定されることを防いだり、値の取得や設定の際に特定の処理を行わせることができます。<br>
  <br>
  例えば、変数age(年齢)にマイナスの値を設定されたくないときは、以下のようなロジックが考えられます。
  
```c#:Person.cs
public class Person
{
    private int age; // フィールド

    public int Age // プロパティ
        {
            get { return age; }
            set
                {
                    if (value >= 0) 
                    age = value;
                }
        }
}
```
  </dd>
</dl>

*****
### 自動実装プロパティとは？
C#ではプロパティの実装が一般的であることから、「自動実装プロパティ」という簡略構文が用意されています。
```c#:Person.cs
public class Person
{
    public string Name { get; set; }
}
```
自動実装プロパティでは、フィールドを定義することなく`Name`プロパティを利用することができます。
シンプルなデータ保持で、ロジックが不要な場合に便利です。
*****

### 読み取り専用/書き込み専用のプロパティ
プロパティはアクセサの組み合わせで、読み取り専用/書き込み専用にすることも可能です。
```c#:Person.cs
public class Person
{
    public string Name { get; } // 読み取り専用
    public int Age { private get; set; } // 書き込み専用
}
```

# フィールドとプロパティの違い
以下の表にフィールドとプロパティの主な違いをまとめてみました。
| 項目 | フィールド | プロパティ |
|:-----------|------------:|------------:|
| アクセス       | 直接        | 間接（getter/setterを経由）         |
| カプセル化     | 弱い      | 強い       |
| 制御の可否      | 実装不可       | 実装可能（バリデーションなど）|
| 利用目的         | 内部データ保持          | 外部への公開、制御付きアクセス         |
| 拡張性         | 低い          | 高い（ロジックの追加が可能）         |

*****
### どうやって使い分ける？
フィールドとプロパティは次のような使い分けが基本です。

一時的なデータとしてクラス内だけで使う　⇒　**フィールド（private）**
外部からデータの読み書きを行う　⇒　**プロパティ（public + 制御）**

一般的には **「フィールドはprivateにして、プロパティを介してアクセスさせる」** のが推奨されます。

# おわりに
ここまでC#のフィールドとプロパティについて説明してきました。
フィールドとプロパティの違いについて理解しておくことで、より安全で保守のしやすいコードが書けるようになります。
この記事がフィールドとプロパティの部分でつまづいている初心者の方の助けになれば幸いです！
AIエージェント時代の開発者に大切な視点

### はじめに

「もし、あなたが今日設計しているアプリケーションのUXが、明日にはAIによって動的に最適化され、あなたの知らないサービスと勝手に連携し始めるとしたら、あなたの仕事の価値はどこにあるでしょうか？」

国内の生成AI市場は2028年には8,028億円に達すると予測されるほどの急成長を遂げています。しかし、その一方で、国内企業の71.3%が生成AIを利用しているにも関わらず、「使いこなせている」と回答した企業はわずか23.3%に留まっています。この**「活用ギャップ」**は、ユーザーがAIの能力を最大限に引き出すために、知識や労力、つまり「プロンプトエンジニアリング」などのコストを強いられていることの現れです。

このユーザーが感じるフラストレーション、そしてユーザーの習熟に頼るAI活用が限界を迎える中で、AIエージェントによる抜本的な顧客体験の変革が必然的に発生すると予想されます。そしてそれは、ソフトウェア開発の構造そのものを根底から変えることになるでしょう。

### 1. 価値の源泉が「機能」から「パーソナライズされた体験」へ

AIによる開発コストの劇的な低下は、機能そのものをコモディティ化させます。これまでの「いかに優れた機能を提供するか」という競争から、「いかにユーザー固有の文脈に深く適応し、パーソナライズされた体験を自動的に提供できるか」へと価値の源泉がシフトします。

これは、ユーザー固有の文脈を深く理解したエージェントが、窓口の向こう側に登場する事を意味します。これまで人間のみを想定していたUXは、根本的に見直しを迫られます。

### 2. 「窓口の表側」への主戦場移行

ユーザー側エージェントの登場は、特定のプラットフォームに依存する**心理的スイッチングコストを限りなくゼロに近づけます**。これは、ユーザーが「〇〇を使いたいから、〇〇のアプリを立ち上げる」という行動から、「AIエージェントに目的を伝えれば、最適な複数のサービスを自動的に連携して目的を達成してくれる」という、**圧倒的な自由と利便性**を手に入れることを意味します。

ユーザーはエージェントを介して、複数のマイクロサービスや異なるベンダーの機能を意識することなく横断的に活用できるようになります。これにより、競争の主軸は、もはやプラットフォームの「窓口の裏側」で機能や性能を競うことではなく、「窓口の表側」、すなわちユーザーエージェントが提示するUX、つまり「いかにユーザーの意図を正確に汲み取り、最適な形でタスクを完遂するか」という部分へと劇的に移行するのです。

**具体的な変化の例:**

- **ユーザーエージェントによる自動的なサービス選択と連携:** ユーザーは特定のアプリケーションを立ち上げる必要なく、自身の求める結果に基づいて最適なAIサービスやマイクロサービスが自動的に連携され、利用されるようになります。
- **シームレスなコンテキスト引き継ぎ:** 異なるサービス間でのコンテキスト情報の断絶が解消され、ユーザーは途切れることのない一貫した体験を得られるようになります。
- **動的なUI生成と最適化:** AIがユーザーの状況やタスクに応じて、その都度最適なUIを動的に生成・調整することで、自由度の高い機能と戸惑わせないUXの両立が可能になります。

### 3. サーバ側エージェントに求められる「信頼性」と「性能」、そして「提供コスト」

主戦場が「窓口の表側」に移行しても、その裏側を支えるサーバ側エージェントの役割は極めて重要です。老舗か新興かは関係なく、求められるのは以下の点です。

- **信頼性:** ユーザーエージェントから要求されたタスクを、正確かつセキュアに遂行できる信頼性が最重要視されます。特に、ユーザーデータの適切な保護と、AIの判断における責任の所在の明確化が不可欠です。
- **性能:** 多数のマイクロサービスを横断した複雑な活用が自動的に行われるため、高速かつ効率的な処理能力が求められます。
- **限りなく低い提供コスト:** 機能がコモディティ化する中で、サービス提供側は圧倒的な低コストでの運用が求められます。これは、競争力の源泉が「機能」から「体験」に移行する中で、提供価格を維持するための必須要件となります。

### 4. 構造変化に対応した新しいUX発想の重要性

これまでの「AIにアプリという鎧を着せてサービスの体裁を作る」アプローチでは、AIの真価を発揮できません。未来のUXは、以下のような要素を中心に再考される必要があります。

- **プロンプトエンジニアリングからの解放:** ユーザーが自身の要求を言語化するコスト（プロンプトエンジニアリング）を極小化する仕組みが求められます。AIがユーザーの意図を先行的に予測し、対話を通じて自然な形でタスクを遂行するようなUXが理想的です。
- **AIによるUIの自己最適化:** 自由度の高すぎるUIにユーザーが戸惑わないよう、AIがUIそのものに統合され、ユーザーの行動や文脈に合わせてUIが動的に変化し、最適な情報や操作を提供することが重要です。
- **AI主導のマルチモーダルなインタラクション:** テキストだけでなく、音声、画像、ジェスチャーなど、様々な入出力形式をAIが統合的に処理し、最も自然で効率的なインタラクションを提供する必要があります。
- **透明性と制御可能性:** AIが自動的にタスクを遂行する中でも、ユーザーがそのプロセスを理解し、必要に応じて介入できる透明性と制御可能性を担保するUXが不可欠です。

### 5. 変革に向けた注力ポイント

この構造変化はすでに始まっており、決して遠い未来ではありません。ソフトウェア開発企業、技術者は、以下の点に注力すべきです。

- **エージェントエコシステムへの視点転換:** 個別のアプリケーション開発だけでなく、ユーザー側エージェントと連携し、そのエコシステムの一部として機能するマイクロサービスやコンポーネント開発へと視点を転換する。
- **UXデザインの再定義:** AIを前提とした新しいUXの概念を理解し、ユーザーが「指示」するのではなく、「対話」を通じて目的を達成するようなデザイン思考を強化する。
- **データガバナンスとセキュリティの徹底:** ユーザーデータの保護とプライバシーを最優先し、責任の所在を明確にするための技術的・法的枠組みを構築する。
- **AIを活用した開発プロセスの変革:** 開発者自身もAIを積極的に活用し、開発の生産性を向上させるとともに、新しいUXを迅速にプロトタイプし、検証するサイクルを回す。

この変革期において、最も価値を生み出すのは、既存の枠組みにとらわれず、ユーザー中心の視点で「新しい体験」を発想し、それを実現する力となるでしょう。
引数の文字列をMD5形式暗号化

/*
********************************************************************************
 関数名：fn_create_hash
 概要　：引数に指定された文字列を暗号化して返します
 引数　：[@str]      ...暗号化する文字列
 　　　　[@salt_base]...saltのベースとなる文字列
 戻り値：暗号化したバイナリ
********************************************************************************
*/
CREATE FUNCTION fn_create_hash
(
    @str VARCHAR(20)        --暗号化する文字列
  , @salt_base VARCHAR(20)  --saltのベースとなる文字列
)
RETURNS VARCHAR(100)
AS
BEGIN

    /*
    暗号化アルゴリズムは、次のとおりです。

        MD5([暗号化する文字列] + salt)

    MD5()は、引数の文字列をMD5形式で暗号化する関数とします。
    salt値は、次のロジックで生成します。

        HASHBYTES('MD5', 'gihyo')
    */

    RETURN UPPER(
        master.dbo.fn_varbintohexstr(
            HASHBYTES(
                'MD5', @str + UPPER(
                    master.dbo.fn_varbintohexstr(
                        HASHBYTES('MD5', @salt_base)
                    )
                )
            )
        )
    );
END
GO
【Python初心者】ジェネレータの仕組みをやさしく解説

Pythonで `for` 文を使っていると、「ジェネレータ」という言葉が出てきました。  
最初は関数のように見えるのに、`next()` が使えたり、for文でも使えたりして混乱しました。  
この記事では、学習の記録として「ジェネレータとは何か？」について丁寧にまとめてみます。

## 1. ジェネレータとは？

ジェネレータとは、`yield` を使って値を**1つずつ返す関数のようなもの**です。  
普通の関数と違って `return` の代わりに、`yield` を使って値を一時停止しながら返します。  
（実際にはジェネレータ関数内でも `return` を使うことはありますが、それは値を返すためではなく、終了を明示するためです）

## 2. ジェネレータ関数の書き方

```python
def count_up(n):
    for i in range(n):
        yield i
```

この関数を呼び出しても、すぐに `for` が実行されるわけではありません。  
代わりに、「ジェネレータオブジェクト」という特別なイテレータが返ってきます。

```python
gen = count_up(3)
print(gen)  # <generator object count_up at 0x...>
```

## 3. 値を1つずつ取り出すには？

ジェネレータはイテレータなので、`next()` を使って値を1つずつ取り出せます。

```python
gen = count_up(3)
print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 2
# print(next(gen))  # StopIteration 例外になる
```

もちろん `for` 文でも扱えます。

```python
for num in count_up(3):
    print(num)
```

## 4. 普通の関数との違い

| 比較項目 | 通常の関数 | ジェネレータ関数 |
|----------|------------|------------------|
| 終了方法 | `return`   | `yield`（一時停止） |
| 値の返し方 | 1回だけ     | 使い切るまで順に返す |
| 処理の状態 | 呼び出すたびに最初から | 前回の `yield` から再開 |
| イテレータ？ | ❌ | ✅ |

## 5. なぜ使うの？

### ✅ メモリを節約できる

リストのように**全部の値をあらかじめメモリに持つ必要がない**ため、巨大なデータでも安全に処理できます。

```python
def infinite():
    n = 0
    while True:
        yield n
        n += 1

for i in infinite():
    if i > 5:
        break
    print(i)
```

このように「無限ループのように値を作り続ける」こともできます。

### ✅ 処理の途中経過をその都度返したいときに便利

たとえばファイルの各行を読み込みながら1行ずつ処理したい場合などに使えます。

## 6. ジェネレータの注意点

- 一度使い切ると再利用できない（`for` 文1回で終了）
- `list()` にするとすべての値を取り出してしまう（使い切りに注意）
- 途中で `StopIteration` が発生したら終了

```python
g = count_up(3)
print(list(g))  # [0, 1, 2]
print(list(g))  # []（使い切ったあとなので空）
```

## 7. 応用：手動で next() を呼び出す

```python
g = count_up(2)
print(next(g))  # 0
print(next(g))  # 1
# print(next(g))  # StopIteration
```

`for` 文は内部で自動的に `next()` を呼んでくれている、ということがわかりました。

## 8. まとめ

✔ ジェネレータは `yield` を使って、値を1つずつ返す特別な関数  
✔ 関数を呼び出すと「ジェネレータオブジェクト（イテレータ）」が返る  
✔ メモリ効率がよく、`for` 文や `next()` で繰り返し処理できる  
✔ 使い切るまで順に値を返せるが、一度使い切ると再利用はできない

最初は「関数なのにイテレータ？」「listと何が違うの？」と混乱しましたが、  
「**yield = 一時停止しながら値を返す**」というイメージを持つと理解が深まりました。

今後は、リストではメモリがきついような場合や、逐次処理をしたいときに使ってみたいです。
✨ 初心者必見！Java文法を簡単に学ぼう！

<section style="font-family: sans-serif; line-height: 1.8;">
  <h2>✨ Java文法を簡単に学ぼう！</h2>

  みなさん、こんにちは！韓国在住のsanaです:woman_tone1: 
  
  <p>
    Javaの基礎を短時間で効率よく学びたい方へ！<br>
    約1時間でJava文法のすべて簡単に習得しませんか？🎓
  </p>

  <h3>📝 講座概要</h3>
  <ul>
    <li><strong>時間：</strong> 約47分（43レッスン）</li>
    <li><strong>料金：</strong> 完全無料</li>
    <li><strong>対象：</strong> 初心者 / 文法を復習したい方</li>
    <li><strong>形式：</strong> オンライン動画、インストール不要</li>
  </ul>

  <h3>📚 カリキュラム例</h3>
  <ul>
    <li>Javaの基本文法とデータ型</li>
    <li>if/switch文、for/whileループ</li>
    <li>クラス、継承、オーバーライド</li>
    <li>抽象クラスとインターフェース</li>
    <li>例外処理、ラムダ式</li>
  </ul>

  <h3>🎯 おすすめの受講者</h3>
  <ul>
    <li>Javaをゼロから始めたい方</li>
    <li>Springなど実務フレームワークの前に基礎を固めたい方</li>
    <li>他言語経験者で、Javaだけ復習したい方</li>
  </ul>

  <h3>:sunny: まとめ</h3>
  <ul>
   初心者の方がいきなりJavaの文法を覚えるのは難しいですよね…。多くの文法を教材で覚えるのは時間もかかり大変です:disappointed_relieved:
   大変な勉強こそ、YouTubeやオンライン講座を利用して学ぶのが一番です:point_up:
   
  </ul>
  
  https://u.inf.run/45rbJjq
指定したspidのプロセスを削除

--概要　　　：指定したspidのプロセスを削除します。
--引数　　　：[@target_spid]...プロセスを削除するspid
--戻り値　　：正常終了なら0、そうでなければ-1
--結果セット：例外が発生した場合、エラー情報
CREATE PROCEDURE [sp_kill_process]
    @target_spid SMALLINT
AS
BEGIN
    SET NOCOUNT ON;

    --KILLコマンドを実行する動的SQLを作成します。
    --※「KILL @spid;」はエラーとなるため、動的SQLで対応
    DECLARE @sql VARCHAR(MAX);
    SET @sql = 'KILL ' + CONVERT(VARCHAR, @target_spid);

    --spidを削除します。
    BEGIN TRY
        EXECUTE (@sql);
    END TRY
    BEGIN CATCH
        EXECUTE sp_returnerror 'sp_kill_process:プロセスの削除に失敗しました。';
        RETURN (-1);
    END CATCH

    --正常終了を返します。
    RETURN (0);
END
GO
指定したホスト名が生成したSQL Serverのプロセスを取得し、spidを求める

--概要　　　：指定したホスト名が生成したSQL Serverのプロセスを取得し、spidを返します。
--引数　　　：[@hostname]...ホスト名
--戻り値　　：正常終了なら0、そうでなければ-1
--結果セット：正常終了した場合、指定したホストのspid結果リスト
--　　　　　　例外が発生した場合、エラー情報
CREATE PROCEDURE [sp_get_process]
    @hostname VARCHAR(100)
AS
BEGIN
    SET NOCOUNT ON;

    --spidを取得します。
    BEGIN TRY
        SELECT [spid]
        FROM [sys].[sysprocesses] WITH (nolock)
        WHERE [hostname] = @hostname
        AND [spid] <> @@spid
        ORDER BY [spid];
    END TRY
    BEGIN CATCH
        EXECUTE sp_returnerror 'sp_get_process:プロセスの取得に失敗しました。';
        RETURN (-1);
    END CATCH

    --正常終了を返します。
    RETURN (0);
END
GO
エラーの詳細を出力

--概要　　　：エラーの詳細を返します
--引数　　　：[@message]…エラー詳細に含める任意の文字列
--戻り値　　：なし
--結果セット：エラー情報
CREATE PROCEDURE [dbo].[sp_returnerror]
    @message VARCHAR(255)   --エラー詳細に含める任意の文字列
AS
BEGIN
   --結果件数を表示しないようにします。
    SET NOCOUNT ON;

    --各種エラー関数から結果セットに含めるレコードを生成します。
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage,
        @message AS ApplicationMessage;
END
猿でもわかるAIプログラミングシリーズ 🐵💻 簡単なスパムメール判定AIを作ってみよう


## 1. はじめに：なぜスパムメール判定がAI入門に最適なのか？

みなさん、メールボックスにこんな件名を見たことありませんか？

> 「あなたに高額報酬のチャンス！今すぐクリック！」

スパムメール（迷惑メール）は、日常生活の中で誰もが遭遇する“わかりやすい課題”です。そして、機械学習・AIを使って「これがスパムか否か？」を自動で判断する処理は、**自然言語処理（NLP）**や**分類モデル**の初歩としてぴったりです。

本記事では、Python + scikit-learn を使って、簡単なスパム判定AIを一緒に作ってみましょう！

---

## 2. スパム判定AIの全体像

スパム判定AIは、以下の流れで構成されます：

1. **データの準備**（スパム・非スパムのラベル付きテキスト）
2. **前処理**（テキスト→数値への変換）
3. **モデルの学習**（分類器の訓練）
4. **評価と予測**（新しいメールへの適用）

使う技術は以下の通り：

| 機能      | 使用ライブラリ                           |
| ------- | --------------------------------- |
| データ前処理  | pandas, scikit-learn              |
| 単語ベクトル化 | CountVectorizer / TfidfVectorizer |
| モデル     | Naive Bayes（ナイーブベイズ）              |
| 評価      | 精度, 適合率, 再現率                      |

---

## 3. 実装：スパム判定AIを作ってみよう

### 3.1 必要なライブラリをインストール

```bash
pip install pandas scikit-learn
```

### 3.2 サンプルデータを用意（ダミーでもOK）

```python
import pandas as pd

data = {
    'text': [
        '今すぐお金がもらえる！',
        '会議は15時からです。',
        '無料で稼げる方法はこちら',
        '明日の予定を確認してください',
        'あなたに特別なオファーがあります！',
        '資料を共有しましたのでご確認ください'
    ],
    'label': [1, 0, 1, 0, 1, 0]  # 1: スパム, 0: 正常メール
}

df = pd.DataFrame(data)
```

### 3.3 テキストをベクトル化（CountVectorizer）

```python
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(df['text'])
```

### 3.4 モデルを訓練（Naive Bayes）

```python
from sklearn.naive_bayes import MultinomialNB

y = df['label']
model = MultinomialNB()
model.fit(X, y)
```

### 3.5 新しいメールを予測！

```python
test_mail = ['今すぐクリックで100万円！']
X_test = vectorizer.transform(test_mail)
prediction = model.predict(X_test)

print('スパム判定結果:', 'スパム' if prediction[0] == 1 else '正常')
```

---

## 4. 実務で役立つTIPS & よくある落とし穴

### ✅ 実務TIPS：

* **TfidfVectorizer**を使うと、「よく使われる単語を抑えつつ、特徴的な単語を強調」できます。
* モデル精度が低い場合は、**StopWordsの除去** や **n-gramの導入**も検討しましょう。
* scikit-learnの`pipeline`機能を使うと、前処理とモデルを一括管理できます。

### ❌ よくあるエラーと対策：

| 問題                 | 原因                           | 解決策                      |
| ------------------ | ---------------------------- | ------------------------ |
| 精度が異常に高い/低い        | データが偏っている                    | データのバランス確認、クロスバリデーション導入  |
| 新しいデータで予測できない      | `fit_transform`をテストデータに使っている | テストには`transform`のみ使うこと   |
| UnicodeDecodeError | データ読み込み時のエンコーディング            | `encoding='utf-8'` で指定する |

---

## 5. 応用：もっと本格的なスパム判定を目指すなら？

より実用的なスパムフィルタを目指すなら、以下の拡張がオススメです：

* **大規模な実データセット**（例：SMS Spam Collection）
* **TfidfVectorizer + ロジスティック回帰モデル**の組み合わせ
* **日本語対応（MeCab + Janome + sklearn）**
* **FlaskやFastAPIと組み合わせてWeb API化**

---

## 6. まとめ：シンプルだけど“使える”AIを自分で作れる！

スパム判定は、単純なルールベースでは限界がありますが、AI（機械学習）を使うことで柔軟に対応可能です。しかも、**Pythonとscikit-learnさえあれば自作できる**のです！

### ✅ メリット：

* 実データに近いタスクでNLPを学べる
* 機械学習のパイプラインが体験できる
* 少量データでも動かせるのでPoCに最適

### ⚠️ デメリット：

* 小規模データでは過学習リスクあり
* 日本語対応はひと工夫が必要

---

## 🚀 最後に

機械学習は「魔法」ではありませんが、「手順」を踏めば誰でも動くAIが作れます。
次回は、このスパム判定AIを**Webアプリ化する方法**も紹介予定です！

気になる方は、ぜひ「いいね」や「ストック」をお願いします 🙌
コメント・質問も大歓迎です！
【Android Studio / Google Play Console】デバッグシンボル未アップロード警告対策

# 概要

下記のような警告が出たので、その対策方法を解説。

```
この App Bundle にはネイティブ コードが含まれ、デバッグ シンボルがアップロードされていません。クラッシュや ANR を簡単に分析、デバッグできるよう、シンボル ファイルをアップロードすることをおすすめします。
```

# 対策

## 1. コード修正

app/build.gradleに以下を追加

```build.gradle
android {
    buildTypes {
        release {
            ndk {
                debugSymbolLevel = 'SYMBOL_TABLE'
            }
        }
    }
}
```

## 2. シンボルファイルのzip化

aabビルド後、以下の場所にシンボルファイルが出力される。

```
app/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib
```

下記コマンドでzipファイル化。

```
cd app/build/intermediates/merged_native_libs/release/out/lib
zip -r native-debug-symbols.zip arm64-v8a armeabi-v7a x86 x86_64
```

## 3. Google Play Consoleにアップロード

aabアップロード後、右側の`ネイティブデバッグシンボル(.zip)をアップロード`から、先ほど作成したzipファイルをアップロード。

![説明画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694762/3f7f3ee1-a75c-4006-8805-ee3bcbb1b06f.png)
猿でもわかるAIプログラミングシリーズ 🐵💻 データクリーニングって何？やってみよう



### 1. はじめに：AIの前に立ちはだかる“汚れたデータ”

AI開発の現場でよく聞くセリフ：

> 「モデルの精度が出ないんだよね…」
> 「学習に使ったデータ、ちょっと怪しくて…」

実は、こうしたトラブルの多くは**データクリーニングの不足**が原因です。

AIや機械学習は魔法ではありません。\*\*「ゴミを入れればゴミが出る（Garbage In, Garbage Out）」\*\*という原則が厳然と存在します。
つまり、どれだけアルゴリズムが優れていても、**学習データが汚れていたら意味がない**のです。

本記事では、実務でも頻出する「CSVデータの前処理・クリーニング」について、**Python + Pandas**を使って実例を交えながら解説します。

---

### 2. データクリーニングとは？何をするの？

ざっくり言えば、データクリーニングとは以下のような処理の総称です：

| 処理内容      | 目的                  |
| --------- | ------------------- |
| 欠損値の処理    | NaNを埋める・除外する        |
| 異常値の検出・除去 | 範囲外の値・ありえないデータを取り除く |
| 重複データの除外  | 同じデータが重複していないか確認    |
| フォーマット統一  | 日付や文字列の形式を揃える       |
| 不要な列・行の削除 | 分析に関係ないデータを削る       |

これらの作業は地味ですが、モデルの精度を劇的に変える「土台作り」なんです。

---

### 3. 実践：Pandasでデータクリーニングしてみよう

今回は以下のようなCSVを例に説明します。

#### 👇 サンプルデータ（`sales.csv`）

```csv
id,date,customer,amount,category
1,2024/01/01,A社,1000,雑貨
2,2024/01/02,B社,,食品
3,2024/01/03,C社,-500,食品
4,2024/01/03,C社,-500,食品
5,不明,D社,3000,家電
```

#### ステップ1：CSV読み込み

```python
import pandas as pd

df = pd.read_csv("sales.csv")
print(df)
```

#### ステップ2：欠損値の確認と処理

```python
# 欠損値の有無を確認
print(df.isnull().sum())

# amountの欠損値を0にする（もしくは中央値など）
df["amount"].fillna(0, inplace=True)
```

#### ステップ3：異常値の検出（マイナス金額など）

```python
# amount < 0 をフィルタリング
invalid = df[df["amount"] < 0]
print("異常値:\n", invalid)

# 取り除く
df = df[df["amount"] >= 0]
```

#### ステップ4：重複データの除去

```python
df = df.drop_duplicates()
```

#### ステップ5：日付フォーマットの統一

```python
# '不明'という文字列をNaTに変換
df["date"] = pd.to_datetime(df["date"], errors='coerce')

# NaTの行を削除
df = df.dropna(subset=["date"])
```

---

### 4. 現場で役立つTIPS＆ハマりがちな罠

#### ✅ 実践的なポイント

* 欠損値はすぐに埋めるのではなく、\*\*「なぜ欠損したか」\*\*をまず確認する
* 異常値は**ドメイン知識**（例：売上がマイナスになることはあるか？）と照らし合わせて判断
* 重複は`subset`引数を使って**特定のカラムだけで比較**できる

```python
df.drop_duplicates(subset=["date", "customer"])
```

#### ❌ よくあるミス

* 文字列と数値が混在 → `astype()`で明示的に変換を
* 欠損値を`fillna()`したのに`inplace=True`を忘れて効果が反映されていない
* 日付の型が`object`のまま → モデル学習時にエラーになりやすい

---

### 5. 応用編：関数化して再利用できるようにしよう

本番業務では、何度も似た処理を行うので関数化しておくと便利です。

```python
def clean_sales_data(df):
    df = df.copy()
    df["amount"].fillna(0, inplace=True)
    df = df[df["amount"] >= 0]
    df = df.drop_duplicates()
    df["date"] = pd.to_datetime(df["date"], errors="coerce")
    df = df.dropna(subset=["date"])
    return df

df_clean = clean_sales_data(df)
```

さらに、**scikit-learnの前処理パイプライン**と組み合わせることで、機械学習ワークフローに統合も可能です（これは別記事で詳しく紹介予定！）。

---

### 6. まとめ：データクリーニングは地味だが最強

| 項目     | 内容                                       |
| ------ | ---------------------------------------- |
| ✅ メリット | モデルの精度UP／不具合の早期発見／再現性の確保                 |
| ⚠️ 注意点 | データの意味を無視した機械的な処理は危険                     |
| 🌱 将来性 | 自動化ツール（Great Expectationsなど）との連携も今後のトレンド |

---

データクリーニングは派手な仕事ではありません。ですが、**良いモデルの裏には、必ず丁寧な前処理がある**ものです。

まずはこの記事のサンプルから試してみて、「汚れたデータ」に立ち向かう第一歩を踏み出しましょう💪

---

### 📎 次回予告

次回は、「簡単なスパムメール判定AIを作ってみよう」です。お楽しみに！

---

ご質問やフィードバックがあれば、コメントでぜひ教えてください！
音声ファイルの前処理手法の比較

## はじめに
音声ファイルを処理し、それを文字に起こし、ベクトル化してRAGを構築する。このフローの中で、音声ファイルのノイズによるRAGの検索精度悪化を避けよう、というのが今回の実験のモチベーションです。

本記事では、音声ファイルに対して複数のノイズ除去手法を適用し、その聞こえ方と文字起こし時の精度にどのような違いが出るかを比較します。音声ファイルはwav形式で、文字起こしにはAssemblyAIを用いています。

比較対象としたのは、統計的手法のnoisereduce、STFTを用いたlibrosa処理、古典的なバンドパスフィルタ（scipy）、深層学習モデルのdemucsという4種類です。

## 比較・検証
まず、各手法について簡単に説明します。
- noisereduce
    - Python製のシンプルなノイズリダクションライブラリで、音声信号から統計的にノイズ成分を除去
```python
import noisereduce as nr

denoised = nr.reduce_noise(y=data.astype(np.float32), sr=rate)
```
- librosa
    - librosaで音声を短時間フーリエ変換（STFT）し、周波数領域で不要な成分をマスキングして除去する手法
```python
import librosa, numpy as np

y, sr = librosa.load("input.wav", sr=None)
mag, phase = np.abs(librosa.stft(y)), np.angle(librosa.stft(y))
noise = np.mean(mag[:, :30], axis=1, keepdims=True)
denoised = np.maximum(mag - noise, 0) * np.exp(1j * phase)
y_denoised = librosa.istft(denoised)
```
- バンドパスフィルタ（scipy）
    - scipyの信号処理ツールを使って特定の周波数帯域のみを通し、それ以外を除去する古典的な方法
```python
import soundfile as sf
from scipy.signal import butter, lfilter

y, sr = sf.read("input.wav")
b, a = butter(5, [300/(sr/2), 3400/(sr/2)], btype='band')
y_filtered = lfilter(b, a, y)
sf.write("output.wav", y_filtered, sr)
```
- demucs
    - Meta（旧Facebook）が開発した深層学習ベースの音源分離モデル
```python
import subprocess

subprocess.run([
    "demucs",
    "--two-stems", "vocals",
    "--out", "output",
    "input.wav"
], check=True)
```
### 各手法でのノイズ除去後の聞こえ方
4つのノイズ除去手法を適用した後の音声を比較したところ、noisereduceは最も耳あたりが良く、クリアに聞こえる結果となりました。ただし、demucs・librosa・バンドパスフィルタについては、明確な差はあまり見られず、いずれもノイズ除去によって聞き取りやすさは一定向上していました。

### 文字起こしの精度
以下は今回行った処理を擬似的に簡潔に再現したものです。
```python
import assemblyai as aai

aai.settings.api_key = "YOUR_API_KEY"
transcriber = aai.Transcriber()
config = aai.TranscriptionConfig(speaker_labels=True, language_code="ja")

transcript = transcriber.transcribe("input.wav", config=config)

for u in transcript.utterances:
    print(f"スピーカー {u.speaker}: {u.text}")
```
AssemblyAIのSTTを用いて、各手法でノイズ除去後の音声を文字起こしした結果、全体的な認識精度には大きな差は見られませんでした。一方で、話者分離（speaker diarization）の精度には差があり、librosaを用いた処理が最も正確に話者を区別できていました。逆に、noisereduceは音質こそ良好でしたが、話者分離が正しく行われないケースが多く見られました。

## 最後に
本記事では、4つの異なるノイズ除去手法を用いて音声処理を行い、それぞれの聞こえ方や文字起こし精度への影響を比較しました。その結果、音質が最もクリアだったのはnoisereduceでしたが、文字起こしにおいてはlibrosaによる前処理が最も安定した話者分離精度を示しました。

そのため、今回のプロジェクトでは最終的にlibrosaを用いた前処理を採用し、AssemblyAIでの文字起こしの前段階として組み込みました。

なお、今回の評価は主に音声の主観的な聞こえ方やSTT出力の見た目に基づく定性的な判断にとどまっています。今後より信頼性の高い比較を行うためには、以下のような定量的な評価指標の導入が有効です。

- SNR（Signal-to-Noise Ratio）：ノイズ除去前後の信号の明瞭さを数値化
- WER（Word Error Rate）：文字起こしの誤り率（正解テキストとの比較）
- SAR/SIR（音源分離評価指標）：demucsなどの音源分離性能評価にも使用可能

こうした評価指標を今後の検証に取り入れることで、より客観的かつ再現性のある音声処理フローの最適化が可能になると考えています。
指定年月の第n w曜日の日付を求める

--概要　　　：任意の年月の第ｎ回目の〇曜日の日付を求めます
--引数　　　：[@year] 　　…対象年
--　　　　　　[@month]　　…対象月
--　　　　　　[@num]　　　…何番目の曜日か、第1曜日なら1、第3曜日なら3
--　　　　　　[@dayofweek]…1（日曜日）から7（土曜日）までの数字を返します
--戻り値　　：任意の年月の第n日曜日の日付
IF (EXISTS(SELECT * FROM sys.objects WHERE (type = 'FN') AND (name = 'fn_getdate_dayofweek')))
BEGIN
    DROP FUNCTION fn_getdate_dayofweek;
END
GO

CREATE FUNCTION fn_getdate_dayofweek
(
    @yyyy INT
  , @mm INT
  , @num INT
  , @dayofweek INT
)
RETURNS DATETIME
AS
BEGIN
    --年および月の文字列型を変数に取得します
    DECLARE @str_yyyy VARCHAR(4);
    SET @str_yyyy = CONVERT(VARCHAR, @yyyy);

    DECLARE @str_mm VARCHAR(2);
    SET @str_mm = RIGHT('00' + CONVERT(VARCHAR, @mm), 2);

    --日付データの作業用変数です
    DECLARE @date_work DATETIME;
    SET @date_work = NULL;

    --指定した年月の1日の曜日を取得します
    SET @date_work = CONVERT(DATETIME, @str_yyyy + '-' + @str_mm + '-01');
    DECLARE @dayofweek_first INT;
    SET @dayofweek_first = DATEPART(weekday, @date_work);

    --指定した曜日の第1曜日の日を求めます
    DECLARE @int_dd INT;
    SET @int_dd = @dayofweek - @dayofweek_first + 1;
    IF (@int_dd <= 0)
    BEGIN
        SET @int_dd = @int_dd + 7;
    END

    --求めた日を日付型に変換します。
    SET @date_work = CONVERT(DATETIME, @str_yyyy + '-' + @str_mm + '-' + CONVERT(VARCHAR, @int_dd));

    --指定した回数分、週を移動します。
    RETURN (DATEADD(day, (@num - 1) * 7, @date_work));
END
GO
指定年月の月末日を求める

--概要　　　：引数に指定された年の春分の日を返します
--引数　　　：[@yyyy]…対象年
--戻り値　　：引数に指定された年の春分の日
IF (EXISTS(SELECT * FROM sys.objects WHERE (type = 'FN') AND (name = 'fn_getdate_monthend')))
BEGIN
    DROP FUNCTION fn_getdate_monthend;
END
GO

CREATE FUNCTION fn_getdate_monthend
(
    @year INT
  , @month INT
)
RETURNS DATETIME
AS
BEGIN
    --対象月の月初の文字列型を求めます
    DECLARE @strdate_start VARCHAR(10);
    SET @strdate_start = CONVERT(VARCHAR, @year) + '-' + CONVERT(VARCHAR, @month) + '-1';

    --対象月の月初の日付型を求めます
    DECLARE @date_month_start DATETIME;
    SET @date_month_start = CONVERT(DATETIME, @strdate_start);

    --対象月の月初の1カ月後を求めます
    DECLARE @date_month_plus1 DATETIME;
    SET @date_month_plus1 = DATEADD(month, 1, @date_month_start);

    --対象月の月初の1カ月後の前日を返します
    RETURN DATEADD(day, -1, @date_month_plus1);
END
GO