diff --git a/.env.example b/.env.example index 495816f..3c97b54 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,6 @@ FRONTEND_ENDPOINT=http://localhost:${FRONTEND_PORT} BACKEND_ENDPOINT=http://localhost:${BACKEND_PORT}/api/v1 OLLAMA_ENDPOINT=http://host.docker.internal:${OLLAMA_PORT}/v1 +MODEL_NAME_PREFIX=thor- + CLOUDFLARE_TUNNEL_TOKEN=changeme \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index af60fa3..3c36793 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,7 +46,7 @@ "rgba(79,236,236, 0.2)" ], // Error Lens - "errorLens.gutterIconsEnabled": true, + "errorLens.gutterIconsEnabled": false, "errorLens.gutterIconSet": "emoji", "errorLens.gutterEmoji": { "error": "🔥", diff --git a/CLAUDE.md b/CLAUDE.md index 47f9fde..d6e2c5c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Thor is an AI-driven full-stack monorepo project that extracts and analyzes step count data from Apple Healthcare exports. +Thor is an AI-driven full-stack monorepo that extracts step count data from Apple Healthcare XML exports to estimate and visualize sleep patterns, with LLM-generated feedback via Ollama. ## Architecture @@ -22,136 +22,138 @@ Thor-Monorepo/ ``` backend/src/ -├── core/ # Common utilities (environment variables, etc.) -├── routers/ # FastAPI endpoint definitions -├── schemas/ # Pydantic schemas (request/response models) -└── usecases/ # Business logic layer +├── core/ # Shared utilities (Envs class, constants) +├── routers/ # FastAPI endpoint definitions (thin layer only) +├── schemas/ # Pydantic request/response models +└── usecases/ # All business logic + ├── extract_steps/ # Apple XML parsing + ├── estimate_sleep/# K-means clustering → feature extraction → sleep time estimation + ├── llm_feedback/ # Ollama integration (via OpenAI-compatible SDK) + └── via_email/ # SMTP email delivery with Jinja2 templates ``` -**Important design principles:** +**Design principles:** +- `routers/` must only contain endpoint definitions — business logic goes in `usecases/` +- `schemas/` holds all Pydantic models +- `core/` contains shared utilities: `load_env.py` (Envs class) and `constants.py` -- `routers/` should only contain endpoint definitions -- Business logic must be placed in `usecases/` -- Request/response type definitions go in `schemas/` -- `core/` contains shared utilities like environment variables and middleware +### Backend API Endpoints (`/api/v1`) -### Frontend Architecture +1. **POST `/extract-steps`** — Parses Apple Healthcare XML, returns step data in 15-min intervals. Body is raw `text/xml`. Query params: `months_of_extract` (int) OR `start_date_of_extract`+`end_date_of_extract` (ISO 8601), `include_recorded_sleep` (bool). +2. **POST `/estimate-sleep`** — Runs ML pipeline (K-means → feature extraction → late-night detection → bed/wake time estimation). Returns daily sleep estimates + available LLM models. +3. **POST `/feedback`** — Fetches LLM feedback from Ollama for a previously saved estimation (identified by `id`). Params: `id`, `llm` (model name), `lang` (`ja`/`en`). +4. **POST `/via-email`** — One-shot workflow: extract → estimate → feedback → send email. FormData with `xml_file` (multipart) + `req` (JSON string). -Built with React Router v7, a full-stack framework with server-side rendering support. +**Data persistence pattern**: `extract-steps` generates a hash-based `id` and saves data as JSON to `VAULT_DIR`. Subsequent endpoints use this `id` to retrieve the saved data, enabling stateless requests. -## Development Environment +### Frontend Architecture -### Prerequisites +Single-page application (all UI in `app/routes/home.tsx`) with three UI states: +1. **Input** — Survey form (3 questions about phone habits + bedtime), XML file upload, optional email +2. **Loading** — Progress through extract → estimate → feedback stages +3. **Results** — Recharts timeline/bar charts for bed/wake times; LLM feedback rendered as Markdown via react-markdown -- VSCode with Dev Container extension -- Docker running -- GitHub SSH connection configured +Key directories: +- `app/components/` — Reusable components (survey-form, file-upload, result-view, ai-feedback) +- `app/utils/` — SWR hooks (`use-extract-steps`, `use-estimate-sleep`, `use-ai-feedback`) + fetch wrappers in `api.ts` +- `app/core/` — `constants.ts` (API endpoint), `survey-schema.ts` (Zod validation) +- `app/locales/` — `translation-ja.json` and `translation-en.json` (i18next) + +## Development Environment ### Container Services -- `thor-workspace`: Development workspace -- `thor-backend`: FastAPI server (port 8000) -- `thor-frontend`: React Router dev server (port 5173) -- `thor-ollama`: AI/LLM service (port 11434) +- `thor-workspace`: Dev Container (VSCode) +- `thor-backend`: FastAPI on port 8000 +- `thor-frontend`: React Router dev server on port 5173 +- `thor-ollama`: Ollama LLM service on port 11434 ### Environment Variables -- Backend environment variables are managed in `backend/.env` -- Defined and accessed via the `Envs` class in `src/core/load_env.py` +**Backend** (`backend/.env`) — all defined in `src/core/load_env.py` as the `Envs` class: -## Development Commands - -### Task Runner - -This project uses Go-Task. View available commands with `task -l`. - -### Start Development Servers +Required: +``` +DATA_ID_SALT= # Salt for ID generation +MAIL_ADDRESS= # Gmail address +MAIL_USERNAME= # Same as MAIL_ADDRESS +MAIL_PASSWORD= # Gmail App Password (16 chars, not account password) +MAIL_FROM= # Sender address +``` -```bash -# Start backend -task backend:dev +Optional (defaults shown): +``` +IS_DEBUG=false +DATASTORE_DIR=./datastore +VAULT_DIR=./datastore/vault +OLLAMA_ENDPOINT=http://host.docker.internal:11434/v1/ +FRONTEND_ENDPOINT=http://localhost:5173 +API_V1_PREFIX=/api/v1 +MAIL_PORT=587 +MAIL_SERVER=smtp.gmail.com +``` -# Start frontend -task frontend:dev +**Frontend** (`frontend/.env`): +``` +VITE_BACKEND_ENDPOINT=http://localhost:8000 ``` -Backend runs at `http://localhost:8000`, Frontend at `http://localhost:5173`. +## Development Commands -### Format & Lint +This project uses Go-Task. View all commands with `task -l`. -```bash -# Format everything -task format +### Servers -# Lint everything -task lint +```bash +task backend:dev # FastAPI with auto-reload on port 8000 +task frontend:dev # Vite HMR on port 5173 +``` -# Backend only -task backend:format -task backend:fix +### Format, Lint & Type Check -# Frontend only -task frontend:format -task frontend:lint +```bash +task format # Format backend (Ruff) + frontend (Prettier) +task lint # Lint and auto-fix backend (Ruff) + frontend (ESLint) +task type-check # TypeScript type check (frontend only) +task check # CI-equivalent: format-check + lint-check + type-check (no auto-fix) ``` ### Tests ```bash -# Run all tests -task test - -# Backend only -task backend:test - -# Frontend only -task frontend:test +task test # Run all tests +task backend:test # pytest -v +task frontend:test # pnpm run test ``` -### Type Checking - +Run a single backend test file or function: ```bash -# Type check everything -task type-check - -# Frontend only -task frontend:type-check +task backend -- uv run pytest path/to/test_file.py -v +task backend -- uv run pytest path/to/test_file.py::test_function_name -v ``` -### CI-equivalent Checks +### Arbitrary Container Commands ```bash -# Run pre-push checks (format-check + lint-check + type-check) -task check +task backend -- # Run in backend container (e.g., uv add ) +task frontend -- # Run in frontend container (e.g., pnpm add ) ``` ## Commit Conventions -Git hooks are managed by Lefthook. - -### Commit Message Format - -``` -: -``` - -Allowed `` values: +Git hooks via Lefthook: +- **pre-commit**: Auto-formats and lints, stages fixes +- **pre-push**: Runs `task check` (no auto-fix) -- `feat`: New feature -- `fix`: Bug fix -- `refactor`: Code refactoring -- `chore`: Other changes +Commit message format: `: ` -### Automatic Hooks - -- **pre-commit**: Auto-formats and lints code, staging fixes automatically -- **pre-push**: Runs all checks (format-check + lint-check + type-check) +Allowed types: `feat`, `fix`, `refactor`, `chore` ## API Development Guide ### Receiving XML Files -For large files like Apple Healthcare's export.xml, use this pattern: +For raw XML bodies (not JSON), use `Body()` directly — `BaseModel` schemas expect JSON and won't work: ```python from fastapi import APIRouter, Body @@ -164,40 +166,20 @@ async def extract_steps( example="..." ) ): - # xml_data contains raw XML string pass ``` -Client-side usage: - ```bash -# Using curl curl -X POST -H 'Content-Type: text/xml' \ --data-binary @export.xml \ - http://localhost:8000/api/v1/extract-steps - -# Using JavaScript fetch -const file = document.getElementById('input').files[0]; -await fetch('http://localhost:8000/api/v1/extract-steps', { - method: 'POST', - headers: {'Content-Type': 'text/xml'}, - body: file -}); + 'http://localhost:8000/api/v1/extract-steps?months_of_extract=1' ``` -**Note**: Using a `BaseModel` schema expects JSON format and won't work with raw XML. Use `Body()` directly instead. - -## Package Management +### LLM Integration -- **Backend**: Uses `uv` (dependencies managed in `pyproject.toml`) -- **Frontend**: Uses `pnpm` (dependencies managed in `package.json`) +The backend uses the OpenAI Python SDK pointed at the Ollama endpoint — not actual OpenAI. The `OLLAMA_ENDPOINT` env var configures this. Model names come from Ollama's model list (e.g., `thor-gemma3:latest`). -Execute commands inside containers: - -```bash -# Run arbitrary command in backend container -task backend -- +## Package Management -# Run arbitrary command in frontend container -task frontend -- -``` +- **Backend**: `uv` (`pyproject.toml`) +- **Frontend**: `pnpm` (`package.json`) diff --git a/README.md b/README.md index f73694e..01f33c0 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,10 @@ Thor の AI 駆動のフルスタック https://github.com/273Do/Thor -https://github.com/273Do/Thor-Web-App-Frontend -https://github.com/273Do/Thor-Web-App-Backend ## アプリ抂芁 -本 web アプリは、iPhone のヘルスケアデヌタから睡眠パタヌンを掚定・分析するWebサヌビスです。 +本 web アプリは、iPhone のヘルスケアデヌタから睡眠パタヌンを掚定・分析し、LLM からフィヌドバックを取埗するWebサヌビスです。 ### フロヌ @@ -29,6 +27,20 @@ https://github.com/273Do/Thor-Web-App-Backend 本サヌビスは Apple ヘルスケアの XML ゚クスポヌトを利甚するため、iPhone ナヌザヌを察象ずしおいたす。Android など他のデバむスをお䜿いの方は、ヘルスデヌタを[指定の圢匏](https://github.com/273Do/Thor-Monorepo/blob/1923da19e313a79f8afa3c7c4b4036ce1542586a/backend/src/schemas/estimate_sleep.py#L29-L46)に倉換したうえで API を盎接呌び出すこずでご利甚いただけたす。 +### LLM を甚いたフィヌドバック + +独自のアルゎリズムによっお掚定された睡眠デヌタを参考に、ロヌカル LLM を䜿甚しおフィヌドバックを行いたす。察応経路によっお䜿甚するモデルずプロンプトが異なりたす。 + +- ブラりザ衚瀺では解析結果がグラフで芖芚的に確認できるため、LLM フィヌドバックは補助的な䜍眮づけです。応答速床がナヌザヌ䜓隓に盎結するため、軜量モデルを䜿甚し、プロンプトも睡眠傟向の芁玄ず手軜なアドバむスに絞っおいたす。 + +- メヌル送信では応答速床より質を優先できたす。グラフを䌎わないテキストのみの出力になりたす。睡眠パタヌ + ンの詳现な分析・専門的芋解・生掻習慣ぞの圱響・具䜓的な改善アドバむスを含む包括的なフィヌドバックを返すプロンプトを䜿甚しおいたす。 + +| 皮別 | モデル | レスポンス内容 | レスポンス速床 | 察応経路 | +| ---------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------- | -------------------- | +| 専門的なフィヌドバック | [gemma3:27b](https://ollama.com/library/gemma3:27b) | 解析結果をもずにした睡眠異垞・専門的芋解・生掻ぞの圱響・具䜓的な改善アドバむス | 1分皋床 | メヌル経由のみ | +| 簡易的なフィヌドバック | [mistral-small3.1:24b](https://ollama.com/library/mistral-small3.1:24b) | 睡眠傟向のサマリヌず、すぐに実践できる簡単なアドバむス | 10秒皋床 | ブラりザ・メヌル経由 | + ## プロゞェクト構成 - **Frontend**: React + TypeScript + Tailwind CSS @@ -58,24 +70,25 @@ git clone git@github.com:273Do/Thor-Monorepo.git ### 3. LLM の甚意 -以䞋のコマンドを実行しお`ollama/` 内に甚意された LLM を読み蟌みたす。 +以䞋のコマンドを実行しお`ollama/` 内に甚意された LLM を読み蟌みたす。かなり時間がかかりたす。 ```bash chmod +x ollama/setup.sh ./ollama/setup.sh ``` -うたく読み蟌めるず以䞋のように衚瀺されたすが、自䜜モデルthor-\*はベヌスモデルの重みを共有しお参照しおいるだけなので、ディスク容量が2倍になるわけではありたせん。Modelfile -で蚭定したパラメヌタの差分だけが远加で保持されおいたす。 +うたく読み蟌めるず以䞋のように衚瀺されたすが、自䜜モデルthor-\*はベヌスモデルの重みを共有しお参照しおいるだけなので、ディスク容量が2倍になるわけではありたせん。Modelfile で蚭定したパラメヌタの差分だけが远加で保持されおいたす。 ``` NAME ID SIZE MODIFIED -gemma3:12b hogehogehoge o GB x seconds ago -thor-gemma3:latest fugafugafuga o GB x seconds ago -thor-llama3:latest piyopiyopiyo o GB x seconds ago -llama3.1:8b fofoofoofoof o GB x seconds ago +gemma3:27b abcdefghijkl 17 GB X hours ago +thor-gemma3-27b:latest mnopqrstuvwx 17 GB X hours ago +thor-mistral-small3-1-24b:latest yz0123456789 15 GB X hours ago +mistral-small3.1:24b ABCDEFGHIJKL 15 GB X hours ago ``` +Modelfile の各パラメヌタ詳现は[公匏ドキュメント](https://docs.ollama.com/modelfile#parameter)を参照。 + ### 4. 起動方法 VSCode で Dev Container でプロゞェクトを開きたす。 @@ -122,7 +135,7 @@ claude ## 公開蚭定 -- cloudflare tunnel を䜿甚しおアプリを公開したす。 +- cloudflare tunnel を䜿甚しおアプリを公開したす。(未察応) 1. [Cloudflareダッシュボヌド](https://dash.cloudflare.com)から [Zero Trust] > [Networks] > [Overview] > [Manage Tunnels] > [Create new cloudflared Tunnel] を遞択したす。 diff --git a/backend/.env.example b/backend/.env.example index 928a8aa..0c4b81f 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -29,4 +29,4 @@ MAIL_USERNAME=${MAIL_ADDRESS} MAIL_PASSWORD=xxxxxxxxxxxxxxxx MAIL_FROM=${MAIL_ADDRESS} MAIL_PORT=587 -MAIL_SERVER=smtp.mail.com \ No newline at end of file +MAIL_SERVER=smtp.gmail.com \ No newline at end of file diff --git a/backend/datastore/prompts/prompt-en.md b/backend/datastore/prompts/prompt-en.md new file mode 100644 index 0000000..2c2c51f --- /dev/null +++ b/backend/datastore/prompts/prompt-en.md @@ -0,0 +1,24 @@ +As a college student sleep expert, analyze the step-estimated sleep data in English. Goal: trend analysis and anomaly detection. Prioritize clear writing over specific figures. + +**Data format**: "date": ["bed_time", "wake_time"] + +**Abnormal day**: bed_time ≥ median+2h late / sleep duration ≀ median-2h / under 5h / over 10h +**Judgment**: abnormal days ≥1/3→Needs Improvement / ≥1/5→Caution / else→Good + +Output in Markdown using the following format. + +# Sleep Data Analysis Report + +## 🧭 Sleep Pattern Judgment + +(judgment and rationale in 1 sentence) + +## 🔍 Analysis + +(bullet points for strengths/areas to improve; mention frequency and consecutive occurrences of abnormal days) + +## 💡 Advice + +(3 items tailored to judgment; if Needs Improvement, include recommendation for sleep clinic or campus health center) + +## ⚠ Notes diff --git a/backend/datastore/prompts/prompt-ja.md b/backend/datastore/prompts/prompt-ja.md new file mode 100644 index 0000000..80f51a7 --- /dev/null +++ b/backend/datastore/prompts/prompt-ja.md @@ -0,0 +1,24 @@ +倧孊生の睡眠専門家ずしお歩数掚定の睡眠デヌタを日本語で分析せよ。傟向把握・異垞怜知が目的。具䜓的な数倀より文章での説明を優先する。 + +**デヌタ圢匏**: "日付": ["就寝時間", "起床時間"] + +**ç•°åžžæ—¥**: 就寝が䞭倮倀+2h以䞊遅い / 睡眠時間が䞭倮倀-2h以䞊短い / 5h未満 / 10h超 +**刀定**: 異垞日が1/3以䞊→芁改善 / 1/5以䞊→芁泚意 / それ以倖→良奜 + +Markdownで以䞋の圢匏で出力せよ。 + +# 睡眠デヌタ分析レポヌト + +## 🧭 睡眠パタヌン刀定 + +刀定ず根拠を1文 + +## 🔍 分析結果 + +良い点・改善点を箇条曞き。異垞日の頻床・連続性に觊れる + +## 💡 アドバむス + +刀定に沿った3項目。芁改善の堎合は睡眠科・保健管理センタヌぞの盞談を含める + +## ⚠ 泚意事項 diff --git a/backend/datastore/prompts/specialized-prompt-en.md b/backend/datastore/prompts/specialized-prompt-en.md new file mode 100644 index 0000000..f192c9f --- /dev/null +++ b/backend/datastore/prompts/specialized-prompt-en.md @@ -0,0 +1,173 @@ +You are an expert in sleep medicine, chronobiology, and college student mental health. +You have knowledge in behavioral science and public health, and can provide evidence-based analysis and practical lifestyle improvement guidance. + +## Your Role + +The COVID-19 pandemic has drastically changed the learning environment, making it increasingly important to support college students in maintaining academic motivation and protecting their mental health. Analyze the **daily bed/wake time data** estimated from step count data, and provide **specialized and specific** feedback and advice on college students' health, lifestyle, and sleep habits. Note that college students tend to exhibit behavioral patterns that differ significantly from the general population. + +## Background on the Estimation Method + +The following explains how the data is estimated. Use this to correctly understand the accuracy and limitations of the analysis. + +- **Data used for the following estimates**: Step count data recorded by smartphones. +- **Late-night detection**: Estimated using a machine learning model with features including hourly step totals, record counts, and a survey item on usual bedtime (before 3:00 AM = 0, at or after 3:00 AM = 1). +- **Bed/wake time estimation**: + - Non-late-night days: Traces steps from 21:00 to 25:00 (next day 01:00); the first record is the bed time. The first record from 04:15 to 12:00 is the wake time. + - Late-night days: If records exist between 00:00–03:00, that time is used; otherwise 03:00 is the starting point. The longest interval between step records up to 21:00 is estimated as the sleep period. Survey-based time correction is applied. +- **Purpose of estimation**: Not precise time identification, but **trend analysis of bed/wake patterns and sleep anomaly detection**. Precise times are not required as the purpose is trend analysis and anomaly detection. Therefore, there is no need to include specific figures or tables; instead, focus on conveying the information clearly in writing. + +## Data Spec + +### Bed/Wake Time Data (JSON format) + +```json +{ + "2025-01-01": ["23:30", "07:15"], + "2025-01-02": ["01:00", "08:00"], + "2025-01-03": ["02:45", "11:00"] +} +``` + +- **Key**: Date (YYYY-MM-DD) +- **Value**: Array of `["bed_time", "wake_time"]` (HH:MM format) +- If either time could not be estimated, an empty string is used +- If both are empty strings, treat the day as having no data + +## Analysis Points (Required) + +Cover **all** of the following points. + +### Quantitative Sleep Analysis + +- Statistical evaluation of sleep duration using mean, median, and standard deviation +- Comparison with recommended sleep duration (ages 18–25: 7–9 hours) +- Sleep debt estimation (if chronic deficiency is observed) +- Proportion of missing data and its impact on analysis + +### Circadian Rhythm Evaluation + +- Consistency of bed/wake times (evaluated by standard deviation) +- Presence and severity of Social Jetlag + - Mid-sleep point difference of 1+ hour between weekdays and weekends = mild; 2+ hours = severe +- Tendency toward phase advance or delay (morningness/eveningness) + +### Sleep Pattern Judgment + +Evaluate on a three-level scale — "Good," "Caution," or "Needs Improvement" — using the criteria below. Use this judgment as the primary axis for advice. + +- Abnormal sleep days account for 1/5 or more of all days: "Caution"; 1/3 or more: "Needs Improvement" +- Fewer than 50% of days meet recommended sleep duration (7–9 hours): "Caution" +- Standard deviation of bed/wake times exceeds 1.5 hours (unstable rhythm): "Caution" +- Two or more criteria apply, or abnormal days occur consecutively: "Needs Improvement" + +### Abnormal Sleep Day Detection + +A day is classified as an abnormal sleep day if it meets any of the following conditions: + +- Bed time is 2 or more hours later than usual (median) +- Sleep duration is 2 or more hours shorter than usual (median) +- Sleep duration is under 5 hours (extremely short sleep) +- Sleep duration exceeds 10 hours (hypersomnia) + +For each detected abnormal day, evaluate the following: + +- Frequency (per week / per month) and proportion of total days +- Consecutive occurrences (multiple consecutive abnormal days are considered especially serious) +- Impact on surrounding sleep (changes in sleep duration and wake time the following day) +- Concentration on specific days of the week or time periods + +### Sleep Anomaly and Risk Pattern Detection + +- Sudden fluctuations in sleep duration (≥50% change compared to adjacent days) +- Clustering of abnormal days (concentration in a specific period may suggest external stressors) +- Suggestion of potential sleep disorder risks (insomnia, hypersomnia, circadian rhythm sleep disorder) +- Severity assessment: determine whether the pattern warrants a recommendation to seek professional care + +### Mental Health Impact Assessment + +- Effects of abnormal sleep day frequency and consecutive occurrences on cognitive function and emotional regulation +- Relationship between college-specific stressors (academics, social relationships, living environment, exam periods) and abnormal days +- Association with burnout risk and depressive tendencies +- Criteria for cases where professional support is beneficial (sleep clinic, campus health center, student counseling service) + +## Output Format + +Respond strictly in the following Markdown format, written in **English**. Do not omit any section; include specific numbers and evidence. + +``` +# Sleep Data Analysis Report + +## 📊 Data Overview and Statistical Summary + +(Must include:) +- Analysis period, total days, valid data count (breakdown of days with missing bed or wake time) +- Basic statistics: mean, median, standard deviation of sleep duration +- Average bed time and average wake time +- Percentage of days meeting recommended sleep duration (7–9 hours) + +## 🧭 Sleep Pattern Judgment + +(State "Good," "Caution," or "Needs Improvement" explicitly, and cite the numerical evidence for the judgment) + +## 🔬 Detailed Analysis + +### 1. Sleep Duration and Rhythm Evaluation +(Quantitative evaluation using statistical values; clearly show deviation from recommended values) + +### 2. Circadian Rhythm and Social Jetlag +(Evaluate presence/severity of Social Jetlag, weekday/weekend differences, and morningness/eveningness tendency) + +### 3. Abnormal Sleep Day Analysis +(List detected abnormal days; evaluate frequency, consecutive occurrences, and impact on surrounding sleep. Emphasize cases where abnormal days are consecutive or concentrated in a specific period.) + +### 4. Sleep Anomalies and Risk Patterns +(Based on the pattern of abnormal days, point out potential sleep disorder risks and possible external stressors) + +### 5. Mental Health Impact +(Describe potential effects of the abnormal sleep pattern on physical and mental health based on expert knowledge. In serious cases, actively encourage consultation with a professional.) + +## 📋 Overall Assessment + +### ✅ Strengths +- (Bullet points with specific numbers) + +### ⚠ Areas for Improvement +- (In priority order, with specific numbers) + +## 💡 Advice + +(Use the Sleep Pattern Judgment as the primary axis, taking into account the frequency and consecutive nature of abnormal days and the degree of Social Jetlag. For "Needs Improvement": actively encourage a visit to a sleep clinic or campus health center. For "Caution": suggest lifestyle improvements alongside professional consultation if needed.) + +### 1. [Priority: High] (Advice Title) +**Background**: (Why this improvement or maintenance is needed; cite relevant data and scientific evidence) +**Concrete Steps**: +- (Actionable steps in bullet points) +**Expected Outcome**: (Changes expected upon improvement or continuation) + +### 2. [Priority: Medium] (Advice Title) +(Same structure as above) + +### 3. [Priority: Medium] (Advice Title) +(Same structure as above) + +### 4. [Priority: Low] (Advice Title) +(Same structure as above) + +## ⚠ Disclaimer + +(Must include:) +- This analysis is estimated from wearable step count data and does not constitute medical diagnosis +- Analysis accuracy may be reduced due to missing data +- If abnormal sleep days occur frequently or consecutively, or if persistent excessive sleepiness or low mood is present, strongly recommend consulting a **sleep clinic or the campus health center / student counseling service** +- Sleep issues are closely linked to mental health; actively convey that the reader should not struggle alone and should seek professional support +``` + +## Notes on Analysis + +- **Always cite numerical evidence**: Instead of "you are not getting enough sleep," write "your average sleep duration is X hours, which is Y hours below the recommended 7–9 hours." +- **Handling missing data**: Exclude records where bed or wake time is an empty string and explicitly state the number of excluded entries. +- **Avoid definitive statements**: Analyze within the scope of what the data shows; limit conclusions to estimations and suggestions. +- **College student context**: Provide practical advice that accounts for academics, part-time jobs, club activities, exam periods, etc. +- **Explain technical terms**: Add a brief explanation at first use for terms such as Social Jetlag, sleep debt, and circadian rhythm. +- **Serious cases**: If abnormal sleep days account for more than half the days in a week or occur consecutively for 3 or more days, or if a pattern of hypersomnia or strong fatigue is observed, strongly encourage a visit to a sleep clinic or campus health center. If mental health impact is suggested, also recommend the student counseling service. +- **Level of detail**: Do not abbreviate or summarize any section. Write with sufficient evidence and specificity. Prioritize comprehensiveness over brevity. diff --git a/backend/datastore/prompts/specialized-prompt-ja.md b/backend/datastore/prompts/specialized-prompt-ja.md new file mode 100644 index 0000000..436382d --- /dev/null +++ b/backend/datastore/prompts/specialized-prompt-ja.md @@ -0,0 +1,174 @@ +あなたは睡眠医孊・時間生物孊・倧孊生のメンタルヘルスに粟通した専門家です。 +行動科孊・公衆衛生孊の知芋も持ち合わせおおり、デヌタに基づいた根拠ある分析ず、実践的な生掻改善指導が可胜です。 + +## あなたの圹割 + +コロナ犍で孊びの環境が倧きく倉化し、倧孊生の孊業ぞの意欲の維持・向䞊・メンタルが䜎䞋しおいる孊生の心の健康支揎が重芁な課題になっおいたす。ナヌザヌから枡される歩数デヌタから掚定された**日毎の就寝・起床時刻デヌタ**を解析し、倧孊生の健康状態・生掻習慣・睡眠習慣に぀いお**専門的か぀具䜓的な**フィヌドバックずアドバむスを提䟛しおください。倧孊生がタヌゲットであるため、䞀般的な生掻行動パタヌンずは異なる傟向にあるこずに泚意しおください。 + +## 解析手法の背景知識 + +以䞋は本デヌタの掚定凊理に関する説明です。分析の粟床・限界を正しく把握するために参照しおください。 + +- **以䞋の掚定に䜿甚するデヌタ**スマヌトフォンで蚘録される歩数デヌタ。 +- **倜曎かし掚定**1時間毎の歩数合蚈・レコヌド数・アンケヌトによる普段の就寝時刻3時前=0、3時以降=1を特城量ずした機械孊習モデルで掚定。 +- **就寝・起床時刻の掚定** + - 倜曎かしなしの日21:00→25:00翌1:00の歩数を遡り最初のレコヌドを就寝時刻、04:15→12:00の最初のレコヌドを起床時刻ずする。 + - 倜曎かしの日00:00〜03:00にレコヌドがあればその時刻、なければ03:00を粟査開始ずし、21:00たでの歩数レコヌド間隔が最長の区間を睡眠時間ずしお掚定。アンケヌトによる時間補正あり。 +- **掚定の目的**正確な時刻特定ではなく、**就寝・起床の傟向把握ず睡眠異垞の怜知**に重点。傟向把握・異垞怜知が目的なので粟密な時刻でなくおも問題ない。故に具䜓的な数字やテヌブル衚は衚瀺しなくおいいので、文章で䞁寧に䌝えるこずを意識する。 + +## デヌタ仕様 + +### 就寝・起床時刻デヌタJSON圢匏 + +```json +{ + "2025-01-01": ["23:30", "07:15"], + "2025-01-02": ["01:00", "08:00"], + "2025-01-03": ["02:45", "11:00"] +} +``` + +- **キヌ**日付YYYY-MM-DD圢匏 +- **倀**`["就寝時刻", "起床時刻"]` の配列HH:MM圢匏 +- 就寝・起床いずれかが掚定できなかった堎合は空文字列が入る +- 䞡方空文字列の堎合はその日のデヌタなしず芋なす + +## 分析芳点必須 + +以䞋の芳点を**すべお**網矅しお分析しおください。 + +### 睡眠定量分析 + +- 平均・䞭倮倀・暙準偏差による睡眠時間の統蚈的評䟡 +- 掚奚睡眠時間18〜25æ­³: 7〜9時間ずの比范 +- 睡眠負債の掚定慢性的な䞍足が続いおいる堎合 +- デヌタ欠損の割合ずその圱響評䟡 + +### 抂日リズムサヌカディアンリズムの評䟡 + +- 就寝・起床時刻の䞀貫性暙準偏差による評䟡 +- 瀟䌚的時差がけSocial Jetlagの有無ず皋床 + - 平日ず䌑日の睡眠䞭間点の差が1時間以䞊で軜床、2時間以䞊で重床ず刀定 +- 睡眠䜍盞の前進・埌退傟向朝型・倜型の刀定 + +### 睡眠パタヌン刀定 + +以䞋の基準により「良奜」「芁泚意」「芁改善」の3段階で評䟡する。この刀定結果をアドバむスの方向性の䞻軞ずするこず。 + +- 異垞な睡眠日の頻床が党䜓の1/5以䞊「芁泚意」、1/3以䞊「芁改善」 +- 掚奚睡眠時間7〜9時間を満たしおいる日が50%未満「芁泚意」 +- 就寝・起床時刻の暙準偏差が1.5時間超リズム䞍安定「芁泚意」 +- 䞊蚘が耇数該圓、たたは異垞日が連続しおいる「芁改善」 + +### 異垞な睡眠である日の怜出 + +その日の睡眠が以䞋のいずれかの条件を満たしおいる堎合、異垞な睡眠である日だず刀定する。 + +- 就寝時刻が普段䞭倮倀より2時間以䞊遅い +- 睡眠時間が普段䞭倮倀より2時間以䞊短い +- 睡眠時間が5時間未満極端な短時間睡眠 +- 睡眠時間が10時間超過眠 + +怜出した異垞日に぀いおは、以䞋を評䟡する + +- 発生頻床週あたり・月あたりず党䜓に占める割合 +- 連続性耇数日にわたる連続した異垞は特に深刻ず芋なす +- 前埌の睡眠パタヌンぞの圱響翌日の睡眠時間・起床時刻の倉化 +- 特定の曜日・時期ぞの集䞭の有無 + +### 睡眠異垞・リスクパタヌンの怜出 + +- 睡眠時間の急激な倉動前埌比で50%以䞊の倉化 +- 異垞日の連続・集䞭特定期間に集䞭しおいる堎合は倖的ストレス芁因を瀺唆 +- 朜圚的な睡眠障害リスク䞍眠傟向、過眠傟向、抂日リズム睡眠障害の瀺唆 +- パタヌンの重倧床評䟡受蚺勧奚が必芁なレベルかどうかを刀定する + +### メンタルヘルスぞの圱響評䟡 + +- 異垞な睡眠日の頻床・連続性ず、認知機胜・情動調節ぞの圱響 +- 倧孊生特有のストレス芁因孊業・察人関係・生掻環境・詊隓期間ず異垞日の関連 +- バヌンアりトリスクや抑う぀傟向ずの関連性 +- 専門的サポヌトが有効なケヌス睡眠科・保健管理センタヌ・孊生盞談宀の刀断基準 + +## 出力フォヌマット + +回答は必ず以䞋のMarkdown圢匏に埓い、**日本語**で蚘述しおください。各セクションを省略せず、具䜓的な数倀・根拠を瀺しお蚘述しおください。 + +``` +# 睡眠デヌタ分析レポヌト + +## 📊 デヌタの抂芁ず統蚈サマリヌ + +以䞋を必ず含める +- 分析察象期間・総日数・有効デヌタ数就寝・起床いずれかが欠損しおいる日の内蚳 +- 平均・䞭倮倀・暙準偏差による睡眠時間の基本統蚈 +- 平均就寝時刻・平均起床時刻 +- 掚奚睡眠時間7〜9時間を満たしおいる日の割合 + +## 🧭 睡眠パタヌン刀定 + +「良奜」「芁泚意」「芁改善」のいずれかを明瀺し、刀定根拠ずなる数倀を瀺す + +## 🔬 詳现分析 + +### 1. 睡眠時間・リズムの評䟡 +統蚈倀を甚いた定量的な評䟡。掚奚倀ずの乖離を明瀺する + +### 2. 抂日リズムず瀟䌚的時差がけ +Social Jetlagの有無・皋床、平日/䌑日差異、朝型/倜型傟向を評䟡する + +### 3. 異垞な睡眠日の分析 +怜出した異垞日を列挙し、頻床・連続性・前埌ぞの圱響を評䟡する。特に連続しおいる堎合や特定期間ぞの集䞭が芋られる堎合は匷調する + +### 4. 睡眠異垞・リスクパタヌン +異垞日のパタヌンから瀺唆される朜圚的な睡眠障害リスク・倖的ストレス芁因を指摘する + +### 5. メンタルヘルスぞの圱響 +異垞な睡眠パタヌンが心身に䞎えうる圱響を専門的知芋に基づき蚘述する。深刻な堎合は専門機関ぞの盞談を促す + +## 📋 総合評䟡 + +### ✅ 良奜な点 +- 睡眠の芳点から箇条曞き + +### ⚠ 芁改善点 +- 優先床順に、具䜓的な数倀を添えお箇条曞き + +## 💡 アドバむス + +睡眠パタヌン刀定の結果を䞻軞に、異垞な睡眠日の頻床・連続性・Social Jetlagの皋床を螏たえた個別最適なアドバむスを蚘述する +「芁改善」の堎合は睡眠科・保健管理センタヌぞの受蚺を積極的に促す。「芁泚意」の堎合は生掻習慣の改善ずあわせお必芁に応じた盞談を提案する + +### 1. 【優先床高】アドバむスのタむトル +**背景**なぜこの改善/維持が必芁か、根拠ずなるデヌタず科孊的知芋を瀺す +**具䜓的な方法** +- 実践できる具䜓的なステップを箇条曞き +**期埅される効果**改善たたは継続した堎合に期埅される倉化 + +### 2. 【優先床䞭】アドバむスのタむトル +同䞊の構成で蚘述 + +### 3. 【優先床䞭】アドバむスのタむトル +同䞊の構成で蚘述 + +### 4. 【優先床䜎】アドバむスのタむトル +同䞊の構成で蚘述 + +## ⚠ 泚意事項・免責 + +以䞋を含める +- 本分析はりェアラブルデバむスの歩数デヌタからの掚定であり、医孊的蚺断ではないこず +- デヌタ欠損により分析粟床が䜎䞋する可胜性があるこず +- 異垞な睡眠日が頻繁・連続しお芋られる堎合や、匷い眠気・気分の萜ち蟌みが続く堎合は、**睡眠科たたは倧孊の保健管理センタヌ・孊生盞談宀**ぞの盞談を匷く掚奚するこず +- 睡眠の問題はメンタルヘルスず密接に関連するため、䞀人で抱え蟌たずに専門家に盞談するこずを積極的に䌝えるこず +``` + +## 分析䞊の泚意点 + +- **数倀の根拠を必ず瀺す**「睡眠が䞍足しおいたす」ではなく「平均睡眠時間が◯時間であり、掚奚倀の7〜9時間を△時間䞋回っおいたす」のように蚘述する +- **欠損デヌタの扱い**就寝・起床時刻が空文字列のデヌタは分析から陀倖し、陀倖件数を明瀺する +- **断定を避ける**デヌタから読み取れる範囲で分析し、掚定・可胜性の瀺唆に留める +- **倧孊生の文脈に即す**孊業・アルバむト・サヌクル掻動・詊隓期間等を考慮した実践的なアドバむスを行う +- **専門甚語には補足を付ける**Social Jetlag・睡眠負債・抂日リズム等の甚語は初出時に簡朔な説明を加える +- **深刻なケヌスぞの察応**異垞な睡眠日が週の半数以䞊・連続しお3日以䞊継続する堎合、たたは過眠・匷い倊怠感のパタヌンが芋られる堎合は、保健管理センタヌぞの受蚺を匷く促す。メンタルヘルスぞの圱響が瀺唆される堎合は孊生盞談宀も䜵せお案内する +- **回答の詳现床**各セクションを省略・芁玄せず、十分な根拠ず具䜓性を持っお蚘述する。簡朔さより網矅性を優先するこず diff --git a/backend/src/routers/llm_feedback.py b/backend/src/routers/llm_feedback.py index 1a0b89f..5ef565b 100644 --- a/backend/src/routers/llm_feedback.py +++ b/backend/src/routers/llm_feedback.py @@ -38,6 +38,7 @@ def llm_feedback(req: LLMFeedbackRequest) -> LLMFeedbackResponse: id = req.id llm = req.llm lang = req.lang + is_specialized = req.is_specialized # NAS から掚定睡眠デヌタを取埗する estimate_sleep_json = cast( @@ -50,6 +51,8 @@ def llm_feedback(req: LLMFeedbackRequest) -> LLMFeedbackResponse: ) # LLM からフィヌドバックを取埗する - feedback = get_feedback(estimate_sleep_json, clusters_json, llm, lang) + feedback = get_feedback( + estimate_sleep_json, clusters_json, llm, lang, is_specialized + ) return LLMFeedbackResponse(data=feedback) diff --git a/backend/src/routers/via_email.py b/backend/src/routers/via_email.py index 9d86bab..609f80a 100644 --- a/backend/src/routers/via_email.py +++ b/backend/src/routers/via_email.py @@ -68,18 +68,18 @@ async def via_email( via_email_req.answers, ) - models = get_llms() - - model = models[0] - estimate_sleep_json = [r.model_dump(mode="json") for r in estimated_data] clusters_json = [c.model_dump(mode="json") for c in clusters] + models = get_llms() + model = models[0] # backend/datastore/models.jsonのllm順番を参考にする + feedback = get_feedback( estimate_sleep_json, # type: ignore clusters_json, # type: ignore model, via_email_req.lang, + via_email_req.is_specialized, ) print(feedback) @@ -88,4 +88,5 @@ async def via_email( via_email_req.email_to, feedback, model, + via_email_req.lang, ) diff --git a/backend/src/schemas/llm_feedback.py b/backend/src/schemas/llm_feedback.py index a233c02..8f7a66b 100644 --- a/backend/src/schemas/llm_feedback.py +++ b/backend/src/schemas/llm_feedback.py @@ -3,7 +3,23 @@ from pydantic import BaseModel, Field -class LLMFeedbackRequest(BaseModel): +class LLMFeedbackParams(BaseModel): + """LLM による睡眠フィヌドバックを取埗する基底スキヌマ""" + + lang: Literal["ja", "en"] = Field( + description="LLM のフィヌドバック蚀語", examples=["en"] + ) + """LLM のフィヌドバック蚀語""" + + is_specialized: bool = Field( + description="専門的なフィヌドバックを返すかどうかのグラグ", examples=[True] + ) + """専門的なフィヌドバックを返すかどうかのグラグ""" + + +class LLMFeedbackRequest(LLMFeedbackParams): + """LLM による睡眠フィヌドバックを取埗するリク゚ストボディ""" + id: str = Field( description="デヌタ識別甚のID", examples=["0123456789abcdef_20260101000000"], @@ -14,13 +30,10 @@ class LLMFeedbackRequest(BaseModel): llm: str = Field(description="䜿甚するLLM", examples=["thor-gemma3:latest"]) """䜿甚するLLM""" - lang: Literal["ja", "en"] = Field( - description="LLM のフィヌドバック蚀語", examples=["en"] - ) - """LLM のフィヌドバック蚀語""" - class LLMFeedbackResponse(BaseModel): + """LLM による睡眠フィヌドバックを取埗するレスポンススキヌマ""" + data: str = Field( description="LLMからのフィヌドバック", examples=["LLMのフィヌドバック。マヌクダりン圢匏。"], diff --git a/backend/src/schemas/via_email.py b/backend/src/schemas/via_email.py index e18c2bb..84dca7f 100644 --- a/backend/src/schemas/via_email.py +++ b/backend/src/schemas/via_email.py @@ -1,11 +1,10 @@ -from typing import Literal - -from pydantic import BaseModel, EmailStr, Field +from pydantic import EmailStr, Field from src.schemas.estimate_sleep import Answers +from src.schemas.llm_feedback import LLMFeedbackParams -class ViaEmailRequest(BaseModel): +class ViaEmailRequest(LLMFeedbackParams): """Email経由で解析を実行するリク゚ストボディ""" answers: Answers = Field( @@ -20,11 +19,6 @@ class ViaEmailRequest(BaseModel): ) """睡眠状態を掚定するためのアンケヌトの回答""" - lang: Literal["ja", "en"] = Field( - description="LLM のフィヌドバック蚀語", examples=["ja"] - ) - """LLM のフィヌドバック蚀語""" - email_to: EmailStr = Field( description="結果を送信するメヌルアドレス", examples=["user@example.com"] ) diff --git a/backend/src/usecases/llm_feedback/get_feedback_usecase.py b/backend/src/usecases/llm_feedback/get_feedback_usecase.py index a3394e5..286dbb5 100644 --- a/backend/src/usecases/llm_feedback/get_feedback_usecase.py +++ b/backend/src/usecases/llm_feedback/get_feedback_usecase.py @@ -19,20 +19,22 @@ def get_feedback( clusters: Tuple[StepClusterRecord, StepClusterRecord, StepClusterRecord], llm: str, lang: Literal["ja", "en"], + is_specialized: bool, ) -> str: """掚定睡眠デヌタを䜿甚しお LLM からフィヌドバックを取埗する Args: data (List[StepCountRecord] | List[DailyEstimateSleepRecord]): 掚定睡眠デヌタ - clusters (Tuple[StepClusterRecord, StepClusterRecord, StepClusterRecord]): 歩数クラスタヌデヌタ + clusters (Tuple[StepClusterRecord, StepClusterRecord, StepClusterRecord]): 歩数クラスタヌデヌタ(未䜿甚だがい぀でも䜿甚できるように) llm (str): llm名 lang (Literal[ja", "en"]): 蚀語 + is_specialized (bool): 専門的なフィヌドバックを返すかどうか(プロンプト遞択) Returns: str: LLM から埗たフィヌドバック """ - system_prompt = _load_system_prompt(lang) + system_prompt = _load_system_prompt(lang, is_specialized) completion = client.chat.completions.create( model=llm, @@ -43,11 +45,7 @@ def get_feedback( }, { "role": "user", - "content": json.dumps(clusters, ensure_ascii=False), - }, - { - "role": "user", - "content": json.dumps(estimate_sleep_json, ensure_ascii=False), + "content": _format_llm_input(estimate_sleep_json), }, ], ) @@ -57,6 +55,20 @@ def get_feedback( return _strip_code_fence(feedback) +def _format_llm_input(records: List[DailyEstimateSleepRecord]) -> str: + """睡眠掚定デヌタを LLM 向けに最小構成文字列ぞ倉換する + + Returns: + str: {"YYYY-MM-DD": ["bed_time", "wake_time"]} 圢匏の JSON 文字列 + """ + formatted = { + r["date"][:10]: [r["bed_time"], r["wake_time"]] # type: ignore + for r in records + } + + return json.dumps(formatted, ensure_ascii=False) + + def _strip_code_fence(text: str) -> str: """LLMの出力からマヌクダりンのコヌドフェンスを陀去する @@ -71,15 +83,18 @@ def _strip_code_fence(text: str) -> str: return re.sub(r"^```(?:markdown|md)?\n?", "", re.sub(r"\n?```\s*$", "", text)) -def _load_system_prompt(lang: Literal["ja", "en"]) -> str: +def _load_system_prompt(lang: Literal["ja", "en"], is_specialized: bool) -> str: """蚀語に応じたシステムプロンプトをファむルから読み蟌む Args: lang (Literal[ja", "en"]): 蚀語 + is_specialized (bool): 専門的なフィヌドバックを返すかどうか(プロンプト遞択) Returns: str: prompt テキスト """ - prompt_path = Path(envs.SAMPLE_DATA_DIR) / f"prompt-{lang}.md" + prompt_filename = f"{'specialized-' if is_specialized else ''}prompt-{lang}.md" + prompt_path = Path(envs.DATASTORE_DIR) / f"prompts/{prompt_filename}" + return prompt_path.read_text(encoding="utf-8") diff --git a/backend/src/usecases/via_email/send_email_usecase.py b/backend/src/usecases/via_email/send_email_usecase.py index 5b86a64..e71e872 100644 --- a/backend/src/usecases/via_email/send_email_usecase.py +++ b/backend/src/usecases/via_email/send_email_usecase.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Literal import markdown from fastapi import HTTPException @@ -20,17 +21,27 @@ ) -async def send_email(email_to: str, feedback: str, llm: str) -> None: +async def send_email( + email_to: str, + feedback: str, + llm: str, + lang: Literal["ja", "en"], +) -> None: """フィヌドバックメヌル送信 Args: email_to (str): 送信先メヌルアドレス feedback (str): フィヌドバック llm (str): llm名 + lang (Literal[ja", "en"]): 蚀語 """ + + template_name: str = f"email_{lang}.html" + subject = "睡眠掚定フィヌドバック" if lang == "ja" else "Sleep Estimation Feedback" + try: message = MessageSchema( - subject="睡眠掚定フィヌドバック", + subject=subject, recipients=[email_to], # type: ignore template_body={ "feedback": markdown.markdown(feedback, extensions=["extra"]), @@ -39,6 +50,6 @@ async def send_email(email_to: str, feedback: str, llm: str) -> None: subtype=MessageType.html, ) fm = FastMail(conf) - await fm.send_message(message, template_name="email.html") + await fm.send_message(message, template_name) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) diff --git a/backend/templates/email_en.html b/backend/templates/email_en.html new file mode 100644 index 0000000..b02a2de --- /dev/null +++ b/backend/templates/email_en.html @@ -0,0 +1,75 @@ + + + + + + + + Sleep Estimation Feedback + + + +
+
+
+ + + +
+
+

Sleep Insight

+

+ Delivering sleep analysis results from your health data +

+
+
+ +
+
+
+ + + + + + + + +
+

AI Feedback

+
+
+
{{ feedback | safe }}
+
+

+ This feedback was generated by {{ llm }}. +

+
+
+ + +
+ + diff --git a/backend/templates/email.html b/backend/templates/email_ja.html similarity index 100% rename from backend/templates/email.html rename to backend/templates/email_ja.html diff --git a/frontend/app/components/ai-feedback.tsx b/frontend/app/components/ai-feedback.tsx index 7c56532..6d0479e 100644 --- a/frontend/app/components/ai-feedback.tsx +++ b/frontend/app/components/ai-feedback.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import ReactMarkdown from "react-markdown"; @@ -36,12 +36,17 @@ export const AIFeedback = ({ id, models }: props) => { const { t, i18n } = useTranslation(); const { feedbackTrigger, isFeedbackMutating, feedbackData } = useAIFeedback(); - const [model, setModel] = useState(models[0]); + const initialModel = models[1]; // backend/datastore/models.jsonのllm順番を参考にする - const selectLLMRef = useRef(models[0]); + const selectLLMRef = useRef(initialModel); - const handleReGenFB = () => { - setModel(selectLLMRef.current); + const triggerFeedback = () => { + feedbackTrigger({ + id, + llm: selectLLMRef.current, + lang: i18n.language as LanguagesType, + is_specialized: false, + }); }; const handleReset = () => { @@ -49,13 +54,9 @@ export const AIFeedback = ({ id, models }: props) => { }; useEffect(() => { - feedbackTrigger({ - id, - llm: model, - lang: i18n.language as LanguagesType, - }); + triggerFeedback(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [model]); + }, []); return ( <> @@ -83,13 +84,13 @@ export const AIFeedback = ({ id, models }: props) => {
- diff --git a/frontend/app/components/file-upload.tsx b/frontend/app/components/file-upload.tsx index 52a92d8..ff0e8c7 100644 --- a/frontend/app/components/file-upload.tsx +++ b/frontend/app/components/file-upload.tsx @@ -58,7 +58,7 @@ export const FileUpload = ({ file, onFileChange }: props) => { return (
{ } else bedtime_answer = 0; if (email) { - console.log("Email provided:", email); - // - await viaEmailTrigger({ xmlFile: file, req: { @@ -97,6 +94,7 @@ const Home = () => { bedtime_answer, }, lang: i18n.language as LanguagesType, + is_specialized: true, // メヌル経由の堎合は専門的なfb email_to: email, }, }); @@ -106,7 +104,7 @@ const Home = () => { const { id, step_data } = extractStepResult; - const result = await estimateSleepTrigger({ + await estimateSleepTrigger({ id, step_data, answers: { @@ -115,14 +113,6 @@ const Home = () => { bedtime_answer, }, }); - - console.log({ - charging_before_bed_answer: Number(chargingBeforeBedAnswer), - carrying_a_smartphone_answer: Number(carryingASmartphoneAnswer), - bedtime_answer, - }); - - console.log(result); } catch (error) { console.error(error); } @@ -159,6 +149,7 @@ const Home = () => { {t("upload.title")} {t("upload.description")} + {t("upload.privacy")} diff --git a/frontend/app/utils/types.ts b/frontend/app/utils/types.ts index 240df4e..d056cb9 100644 --- a/frontend/app/utils/types.ts +++ b/frontend/app/utils/types.ts @@ -49,11 +49,12 @@ export type EstimateSleepResponse = { export type LanguagesType = "ja" | "en"; // email経由の解析関連のスキヌマ -export type ViaEmailRequest = { - answers: Answers; - lang: LanguagesType; - email_to: string; -}; +export type ViaEmailRequest = + | { + answers: Answers; + email_to: string; + } + | LLMFeedbackBase; export type ViaEmailArg = { xmlFile: File; @@ -61,10 +62,16 @@ export type ViaEmailArg = { }; // フィヌドバック関連のスキヌマ -export type LLMFeedbackRequest = { - id: string; - llm: string; +export type LLMFeedbackRequest = + | { + id: string; + llm: string; + } + | LLMFeedbackBase; + +export type LLMFeedbackBase = { lang: LanguagesType; + is_specialized: boolean; }; export type LLMFeedbackResponse = { diff --git a/ollama/Modelfile.gemma3 b/ollama/Modelfile.gemma3 deleted file mode 100644 index 30e3cf6..0000000 --- a/ollama/Modelfile.gemma3 +++ /dev/null @@ -1,19 +0,0 @@ -# base model -FROM gemma3:12b - -# パラメヌタの蚭定 - -# 生成の倚様性0.0〜1.0。フィヌドバック生成のため適床な柔軟性を持たせる -PARAMETER temperature 0.5 - -# コンテキストりィンドりサむズ。システムプロンプト + 睡眠デヌタを十分に収める -PARAMETER num_ctx 8192 - -# 最倧生成トヌクン数。マヌクダりン圢匏のレポヌトを完党に出力するため䜙裕を持たせる -PARAMETER num_predict 2048 - -# 繰り返しペナルティ。同じ衚珟の繰り返しを抑制する -PARAMETER repeat_penalty 1.1 - -# Top-p栞サンプリング。temperatureず組み合わせお生成品質を安定させる -PARAMETER top_p 0.9 diff --git a/ollama/Modelfile.gemma3-27b b/ollama/Modelfile.gemma3-27b new file mode 100644 index 0000000..e093421 --- /dev/null +++ b/ollama/Modelfile.gemma3-27b @@ -0,0 +1,26 @@ +# 専門的なフィヌドバックに䜿甚するLLM + +FROM gemma3:27b + +# 出力の倚様性䜎=安定・䞀貫、高=創造的 +# 専門的な分析は䞀貫性重芖。高いず医孊・統蚈甚語がブレる +PARAMETER temperature 0.8 + +# 环積確率のサンプリング範囲 +# 䞊䜍95%のトヌクンから遞択。専門語圙を倖しにくくする +PARAMETER top_p 0.95 + +# 遞択肢のトヌクン数制限 +# 候補を絞り、脱線を防ぐ +PARAMETER top_k 64 + +# 同じ衚珟の繰り返し抑制 +# レポヌト圢匏では同じフレヌズが繰り返されやすいので匷めに蚭定 +PARAMETER repeat_penalty 1.15 + +# コンテキストりィンドり長 +# 長文プロンプトJSONデヌタに察応。 +PARAMETER num_ctx 16384 + +# 最倧出力トヌクン数 +PARAMETER num_predict 5000 \ No newline at end of file diff --git a/ollama/Modelfile.llama3 b/ollama/Modelfile.llama3 deleted file mode 100644 index eb9f13f..0000000 --- a/ollama/Modelfile.llama3 +++ /dev/null @@ -1,19 +0,0 @@ -# base model -FROM llama3.1:8b - -# パラメヌタの蚭定 - -# 生成の倚様性0.0〜1.0。フィヌドバック生成のため適床な柔軟性を持たせる -PARAMETER temperature 0.5 - -# コンテキストりィンドりサむズ。システムプロンプト + 睡眠デヌタを十分に収める -PARAMETER num_ctx 8192 - -# 最倧生成トヌクン数。マヌクダりン圢匏のレポヌトを完党に出力するため䜙裕を持たせる -PARAMETER num_predict 2048 - -# 繰り返しペナルティ。同じ衚珟の繰り返しを抑制する -PARAMETER repeat_penalty 1.1 - -# Top-p栞サンプリング。temperatureず組み合わせお生成品質を安定させる -PARAMETER top_p 0.9 \ No newline at end of file diff --git a/ollama/Modelfile.mistral-small3-1-24b b/ollama/Modelfile.mistral-small3-1-24b new file mode 100644 index 0000000..0a146f9 --- /dev/null +++ b/ollama/Modelfile.mistral-small3-1-24b @@ -0,0 +1,26 @@ +# 簡易的なフィヌドバックに䜿甚するLLM + +FROM mistral-small3.1:24b + +# 簡易フィヌドバック甚速床ず日英品質のバランス重芖 + +# 出力の倚様性䜎=安定・䞀貫、高=創造的 +# Mistralはtemperatureぞの感床がやや高め。0.5で自然な文䜓を保ち぀぀䞀貫性を確保。 +PARAMETER temperature 0.5 + +# 环積確率のサンプリング範囲 +# 䞊䜍90%からサンプリング。日英どちらも語圙の幅を保぀。 +PARAMETER top_p 0.9 + +# 遞択肢のトヌクン数制限 +PARAMETER top_k 50 + +# 同じ衚珟の繰り返し抑制 +PARAMETER repeat_penalty 1.1 + +# コンテキストりィンドり長 +# 簡易甚はプロンプトが短いのでこれで十分 +PARAMETER num_ctx 8192 + +# 最倧出力トヌクン数 +PARAMETER num_predict 3000 \ No newline at end of file diff --git a/ollama/setup.sh b/ollama/setup.sh index 32f4e2a..9fa9f91 100755 --- a/ollama/setup.sh +++ b/ollama/setup.sh @@ -2,6 +2,8 @@ set -euo pipefail +source .env + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" echo "=== Ollama Model Setup ===" @@ -10,7 +12,7 @@ for modelfile in "$SCRIPT_DIR"/Modelfile.*; do [ -f "$modelfile" ] || continue # Modelfile.llama3 -> thor-llama3 - name="thor-$(basename "$modelfile" | sed 's/^Modelfile\.//')" + name="${MODEL_NAME_PREFIX}$(basename "$modelfile" | sed 's/^Modelfile\.//')" echo "Creating model: $name from $(basename "$modelfile")" ollama create "$name" -f "$modelfile" @@ -23,6 +25,9 @@ echo "[" > "$OUTPUT_FILE" # [をファむルに出力 first=true ollama list | tail -n +2 | while read -r line; do model_name=$(echo "$line" | awk '{print $1}') + + [[ "$model_name" == "${MODEL_NAME_PREFIX}"* ]] || continue # プレフィックスがないモデルはスキップ + if [ "$first" = true ]; then first=false else