github-actions[bot] commited on
Commit
7863d4f
·
0 Parent(s):

Deploy from GitHub Actions - 881aa69300369910bd973fcba04304e70ccfb3f2

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .env.example +6 -0
  2. .python-version +1 -0
  3. Dockerfile +33 -0
  4. README.md +29 -0
  5. app/__init__.py +1 -0
  6. app/config.py +105 -0
  7. app/data/evaluation/compare_strategies.py +363 -0
  8. app/data/evaluation/run_evaluation.py +269 -0
  9. app/data/evaluation/test_queries.json +204 -0
  10. app/data/evaluation/test_queries_hard.json +227 -0
  11. app/data/evaluation/test_queries_light.json +55 -0
  12. app/data/evaluation/test_queries_medium.json +128 -0
  13. app/data/regulations-optimized/リモートワーク規程_アルバイト.md +70 -0
  14. app/data/regulations-optimized/リモートワーク規程_パート.md +85 -0
  15. app/data/regulations-optimized/リモートワーク規程_正社員.md +84 -0
  16. app/data/regulations-optimized/休暇規程_アルバイト.md +72 -0
  17. app/data/regulations-optimized/休暇規程_パート.md +76 -0
  18. app/data/regulations-optimized/休暇規程_正社員.md +80 -0
  19. app/data/regulations-optimized/服務規程_アルバイト.md +105 -0
  20. app/data/regulations-optimized/服務規程_パート.md +88 -0
  21. app/data/regulations-optimized/服務規程_正社員.md +86 -0
  22. app/data/regulations-optimized/福利厚生規程_アルバイト.md +94 -0
  23. app/data/regulations-optimized/福利厚生規程_パート.md +103 -0
  24. app/data/regulations-optimized/福利厚生規程_正社員.md +104 -0
  25. app/data/regulations-optimized/経費精算規程_アルバイト.md +58 -0
  26. app/data/regulations-optimized/経費精算規程_パート.md +68 -0
  27. app/data/regulations-optimized/経費精算規程_正社員.md +110 -0
  28. app/data/regulations-optimized/通勤手当規程_アルバイト.md +61 -0
  29. app/data/regulations-optimized/通勤手当規程_パート.md +60 -0
  30. app/data/regulations-optimized/通勤手当規程_正社員.md +56 -0
  31. app/data/regulations/リモートワーク規程.md +166 -0
  32. app/data/regulations/休暇規程.md +130 -0
  33. app/data/regulations/服務規程.md +147 -0
  34. app/data/regulations/福利厚生規程.md +168 -0
  35. app/data/regulations/経費精算規程.md +155 -0
  36. app/data/regulations/通勤手当規程.md +93 -0
  37. app/data/uploads/.gitkeep +0 -0
  38. app/main.py +142 -0
  39. app/models/__init__.py +1 -0
  40. app/models/schemas.py +137 -0
  41. app/routers/__init__.py +1 -0
  42. app/routers/chat.py +126 -0
  43. app/routers/documents.py +198 -0
  44. app/routers/evaluation.py +415 -0
  45. app/routers/health.py +29 -0
  46. app/services/__init__.py +1 -0
  47. app/services/document_preprocessor.py +213 -0
  48. app/services/document_service.py +487 -0
  49. app/services/embedding_service.py +56 -0
  50. app/services/question_generator.py +125 -0
.env.example ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Google API Key (Gemini)
2
+ # https://aistudio.google.com/apikey で無料で取得可能
3
+ GOOGLE_API_KEY=your_api_key_here
4
+
5
+ # フロントエンドURL (CORS設定用)
6
+ FRONTEND_URL=https://your-app.vercel.app
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ build-essential \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Install uv
12
+ RUN pip install uv
13
+
14
+ # Copy dependency files
15
+ COPY pyproject.toml ./
16
+
17
+ # Install dependencies using uv
18
+ RUN uv pip install --system -e .
19
+
20
+ # Copy application code
21
+ COPY . .
22
+
23
+ # Create necessary directories
24
+ RUN mkdir -p ./app/data/uploads ./chroma_db
25
+
26
+ # Set environment variables
27
+ ENV PYTHONUNBUFFERED=1
28
+
29
+ # HF Spaces uses port 7860
30
+ EXPOSE 7860
31
+
32
+ # Start the application
33
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: RAG Backend API
3
+ emoji: 🔍
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
9
+
10
+ # RAG Backend API
11
+
12
+ RAGドキュメント検索デモ用バックエンドAPI
13
+
14
+ ## エンドポイント
15
+
16
+ - `GET /api/health` - ヘルスチェック
17
+ - `POST /api/chat` - チャット (ストリーミング対応)
18
+ - `GET /api/documents` - ドキュメント一覧
19
+ - `POST /api/documents/upload` - ドキュメントアップロード
20
+ - `DELETE /api/documents/{id}` - ドキュメント削除
21
+ - `POST /api/documents/rebuild` - ベクトルストア再構築
22
+
23
+ ## 技術スタック
24
+
25
+ - FastAPI
26
+ - LangChain
27
+ - Chroma (ベクトルDB)
28
+ - HuggingFace Embeddings (intfloat/multilingual-e5-large)
29
+ - Google Gemini 2.0 Flash (LLM)
app/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # RAG Backend API
app/config.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ アプリケーション設定
3
+ """
4
+
5
+ from enum import Enum
6
+ from pathlib import Path
7
+ from functools import lru_cache
8
+
9
+ from pydantic_settings import BaseSettings
10
+
11
+
12
+ class ChunkingStrategy(str, Enum):
13
+ """チャンキング戦略"""
14
+ STANDARD = "standard" # 1000/200 - baseline
15
+ LARGE = "large" # 2000/500 - more context
16
+ PARENT_CHILD = "parent_child" # small chunks for retrieval, parent for context
17
+ HYPOTHETICAL_QUESTIONS = "hypothetical_questions" # LLM generates questions, index questions, retrieve chunks
18
+
19
+
20
+ class DocumentSet(str, Enum):
21
+ """ドキュメントセット"""
22
+ ORIGINAL = "original" # Original regulation docs
23
+ OPTIMIZED = "optimized" # Preprocessed/restructured docs
24
+
25
+
26
+ # Chunking strategy configurations
27
+ CHUNKING_CONFIGS = {
28
+ ChunkingStrategy.STANDARD: {"chunk_size": 1000, "chunk_overlap": 200},
29
+ ChunkingStrategy.LARGE: {"chunk_size": 2000, "chunk_overlap": 500},
30
+ ChunkingStrategy.PARENT_CHILD: {
31
+ "parent_chunk_size": 2000,
32
+ "parent_chunk_overlap": 200,
33
+ "child_chunk_size": 400,
34
+ "child_chunk_overlap": 50,
35
+ },
36
+ ChunkingStrategy.HYPOTHETICAL_QUESTIONS: {
37
+ "chunk_size": 1000,
38
+ "chunk_overlap": 200,
39
+ "questions_per_chunk": 3,
40
+ },
41
+ }
42
+
43
+
44
+ # Base directory (computed once at module load)
45
+ _BASE_DIR = Path(__file__).parent
46
+
47
+
48
+ class Settings(BaseSettings):
49
+ """アプリケーション設定"""
50
+
51
+ # API Keys
52
+ google_api_key: str = ""
53
+
54
+ # Paths - use the module-level constant
55
+ base_dir: Path = _BASE_DIR
56
+ documents_dir: Path = _BASE_DIR / "data" / "regulations"
57
+ documents_dir_optimized: Path = _BASE_DIR / "data" / "regulations-optimized"
58
+ uploads_dir: Path = _BASE_DIR / "data" / "uploads"
59
+ chroma_db_dir: Path = _BASE_DIR.parent / "chroma_db"
60
+ evaluation_queries_path: Path = _BASE_DIR / "data" / "evaluation" / "test_queries_hard.json"
61
+
62
+ # Embedding Model - Better Japanese support
63
+ embedding_model: str = "intfloat/multilingual-e5-large"
64
+
65
+ # LLM Model
66
+ llm_model: str = "gemini-2.0-flash"
67
+ llm_temperature: float = 0.3
68
+
69
+ # RAG Config (default values - can be overridden by strategy)
70
+ chunk_size: int = 1000
71
+ chunk_overlap: int = 200
72
+ retriever_k: int = 4 # Default retrieval count
73
+
74
+ # Reranking Config
75
+ reranker_model: str = "cross-encoder/ms-marco-MiniLM-L-6-v2"
76
+ reranker_initial_k: int = 10 # Retrieve more candidates for reranking
77
+ reranker_top_k: int = 4 # Final count after reranking
78
+
79
+ # Default strategy and dataset
80
+ default_chunking_strategy: ChunkingStrategy = ChunkingStrategy.STANDARD
81
+ default_document_set: DocumentSet = DocumentSet.ORIGINAL
82
+
83
+ # Rate Limiting
84
+ requests_per_minute: int = 15 # Per-IP limit
85
+ global_requests_per_minute: int = 10 # Global limit for Gemini free tier
86
+
87
+ # CORS
88
+ frontend_url: str = ""
89
+ allowed_origins: list[str] = [
90
+ "http://localhost:3000",
91
+ "http://127.0.0.1:3000",
92
+ ]
93
+
94
+ # Server
95
+ port: int = 7860 # HF Spaces default port
96
+
97
+ class Config:
98
+ env_file = ".env"
99
+ extra = "ignore"
100
+
101
+
102
+ @lru_cache
103
+ def get_settings() -> Settings:
104
+ """設定のシングルトンインスタンスを取得"""
105
+ return Settings()
app/data/evaluation/compare_strategies.py ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Strategy Comparison Evaluation Script
3
+
4
+ This script evaluates the RAG system across all combinations of:
5
+ - Document sets: original, optimized
6
+ - Chunking strategies: standard, large, parent_child
7
+
8
+ Usage:
9
+ cd backend
10
+ python -m evaluation.compare_strategies
11
+
12
+ Results are saved to evaluation/results/comparison_<timestamp>.json
13
+ """
14
+
15
+ import json
16
+ import sys
17
+ import io
18
+ from pathlib import Path
19
+ from datetime import datetime
20
+ from typing import Optional
21
+
22
+ # Fix Windows console encoding for Japanese output
23
+ if sys.platform == "win32":
24
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
25
+ sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
26
+
27
+ import os
28
+
29
+ # Add backend to path and change to backend directory for .env loading
30
+ backend_dir = Path(__file__).parent.parent / "backend"
31
+ sys.path.insert(0, str(backend_dir))
32
+ os.chdir(backend_dir)
33
+
34
+
35
+ def load_test_queries(path: str = "test_queries.json") -> dict:
36
+ """Load test queries from JSON file."""
37
+ with open(Path(__file__).parent / path, "r", encoding="utf-8") as f:
38
+ return json.load(f)
39
+
40
+
41
+ def check_answer_quality(
42
+ answer: str,
43
+ expected_contains: list[str],
44
+ must_not_contain: Optional[list[str]] = None
45
+ ) -> tuple[bool, str, dict]:
46
+ """
47
+ Check if answer contains expected content and doesn't contain prohibited content.
48
+
49
+ Returns:
50
+ (is_correct, reason, details)
51
+ """
52
+ answer_lower = answer.lower()
53
+
54
+ # Check for required content
55
+ found_required = []
56
+ missing_required = []
57
+ for term in expected_contains:
58
+ if term.lower() in answer_lower or term in answer:
59
+ found_required.append(term)
60
+ else:
61
+ missing_required.append(term)
62
+
63
+ # Check for prohibited content
64
+ found_prohibited = []
65
+ if must_not_contain:
66
+ for term in must_not_contain:
67
+ if term.lower() in answer_lower or term in answer:
68
+ found_prohibited.append(term)
69
+
70
+ # Determine correctness
71
+ has_required = len(found_required) > 0
72
+ has_prohibited = len(found_prohibited) > 0
73
+
74
+ is_correct = has_required and not has_prohibited
75
+
76
+ # Build reason
77
+ reasons = []
78
+ if found_required:
79
+ reasons.append(f"Found: {found_required}")
80
+ if missing_required:
81
+ reasons.append(f"Missing: {missing_required}")
82
+ if found_prohibited:
83
+ reasons.append(f"Prohibited found: {found_prohibited}")
84
+
85
+ details = {
86
+ "found_required": found_required,
87
+ "missing_required": missing_required,
88
+ "found_prohibited": found_prohibited,
89
+ }
90
+
91
+ return is_correct, "; ".join(reasons), details
92
+
93
+
94
+ def run_single_evaluation(
95
+ rag_service,
96
+ queries: list[dict],
97
+ document_set: str,
98
+ strategy: str,
99
+ ) -> dict:
100
+ """
101
+ Run evaluation for a single document_set + strategy combination.
102
+ """
103
+ results = {
104
+ "document_set": document_set,
105
+ "strategy": strategy,
106
+ "summary": {
107
+ "total": 0,
108
+ "correct": 0,
109
+ "incorrect": 0,
110
+ "by_category": {
111
+ "general": {"total": 0, "correct": 0},
112
+ "exception": {"total": 0, "correct": 0}
113
+ }
114
+ },
115
+ "details": []
116
+ }
117
+
118
+ for query in queries:
119
+ try:
120
+ # Get answer from RAG with specific settings
121
+ response = rag_service.query(
122
+ query["question"],
123
+ document_set=document_set,
124
+ strategy=strategy
125
+ )
126
+
127
+ answer = response["answer"]
128
+ chunks = response.get("chunks", [])
129
+
130
+ # Check answer quality
131
+ is_correct, reason, check_details = check_answer_quality(
132
+ answer,
133
+ query["expected_answer_contains"],
134
+ query.get("expected_answer_must_not_contain")
135
+ )
136
+
137
+ # Update summary
138
+ results["summary"]["total"] += 1
139
+ category = query["category"]
140
+ results["summary"]["by_category"][category]["total"] += 1
141
+
142
+ if is_correct:
143
+ results["summary"]["correct"] += 1
144
+ results["summary"]["by_category"][category]["correct"] += 1
145
+ else:
146
+ results["summary"]["incorrect"] += 1
147
+
148
+ # Store details
149
+ results["details"].append({
150
+ "id": query["id"],
151
+ "category": category,
152
+ "question": query["question"],
153
+ "answer": answer[:500] + "..." if len(answer) > 500 else answer,
154
+ "is_correct": is_correct,
155
+ "reason": reason,
156
+ "chunks_retrieved": len(chunks),
157
+ "top_chunk_score": chunks[0]["score"] if chunks else None,
158
+ "sources": [c["filename"] for c in chunks[:3]] if chunks else []
159
+ })
160
+
161
+ except Exception as e:
162
+ print(f" ERROR on {query['id']}: {e}")
163
+ results["details"].append({
164
+ "id": query["id"],
165
+ "category": query["category"],
166
+ "question": query["question"],
167
+ "answer": None,
168
+ "is_correct": False,
169
+ "reason": f"Error: {str(e)}",
170
+ "chunks_retrieved": 0,
171
+ "top_chunk_score": None,
172
+ "sources": []
173
+ })
174
+ results["summary"]["total"] += 1
175
+ results["summary"]["incorrect"] += 1
176
+ results["summary"]["by_category"][query["category"]]["total"] += 1
177
+
178
+ # Calculate percentages
179
+ total = results["summary"]["total"]
180
+ if total > 0:
181
+ results["summary"]["accuracy"] = round(
182
+ results["summary"]["correct"] / total * 100, 1
183
+ )
184
+
185
+ for cat in ["general", "exception"]:
186
+ cat_total = results["summary"]["by_category"][cat]["total"]
187
+ if cat_total > 0:
188
+ cat_correct = results["summary"]["by_category"][cat]["correct"]
189
+ results["summary"]["by_category"][cat]["accuracy"] = round(
190
+ cat_correct / cat_total * 100, 1
191
+ )
192
+
193
+ return results
194
+
195
+
196
+ def run_comparison(rag_service, queries: list[dict]) -> dict:
197
+ """
198
+ Run evaluation across all combinations of document sets and strategies.
199
+ """
200
+ document_sets = ["original", "optimized"]
201
+ strategies = ["standard", "large", "parent_child"]
202
+
203
+ comparison_results = {
204
+ "metadata": {
205
+ "timestamp": datetime.now().isoformat(),
206
+ "total_queries": len(queries),
207
+ "document_sets": document_sets,
208
+ "strategies": strategies,
209
+ },
210
+ "results": [],
211
+ "comparison_matrix": {}
212
+ }
213
+
214
+ for doc_set in document_sets:
215
+ comparison_results["comparison_matrix"][doc_set] = {}
216
+
217
+ for strategy in strategies:
218
+ print(f"\n{'='*60}")
219
+ print(f"Testing: {doc_set} + {strategy}")
220
+ print(f"{'='*60}")
221
+
222
+ result = run_single_evaluation(
223
+ rag_service, queries, doc_set, strategy
224
+ )
225
+ comparison_results["results"].append(result)
226
+
227
+ # Add to matrix
228
+ comparison_results["comparison_matrix"][doc_set][strategy] = {
229
+ "overall": result["summary"].get("accuracy", 0),
230
+ "general": result["summary"]["by_category"]["general"].get("accuracy", 0),
231
+ "exception": result["summary"]["by_category"]["exception"].get("accuracy", 0),
232
+ }
233
+
234
+ print(f" Overall: {result['summary'].get('accuracy', 0)}%")
235
+ print(f" General: {result['summary']['by_category']['general'].get('accuracy', 0)}%")
236
+ print(f" Exception: {result['summary']['by_category']['exception'].get('accuracy', 0)}%")
237
+
238
+ return comparison_results
239
+
240
+
241
+ def print_comparison_table(results: dict):
242
+ """Print a formatted comparison table."""
243
+ matrix = results["comparison_matrix"]
244
+
245
+ print("\n" + "=" * 80)
246
+ print("COMPARISON RESULTS")
247
+ print("=" * 80)
248
+
249
+ # Header
250
+ print(f"\n{'Dataset':<12} {'Strategy':<15} {'Overall':>10} {'General':>10} {'Exception':>10}")
251
+ print("-" * 67)
252
+
253
+ # Data rows
254
+ for doc_set in ["original", "optimized"]:
255
+ for strategy in ["standard", "large", "parent_child"]:
256
+ data = matrix[doc_set][strategy]
257
+ print(f"{doc_set:<12} {strategy:<15} {data['overall']:>9}% {data['general']:>9}% {data['exception']:>9}%")
258
+ print()
259
+
260
+ # Best combinations
261
+ print("\n" + "-" * 67)
262
+ print("KEY FINDINGS:")
263
+ print("-" * 67)
264
+
265
+ best_overall = None
266
+ best_exception = None
267
+ best_score = 0
268
+ best_exception_score = 0
269
+
270
+ for doc_set in matrix:
271
+ for strategy in matrix[doc_set]:
272
+ data = matrix[doc_set][strategy]
273
+ if data["overall"] > best_score:
274
+ best_score = data["overall"]
275
+ best_overall = (doc_set, strategy)
276
+ if data["exception"] > best_exception_score:
277
+ best_exception_score = data["exception"]
278
+ best_exception = (doc_set, strategy)
279
+
280
+ if best_overall:
281
+ print(f"\n✓ Best overall accuracy: {best_overall[0]} + {best_overall[1]} ({best_score}%)")
282
+ if best_exception:
283
+ print(f"✓ Best exception handling: {best_exception[0]} + {best_exception[1]} ({best_exception_score}%)")
284
+
285
+ # Improvement analysis
286
+ baseline = matrix.get("original", {}).get("standard", {})
287
+ optimized = matrix.get("optimized", {}).get("standard", {})
288
+
289
+ if baseline and optimized:
290
+ improvement = optimized.get("exception", 0) - baseline.get("exception", 0)
291
+ print(f"\n→ Optimized dataset improvement on exceptions: +{improvement:.1f}%")
292
+
293
+
294
+ def main():
295
+ """Main entry point."""
296
+ import argparse
297
+
298
+ parser = argparse.ArgumentParser(description="Compare RAG strategies")
299
+ parser.add_argument("--mock", action="store_true", help="Use mock RAG service")
300
+ parser.add_argument("--save", action="store_true", default=True, help="Save results to file")
301
+ args = parser.parse_args()
302
+
303
+ # Load queries
304
+ data = load_test_queries()
305
+ queries = data["queries"]
306
+
307
+ print(f"Loaded {len(queries)} test queries")
308
+ print(f" General: {len([q for q in queries if q['category'] == 'general'])}")
309
+ print(f" Exception: {len([q for q in queries if q['category'] == 'exception'])}")
310
+
311
+ if args.mock:
312
+ print("\nUsing MOCK RAG service")
313
+
314
+ class MockRAGService:
315
+ def query(self, question, document_set="original", strategy="standard"):
316
+ # Simulate different accuracy based on settings
317
+ import random
318
+ if document_set == "optimized" and "アルバイト" in question:
319
+ # Higher chance of correct answer for optimized + exception
320
+ answer = "アルバイトは紙の通勤届(短期雇用者用)を店舗責任者に提出"
321
+ else:
322
+ answer = f"Mock answer for: {question}"
323
+ return {
324
+ "answer": answer,
325
+ "chunks": [{"filename": "test.md", "score": 0.85}]
326
+ }
327
+
328
+ rag_service = MockRAGService()
329
+ else:
330
+ # Import real RAG service
331
+ try:
332
+ from app.services.rag_service import get_rag_service
333
+
334
+ print("\nInitializing RAG service...")
335
+ rag_service = get_rag_service()
336
+ print("RAG service initialized successfully")
337
+ except ImportError as e:
338
+ print(f"Error importing RAG service: {e}")
339
+ print("Use --mock flag to test the evaluation script")
340
+ return
341
+
342
+ # Run comparison
343
+ results = run_comparison(rag_service, queries)
344
+
345
+ # Print comparison table
346
+ print_comparison_table(results)
347
+
348
+ # Save results
349
+ if args.save:
350
+ results_dir = Path(__file__).parent / "results"
351
+ results_dir.mkdir(exist_ok=True)
352
+
353
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
354
+ results_file = results_dir / f"comparison_{timestamp}.json"
355
+
356
+ with open(results_file, "w", encoding="utf-8") as f:
357
+ json.dump(results, f, ensure_ascii=False, indent=2)
358
+
359
+ print(f"\n\nResults saved to: {results_file}")
360
+
361
+
362
+ if __name__ == "__main__":
363
+ main()
app/data/evaluation/run_evaluation.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RAG Evaluation Script
3
+
4
+ This script evaluates the RAG system's accuracy on test queries,
5
+ specifically comparing performance on general rules vs exception cases.
6
+
7
+ Usage:
8
+ python run_evaluation.py [--baseline] [--improved]
9
+ """
10
+
11
+ import json
12
+ import sys
13
+ import os
14
+ from pathlib import Path
15
+ from datetime import datetime
16
+
17
+ # Add backend to path
18
+ sys.path.insert(0, str(Path(__file__).parent.parent / "backend"))
19
+
20
+ from typing import Optional
21
+
22
+
23
+ def load_test_queries(path: str = "test_queries.json") -> dict:
24
+ """Load test queries from JSON file."""
25
+ with open(Path(__file__).parent / path, "r", encoding="utf-8") as f:
26
+ return json.load(f)
27
+
28
+
29
+ def check_answer_quality(
30
+ answer: str,
31
+ expected_contains: list[str],
32
+ must_not_contain: Optional[list[str]] = None
33
+ ) -> tuple[bool, str]:
34
+ """
35
+ Check if answer contains expected content and doesn't contain prohibited content.
36
+
37
+ Returns:
38
+ (is_correct, reason)
39
+ """
40
+ answer_lower = answer.lower()
41
+
42
+ # Check for required content
43
+ found_required = []
44
+ missing_required = []
45
+ for term in expected_contains:
46
+ if term.lower() in answer_lower or term in answer:
47
+ found_required.append(term)
48
+ else:
49
+ missing_required.append(term)
50
+
51
+ # Check for prohibited content
52
+ found_prohibited = []
53
+ if must_not_contain:
54
+ for term in must_not_contain:
55
+ if term.lower() in answer_lower or term in answer:
56
+ found_prohibited.append(term)
57
+
58
+ # Determine correctness
59
+ # Must have at least one required term and no prohibited terms
60
+ has_required = len(found_required) > 0
61
+ has_prohibited = len(found_prohibited) > 0
62
+
63
+ is_correct = has_required and not has_prohibited
64
+
65
+ # Build reason
66
+ reasons = []
67
+ if found_required:
68
+ reasons.append(f"Found: {found_required}")
69
+ if missing_required:
70
+ reasons.append(f"Missing: {missing_required}")
71
+ if found_prohibited:
72
+ reasons.append(f"Prohibited found: {found_prohibited}")
73
+
74
+ return is_correct, "; ".join(reasons)
75
+
76
+
77
+ def run_evaluation(
78
+ rag_service,
79
+ queries: list[dict],
80
+ save_results: bool = True,
81
+ results_prefix: str = "results"
82
+ ) -> dict:
83
+ """
84
+ Run evaluation on all queries and return results.
85
+ """
86
+ results = {
87
+ "metadata": {
88
+ "timestamp": datetime.now().isoformat(),
89
+ "total_queries": len(queries),
90
+ },
91
+ "summary": {
92
+ "total": 0,
93
+ "correct": 0,
94
+ "incorrect": 0,
95
+ "by_category": {
96
+ "general": {"total": 0, "correct": 0},
97
+ "exception": {"total": 0, "correct": 0}
98
+ }
99
+ },
100
+ "details": []
101
+ }
102
+
103
+ for query in queries:
104
+ print(f"\nProcessing: {query['id']} - {query['question'][:50]}...")
105
+
106
+ try:
107
+ # Get answer from RAG
108
+ answer, sources = rag_service.query(query["question"])
109
+
110
+ # Check answer quality
111
+ is_correct, reason = check_answer_quality(
112
+ answer,
113
+ query["expected_answer_contains"],
114
+ query.get("expected_answer_must_not_contain")
115
+ )
116
+
117
+ # Update summary
118
+ results["summary"]["total"] += 1
119
+ category = query["category"]
120
+ results["summary"]["by_category"][category]["total"] += 1
121
+
122
+ if is_correct:
123
+ results["summary"]["correct"] += 1
124
+ results["summary"]["by_category"][category]["correct"] += 1
125
+ else:
126
+ results["summary"]["incorrect"] += 1
127
+
128
+ # Store details
129
+ results["details"].append({
130
+ "id": query["id"],
131
+ "category": category,
132
+ "question": query["question"],
133
+ "answer": answer[:500] + "..." if len(answer) > 500 else answer,
134
+ "is_correct": is_correct,
135
+ "reason": reason,
136
+ "sources": [s.metadata.get("source", "unknown") for s in sources] if sources else []
137
+ })
138
+
139
+ status = "✓" if is_correct else "✗"
140
+ print(f" {status} {reason}")
141
+
142
+ except Exception as e:
143
+ print(f" ERROR: {e}")
144
+ results["details"].append({
145
+ "id": query["id"],
146
+ "category": query["category"],
147
+ "question": query["question"],
148
+ "answer": None,
149
+ "is_correct": False,
150
+ "reason": f"Error: {str(e)}",
151
+ "sources": []
152
+ })
153
+ results["summary"]["total"] += 1
154
+ results["summary"]["incorrect"] += 1
155
+ results["summary"]["by_category"][query["category"]]["total"] += 1
156
+
157
+ # Calculate percentages
158
+ total = results["summary"]["total"]
159
+ if total > 0:
160
+ results["summary"]["accuracy"] = round(
161
+ results["summary"]["correct"] / total * 100, 1
162
+ )
163
+
164
+ for cat in ["general", "exception"]:
165
+ cat_total = results["summary"]["by_category"][cat]["total"]
166
+ if cat_total > 0:
167
+ cat_correct = results["summary"]["by_category"][cat]["correct"]
168
+ results["summary"]["by_category"][cat]["accuracy"] = round(
169
+ cat_correct / cat_total * 100, 1
170
+ )
171
+
172
+ # Save results
173
+ if save_results:
174
+ results_dir = Path(__file__).parent / "results"
175
+ results_dir.mkdir(exist_ok=True)
176
+
177
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
178
+ results_file = results_dir / f"{results_prefix}_{timestamp}.json"
179
+
180
+ with open(results_file, "w", encoding="utf-8") as f:
181
+ json.dump(results, f, ensure_ascii=False, indent=2)
182
+
183
+ print(f"\nResults saved to: {results_file}")
184
+
185
+ return results
186
+
187
+
188
+ def print_summary(results: dict):
189
+ """Print evaluation summary."""
190
+ summary = results["summary"]
191
+
192
+ print("\n" + "=" * 60)
193
+ print("EVALUATION SUMMARY")
194
+ print("=" * 60)
195
+ print(f"\nOverall Accuracy: {summary.get('accuracy', 0)}%")
196
+ print(f" Correct: {summary['correct']}/{summary['total']}")
197
+ print()
198
+
199
+ print("By Category:")
200
+ for cat, stats in summary["by_category"].items():
201
+ acc = stats.get("accuracy", 0)
202
+ print(f" {cat.upper():12} : {acc}% ({stats['correct']}/{stats['total']})")
203
+
204
+ print("\n" + "=" * 60)
205
+
206
+ # Show incorrect answers
207
+ incorrect = [d for d in results["details"] if not d["is_correct"]]
208
+ if incorrect:
209
+ print("\nINCORRECT ANSWERS:")
210
+ print("-" * 40)
211
+ for item in incorrect:
212
+ print(f"\n[{item['id']}] {item['question']}")
213
+ print(f" Category: {item['category']}")
214
+ print(f" Reason: {item['reason']}")
215
+ if item['answer']:
216
+ print(f" Answer preview: {item['answer'][:200]}...")
217
+
218
+
219
+ class MockRAGService:
220
+ """Mock RAG service for testing the evaluation script."""
221
+
222
+ def query(self, question: str) -> tuple[str, list]:
223
+ # Return dummy response for testing
224
+ return f"This is a mock answer for: {question}", []
225
+
226
+
227
+ def main():
228
+ """Main entry point."""
229
+ import argparse
230
+
231
+ parser = argparse.ArgumentParser(description="Run RAG evaluation")
232
+ parser.add_argument("--mock", action="store_true", help="Use mock RAG service")
233
+ parser.add_argument("--prefix", default="results", help="Results file prefix")
234
+ args = parser.parse_args()
235
+
236
+ # Load queries
237
+ data = load_test_queries()
238
+ queries = data["queries"]
239
+
240
+ print(f"Loaded {len(queries)} test queries")
241
+ print(f" General: {len([q for q in queries if q['category'] == 'general'])}")
242
+ print(f" Exception: {len([q for q in queries if q['category'] == 'exception'])}")
243
+
244
+ if args.mock:
245
+ print("\nUsing MOCK RAG service (for testing evaluation script)")
246
+ rag_service = MockRAGService()
247
+ else:
248
+ # Import real RAG service
249
+ try:
250
+ from app.services.rag_service import get_rag_service
251
+
252
+ print("\nInitializing RAG service...")
253
+ rag_service = get_rag_service()
254
+
255
+ print("RAG service initialized successfully")
256
+ except ImportError as e:
257
+ print(f"Error importing RAG service: {e}")
258
+ print("Use --mock flag to test the evaluation script")
259
+ return
260
+
261
+ # Run evaluation
262
+ results = run_evaluation(rag_service, queries, results_prefix=args.prefix)
263
+
264
+ # Print summary
265
+ print_summary(results)
266
+
267
+
268
+ if __name__ == "__main__":
269
+ main()
app/data/evaluation/test_queries.json ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "description": "Test queries for RAG accuracy evaluation",
4
+ "created": "2024-12-15",
5
+ "purpose": "Evaluate retrieval accuracy for general rules vs exception cases"
6
+ },
7
+ "queries": [
8
+ {
9
+ "id": "general_01",
10
+ "category": "general",
11
+ "topic": "通勤手当",
12
+ "question": "正社員の通勤手当の申請方法を教えてください。",
13
+ "expected_answer_contains": ["HR Portal", "オンラインシステム", "通勤届"],
14
+ "source_file": "通勤手当規程.md",
15
+ "difficulty": "easy"
16
+ },
17
+ {
18
+ "id": "general_02",
19
+ "category": "general",
20
+ "topic": "通勤手当",
21
+ "question": "通勤手当の上限額はいくらですか?",
22
+ "expected_answer_contains": ["50,000円", "5万円"],
23
+ "source_file": "通勤手当規程.md",
24
+ "difficulty": "easy"
25
+ },
26
+ {
27
+ "id": "general_03",
28
+ "category": "general",
29
+ "topic": "休暇",
30
+ "question": "年次有給休暇は入社後どのくらいで付与されますか?",
31
+ "expected_answer_contains": ["6ヶ月", "10日"],
32
+ "source_file": "休暇規程.md",
33
+ "difficulty": "easy"
34
+ },
35
+ {
36
+ "id": "general_04",
37
+ "category": "general",
38
+ "topic": "休暇",
39
+ "question": "結婚休暇は何日取得できますか?",
40
+ "expected_answer_contains": ["5日"],
41
+ "source_file": "休暇規程.md",
42
+ "difficulty": "easy"
43
+ },
44
+ {
45
+ "id": "general_05",
46
+ "category": "general",
47
+ "topic": "経費",
48
+ "question": "経費精算の締め日と支払日を教えてください。",
49
+ "expected_answer_contains": ["15日", "25日"],
50
+ "source_file": "経費精算規程.md",
51
+ "difficulty": "easy"
52
+ },
53
+ {
54
+ "id": "general_06",
55
+ "category": "general",
56
+ "topic": "経費",
57
+ "question": "出張時の宿泊費の上限額は?",
58
+ "expected_answer_contains": ["12,000円", "10,000円"],
59
+ "source_file": "経費精算規程.md",
60
+ "difficulty": "easy"
61
+ },
62
+ {
63
+ "id": "general_07",
64
+ "category": "general",
65
+ "topic": "リモートワーク",
66
+ "question": "リモートワークは週何日まで可能ですか?",
67
+ "expected_answer_contains": ["週3日", "3日"],
68
+ "source_file": "リモートワーク規程.md",
69
+ "difficulty": "easy"
70
+ },
71
+ {
72
+ "id": "general_08",
73
+ "category": "general",
74
+ "topic": "リモートワーク",
75
+ "question": "リモートワーク手当はいくらですか?",
76
+ "expected_answer_contains": ["5,000円", "3,000円"],
77
+ "source_file": "リモートワーク規程.md",
78
+ "difficulty": "easy"
79
+ },
80
+ {
81
+ "id": "general_09",
82
+ "category": "general",
83
+ "topic": "福利厚生",
84
+ "question": "結婚祝金はいくらもらえますか?",
85
+ "expected_answer_contains": ["30,000円", "3万円"],
86
+ "source_file": "福利厚生規程.md",
87
+ "difficulty": "easy"
88
+ },
89
+ {
90
+ "id": "general_10",
91
+ "category": "general",
92
+ "topic": "服務",
93
+ "question": "正社員の健康診断はどのように実施されますか?",
94
+ "expected_answer_contains": ["年1回", "会社指定", "医療機関"],
95
+ "source_file": "服務規程.md",
96
+ "difficulty": "easy"
97
+ },
98
+ {
99
+ "id": "exception_01",
100
+ "category": "exception",
101
+ "topic": "通勤手当",
102
+ "question": "アルバイトの通勤手当の申請方法を教えてください。",
103
+ "expected_answer_contains": ["紙", "通勤届(短期雇用者用)", "店舗責任者"],
104
+ "expected_answer_must_not_contain": ["HR Portal", "オンラインシステム"],
105
+ "source_file": "通勤手当規程.md",
106
+ "difficulty": "hard",
107
+ "notes": "Exception in 附則第3条 - different from regular employee process"
108
+ },
109
+ {
110
+ "id": "exception_02",
111
+ "category": "exception",
112
+ "topic": "通勤手当",
113
+ "question": "アルバイトの通勤手当の上限額はいくらですか?",
114
+ "expected_answer_contains": ["20,000円", "2万円"],
115
+ "expected_answer_must_not_contain": ["50,000円", "5万円"],
116
+ "source_file": "通勤手当規程.md",
117
+ "difficulty": "hard",
118
+ "notes": "Exception: 20,000 vs regular 50,000"
119
+ },
120
+ {
121
+ "id": "exception_03",
122
+ "category": "exception",
123
+ "topic": "休暇",
124
+ "question": "アルバイトは結婚休暇を取得できますか?",
125
+ "expected_answer_contains": ["付与しない", "認められない", "対象外", "できない"],
126
+ "source_file": "休暇規程.md",
127
+ "difficulty": "hard",
128
+ "notes": "Exception: アルバイトは結婚休暇対象外"
129
+ },
130
+ {
131
+ "id": "exception_04",
132
+ "category": "exception",
133
+ "topic": "休暇",
134
+ "question": "パートタイム従業員は時間単位で有給休暇を取得できますか?",
135
+ "expected_answer_contains": ["認められない", "できない", "対象外"],
136
+ "source_file": "休暇規程.md",
137
+ "difficulty": "hard",
138
+ "notes": "Exception: パートは時間単位取得不可"
139
+ },
140
+ {
141
+ "id": "exception_05",
142
+ "category": "exception",
143
+ "topic": "経費",
144
+ "question": "アルバイトが経費精算できるものは何ですか?",
145
+ "expected_answer_contains": ["交通費", "のみ"],
146
+ "expected_answer_must_not_contain": ["接待交際費", "出張"],
147
+ "source_file": "経費精算規程.md",
148
+ "difficulty": "hard",
149
+ "notes": "Exception: アルバイトは交通費のみ"
150
+ },
151
+ {
152
+ "id": "exception_06",
153
+ "category": "exception",
154
+ "topic": "経費",
155
+ "question": "パートタイム従業員の経費精算はどのように行いますか?",
156
+ "expected_answer_contains": ["紙", "店舗責任者", "現金"],
157
+ "expected_answer_must_not_contain": ["オンラインシステム"],
158
+ "source_file": "経費精算規程.md",
159
+ "difficulty": "hard",
160
+ "notes": "Exception: パートは紙申請、現金支給"
161
+ },
162
+ {
163
+ "id": "exception_07",
164
+ "category": "exception",
165
+ "topic": "リモートワーク",
166
+ "question": "アルバイトはリモートワークができますか?",
167
+ "expected_answer_contains": ["原則", "認めない", "条件"],
168
+ "source_file": "リモートワーク規程.md",
169
+ "difficulty": "hard",
170
+ "notes": "Exception: 原則不可、条件付きで可能"
171
+ },
172
+ {
173
+ "id": "exception_08",
174
+ "category": "exception",
175
+ "topic": "リモートワーク",
176
+ "question": "アルバイトがリモートワークする場合、通信費は支給されますか?",
177
+ "expected_answer_contains": ["支給しない", "対象外"],
178
+ "source_file": "リモートワーク規程.md",
179
+ "difficulty": "hard",
180
+ "notes": "Exception: アルバイトは通信費・手当なし"
181
+ },
182
+ {
183
+ "id": "exception_09",
184
+ "category": "exception",
185
+ "topic": "福利厚生",
186
+ "question": "アルバイトの結婚祝金はいくらですか?",
187
+ "expected_answer_contains": ["10,000円", "1万円", "勤続1年以上"],
188
+ "expected_answer_must_not_contain": ["30,000円", "3万円"],
189
+ "source_file": "福利厚生規程.md",
190
+ "difficulty": "hard",
191
+ "notes": "Exception: 10,000 vs regular 30,000"
192
+ },
193
+ {
194
+ "id": "exception_10",
195
+ "category": "exception",
196
+ "topic": "服務",
197
+ "question": "アルバイトの健康診断の対象条件を教えてください。",
198
+ "expected_answer_contains": ["週20時間以上", "1年以上"],
199
+ "source_file": "服務規程.md",
200
+ "difficulty": "hard",
201
+ "notes": "Exception: specific conditions for アルバイト health check"
202
+ }
203
+ ]
204
+ }
app/data/evaluation/test_queries_hard.json ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "description": "Hard test suite - realistic RAG challenge scenarios",
4
+ "created": "2024-12-18",
5
+ "updated": "2024-12-18",
6
+ "purpose": "Tests RAG against realistic enterprise document patterns: implicit exceptions, multi-hop reasoning, negation understanding, etc."
7
+ },
8
+ "queries": [
9
+ {
10
+ "id": "implicit_01",
11
+ "category": "implicit_exception",
12
+ "topic": "通勤手当",
13
+ "question": "アルバイトの通勤手当の申請方法を教えてください。",
14
+ "expected_answer_contains": [
15
+ "届出書",
16
+ "店舗責任者"
17
+ ],
18
+ "source_file": "通勤手当規程.md",
19
+ "difficulty": "hard",
20
+ "notes": "Exception buried in 第11条(届出の特例)using '第2条の2に定める短期雇用者' instead of 'アルバイト'"
21
+ },
22
+ {
23
+ "id": "implicit_02",
24
+ "category": "implicit_exception",
25
+ "topic": "通勤手当",
26
+ "question": "アルバイトの通勤手当の上限額はいくらですか?",
27
+ "expected_answer_contains": ["20,000円", "2万円"],
28
+ "source_file": "通勤手当規程.md",
29
+ "difficulty": "hard",
30
+ "notes": "Exception in 第12条(支給額の特例)- uses '第2条の2に定める者'"
31
+ },
32
+ {
33
+ "id": "implicit_03",
34
+ "category": "implicit_exception",
35
+ "topic": "休暇",
36
+ "question": "アルバイトは結婚休暇を取得できますか?",
37
+ "expected_answer_contains": [
38
+ "付与しない",
39
+ "認められない",
40
+ "対象外",
41
+ "できない",
42
+ "取得できません",
43
+ "付与されません",
44
+ "認められていません",
45
+ "ありません"
46
+ ],
47
+ "source_file": "休暇規程.md",
48
+ "difficulty": "hard",
49
+ "notes": "Exception in 第15条(特別休暇の特例)- uses '第2条の2に定める者'"
50
+ },
51
+ {
52
+ "id": "implicit_04",
53
+ "category": "implicit_exception",
54
+ "topic": "経費",
55
+ "question": "アルバイトが経費精算できる項目を教えてください。",
56
+ "expected_answer_contains": ["交通費"],
57
+ "source_file": "経費精算規程.md",
58
+ "difficulty": "hard",
59
+ "notes": "Exception in 第17条(精算対象経費の特例)- uses '第2条の2に定める者'"
60
+ },
61
+ {
62
+ "id": "implicit_05",
63
+ "category": "implicit_exception",
64
+ "topic": "リモートワーク",
65
+ "question": "アルバイトはリモートワークできますか?",
66
+ "expected_answer_contains": [
67
+ "原則",
68
+ "認めない",
69
+ "できません",
70
+ "不可"
71
+ ],
72
+ "source_file": "リモートワーク規程.md",
73
+ "difficulty": "hard",
74
+ "notes": "Exception in 第19条(対象外)- uses '第2条の2に定める者'"
75
+ },
76
+ {
77
+ "id": "multihop_01",
78
+ "category": "multi_hop",
79
+ "topic": "福利厚生",
80
+ "question": "正社員とアルバイトの結婚祝金の差額はいくらですか?",
81
+ "expected_answer_contains": ["20,000円", "2万円"],
82
+ "source_file": "福利厚生規程.md",
83
+ "difficulty": "very_hard",
84
+ "notes": "Requires: (1) Find 正社員=30,000円 in 第6条 (2) Find アルバイト=10,000円 in 第18条 (3) Calculate difference"
85
+ },
86
+ {
87
+ "id": "multihop_02",
88
+ "category": "multi_hop",
89
+ "topic": "通勤手当",
90
+ "question": "正社員とアルバイトの通勤手当の上限額の差額を教えてください。",
91
+ "expected_answer_contains": ["30,000円", "3万円"],
92
+ "source_file": "通勤手当規程.md",
93
+ "difficulty": "very_hard",
94
+ "notes": "Requires: (1) Find 正社員=50,000円 in 第3条 (2) Find アルバイト=20,000円 in 第12条 (3) Calculate difference"
95
+ },
96
+ {
97
+ "id": "negation_01",
98
+ "category": "negation",
99
+ "topic": "福利厚生",
100
+ "question": "アルバイトが利用できない福利厚生制度を教えてください。",
101
+ "expected_answer_contains": [
102
+ "社員持株会",
103
+ "社員旅行",
104
+ "レクリエーション",
105
+ "資格取得支援",
106
+ "健康増進",
107
+ "育児・介護支援"
108
+ ],
109
+ "source_file": "福利厚生規程.md",
110
+ "difficulty": "very_hard",
111
+ "notes": "Requires understanding of what is NOT available - scattered across 第20-23条"
112
+ },
113
+ {
114
+ "id": "negation_02",
115
+ "category": "negation",
116
+ "topic": "経費",
117
+ "question": "アルバイトが経費精算できない項目は何ですか?",
118
+ "expected_answer_contains": [
119
+ "消耗品",
120
+ "接待交際費",
121
+ "出張旅費"
122
+ ],
123
+ "source_file": "経費精算規程.md",
124
+ "difficulty": "very_hard",
125
+ "notes": "Requires understanding negation in 第17条"
126
+ },
127
+ {
128
+ "id": "conditional_01",
129
+ "category": "conditional",
130
+ "topic": "福利厚生",
131
+ "question": "勤続6ヶ月のアルバイトは結婚祝金をもらえますか?",
132
+ "expected_answer_contains": [
133
+ "もらえません",
134
+ "対象外",
135
+ "1年以上",
136
+ "支給されません",
137
+ "条件を満たさない"
138
+ ],
139
+ "source_file": "福利厚生規程.md",
140
+ "difficulty": "very_hard",
141
+ "notes": "Requires checking condition '勤続1年以上の者に限る' in 第18条"
142
+ },
143
+ {
144
+ "id": "conditional_02",
145
+ "category": "conditional",
146
+ "topic": "リモートワーク",
147
+ "question": "入社8ヶ月のアルバイトはリモートワークできますか?",
148
+ "expected_answer_contains": [
149
+ "できません",
150
+ "1年以上",
151
+ "対象外",
152
+ "条件を満たさない"
153
+ ],
154
+ "source_file": "リモートワーク規程.md",
155
+ "difficulty": "very_hard",
156
+ "notes": "Requires checking condition '入社後1年以上経過' in 第19条"
157
+ },
158
+ {
159
+ "id": "not_in_docs_01",
160
+ "category": "not_in_documents",
161
+ "topic": "福利厚生",
162
+ "question": "アルバイトの退職金はいくらですか?",
163
+ "expected_answer_contains": [
164
+ "規定なし",
165
+ "記載なし",
166
+ "定めがない",
167
+ "見つからない",
168
+ "情報がない",
169
+ "わかりません",
170
+ "含まれていません",
171
+ "ありません",
172
+ "確認できません"
173
+ ],
174
+ "source_file": null,
175
+ "difficulty": "very_hard",
176
+ "notes": "No retirement benefit mentioned for any employee type - should not hallucinate"
177
+ },
178
+ {
179
+ "id": "not_in_docs_02",
180
+ "category": "not_in_documents",
181
+ "topic": "給与",
182
+ "question": "アルバイトのボーナス支給額を教えてください。",
183
+ "expected_answer_contains": [
184
+ "規定なし",
185
+ "記載なし",
186
+ "定めがない",
187
+ "見つからない",
188
+ "情報がない",
189
+ "わかりません",
190
+ "含まれていません",
191
+ "ありません",
192
+ "確認できません"
193
+ ],
194
+ "source_file": null,
195
+ "difficulty": "very_hard",
196
+ "notes": "No bonus information in regulations - should not hallucinate"
197
+ },
198
+ {
199
+ "id": "cross_doc_01",
200
+ "category": "cross_document",
201
+ "topic": "申請方法",
202
+ "question": "アルバイトの各種届出は全て紙で行う必要がありますか?",
203
+ "expected_answer_contains": [
204
+ "紙",
205
+ "店舗責任者"
206
+ ],
207
+ "source_file": "multiple",
208
+ "difficulty": "very_hard",
209
+ "notes": "Requires cross-referencing multiple regulations to confirm paper-based process"
210
+ },
211
+ {
212
+ "id": "cross_doc_02",
213
+ "category": "cross_document",
214
+ "topic": "経費・福利厚生",
215
+ "question": "アルバイトが会社から金銭的支援を受けられる項目を全て教えてください。",
216
+ "expected_answer_contains": [
217
+ "通勤手当",
218
+ "交通費",
219
+ "結婚祝金",
220
+ "弔慰金"
221
+ ],
222
+ "source_file": "multiple",
223
+ "difficulty": "very_hard",
224
+ "notes": "Requires aggregating from 通勤手当規程, 経費精算規程, 福利厚生規程"
225
+ }
226
+ ]
227
+ }
app/data/evaluation/test_queries_light.json ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "description": "Light test suite for quick RAG accuracy evaluation",
4
+ "created": "2024-12-16",
5
+ "purpose": "4 queries for demo - 2 general (baseline) + 2 exception (key differentiator)"
6
+ },
7
+ "queries": [
8
+ {
9
+ "id": "general_02",
10
+ "category": "general",
11
+ "topic": "通勤手当",
12
+ "question": "通勤手当の上限額はいくらですか?",
13
+ "expected_answer_contains": ["50,000円", "5万円"],
14
+ "source_file": "通勤手当規程.md",
15
+ "difficulty": "easy"
16
+ },
17
+ {
18
+ "id": "general_03",
19
+ "category": "general",
20
+ "topic": "休暇",
21
+ "question": "年次有給休暇は入社後どのくらいで付与されますか?",
22
+ "expected_answer_contains": ["6ヶ月", "10日"],
23
+ "source_file": "休暇規程.md",
24
+ "difficulty": "easy"
25
+ },
26
+ {
27
+ "id": "exception_02",
28
+ "category": "exception",
29
+ "topic": "通勤手当",
30
+ "question": "アルバイトの通勤手当の上限額はいくらですか?",
31
+ "expected_answer_contains": ["20,000円", "2万円"],
32
+ "expected_answer_must_not_contain": ["50,000円", "5万円"],
33
+ "source_file": "通勤手当規程.md",
34
+ "difficulty": "hard",
35
+ "notes": "Exception: 20,000 vs regular 50,000"
36
+ },
37
+ {
38
+ "id": "exception_03",
39
+ "category": "exception",
40
+ "topic": "休暇",
41
+ "question": "アルバイトは結婚休暇を取得できますか?",
42
+ "expected_answer_contains": [
43
+ "付与しない",
44
+ "認められない",
45
+ "対象外",
46
+ "できない",
47
+ "取得できません",
48
+ "付与されません"
49
+ ],
50
+ "source_file": "休暇規程.md",
51
+ "difficulty": "hard",
52
+ "notes": "Exception: アルバイトは結婚休暇対象外"
53
+ }
54
+ ]
55
+ }
app/data/evaluation/test_queries_medium.json ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "description": "Medium test suite for RAG accuracy evaluation",
4
+ "created": "2024-12-18",
5
+ "purpose": "10 queries for balanced testing - 5 general (baseline) + 5 exception (key differentiator)"
6
+ },
7
+ "queries": [
8
+ {
9
+ "id": "general_01",
10
+ "category": "general",
11
+ "topic": "通勤手当",
12
+ "question": "正社員の通勤手当の申請方法を教えてください。",
13
+ "expected_answer_contains": ["HR Portal", "オンラインシステム", "通勤届"],
14
+ "source_file": "通勤手当規程.md",
15
+ "difficulty": "easy"
16
+ },
17
+ {
18
+ "id": "general_02",
19
+ "category": "general",
20
+ "topic": "通勤手当",
21
+ "question": "通勤手当の上限額はいくらですか?",
22
+ "expected_answer_contains": ["50,000円", "5万円"],
23
+ "source_file": "通勤手当規程.md",
24
+ "difficulty": "easy"
25
+ },
26
+ {
27
+ "id": "general_03",
28
+ "category": "general",
29
+ "topic": "休暇",
30
+ "question": "年次有給休暇は入社後どのくらいで付与されますか?",
31
+ "expected_answer_contains": ["6ヶ月", "10日"],
32
+ "source_file": "休暇規程.md",
33
+ "difficulty": "easy"
34
+ },
35
+ {
36
+ "id": "general_04",
37
+ "category": "general",
38
+ "topic": "休暇",
39
+ "question": "結婚休暇は何日取得できますか?",
40
+ "expected_answer_contains": ["5日"],
41
+ "source_file": "休暇規程.md",
42
+ "difficulty": "easy"
43
+ },
44
+ {
45
+ "id": "general_05",
46
+ "category": "general",
47
+ "topic": "経費",
48
+ "question": "経費精算の締め日と支払日を教えてください。",
49
+ "expected_answer_contains": ["15日", "25日"],
50
+ "source_file": "経費精算規程.md",
51
+ "difficulty": "easy"
52
+ },
53
+ {
54
+ "id": "exception_01",
55
+ "category": "exception",
56
+ "topic": "通勤手当",
57
+ "question": "アルバイトの通勤手当の申請方法を教えてください。",
58
+ "expected_answer_contains": [
59
+ "紙",
60
+ "通勤届(短期雇用者用)",
61
+ "店舗責任者"
62
+ ],
63
+ "expected_answer_must_not_contain": ["HR Portal", "オンラインシステム"],
64
+ "source_file": "通勤手当規程.md",
65
+ "difficulty": "hard",
66
+ "notes": "Exception in 附則第3条 - different from regular employee process"
67
+ },
68
+ {
69
+ "id": "exception_02",
70
+ "category": "exception",
71
+ "topic": "通勤手当",
72
+ "question": "アルバイトの通勤手当の上限額はいくらですか?",
73
+ "expected_answer_contains": ["20,000円", "2万円"],
74
+ "expected_answer_must_not_contain": ["50,000円", "5万円"],
75
+ "source_file": "通勤手当規程.md",
76
+ "difficulty": "hard",
77
+ "notes": "Exception: 20,000 vs regular 50,000"
78
+ },
79
+ {
80
+ "id": "exception_03",
81
+ "category": "exception",
82
+ "topic": "休暇",
83
+ "question": "アルバイトは結婚休暇を取得できますか?",
84
+ "expected_answer_contains": [
85
+ "付与しない",
86
+ "認められない",
87
+ "対象外",
88
+ "できない",
89
+ "取得できません",
90
+ "付与されません",
91
+ "認められていません"
92
+ ],
93
+ "source_file": "休暇規程.md",
94
+ "difficulty": "hard",
95
+ "notes": "Exception: アルバイトは結婚休暇対象外"
96
+ },
97
+ {
98
+ "id": "exception_04",
99
+ "category": "exception",
100
+ "topic": "休暇",
101
+ "question": "パートタイム従業員は時間単位で有給休暇を取得できますか?",
102
+ "expected_answer_contains": [
103
+ "認められない",
104
+ "できない",
105
+ "対象外",
106
+ "認められていません",
107
+ "できません",
108
+ "取得できません",
109
+ "適用されません",
110
+ "許可されていません"
111
+ ],
112
+ "source_file": "休暇規程.md",
113
+ "difficulty": "hard",
114
+ "notes": "Exception: パートは時間単位取得不可"
115
+ },
116
+ {
117
+ "id": "exception_05",
118
+ "category": "exception",
119
+ "topic": "経費",
120
+ "question": "アルバイトが経費精算できるものは何ですか?",
121
+ "expected_answer_contains": ["交通費", "のみ"],
122
+ "expected_answer_must_not_contain": ["接待交際費", "出張"],
123
+ "source_file": "経費精算規程.md",
124
+ "difficulty": "hard",
125
+ "notes": "Exception: アルバイトは交通費のみ"
126
+ }
127
+ ]
128
+ }
app/data/regulations-optimized/リモートワーク規程_アルバイト.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # リモートワーク規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、アルバイトおよび臨時スタッフのリモートワークに関する事項を定める。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
10
+
11
+ ## 第2章 リモートワークの基本方針
12
+
13
+ ### 第3条(原則)
14
+ **⚠️ アルバイトおよび臨時スタッフについては、原則としてリモートワークを認めない。**
15
+
16
+ ## 第3章 例外的なリモートワーク
17
+
18
+ ### 第4条(例外条件)
19
+ ただし、**以下の条件をすべて満たす場合に限り**、所属長および人事部長の承認を得てリモートワークを行うことができる。
20
+
21
+ **必須条件(全て満たすこと):**
22
+ 1. **入社後1年以上経過していること**
23
+ 2. **週20時間以上の所定労働時間があること**
24
+ 3. **リモートワークに適した業務(データ入力、文書作成等)を担当していること**
25
+ 4. **過去6ヶ月間の勤怠に問題がないこと**
26
+
27
+ ### 第5条(実施条件)
28
+ 条件を満たし、リモートワークが承認された場合の条件:
29
+
30
+ 1. **実施日数は、週1日を上限とする**(正社員は週3日、パートは週2日)
31
+ 2. **ノートパソコンは貸与するが、モバイルWi-Fiルーターは貸与しない**
32
+ 3. **通信費およびリモートワーク手当は支給しない**
33
+ 4. **承認されたリモートワークは、3ヶ月ごとに更新の可否を審査する**
34
+
35
+ ### 第6条(申請手続)
36
+ **申請方法:**
37
+ 紙の「リモートワーク特別申請書」を店舗責任者に提出する。店舗責任者の意見を付して人事部に送付し、人事部長が可否を判断する。
38
+
39
+ **申請から承認まで1ヶ月程度を要する。**(正社員は即時、パートは2週間)
40
+
41
+ ### 第7条(勤務時間)
42
+ リモートワーク時の勤務時間は、通常勤務と同様とする。
43
+
44
+ 勤務の開始時および終了時には、店舗責任者にチャットまたはメールで報告すること。
45
+
46
+ ### 第8条(業務報告)
47
+ リモートワークを行った日は、当日の業務終了時に業務報告を行わなければならない。
48
+
49
+ ## 第4章 情報セキュリティ
50
+
51
+ ### 第9条(セキュリティ対策)
52
+ リモートワークを行うアルバイト・臨時スタッフは、以下のセキュリティ対策を講じなければならない。
53
+
54
+ 1. 会社指定のVPNを使用すること
55
+ 2. パスワードを適切に管理すること
56
+ 3. 公共の場所での業務を控えること
57
+ 4. 機密情報を含む書類を自宅に持ち帰らないこと
58
+
59
+ ## 附則
60
+ 本規程は、2024年4月1日から施行する。
61
+
62
+ ---
63
+ **アルバイト・臨時スタッフ向けのポイント:**
64
+ - 基本方針:**原則としてリモートワーク不可**
65
+ - 例外条件:入社1年以上、週20時間以上、適した業務、勤怠問題なし(全て必須)
66
+ - 実施日数:週1日まで(例外的に認められた場合)
67
+ - 申請方法:「リモートワーク特別申請書」→人事部長の承認必要(1ヶ月かかる)
68
+ - 機器貸与:ノートPCのみ(Wi-Fiルーターなし)
69
+ - 通信費・手当:**支給なし**
70
+ - 更新審査:3ヶ月ごと
app/data/regulations-optimized/リモートワーク規程_パート.md ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # リモートワーク規程(パートタイム従業員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、パートタイム従業員のリモートワーク(在宅勤務およびサテライトオフィス勤務)に関する事項を定め、多様な働き方の実現と生産性の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するパートタイム従業員に適用する。ただし、業務の性質上リモートワークが困難な職種については、この限りでない。
10
+
11
+ ### 第3条(定義)
12
+ 1. リモートワーク:会社の事業所以外の場所で、情報通信機器を利用して業務を行う勤務形態
13
+ 2. 在宅勤務:従業員の自宅において業務を行う勤務形態
14
+ 3. サテライトオフィス勤務:会社が指定するサテライトオフィスにおいて業務を行う勤務形態
15
+
16
+ ## 第2章 リモートワークの実施
17
+
18
+ ### 第4条(対象者)
19
+ **パートタイム従業員のリモートワーク対象者条件(正社員より厳しい):**
20
+
21
+ 1. **入社後1年以上経過していること**(正社員は6ヶ月)
22
+ 2. **週20時間以上勤務していること**
23
+ 3. 自律的に業務を遂行できる能力を有すること
24
+ 4. リモートワークに適した業務を担当していること
25
+ 5. 自宅等において業務を行うための環境が整っていること
26
+
27
+ ### 第5条(申請手続)
28
+ リモートワークを希望するパートタイム従業員は、「リモートワーク申請書」を提出し、承認を得なければならない。
29
+
30
+ **パートタイム従業員のリモートワーク申請は、紙の「リモートワーク申請書(パートタイム用)」を使用し、店舗責任者を経由して人事部に提出する。**
31
+
32
+ **申請から承認まで2週間程度を要する。**
33
+
34
+ ### 第6条(実施日数)
35
+ **パートタイム従業員のリモートワーク実施日数は、週2日を上限とする。**(正社員は週3日)
36
+
37
+ ### 第7条(勤務時間)
38
+ リモートワーク時の勤務時間は、通常勤務と同様とする。
39
+
40
+ 勤務の開始時および終了時には、店舗責任者にチャットまたはメールで報告すること。
41
+
42
+ ### 第8条(業務報告)
43
+ リモートワークを行った日は、当日の業務終了時に業務報告を行わなければならない。
44
+
45
+ ## 第3章 設備・経費
46
+
47
+ ### 第9条(機器の貸与)
48
+ 会社は、リモートワークに必要な以下の機器を貸与する。
49
+
50
+ 1. ノートパソコン
51
+ 2. モバイルWi-Fiルーター(必要に応じて)
52
+ 3. その他業務に必要な機器
53
+
54
+ ### 第10条(通信環境)
55
+ パートタイム従業員は、リモートワークを行う場所において、安定したインターネット接続環境を確保しなければならない。
56
+
57
+ 通信費は、月額3,000円を上限として実費を支給する。
58
+
59
+ ### 第11条(リモートワーク手当)
60
+ **パートタイム従業員のリモートワーク手当:**
61
+
62
+ - **月5日以上実施した場合:月額2,000円**(正社員は5,000円/3,000円)
63
+ - 月5日未満:支給なし
64
+
65
+ ## 第4章 情報セキュリティ
66
+
67
+ ### 第12条(セキュリティ対策)
68
+ リモートワークを行うパートタイム従業員は、以下のセキュリティ対策を講じなければならない。
69
+
70
+ 1. 会社指定のVPNを使用すること
71
+ 2. パスワードを適切に管理すること
72
+ 3. 公共の場所での業務を控えること
73
+ 4. 機密情報を含む書類を自宅に持ち帰らないこと
74
+
75
+ ## 附則
76
+ 本規程は、2024年4月1日から施行する。
77
+
78
+ ---
79
+ **パートタイム従業員向けのポイント:**
80
+ - 対象条件:入社後1年以上 かつ 週20時間以上勤務
81
+ - 実施日数:週2日まで(正社員は週3日)
82
+ - 申請方法:紙の申請書を店舗責任者経由で人事部に提出(承認まで2週間)
83
+ - 機器貸与:ノートPC、モバイルWi-Fiルーター
84
+ - 通信費:月額3,000円まで支給
85
+ - リモートワーク手当:月2,000円(月5日以上の場合のみ)
app/data/regulations-optimized/リモートワーク規程_正社員.md ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # リモートワーク規程(正社員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、正社員のリモートワーク(在宅勤務およびサテライトオフィス勤務)に関する事項を定め、多様な働き方の実現と生産性の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務する正社員に適用する。ただし、業務の性質上リモートワークが困難な職種については、この限りでない。
10
+
11
+ ### 第3条(定義)
12
+ 1. リモートワーク:会社の事業所以外の場所で、情報通信機器を利用して業務を行う勤務形態
13
+ 2. 在宅勤務:従業員の自宅において業務を行う勤務形態
14
+ 3. サテライトオフィス勤務:会社が指定するサテライトオフィスにおいて業務を行う勤務形態
15
+
16
+ ## 第2章 リモートワークの実施
17
+
18
+ ### 第4条(対象者)
19
+ リモートワークの対象者は、以下の条件を満たす正社員とする。
20
+
21
+ 1. **入社後6ヶ月以上経過していること**
22
+ 2. 自律的に業務を遂行できる能力を有すること
23
+ 3. リモートワークに適した業務を担当していること
24
+ 4. 自宅等において業務を行うための環境が整っていること
25
+
26
+ ### 第5条(申請手続)
27
+ リモートワークを希望する正社員は、「リモートワーク申請書」を所属長に提出し、承認を得なければならない。
28
+
29
+ **正社員の場合、リモートワーク申請はオンラインシステム「HR Portal」から申請する。** 承認後、勤怠管理システムにリモートワーク日として登録する。
30
+
31
+ ### 第6条(実施日数)
32
+ **リモートワークの実施日数は、原則として週3日を上限とする。** ただし、業務の都合により、所属長の判断で日数を増減することができる。
33
+
34
+ ### 第7条(勤務時間)
35
+ リモートワーク時の勤務時間は、通常勤務と同様とする。ただし、フレックスタイム制の適用を受ける従業員は、コアタイムを除き、始業・終業時刻を自由に設定できる。
36
+
37
+ 勤務の開始時および終了時には、所属長にチャットまたはメールで報告すること。
38
+
39
+ ### 第8条(業務報告)
40
+ リモートワークを行った日は、当日の業務終了時に業務報告を行わなければならない。
41
+
42
+ ## 第3章 設備・経費
43
+
44
+ ### 第9条(機器の貸与)
45
+ 会社は、リモートワークに必要な以下の機器を貸与する。
46
+
47
+ 1. ノートパソコン
48
+ 2. モバイルWi-Fiルーター(必要に応じて)
49
+ 3. その他業務に必要な機器
50
+
51
+ ### 第10条(通信環境)
52
+ 従業員は、リモートワークを行う場所において、安定したインターネット接続環境を確保しなければならない。
53
+
54
+ **通信費は、月額3,000円を上限として実費を支給する。**
55
+
56
+ ### 第11条(リモートワーク手当)
57
+ リモートワークを行う正社員には、光熱費等の負担に対する手当として、リモートワーク手当を支給する。
58
+
59
+ **支給額:**
60
+ - 月10日以上:月額5,000円
61
+ - 月5日以上10日未満:月額3,000円
62
+ - 月5日未満:支給なし
63
+
64
+ ## 第4章 情報セキュリティ
65
+
66
+ ### 第12条(セキュリティ対策)
67
+ リモートワークを行う正社員は、以下のセキュリティ対策を講じなければならない。
68
+
69
+ 1. 会社指定のVPNを使用すること
70
+ 2. パスワードを適切に管理すること
71
+ 3. 公共の場所での業務を控えること
72
+ 4. 機密情報を含む書類を自宅に持ち帰らないこと
73
+
74
+ ## 附則
75
+ 本規程は、2024年4月1日から施行する。
76
+
77
+ ---
78
+ **正社員向けのポイント:**
79
+ - 対象条件:入社後6ヶ月以上
80
+ - 実施日数:週3日まで
81
+ - 申請方法:HR Portalからオンライン申請
82
+ - 機器貸与:ノートPC、モバイルWi-Fiルーター
83
+ - 通信費:月額3,000円まで支給
84
+ - リモートワーク手当:月5,000円(月10日以上)または3,000円(月5〜9日)
app/data/regulations-optimized/休暇規程_アルバイト.md ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 休暇規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、アルバイトおよび臨時スタッフの休暇に関する事項を定め、従業員の心身のリフレッシュと業務効率の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
10
+
11
+ ## 第2章 年次有給休暇
12
+
13
+ ### 第3条(年次有給休暇の付与)
14
+ **アルバイト・臨時スタッフの年次有給休暇は、6ヶ月間継続勤務し、全労働日の8割以上出勤した場合に、週の所定労働日数に応じて比例付与する。**
15
+
16
+ 付与日数は、週の所定労働日数に基づき、法定の比例付与基準により決定される。
17
+
18
+ ### 第4条(年次有給休暇の取得)
19
+ アルバイト・臨時スタッフは、年次有給休暇を取得しようとする場合、原則として1週間前までに店舗責任者に届け出なければならない。
20
+
21
+ **アルバイト・臨時スタッフの制限事項:**
22
+ - **半日単位および時間単位の取得は認められない。1日単位でのみ取得可能。**
23
+ - 急な休暇の場合は、店舗責任者に電話連絡の上、次回出勤時に届出を提出する。
24
+
25
+ **休暇申請は、店舗備え付けの「シフト変更届」に記入し、1週間前までに店舗責任者に提出する。**
26
+
27
+ ### 第5条(年次有給休暇の繰越)
28
+ 年次有給休暇は、付与日から2年間有効とする。当該年度内に取得しなかった日数は、翌年度に限り繰り越すことができる。
29
+
30
+ ## 第3章 特別休暇
31
+
32
+ ### 第7条(特別休暇の種類)
33
+ **アルバイト・臨時スタッフに付与される特別休暇は、正社員より限定されている。**
34
+
35
+ 付与される特別休暇:
36
+ 1. 忌引休暇(限定):
37
+ - **配偶者・父母・子の場合のみ付与**
38
+ - **日数は正社員の半分(端数切り上げ)**
39
+ - 配偶者:5日(正社員10日の半分)
40
+ - 父母・子:4日(正社員7日の半分、端数切り上げ)
41
+ 2. 生理休暇:生理日の就業が著しく困難な場合、必要な期間
42
+ 3. 裁判員休暇:裁判員として裁判所に出頭する場合、必要な日数
43
+
44
+ **付与されない特別休暇:**
45
+ - ❌ 結婚休暇
46
+ - ❌ 配偶者出産休暇
47
+ - ❌ 忌引休暇(祖父母・兄弟姉妹・配偶者の父母)
48
+
49
+ ### 第8条(特別休暇の申請)
50
+ 特別休暇を取得しようとするアルバイト・臨時スタッフは、事前に店舗責任者に届け出なければならない。ただし、忌引休暇等の緊急の場合は、電話連絡の上、次回出勤時に届出を提出する。
51
+
52
+ ## 第4章 その他の休暇
53
+
54
+ ### 第9条(産前産後休暇)
55
+ 6週間(多胎妊娠の場合は14週間)以内に出産予定の女性アルバイト・臨時スタッフは、請求により産前休暇を取得できる。出産後8週間は産後休暇として就業させない。
56
+
57
+ ### 第10条(育児休業)
58
+ 1歳未満の子を養育するアルバイト・臨時スタッフは、一定の要件を満たす場合、育児休業を取得することができる。詳細は育児・介護休業規程に定める。
59
+
60
+ ### 第11条(介護休暇)
61
+ 要介護状態にある家族を介護するアルバイト・臨時スタッフは、介護休暇を取得することができる。詳細は育児・介護休業規程に定める。
62
+
63
+ ## 附則
64
+ 本規程は、2024年4月1日から施行する。
65
+
66
+ ---
67
+ **アルバイト・臨時スタッフ向けのポイント:**
68
+ - 年次有給休暇:比例付与(週の労働日数による)
69
+ - 取得単位:1日単位のみ(半日・時間単位は不可)
70
+ - 申請方法:「シフト変更届」を1週間前までに店舗責任者に提出
71
+ - 特別休暇:忌引休暇のみ(配偶者・父母・子に限定、日数は半分)
72
+ - 結婚休暇・配偶者出産休暇は付与されない
app/data/regulations-optimized/休暇規程_パート.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 休暇規程(パートタイム従業員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、パートタイム従業員の休暇に関する事項を定め、従業員の心身のリフレッシュと業務効率の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するパートタイム従業員に適用する。
10
+
11
+ ## 第2章 年次有給休暇
12
+
13
+ ### 第3条(年次有給休暇の付与)
14
+ **パートタイム従業員の年次有給休暇は、週の所定労働日数に応じて比例付与する。**
15
+
16
+ 6ヶ月間継続勤務し、全労働日の8割以上出勤した場合に付与する。
17
+
18
+ **週4日勤務の場合の付与日数:**
19
+
20
+ | 勤続年数 | 付与日数 |
21
+ |---------|---------|
22
+ | 6ヶ月 | 7日 |
23
+ | 1年6ヶ月 | 8日 |
24
+ | 2年6ヶ月 | 9日 |
25
+ | 3年6ヶ月以上 | 10日 |
26
+
27
+ ※週3日以下の勤務の場合は、さらに比例して付与日数が減少します。
28
+
29
+ ### 第4条(年次有給休暇の取得)
30
+ パートタイム従業員は、年次有給休暇を取得しようとする場合、原則として3日前までに店舗責任者に届け出なければならない。ただし、緊急やむを得ない場合は、当日の始業時刻までに届け出ることができる。
31
+
32
+ **年次有給休暇は、1日単位でのみ取得できる。半日単位および時間単位の取得は認められない。**
33
+
34
+ **パートタイム従業員の休暇申請は、紙の「休暇届(パートタイム用)」を使用し、店舗責任者に提出する。**
35
+
36
+ ### 第5条(年次有給休暇の繰越)
37
+ 年次有給休暇は、付与日から2年間有効とする。当該年度内に取得しなかった日数は、翌年度に限り繰り越すことができる。
38
+
39
+ ## 第3章 特別休暇
40
+
41
+ ### 第7条(特別休暇の種類)
42
+ 会社は、パートタイム従業員に対し、以下の特別休暇を有給で付与する。
43
+
44
+ 1. 結婚休暇:本人が結婚する場合、5日
45
+ 2. 忌引休暇:
46
+ - 配偶者:10日
47
+ - 父母・子:7日
48
+ - 祖父母・兄弟姉妹:3日
49
+ - 配偶者の父母:3日
50
+ 3. 配偶者出産休暇:配偶者が出産する場合、3日
51
+ 4. 生理休暇:生理日の就業が著しく困難な場合、必要な期間
52
+ 5. 裁判員休暇:裁判員として裁判所に出頭する場合、必要な日数
53
+
54
+ ### 第8条(特別休暇の申請)
55
+ 特別休暇を取得しようとするパートタイム従業員は、事前に店舗責任者に届け出なければならない。ただし、忌引休暇等の緊急の場合は、事後速やかに届け出ることができる。
56
+
57
+ ## 第4章 その他の休暇
58
+
59
+ ### 第9条(産前産後休暇)
60
+ 6週間(多胎妊娠の場合は14週間)以内に出産予定の女性パートタイム従業員は、請求により産前休暇を取得できる。出産後8週間は産後休暇として就業させない。
61
+
62
+ ### 第10条(育児休業)
63
+ 1歳未満の子を養育するパートタイム従業員は、育児休業を取得することができる。詳細は育児・介護休業規程に定める。
64
+
65
+ ### 第11条(介護休暇)
66
+ 要介護状態にある家族を介護するパートタイム従業員は、介護休暇を取得することができる。詳細は育児・介護休業規程に定める。
67
+
68
+ ## 附則
69
+ 本規程は、2024年4月1日から施行する。
70
+
71
+ ---
72
+ **パートタイム従業員向けのポイント:**
73
+ - 年次有給休暇:比例付与(週4日勤務で最大10日)
74
+ - 取得単位:1日単位のみ(半日・時間単位は不可)
75
+ - 申請方法:紙の「休暇届(パートタイム用)」を店舗責任者に提出
76
+ - 特別休暇:正社員と同様に付与
app/data/regulations-optimized/休暇規程_正社員.md ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 休暇規程(正社員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、正社員の休暇に関する事項を定め、従業員の心身のリフレッシュと業務効率の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務する正社員に適用する。
10
+
11
+ ## 第2章 年次有給休暇
12
+
13
+ ### 第3条(年次有給休暇の付与)
14
+ 会社は、6ヶ月間継続勤務し、全労働日の8割以上出勤した正社員に対し、10日の年次有給休暇を付与する。その後、勤続年数に応じて以下のとおり付与日数を増加させる。
15
+
16
+ | 勤続年数 | 付与日数 |
17
+ |---------|---------|
18
+ | 6ヶ月 | 10日 |
19
+ | 1年6ヶ月 | 11日 |
20
+ | 2年6ヶ月 | 12日 |
21
+ | 3年6ヶ月 | 14日 |
22
+ | 4年6ヶ月 | 16日 |
23
+ | 5年6ヶ月 | 18日 |
24
+ | 6年6ヶ月以上 | 20日 |
25
+
26
+ ### 第4条(年次有給休暇の取得)
27
+ 正社員は、年次有給休暇を取得しようとする場合、原則として3日前までに所属長に届け出なければならない。ただし、緊急やむを得ない場合は、当日の始業時刻までに届け出ることができる。
28
+
29
+ 会社は、業務の正常な運営を妨げる場合には、従業員が指定した日を変更することができる。
30
+
31
+ **年次有給休暇は、1日単位、半日単位、または時間単位(年間5日分を限度)で取得することができる。**
32
+
33
+ **正社員の休暇申請は、オンラインシステム「HR Portal」から申請する。** 申請後、所属長の承認を経て人事部で記録される。
34
+
35
+ ### 第5条(年次有給休暇の繰越)
36
+ 年次有給休暇は、付与日から2年間有効とする。当該年度内に取得しなかった日数は、翌年度に限り繰り越すことができる。
37
+
38
+ ### 第6条(計画的付与)
39
+ 会社は、労使協定を締結した場合、年次有給休暇のうち5日を超える部分について、計画的に付与日を指定することができる。
40
+
41
+ ## 第3章 特別休暇
42
+
43
+ ### 第7条(特別休暇の種類)
44
+ 会社は、正社員に対し、以下の特別休暇を有給で付与する。
45
+
46
+ 1. 結婚休暇:本人が結婚する場合、5日
47
+ 2. 忌引休暇:
48
+ - 配偶者:10日
49
+ - 父母・子:7日
50
+ - 祖父母・兄弟姉妹:3日
51
+ - 配偶者の父母:3日
52
+ 3. 配偶者出産休暇:配偶者が出産する場合、3日
53
+ 4. 生理休暇:生理日の就業が著しく困難な場合、必要な期間
54
+ 5. 裁判員休暇:裁判員として裁判所に出頭する場合、必要な日数
55
+
56
+ ### 第8条(特別休暇の申請)
57
+ 特別休暇を取得しようとする正社員は、事前に所属長に届け出なければならない。ただし、忌引休暇等の緊急の場合は、事後速やかに届け出ることができる。
58
+
59
+ 特別休暇の申請には、必要に応じて証明書類の提出を求めることがある。
60
+
61
+ ## 第4章 その他の休暇
62
+
63
+ ### 第9条(産前産後休暇)
64
+ 6週間(多胎妊娠の場合は14週間)以内に出産予定の女性正社員は、請求により産前休暇を取得できる。出産後8週間は産後休暇として就業させない。ただし、産後6週間を経過し、医師が認めた場合は就業できる。
65
+
66
+ ### 第10条(育児休業)
67
+ 1歳未満の子を養育する正社員は、育児休業を取得することができる。詳細は育児・介護休業規程に定める。
68
+
69
+ ### 第11条(介護休暇)
70
+ 要介護状態にある家族を介護する正社員は、介護休暇を取得することができる。詳細は育児・介護休業規程に定める。
71
+
72
+ ## 附則
73
+ 本規程は、2024年4月1日から施行する。
74
+
75
+ ---
76
+ **正社員向けのポイント:**
77
+ - 年次有給休暇:最大20日(勤続6年6ヶ月以上)
78
+ - 取得単位:1日、半日、時間単位(年5日分まで)
79
+ - 申請方法:HR Portalからオンライン申請
80
+ - 特別休暇:結婚5日、忌引(配偶者10日等)、配偶者出産3日など全て付与
app/data/regulations-optimized/服務規程_アルバイト.md ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 服務規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、アルバイトおよび臨時スタッフが遵守すべき服務に関する事項を定め、職場秩序の維持と業務の円滑な遂行を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
10
+
11
+ ## 第2章 勤務
12
+
13
+ ### 第3条(出勤・退勤)
14
+ アルバイト・臨時スタッフは、所定の始業時刻までに出勤し、終業時刻まで職務に専念しなければならない。
15
+
16
+ **アルバイト・臨時スタッフの出退勤の記録は、タイムカードまたは店舗端末により行う。**
17
+
18
+ **⚠️ 勤怠管理システムへのアクセス権限は付与されない。**
19
+
20
+ ### 第4条(遅刻・早退・欠勤)
21
+ アルバイト・臨時スタッフは、遅刻、早退または欠勤をする場合は、事前に店舗責任者に届け出なければならない。
22
+
23
+ **アルバイト・臨時スタッフの届出方法:**
24
+ - 原則として**電話で店舗責任者に連絡**
25
+ - 次回出勤時に「勤怠届(アルバイト用)」を提出
26
+
27
+ **⚠️ 無断欠勤が3回以上続いた場合は、解雇事由となることがある。**
28
+
29
+ ### 第5条(時間外勤務・休日勤務)
30
+ **アルバイト・臨時スタッフには、時間外勤務および休日勤務は原則として命じない。**
31
+
32
+ ### 第6条(出張)
33
+ **アルバイト・臨時スタッフには出張を命じない。**
34
+
35
+ ## 第3章 服務心得
36
+
37
+ ### 第7条(服務の基本)
38
+ アルバイト・臨時スタッフは、会社の方針を理解し、職場の秩序を維持するとともに、誠実に職務を遂行しなければならない。
39
+
40
+ ### 第8条(禁止事項)
41
+ アルバイト・臨時スタッフは、以下の行為をしてはならない。
42
+
43
+ 1. 会社の許可なく、会社の施設、物品を私的に使用すること
44
+ 2. **競業他社での勤務**(副業は原則自由だが、競業は禁止)
45
+ 3. 会社の秘密情報を外部に漏らすこと
46
+ 4. ハラスメントに該当する行為を行うこと
47
+ 5. 会社の信用を損なう行為を行うこと
48
+ 6. その他、職場の秩序を乱す行為を行うこと
49
+
50
+ ### 第9条(副業について)
51
+ **アルバイト・臨時スタッフは、副業について会社への届出なしに行うことができる。**
52
+
53
+ ただし、**競業他社での勤務は禁止**する。
54
+
55
+ ### 第10条(SNS等の利用)
56
+ アルバイト・臨時スタッフは、SNS等において、会社や業務に関する情報を発信する場合は、以下の事項を遵守すること。
57
+
58
+ 1. 会社の秘密情報を投稿しないこと
59
+ 2. 会社や取引先の信用を損なう投稿をしないこと
60
+ 3. 個人的な見解であることを明示すること
61
+
62
+ ### 第11条(身だしなみ)
63
+ アルバイト・臨時スタッフは、業務に適した清潔な身だしなみを保つこと。制服が定められている場合は、制服を着用すること。
64
+
65
+ ## 第4章 安全衛生
66
+
67
+ ### 第12条(安全衛生の遵守)
68
+ アルバイト・臨時スタッフは、安全衛生に関する法令および会社の規則を遵守し、災害の防止と健康の保持増進に努めなければならない。
69
+
70
+ ### 第13条(健康診断)
71
+ **アルバイト・臨時スタッフの健康診断:**
72
+
73
+ **対象者:**
74
+ - **週20時間以上勤務** かつ **1年以上継続勤務が見込まれる者**
75
+
76
+ **実施方法:**
77
+ - 年1回、会社指定日に**店舗単位で実施**
78
+ - **個別の日程調整は行わない**
79
+ - オプション検査は受けられない
80
+
81
+ ### 第14条(感染症対策)
82
+ アルバイト・臨時スタッフは、感染症の予防に努めること。感染症に罹患した場合、または罹患した疑いがある場合は、速やかに店舗責任者に報告し、会社の指示に従うこと。
83
+
84
+ ## 第5章 ハラスメント防止
85
+
86
+ ### 第15条(ハラスメントの禁止)
87
+ アルバイト・臨時スタッフは、パワーハラスメント、セクシャルハラスメント、マタニティハラスメント等のハラスメント行為を行ってはならない。
88
+
89
+ ### 第16条(相談窓口)
90
+ 会社は、ハラスメントに関する相談窓口を設置する。ハラスメントを受けた場合、または目撃した場合は、相談窓口に相談すること。
91
+
92
+ 相談者のプライバシーは厳守し、相談したことを理由に不利益な取扱いは行わない。
93
+
94
+ ## 附則
95
+ 本規程は、2024年4月1日から施行する。
96
+
97
+ ---
98
+ **アルバイト・臨時スタッフ向けのポイント:**
99
+ - 出退勤:タイムカードまたは店舗端末(勤怠管理システムへのアクセス権なし)
100
+ - 届出方法:電話で店舗責任者に連絡→次回出勤時に書面提出
101
+ - 時間外・休日勤務:原則なし
102
+ - 出���:なし
103
+ - 副業:届出不要で可能(ただし競業他社は禁止)
104
+ - 健康診断:週20時間以上&1年以上勤務見込みの場合のみ、店舗単位で実施
105
+ - ⚠️ 無断欠勤3回以上で解雇事由
app/data/regulations-optimized/服務規程_パート.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 服務規程(パートタイム従業員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、パートタイム従業員が遵守すべき服務に関する事項を定め、職場秩序の維持と業務の円滑な遂行を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するパートタイム従業員に適用する。
10
+
11
+ ## 第2章 勤務
12
+
13
+ ### 第3条(出勤・退勤)
14
+ パートタイム従業員は、所定の始業時刻までに出勤し、終業時刻まで職務に専念しなければならない。
15
+
16
+ **パートタイム従業員の出退勤の記録は、タイムカードまたは店舗端末により行う。**
17
+
18
+ ### 第4条(遅刻・早退・欠勤)
19
+ パートタイム従業員は、遅刻、早退または欠勤をする場合は、事前に店舗責任者に届け出なければならない。やむを得ない事情により事前に届け出ができない場合は、速やかに届け出ること。
20
+
21
+ **勤怠に関する届出は、店舗備え付けの用紙を使用し、店舗責任者に提出する。**
22
+
23
+ ### 第5条(時間外勤務・休日勤務)
24
+ **パートタイム従業員には、時間外勤務は原則として命じない。業務上やむを得ない場合は、本人の同意を得て行う。**
25
+
26
+ ### 第6条(出張)
27
+ 会社は、業務上必要がある場合、パートタイム従業員に出張を命じることがある。
28
+
29
+ ## 第3章 服務心得
30
+
31
+ ### 第7条(服務の基本)
32
+ パートタイム従業員は、会社の方針を理解し、職場の秩序を維持するとともに、誠実に職務を遂行しなければならない。
33
+
34
+ ### 第8条(禁止事項)
35
+ パートタイム従業員は、以下の行為をしてはならない。
36
+
37
+ 1. 会社の許可なく、会社の施設、物品を私的に使用すること
38
+ 2. 会社の許可なく、在職中に他の会社の業務に従事すること
39
+ 3. 会社の秘密情報を外部に漏らすこと
40
+ 4. ハラスメントに該当する行為を行うこと
41
+ 5. 会社の信用を損なう行為を行うこと
42
+ 6. その他、職場の秩序を乱す行為を行うこと
43
+
44
+ ### 第9条(SNS等の利用)
45
+ パートタイム従業員は、SNS等において、会社や業務に関する情報を発信する場合は、以下の事項を遵守すること。
46
+
47
+ 1. 会社の秘密情報を投稿しないこと
48
+ 2. 会社や取引先の信用を損なう投稿をしないこと
49
+ 3. 個人的な見解であることを明示すること
50
+
51
+ ### 第10条(身だしなみ)
52
+ パートタイム従業員は、業務に適した清潔な身だしなみを保つこと。制服が定められている場合は、制服を着用すること。
53
+
54
+ ## 第4章 安全衛生
55
+
56
+ ### 第11条(安全衛生の遵守)
57
+ パートタイム従業員は、安全衛生に関する法令および会社の規則を遵守し、災害の防止と健康の保持増進に努めなければならない。
58
+
59
+ ### 第12条(健康診断)
60
+ パートタイム従業員は、会社が実施する健康診断を受診しなければならない。
61
+
62
+ **パートタイム従業員の健康診断:**
63
+ - **週20時間以上勤務する者を対象**
64
+ - **年1回実施**
65
+ - **オプション検査は受けられない**
66
+
67
+ ### 第13条(感染症対策)
68
+ パートタイム従業員は、感染症の予防に努めること。感染症に罹患した場合、または罹患した疑いがある場合は、速やかに店舗責任者に報告し、会社の指示に従うこと。
69
+
70
+ ## 第5章 ハラスメント防止
71
+
72
+ ### 第14条(ハラスメントの禁止)
73
+ パートタイム従業員は、パワーハラスメント、セクシャルハラスメント、マタニティハラスメント等のハラスメント行為を行ってはならない。
74
+
75
+ ### 第15条(相談窓口)
76
+ 会社は、ハラスメントに関する相談窓口を設置する。ハラスメントを受けた場合、または目撃した場合は、相談窓口に相談すること。
77
+
78
+ 相談者のプライバシーは厳守し、相談したことを理由に不利益な取扱いは行わない。
79
+
80
+ ## 附則
81
+ 本規程は、2024年4月1日から施行する。
82
+
83
+ ---
84
+ **パートタイム従業員向けのポイント:**
85
+ - 出退勤:タイムカードまたは店舗端末で記録
86
+ - 時間外勤務:原則なし(やむを得ない場合は本人同意が必要)
87
+ - 届出方法:店舗備え付けの用紙を店舗責任者に提出
88
+ - 健康診断:週20時間以上勤務者のみ、年1回(オプション検査なし)
app/data/regulations-optimized/服務規程_正社員.md ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 服務規程(正社員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、正社員が遵守すべき服務に関する事項を定め、職場秩序の維持と業務の円滑な遂行を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務する正社員に適用する。
10
+
11
+ ## 第2章 勤務
12
+
13
+ ### 第3条(出勤・退勤)
14
+ 正社員は、所定の始業時刻までに出勤し、終業時刻まで職務に専念しなければならない。
15
+
16
+ **正社員の出退勤は、勤怠管理システムにより打刻する。** 打刻忘れの場合は、当日中に所属長に報告し、翌営業日までにシステム上で修正申請を行うこと。
17
+
18
+ ### 第4条(遅刻・早退・欠勤)
19
+ 正社員は、遅刻、早退または欠勤をする場合は、事前に所属長に届け出なければならない。やむを得ない事情により事前に届け出ができない場合は、速やかに届け出ること。
20
+
21
+ ### 第5条(時間外勤務・休日勤務)
22
+ 会社は、業務上必要がある場合、正社員に時間外勤務または休日勤務を命じることがある。
23
+
24
+ 時間外勤務および休日勤務を行う場合は、事前に所属長の承認を得なければならない。
25
+
26
+ ### 第6条(出張)
27
+ 会社は、業務上必要がある場合、正社員に出張を命じることがある。出張を命じられた正社員は、正当な理由なくこれを拒否することはできない。
28
+
29
+ ## 第3章 服務心得
30
+
31
+ ### 第7条(服務の基本)
32
+ 正社員は、会社の方針を理解し、職場の秩序を維持するとともに、誠実に職務を遂行しなければならない。
33
+
34
+ ### 第8条(禁止事項)
35
+ 正社員は、以下の行為をしてはならない。
36
+
37
+ 1. 会社の許可なく、会社の施設、物品を私的に使用すること
38
+ 2. 会社の許可なく、在職中に他の会社の業務に従事すること
39
+ 3. 会社の秘密情報を外部に漏らすこと
40
+ 4. ハラスメントに該当する行為を行うこと
41
+ 5. 会社の信用を損なう行為を行うこと
42
+ 6. その他、職場の秩序を乱す行為を行うこと
43
+
44
+ ### 第9条(SNS等の利用)
45
+ 正社員は、SNS等において、会社や業務に関する情報を発信する場合は、以下の事項を遵守すること。
46
+
47
+ 1. 会社の秘密情報を投稿しないこと
48
+ 2. 会社や取引先の信用を損なう投稿をしないこと
49
+ 3. 個人的な見解であることを明示すること
50
+
51
+ ### 第10条(身だしなみ)
52
+ 正社員は、業務に適した清潔な身だしなみを保つこと。制服が定められている場合は、制服を着用すること。
53
+
54
+ ## 第4章 安全衛生
55
+
56
+ ### 第11条(安全衛生の遵守)
57
+ 正社員は、安全衛生に関する法令および会社の規則を遵守し、災害の防止と健康の保持増進に努めなければならない。
58
+
59
+ ### 第12条(健康診断)
60
+ 正社員は、会社が実施する健康診断を受診しなければならない。
61
+
62
+ **正社員の健康診断は、年1回、会社指定の医療機関で実施する。希望者はオプション検査(費用は自己負担)を受けることができる。** 予約は、人事部が一括して行う。
63
+
64
+ ### 第13条(感染症対策)
65
+ 正社員は、感染症の予防に努めること。感染症に罹患した場合、または罹患した疑いがある場合は、速やかに所属長に報告し、会社の指示に従うこと。
66
+
67
+ ## 第5章 ハラスメント防止
68
+
69
+ ### 第14条(ハラスメントの禁止)
70
+ 正社員は、パワーハラスメント、セクシャルハラスメント、マタニティハラスメント等のハラスメント行為を行ってはならない。
71
+
72
+ ### 第15条(相談窓口)
73
+ 会社は、ハラスメントに関する相談窓口を設置する。ハラスメントを受けた場合、または目撃した場合は、相談窓口に相談すること。
74
+
75
+ 相談者のプライバシーは厳守し、相談したことを理由に不利益な取扱いは行わない。
76
+
77
+ ## 附則
78
+ 本規程は、2024年4月1日から施行する。
79
+
80
+ ---
81
+ **正社員向けのポイント:**
82
+ - 出退勤:勤怠管理システムで打刻
83
+ - 時間外・休日勤務:業務上必要な場合に命じられる
84
+ - 出張:命じられる可能性あり
85
+ - 健康診断:年1回、オプション検査も可能
86
+ - 副業:会社の許可が必要
app/data/regulations-optimized/福利厚生規程_アルバイト.md ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 福利厚生規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、アルバイトおよび臨時スタッフの福利厚生に関する事項を定め、従業員の生活の安定と向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
10
+
11
+ ## 第2章 社会保険
12
+
13
+ ### 第3条(社会保険の加入)
14
+ アルバイト・臨時スタッフは、法令に基づき、加入要件を満たす者が健康保険、厚生年金保険、雇用保険および労災保険に加入する。
15
+
16
+ ### 第4条(保険料の負担)
17
+ 健康保険および厚生年金保険の保険料は、法令に定める割合に基づき、会社と従業員が折半して負担する。
18
+
19
+ ## 第3章 慶弔見舞金
20
+
21
+ ### 第5条(慶弔見舞金の種類と支給額)
22
+ **アルバイト・臨時スタッフに支給される慶弔見舞金は、大幅に限定される。**
23
+
24
+ **支給される項目:**
25
+
26
+ | 種類 | 条件 | 支給額 |
27
+ |-----|------|-------|
28
+ | 結婚祝金 | **勤続1年以上の者に限る** | **10,000円** |
29
+ | 弔慰金 | 配偶者・父母・子の死亡時のみ | **10,000円** |
30
+
31
+ **支給されない項目:**
32
+ - ❌ 出産祝金
33
+ - ❌ 弔慰金(祖父母・兄弟姉妹・配偶者の父母)
34
+ - ❌ 傷病見舞金
35
+ - ❌ 災害見舞金
36
+
37
+ ### 第6条(申請手続)
38
+ **アルバイト・臨時スタッフの慶弔見舞金申請方法:**
39
+
40
+ 1. **店舗責任者に口頭で申し出る**
41
+ 2. 店舗責任者が「慶弔見舞金申請書(簡易版)」を作成し、人事部に提出
42
+ 3. **支給は、申請から2週間以内に現金で行う**
43
+
44
+ ## 第4章 社員持株会・社員旅行・レクリエーション
45
+
46
+ ### 第7条(参加不可の制度)
47
+ **アルバイト・臨時スタッフは、以下の制度に参加できない。**
48
+
49
+ - ❌ 社員持株会
50
+ - ❌ 社員旅行
51
+ - ❌ レクリエーション活動
52
+
53
+ ## 第5章 その他の福利厚生
54
+
55
+ ### 第8条(社員食堂)
56
+ **アルバイト・臨時スタッフは、勤務日に限り社員食堂を利用できる。**
57
+
58
+ **⚠️ ただし、会社補助はなく、全額自己負担とする。**
59
+
60
+ ### 第9条(資格取得支援)
61
+ **アルバイト・臨時スタッフは資格取得支援の対象外。**
62
+
63
+ - ❌ 受験料補助
64
+ - ❌ 合格祝金
65
+ - ❌ 資格手当
66
+
67
+ ### 第10条(健康増進支援)
68
+ **アルバイト・臨時スタッフは健康増進支援の対象外。**
69
+
70
+ - ❌ スポーツジム利用補助
71
+ - ❌ 人間ドック補助
72
+ - ❌ インフルエンザ予防接種補助
73
+
74
+ ### 第11条(育児・介護支援)
75
+ **アルバイト・臨時スタッフは育児・介護支援の対象外。**
76
+
77
+ - ❌ 保育所利用補助
78
+ - ❌ 介護サービス利用補助
79
+ - ❌ ベビーシッター利用補助
80
+
81
+ ## 附則
82
+ 本規程は、2024年4月1日から施行する。
83
+
84
+ ---
85
+ **アルバイト・臨時スタッフ向けのポイント:**
86
+ - 慶弔見舞金:結婚祝金10,000円(勤続1年以上)、弔慰金10,000円(配偶者・父母・子のみ)
87
+ - 出産祝金:なし
88
+ - 申請方法:店舗責任者に口頭申出→現金支給(2週間以内)
89
+ - 社員持株会:加入不可
90
+ - 社員旅行・レクリエーション:参加不可
91
+ - 資格取得支援:対象外
92
+ - 健康増進支援:対象外
93
+ - 育児・介護支援:対象外
94
+ - 社員食堂:利用可能だが全額自己負担
app/data/regulations-optimized/福利厚生規程_パート.md ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 福利厚生規程(パートタイム従業員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、パートタイム従業員の福利厚生に関する事項を定め、従業員の生活の安定と向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するパートタイム従業員に適用する。
10
+
11
+ ## 第2章 社会保険
12
+
13
+ ### 第3条(社会保険の加入)
14
+ パートタイム従業員は、法令に基づき、加入要件を満たす者が健康保険、厚生年金保険、雇用保険および労災保険に加入する。
15
+
16
+ ### 第4条(保険料の負担)
17
+ 健康保険および厚生年金保険の保険料は、法令に定める割合に基づき、会社と従業員が折半して負担する。
18
+
19
+ ## 第3章 慶弔見舞金
20
+
21
+ ### 第5条(慶弔見舞金の種類)
22
+ **パートタイム従業員に支給される慶弔見舞金は、正社員より限定される。**
23
+
24
+ 支給対象:
25
+ 1. 結婚祝金:パートタイム従業員本人が結婚した場合
26
+ 2. 出産祝金:パートタイム従業員または配偶者が出産した場合
27
+ 3. 弔慰金:**配偶者・父母・子の死亡時のみ**
28
+
29
+ **支給されない項目:**
30
+ - ❌ 弔慰金(祖父母・兄弟姉妹・配偶者の父母)
31
+ - ❌ 傷病見舞金
32
+ - ❌ 災害見舞金
33
+
34
+ ### 第6条(支給額)
35
+ **パートタイム従業員の慶弔見舞金は、正社員の半額。**
36
+
37
+ | 種類 | 支給額 |
38
+ |-----|-------|
39
+ | 結婚祝金 | **15,000円**(正社員30,000円の半額) |
40
+ | 出産祝金 | **5,000円**(正社員10,000円の半額) |
41
+ | 弔慰金(配偶者・父母・子) | **25,000円**(正社員50,000円の半額) |
42
+
43
+ ### 第7条(申請手続)
44
+ 慶弔見舞金の支給を受けようとするパートタイム従業員は、所定の申請書に必要書類を添えて提出すること。
45
+
46
+ **パートタイム従業員の福利厚生申請は、紙の申請書を使用し、店舗責任者を経由して人事部に提出する。**
47
+
48
+ ## 第4章 社員持株会
49
+
50
+ ### 第8条(社員持株会)
51
+ **パートタイム従業員は社員持株会には加入できない。**
52
+
53
+ ## 第5章 その他の福利厚生
54
+
55
+ ### 第11条(社員食堂)
56
+ パートタイム従業員は、社員食堂を利用できる。利用料金は、会社が一部を補助する。
57
+
58
+ ### 第12条(レクリエーション)
59
+ パートタイム従業員も、会社のレクリエーション活動に参加できる。
60
+
61
+ ### 第13条(資格取得支援)
62
+ **パートタイム従業員の資格取得支援:**
63
+
64
+ - **受験料補助のみ対象**
65
+ - **上限は年間20,000円**
66
+
67
+ **支給されない項目:**
68
+ - ❌ 合格祝金
69
+ - ❌ 資格手当
70
+
71
+ ### 第14条(社員旅行)
72
+ パートタイム従業員も、社員旅行に参加できる。参加は任意とし、参加費用の一部を会社が負担する。
73
+
74
+ ### 第15条(健康増進支援)
75
+ **パートタイム従業員の健康増進支援:**
76
+
77
+ - **インフルエンザ予防接種のみ対象**(費用を会社が全額負担)
78
+
79
+ **対象外の項目:**
80
+ - ❌ スポーツジム利用補助
81
+ - ❌ 人間ドック補助
82
+
83
+ ### 第16条(育児・介護支援)
84
+ **パートタイム従業員は育児・介護支援の対象外。**
85
+
86
+ - ❌ 保育所利用補助
87
+ - ❌ 介護サービス利用補助
88
+ - ❌ ベビーシッター利用補助
89
+
90
+ ## 附則
91
+ 本規程は、2024年4月1日から施行する。
92
+
93
+ ---
94
+ **パートタイム従業員向けのポイント:**
95
+ - 慶弔見舞金:正社員の半額(結婚15,000円、出産5,000円、弔慰金25,000円)
96
+ - 弔慰金対象:配偶者・父母・子のみ(祖父母等は対象外)
97
+ - 申請方法:紙の申請書を店舗責任者経由で人事部に提出
98
+ - 社員持株会:加入不可
99
+ - 資格取得支援:受験料補助のみ(年間上限20,000円)
100
+ - 健康増進支援:インフルエンザ予防接種のみ
101
+ - 育児・介護支援:対象外
102
+ - 社員旅行:参加可能
103
+ - 社員食堂:利用可能(補助あり)
app/data/regulations-optimized/福利厚生規程_正社員.md ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 福利厚生規程(正社員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、正社員の福利厚生に関する事項を定め、従業員の生活の安定と向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務する正社員に適用する。
10
+
11
+ ## 第2章 社会保険
12
+
13
+ ### 第3条(社会保険の加入)
14
+ 正社員は、法令に基づき、健康保険、厚生年金保険、雇用保険および労災保険に加入する。
15
+
16
+ ### 第4条(保険料の負担)
17
+ 健康保険および厚生年金保険の保険料は、法令に定める割合に基づき、会社と従業員が折半して負担する。
18
+
19
+ ## 第3章 慶弔見舞金
20
+
21
+ ### 第5条(慶弔見舞金の種類)
22
+ 会社は、正社員に対し、以下の慶弔見舞金を支給する。
23
+
24
+ 1. 結婚祝金:正社員本人が結婚した場合
25
+ 2. 出産祝金:正社員または配偶者が出産した場合
26
+ 3. 弔慰金:正社員の親族が死亡した場合
27
+ 4. 傷病見舞金:正社員が業務外の傷病により一定期間休業した場合
28
+ 5. 災害見舞金:正社員が自然災害等により被害を受けた場合
29
+
30
+ ### 第6条(支給額)
31
+ 慶弔見舞金の支給額は、以下のとおりとする。
32
+
33
+ | 種類 | 支給額 |
34
+ |-----|-------|
35
+ | 結婚祝金 | 30,000円 |
36
+ | 出産祝金 | 10,000円(子1人につき) |
37
+ | 弔慰金(配偶者・父母・子) | 50,000円 |
38
+ | 弔慰金(祖父母・兄弟姉妹) | 20,000円 |
39
+ | 弔慰金(配偶者の父母) | 20,000円 |
40
+ | 傷病見舞金 | 10,000円 |
41
+ | 災害見舞金 | 被害状況に応じて決定 |
42
+
43
+ ### 第7条(申請手続)
44
+ 慶弔見舞金の支給を受けようとする正社員は、所定の申請書に必要書類を添えて、人事部に提出すること。
45
+
46
+ **正社員の場合、申請はオンラインシステム「HR Portal」から行う。** 証明書類はスキャンしてアップロードするか、人事部に原本を郵送する。
47
+
48
+ ## 第4章 社員持株会
49
+
50
+ ### 第8条(社員持株会)
51
+ 会社は、正社員の財産形成を支援するため、社員持株会制度を設ける。
52
+
53
+ ### 第9条(加入資格)
54
+ **社員持株会に加入できる者は、入社1年以上の正社員とする。**
55
+
56
+ ### 第10条(奨励金)
57
+ 会社は、持株会を通じて自社株式を購入する正社員に対し、**拠出金額の10%を奨励金として支給する。**
58
+
59
+ ## 第5章 その他の福利厚生
60
+
61
+ ### 第11条(社員食堂)
62
+ 会社は、本社および一部の事業所に社員食堂を設置する。利用料金は、会社が一部を補助する。
63
+
64
+ ### 第12条(レクリエーション)
65
+ 会社は、正社員の親睦を深めるため、レクリエーション活動を支援する。
66
+
67
+ ### 第13条(資格取得支援)
68
+ 会社は、業務に関連する資格の取得を奨励し、以下の支援を行う。
69
+
70
+ 1. **受験料の補助:会社が指定する資格について、受験料の全額または一部を補助する。**
71
+ 2. **合格祝金:会社が指定する資格に合格した場合、合格祝金を支給する。**
72
+ 3. **資格手当:特定の資格を保有する正社員に対し、資格手当を支給する。**
73
+
74
+ **正社員の資格取得支援申請は、オンラインシステム「HR Portal」から行う。** 申請時に、資格名、受験予定日、業務との関連性を記載する。
75
+
76
+ ### 第14条(社員旅行)
77
+ 会社は、2年に1回、社員旅行を実施する。参加は任意とし、参加費用の一部を会社が負担する。
78
+
79
+ ### 第15条(健康増進支援)
80
+ 会社は、正社員の健康増進を支援するため、以下の制度を設ける。
81
+
82
+ 1. **スポーツジム利用補助:月額3,000円を上限として、スポーツジム利用料を補助する。**
83
+ 2. **人間ドック補助:35歳以上の正社員を対象に、人間ドック費用の一部を補助する。**
84
+ 3. **インフルエンザ予防接種:費用を会社が全額負担する。**
85
+
86
+ ### 第16条(育児・介護支援)
87
+ 会社は、正社員の育児および介護を支援するため、以下の制度を設ける。
88
+
89
+ 1. **保育所利用補助:認可外保育所を利用する場合、月額20,000円を上限として補助する。**
90
+ 2. **介護サービス利用補助:介護サービスを利用する場合、月額10,000円を上限として補助する。**
91
+ 3. **ベビーシッター利用補助:ベビーシッターを利用する場合、1回あたり2,000円を補助する(月4回まで)。**
92
+
93
+ ## 附則
94
+ 本規程は、2024年4月1日から施行する。
95
+
96
+ ---
97
+ **正社員向けのポイント:**
98
+ - 慶弔見舞金:結婚祝金30,000円、出産祝金10,000円、弔慰金最大50,000円など全額支給
99
+ - 申請方法:HR Portalからオンライン申請
100
+ - 社員持株会:加入可能(奨励金10%)
101
+ - 資格取得支援:受験料補助、合格祝金、資格手当の全て対象
102
+ - 健康増進支援:スポーツジム補助、人間ドック補助、インフルエンザ予防接種
103
+ - 育児・介護支援:保育所補助、介護サービス補助、ベビーシッター補助
104
+ - 社員旅行:参加可能
app/data/regulations-optimized/経費精算規程_アルバイト.md ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 経費精算規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、アルバイトおよび臨時スタッフが業務遂行上必要とした経費の精算に関する事項を定め、経費処理の適正化と効率化を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
10
+
11
+ ## 第2章 経費の種類
12
+
13
+ ### 第4条(精算対象経費)
14
+ **アルバイト・臨時スタッフが精算できる経費は、以下のみに厳しく限定される。**
15
+
16
+ **精算可能:**
17
+ - **業務上必要な交通費(通勤手当を除く)のみ**
18
+ - **1回あたり5,000円を上限とする**
19
+
20
+ **精算対象外(以下は全て不可):**
21
+ - ❌ 消耗品費(業務上必要な物品は正社員が購入し精算すること)
22
+ - ❌ 接待交際費
23
+ - ❌ 出張旅費
24
+ - ❌ 会議費
25
+ - ❌ 通信費
26
+
27
+ ## 第3章 精算手続
28
+
29
+ ### 第6条(精算申請)
30
+ **アルバイト・臨時スタッフの精算申請期限は、支出日から2週間以内。**
31
+
32
+ ⚠️ **2週間を超えた場合は、精算を認めないことがある。**(正社員の1ヶ月より短い)
33
+
34
+ **申請方法:**
35
+ 店舗備え付けの「交通費精算書(アルバイト用)」に記入し、領収書または交通系ICカードの利用履歴を添付して店舗責任者に提出する。
36
+
37
+ ### 第8条(支払)
38
+ **精算は、給与支給日に給与とともに支給する。**
39
+
40
+ ## 第4章 出張について
41
+
42
+ ### 第9条(出張)
43
+ **アルバイト・臨時スタッフには出張を命じない。**
44
+
45
+ 業務上の移動が必要な場合は、上記の交通費精算(1回5,000円上限)の範囲で対応する。
46
+
47
+ ## 附則
48
+ 本規程は、2024年4月1日から施行する。
49
+
50
+ ---
51
+ **アルバイト・臨時スタッフ向けのポイント:**
52
+ - 精算対象:業務上の交通費のみ(通勤手当除く)
53
+ - 上限:1回あたり5,000円
54
+ - 申請期限:支出日から2週間以内(超過すると精算不可の場合あり)
55
+ - 申請方法:「交通費精算書(アルバイト用)」を店舗責任者に提出
56
+ - 支払:給与支給日に給与と一緒
57
+ - 消耗品費:精算不可(正社員に購入を依頼)
58
+ - 出張:なし
app/data/regulations-optimized/経費精算規程_パート.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 経費精算規程(パートタイム従業員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、パートタイム従業員が業務遂行上必要とした経費の精算に関する事項を定め、経費処理の適正化と効率化を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するパートタイム従業員に適用する。
10
+
11
+ ### 第3条(定義)
12
+ 本規程において「経費」とは、業務遂行上必要な費用であって、会社が負担すべきものをいう。
13
+
14
+ ## 第2章 経費の種類
15
+
16
+ ### 第4条(精算対象経費)
17
+ **パートタイム従業員が精算できる経費は、以下に限定される。**
18
+
19
+ 1. **交通費:業務上の移動に要した電車、バス、タクシー等の費用**
20
+ 2. **消耗品費:業務上必要な文具、備品等の購入費**
21
+
22
+ **以下は精算対象外:**
23
+ - ❌ 接待交際費(原則として認められない)
24
+ - ❌ 会議費
25
+ - ❌ 通信費
26
+
27
+ ### 第5条(精算対象外経費)
28
+ 以下の費用は、原則として精算対象外とする。
29
+
30
+ 1. 私的な目的に使用した費用
31
+ 2. 会社の事前承認なく支出した高額経費
32
+ 3. 領収書のない経費(交通系ICカード利用を除く)
33
+ 4. 会社の経費精算基準を超える費用
34
+
35
+ ## 第3章 精算手続
36
+
37
+ ### 第6条(精算申請)
38
+ パートタイム従業員は、経費を支出した場合、原則として支出日から1ヶ月以内に精算申請を行わなければならない。
39
+
40
+ **パートタイム従業員の経費精算は、紙の「経費精算書(パートタイム用)」を使用し、店舗責任者を経由して経理部に提出する。オンラインシステムは利用できない。**
41
+
42
+ ### 第7条(承認)
43
+ 経費精算は、店舗責任者の承認を経て経理部で処理される。
44
+
45
+ ### 第8条(支払)
46
+ **精算は月1回、翌月15日に現金で支給する。**
47
+
48
+ ## 第4章 出張旅費
49
+
50
+ ### 第9条(出張について)
51
+ **パートタイム従業員には、原則として出張を命じない。**
52
+
53
+ やむを得ず出張を命じる場合は、正社員の基準を準用する。
54
+
55
+ | 区分 | 日当(日帰り) | 日当(宿泊) |
56
+ |-----|--------------|-------------|
57
+ | パートタイム | 1,000円 | 2,000円 |
58
+
59
+ ## 附則
60
+ 本規程は、2024年4月1日から施行する。
61
+
62
+ ---
63
+ **パートタイム従業員向けのポイント:**
64
+ - 精算対象:交通費と消耗品費のみ
65
+ - 接待交際費:原則不可
66
+ - 申請方法:紙の「経費精算書(パートタイム用)」を店舗責任者経由で提出
67
+ - 支払:翌月15日に現金支給(給与振込ではない)
68
+ - 出張:原則なし
app/data/regulations-optimized/経費精算規程_正社員.md ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 経費精算規程(正社員向け)
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、正社員が業務遂行上必要とした経費の精算に関する事項を定め、経費処理の適正化と効率化を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務する正社員に適用する。
10
+
11
+ ### 第3条(定義)
12
+ 本規程において「経費」とは、業務遂行上必要な費用であって、会社が負担すべきものをいう。
13
+
14
+ ## 第2章 経費の種類
15
+
16
+ ### 第4条(精算対象経費)
17
+ 正社員が精算できる経費は、以下のとおりとする。
18
+
19
+ 1. 交通費:業務上の移動に要した電車、バス、タクシー等の費用
20
+ 2. 出張旅費:出張に伴う交通費、宿泊費、日当
21
+ 3. 接待交際費:取引先との会食、贈答品等の費用
22
+ 4. 会議費:社内外の会議に伴う飲食費、会場費
23
+ 5. 消耗品費:業務上必要な文具、備品等の購入費
24
+ 6. 通信費:業務上必要な郵送料、電話料金
25
+ 7. その他:上記以外で業務上必要と認められた費用
26
+
27
+ ### 第5条(精算対象外経費)
28
+ 以下の費用は、原則として精算対象外とする。
29
+
30
+ 1. 私的な目的に使用した費用
31
+ 2. 会社の事前承認なく支出した高額経費
32
+ 3. 領収書のない経費(交通系ICカード利用を除く)
33
+ 4. 会社の経費精算基準を超える費用
34
+
35
+ ## 第3章 精算手続
36
+
37
+ ### 第6条(精算申請)
38
+ 正社員は、経費を支出した場合、原則として支出日から1ヶ月以内に精算申請を行わなければならない。
39
+
40
+ **正社員の経費精算は、オンラインシステム「経費精算システム」から申請する。** 領収書は、原本をスキャンまたは撮影してシステムにアップロードし、原本は各部署で3ヶ月間保管した後、経理部に送付する。
41
+
42
+ ### 第7条(承認)
43
+ 経費精算は、以下の承認フローに従う。
44
+
45
+ 1. 5万円未満:所属長の承認
46
+ 2. 5万円以上20万円未満:所属長および部門長の承認
47
+ 3. 20万円以上:所属長、部門長および経理部長の承認
48
+
49
+ ### 第8条(支払)
50
+ 承認された経費は、毎月15日締め、当月25日に給与振込口座に振り込む。15日以降に承認された分は、翌月25日の支払となる。
51
+
52
+ ## 第4章 出張旅費
53
+
54
+ ### 第9条(出張の定義)
55
+ 本規程において「出張」とは、業務のため勤務地を離れ、宿泊を伴うまたは片道100km以上の移動を伴う勤務をいう。
56
+
57
+ ### 第10条(出張の手続)
58
+ 出張を行う場合は、事前に「出張申請書」を提出し、所属長の承認を得なければならない。
59
+
60
+ ### 第11条(交通費)
61
+ 出張の交通費は、以下の基準により支給する。
62
+
63
+ 1. 鉄道:普通車指定席。ただし、片道100km以上の場合は新幹線普通車指定席を利用できる。
64
+ 2. 航空機:エコノミークラス。ただし、部長以上はビジネスクラスを利用できる。
65
+ 3. タクシー:原則として深夜・早朝または交通機関がない場合に限り利用できる。
66
+
67
+ ### 第12条(宿泊費)
68
+ 宿泊費は、実費精算とし、上限は以下のとおりとする。
69
+
70
+ | 地域 | 上限額(1泊) |
71
+ |-----|-------------|
72
+ | 東京23区、大阪市内 | 12,000円 |
73
+ | その他の都市 | 10,000円 |
74
+ | 海外 | 別途定める |
75
+
76
+ ### 第13条(日当)
77
+ 出張時の日当は、以下のとおり支給する。
78
+
79
+ | 区分 | 日当(日帰り) | 日当(宿泊) |
80
+ |-----|--------------|-------------|
81
+ | 一般社員 | 1,000円 | 2,000円 |
82
+ | 課長以上 | 1,500円 | 3,000円 |
83
+
84
+ ## 第5章 接待交際費
85
+
86
+ ### 第14条(接待交際費の基準)
87
+ 接待交際費は、1人あたり以下の金額を目安とする。
88
+
89
+ - 会食:10,000円以内
90
+ - 贈答品:5,000円以内
91
+
92
+ 上記を超える場合は、事前に部門長の承認を得ること。
93
+
94
+ ### 第15条(接待交際費の申請)
95
+ 接待交際費の精算には、以下の情報を記載すること。
96
+
97
+ 1. 日時・場所
98
+ 2. 相手先の会社名・氏名
99
+ 3. 参加者(自社)
100
+ 4. 目的・内容
101
+
102
+ ## 附則
103
+ 本規程は、2024年4月1日から施行する。
104
+
105
+ ---
106
+ **正社員向けのポイント:**
107
+ - 精算対象:交通費、出張旅費、接待交際費、会議費、消耗品費、通信費など全て
108
+ - 申請方法:オンラインシステム「経費精算システム」から申請
109
+ - 支払:毎月15日締め、25日に給与口座へ振込
110
+ - 出張:可能(日当あり:一般1,000〜2,000円、課長以上1,500〜3,000円)
app/data/regulations-optimized/通勤手当規程_アルバイト.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 通勤手当規程(アルバイト・臨時スタッフ向け)
2
+
3
+ ## 第1条(目的)
4
+ 本規程は、アルバイトおよび臨時スタッフの通勤に要する費用の一部を会社が負担することにより、従業員の経済的負担を軽減し、もって従業員の福利厚生の向上を図ることを目的とする。
5
+
6
+ ## 第2条(適用範囲)
7
+ 本規程は、当社に勤務するアルバイトおよび臨時スタッフに適用する。
8
+
9
+ ## 第3条(通勤手当の支給)
10
+ 会社は、アルバイトおよび臨時スタッフに対し、通勤に要する実費相当額を通勤手当として支給する。
11
+
12
+ **アルバイト・臨時スタッフの特則:**
13
+ - **通勤手当の上限額は月額20,000円とする。**(正社員の50,000円より低い上限)
14
+ - **定期券の支給は行わず、実際の出勤日数に基づき日割りで支給する。**
15
+
16
+ 通勤手当の支給額は、最も経済的かつ合理的な経路および方法により算出した金額とする。
17
+
18
+ ## 第4条(申請方法)
19
+ アルバイト・臨時スタッフは、入社時および通勤経路の変更時に、通勤届を提出しなければならない。
20
+
21
+ **アルバイト・臨時スタッフの通勤届は、入社時に配布される紙の「通勤届(短期雇用者用)」に記入し、店舗責任者に提出する。**
22
+
23
+ 届出の変更は随時受け付けるが、**変更届提出月の翌月から適用**となる。
24
+
25
+ ※オンラインシステム(HR Portal)は利用できません。
26
+
27
+ ## 第5条(定期券について)
28
+ **アルバイト・臨時スタッフには定期券は支給されません。** 実際の出勤日数に基づき、日割りで交通費を計算して支給します。
29
+
30
+ ## 第6条(自動車通勤)
31
+ **アルバイト・臨時スタッフの自動車通勤は原則として認めない。**
32
+
33
+ 特別の事情がある場合は、店舗責任者の承認を得た上で総務部に申請する。承認された場合、通勤距離に応じて以下のとおり支給する。
34
+
35
+ - 片道2km以上10km未満:月額4,200円
36
+ - 片道10km以上25km未満:月額7,100円
37
+ - 片道25km以上35km未満:月額12,900円
38
+ - 片道35km以上:月額16,100円
39
+
40
+ ## 第7条(自転車通勤)
41
+ 自転車による通勤を希望するアルバイト・臨時スタッフは、「自転車通勤届」を店舗責任者に提出する。自転車通勤者には、月額2,000円の通勤手当を支給する。
42
+
43
+ ## 第8条(支給日)
44
+ 通勤手当は、毎月の給与支給日に給与とともに支給する。
45
+
46
+ ## 第9条(届出義務)
47
+ アルバイト・臨時スタッフは、住所変更、通勤経路の変更、利用交通機関の変更等があった場合は、速やかに届け出なければならない。届出を怠り、または虚偽の届出をした場合は、懲戒処分の対象となることがある。
48
+
49
+ ## 第10条(返還)
50
+ 不正な届出により通勤手当を過大に受給した場合は、過大受給額を返還しなければならない。
51
+
52
+ ## 附則
53
+ 本規程は、2024年4月1日から施行する。
54
+
55
+ ---
56
+ **アルバイト・臨時スタッフ向けのポイント:**
57
+ - 上限額:月額20,000円(正社員の50,000円より低い)
58
+ - 定期券:支給なし(日割り計算で支給)
59
+ - 申請方法:紙の「通勤届(短期雇用者用)」を店舗責任者に提出
60
+ - 自動車通勤:原則不可(特別の事情がある場合のみ申請可)
61
+ - 変更届の適用:翌月から
app/data/regulations-optimized/通勤手当規程_パート.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 通勤手当規程(パートタイム従業員向け)
2
+
3
+ ## 第1条(目的)
4
+ 本規程は、パートタイム従業員の通勤に要する費用の一部を会社が負担することにより、従業員の経済的負担を軽減し、もって従業員の福利厚生の向上を図ることを目的とする。
5
+
6
+ ## 第2条(適用範囲)
7
+ 本規程は、当社に勤務するパートタイム従業員に適用する。
8
+
9
+ ## 第3条(通勤手当の支給)
10
+ 会社は、パートタイム従業員に対し、以下の基準で通勤手当を支給する。
11
+
12
+ **パートタイム従業員の支給基準:**
13
+ - **週の所定労働日数が4日以上の場合:** 正社員に準じて支給(上限月額50,000円)
14
+ - **週の所定労働日数が3日以下の場合:** 実際の出勤日数に応じた実費を支給
15
+
16
+ 通勤手当の支給額は、最も経済的かつ合理的な経路および方法により算出した金額とする。
17
+
18
+ ## 第4条(申請方法)
19
+ パートタイム従業員は、入社時および通勤経路の変更時に、所定の「通勤届」を人事部に提出しなければならない。
20
+
21
+ **パートタイム従業員の通勤届は、紙の申請書を使用し、店舗責任者を経由して人事部に提出する。オンラインシステム(HR Portal)は利用できない。**
22
+
23
+ 申請は、毎月15日までに提出されたものを翌月分から適用する。届出を怠った場合、通勤手当の支給が遅延することがある。
24
+
25
+ ## 第5条(定期券の購入)
26
+ 電車またはバスを利用するパートタイム従業員で、週4日以上勤務する場合は、6ヶ月定期券の購入費用を支給する。週3日以下の勤務の場合は、実際の出勤日数に応じた実費を支給する。
27
+
28
+ 定期券は、会社指定の方法により購入し、購入後に領収書を提出すること。
29
+
30
+ ## 第6条(自動車通勤)
31
+ 自動車による通勤を希望するパートタイム従業員は、事前に会社の許可を得なければならない。許可を得た場合、通勤距離に応じて以下のとおり支給する。
32
+
33
+ - 片道2km以上10km未満:月額4,200円
34
+ - 片道10km以上25km未満:月額7,100円
35
+ - 片道25km以上35km未満:月額12,900円
36
+ - 片道35km以上:月額16,100円
37
+
38
+ 自動車通勤の許可申請は、「自動車通勤許可申請書」を総務部に提出する。
39
+
40
+ ## 第7条(自転車通勤)
41
+ 自転車による通勤を希望するパートタイム従業員は、「自転車通勤届」を総務部に提出する。自転車通勤者には、月額2,000円の通勤手当を支給する。
42
+
43
+ ## 第8条(支給日)
44
+ 通勤手当は、毎月の給与支給日に給与とともに支給する。
45
+
46
+ ## 第9条(届出義務)
47
+ パートタイム従業員は、住所変更、通勤経路の変更、利用交通機関の変更等があった場合は、速やかに届け出なければならない。届出を怠り、または虚偽の届出をした場合は、懲戒処分の対象となることがある。
48
+
49
+ ## 第10条(返還)
50
+ 不正な届出により通勤手当を過大に受給した場合は、過大受給額を返還しなければならない。
51
+
52
+ ## 附則
53
+ 本規程は、2024年4月1日から施行する。
54
+
55
+ ---
56
+ **パートタイム従業員向けのポイント:**
57
+ - 週4日以上勤務:正社員と同様(上限50,000円、定期券支給)
58
+ - 週3日以下勤務:実際の出勤日数に応じた実費支給
59
+ - 申請方法:紙の申請書を店舗責任者経由で提出(オンライン不可)
60
+ - 自動車通勤:申請許可制で可能
app/data/regulations-optimized/通勤手当規程_正社員.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 通勤手当規程(正社員向け)
2
+
3
+ ## 第1条(目的)
4
+ 本規程は、正社員の通勤に要する費用の一部を会社が負担することにより、従業員の経済的負担を軽減し、もって従業員の福利厚生の向上を図ることを目的とする。
5
+
6
+ ## 第2条(適用範囲)
7
+ 本規程は、当社に勤務する正社員に適用する。役員および出向者については別途定める。
8
+
9
+ ## 第3条(通勤手当の支給)
10
+ 会社は、正社員に対し、通勤に要する実費相当額を通勤手当として支給する。通勤手当の支給額は、最も経済的かつ合理的な経路および方法により算出した金額とする。
11
+
12
+ **通勤手当の上限額は月額50,000円とする。** この上限額を超える場合は、上限額までの支給とする。
13
+
14
+ ## 第4条(申請方法)
15
+ 正社員は、入社時および通勤経路の変更時に、所定の「通勤届」を人事部に提出しなければならない。
16
+
17
+ **正社員の通勤届は、オンラインシステム「HR Portal」から申請する。** 申請後、上長の承認を経て人事部で処理される。処理完了まで通常5営業日を要する。
18
+
19
+ 申請は、毎月15日までに提出されたものを翌月分から適用する。届出を怠った場合、通勤手当の支給が遅延することがある。
20
+
21
+ ## 第5条(定期券の購入)
22
+ 電車またはバスを利用する正社員には、**原則として6ヶ月定期券の購入費用を支給する。** ただし、勤務期間が6ヶ月未満の見込みの場合は、1ヶ月または3ヶ月定期券の購入費用を支給する。
23
+
24
+ 定期券は、会社指定の方法により購入し、購入後に領収書を提出すること。
25
+
26
+ ## 第6条(自動車通勤)
27
+ 自動車による通勤を希望する正社員は、事前に会社の許可を得なければならない。許可を得た場合、通勤距離に応じて以下のとおり支給する。
28
+
29
+ - 片道2km以上10km未満:月額4,200円
30
+ - 片道10km以上25km未満:月額7,100円
31
+ - 片道25km以上35km未満:月額12,900円
32
+ - 片道35km以上:月額16,100円
33
+
34
+ 自動車通勤の許可申請は、「自動車通勤許可申請書」を総務部に提出する。
35
+
36
+ ## 第7条(自転車通勤)
37
+ 自転車による通勤を希望する正社員は、「自転車通勤届」を総務部に提出する。自転車通勤者には、月額2,000円の通勤手当を支給する。
38
+
39
+ ## 第8条(支給日)
40
+ 通勤手当は、毎月の給与支給日に給与とともに支給する。
41
+
42
+ ## 第9条(届出義務)
43
+ 正社員は、住所変更、通勤経路の変更、利用交通機関の変更等があった場合は、速やかに届け出なければならない。届出を怠り、または虚偽の届出をした場合は、懲戒処分の対象となることがある。
44
+
45
+ ## 第10条(返還)
46
+ 不正な届出により通勤手当を過大に受給した場合は、過大受給額を返還しなければならない。
47
+
48
+ ## 附則
49
+ 本規程は、2024年4月1日から施行する。
50
+
51
+ ---
52
+ **正社員向けのポイント:**
53
+ - 上限額:月額50,000円
54
+ - 申請方法:HR Portalからオンライン申請
55
+ - 定期券:6ヶ月定期券支給
56
+ - 自動車通勤:申請許可制で可能
app/data/regulations/リモートワーク規程.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # リモートワーク規程
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、従業員のリモートワーク(在宅勤務およびサテライトオフィス勤務)に関する事項を定め、多様な働き方の実現と生産性の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するすべての従業員に適用する。ただし、業務の性質上リモートワークが困難な職種については、この限りでない。
10
+
11
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
12
+
13
+ ### 第2条の2(短期雇用者の定義)
14
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
15
+
16
+ ### 第3条(定義)
17
+ 本規程において使用する用語の定義は、以下のとおりとする。
18
+
19
+ 1. リモートワーク:会社の事業所以外の場所で、情報通信機器を利用して業務を行う勤務形態
20
+ 2. 在宅勤務:従業員の自宅において業務を行う勤務形態
21
+ 3. サテライトオフィス勤務:会社が指定するサテライトオフィスにおいて業務を行う勤務形態
22
+
23
+ ## 第2章 リモートワークの実施
24
+
25
+ ### 第4条(対象者)
26
+ リモートワークの対象者は、以下の条件を満たす従業員とする。
27
+
28
+ 1. 入社後6ヶ月以上経過していること
29
+ 2. 自律的に業務を遂行できる能力を有すること
30
+ 3. リモートワークに適した業務を担当していること
31
+ 4. 自宅等において業務を行うための環境が整っていること
32
+
33
+ ### 第5条(申請手続)
34
+ リモートワークを希望する従業員は、「リモートワーク申請書」を所属長に提出し、承認を得なければならない。
35
+
36
+ 申請書には、以下の事項を記載する。
37
+
38
+ 1. リモートワークを行う場所
39
+ 2. リモートワークを行う日数(週あたり)
40
+ 3. 業務内容
41
+ 4. 連絡方法
42
+
43
+ リモートワーク申請はオンラインシステム「HR Portal」から申請する。承認後、勤怠管理システムにリモートワーク日として登録する。申請は、リモートワーク開始希望日の1週間前までに行うこと。
44
+
45
+ ### 第6条(実施日数)
46
+ リモートワークの実施日数は、原則として週3日を上限とする。ただし、業務の都合により、所属長の判断で日数を増減することができる。
47
+
48
+ フルリモート勤務は、特別な事情がある場合に限り、人事部長の承認を得て認めることがある。
49
+
50
+ ### 第7条(勤務時間)
51
+ リモートワーク時の勤務時間は、通常勤務と同様とする。ただし、フレックスタイム制の適用を受ける従業員は、コアタイムを除き、始業・終業時刻を自由に設定できる。
52
+
53
+ 勤務の開始時および終了時には、所属長にチャットまたはメールで報告すること。報告は、始業時刻の5分前から始業時刻までの間、および終業時刻から15分以内に行うこと。
54
+
55
+ ### 第8条(業務報告)
56
+ リモートワークを行った日は、当日の業務終了時に業務報告を行わなければならない。報告は、所定の様式により行う。
57
+
58
+ 業務報告には、以下の事項を記載すること。
59
+
60
+ 1. 当日の業務内容
61
+ 2. 業務の進捗状況
62
+ 3. 翌日の予定
63
+ 4. 特記事項
64
+
65
+ ## 第3章 設備・経費
66
+
67
+ ### 第9条(機器の貸与)
68
+ 会社は、リモートワークに必要な以下の機器を貸与する。
69
+
70
+ 1. ノートパソコン
71
+ 2. モバイルWi-Fiルーター(必要に応じて)
72
+ 3. その他業務に必要な機器
73
+
74
+ 貸与機器は、業務目的以外に使用してはならない。貸与機器の紛失、盗難、故障等が発生した場合は、直ちに所属長および情報システム部に報告すること。
75
+
76
+ ### 第10条(通信環境)
77
+ 従業員は、リモートワークを行う場所において、安定したインターネット接続環境を確保しなければならない。
78
+
79
+ 通信費は、月額3,000円を上限として実費を支給する。領収書の提出は不要とし、リモートワーク日数に応じて自動計算される。
80
+
81
+ ### 第11条(リモートワーク手当)
82
+ リモートワークを行う従業員には、光熱費等の負担に対する手当として、リモートワーク手当を支給する。
83
+
84
+ 支給額は、リモートワークを行った日数に応じて以下のとおりとする。
85
+
86
+ - 月10日以上:月額5,000円
87
+ - 月5日以上10日未満:月額3,000円
88
+ - 月5日未満:支給なし
89
+
90
+ ## 第4章 情報セキュリティ
91
+
92
+ ### 第12条(セキュリティ対策)
93
+ リモートワークを行う従業員は、以下の���キュリティ対策を講じなければならない。
94
+
95
+ 1. 会社指定のVPNを使用すること
96
+ 2. パスワードを適切に管理すること
97
+ 3. 公共の場所での業務を控えること
98
+ 4. 機密情報を含む書類を自宅に持ち帰らないこと
99
+
100
+ ### 第13条(情報漏洩防止)
101
+ リモートワーク中に情報漏洩が発生した場合、または情報漏洩のおそれがある場合は、直ちに所属長および情報セキュリティ担当者に報告しなければならない。
102
+
103
+ 情報漏洩が従業員の故意または重大な過失によるものである場合は、懲戒処分の対象となることがある。
104
+
105
+ ## 第5章 健康管理
106
+
107
+ ### 第14条(健康管理)
108
+ リモートワークを行う従業員は、自己の健康管理に十分注意し、規則正しい生活を心がけること。
109
+
110
+ 長時間の連続作業を避け、適度な休憩を取ること。1時間ごとに5分程度の休憩を取ることを推奨する。
111
+
112
+ ### 第15条(コミュニケーション)
113
+ リモートワーク時も、チームメンバーとの円滑なコミュニケーションを維持すること。定期的なオンラインミーティングに参加し、業務の進捗を共有すること。
114
+
115
+ 週1回以上は、所属長とのオンライン面談を行うこと。
116
+
117
+ ## 第6章 リモートワークの特例
118
+
119
+ ### 第16条(対象者の特例)
120
+ 第4条の規定にかかわらず、以下の場合は入社後6ヶ月未満でもリモートワークを認めることがある。
121
+
122
+ 1. 感染症の流行等により、出社が困難な場合
123
+ 2. 育児または介護のため、一時的にリモートワークが必要な場合
124
+ 3. その他、人事部長が特に認めた場合
125
+
126
+ 週の所定労働時間が20時間以上のパートタイム従業員については、入社後1年以上経過した者を対象とする。実施日数は、週2日を上限とする。
127
+
128
+ ### 第17条(申請方法の特例)
129
+ 第5条の規定にかかわらず、業務の性質上オンラインシステムを利用することが困難な者については、紙の申請書を使用することができる。
130
+
131
+ 週の所定労働時間が20時間以上のパートタイム従業員については、紙の「リモートワーク申請書(パートタイム用)」を使用し、店舗責任者を経由して人事部に提出する。申請から承認まで2週間程度を要する。
132
+
133
+ ### 第18条(手当の特例)
134
+ 第10条および第11条の規定にかかわらず、以下の者は手当の支給を制限する。
135
+
136
+ 1. 月のリモートワーク日数が3日未満の者には、通信費を支給しない。
137
+
138
+ 週の所定労働時間が20時間以上のパートタイム従業員については、リモートワーク手当は月5日以上実施した場合に月額2,000円を支給する。
139
+
140
+ ### 第19条(対象外)
141
+ 第2条の2に定める者については、原則としてリモートワークを認めない。
142
+
143
+ ただし、以下の条件をすべて満たす場合に限り、所属長および人事部長の承認を得てリモートワークを行うことができる。
144
+
145
+ 1. 入社後1年以上経過していること
146
+ 2. 週20時間以上の所定労働時間があること
147
+ 3. リモートワークに適した業務(データ入力、文書作成等)を担当していること
148
+ 4. 過去6ヶ月間の勤怠に問題がないこと
149
+
150
+ ### 第20条(条件の特例)
151
+ 第19条に基づきリモートワークが承認された第2条の2に定める者については、以下の条件を適用する。
152
+
153
+ 1. 実施日数は、週1日を上限とする。
154
+ 2. ノートパソコンは貸与するが、モバイルWi-Fiルーターは貸与しない。
155
+ 3. 通信費およびリモートワーク手当は支給しない。
156
+ 4. 承認されたリモートワークは、3ヶ月ごとに更新の可否を審査する。
157
+
158
+ 申請は、紙の「リモートワーク特別申請書」を店舗責任者に提出する。店舗責任者の意見を付して人事部に送付し、人事部長が可否を判断する。申請から承認まで1ヶ月程度を要する。
159
+
160
+ ## 附則
161
+
162
+ ### 附則第1条
163
+ 本規程は、2024年4月1日から施行する。
164
+
165
+ ### 附則第2条(経過措置)
166
+ 本規程施行前に承認されたリモートワークについては、承認期間満了まで従前の例による。
app/data/regulations/休暇規程.md ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 休暇規程
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、従業員の休暇に関する事項を定め、従業員の心身のリフレッシュと業務効率の向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するすべての従業員に適用する。
10
+
11
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
12
+
13
+ ### 第2条の2(短期雇用者の定義)
14
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
15
+
16
+ ## 第2章 年次有給休暇
17
+
18
+ ### 第3条(年次有給休暇の付与)
19
+ 会社は、6ヶ月間継続勤務し、全労働日の8割以上出勤した従業員に対し、10日の年次有給休暇を付与する。その後、勤続年数に応じて以下のとおり付与日数を増加させる。
20
+
21
+ | 勤続年数 | 付与日数 |
22
+ |---------|---------|
23
+ | 6ヶ月 | 10日 |
24
+ | 1年6ヶ月 | 11日 |
25
+ | 2年6ヶ月 | 12日 |
26
+ | 3年6ヶ月 | 14日 |
27
+ | 4年6ヶ月 | 16日 |
28
+ | 5年6ヶ月 | 18日 |
29
+ | 6年6ヶ月以上 | 20日 |
30
+
31
+ ### 第4条(年次有給休暇の取得)
32
+ 従業員は、年次有給休暇を取得しようとする場合、原則として3日前までに所属長に届け出なければならない。ただし、緊急やむを得ない場合は、当日の始業時刻までに届け出ることができる。
33
+
34
+ 会社は、業務の正常な運営を妨げる場合には、従業員が指定した日を変更することができる。時季変更権の行使は、代替日を提案することにより行う。
35
+
36
+ 年次有給休暇は、1日単位、半日単位、または時間単位(年間5日分を限度)で取得することができる。時間単位の取得は、1時間を最小単位とする。
37
+
38
+ 休暇申請は、オンラインシステム「HR Portal」から申請する。申請後、所属長の承認を経て人事部で記録される。承認は原則として申請日から2営業日以内に行われる。
39
+
40
+ ### 第5条(年次有給休暇の繰越)
41
+ 年次有給休暇は、付与日から2年間有効とする。当該年度内に取得しなかった日数は、翌年度に限り繰り越すことができる。
42
+
43
+ 繰越日数の上限は、その年度に付与された日数とする。繰越分と当年度分がある場合は、繰越分から優先的に消化されるものとする。
44
+
45
+ ### 第6条(計画的付与)
46
+ 会社は、労使協定を締結した場合、年次有給休暇のうち5日を超える部分について、計画的に付与日を指定することができる。
47
+
48
+ 計画的付与の対象日は、年度当初に従業員に通知する。通知後の変更は、やむを得ない事情がある場合に限り認める。
49
+
50
+ ## 第3章 特別休暇
51
+
52
+ ### 第7条(特別休暇の種類)
53
+ 会社は、以下の特別休暇を有給で付与する。
54
+
55
+ 1. 結婚休暇:本人が結婚する場合、5日
56
+ 2. 忌引休暇:
57
+ - 配偶者:10日
58
+ - 父母・子:7日
59
+ - 祖父母・兄弟姉妹:3日
60
+ - 配偶者の父母:3日
61
+ 3. 配偶者出産休暇:配偶者が出産する場合、3日
62
+ 4. 生理休暇:生理日の就業が著しく困難な場合、必要な期間
63
+ 5. 裁判員休暇:裁判員として裁判所に出頭する場合、必要な日数
64
+
65
+ 結婚休暇は、入籍日または挙式日から6ヶ月以内に取得すること。6ヶ月を超えた場合は、取得できないものとする。連続して取得することを原則とするが、業務の都合により分割取得を認めることがある。
66
+
67
+ ### 第8条(特別休暇の申請)
68
+ 特別休暇を取得しようとする従業員は、事前に所属長に届け出なければならない。ただし、忌引休暇等の緊急の場合は、事後速やかに届け出ることができる。
69
+
70
+ 特別休暇の申請には、必要に応じて証明書類の提出を求めることがある。結婚休暇の場合は婚姻届受理証明書または結婚式の招待状の写し、忌引休暇の場合は会葬礼状または死亡診断書の写しを提出すること。
71
+
72
+ ## 第4章 その他の休暇
73
+
74
+ ### 第9条(産前産後休暇)
75
+ 6週間(多胎妊娠の場合は14週間)以内に出産予定の女性従業員は、請求により産前休暇を取得できる。出産後8週間は産後休暇として就業させない。ただし、産後6週間を経過し、医師が認めた場合は就業できる。
76
+
77
+ 産前産後休暇中の給与は、無給とする。ただし、健康保険から出産手当金が支給される。
78
+
79
+ ### 第10条(育児休業)
80
+ 1歳未満の子を養育する従業員は、育児休業を取得することができる。詳細は育児・介護休業規程に定める。
81
+
82
+ ### 第11条(介護休暇)
83
+ 要介護状態にある家族を介護する従業員は、介護休暇を取得することができる。詳細は育児・介護休業規程に定める。
84
+
85
+ ## 第5章 休暇取得の特例
86
+
87
+ ### 第12条(申請方法の特例)
88
+ 第4条の規定にかかわらず、業務の性質上オンラインシステムを利用することが困難な者については、紙の届出書を使用することができる。届出書は、所属長を経由して人事部に提出する。
89
+
90
+ 第2条の2に定める短期雇用者については、店舗備え付けの届出書に記入し、1週間前までに店舗責任者に提出する。急な休暇の場合は、店舗責任者に電話連絡の上、次回出勤時に届出を提出する。
91
+
92
+ ### 第13条(年次有給休暇付与の特例)
93
+ 第3条の規定にかかわらず、週の所定労働日数が4日以下または年間所定労働日数が216日以下の従業員については、所定労働日数に応じて比例付与する。
94
+
95
+ 比例付与の日数は、法令に定める基準による。
96
+
97
+ 第2条の2に定める者については、6ヶ月間継続勤務し、全労働日の8割以上出勤した場合に、週の所定労働日数に応じて比例付与する。
98
+
99
+ ### 第14条(取得単位の特例)
100
+ 第4条の規定にかかわらず、比例付与の対象となる従業員については、半日単位および時間単位での取得を認めないことがある。
101
+
102
+ 第2条の2に定める者については、半日単位および時間単位の取得は認めない。
103
+
104
+ ### 第15条(特別休暇の特例)
105
+ 第7条の規定にかかわらず、以下の場合は特別休暇の付与を制限する。
106
+
107
+ 1. 入社後6ヶ月未満の従業員の結婚休暇は、3日を上限とする。
108
+ 2. 試用期間中の従業員の忌引休暇は、配偶者・父母・子に限る。
109
+
110
+ 第2条の2に定める者については、結婚休暇および配偶者出産休暇は付与しない。忌引休暇は、配偶者・父母・子の場合のみ付与し、日数は正社員の半分(端数切り上げ)とする。
111
+
112
+ ## 附則
113
+
114
+ ### 附則第1条
115
+ 本規程は、2024年4月1日から施行する。
116
+
117
+ ### 附則第2条(経過措置)
118
+ 本規程施行前に付与された年次有給休暇については、従前の例による。
119
+
120
+ ### 附則第3条(週所定労働日数4日以上の非正規従業員)
121
+ 週の所定労働日数が4日以上のパートタイム従業員の年次有給休暇については、以下の付与日数を適用する。
122
+
123
+ | 勤続年数 | 付与日数 |
124
+ |---------|---------|
125
+ | 6ヶ月 | 7日 |
126
+ | 1年6ヶ月 | 8日 |
127
+ | 2年6ヶ月 | 9日 |
128
+ | 3年6ヶ月以上 | 10日 |
129
+
130
+ 届出は紙の申請書を使用し、店舗責任者に提出する。
app/data/regulations/服務規程.md ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 服務規程
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、従業員が遵守すべき服務に関する事項を定め、職場秩序の維持と業務の円滑な遂行を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するすべての従業員に適用する。
10
+
11
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
12
+
13
+ ### 第2条の2(短期雇用者の定義)
14
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
15
+
16
+ ## 第2章 勤務
17
+
18
+ ### 第3条(出勤・退勤)
19
+ 従業員は、所定の始業時刻までに出勤し、終業時刻まで職務に専念しなければならない。
20
+
21
+ 出勤および退勤の際は、所定の方法により出退勤の記録を行うこと。
22
+
23
+ 出退勤は勤怠管理システムにより打刻する。打刻忘れの場合は、当日中に所属長に報告し、翌営業日までにシステム上で修正申請を行うこと。修正申請は月3回までとし、それを超える場合は人事部の承認を要する。
24
+
25
+ ### 第4条(遅刻・早退・欠勤)
26
+ 従業員は、遅刻、早退または欠勤をする場合は、事前に所属長に届け出なければならない。やむを得ない事情により事前に届け出ができない場合は、速やかに届け出ること。
27
+
28
+ 遅刻は、始業時刻から30分以内の到着を目安とする。30分を超える遅刻が月2回以上続いた場合は、人事面談の対象となることがある。
29
+
30
+ ### 第5条(時間外勤務・休日勤務)
31
+ 会社は、業務上必要がある場合、従業員に時間外勤務または休日勤務を命じることがある。
32
+
33
+ 時間外勤務および休日勤務を行う場合は、事前に所属長の承認を得なければならない。承認は、勤怠管理システム上で行う。急を要する場合は、事後速やかに申請すること。
34
+
35
+ 時間外勤務の上限は、原則として月45時間、年360時間とする。
36
+
37
+ ### 第6条(出張)
38
+ 会社は、業務上必要がある場合、従業員に出張を命じることがある。出張を命じられた従業員は、正当な理由なくこれを拒否することはできない。
39
+
40
+ 出張の手続については、経費精算規程に定める。
41
+
42
+ ## 第3章 服務心得
43
+
44
+ ### 第7条(服務の基本)
45
+ 従業員は、会社の方針を理解し、職場の秩序を維持するとともに、誠実に職務を遂行しなければならない。
46
+
47
+ ### 第8条(禁止事項)
48
+ 従業員は、以下の行為をしてはならない。
49
+
50
+ 1. 会社の許可なく、会社の施設、物品を私的に使用すること
51
+ 2. 会社の許可なく、在職中に他の会社の業務に従事すること
52
+ 3. 会社の秘密情報を外部に漏らすこと
53
+ 4. ハラスメントに該当する行為を行うこと
54
+ 5. 会社の信用を損なう行為を行うこと
55
+ 6. その他、職場の秩序を乱す行為を行うこと
56
+
57
+ ### 第9条(SNS等の利用)
58
+ 従業員は、SNS等において、会社や業務に関する情報を発信する場合は、以下の事項を遵守すること。
59
+
60
+ 1. 会社の秘密情報を投稿しないこと
61
+ 2. 会社や取引先の信用を損なう投稿をしないこと
62
+ 3. 個人的な見解であることを明示すること
63
+
64
+ SNSへの投稿により会社に損害を与えた場合は、懲戒処分の対象となることがある。
65
+
66
+ ### 第10条(身だしなみ)
67
+ 従業員は、業務に適した清潔な身だしなみを保つこと。
68
+
69
+ 制服が定められている場合は、制服を着用すること。制服の貸与・返却については、総務部の指示に従うこと。
70
+
71
+ ## 第4章 安全衛生
72
+
73
+ ### 第11条(安全衛生の遵守)
74
+ 従業員は、安全衛生に関する法令および会社の規則を遵守し、災害の防止と健康の保持増進に努めなければならない。
75
+
76
+ ### 第12条(健康診断)
77
+ 従業員は、会社が実施する健康診断を受診しなければならない。健康診断の結果に基づき、会社が就業上の措置を講じた場合は、これに従うこと。
78
+
79
+ 健康診断は、年1回、会社指定の医療機関で実施する。希望者はオプション検査(費用は自己負担)を受けることができる。予約は、人事部が一括して行う。健康診断の日程は、年度当初に各部署に通知する。
80
+
81
+ ### 第13条(感染症対策)
82
+ 従業員は、感染症の予防に努めること。感染症に罹患した場合、または罹患した疑いがある場合は、速やかに所属長に報告し、会社の指示に従うこと。
83
+
84
+ 感染症により出勤停止となった場合、出勤停止期間中の給与については、就業規則に定める。
85
+
86
+ ## 第5章 ハラスメント防止
87
+
88
+ ### 第14条(ハラスメントの禁止)
89
+ 従業員は、パワーハラスメント、セクシャルハラスメント、マタニティハラスメント等のハラスメント行為を行ってはならない。
90
+
91
+ ### 第15条(相談窓口)
92
+ 会社は、ハラスメントに関する相談窓口を設置する。ハラスメントを受けた場合、または目撃した場合は、相談窓口に相談すること。
93
+
94
+ 相談者のプライバシーは厳守し、相談したことを理由に不利益な取扱いは行わない。相談窓口への連絡は、社内イントラネットまたは専用メールアドレスから行う。
95
+
96
+ ## 第6章 服務の特例
97
+
98
+ ### 第16条(出退勤記録の特例)
99
+ 第3条の規定にかかわらず、業務の性質上勤怠管理システムを利用することが困難な者については、タイムカードまたは店舗端末により出退勤の記録を行う。
100
+
101
+ 週の所定労働日数が3日以下のパートタイム従業員については、タイムカードまたは店舗端末により出退勤の記録を行う。
102
+
103
+ 第2条の2に定める者については、タイムカードまたは店舗端末により出退勤の記録を行う。勤怠管理システムへのアクセス権限は付与されない。
104
+
105
+ ### 第17条(時間外勤務の特例)
106
+ 第5条の規定にかかわらず、以下の者には時間外勤務を命じないことを原則とする。
107
+
108
+ 1. 妊娠中の従業員
109
+ 2. 育児短時間勤務中の従業員
110
+ 3. 介護短時間勤務中の従業員
111
+
112
+ 週の所定労働日数が3日以下のパートタイム従業員については、時間外勤務は原則として命じない。業務上やむを得ない場合は、本人の同意を得て行う。
113
+
114
+ 第2条の2に定める者については、時間外勤務および休日勤務は原則として命じない。
115
+
116
+ ### 第18条(出張の特例)
117
+ 第6条の規定に関し、第2条の2に定める者には出張を命じない。
118
+
119
+ ### 第19条(健康診断の特例)
120
+ 第12条の規定にかかわらず、以下の者は健康診断の対象外とする。
121
+
122
+ 1. 入社後6ヶ月未満の者(入社時健康診断の実施から6ヶ月未満の場合)
123
+ 2. 週の所定労働時間が20時間未満の者
124
+
125
+ 週の所定労働時間が20時間以上のパートタイム従業員については、年1回の健康診断を実施する。オプション検査は受けられない。
126
+
127
+ 第2条の2に定める者については、週20時間以上かつ1年以上継続勤務が見込まれる者を対象とする。実施は年1回、会社指定日に店舗単位で実施し、個別の日程調整は行わない。
128
+
129
+ ### 第20条(副業の特例)
130
+ 第8条第2号の規定に関し、会社の許可を得て副業を行うことができる。副業の許可申請は、人事部に「副業届」を提出する。
131
+
132
+ 週の所定労働日数が3日以下のパートタイム従業員については、会社への届出なしに副業を行うことができる。ただし、競業他社での勤務は禁止する。
133
+
134
+ 第2条の2に定める者については、会社への届出なしに副業を行うことができる。ただし、競業他社での勤務は禁止する。
135
+
136
+ ### 第21条(届出方法の特例)
137
+ 第4条の規定に関し、遅刻・早退・欠勤の届出について、業務の性質上勤怠管理システムを利用することが困難な者については、店舗備え付けの用紙を使用し、店舗責任者に提出する。
138
+
139
+ 第2条の2に定める者については、原則として電話で店舗責任者に連絡し、次回出勤時に「勤怠届(アルバイト用)」を提出する。無断欠勤が3回以上続いた場合は、解雇事由となることがある。
140
+
141
+ ## 附則
142
+
143
+ ### 附則第1条
144
+ 本規程は、2024年4月1日から施行する。
145
+
146
+ ### 附則第2条(経過措置)
147
+ 本規程施行前に届け出た副業については、届出内容に変更が生じるまで有効とする。
app/data/regulations/福利厚生規程.md ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 福利厚生規程
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、従業員の福利厚生に関する事項を定め、従業員の生活の安定と向上を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するすべての従業員に適用する。ただし、一部の制度については、雇用形態により適用が異なる場合がある。
10
+
11
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
12
+
13
+ ### 第2条の2(短期雇用者の定義)
14
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
15
+
16
+ ## 第2章 社会保険
17
+
18
+ ### 第3条(社会保険の加入)
19
+ 従業員は、法令に基づき、健康保険、厚生年金保険、雇用保険および労災保険に加入する。
20
+
21
+ ### 第4条(保険料の負担)
22
+ 健康保険および厚生年金保険の保険料は、法令に定める割合に基づき、会社と従業員が折半して負担する。
23
+
24
+ ## 第3章 慶弔見舞金
25
+
26
+ ### 第5条(慶弔見舞金の種類)
27
+ 会社は、従業員に対し、以下の慶弔見舞金を支給する。
28
+
29
+ 1. 結婚祝金:従業員本人が結婚した場合
30
+ 2. 出産祝金:従業員または配偶者が出産した場合
31
+ 3. 弔慰金:従業員の親族が死亡した場合
32
+ 4. 傷病見舞金:従業員が業務外の傷病により一定期間休業した場合
33
+ 5. 災害見舞金:従業員が自然災害等により被害を受けた場合
34
+
35
+ ### 第6条(支給額)
36
+ 慶弔見舞金の支給額は、以下のとおりとする。
37
+
38
+ | 種類 | 支給額 |
39
+ |-----|-------|
40
+ | 結婚祝金 | 30,000円 |
41
+ | 出産祝金 | 10,000円(子1人につき) |
42
+ | 弔慰金(配偶者・父母・子) | 50,000円 |
43
+ | 弔慰金(祖父母・兄弟姉妹) | 20,000円 |
44
+ | 弔慰金(配偶者の父母) | 20,000円 |
45
+ | 傷病見舞金 | 10,000円 |
46
+ | 災害見舞金 | 被害状況に応じて決定 |
47
+
48
+ ### 第7条(申請手続)
49
+ 慶弔見舞金の支給を受けようとする従業員は、所定の申請書に必要書類を添えて、人事部に提出すること。
50
+
51
+ 申請はオンラインシステム「HR Portal」から行う。証明書類はスキャンしてアップロードするか、人事部に原本を郵送する。申請は事由発生日から1ヶ月以内に行うこと。1ヶ月を超えた場合は、支給を認めないことがある。
52
+
53
+ ## 第4章 社員持株会
54
+
55
+ ### 第8条(社員持株会)
56
+ 会社は、従業員の財産形成を支援するため、社員持株会制度を設ける。
57
+
58
+ ### 第9条(加入資格)
59
+ 社員持株会に加入できる者は、入社1年以上の正社員とする。
60
+
61
+ ### 第10条(奨励金)
62
+ 会社は、持株会を通じて自社株式を購入する従業員に対し、拠出金額の10%を奨励金として支給する。
63
+
64
+ ## 第5章 その他の福利厚生
65
+
66
+ ### 第11条(社員食堂)
67
+ 会社は、本社および一部の事業所に社員食堂を設置する。利用料金は、会社が一部を補助する。
68
+
69
+ 食堂の利用時間は、11:30から13:30までとする。混雑緩和のため、部署ごとに利用時間帯を指定することがある。
70
+
71
+ ### 第12条(レクリエーション)
72
+ 会社は、従業員の親睦を深めるため、レクリエーション活動を支援する。具体的な内容は、別途定める。
73
+
74
+ レクリエーション活動への参加は任意とする。参加費用は、会社が一部を補助する。
75
+
76
+ ### 第13条(資格取得支援)
77
+ 会社は、業務に関連する資格の取得を奨励し、以下の支援を行う。
78
+
79
+ 1. 受験料の補助:会社が指定する資格について、受験料の全額または一部を補助する。
80
+ 2. 合格祝金:会社が指定する資格に合格した場合、合格祝金を支給する。
81
+ 3. 資格手当:特定の資格を保有する従業員に対し、資格手当を支給する。
82
+
83
+ 資格取得支援申請は、オンラインシステム「HR Portal」から行う。申請時に、資格名、受験予定日、業務との関連性を記載する。
84
+
85
+ ### 第14条(社員旅行)
86
+ 会社は、2年に1回、社員旅行を実施する。参加は任意とし、参加費用の一部を会社が負担する。
87
+
88
+ 社員旅行は、国内を原則とする。行先は、従業員アンケートの結果を参考に決定する。
89
+
90
+ ### 第15条(健康増進支援)
91
+ 会社は、従業員の健康増進を支援するため、以下の制度を設ける。
92
+
93
+ 1. スポーツジム利用補助:月額3,000円を上限として、スポーツジム利用料を補助する。
94
+ 2. 人間ドック補助:35歳以上の従業員を対象に、人間ドック費用の一部を補助する。
95
+ 3. インフルエンザ予防接種:費用を会社が全額負担する。
96
+
97
+ ### 第16条(育児・介護支援)
98
+ 会社は、従業員の育児および介護を支援するため、以下の制度を設ける。
99
+
100
+ 1. 保育所利用補助:認可外保育所を利用する場合、月額20,000円を上限として補助する。
101
+ 2. 介護サービス利用補助:介護サービスを利用する場合、月額10,000円を上限として補助する。
102
+ 3. ベビーシッター利用補助:ベビーシッターを利用する場合、1回あたり2,000円を補助する(月4回まで)。
103
+
104
+ ## 第6章 福利厚生の特例
105
+
106
+ ### 第17条(社会保険の特例)
107
+ 第3条の規定に関し、法令に基づき加入要件を満たさない者は、社会保険に加入しない。
108
+
109
+ ### 第18条(慶弔見舞金の特例)
110
+ 第5条および第6条の規定にかかわらず、以下の場合は慶弔見舞金の支給を制限する。
111
+
112
+ 1. 入社後6ヶ月未満の従業員には、災害見舞金のみを支給する。
113
+ 2. 試用期間中の従業員には、弔慰金(配偶者・父母・子に限る)および災害見舞金のみを支給する。
114
+
115
+ 週の所定労働日数が3日以下のパートタイム従業員については、結婚祝金、出産祝金および弔慰金(配偶者・父母・子に限る)を正社員の半額で支給する。
116
+
117
+ 第2条の2に定める者については、以下のみ支給する。
118
+ - 結婚祝金:10,000円(勤続1年以上の者に限る)
119
+ - 弔慰金:配偶者・父母・子の死亡時に10,000円
120
+
121
+ ### 第19条(申請方法の特例)
122
+ 第7条の規定にかかわらず、業務の性質上オンラインシステムを利用することが困難な者については、紙の申請書を使用することができる。
123
+
124
+ 週の所定労働日数が3日以下のパートタイム従業員については、紙の申請書を使用し、店舗責任者を経由して人事部に提出する。
125
+
126
+ 第2条の2に定める者については、店舗責任者に口頭で申し出る。店舗責任者が「慶弔見舞金申請書(簡易版)」を作成し、人事部に提出する。支給は、申請から2週間以内に現金で行う。
127
+
128
+ ### 第20条(持株会・レクリエーションの特例)
129
+ 第8条から第10条までの規定に関し、社員持株会には正社員以外は加入できない。
130
+
131
+ 第12条の規定に関し、レクリエーション活動には正社員および契約社員のみが参加できる。
132
+
133
+ 第2条の2に定める者については、社員持株会、社員旅行、レクリエーション活動には参加できない。
134
+
135
+ ### 第21条(資格取得支援の特例)
136
+ 第13条の規定にかかわらず、以下の場合は資格取得支援を制限する。
137
+
138
+ 1. 契約社員は、受験料の補助のみ対象とし、上限は年間30,000円とする。
139
+
140
+ 週の所定労働日数が3日以下のパートタイム従業員については、受験料補助のみ対象とし、上限は年間20,000円とする。
141
+
142
+ 第2条の2に定める者については、資格取得支援は対象外とする。
143
+
144
+ ### 第22条(健康増進支援の特例)
145
+ 第15条の規定にかかわらず、以下の場合は健康増進支援を制限する。
146
+
147
+ 1. 入社1年未満の従業員は、スポーツジム利用補助の対象外とする。
148
+ 2. 35歳未満の従業員は、人間ドック補助の対象外とする。
149
+
150
+ 週の所定労働日数が3日以下のパートタイム従業員については、インフルエンザ予防接種のみ対象とする。
151
+
152
+ 第2条の2に定める者については、健康増進支援は対象外とする。
153
+
154
+ ### 第23条(育児・介護支援の特例)
155
+ 第16条の規定に関し、入社1年未満の従業員は、育児・介護支援の対象外とする。
156
+
157
+ 週の所定労働日数が3日以下のパートタイム従業員および第2条の2に定める者については、育児・介護支援は対象外とする。
158
+
159
+ ### 第24条(社員食堂の特例)
160
+ 第11条の規定に関し、第2条の2に定める者は、勤務日に限り社員食堂を利用できる。ただし、会社補助はなく、全額自己負担とする。
161
+
162
+ ## 附則
163
+
164
+ ### 附則第1条
165
+ 本規程は、2024年4月1日から施行する。
166
+
167
+ ### 附則第2条(経過措置)
168
+ 本規程施行前に申請された慶弔見舞金については、従前の例による。
app/data/regulations/経費精算規程.md ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 経費精算規程
2
+
3
+ ## 第1章 総則
4
+
5
+ ### 第1条(目的)
6
+ 本規程は、従業員が業務遂行上必要とした経費の精算に関する事項を定め、経費処理の適正化と効率化を図ることを目的とする。
7
+
8
+ ### 第2条(適用範囲)
9
+ 本規程は、当社に勤務するすべての従業員に適用する。
10
+
11
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
12
+
13
+ ### 第2条の2(短期雇用者の定義)
14
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
15
+
16
+ ### 第3条(定義)
17
+ 本規程において「経費」とは、業務遂行上必要な費用であって、会社が負担すべきものをいう。
18
+
19
+ ## 第2章 経費の種類
20
+
21
+ ### 第4条(精算対象経費)
22
+ 精算対象となる経費は、以下のとおりとする。
23
+
24
+ 1. 交通費:業務上の移動に要した電車、バス、タクシー等の費用
25
+ 2. 出張旅費:出張に伴う交通費、宿泊費、日当
26
+ 3. 接待交際費:取引先との会食、贈答品等の費用
27
+ 4. 会議費:社内外の会議に伴う飲食費、会場費
28
+ 5. 消耗品費:業務上必要な文具、備品等の購入費
29
+ 6. 通信費:業務上必要な郵送料、電話料金
30
+ 7. その他:上記以外で業務上必要と認められた費用
31
+
32
+ ### 第5条(精算対象外経費)
33
+ 以下の費用は、原則として精算対象外とする。
34
+
35
+ 1. 私的な目的に使用した費用
36
+ 2. 会社の事前承認なく支出した高額経費
37
+ 3. 領収書のない経費(交通系ICカード利用を除く)
38
+ 4. 会社の経費精算基準を超える費用
39
+
40
+ ## 第3章 精算手続
41
+
42
+ ### 第6条(精算申請)
43
+ 従業員は、経費を支出した場合、原則として支出日から1ヶ月以内に精算申請を行わなければならない。
44
+
45
+ 精算申請は、所定の「経費精算書」に必要事項を記入し、領収書を添付して提出する。領収書は、支払先、日付、金額、品目が明記されているものに限る。
46
+
47
+ 経費精算はオンラインシステム「経費精算システム」から申請する。領収書は、原本をスキャンまたは撮影してシステムにアップロードし、原本は各部署で3ヶ月間保管した後、経理部に送付する。電子帳簿保存法に対応するため、撮影は鮮明に行い、金額が判読できるようにすること。
48
+
49
+ ### 第7条(承認)
50
+ 経費精算は、以下の承認フローに従う。
51
+
52
+ 1. 5万円未満:所属長の承認
53
+ 2. 5万円以上20万円未満:所属長および部門長の承認
54
+ 3. 20万円以上:所属長、部門長および経理部長の承認
55
+
56
+ 承認者が不在の場合は、代理承認者が承認を行う。代理承認者は、各部門であらかじめ指定しておくこと。
57
+
58
+ ### 第8条(支払)
59
+ 承認された経費は、毎月15日締め、当月25日に給与振込口座に振り込む。15日以降に承認された分は、翌月25日の支払となる。
60
+
61
+ ## 第4章 出張旅費
62
+
63
+ ### 第9条(出張の定義)
64
+ 本規程において「出張」とは、業務のため勤務地を離れ、宿泊を伴うまたは片道100km以上の移動を伴う勤務をいう。
65
+
66
+ ### 第10条(出張の手続)
67
+ 出張を行う場合は、事前に「出張申請書」を提出し、所属長の承認を得なければならない。
68
+
69
+ 出張申請は、原則として出張開始の3営業日前までに行うこと。急な出張の場合は、事後速やかに申請すること。
70
+
71
+ ### 第11条(交通費)
72
+ 出張の交通費は、以下の基準により支給する。
73
+
74
+ 1. 鉄道:普通車指定席。ただし、片道100km以上の場合は新幹線普通車指定席を利用できる。
75
+ 2. 航空機:エコノミークラス。ただし、部長以上はビジネスクラスを利用できる。
76
+ 3. タクシー:原則として深夜・早朝または交通機関がない場合に限り利用できる。
77
+
78
+ ### 第12条(宿泊費)
79
+ 宿泊費は、実費精算とし、上限は以下のとおりとする。
80
+
81
+ | 地域 | 上限額(1泊) |
82
+ |-----|-------------|
83
+ | 東京23区、大阪市内 | 12,000円 |
84
+ | その他の都市 | 10,000円 |
85
+ | 海外 | 別途定める |
86
+
87
+ 上限を超える場合は、事前に経理部長の承認を得ること。
88
+
89
+ ### 第13条(日当)
90
+ 出張時の日当は、以下のとおり支給する。
91
+
92
+ | 区分 | 日当(日帰り) | 日当(宿泊) |
93
+ |-----|--------------|-------------|
94
+ | 一般社員 | 1,000円 | 2,000円 |
95
+ | 課長以上 | 1,500円 | 3,000円 |
96
+
97
+ ## 第5章 接待交際費
98
+
99
+ ### 第14条(接待交際費の基準)
100
+ 接待交際費は、1人あたり以下の金額を目安とする。
101
+
102
+ - 会食:10,000円��内
103
+ - 贈答品:5,000円以内
104
+
105
+ 上記を超える場合は、事前に部門長の承認を得ること。
106
+
107
+ ### 第15条(接待交際費の申請)
108
+ 接待交際費の精算には、以下の情報を記載すること。
109
+
110
+ 1. 日時・場所
111
+ 2. 相手先の会社名・氏名
112
+ 3. 参加者(自社)
113
+ 4. 目的・内容
114
+
115
+ ## 第6章 精算の特例
116
+
117
+ ### 第16条(申請方法の特例)
118
+ 第6条の規定にかかわらず、業務の性質上オンラインシステムを利用することが困難な者については、紙の経費精算書を使用することができる。精算書は、所属長を経由して経理部に提出する。
119
+
120
+ 週の所定労働日数が3日以下のパートタイム従業員については、紙の「経費精算書(パートタイム用)」を使用し、店舗責任者を経由して経理部に提出する。精算は月1回、翌月15日に現金で支給する。
121
+
122
+ ### 第17条(精算対象経費の特例)
123
+ 第4条の規定にかかわらず、以下の場合は精算対象経費を制限する。
124
+
125
+ 1. 試用期間中の従業員は、接待交際費の精算を認めない。
126
+ 2. 入社1年未満の従業員は、贈答品の購入に事前承認を要する。
127
+
128
+ 週の所定労働日数が3日以下のパートタイム従業員については、精算対象経費は交通費および消耗品費に限る。接待交際費は、原則として認めない。
129
+
130
+ 第2条の2に定める者については、精算対象経費は業務上必要な交通費(通勤手当を除く)のみとする。消耗品費、接待交際費、出張旅費は認めない。業務上必要な物品は、正社員が購入し精算すること。
131
+
132
+ ### 第18条(精算上限の特例)
133
+ 第4条に定める交通費について、第2条の2に定める者は1回あたり5,000円を上限とする。
134
+
135
+ ### 第19条(申請期限の特例)
136
+ 第6条の規定にかかわらず、以下の者は申請期限を異なるものとする。
137
+
138
+ 1. 長期出張者(1ヶ月以上の出張):出張終了後2週間以内
139
+ 2. 海外出張者:帰国後1ヶ月以内
140
+
141
+ 第2条の2に定める者については、支出日から2週間以内に申請すること。2週間を超えた場合は、精算を認めないことがある。
142
+
143
+ ### 第20条(精算方法の特例)
144
+ 第6条の規定にかかわらず、第2条の2に定める者については、店舗備え付けの「交通費精算書(アルバイト用)」に記入し、領収書または交通系ICカードの利用履歴を添付して店舗責任者に提出する。精算は、給与支給日に給与とともに支給する。
145
+
146
+ ## 附則
147
+
148
+ ### 附則第1条
149
+ 本規程は、2024年4月1日から施行する。
150
+
151
+ ### 附則第2条(経過措置)
152
+ 本規程施行前に発生した経費については、従前の例による。
153
+
154
+ ### 附則第3条(出張の特例)
155
+ 週の所定労働日数が4日以上のパートタイム従業員について、出張を命じる場合は、正社員の基準を準用する。週3日以下のパートタイム従業員および第2条の2に定める者については、出張は原則として命じない。やむを得ず出張を命じる場合は、正社員の基準を準用する。
app/data/regulations/通勤手当規程.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 通勤手当規程
2
+
3
+ ## 第1条(目的)
4
+ 本規程は、従業員の通勤に要する費用の一部を会社が負担することにより、従業員の経済的負担を軽減し、もって従業員の福利厚生の向上を図ることを目的とする。
5
+
6
+ ## 第2条(適用範囲)
7
+ 本規程は、当社に勤務するすべての従業員に適用する。ただし、役員および出向者については別途定める。
8
+
9
+ 本規程において「従業員」とは、正社員、契約社員、パートタイム従業員、および第2条の2に定める短期雇用者を含む。
10
+
11
+ ### 第2条の2(短期雇用者の定義)
12
+ 本規程において「短期雇用者」とは、雇用期間が1年未満の者、または週の所定労働時間が30時間未満の者をいう。具体的な該当者については、人事部が個別に判断する。
13
+
14
+ ## 第3条(通勤手当の支給)
15
+ 会社は、従業員に対し、通勤に要する実費相当額を通勤手当として支給する。通勤手当の支給額は、最も経済的かつ合理的な経路および方法により算出した金額とする。
16
+
17
+ 通勤手当の上限額は月額50,000円とする。この上限額を超える場合は、上限額までの支給とする。
18
+
19
+ ## 第4条(申請方法)
20
+ 従業員は、入社時および通勤経路の変更時に、所定の「通勤届」を人事部に提出しなければならない。通勤届には、住所、通勤経路、利用交通機関、および所要時間を記載する。
21
+
22
+ 申請は、毎月15日までに提出されたものを翌月分から適用する。届出を怠った場合、通勤手当の支給が遅延することがある。届出内容に虚偽があった場合は、第10条に基づき過大受給額の返還を求めるとともに、懲戒処分の対象となることがある。
23
+
24
+ 申請は、オンラインシステム「HR Portal」から行う。申請後、上長の承認を経て人事部で処理される。処理完了まで通常5営業日を要する。申請が集中する4月および10月は、処理に7営業日程度を要することがある。
25
+
26
+ ## 第5条(定期券の購入)
27
+ 電車またはバスを利用する従業員には、原則として6ヶ月定期券の購入費用を支給する。ただし、勤務期間が6ヶ月未満の見込みの場合は、1ヶ月または3ヶ月定期券の購入費用を支給する。
28
+
29
+ 定期券は、会社指定の方法により購入し、購入後に領収書を提出すること。領収書の提出がない場合は、通勤手当の支給を保留することがある。紛失した場合は、交通機関発行の利用証明書をもって代えることができる。
30
+
31
+ 定期券購入時に会社負担の事務手数料が発生する場合は、月額500円を上限として会社が負担する。
32
+
33
+ ## 第6条(自動車通勤)
34
+ 自動車による通勤を希望する従業員は、事前に会社の許可を得なければならない。許可を得た場合、通勤距離に応じて以下のとおり支給する。
35
+
36
+ - 片道2km以上10km未満:月額4,200円
37
+ - 片道10km以上25km未満:月額7,100円
38
+ - 片道25km以上35km未満:月額12,900円
39
+ - 片道35km以上:月額16,100円
40
+
41
+ 自動車通勤の許可申請は、「自動車通勤許可申請書」を総務部に提出する。申請には、運転免許証の写し、自動車検査証の写し、任意保険の加入証明書を添付すること。許可の有効期間は1年間とし、更新時には再度申請が必要である。
42
+
43
+ 自動車通勤中の事故については、会社は一切の責任を負わない。従業員は、対人対物無制限の任意保険に加入していなければならない。
44
+
45
+ ## 第7条(自転車通勤)
46
+ 自転車による通勤を希望する従業員は、「自転車通勤届」を総務部に提出する。自転車通勤者には、月額2,000円の通勤手当を支給する。
47
+
48
+ 自転車通勤者は、ヘルメットの着用に努めること。また、自転車保険への加入を推奨する。
49
+
50
+ ## 第8条(支給日)
51
+ 通勤手当は、毎月の給与支給日に給与とともに支給する。
52
+
53
+ ## 第9条(届出義務)
54
+ 従業員は、住所変更、通勤経路の変更、利用交通機関の変更等があった場合は、速やかに届け出なければならない。届出を怠り、または虚偽の届出をした場合は、懲戒処分の対象となることがある。
55
+
56
+ 転居により通勤経路が変更となる場合は、転居日から14日以内に届け出ること。14日を超えて届け出た場合は、届出日から変更を適用する。
57
+
58
+ ## 第10条(返還)
59
+ 不正な届出により通勤手当を過大に受給した場合は、過大受給額を返還しなければならない。
60
+
61
+ 返還額は、不正受給が判明した月から遡って最大3年分とする。故意による不正受給の場合は、過大受給額に年14.6%の利息を付して返還を求めることがある。
62
+
63
+ ## 第11条(届出の特例)
64
+ 第4条の規定にかかわらず、業務の性質上オンラインシステムを利用することが困難な者については、紙の届出書を使用することができる。届出書は、所属長を経由して人事部に提出する。
65
+
66
+ 第2条の2に定める短期雇用者については、所定の届出書を店舗責任者に提出するものとし、届出の変更は随時受け付けるが、変更届提出月の翌月から適用となる。
67
+
68
+ ## 第12条(支給額の特例)
69
+ 第3条の規定にかかわらず、以下の場合は支給額を調整する。
70
+
71
+ 1. 休職期間中は、通勤手当を支給しない。
72
+ 2. 1ヶ月の所定労働日数の半分以上を欠勤した場合は、日割り計算により支給する。
73
+ 3. 育児短時間勤務または介護短時間勤務の場合でも、通勤経路に変更がなければ通常どおり支給する。
74
+
75
+ 第2条の2に定める者については、上限額を月額20,000円とし、定期券の支給は行わず、実際の出勤日数に基づき日割りで支給する。
76
+
77
+ ## 第13条(自動車通勤の特例)
78
+ 第6条の規定にかかわらず、公共交通機関の利用が著しく困難な地域に居住する従業員については、通勤距離にかかわらず自動車通勤を許可することがある。この場合、支給額は第6条に定める基準による。
79
+
80
+ 第2条の2に定める者については、自動車通勤は原則として認めない。特別の事情がある場合は、店舗責任者の承認を得た上で総務部に申請する。
81
+
82
+ ## 附則
83
+
84
+ ### 附則第1条
85
+ 本規程は、2024年4月1日から施行する。
86
+
87
+ ### 附則第2条(経過措置)
88
+ 本規程施行前に届け出た通勤経路については、届出内容に変更が生じるまで有効とする。
89
+
90
+ ### 附則第3条(週所定労働日数4日以上の非正規従業員)
91
+ 週の所定労働日数が4日以上のパートタイム従業員については、第3条から第10条までの規定を準用する。ただし、届出は紙の申請書を使用し、店舗責任者を経由して人事部に提出する。
92
+
93
+ 週の所定労働日数が3日以下の場合は、実際の出勤日数に応じた実費を支給する。
app/data/uploads/.gitkeep ADDED
File without changes
app/main.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RAG Backend API - FastAPI メインアプリケーション
3
+ """
4
+
5
+ import os
6
+ from contextlib import asynccontextmanager
7
+
8
+ from fastapi import FastAPI, Request
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import JSONResponse, PlainTextResponse, RedirectResponse
11
+
12
+ from app.config import get_settings
13
+ from app.routers import chat, documents, evaluation, health
14
+ from app.services.rag_service import get_rag_service
15
+ from app.services.vectorstore_service import get_vectorstore_service
16
+ from app.utils.errors import RAGException
17
+
18
+
19
+ @asynccontextmanager
20
+ async def lifespan(app: FastAPI):
21
+ """アプリケーションのライフサイクル管理"""
22
+ print("=" * 50, flush=True)
23
+ print("RAG Backend API を起動中...", flush=True)
24
+ print("=" * 50, flush=True)
25
+
26
+ # RAGサービスを初期化
27
+ try:
28
+ rag_service = get_rag_service()
29
+ rag_service.initialize()
30
+ print("初期化完了!", flush=True)
31
+
32
+ # Pre-build all collections if enabled (for faster demo switching)
33
+ if os.getenv("PREBUILD_COLLECTIONS", "false").lower() == "true":
34
+ print("=" * 50, flush=True)
35
+ print("Pre-building all vector collections...", flush=True)
36
+ vectorstore_service = get_vectorstore_service()
37
+ results = vectorstore_service.build_all_collections()
38
+ new_count = sum(1 for v in results.values() if v >= 0)
39
+ existing_count = sum(1 for v in results.values() if v < 0)
40
+ print(f"Pre-build complete: {new_count} new, {existing_count} existing", flush=True)
41
+ except Exception as e:
42
+ print(f"初期化エラー: {e}", flush=True)
43
+
44
+ # 登録されたルートを出力
45
+ print("=" * 50, flush=True)
46
+ print("登録されたルート:", flush=True)
47
+ for route in app.routes:
48
+ methods = getattr(route, "methods", None)
49
+ path = getattr(route, "path", None)
50
+ if methods and path:
51
+ print(f" {methods} {path}", flush=True)
52
+ print("=" * 50, flush=True)
53
+
54
+ yield
55
+
56
+ print("RAG Backend API を終了中...", flush=True)
57
+
58
+
59
+ # FastAPIアプリケーションを作成
60
+ app = FastAPI(
61
+ title="RAG Backend API",
62
+ description="RAGドキュメント検索デモ用バックエンドAPI",
63
+ version="1.0.0",
64
+ lifespan=lifespan,
65
+ )
66
+
67
+ # 設定を取得
68
+ settings = get_settings()
69
+
70
+ # CORSミドルウェアを設定
71
+ origins = settings.allowed_origins.copy()
72
+ if settings.frontend_url:
73
+ origins.append(settings.frontend_url)
74
+
75
+ # Vercelのプレビューデプロイ用
76
+ origins.append("https://*.vercel.app")
77
+
78
+ app.add_middleware(
79
+ CORSMiddleware,
80
+ allow_origins=origins,
81
+ allow_origin_regex=r"https://.*\.vercel\.app",
82
+ allow_credentials=True,
83
+ allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
84
+ allow_headers=["*"],
85
+ )
86
+
87
+
88
+ # グローバル例外ハンドラ
89
+ @app.exception_handler(RAGException)
90
+ async def rag_exception_handler(request: Request, exc: RAGException):
91
+ """RAG例外のハンドラ"""
92
+ return JSONResponse(
93
+ status_code=400,
94
+ content={
95
+ "error": exc.message,
96
+ "code": exc.code,
97
+ },
98
+ )
99
+
100
+
101
+ @app.exception_handler(Exception)
102
+ async def general_exception_handler(request: Request, exc: Exception):
103
+ """一般例外のハンドラ"""
104
+ print(f"予期しないエラー: {exc}", flush=True)
105
+ return JSONResponse(
106
+ status_code=500,
107
+ content={
108
+ "error": "内部エラーが発生しました。",
109
+ "code": "INTERNAL_ERROR",
110
+ },
111
+ )
112
+
113
+
114
+ # ルーターを登録
115
+ app.include_router(health.router, prefix="/api")
116
+ app.include_router(chat.router, prefix="/api")
117
+ app.include_router(documents.router, prefix="/api")
118
+ app.include_router(evaluation.router, prefix="/api")
119
+
120
+
121
+ # ルートエンドポイント
122
+ @app.get("/")
123
+ async def root():
124
+ """Redirect to API documentation"""
125
+ return RedirectResponse(url="/docs")
126
+
127
+
128
+ @app.get("/healthz")
129
+ async def healthz():
130
+ """Lightweight liveness probe for keep-alive pings"""
131
+ return PlainTextResponse("ok")
132
+
133
+
134
+ if __name__ == "__main__":
135
+ import uvicorn
136
+
137
+ uvicorn.run(
138
+ "app.main:app",
139
+ host="0.0.0.0",
140
+ port=settings.port,
141
+ reload=True,
142
+ )
app/models/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Models
app/models/schemas.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pydantic スキーマ定義
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Literal, Optional
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+ from app.config import ChunkingStrategy, DocumentSet
11
+
12
+
13
+ # Chat schemas
14
+ class MessageHistory(BaseModel):
15
+ """会話履歴のメッセージ"""
16
+
17
+ role: Literal["user", "assistant"] = Field(
18
+ ..., description="Message sender role"
19
+ )
20
+ content: str = Field(
21
+ ..., description="Message content"
22
+ )
23
+
24
+
25
+ class ChatRequest(BaseModel):
26
+ """チャットリクエスト"""
27
+
28
+ message: str = Field(
29
+ ...,
30
+ min_length=1,
31
+ max_length=2000,
32
+ description="The user's question to ask the RAG system",
33
+ json_schema_extra={"example": "契約社員の有給休暇は何日ですか?"},
34
+ )
35
+ history: list[MessageHistory] = Field(
36
+ default_factory=list,
37
+ description="Previous conversation messages for context",
38
+ )
39
+ document_set: Optional[DocumentSet] = Field(
40
+ None,
41
+ description="Document set to search: 'original' or 'optimized'",
42
+ )
43
+ strategy: Optional[ChunkingStrategy] = Field(
44
+ None,
45
+ description="Chunking strategy: 'standard', 'large', or 'parent_child'",
46
+ )
47
+ use_reranking: Optional[bool] = Field(
48
+ None,
49
+ description="Enable cross-encoder reranking for improved relevance",
50
+ )
51
+
52
+
53
+ class Source(BaseModel):
54
+ """引用元情報"""
55
+
56
+ filename: str = Field(..., description="Source document filename")
57
+ start_line: int = Field(..., description="Starting line number in document")
58
+ end_line: int = Field(..., description="Ending line number in document")
59
+ content_preview: str = Field(..., description="Preview of the matched content")
60
+
61
+
62
+ class ChatResponse(BaseModel):
63
+ """チャットレスポンス(非ストリーミング用)"""
64
+
65
+ answer: str = Field(..., description="Generated answer from the RAG system")
66
+ sources: list[Source] = Field(..., description="Source documents used for the answer")
67
+ processing_time_ms: int = Field(..., description="Processing time in milliseconds")
68
+
69
+
70
+ # Document schemas
71
+ class DocumentInfo(BaseModel):
72
+ """ドキュメント情報"""
73
+
74
+ id: str = Field(..., description="Unique document identifier")
75
+ filename: str = Field(..., description="Document filename")
76
+ type: Literal["sample", "uploaded"] = Field(..., description="Document type")
77
+ status: Literal["ready", "processing", "error"] = Field(..., description="Processing status")
78
+ line_count: int = Field(0, description="Number of lines in document")
79
+
80
+
81
+ class DocumentListResponse(BaseModel):
82
+ """ドキュメント一覧レスポンス"""
83
+
84
+ documents: list[DocumentInfo] = Field(..., description="List of documents")
85
+ total: int = Field(..., description="Total number of documents")
86
+
87
+
88
+ class DocumentUploadResponse(BaseModel):
89
+ """ドキュメントアップロードレスポンス"""
90
+
91
+ id: str = Field(..., description="Uploaded document ID")
92
+ filename: str = Field(..., description="Uploaded filename")
93
+ status: str = Field(..., description="Upload status")
94
+ message: str = Field(..., description="Status message")
95
+
96
+
97
+ class DocumentDeleteResponse(BaseModel):
98
+ """ドキュメント削除レスポンス"""
99
+
100
+ success: bool = Field(..., description="Whether deletion succeeded")
101
+ message: str = Field(..., description="Status message")
102
+
103
+
104
+ class DocumentContentResponse(BaseModel):
105
+ """ドキュメント内容レスポンス"""
106
+
107
+ id: str = Field(..., description="Document ID")
108
+ filename: str = Field(..., description="Document filename")
109
+ content: str = Field(..., description="Full document content")
110
+ line_count: int = Field(..., description="Number of lines")
111
+
112
+
113
+ class RebuildResponse(BaseModel):
114
+ """再構築レスポンス"""
115
+
116
+ status: str = Field(..., description="Rebuild status")
117
+ chunk_count: int = Field(..., description="Number of chunks created")
118
+ message: str = Field(..., description="Status message")
119
+
120
+
121
+ # Health schemas
122
+ class HealthResponse(BaseModel):
123
+ """ヘルスチェックレスポンス"""
124
+
125
+ status: str = Field(..., description="Service status", json_schema_extra={"example": "ok"})
126
+ model_loaded: bool = Field(..., description="Whether embedding model is loaded")
127
+ vectorstore_ready: bool = Field(..., description="Whether vector store is initialized")
128
+ document_count: int = Field(..., description="Number of indexed documents")
129
+
130
+
131
+ # Error schemas
132
+ class ErrorResponse(BaseModel):
133
+ """エラーレスポンス"""
134
+
135
+ error: str = Field(..., description="Error message")
136
+ code: str = Field(..., description="Error code", json_schema_extra={"example": "RATE_LIMIT_EXCEEDED"})
137
+ detail: str | None = Field(None, description="Additional error details")
app/routers/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Routers
app/routers/chat.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ チャットルーター - ストリーミング対応
3
+ """
4
+
5
+ import json
6
+
7
+ from fastapi import APIRouter, Request
8
+ from fastapi.responses import StreamingResponse
9
+
10
+ from app.models.schemas import ChatRequest, ErrorResponse
11
+ from app.services.rag_service import get_rag_service
12
+ from app.utils.rate_limiter import rate_limiter, global_rate_limiter
13
+ from app.utils.errors import RateLimitException, RAGException, ErrorMessages
14
+
15
+ router = APIRouter(prefix="/chat", tags=["chat"])
16
+
17
+
18
+ def get_client_ip(request: Request) -> str:
19
+ """クライアントIPを取得"""
20
+ forwarded = request.headers.get("X-Forwarded-For")
21
+ if forwarded:
22
+ return forwarded.split(",")[0].strip()
23
+ return request.client.host if request.client else "unknown"
24
+
25
+
26
+ @router.post(
27
+ "",
28
+ summary="Send chat message",
29
+ description="RAG-powered chat endpoint with SSE streaming. Returns sources, tokens, and completion events.",
30
+ responses={
31
+ 200: {"description": "SSE stream with tokens, sources, and completion events"},
32
+ 429: {"model": ErrorResponse, "description": "Rate limit exceeded"},
33
+ },
34
+ )
35
+ async def chat(request: Request, chat_request: ChatRequest):
36
+ """
37
+ RAGチャットエンドポイント(ストリーミング対応)
38
+
39
+ Server-Sent Events (SSE) 形式でレスポンスを返す:
40
+ - event: sources - 引用元情報
41
+ - event: token - 生成されたトークン
42
+ - event: done - 完了シグナル
43
+ - event: error - エラー
44
+ """
45
+ # グローバルレート制限チェック(APIキー全体の制限)
46
+ if not global_rate_limiter.check_limit():
47
+ async def global_rate_limit_error():
48
+ error_data = {
49
+ "type": "error",
50
+ "data": {
51
+ "message": ErrorMessages.RATE_LIMIT_EXCEEDED,
52
+ "code": "GLOBAL_RATE_LIMIT_EXCEEDED",
53
+ },
54
+ }
55
+ yield f"event: error\ndata: {json.dumps(error_data, ensure_ascii=False)}\n\n"
56
+
57
+ return StreamingResponse(
58
+ global_rate_limit_error(),
59
+ media_type="text/event-stream",
60
+ headers={
61
+ "Cache-Control": "no-cache",
62
+ "Connection": "keep-alive",
63
+ "X-Accel-Buffering": "no",
64
+ },
65
+ )
66
+
67
+ # Per-IPレート制限チェック(単一ユーザーの乱用防止)
68
+ client_ip = get_client_ip(request)
69
+ if not rate_limiter.check_limit(client_ip):
70
+ async def rate_limit_error():
71
+ error_data = {
72
+ "type": "error",
73
+ "data": {
74
+ "message": ErrorMessages.RATE_LIMIT_EXCEEDED,
75
+ "code": "RATE_LIMIT_EXCEEDED",
76
+ },
77
+ }
78
+ yield f"event: error\ndata: {json.dumps(error_data, ensure_ascii=False)}\n\n"
79
+
80
+ return StreamingResponse(
81
+ rate_limit_error(),
82
+ media_type="text/event-stream",
83
+ headers={
84
+ "Cache-Control": "no-cache",
85
+ "Connection": "keep-alive",
86
+ "X-Accel-Buffering": "no",
87
+ },
88
+ )
89
+
90
+ async def generate():
91
+ try:
92
+ rag_service = get_rag_service()
93
+
94
+ # 会話履歴を辞書形式に変換
95
+ history = [
96
+ {"role": msg.role, "content": msg.content}
97
+ for msg in chat_request.history
98
+ ]
99
+
100
+ async for event in rag_service.stream_query(
101
+ chat_request.message,
102
+ history,
103
+ document_set=chat_request.document_set,
104
+ strategy=chat_request.strategy,
105
+ use_reranking=chat_request.use_reranking,
106
+ ):
107
+ event_type = event.get("type", "token")
108
+ event_data = json.dumps(event.get("data", {}), ensure_ascii=False)
109
+ yield f"event: {event_type}\ndata: {event_data}\n\n"
110
+
111
+ except RAGException as e:
112
+ error_data = {"message": e.message, "code": e.code}
113
+ yield f"event: error\ndata: {json.dumps(error_data, ensure_ascii=False)}\n\n"
114
+ except Exception as e:
115
+ error_data = {"message": ErrorMessages.INTERNAL_ERROR, "code": "INTERNAL_ERROR"}
116
+ yield f"event: error\ndata: {json.dumps(error_data, ensure_ascii=False)}\n\n"
117
+
118
+ return StreamingResponse(
119
+ generate(),
120
+ media_type="text/event-stream",
121
+ headers={
122
+ "Cache-Control": "no-cache",
123
+ "Connection": "keep-alive",
124
+ "X-Accel-Buffering": "no",
125
+ },
126
+ )
app/routers/documents.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ドキュメント管理ルーター
3
+ """
4
+
5
+ from fastapi import APIRouter, UploadFile, File, HTTPException, Query
6
+
7
+ from typing import Literal
8
+
9
+ from app.config import DocumentSet
10
+ from app.models.schemas import (
11
+ DocumentListResponse,
12
+ DocumentInfo,
13
+ DocumentUploadResponse,
14
+ DocumentDeleteResponse,
15
+ DocumentContentResponse,
16
+ RebuildResponse,
17
+ ErrorResponse,
18
+ )
19
+ from app.services.document_service import get_document_service
20
+ from app.services.rag_service import get_rag_service
21
+ from app.services.vectorstore_service import get_vectorstore_service
22
+ from app.utils.errors import DocumentException, RAGException, ErrorMessages
23
+
24
+ router = APIRouter(prefix="/documents", tags=["documents"])
25
+
26
+
27
+ @router.get("/debug/routes", include_in_schema=False)
28
+ async def debug_routes():
29
+ """デバッグ用: ルート確認"""
30
+ return {
31
+ "message": "Documents router is working",
32
+ "available_routes": [
33
+ "GET /api/documents",
34
+ "GET /api/documents/debug/routes",
35
+ "GET /api/documents/debug/test-content/{doc_id}",
36
+ "GET /api/documents/{doc_id}/content",
37
+ "POST /api/documents/upload",
38
+ "DELETE /api/documents/{doc_id}",
39
+ "POST /api/documents/rebuild",
40
+ "POST /api/documents/build-all",
41
+ ]
42
+ }
43
+
44
+
45
+ @router.get("/debug/test-content/{doc_id}", include_in_schema=False)
46
+ async def debug_test_content(doc_id: str):
47
+ """デバッグ用: コンテンツエンドポイントのテスト"""
48
+ print(f"[DEBUG] debug_test_content called with doc_id: {doc_id}", flush=True)
49
+ doc_service = get_document_service()
50
+ all_docs = doc_service.list_documents()
51
+ doc_ids = [d.id for d in all_docs]
52
+ return {
53
+ "received_doc_id": doc_id,
54
+ "available_doc_ids": doc_ids,
55
+ "doc_id_found": doc_id in doc_ids,
56
+ }
57
+
58
+
59
+ @router.get("", summary="List documents", response_model=DocumentListResponse)
60
+ async def list_documents(
61
+ document_set: str = Query("original", description="Document set to list: 'original' or 'optimized'")
62
+ ) -> DocumentListResponse:
63
+ """全てのドキュメント一覧を取得"""
64
+ doc_service = get_document_service()
65
+ # Convert string to enum
66
+ try:
67
+ doc_set_enum = DocumentSet(document_set)
68
+ except ValueError:
69
+ doc_set_enum = DocumentSet.ORIGINAL
70
+ documents = doc_service.list_documents(document_set=doc_set_enum)
71
+
72
+ return DocumentListResponse(
73
+ documents=[
74
+ DocumentInfo(
75
+ id=doc.id,
76
+ filename=doc.filename,
77
+ type=doc.type if doc.type in ("sample", "uploaded") else "sample", # type: ignore[arg-type]
78
+ status=doc.status if doc.status in ("ready", "processing", "error") else "ready", # type: ignore[arg-type]
79
+ line_count=doc.line_count,
80
+ )
81
+ for doc in documents
82
+ ],
83
+ total=len(documents),
84
+ )
85
+
86
+
87
+ @router.post("/upload", summary="Upload document", response_model=DocumentUploadResponse)
88
+ async def upload_document(file: UploadFile = File(...)) -> DocumentUploadResponse:
89
+ """新しいドキュメントをアップロード"""
90
+ if not file.filename:
91
+ raise HTTPException(
92
+ status_code=400,
93
+ detail=ErrorMessages.INVALID_REQUEST,
94
+ )
95
+
96
+ try:
97
+ doc_service = get_document_service()
98
+ content = await file.read()
99
+ doc_info = await doc_service.upload_document(file.filename, content)
100
+
101
+ return DocumentUploadResponse(
102
+ id=doc_info.id,
103
+ filename=doc_info.filename,
104
+ status=doc_info.status,
105
+ message="ドキュメントをアップロードしました。ベクトルストアを再構築してください。",
106
+ )
107
+ except DocumentException as e:
108
+ raise HTTPException(status_code=400, detail=e.message)
109
+
110
+
111
+ @router.get("/{doc_id}/content", summary="Get document content", response_model=DocumentContentResponse)
112
+ async def get_document_content(doc_id: str) -> DocumentContentResponse:
113
+ """ドキュメントの内容を取得"""
114
+ print(f"[DEBUG] get_document_content called with doc_id: {doc_id}", flush=True)
115
+ try:
116
+ doc_service = get_document_service()
117
+ print(f"[DEBUG] Searching for document: {doc_id}", flush=True)
118
+ doc_info, content = doc_service.get_document_content(doc_id)
119
+ print(f"[DEBUG] Found document: {doc_info.filename}", flush=True)
120
+
121
+ return DocumentContentResponse(
122
+ id=doc_info.id,
123
+ filename=doc_info.filename,
124
+ content=content,
125
+ line_count=doc_info.line_count,
126
+ )
127
+ except DocumentException as e:
128
+ print(f"[DEBUG] DocumentException: {e.message}", flush=True)
129
+ raise HTTPException(status_code=404, detail=e.message)
130
+ except Exception as e:
131
+ print(f"[DEBUG] Unexpected error: {e}", flush=True)
132
+ raise HTTPException(status_code=500, detail=str(e))
133
+
134
+
135
+ @router.delete("/{doc_id}", summary="Delete document", response_model=DocumentDeleteResponse)
136
+ async def delete_document(doc_id: str) -> DocumentDeleteResponse:
137
+ """アップロードされたドキュメントを削除"""
138
+ try:
139
+ doc_service = get_document_service()
140
+ doc_service.delete_document(doc_id)
141
+
142
+ return DocumentDeleteResponse(
143
+ success=True,
144
+ message="ドキュメントを削除しました。ベクトルストアを再構築してください。",
145
+ )
146
+ except DocumentException as e:
147
+ raise HTTPException(status_code=404, detail=e.message)
148
+
149
+
150
+ @router.post("/rebuild", summary="Rebuild vector store", response_model=RebuildResponse)
151
+ async def rebuild_vectorstore() -> RebuildResponse:
152
+ """ベクトルストアを再構築"""
153
+ try:
154
+ rag_service = get_rag_service()
155
+ chunk_count = rag_service.rebuild_vectorstore()
156
+
157
+ return RebuildResponse(
158
+ status="completed",
159
+ chunk_count=chunk_count,
160
+ message=f"ベクトルストアを再構築しました。{chunk_count}個のチャンクを作成しました。",
161
+ )
162
+ except RAGException as e:
163
+ raise HTTPException(status_code=400, detail=e.message)
164
+
165
+
166
+ @router.get("/options", summary="Get available options")
167
+ async def get_options():
168
+ """Get available strategies, document sets, and collections for the UI"""
169
+ rag_service = get_rag_service()
170
+ return rag_service.get_available_options()
171
+
172
+
173
+ @router.post("/build-all", summary="Pre-build all collections")
174
+ async def build_all_collections():
175
+ """
176
+ Pre-build all vector collections for faster switching.
177
+ Builds all combinations of document_set × chunking_strategy.
178
+ """
179
+ try:
180
+ vectorstore_service = get_vectorstore_service()
181
+ results = vectorstore_service.build_all_collections()
182
+
183
+ # Count new vs existing
184
+ new_count = sum(1 for v in results.values() if v >= 0)
185
+ existing_count = sum(1 for v in results.values() if v < 0)
186
+
187
+ return {
188
+ "status": "completed",
189
+ "collections": results,
190
+ "summary": {
191
+ "new_collections": new_count,
192
+ "existing_collections": existing_count,
193
+ "total": len(results),
194
+ },
195
+ "message": f"Built {new_count} new collections, {existing_count} already existed.",
196
+ }
197
+ except Exception as e:
198
+ raise HTTPException(status_code=500, detail=str(e))
app/routers/evaluation.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 評価ルーター - RAG精度テスト用API
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import logging
8
+ from typing import Optional
9
+
10
+ from fastapi import APIRouter, HTTPException
11
+ from fastapi.responses import StreamingResponse
12
+ from pydantic import BaseModel
13
+
14
+ from app.config import DocumentSet, ChunkingStrategy, get_settings
15
+ from app.services.rag_service import get_rag_service
16
+
17
+
18
+ router = APIRouter(prefix="/evaluate", tags=["evaluation"])
19
+
20
+ # Set up logging for evaluation
21
+ logger = logging.getLogger(__name__)
22
+ logger.setLevel(logging.INFO)
23
+
24
+
25
+ # Load light test queries
26
+ def load_test_queries() -> list[dict]:
27
+ """Load test queries from JSON file."""
28
+ settings = get_settings()
29
+ queries_path = settings.evaluation_queries_path
30
+
31
+ if not queries_path.exists():
32
+ raise FileNotFoundError(f"Test queries file not found: {queries_path}")
33
+
34
+ with open(queries_path, "r", encoding="utf-8") as f:
35
+ data = json.load(f)
36
+ return data["queries"]
37
+
38
+
39
+ def check_answer_quality(
40
+ answer: str,
41
+ expected_contains: list[str],
42
+ must_not_contain: Optional[list[str]] = None
43
+ ) -> dict:
44
+ """
45
+ Check if answer contains expected content and doesn't contain prohibited content.
46
+
47
+ Returns:
48
+ dict with is_correct, found_terms, missing_terms, prohibited_found, explanation
49
+ """
50
+ answer_lower = answer.lower()
51
+
52
+ # Check for required content
53
+ found_terms = []
54
+ missing_terms = []
55
+ for term in expected_contains:
56
+ if term.lower() in answer_lower or term in answer:
57
+ found_terms.append(term)
58
+ else:
59
+ missing_terms.append(term)
60
+
61
+ # Check for prohibited content
62
+ prohibited_found = []
63
+ if must_not_contain:
64
+ for term in must_not_contain:
65
+ if term.lower() in answer_lower or term in answer:
66
+ prohibited_found.append(term)
67
+
68
+ # Determine correctness
69
+ has_required = len(found_terms) > 0
70
+ has_prohibited = len(prohibited_found) > 0
71
+ is_correct = has_required and not has_prohibited
72
+
73
+ # Build explanation
74
+ if is_correct:
75
+ explanation = "正しい情報が含まれています"
76
+ elif has_prohibited:
77
+ explanation = "誤った情報が含まれています(正社員のルールを回答)"
78
+ elif not has_required:
79
+ explanation = "必要な情報が見つかりません"
80
+ else:
81
+ explanation = "回答の精度に問題があります"
82
+
83
+ return {
84
+ "is_correct": is_correct,
85
+ "found_terms": found_terms,
86
+ "missing_terms": missing_terms,
87
+ "prohibited_found": prohibited_found,
88
+ "explanation": explanation,
89
+ }
90
+
91
+
92
+ # Response schemas
93
+ class QueryResult(BaseModel):
94
+ """Single query evaluation result"""
95
+ id: str
96
+ category: str
97
+ question: str
98
+ answer: str
99
+ is_correct: bool
100
+ found_terms: list[str]
101
+ missing_terms: list[str]
102
+ prohibited_found: list[str]
103
+ explanation: str
104
+
105
+
106
+ class ScoreResult(BaseModel):
107
+ """Score summary"""
108
+ correct: int
109
+ total: int
110
+ percentage: float
111
+
112
+
113
+ class DatasetResult(BaseModel):
114
+ """Results for a single dataset"""
115
+ queries: list[QueryResult]
116
+ score: ScoreResult
117
+
118
+
119
+ class EvaluationResponse(BaseModel):
120
+ """Full evaluation response"""
121
+ status: str
122
+ document_set: str
123
+ results: DatasetResult
124
+
125
+
126
+ @router.post("/quick", summary="Run quick evaluation", response_model=EvaluationResponse)
127
+ async def run_quick_evaluation(
128
+ document_set: str = "original",
129
+ strategy: str = "standard",
130
+ use_reranking: bool = False,
131
+ ) -> EvaluationResponse:
132
+ """
133
+ Run quick evaluation with 4 test queries on the specified dataset.
134
+
135
+ Args:
136
+ document_set: "original" or "optimized"
137
+ strategy: "standard", "large", or "parent_child"
138
+ use_reranking: Whether to apply cross-encoder reranking
139
+
140
+ Returns:
141
+ Evaluation results with scoring for each query
142
+ """
143
+ try:
144
+ # Validate inputs
145
+ try:
146
+ doc_set_enum = DocumentSet(document_set)
147
+ except ValueError:
148
+ raise HTTPException(status_code=400, detail=f"Invalid document_set: {document_set}")
149
+
150
+ try:
151
+ strategy_enum = ChunkingStrategy(strategy)
152
+ except ValueError:
153
+ raise HTTPException(status_code=400, detail=f"Invalid strategy: {strategy}")
154
+
155
+ # Load test queries
156
+ test_queries = load_test_queries()
157
+
158
+ # Get RAG service
159
+ rag_service = get_rag_service()
160
+
161
+ # Run evaluation
162
+ query_results = []
163
+ correct_count = 0
164
+
165
+ for query in test_queries:
166
+ logger.info(f"[Evaluation] Processing: {query['id']} - {query['question'][:30]}...")
167
+ print(f"[Evaluation] Processing: {query['id']} - {query['question'][:30]}...", flush=True)
168
+
169
+ try:
170
+ # Query RAG
171
+ result = rag_service.query(
172
+ question=query["question"],
173
+ document_set=document_set,
174
+ strategy=strategy,
175
+ use_reranking=use_reranking,
176
+ )
177
+ answer = result.get("answer", "")
178
+
179
+ # Log full answer for debugging
180
+ logger.info(f"[Evaluation] Q: {query['question']}")
181
+ logger.info(f"[Evaluation] A: {answer[:300]}...")
182
+ print(f"[Evaluation] Answer preview: {answer[:200]}...", flush=True)
183
+
184
+ # Check answer quality
185
+ scoring = check_answer_quality(
186
+ answer,
187
+ query["expected_answer_contains"],
188
+ query.get("expected_answer_must_not_contain"),
189
+ )
190
+
191
+ if scoring["is_correct"]:
192
+ correct_count += 1
193
+
194
+ query_results.append(QueryResult(
195
+ id=query["id"],
196
+ category=query["category"],
197
+ question=query["question"],
198
+ answer=answer[:500] + "..." if len(answer) > 500 else answer,
199
+ is_correct=scoring["is_correct"],
200
+ found_terms=scoring["found_terms"],
201
+ missing_terms=scoring["missing_terms"],
202
+ prohibited_found=scoring["prohibited_found"],
203
+ explanation=scoring["explanation"],
204
+ ))
205
+
206
+ status = "✓" if scoring["is_correct"] else "✗"
207
+ logger.info(f"[Evaluation] {status} {scoring['explanation']} | found={scoring['found_terms']} | prohibited={scoring['prohibited_found']}")
208
+ print(f"[Evaluation] {status} {scoring['explanation']}", flush=True)
209
+
210
+ except Exception as e:
211
+ print(f"[Evaluation] ERROR: {e}", flush=True)
212
+ query_results.append(QueryResult(
213
+ id=query["id"],
214
+ category=query["category"],
215
+ question=query["question"],
216
+ answer=f"Error: {str(e)}",
217
+ is_correct=False,
218
+ found_terms=[],
219
+ missing_terms=query["expected_answer_contains"],
220
+ prohibited_found=[],
221
+ explanation=f"エラーが発生しました: {str(e)}",
222
+ ))
223
+
224
+ # Calculate score
225
+ total = len(test_queries)
226
+ percentage = round(correct_count / total * 100, 1) if total > 0 else 0
227
+
228
+ return EvaluationResponse(
229
+ status="completed",
230
+ document_set=document_set,
231
+ results=DatasetResult(
232
+ queries=query_results,
233
+ score=ScoreResult(
234
+ correct=correct_count,
235
+ total=total,
236
+ percentage=percentage,
237
+ ),
238
+ ),
239
+ )
240
+
241
+ except FileNotFoundError as e:
242
+ raise HTTPException(status_code=500, detail=str(e))
243
+ except Exception as e:
244
+ print(f"[Evaluation] Unexpected error: {e}", flush=True)
245
+ raise HTTPException(status_code=500, detail=f"Evaluation failed: {str(e)}")
246
+
247
+
248
+ @router.get("/queries", summary="Get test queries")
249
+ async def get_test_queries():
250
+ """Get the list of test queries (for frontend to display questions)."""
251
+ try:
252
+ queries = load_test_queries()
253
+ return {
254
+ "queries": [
255
+ {
256
+ "id": q["id"],
257
+ "category": q["category"],
258
+ "question": q["question"],
259
+ }
260
+ for q in queries
261
+ ]
262
+ }
263
+ except FileNotFoundError as e:
264
+ raise HTTPException(status_code=500, detail=str(e))
265
+
266
+
267
+ @router.get("/stream", summary="Stream evaluation results")
268
+ async def stream_evaluation(
269
+ document_set: str = "original",
270
+ strategy: str = "standard",
271
+ use_reranking: bool = False,
272
+ ):
273
+ """
274
+ Stream evaluation results with token-by-token answer streaming.
275
+
276
+ SSE Event types:
277
+ - query_start: {id, category, question, index, total}
278
+ - token: {token}
279
+ - query_done: {id, answer, scoring: {is_correct, found_terms, missing_terms, prohibited_found, explanation}}
280
+ - complete: {score: {correct, total, percentage}}
281
+ - error: {message}
282
+ """
283
+
284
+ async def generate():
285
+ try:
286
+ print(f"[Eval] Starting evaluation: document_set={document_set}, strategy={strategy}, use_reranking={use_reranking}", flush=True)
287
+
288
+ # Validate inputs
289
+ try:
290
+ doc_set_enum = DocumentSet(document_set)
291
+ except ValueError:
292
+ yield f"event: error\ndata: {json.dumps({'message': f'Invalid document_set: {document_set}'})}\n\n"
293
+ return
294
+
295
+ try:
296
+ strategy_enum = ChunkingStrategy(strategy)
297
+ except ValueError:
298
+ yield f"event: error\ndata: {json.dumps({'message': f'Invalid strategy: {strategy}'})}\n\n"
299
+ return
300
+
301
+ # Load test queries
302
+ test_queries = load_test_queries()
303
+ total = len(test_queries)
304
+ print(f"[Eval] Loaded {total} test queries", flush=True)
305
+
306
+ # Get RAG service
307
+ rag_service = get_rag_service()
308
+
309
+ correct_count = 0
310
+
311
+ for index, query in enumerate(test_queries):
312
+ query_id = query["id"]
313
+ question = query["question"]
314
+
315
+ # Log query start
316
+ print(f"[Eval {index+1}/{total}] Q: {question}", flush=True)
317
+
318
+ # Send query_start event
319
+ yield f"event: query_start\ndata: {json.dumps({'id': query_id, 'category': query['category'], 'question': question, 'index': index, 'total': total})}\n\n"
320
+
321
+ try:
322
+ # Stream the answer token by token
323
+ full_answer = ""
324
+
325
+ async for event in rag_service.stream_query(
326
+ question=question,
327
+ document_set=doc_set_enum,
328
+ strategy=strategy_enum,
329
+ use_reranking=use_reranking,
330
+ ):
331
+ if event["type"] == "token":
332
+ token = event["data"]["token"]
333
+ full_answer += token
334
+ yield f"event: token\ndata: {json.dumps({'token': token})}\n\n"
335
+ elif event["type"] == "error":
336
+ yield f"event: error\ndata: {json.dumps(event['data'])}\n\n"
337
+ return
338
+ # Skip sources/chunks/done events - we only need tokens
339
+
340
+ # Log answer for debugging (print goes to HF Spaces logs)
341
+ print(f"[Eval {index+1}/{total}] A: {full_answer[:200]}...", flush=True)
342
+
343
+ # Check answer quality
344
+ scoring = check_answer_quality(
345
+ full_answer,
346
+ query["expected_answer_contains"],
347
+ query.get("expected_answer_must_not_contain"),
348
+ )
349
+
350
+ if scoring["is_correct"]:
351
+ correct_count += 1
352
+
353
+ status = "✓" if scoring["is_correct"] else "✗"
354
+ print(f"[Eval {index+1}/{total}] {status} found={scoring['found_terms']} | missing={scoring['missing_terms']} | prohibited={scoring['prohibited_found']}", flush=True)
355
+
356
+ # Send query_done event with scoring
357
+ query_done_data = {
358
+ "id": query_id,
359
+ "answer": full_answer[:500] + "..." if len(full_answer) > 500 else full_answer,
360
+ "scoring": {
361
+ "is_correct": scoring["is_correct"],
362
+ "found_terms": scoring["found_terms"],
363
+ "missing_terms": scoring["missing_terms"],
364
+ "prohibited_found": scoring["prohibited_found"],
365
+ "explanation": scoring["explanation"],
366
+ }
367
+ }
368
+ yield f"event: query_done\ndata: {json.dumps(query_done_data)}\n\n"
369
+
370
+ except Exception as e:
371
+ print(f"[Eval {index+1}/{total}] ERROR: {e}", flush=True)
372
+ # Send error for this query but continue with others
373
+ query_done_data = {
374
+ "id": query_id,
375
+ "answer": f"Error: {str(e)}",
376
+ "scoring": {
377
+ "is_correct": False,
378
+ "found_terms": [],
379
+ "missing_terms": query["expected_answer_contains"],
380
+ "prohibited_found": [],
381
+ "explanation": f"エラーが発生しました: {str(e)}",
382
+ }
383
+ }
384
+ yield f"event: query_done\ndata: {json.dumps(query_done_data)}\n\n"
385
+
386
+ # Send complete event with final score
387
+ percentage = round(correct_count / total * 100, 1) if total > 0 else 0
388
+ complete_data = {
389
+ "score": {
390
+ "correct": correct_count,
391
+ "total": total,
392
+ "percentage": percentage,
393
+ }
394
+ }
395
+ yield f"event: complete\ndata: {json.dumps(complete_data)}\n\n"
396
+
397
+ # Final summary
398
+ print(f"[Eval] Complete: {correct_count}/{total} ({percentage}%)", flush=True)
399
+
400
+ except FileNotFoundError as e:
401
+ print(f"[Eval] File not found: {e}", flush=True)
402
+ yield f"event: error\ndata: {json.dumps({'message': str(e)})}\n\n"
403
+ except Exception as e:
404
+ print(f"[Eval] Unexpected error: {e}", flush=True)
405
+ yield f"event: error\ndata: {json.dumps({'message': f'Evaluation failed: {str(e)}'})}\n\n"
406
+
407
+ return StreamingResponse(
408
+ generate(),
409
+ media_type="text/event-stream",
410
+ headers={
411
+ "Cache-Control": "no-cache",
412
+ "Connection": "keep-alive",
413
+ "X-Accel-Buffering": "no",
414
+ },
415
+ )
app/routers/health.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ヘルスチェックルーター
3
+ """
4
+
5
+ from fastapi import APIRouter
6
+
7
+ from app.models.schemas import HealthResponse
8
+ from app.services.embedding_service import get_embedding_service
9
+ from app.services.vectorstore_service import get_vectorstore_service
10
+ from app.services.document_service import get_document_service
11
+
12
+ router = APIRouter(tags=["health"])
13
+
14
+
15
+ @router.get("/health", summary="Health check", response_model=HealthResponse)
16
+ async def health_check() -> HealthResponse:
17
+ """ヘルスチェックエンドポイント"""
18
+ embedding_service = get_embedding_service()
19
+ vectorstore_service = get_vectorstore_service()
20
+ doc_service = get_document_service()
21
+
22
+ documents = doc_service.list_documents()
23
+
24
+ return HealthResponse(
25
+ status="ok",
26
+ model_loaded=embedding_service.is_ready,
27
+ vectorstore_ready=vectorstore_service.is_ready,
28
+ document_count=len(documents),
29
+ )
app/services/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Services
app/services/document_preprocessor.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ドキュメント前処理サービス
3
+
4
+ 規程文書を従業員タイプ別に分割し、曖昧さを解消する。
5
+ 例:通勤手当規程.md → 通勤手当規程_正社員.md, 通勤手当規程_パート.md, 通勤手当規程_アルバイト.md
6
+ """
7
+
8
+ import re
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ from langchain_google_genai import ChatGoogleGenerativeAI
13
+ from langchain_core.prompts import ChatPromptTemplate
14
+
15
+ from app.config import get_settings
16
+
17
+
18
+ class DocumentPreprocessor:
19
+ """規程文書を従業員タイプ別に分割するサービス"""
20
+
21
+ EMPLOYEE_TYPES = ["正社員", "パートタイム", "アルバイト"]
22
+
23
+ def __init__(self):
24
+ self.settings = get_settings()
25
+ self._llm: Optional[ChatGoogleGenerativeAI] = None
26
+
27
+ def _get_llm(self) -> ChatGoogleGenerativeAI:
28
+ """LLMインスタンスを取得"""
29
+ if self._llm is None:
30
+ self._llm = ChatGoogleGenerativeAI(
31
+ model=self.settings.llm_model,
32
+ google_api_key=self.settings.google_api_key,
33
+ temperature=0.1, # Low temperature for consistent output
34
+ )
35
+ return self._llm
36
+
37
+ def preprocess_document(self, content: str, filename: str, employee_type: str) -> str:
38
+ """
39
+ ドキュメントを特定の従業員タイプ向けに再構成する
40
+
41
+ Args:
42
+ content: 元のドキュメント内容
43
+ filename: ファイル名(コンテキスト用)
44
+ employee_type: 対象の従業員タイプ(正社員、パートタイム、アルバイト)
45
+
46
+ Returns:
47
+ 再構成されたドキュメント
48
+ """
49
+ llm = self._get_llm()
50
+
51
+ template = """あなたは企業の規程文書を再構成する専門家です。
52
+
53
+ 以下の規程文書を「{employee_type}」向けに再構成してください。
54
+
55
+ ## 再構成のルール:
56
+ 1. 一般的な条項(全従業員に適用)は維持する
57
+ 2. 附則や特則で「{employee_type}」に特有のルールがある場合は、それを本文に統合する
58
+ 3. 他の従業員タイプ(例:正社員向けの場合はアルバイト向けの特則)は削除する
59
+ 4. 元の条項番号は維持しつつ、内容を明確にする
60
+ 5. 出力はMarkdown形式で、元の構造を保つ
61
+ 6. 「{employee_type}向け」であることをタイトルに明記する
62
+
63
+ ## 元の文書:
64
+ ファイル名: {filename}
65
+
66
+ {content}
67
+
68
+ ## 出力:
69
+ 「{employee_type}」向けに再構成した規程文書を出力してください。"""
70
+
71
+ prompt = ChatPromptTemplate.from_template(template)
72
+ chain = prompt | llm
73
+
74
+ result = chain.invoke({
75
+ "content": content,
76
+ "filename": filename,
77
+ "employee_type": employee_type,
78
+ })
79
+
80
+ content_result = result.content
81
+ if isinstance(content_result, list):
82
+ # Handle case where content is a list of content blocks
83
+ return "".join(str(item) for item in content_result)
84
+ return str(content_result)
85
+
86
+ def preprocess_all_documents(
87
+ self,
88
+ source_dir: Optional[Path] = None,
89
+ output_dir: Optional[Path] = None,
90
+ ) -> dict[str, list[str]]:
91
+ """
92
+ ソースディレクトリ内の全ドキュメントを処理し、従業員タイプ別に分割
93
+
94
+ Returns:
95
+ dict: {元ファイル名: [生成されたファイル名のリスト]}
96
+ """
97
+ source_dir = source_dir or self.settings.documents_dir
98
+ output_dir = output_dir or self.settings.documents_dir_optimized
99
+
100
+ # 出力ディレクトリを作成
101
+ output_dir.mkdir(parents=True, exist_ok=True)
102
+
103
+ results: dict[str, list[str]] = {}
104
+
105
+ # .mdファイルを処理
106
+ for file_path in source_dir.glob("*.md"):
107
+ print(f"Processing: {file_path.name}", flush=True)
108
+ content = file_path.read_text(encoding="utf-8")
109
+
110
+ generated_files = []
111
+
112
+ for emp_type in self.EMPLOYEE_TYPES:
113
+ # 従業員タイプを安全なファイル名に変換
114
+ safe_emp_type = emp_type.replace("タイム", "") # パートタイム → パート
115
+
116
+ # 出力ファイル名
117
+ output_filename = f"{file_path.stem}_{safe_emp_type}.md"
118
+ output_path = output_dir / output_filename
119
+
120
+ print(f" Generating: {output_filename} ({emp_type}向け)", flush=True)
121
+
122
+ try:
123
+ # LLMで再構成
124
+ restructured = self.preprocess_document(content, file_path.name, emp_type)
125
+
126
+ # 保存
127
+ output_path.write_text(restructured, encoding="utf-8")
128
+ generated_files.append(output_filename)
129
+ print(f" ✓ Saved: {output_filename}", flush=True)
130
+
131
+ except Exception as e:
132
+ print(f" ✗ Error processing {output_filename}: {e}", flush=True)
133
+
134
+ results[file_path.name] = generated_files
135
+
136
+ return results
137
+
138
+ def preprocess_single_document(
139
+ self,
140
+ filename: str,
141
+ source_dir: Optional[Path] = None,
142
+ output_dir: Optional[Path] = None,
143
+ ) -> list[str]:
144
+ """
145
+ 単一のドキュメントを処理
146
+
147
+ Args:
148
+ filename: 処理するファイル名
149
+
150
+ Returns:
151
+ 生成されたファイル名のリスト
152
+ """
153
+ source_dir = source_dir or self.settings.documents_dir
154
+ output_dir = output_dir or self.settings.documents_dir_optimized
155
+
156
+ file_path = source_dir / filename
157
+ if not file_path.exists():
158
+ raise FileNotFoundError(f"File not found: {file_path}")
159
+
160
+ output_dir.mkdir(parents=True, exist_ok=True)
161
+
162
+ content = file_path.read_text(encoding="utf-8")
163
+ generated_files = []
164
+
165
+ for emp_type in self.EMPLOYEE_TYPES:
166
+ safe_emp_type = emp_type.replace("タイム", "")
167
+ output_filename = f"{file_path.stem}_{safe_emp_type}.md"
168
+ output_path = output_dir / output_filename
169
+
170
+ print(f"Generating: {output_filename} ({emp_type}向け)", flush=True)
171
+
172
+ try:
173
+ restructured = self.preprocess_document(content, file_path.name, emp_type)
174
+ output_path.write_text(restructured, encoding="utf-8")
175
+ generated_files.append(output_filename)
176
+ print(f"✓ Saved: {output_filename}", flush=True)
177
+ except Exception as e:
178
+ print(f"✗ Error: {e}", flush=True)
179
+
180
+ return generated_files
181
+
182
+
183
+ # シングルトンインスタンス
184
+ _preprocessor: Optional[DocumentPreprocessor] = None
185
+
186
+
187
+ def get_document_preprocessor() -> DocumentPreprocessor:
188
+ """ドキュメント前処理サービスのインスタンスを取得"""
189
+ global _preprocessor
190
+ if _preprocessor is None:
191
+ _preprocessor = DocumentPreprocessor()
192
+ return _preprocessor
193
+
194
+
195
+ # CLI用のエントリーポイント
196
+ if __name__ == "__main__":
197
+ import sys
198
+
199
+ preprocessor = get_document_preprocessor()
200
+
201
+ if len(sys.argv) > 1:
202
+ # 特定のファイルを処理
203
+ filename = sys.argv[1]
204
+ print(f"Processing single file: {filename}")
205
+ result = preprocessor.preprocess_single_document(filename)
206
+ print(f"Generated files: {result}")
207
+ else:
208
+ # 全ファイルを処理
209
+ print("Processing all documents...")
210
+ results = preprocessor.preprocess_all_documents()
211
+ print("\n=== Summary ===")
212
+ for original, generated in results.items():
213
+ print(f"{original} -> {len(generated)} files")
app/services/document_service.py ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ドキュメント処理サービス
3
+ """
4
+
5
+ import hashlib
6
+ import shutil
7
+ from pathlib import Path
8
+ from typing import Literal
9
+
10
+ from langchain_community.document_loaders import DirectoryLoader
11
+ from langchain_core.documents import Document
12
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
13
+
14
+ from app.config import (
15
+ get_settings,
16
+ ChunkingStrategy,
17
+ DocumentSet,
18
+ CHUNKING_CONFIGS,
19
+ )
20
+ from app.utils.line_tracker import LineTrackingTextLoader, calculate_line_numbers
21
+ from app.utils.errors import DocumentException, ErrorMessages
22
+
23
+
24
+ class DocumentInfo:
25
+ """ドキュメント情報"""
26
+
27
+ def __init__(
28
+ self,
29
+ id: str,
30
+ filename: str,
31
+ doc_type: Literal["sample", "uploaded"],
32
+ status: Literal["ready", "processing", "error"] = "ready",
33
+ line_count: int = 0,
34
+ ):
35
+ self.id = id
36
+ self.filename = filename
37
+ self.type = doc_type
38
+ self.status = status
39
+ self.line_count = line_count
40
+
41
+ def to_dict(self) -> dict:
42
+ return {
43
+ "id": self.id,
44
+ "filename": self.filename,
45
+ "type": self.type,
46
+ "status": self.status,
47
+ "line_count": self.line_count,
48
+ }
49
+
50
+
51
+ class DocumentService:
52
+ """ドキュメント管理サービス"""
53
+
54
+ def __init__(self):
55
+ self.settings = get_settings()
56
+ self._ensure_directories()
57
+
58
+ def _ensure_directories(self) -> None:
59
+ """必要なディレクトリを作成"""
60
+ self.settings.documents_dir.mkdir(parents=True, exist_ok=True)
61
+ self.settings.uploads_dir.mkdir(parents=True, exist_ok=True)
62
+ # Also ensure optimized docs directory exists
63
+ self.settings.documents_dir_optimized.mkdir(parents=True, exist_ok=True)
64
+
65
+ def _get_documents_dir(self, document_set: DocumentSet) -> Path:
66
+ """Get the documents directory for the specified document set"""
67
+ if document_set == DocumentSet.OPTIMIZED:
68
+ return self.settings.documents_dir_optimized
69
+ return self.settings.documents_dir
70
+
71
+ def _iter_documents(self, directory: Path):
72
+ """指定ディレクトリ内の.txtと.mdファイルをイテレート"""
73
+ yield from directory.glob("**/*.txt")
74
+ yield from directory.glob("**/*.md")
75
+
76
+ def _generate_id(self, filename: str, content: str) -> str:
77
+ """ファイル名とコンテンツからIDを生成"""
78
+ hash_input = f"{filename}:{content[:100]}"
79
+ return hashlib.md5(hash_input.encode()).hexdigest()[:12]
80
+
81
+ def load_documents(
82
+ self,
83
+ document_set: DocumentSet = DocumentSet.ORIGINAL
84
+ ) -> list[Document]:
85
+ """Load documents from the specified document set"""
86
+ documents: list[Document] = []
87
+
88
+ # Get the appropriate directory
89
+ docs_dir = self._get_documents_dir(document_set)
90
+
91
+ # Load documents from the directory
92
+ if docs_dir.exists():
93
+ loaded_docs = self._load_from_directory(docs_dir)
94
+ for doc in loaded_docs:
95
+ doc.metadata["doc_type"] = "sample"
96
+ doc.metadata["document_set"] = document_set.value
97
+ documents.extend(loaded_docs)
98
+
99
+ # Also load uploaded documents (always included)
100
+ if self.settings.uploads_dir.exists():
101
+ uploaded_docs = self._load_from_directory(self.settings.uploads_dir)
102
+ for doc in uploaded_docs:
103
+ doc.metadata["doc_type"] = "uploaded"
104
+ doc.metadata["document_set"] = "uploaded"
105
+ documents.extend(uploaded_docs)
106
+
107
+ print(f"{len(documents)}個のドキュメントを読み込みました (document_set={document_set.value})", flush=True)
108
+ return documents
109
+
110
+ def _load_from_directory(self, directory: Path) -> list[Document]:
111
+ """指定ディレクトリからドキュメントを読み込む"""
112
+ if not directory.exists() or not any(self._iter_documents(directory)):
113
+ return []
114
+
115
+ documents: list[Document] = []
116
+ for pattern in ["**/*.txt", "**/*.md"]:
117
+ loader = DirectoryLoader(
118
+ str(directory),
119
+ glob=pattern,
120
+ loader_cls=LineTrackingTextLoader, # type: ignore[arg-type]
121
+ loader_kwargs={"encoding": "utf-8"},
122
+ )
123
+ documents.extend(loader.load())
124
+ return documents
125
+
126
+ def split_documents(
127
+ self,
128
+ documents: list[Document],
129
+ strategy: ChunkingStrategy = ChunkingStrategy.STANDARD,
130
+ ) -> list[Document]:
131
+ """Split documents into chunks using the specified strategy"""
132
+ if not documents:
133
+ return []
134
+
135
+ config = CHUNKING_CONFIGS[strategy]
136
+
137
+ if strategy == ChunkingStrategy.PARENT_CHILD:
138
+ return self._split_parent_child(documents, config)
139
+ elif strategy == ChunkingStrategy.HYPOTHETICAL_QUESTIONS:
140
+ return self._split_hypothetical_questions(documents, config)
141
+ else:
142
+ return self._split_standard(documents, config)
143
+
144
+ def _split_standard(
145
+ self,
146
+ documents: list[Document],
147
+ config: dict,
148
+ ) -> list[Document]:
149
+ """Standard chunking strategy"""
150
+ splitter = RecursiveCharacterTextSplitter(
151
+ chunk_size=config["chunk_size"],
152
+ chunk_overlap=config["chunk_overlap"],
153
+ length_function=len,
154
+ add_start_index=True,
155
+ )
156
+ chunks = splitter.split_documents(documents)
157
+
158
+ # Add line numbers to metadata
159
+ chunks = calculate_line_numbers(chunks)
160
+
161
+ # Add chunking metadata
162
+ for chunk in chunks:
163
+ chunk.metadata["chunking_strategy"] = "standard"
164
+ chunk.metadata["chunk_size"] = config["chunk_size"]
165
+
166
+ print(f"{len(chunks)}個のチャンクに分割しました (standard)", flush=True)
167
+ return chunks
168
+
169
+ def _split_parent_child(
170
+ self,
171
+ documents: list[Document],
172
+ config: dict,
173
+ ) -> list[Document]:
174
+ """
175
+ Parent-child chunking strategy.
176
+
177
+ Creates small chunks for retrieval but stores reference to parent chunk
178
+ for providing more context to the LLM.
179
+ """
180
+ # First, create parent chunks
181
+ parent_splitter = RecursiveCharacterTextSplitter(
182
+ chunk_size=config["parent_chunk_size"],
183
+ chunk_overlap=config["parent_chunk_overlap"],
184
+ length_function=len,
185
+ add_start_index=True,
186
+ )
187
+ parent_chunks = parent_splitter.split_documents(documents)
188
+
189
+ # Then create child chunks from each parent
190
+ child_splitter = RecursiveCharacterTextSplitter(
191
+ chunk_size=config["child_chunk_size"],
192
+ chunk_overlap=config["child_chunk_overlap"],
193
+ length_function=len,
194
+ add_start_index=True,
195
+ )
196
+
197
+ all_chunks = []
198
+ for i, parent in enumerate(parent_chunks):
199
+ # Create a document from the parent's content
200
+ parent_doc = Document(
201
+ page_content=parent.page_content,
202
+ metadata=parent.metadata.copy(),
203
+ )
204
+
205
+ # Split into child chunks
206
+ child_chunks = child_splitter.split_documents([parent_doc])
207
+
208
+ # Add parent reference to each child
209
+ for child in child_chunks:
210
+ child.metadata["parent_id"] = i
211
+ child.metadata["parent_content"] = parent.page_content
212
+ child.metadata["chunking_strategy"] = "parent_child"
213
+ child.metadata["is_child"] = True
214
+
215
+ all_chunks.extend(child_chunks)
216
+
217
+ # Add line numbers to metadata
218
+ all_chunks = calculate_line_numbers(all_chunks)
219
+
220
+ print(f"{len(all_chunks)}個のチャンクに分割しました (parent-child: {len(parent_chunks)} parents)", flush=True)
221
+ return all_chunks
222
+
223
+ def _split_hypothetical_questions(
224
+ self,
225
+ documents: list[Document],
226
+ config: dict,
227
+ ) -> list[Document]:
228
+ """
229
+ Hypothetical Questions chunking strategy.
230
+
231
+ Generates user-facing questions for each chunk using an LLM.
232
+ The questions are indexed (for similarity search), but metadata
233
+ contains the original chunk content (for LLM context).
234
+
235
+ This solves the alias mismatch problem by resolving domain terminology
236
+ (e.g., "第2条の2に定める者") to user language (e.g., "アルバイト")
237
+ at index time.
238
+ """
239
+ from app.services.question_generator import get_question_generator
240
+
241
+ # First, create standard chunks
242
+ splitter = RecursiveCharacterTextSplitter(
243
+ chunk_size=config["chunk_size"],
244
+ chunk_overlap=config["chunk_overlap"],
245
+ length_function=len,
246
+ add_start_index=True,
247
+ separators=["\n## ", "\n### ", "\n\n", "\n", "。", "、", " "],
248
+ )
249
+ chunks = splitter.split_documents(documents)
250
+
251
+ # Add line numbers to metadata
252
+ chunks = calculate_line_numbers(chunks)
253
+
254
+ # Generate questions for each chunk
255
+ generator = get_question_generator()
256
+ num_questions = config.get("questions_per_chunk", 3)
257
+
258
+ print(f"[HypotheticalQuestions] Generating questions for {len(chunks)} chunks...", flush=True)
259
+
260
+ question_docs = []
261
+ for i, chunk in enumerate(chunks):
262
+ chunk_id = f"chunk_{i}"
263
+
264
+ # Generate hypothetical questions
265
+ questions = generator.generate_questions(chunk.page_content, num_questions)
266
+
267
+ if not questions:
268
+ print(f" Warning: No questions generated for chunk {i + 1}, using chunk as-is", flush=True)
269
+ # Fallback: use the chunk itself if no questions generated
270
+ question_doc = Document(
271
+ page_content=chunk.page_content[:200], # Use first 200 chars as "question"
272
+ metadata={
273
+ **chunk.metadata,
274
+ "chunk_id": chunk_id,
275
+ "original_content": chunk.page_content,
276
+ "chunking_strategy": "hypothetical_questions",
277
+ "is_question": True,
278
+ "question_index": 0,
279
+ }
280
+ )
281
+ question_docs.append(question_doc)
282
+ else:
283
+ # Create question documents pointing to this chunk
284
+ for q_idx, question in enumerate(questions):
285
+ question_doc = Document(
286
+ page_content=question,
287
+ metadata={
288
+ **chunk.metadata,
289
+ "chunk_id": chunk_id,
290
+ "original_content": chunk.page_content,
291
+ "chunking_strategy": "hypothetical_questions",
292
+ "is_question": True,
293
+ "question_index": q_idx,
294
+ }
295
+ )
296
+ question_docs.append(question_doc)
297
+
298
+ print(f"{len(question_docs)}個の質問ドキュメントを生成しました (hypothetical_questions: {len(chunks)} original chunks)", flush=True)
299
+ return question_docs
300
+
301
+ def list_documents(
302
+ self,
303
+ document_set: DocumentSet = DocumentSet.ORIGINAL
304
+ ) -> list[DocumentInfo]:
305
+ """Get document info for the specified document set"""
306
+ documents: list[DocumentInfo] = []
307
+
308
+ # Get documents from the appropriate directory
309
+ docs_dir = self._get_documents_dir(document_set)
310
+
311
+ for file_path in self._iter_documents(docs_dir):
312
+ content = file_path.read_text(encoding="utf-8")
313
+ line_count = len(content.split("\n"))
314
+ doc_id = self._generate_id(file_path.name, content)
315
+ documents.append(
316
+ DocumentInfo(
317
+ id=doc_id,
318
+ filename=file_path.name,
319
+ doc_type="sample",
320
+ status="ready",
321
+ line_count=line_count,
322
+ )
323
+ )
324
+
325
+ # Also list uploaded documents
326
+ for file_path in self._iter_documents(self.settings.uploads_dir):
327
+ content = file_path.read_text(encoding="utf-8")
328
+ line_count = len(content.split("\n"))
329
+ doc_id = self._generate_id(file_path.name, content)
330
+ documents.append(
331
+ DocumentInfo(
332
+ id=doc_id,
333
+ filename=file_path.name,
334
+ doc_type="uploaded",
335
+ status="ready",
336
+ line_count=line_count,
337
+ )
338
+ )
339
+
340
+ return documents
341
+
342
+ async def upload_document(self, filename: str, content: bytes) -> DocumentInfo:
343
+ """ドキュメントをアップロード"""
344
+ # バリデーション
345
+ if not filename.endswith(".txt") and not filename.endswith(".md"):
346
+ raise DocumentException(ErrorMessages.INVALID_FILE_TYPE, "INVALID_FILE_TYPE")
347
+
348
+ if len(content) > 1024 * 1024: # 1MB
349
+ raise DocumentException(ErrorMessages.FILE_TOO_LARGE, "FILE_TOO_LARGE")
350
+
351
+ # UTF-8としてデコード
352
+ try:
353
+ text_content = content.decode("utf-8")
354
+ except UnicodeDecodeError:
355
+ raise DocumentException(
356
+ "ファイルのエンコーディングが無効です。UTF-8形式のファイルをアップロードしてください。",
357
+ "INVALID_ENCODING",
358
+ )
359
+
360
+ # ファイルを保存
361
+ file_path = self.settings.uploads_dir / filename
362
+ file_path.write_text(text_content, encoding="utf-8")
363
+
364
+ line_count = len(text_content.split("\n"))
365
+ doc_id = self._generate_id(filename, text_content)
366
+
367
+ return DocumentInfo(
368
+ id=doc_id,
369
+ filename=filename,
370
+ doc_type="uploaded",
371
+ status="ready",
372
+ line_count=line_count,
373
+ )
374
+
375
+ def delete_document(self, doc_id: str) -> bool:
376
+ """アップロードされたドキュメントを削除"""
377
+ # アップロードフォルダ内のみ削除可能
378
+ for file_path in self._iter_documents(self.settings.uploads_dir):
379
+ content = file_path.read_text(encoding="utf-8")
380
+ if self._generate_id(file_path.name, content) == doc_id:
381
+ file_path.unlink()
382
+ return True
383
+
384
+ raise DocumentException(ErrorMessages.DOCUMENT_NOT_FOUND, "NOT_FOUND")
385
+
386
+ def has_documents(
387
+ self,
388
+ document_set: DocumentSet = DocumentSet.ORIGINAL
389
+ ) -> bool:
390
+ """Check if documents exist for the specified document set"""
391
+ docs_dir = self._get_documents_dir(document_set)
392
+ sample_exists = any(self._iter_documents(docs_dir))
393
+ uploads_exist = any(self._iter_documents(self.settings.uploads_dir))
394
+ return sample_exists or uploads_exist
395
+
396
+ def get_document_content(self, doc_id: str) -> tuple[DocumentInfo, str]:
397
+ """ドキュメントの内容を取得"""
398
+ # Check both original and optimized directories
399
+ for docs_dir, doc_set in [
400
+ (self.settings.documents_dir, DocumentSet.ORIGINAL),
401
+ (self.settings.documents_dir_optimized, DocumentSet.OPTIMIZED),
402
+ ]:
403
+ for file_path in self._iter_documents(docs_dir):
404
+ content = file_path.read_text(encoding="utf-8")
405
+ if self._generate_id(file_path.name, content) == doc_id:
406
+ line_count = len(content.split("\n"))
407
+ doc_info = DocumentInfo(
408
+ id=doc_id,
409
+ filename=file_path.name,
410
+ doc_type="sample",
411
+ status="ready",
412
+ line_count=line_count,
413
+ )
414
+ return doc_info, content
415
+
416
+ # アップロードされたドキュメントを検索
417
+ for file_path in self._iter_documents(self.settings.uploads_dir):
418
+ content = file_path.read_text(encoding="utf-8")
419
+ if self._generate_id(file_path.name, content) == doc_id:
420
+ line_count = len(content.split("\n"))
421
+ doc_info = DocumentInfo(
422
+ id=doc_id,
423
+ filename=file_path.name,
424
+ doc_type="uploaded",
425
+ status="ready",
426
+ line_count=line_count,
427
+ )
428
+ return doc_info, content
429
+
430
+ raise DocumentException(ErrorMessages.DOCUMENT_NOT_FOUND, "NOT_FOUND")
431
+
432
+ def get_available_strategies(self) -> list[dict]:
433
+ """Get list of available chunking strategies with descriptions"""
434
+ return [
435
+ {
436
+ "id": ChunkingStrategy.STANDARD.value,
437
+ "name": "Standard (1000/200)",
438
+ "description": "標準的なチャンキング。チャンクサイズ1000文字、オーバーラップ200文字。",
439
+ },
440
+ {
441
+ "id": ChunkingStrategy.LARGE.value,
442
+ "name": "Large (2000/500)",
443
+ "description": "大きめのチャンク。より多くのコンテキストを保持。",
444
+ },
445
+ {
446
+ "id": ChunkingStrategy.PARENT_CHILD.value,
447
+ "name": "Parent-Child",
448
+ "description": "小さなチャンクで検索し、親チャンクをコンテキストとして使用。",
449
+ },
450
+ {
451
+ "id": ChunkingStrategy.HYPOTHETICAL_QUESTIONS.value,
452
+ "name": "Hypothetical Questions",
453
+ "description": "LLMでユーザー視点の質問を生成してインデックス。エイリアス問題を解決。",
454
+ },
455
+ ]
456
+
457
+ def get_available_document_sets(self) -> list[dict]:
458
+ """Get list of available document sets with descriptions"""
459
+ original_count = len(list(self._iter_documents(self.settings.documents_dir)))
460
+ optimized_count = len(list(self._iter_documents(self.settings.documents_dir_optimized)))
461
+
462
+ return [
463
+ {
464
+ "id": DocumentSet.ORIGINAL.value,
465
+ "name": "Original Documents",
466
+ "description": f"元の規程文書 ({original_count}ファイル)",
467
+ "document_count": original_count,
468
+ },
469
+ {
470
+ "id": DocumentSet.OPTIMIZED.value,
471
+ "name": "Optimized Documents",
472
+ "description": f"前処理済み文書 ({optimized_count}ファイル)",
473
+ "document_count": optimized_count,
474
+ },
475
+ ]
476
+
477
+
478
+ # シングルトンインスタンス
479
+ _document_service: DocumentService | None = None
480
+
481
+
482
+ def get_document_service() -> DocumentService:
483
+ """ドキュメントサービスのインスタンスを取得"""
484
+ global _document_service
485
+ if _document_service is None:
486
+ _document_service = DocumentService()
487
+ return _document_service
app/services/embedding_service.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 埋め込みモデルサービス
3
+ """
4
+
5
+ from functools import lru_cache
6
+
7
+ from langchain_huggingface import HuggingFaceEmbeddings
8
+
9
+ from app.config import get_settings
10
+
11
+
12
+ class EmbeddingService:
13
+ """埋め込みモデル管理サービス"""
14
+
15
+ _instance: "EmbeddingService | None" = None
16
+ _embeddings: HuggingFaceEmbeddings | None = None
17
+
18
+ def __new__(cls) -> "EmbeddingService":
19
+ if cls._instance is None:
20
+ cls._instance = super().__new__(cls)
21
+ return cls._instance
22
+
23
+ def __init__(self):
24
+ if self._embeddings is None:
25
+ self._initialize()
26
+
27
+ def _initialize(self) -> None:
28
+ """埋め込みモデルを初期化"""
29
+ settings = get_settings()
30
+ print(f"埋め込みモデルを読み込み中: {settings.embedding_model}", flush=True)
31
+ print("(初回実行時は約2.2GBのモデルをダウンロードします...)", flush=True)
32
+
33
+ self._embeddings = HuggingFaceEmbeddings(
34
+ model_name=settings.embedding_model,
35
+ model_kwargs={"device": "cpu"},
36
+ encode_kwargs={"normalize_embeddings": True}, # e5モデル用に正規化
37
+ )
38
+ print("埋め込みモデルの準備完了!", flush=True)
39
+
40
+ @property
41
+ def embeddings(self) -> HuggingFaceEmbeddings:
42
+ """埋め込みモデルインスタンスを取得"""
43
+ if self._embeddings is None:
44
+ self._initialize()
45
+ return self._embeddings # type: ignore
46
+
47
+ @property
48
+ def is_ready(self) -> bool:
49
+ """モデルが準備できているかチェック"""
50
+ return self._embeddings is not None
51
+
52
+
53
+ @lru_cache
54
+ def get_embedding_service() -> EmbeddingService:
55
+ """埋め込みサービスのシングルトンインスタンスを取得"""
56
+ return EmbeddingService()
app/services/question_generator.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 仮説質問生成サービス - Hypothetical Questions Generation
3
+
4
+ LLMを使用してチャンクからユーザー視点の質問を生成します。
5
+ これにより、エイリアス問題(例:「アルバイト」vs「第2条の2に定める者」)を
6
+ インデックス時に解決できます。
7
+ """
8
+
9
+ from langchain_google_genai import ChatGoogleGenerativeAI
10
+ from langchain_core.documents import Document
11
+
12
+ from app.config import get_settings
13
+
14
+
15
+ class QuestionGenerator:
16
+ """チャンクから仮説質問を生成するサービス"""
17
+
18
+ def __init__(self):
19
+ self.settings = get_settings()
20
+ self._llm = None
21
+
22
+ def _get_llm(self) -> ChatGoogleGenerativeAI:
23
+ """LLMインスタンスを取得(遅延初期化)"""
24
+ if self._llm is None:
25
+ self._llm = ChatGoogleGenerativeAI(
26
+ model=self.settings.llm_model,
27
+ google_api_key=self.settings.google_api_key,
28
+ temperature=0.7, # 多様な質問生成のため少し高めに設定
29
+ max_retries=2,
30
+ )
31
+ return self._llm
32
+
33
+ def generate_questions(self, chunk_content: str, num_questions: int = 3) -> list[str]:
34
+ """
35
+ チャンクの内容からユーザーが尋ねそうな質問を生成
36
+
37
+ Args:
38
+ chunk_content: チャンクのテキスト内容
39
+ num_questions: 生成する質問の数
40
+
41
+ Returns:
42
+ 生成された質問のリスト
43
+ """
44
+ prompt = f"""以下の社内規程の文書セクションを読んで、従業員がこの内容について尋ねそうな質問を{num_questions}個生成してください。
45
+
46
+ 重要な要件:
47
+ - 質問は自然な日本語で、従業員が実際に使う言葉で作成してください
48
+ - 「第2条の2に定める者」「短期雇用者」などの法律用語は、「アルバイト」「パート」「パートタイマー」など一般的な言葉に置き換えてください
49
+ - 質問は具体的で、このテキストから回答できるものにしてください
50
+ - 各質問は1行に1つずつ記載してください
51
+ - 番号や記号は付けないでください
52
+
53
+ 文書セクション:
54
+ {chunk_content}
55
+
56
+ 質問:"""
57
+
58
+ try:
59
+ response = self._get_llm().invoke(prompt)
60
+ # Extract text content from response
61
+ content = response.content
62
+ if isinstance(content, list):
63
+ # Handle case where content is a list of message parts
64
+ content = " ".join(str(part) for part in content)
65
+ content_text = str(content)
66
+
67
+ # 改行で分割して空行を除去
68
+ questions = [
69
+ q.strip().lstrip("0123456789.))・-  ") # 番号や記号を除去
70
+ for q in content_text.strip().split('\n')
71
+ if q.strip() and not q.strip().startswith('#')
72
+ ]
73
+ # 質問として有効なもののみ(?や?で終わるか、十分な長さがあるもの)
74
+ valid_questions = [
75
+ q for q in questions
76
+ if len(q) > 5 and (q.endswith('?') or q.endswith('?') or len(q) > 10)
77
+ ]
78
+ return valid_questions[:num_questions]
79
+ except Exception as e:
80
+ print(f"[QuestionGenerator] Error generating questions: {e}", flush=True)
81
+ return []
82
+
83
+ def generate_questions_for_chunks(
84
+ self,
85
+ chunks: list[Document],
86
+ num_questions: int = 3
87
+ ) -> list[tuple[Document, list[str]]]:
88
+ """
89
+ 複数のチャンクに対して質問を生成
90
+
91
+ Args:
92
+ chunks: チャンクのリスト
93
+ num_questions: 各チャンクに対して生成する質問の数
94
+
95
+ Returns:
96
+ (チャンク, 質問リスト) のタプルのリスト
97
+ """
98
+ results = []
99
+ total = len(chunks)
100
+
101
+ for i, chunk in enumerate(chunks):
102
+ print(f"[QuestionGenerator] Generating questions for chunk {i + 1}/{total}", flush=True)
103
+ questions = self.generate_questions(chunk.page_content, num_questions)
104
+
105
+ if questions:
106
+ results.append((chunk, questions))
107
+ print(f" Generated {len(questions)} questions", flush=True)
108
+ else:
109
+ print(f" Warning: No questions generated for chunk {i + 1}", flush=True)
110
+ # 質問が生成できなかった場合は空リストで追加
111
+ results.append((chunk, []))
112
+
113
+ return results
114
+
115
+
116
+ # シングルトンインスタンス
117
+ _question_generator: QuestionGenerator | None = None
118
+
119
+
120
+ def get_question_generator() -> QuestionGenerator:
121
+ """質問生成サービスのインスタンスを取得"""
122
+ global _question_generator
123
+ if _question_generator is None:
124
+ _question_generator = QuestionGenerator()
125
+ return _question_generator