リアルタイム音声認識とAIによるSOAPカルテ自動生成を実現する、医療従事者向けプロトタイプアプリケーション
本システムは、医師と患者の会話をリアルタイムで音声認識し、AIが自動的にSOAP形式(Subjective, Objective, Assessment, Plan)の電子カルテを生成するWebアプリケーションです。
医療現場では、診察後のカルテ記入作業が医師の大きな負担となっています。本システムは、この作業を自動化し、医師が診察に集中できる環境を提供することを目的としています。
基盤2-3時間、UIUX実装は2-3日の範囲内で実装可能なMVP(Minimum Viable Product)として設計されています。
- Web Speech APIを使用したブラウザネイティブな音声認識
- 連続認識モード対応
- 日本語(ja-JP)完全対応
- マイク許可管理
OpenAI GPT-4/5の軽量モデルを使用した、以下の情報を含む詳細な医療記録の自動生成:
- 主訴(Chief Complaint)
- 症状期間
- 現病歴の詳細
- 症状リスト
- 重症度評価
- 発症様式
- 随伴症状
- 既往歴
- 現在服用中の薬剤
- バイタルサイン(血圧、脈拍、体温、呼吸数)
- 身体所見
- 検査所見
- 診断名(日本語)
- ICD-10コード(国際疾病分類)
- 鑑別診断リスト
- 臨床的評価
- 治療方針
- 処方薬詳細(薬剤名、用量、用法、期間)
- 追加検査項目
- 専門医への紹介
- フォローアップ計画
- 患者指導内容
生成されたSOAPカルテを音声で確認できる、Text-to-Speech(TTS)機能を搭載:
- Web Speech Synthesis APIを使用したブラウザネイティブな音声合成
- 日本語音声による自然な読み上げ
- 読み上げ速度の調整機能(0.5x / 0.75x / 1.0x / 1.25x / 1.5x)
- 複数の日本語音声から選択可能
- SOAP全項目の構造化された読み上げ
- 要約 → 患者情報 → S(主観的情報)→ O(客観的情報)→ A(評価・診断)→ P(治療計画)
- 処方内容の詳細読み上げ(薬剤名、用量、用法、期間)
- 再生/停止コントロール
- 手が離せない状況でのカルテ確認
- 視覚的な確認と聴覚的な確認による二重チェック
- 長文カルテの内容把握がより容易に
- アクセシビリティ向上(視覚障害のあるユーザー支援)
- レスポンシブデザイン(PC/タブレット/スマートフォン対応)
- リアルタイムステータス表示
- 2カラムレイアウト(入力 | 出力)
- PC版:リサイズ可能な分割ビュー、レイアウトプリセット機能
- モバイル版:アコーディオン形式の切り替え
- アクセシビリティ対応(ARIA属性、セマンティックHTML)
| 技術 | バージョン | 用途 |
|---|---|---|
| Next.js | 16.1.6 | Reactフレームワーク(App Router使用) |
| React | 19.2.3 | UIライブラリ |
| TypeScript | 5.x | 型安全性の確保 |
| Tailwind CSS | 4.x | ユーティリティファーストCSS |
| 技術 | 用途 |
|---|---|
| Next.js API Routes | サーバーサイドAPI |
| OpenAI API | GPT-4/5によるテキスト生成 |
| OpenAI Embeddings API | text-embedding-3-small によるセマンティック医療辞書検索 |
| 技術 | 用途 |
|---|---|
| Web Speech API (Recognition) | ブラウザネイティブ音声認識(音声→テキスト) |
| Web Speech API (Synthesis) | ブラウザネイティブ音声合成(テキスト→音声) |
| ツール | 用途 |
|---|---|
| ESLint | コード品質チェック |
| PostCSS | CSS処理 |
| Git | バージョン管理 |
┌─────────────────────────────────────────────────────────┐
│ Browser │
│ ┌───────────────────────────────────────────────────┐ │
│ │ React Component (page.tsx) │ │
│ │ │ │
│ │ ┌────────────┐ ┌──────────────────┐ │ │
│ │ │ Web Speech │ │ User Interface │ │ │
│ │ │ API │─────────▶│ - Input Panel │ │ │
│ │ │ (ja-JP) │ │ - Output Panel │ │ │
│ │ └────────────┘ │ - Controls │ │ │
│ │ └──────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ │ HTTP POST │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Next.js API Route (/api/analyze) │ │
│ │ - リクエスト検証 │ │
│ │ - プロンプト構築 │ │
│ │ - OpenAI API呼び出し │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
└────────────────────────────┼────────────────────────────┘
│
▼
┌─────────────────┐
│ OpenAI API │
│ GPT-6 Luna │
│ JSON Response │
└─────────────────┘
medical-scribe-demo/
├── src/
│ └── app/
│ ├── api/
│ │ └── analyze/
│ │ ├── route.ts # OpenAI API統合
│ │ ├── prompt.ts # システムプロンプト定義
│ │ └── types.ts # TypeScript型定義
│ ├── globals.css # グローバルスタイル
│ ├── layout.tsx # ルートレイアウト
│ └── page.tsx # メインページ(音声認識・読み上げ・UI)
├── .env.local # 環境変数(APIキー)
├── package.json # 依存関係
└── README.md # このファイル
- Node.js 18.x 以上
- npm または yarn
- OpenAI APIキー(現在稼働テストとして設定中)
- Chromeまたは最新のブラウザ(Web Speech API対応)
- リポジトリをクローン
git clone https://github.com/BoxPistols/medical-scribe-demo.git
cd medical-scribe-demo- 依存関係をインストール
npm install- 環境変数を設定
プロジェクトルートに .env.local ファイルを作成:
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxOpenAI APIキーは OpenAI Platform で取得できます。
- 開発サーバーを起動
npm run dev- ブラウザでアクセス
http://localhost:3000
-
録音開始
- 「録音」ボタンをクリック
- ブラウザがマイクへのアクセス許可を要求(初回のみ)
- 録音中は「録音中」ステータスが表示される
-
会話の入力
- 音声で会話を入力
- または、テキストエリアに直接入力も可能
-
SOAPカルテ生成
- 「カルテ生成」ボタンをクリック
- AIが会話を解析(5-15秒程度)
- 右パネルに詳細なSOAPカルテが表示される
-
カルテの音声読み上げ(オプション)
- カルテパネル右上の「読み上げ」ボタンをクリック
- SOAP全項目が日本語音声で読み上げられる
- 読み上げ中は「停止」ボタンで中断可能
- 速度調整: 0.5倍速〜1.5倍速まで選択可能
- 音声選択: 利用可能な日本語音声から選択可能(ブラウザ・OSに依存)
- 手が離せない状況や、聴覚的な確認をしたい場合に便利
-
結果の確認
- 要約、患者情報、SOAP各セクションを確認
- バイタルサイン、処方内容などの詳細情報を閲覧
-
クリア
- 「クリア」ボタンで全データをリセット
以下のような会話を入力してテストできます:
医師: 今日はどうされましたか?
患者: 2週間くらい前から頭痛がひどくて、特に朝起きたときに痛みます。ズキズキする感じです。
医師: 他に症状はありますか?
患者: 少しめまいと吐き気があります。あと最近疲れやすいです。
医師: 既往歴はありますか?何か薬を飲んでいますか?
患者: 高血圧で5年前からアムロジピンを飲んでいます。
医師: では診察しますね。血圧を測りましょう。
患者: はい。
医師: 血圧は145/95、少し高めですね。体温は36.8度、脈拍は78です。目を見せてください。瞳孔は正常ですね。
医師: 頭のCTは必要ないと思いますが、念のため血液検査をしましょう。処方箋を出しますね。
患者: ありがとうございます。
医師: ロキソプロフェン60mgを1日3回、食後に7日分出します。あとアムロジピンは継続してください。1週間後にまた来てください。
会話テキストを受け取り、SOAP形式のカルテを生成します。
リクエスト
{
text: string // 会話テキスト(必須)
}レスポンス(成功時)
{
summary: string,
patientInfo: {
chiefComplaint: string,
duration: string
},
soap: {
subjective: {
presentIllness: string,
symptoms: string[],
severity: string,
onset: string,
associatedSymptoms: string[],
pastMedicalHistory: string,
medications: string[]
},
objective: {
vitalSigns: {
bloodPressure: string,
pulse: string,
temperature: string,
respiratoryRate: string
},
physicalExam: string,
laboratoryFindings: string
},
assessment: {
diagnosis: string,
icd10: string,
differentialDiagnosis: string[],
clinicalImpression: string
},
plan: {
treatment: string,
medications: Array<{
name: string,
dosage: string,
frequency: string,
duration: string
}>,
tests: string[],
referral: string,
followUp: string,
patientEducation: string
}
}
}レスポンス(エラー時)
{
error: string // エラーメッセージ
}-
入力検証
- テキストの存在確認
- モデルIDの検証とフォールバック
- レート制限チェック(gpt-6-luna: 50回/日)
-
セマンティック医療辞書検索
- 入力テキスト(先頭1000文字)を
text-embedding-3-smallでベクトル化 - 事前計算済み医療用語データセットとCosine Similarity検索
- 上位5件(類似度 >= 0.3)の医療用語・ICD-10コードを取得
- 失敗時は検索スキップ(フォールバック)
- 入力テキスト(先頭1000文字)を
-
プロンプト構築
- 静的システムプロンプト(医療記録専門家ロール + JSON出力形式)
- 動的医療コンテキスト(検索結果の注入)
- ストリーミング/非ストリーミング両対応
-
OpenAI API呼び出し
- モデル:
gpt-6-luna - レスポンス形式:
json_object - トークン上限: 16000
- モデル:
-
レスポンス処理
- JSON パース
- トークン使用量・コスト計算
- エラーハンドリング(401/429/SyntaxError)
- クライアントへの返却
User Speech
↓
Web Speech API (Browser)
↓
Recognition Event
↓
State Update (React)
↓
Transcript Display
User Click "Generate"
↓
Fetch API Call
↓
Next.js API Route (/api/analyze)
↓
┌─── セマンティック医療辞書検索 ───┐
│ 入力テキスト(先頭1000文字) │
│ ↓ │
│ text-embedding-3-small (dim=256)│
│ ↓ │
│ Cosine Similarity検索 │
│ (事前計算済み50エントリと比較) │
│ ↓ │
│ 上位5件の医療用語・ICD-10を │
│ システムプロンプトに注入 │
└──────────────────────────────────┘
↓
System Prompt + Medical Context + User Text
↓
OpenAI API (GPT-6 Luna)
↓
JSON Response
↓
Validation & Error Handling
↓
State Update (React)
↓
SOAP Display with Structured Data
User Click "Read Aloud"
↓
Extract Text from SOAP Note
↓
Format for Natural Speech
- 要約 → 患者情報 → S → O → A → P
- 区切り文字、読点の最適化
↓
Create SpeechSynthesisUtterance
- Language: ja-JP
- Rate: 0.5x ~ 1.5x (User Setting)
- Voice: Selected Japanese Voice
↓
Web Speech Synthesis API (Browser)
↓
Audio Output (Speaker/Headphone)
制御機能:
- 再生中: 停止ボタンで即座に中断
- 速度調整: リアルタイムで変更可能
- 音声選択: ブラウザ対応の日本語音声から選択
本システムの核心は、OpenAI APIに送信するプロンプトの品質にあります。以下の戦略で医療記録の質を最大化しています。
あなたは経験豊富な医療記録専門家です。
明確なロールを設定することで、AIの出力品質を向上させています。
JSONスキーマを明示的に指定し、以下を実現:
- 構造化されたデータ出力
- フィールドの一貫性
- クライアント側の処理簡略化
会話から推測できる情報を最大限に活用し、臨床的に妥当な詳細を補完してください
限られた会話情報から、臨床的に矛盾のない記録を生成するよう指示しています。
医学用語を適切に使用し、実際の診療記録としての完成度を高めてください
実際の医療現場で使用される表現を優先させています。
不明な情報は「記載なし」と明記してください
推測と事実を明確に区別し、医療安全を確保しています。
SOAP生成の精度を向上させるため、text-embedding-3-smallを使ったセマンティック検索で関連する医療用語・ICD-10コードを動的にプロンプトへ注入しています。
音声テキスト「2週間前から頭が痛い、朝起きた時にズキズキする、めまいと吐き気もある」
↓
[1] text-embedding-3-small でテキストをベクトル化 (256次元)
↓
[2] 事前計算済み医療用語データセット (50エントリ) とCosine Similarity比較
↓
[3] 上位5件の関連医療用語を取得:
- 片頭痛 (G43.9) - 類似度: 0.78
- 頭痛 (R51) - 類似度: 0.75
- めまい (R42) - 類似度: 0.62
- 本態性高血圧症 (I10) - 類似度: 0.45
- 脳梗塞 (I63.9) - 類似度: 0.38
↓
[4] システムプロンプトの末尾に参考情報として注入
↓
[5] LLMがこの参考情報を踏まえてSOAPノートを生成
| 項目 | 従来(プロンプトのみ) | セマンティック検索導入後 |
|---|---|---|
| ICD-10コード | LLMの学習済み知識に依存。コードの記憶違いや存在しないコードを生成するリスクあり | 実在するICD-10コードを参考情報として提供。正確なコードが出力されやすい |
| 診断名の表記 | 口語表現のまま(例: 「頭痛」)出力されることがある | 正式な病名(例: 「片頭痛 G43.9」)が参照可能 |
| 鑑別診断 | LLMの推論のみで生成 | 類似度ベースで関連疾患が提示されるため、見落としが減少 |
| 関連症状の網羅性 | 会話に明示された症状のみ | 各疾患に紐づくキーワード(関連症状)が提示され、確認漏れを防止 |
| 処理コスト | embedding APIコールなし | +1回のembedding API呼び出し($0.000002/リクエスト、事実上無料) |
| レイテンシ | なし | +約100-200ms(embedding生成 + cosine similarity計算) |
50エントリの事前構築データセットで、日本のプライマリケアの主要疾患をカバーしています。
| カテゴリ | エントリ数 | 代表的な疾患例 |
|---|---|---|
| 呼吸器系 | 6 | 急性上気道炎(J06.9)、肺炎(J18.9)、気管支喘息(J45.9) |
| 消化器系 | 5 | 急性胃腸炎(K52.9)、逆流性食道炎(K21.0)、過敏性腸症候群(K58.9) |
| 循環器系 | 5 | 本態性高血圧症(I10)、狭心症(I20.9)、心房細動(I48.9) |
| 内分泌・代謝 | 5 | 2型糖尿病(E11.9)、脂質異常症(E78.5)、甲状腺機能低下症(E03.9) |
| 筋骨格系 | 6 | 腰痛症(M54.5)、変形性膝関節症(M17.9)、痛風(M10.9) |
| 神経系 | 3 | 片頭痛(G43.9)、不眠症(G47.0)、てんかん(G40.9) |
| 精神科 | 3 | うつ病(F32.9)、全般性不安障害(F41.1)、パニック障害(F41.0) |
| 感染症 | 4 | インフルエンザ(J10.1)、COVID-19(U07.1)、帯状疱疹(B02.9) |
| 皮膚科 | 3 | 蕁麻疹(L50.9)、湿疹(L30.9)、アトピー性皮膚炎(L20.9) |
| 泌尿器系 | 2 | 膀胱炎(N30.9)、前立腺肥大症(N40) |
| 症状・症候 | 5 | 発熱(R50.9)、胸痛(R07.9)、腹痛(R10.4)、めまい(R42)、頭痛(R51) |
| その他 | 3 | 鉄欠乏性貧血(D50.9)、結膜炎(H10.9)、中耳炎(H66.9) |
各エントリは以下の情報を持ちます:
- ICD-10コード: WHO国際疾病分類コード
- 病名(日本語・英語): 正式名称
- 別名・口語表現: 音声認識で入力される可能性のある表現(例: 「風邪」「おなかの風邪」)
- 関連症状キーワード: 疾患に紐づく主要症状
- embeddingモデル:
text-embedding-3-small(OpenAI) - ベクトル次元数: 256(デフォルト1536から削減、ファイルサイズと精度のバランス)
- 事前計算済みファイル:
src/data/medical-terms-embedded.json(約235KB) - 検索閾値: cosine similarity >= 0.3
- 返却件数: 上位5件
- フォールバック: embedding API呼び出し失敗時は従来通り(コンテキストなし)でSOAP生成を継続
# 1. 元データを編集(embeddingなし)
# src/data/medical-terms-source.json を編集
# 2. embeddingを再生成
pnpm generate:embeddings
# 3. 生成されたファイルをcommit
# src/data/medical-terms-embedded.json"Clinical Professionalism meets Modern Web"
医療現場の信頼性と、モダンWebアプリケーションの使いやすさを両立しています。
/* ベースカラー - 清潔感のあるクリニカルパレット */
--bg-primary: #fafbfc; /* 背景 */
--bg-secondary: #ffffff; /* パネル */
/* アクセントカラー - ティール(医療的で落ち着いた印象) */
--accent-primary: #14b8a6; /* プライマリアクション */
--accent-warm: #f59e0b; /* 録音ボタン */
/* SOAPカラー - 視覚的識別性 */
--soap-s: #ef4444; /* 主観的情報(赤) */
--soap-o: #3b82f6; /* 客観的情報(青) */
--soap-a: #10b981; /* 評価(緑) */
--soap-p: #8b5cf6; /* 計画(紫) */| 用途 | フォント | 特徴 |
|---|---|---|
| UI全般 | DM Sans | 可読性が高く、モダンな印象 |
| データ表示 | JetBrains Mono | 等幅で、医療データの視認性向上 |
/* モバイル */
@media (max-width: 640px)
/* タブレット */
@media (max-width: 1024px)
/* デスクトップ */
@media (min-width: 1024px)- セマンティックHTML(
<header>,<main>,<section>) - ARIA属性(
aria-label,aria-pressed) - キーボードナビゲーション対応
- フォーカス表示
- カラーコントラスト比(WCAG AA準拠)
本システムは豊富なキーボードショートカットを備えており、クロスプラットフォーム対応を実装しています。
| OS | 修飾キー | 例 |
|---|---|---|
| macOS | Cmd |
Cmd + R |
| Windows/Linux | Ctrl |
Ctrl + R |
- ブラウザの
navigator.platformとnavigator.userAgentでOSを自動判定 - ショートカットの動作・表示が自動的にOSに最適化される
- ユーザーによるカスタマイズも可能(設定モーダルから変更)
| 機能 | デフォルトキー | 修飾キー付き |
|---|---|---|
| 録音開始/停止 | R |
Cmd/Ctrl + R |
| カルテ生成 | A |
Cmd/Ctrl + A |
| 読み上げ開始/停止 | V |
Cmd/Ctrl + V |
| ショートカット設定 | K |
- |
| ヘルプ | H |
- |
※ 「修飾キー付き」モードは設定から切り替え可能
- ✅ 完了: カルテ音声読み上げ機能(速度調整、音声選択)
- 計画中 (Issue #2): データエクスポート/インポート機能
- JSON形式エクスポート/インポート
- CSV形式エクスポート(Excel対応)
- PDF形式エクスポート
- カルテのコピー機能(クリップボード)
- 履歴機能(LocalStorage/IndexedDB)
- 音声認識精度の表示
- 複数言語対応(英語、中国語)
- ✅ 完了: セマンティック医療辞書検索(text-embedding-3-small + ICD-10、50エントリ)
- 計画中: 医療用語データセット拡充(50 → 500エントリ)
- 計画中: MANBYO辞書・MEDIS標準マスター連携
- RxNorm API統合(標準薬剤名称)
- FHIR形式でのデータエクスポート
- 電子カルテシステムとの連携
- GPT-4による高精度解析
- ファインチューニングモデルの導入
- 医療画像解析統合
- リアルタイムAI提案機能
- ユーザー認証・権限管理
- 患者データベース統合
- 監査ログ機能
- HIPAA/GDPR準拠のセキュリティ
- マルチテナント対応
- クラウドバックアップ
- WebSocket によるリアルタイム通信
- PWA 対応(オフライン機能)
- E2Eテスト(Playwright/Cypress)
- パフォーマンス最適化
- CDN統合
-
デモンストレーション用途のみ
- 本システムは概念実証(PoC)として開発されています
- 実際の臨床現場での使用は想定していません
-
医療機器ではありません
- 薬機法(医薬品医療機器等法)の適用外です
- 診断や治療の根拠として使用しないでください
-
AI生成内容の不確実性
- GPT-6 Lunaの出力は100%正確ではありません
- 必ず医師による確認・修正が必要です
- 幻覚(Hallucination)により事実と異なる内容が生成される可能性があります
-
プライバシーとセキュリティ
- 実際の患者情報は入力しないでください
- OpenAI APIに送信されたデータは30日間保存されます
- 医療機関で使用する場合は、適切なデータ保護対策が必要です
-
ブラウザ互換性
- Web Speech APIはChrome/Edgeで最適に動作します
- SafariおよびFirefoxでは機能が制限される場合があります
OpenAI APIの使用料金が発生します:
- GPT-6 Luna: $0.10 / 1M input tokens, $0.50 / 1M output tokens(
src/app/api/analyze/types.tsのAVAILABLE_MODELSが正) - 1回の解析あたり約$0.001-0.005(会話の長さによる)
- 個人情報保護法: 実際の患者情報を扱う場合は適用対象
- 医療法: 医療広告規制に注意
- 利用規約: OpenAI利用規約の遵守が必要
MIT License
開発期間: メイン機能 2-3時間 / UIUXリファクタリング2-3日 技術スタック: Next.js 14, TypeScript, OpenAI API, Web Speech API