@nahisaho/satori 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -47
- package/package.json +1 -1
- package/src/.github/skills/scientific-cancer-genomics/SKILL.md +287 -0
- package/src/.github/skills/scientific-clinical-reporting/SKILL.md +324 -0
- package/src/.github/skills/scientific-literature-search/SKILL.md +443 -0
- package/src/.github/skills/scientific-metabolomics-databases/SKILL.md +288 -0
- package/src/.github/skills/scientific-molecular-docking/SKILL.md +303 -0
- package/src/.github/skills/scientific-pathway-enrichment/SKILL.md +449 -0
- package/src/.github/skills/scientific-protein-domain-family/SKILL.md +369 -0
- package/src/.github/skills/scientific-protein-interaction-network/SKILL.md +352 -0
- package/src/.github/skills/scientific-systematic-review/SKILL.md +361 -0
- package/src/.github/skills/scientific-variant-effect-prediction/SKILL.md +325 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-clinical-reporting
|
|
3
|
+
description: |
|
|
4
|
+
臨床レポート自動生成スキル。検査結果サマリー (SOAP ノート)、バイオマーカー
|
|
5
|
+
プロファイルレポート、薬理ゲノミクスレポート、臨床試験要約を構造化テンプレート
|
|
6
|
+
(PDF/LaTeX/HTML) で出力。HL7 FHIR DiagnosticReport 形式にも対応。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scientific Clinical Reporting
|
|
10
|
+
|
|
11
|
+
臨床データから構造化レポートを自動生成するパイプラインを提供する。
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- 検査結果を SOAP ノート形式でまとめるとき
|
|
16
|
+
- バイオマーカープロファイルレポートを作成するとき
|
|
17
|
+
- ファーマコゲノミクスレポート (CPIC ガイドライン準拠) が必要なとき
|
|
18
|
+
- 臨床試験の CSR (Clinical Study Report) サマリーを生成するとき
|
|
19
|
+
- HL7 FHIR DiagnosticReport 形式で出力するとき
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
## 1. SOAP ノート生成
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import json
|
|
29
|
+
from datetime import datetime
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def generate_soap_note(patient_data, findings, assessment, plan):
|
|
33
|
+
"""
|
|
34
|
+
SOAP ノート形式の臨床レポートを生成。
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
patient_data: dict — {"id": "...", "age": 45, "sex": "M", ...}
|
|
38
|
+
findings: dict — {"subjective": [...], "objective": [...]}
|
|
39
|
+
assessment: list — 評価・診断リスト
|
|
40
|
+
plan: list — 治療計画リスト
|
|
41
|
+
"""
|
|
42
|
+
soap = {
|
|
43
|
+
"report_type": "SOAP_Note",
|
|
44
|
+
"generated_at": datetime.now().isoformat(),
|
|
45
|
+
"patient": {
|
|
46
|
+
"id": patient_data.get("id", "ANON"),
|
|
47
|
+
"age": patient_data.get("age"),
|
|
48
|
+
"sex": patient_data.get("sex"),
|
|
49
|
+
},
|
|
50
|
+
"S": { # Subjective
|
|
51
|
+
"chief_complaint": findings.get("chief_complaint", ""),
|
|
52
|
+
"history": findings.get("subjective", []),
|
|
53
|
+
},
|
|
54
|
+
"O": { # Objective
|
|
55
|
+
"vitals": findings.get("vitals", {}),
|
|
56
|
+
"lab_results": findings.get("lab_results", []),
|
|
57
|
+
"imaging": findings.get("imaging", []),
|
|
58
|
+
"physical_exam": findings.get("objective", []),
|
|
59
|
+
},
|
|
60
|
+
"A": { # Assessment
|
|
61
|
+
"diagnoses": assessment,
|
|
62
|
+
"differential": findings.get("differential", []),
|
|
63
|
+
},
|
|
64
|
+
"P": { # Plan
|
|
65
|
+
"treatment": plan,
|
|
66
|
+
"follow_up": findings.get("follow_up", ""),
|
|
67
|
+
"referrals": findings.get("referrals", []),
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
print(f"SOAP note: patient={soap['patient']['id']}, "
|
|
72
|
+
f"diagnoses={len(assessment)}, plans={len(plan)}")
|
|
73
|
+
return soap
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 2. バイオマーカープロファイルレポート
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import pandas as pd
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def biomarker_profile_report(biomarkers_df, reference_ranges=None):
|
|
83
|
+
"""
|
|
84
|
+
バイオマーカープロファイルレポート生成。
|
|
85
|
+
|
|
86
|
+
Parameters:
|
|
87
|
+
biomarkers_df: DataFrame — columns: [marker, value, unit, specimen]
|
|
88
|
+
reference_ranges: dict — {"marker": {"low": x, "high": y, "unit": "..."}}
|
|
89
|
+
"""
|
|
90
|
+
if reference_ranges is None:
|
|
91
|
+
reference_ranges = {
|
|
92
|
+
"CEA": {"low": 0, "high": 5.0, "unit": "ng/mL"},
|
|
93
|
+
"AFP": {"low": 0, "high": 10.0, "unit": "ng/mL"},
|
|
94
|
+
"CA19-9": {"low": 0, "high": 37.0, "unit": "U/mL"},
|
|
95
|
+
"CA125": {"low": 0, "high": 35.0, "unit": "U/mL"},
|
|
96
|
+
"PSA": {"low": 0, "high": 4.0, "unit": "ng/mL"},
|
|
97
|
+
"HER2": {"low": 0, "high": 1, "unit": "IHC score"},
|
|
98
|
+
"Ki-67": {"low": 0, "high": 14, "unit": "%"},
|
|
99
|
+
"PD-L1 TPS": {"low": 0, "high": 1, "unit": "%"},
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
results = []
|
|
103
|
+
for _, row in biomarkers_df.iterrows():
|
|
104
|
+
marker = row["marker"]
|
|
105
|
+
value = float(row["value"])
|
|
106
|
+
ref = reference_ranges.get(marker, {})
|
|
107
|
+
|
|
108
|
+
status = "normal"
|
|
109
|
+
if ref:
|
|
110
|
+
if value > ref.get("high", float("inf")):
|
|
111
|
+
status = "HIGH"
|
|
112
|
+
elif value < ref.get("low", float("-inf")):
|
|
113
|
+
status = "LOW"
|
|
114
|
+
|
|
115
|
+
results.append({
|
|
116
|
+
"marker": marker,
|
|
117
|
+
"value": value,
|
|
118
|
+
"unit": row.get("unit", ref.get("unit", "")),
|
|
119
|
+
"reference": f"{ref.get('low', '?')}-{ref.get('high', '?')}",
|
|
120
|
+
"status": status,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
report_df = pd.DataFrame(results)
|
|
124
|
+
abnormal = report_df[report_df["status"] != "normal"]
|
|
125
|
+
|
|
126
|
+
report = {
|
|
127
|
+
"report_type": "Biomarker_Profile",
|
|
128
|
+
"total_markers": len(report_df),
|
|
129
|
+
"abnormal_count": len(abnormal),
|
|
130
|
+
"results": report_df.to_dict("records"),
|
|
131
|
+
"summary": (
|
|
132
|
+
f"{len(abnormal)} of {len(report_df)} markers outside reference range"
|
|
133
|
+
if len(abnormal) > 0
|
|
134
|
+
else "All markers within reference range"
|
|
135
|
+
),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
print(f"Biomarker profile: {len(report_df)} markers, "
|
|
139
|
+
f"{len(abnormal)} abnormal")
|
|
140
|
+
return report
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 3. ファーマコゲノミクスレポート
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
def pharmacogenomics_report(genotypes, medications):
|
|
147
|
+
"""
|
|
148
|
+
CPIC ガイドライン準拠のファーマコゲノミクスレポート。
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
genotypes: dict — {"CYP2D6": "*1/*4", "CYP2C19": "*1/*2", ...}
|
|
152
|
+
medications: list — ["codeine", "clopidogrel", ...]
|
|
153
|
+
"""
|
|
154
|
+
# CPIC phenotype マッピング (簡略)
|
|
155
|
+
cpic_phenotypes = {
|
|
156
|
+
"CYP2D6": {
|
|
157
|
+
"*1/*1": "Normal Metabolizer",
|
|
158
|
+
"*1/*4": "Intermediate Metabolizer",
|
|
159
|
+
"*4/*4": "Poor Metabolizer",
|
|
160
|
+
"*1/*2xN": "Ultrarapid Metabolizer",
|
|
161
|
+
},
|
|
162
|
+
"CYP2C19": {
|
|
163
|
+
"*1/*1": "Normal Metabolizer",
|
|
164
|
+
"*1/*2": "Intermediate Metabolizer",
|
|
165
|
+
"*2/*2": "Poor Metabolizer",
|
|
166
|
+
"*1/*17": "Rapid Metabolizer",
|
|
167
|
+
"*17/*17": "Ultrarapid Metabolizer",
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# 推奨アクション (簡略)
|
|
172
|
+
drug_gene_map = {
|
|
173
|
+
"codeine": {"gene": "CYP2D6", "action": {
|
|
174
|
+
"Poor Metabolizer": "AVOID — use alternative analgesic",
|
|
175
|
+
"Ultrarapid Metabolizer": "AVOID — toxicity risk",
|
|
176
|
+
"Intermediate Metabolizer": "Use with caution, consider alternative",
|
|
177
|
+
}},
|
|
178
|
+
"clopidogrel": {"gene": "CYP2C19", "action": {
|
|
179
|
+
"Poor Metabolizer": "Use alternative antiplatelet (e.g., prasugrel)",
|
|
180
|
+
"Intermediate Metabolizer": "Consider alternative antiplatelet",
|
|
181
|
+
}},
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
recommendations = []
|
|
185
|
+
for drug in medications:
|
|
186
|
+
entry = drug_gene_map.get(drug, {})
|
|
187
|
+
gene = entry.get("gene", "Unknown")
|
|
188
|
+
genotype = genotypes.get(gene, "Unknown")
|
|
189
|
+
phenotype_map = cpic_phenotypes.get(gene, {})
|
|
190
|
+
phenotype = phenotype_map.get(genotype, "Indeterminate")
|
|
191
|
+
|
|
192
|
+
action = entry.get("action", {}).get(phenotype, "Standard dosing")
|
|
193
|
+
recommendations.append({
|
|
194
|
+
"drug": drug,
|
|
195
|
+
"gene": gene,
|
|
196
|
+
"genotype": genotype,
|
|
197
|
+
"phenotype": phenotype,
|
|
198
|
+
"recommendation": action,
|
|
199
|
+
"cpic_level": "A" if drug in drug_gene_map else "N/A",
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
report = {
|
|
203
|
+
"report_type": "Pharmacogenomics",
|
|
204
|
+
"genotypes_tested": genotypes,
|
|
205
|
+
"medications_queried": medications,
|
|
206
|
+
"recommendations": recommendations,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
print(f"PGx report: {len(genotypes)} genes, {len(medications)} drugs, "
|
|
210
|
+
f"{len(recommendations)} recommendations")
|
|
211
|
+
return report
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## 4. 構造化レポート出力 (LaTeX/HTML)
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
def export_clinical_report(report, output_format="html",
|
|
218
|
+
output_path="reports/clinical_report"):
|
|
219
|
+
"""
|
|
220
|
+
臨床レポートを LaTeX/HTML/FHIR JSON 形式で出力。
|
|
221
|
+
|
|
222
|
+
Parameters:
|
|
223
|
+
report: dict — SOAP, Biomarker, PGx レポート
|
|
224
|
+
output_format: "html", "latex", "fhir_json"
|
|
225
|
+
output_path: str — 出力先パス (拡張子なし)
|
|
226
|
+
"""
|
|
227
|
+
import os
|
|
228
|
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
229
|
+
report_type = report.get("report_type", "Clinical")
|
|
230
|
+
|
|
231
|
+
if output_format == "html":
|
|
232
|
+
filepath = f"{output_path}.html"
|
|
233
|
+
html_parts = [
|
|
234
|
+
"<!DOCTYPE html><html><head>",
|
|
235
|
+
f"<title>{report_type} Report</title>",
|
|
236
|
+
"<style>body{font-family:Arial;margin:2em;}"
|
|
237
|
+
"table{border-collapse:collapse;width:100%;}"
|
|
238
|
+
"td,th{border:1px solid #ddd;padding:8px;}</style>",
|
|
239
|
+
"</head><body>",
|
|
240
|
+
f"<h1>{report_type} Report</h1>",
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
if report_type == "SOAP_Note":
|
|
244
|
+
for section in ["S", "O", "A", "P"]:
|
|
245
|
+
html_parts.append(f"<h2>{section}</h2>")
|
|
246
|
+
html_parts.append(f"<pre>{json.dumps(report.get(section, {}), indent=2, ensure_ascii=False)}</pre>")
|
|
247
|
+
|
|
248
|
+
elif report_type == "Biomarker_Profile":
|
|
249
|
+
html_parts.append("<table><tr><th>Marker</th><th>Value</th>"
|
|
250
|
+
"<th>Reference</th><th>Status</th></tr>")
|
|
251
|
+
for r in report.get("results", []):
|
|
252
|
+
status_color = "red" if r["status"] != "normal" else "green"
|
|
253
|
+
html_parts.append(
|
|
254
|
+
f"<tr><td>{r['marker']}</td><td>{r['value']} {r['unit']}</td>"
|
|
255
|
+
f"<td>{r['reference']}</td>"
|
|
256
|
+
f"<td style='color:{status_color}'>{r['status']}</td></tr>"
|
|
257
|
+
)
|
|
258
|
+
html_parts.append("</table>")
|
|
259
|
+
|
|
260
|
+
html_parts.append("</body></html>")
|
|
261
|
+
with open(filepath, "w") as f:
|
|
262
|
+
f.write("\n".join(html_parts))
|
|
263
|
+
|
|
264
|
+
elif output_format == "fhir_json":
|
|
265
|
+
filepath = f"{output_path}.fhir.json"
|
|
266
|
+
fhir = {
|
|
267
|
+
"resourceType": "DiagnosticReport",
|
|
268
|
+
"status": "final",
|
|
269
|
+
"category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0074",
|
|
270
|
+
"code": "LAB"}]}],
|
|
271
|
+
"code": {"text": report_type},
|
|
272
|
+
"issued": report.get("generated_at", datetime.now().isoformat()),
|
|
273
|
+
"result": [],
|
|
274
|
+
}
|
|
275
|
+
with open(filepath, "w") as f:
|
|
276
|
+
json.dump(fhir, f, indent=2)
|
|
277
|
+
|
|
278
|
+
elif output_format == "latex":
|
|
279
|
+
filepath = f"{output_path}.tex"
|
|
280
|
+
with open(filepath, "w") as f:
|
|
281
|
+
f.write(f"\\documentclass{{article}}\n")
|
|
282
|
+
f.write(f"\\title{{{report_type} Report}}\n")
|
|
283
|
+
f.write("\\begin{document}\n\\maketitle\n")
|
|
284
|
+
f.write(f"Report type: {report_type}\n")
|
|
285
|
+
f.write("\\end{document}\n")
|
|
286
|
+
|
|
287
|
+
print(f"Report exported: {filepath}")
|
|
288
|
+
return filepath
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## References
|
|
292
|
+
|
|
293
|
+
### Output Files
|
|
294
|
+
|
|
295
|
+
| ファイル | 形式 |
|
|
296
|
+
|---|---|
|
|
297
|
+
| `reports/soap_note.json` | JSON |
|
|
298
|
+
| `reports/biomarker_profile.json` | JSON |
|
|
299
|
+
| `reports/pgx_report.json` | JSON |
|
|
300
|
+
| `reports/clinical_report.html` | HTML |
|
|
301
|
+
| `reports/clinical_report.tex` | LaTeX |
|
|
302
|
+
| `reports/clinical_report.fhir.json` | FHIR JSON |
|
|
303
|
+
|
|
304
|
+
### 利用可能ツール
|
|
305
|
+
|
|
306
|
+
> 本スキルは ToolUniverse ツールに直接依存しない。
|
|
307
|
+
|
|
308
|
+
| カテゴリ | 主要ツール | 用途 |
|
|
309
|
+
|---|---|---|
|
|
310
|
+
| — | — | — |
|
|
311
|
+
|
|
312
|
+
### 参照スキル
|
|
313
|
+
|
|
314
|
+
| スキル | 関連 |
|
|
315
|
+
|---|---|
|
|
316
|
+
| `scientific-variant-interpretation` | バリアント解釈レポート |
|
|
317
|
+
| `scientific-variant-effect-prediction` | バリアント病原性スコア |
|
|
318
|
+
| `scientific-pharmacogenomics` | PGx ガイドライン |
|
|
319
|
+
| `scientific-precision-oncology` | 精密腫瘍学レポート |
|
|
320
|
+
| `scientific-disease-research` | 疾患情報統合 |
|
|
321
|
+
|
|
322
|
+
### 依存パッケージ
|
|
323
|
+
|
|
324
|
+
`pandas`, `json` (stdlib), `datetime` (stdlib)
|