Skip to main content
Glama
Liuwen-bin

Health Record Agent

by Liuwen-bin
README.md
# Health Record Agent

这是一个可独立运行、可单独上传到 Git 的 Python 电子病历 Agent。目录中已经
包含运行所需的 Python 源码、依赖清单、环境变量模板和中文演示病历数据库,
不依赖原 `health-record-mcp` 项目的 Bun、Node.js 或 TypeScript 文件。

## 目录结构

```text
Agent/
├── data/
│   └── chinese-demo-record.sqlite  # 中文 FHIR 演示病历
├── mcp/
│   ├── a2a_server.py               # A2A JSON-RPC 服务
│   ├── ehr_service.py              # 只读 SQLite 业务层
│   ├── eval_worker.py              # 受限表达式计算子进程
│   ├── file_service.py             # 受限文本文件读写层
│   ├── import_service.py           # CSV 校验、FHIR 转换与事务入库
│   ├── llm_agent.py                # A2A 使用的 LangChain Agent
│   └── mcp_server.py               # stdio/HTTP MCP 服务
├── .env.example
├── .gitignore
├── LICENSE.txt
├── main.py                         # 终端问答入口
└── requirements.txt
```

## 环境要求

- Python 3.11 或更高版本
- 一个兼容 OpenAI Chat Completions API、并支持工具调用的模型服务

以下命令均在 `Agent` 目录中执行。

## 安装

Windows PowerShell:

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
Copy-Item .env.example .env
```

Linux/macOS:

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
cp .env.example .env
```

编辑 `.env`,至少填写:

```dotenv
LLM_BASE_URL=https://your-openai-compatible-endpoint/v1
LLM_API_KEY=your-api-key
LLM_MODEL=your-tool-calling-model
```

默认数据库为 `data/chinese-demo-record.sqlite`。若要使用其他兼容数据库,
可将 `EHR_DB_PATH` 改为相对于本目录的路径或绝对路径。

## 运行终端问答 Agent

```powershell
python main.py
```

可用命令:

- `/clear`:清空对话上下文
- `/tools`:显示 MCP 工具
- `/exit`:退出

终端 Agent 会自动通过 stdio 启动本目录中的 Python MCP 服务。

## 单独运行 MCP

stdio:

```powershell
python mcp/mcp_server.py --transport stdio
```

Streamable HTTP:

```powershell
python mcp/mcp_server.py --transport http --host 127.0.0.1 --port 8000
```

HTTP MCP 地址为 `http://127.0.0.1:8000/mcp`。

服务提供七个只读病历工具:

- `get_patient_summary`
- `get_medication_review_context`
- `grep_record`
- `query_record`
- `eval_record`
- `read_resource`
- `read_attachment`

药物审查应调用:

```text
get_medication_review_context(patientId, asOf?)
```

该工具只读且严格限定到选定患者,返回 schema `1.0` 的统一响应封装和稳定的
`FHIR:<resourceType>/<id>` 证据引用。它会明确列出 `missingFields`;这些字段表示
病历中“未记录”,不能解释为阴性或正常;缺少必需上下文时状态为
`INSUFFICIENT_EVIDENCE`,不会返回 `OK`。活动用药包含可识别的记录强度
`strength`。返回值不包含原始附件正文或 Base64。

例如,按患者编号获取截至指定日期的审查上下文:

```text
get_medication_review_context(patientId="DEMO-P001", asOf="2026-08-31")
```

以及五个受控 CSV 导入/状态工具:

- `validate_csv_from_url`:从平台 MinIO 预签名 URL 下载 CSV 并生成 FHIR 预览。
- `validate_csv_content`:平台只提供附件正文时,接收完整 CSV 文本并生成 FHIR 预览。
- `validate_csv_import`:校验 `uploads/inbox` 中的宽表病例并生成 FHIR 预览。
- `commit_csv_import`:用户确认后将暂存资源事务写入 SQLite。
- `get_import_status`:查询导入状态、警告、错误和资源数量。

终端 Agent 和 A2A Agent 还会直接注册三个本地文件工具;这些工具不经过 MCP:

- `list_files`:列出文件根目录内的 CSV、JSON、TXT。
- `read_text_file`:分段读取文本,单文件最大 2 MB。
- `write_text_file`:原子写入文本,最大 1 MB;覆盖需显式确认。

本地文件根目录由 `EHR_FILE_ROOT` 配置,默认为 `uploads`。工具拒绝绝对路径、
上级目录、隐藏路径和其他扩展名,不能读取 `.env` 或修改 Python 源码。

## 本地 CSV 导入

默认上传目录为:

```text
uploads/
├── inbox/       # 新上传、等待校验
├── processing/  # 已校验、等待用户确认
├── archived/    # 已成功写入数据库
├── failed/      # 格式或字段校验失败
└── reports/     # 每个任务的 JSON 校验报告
```

文件应先完整写入 `inbox`。本地 `write_text_file` 使用临时文件和原子替换,
不会让导入器读到半个 CSV。调用 `validate_csv_import` 后,文件会移动到
`processing` 或 `failed`;只有用户看过预览并明确确认后,Agent 才能调用
`commit_csv_import`。数据库提交成功后,原始文件移动到 `archived`。

当前支持一行一份门诊病历的中文宽表,必填列为 `病历编号`、`患者姓名`、
`性别`、`就诊日期`。建议同时提供 `患者编号` 和 `出生日期`;缺少患者编号时
只生成临时哈希 ID,并返回警告。每行可生成 Patient、Encounter、Composition、
Condition、体温 Observation 和血压 Observation。

导入使用 SHA-256 防止相同文件重复处理;正式资源在一个 SQLite 事务内写入。
遇到同 ID、不同内容的资源默认拒绝覆盖。多患者数据库调用
`get_patient_summary` 时必须传入 `patientId`,防止患者数据混用。
未识别的 CSV 列不会静默丢弃:校验结果会列出这些字段,并将每行原始内容
保存在 `import_job_rows.raw_json` 中,便于后续补充映射和审计。

### 从宿主平台接收上传文件

宿主平台需要把上传接口返回的 MinIO `filepath` 和原始文件名作为 MCP 工具
参数提供给模型。用户要求导入上传的 CSV 时,模型调用:

```text
validate_csv_from_url(fileUrl=<附件 filepath>, fileName=<附件原始文件名>)
```

MCP 服务会把文件原子下载到 `uploads/inbox`,随后复用本地 CSV 校验流程。
`fileUrl` 的主机必须出现在 `EHR_UPLOAD_ALLOWED_HOSTS` 中;多个主机用英文逗号
分隔,例如同一 Docker 网络使用 `EHR_UPLOAD_ALLOWED_HOSTS=minio`。工具禁止
跨主机重定向,下载大小上限与本地 CSV 导入一致,当前为 2 MB。

如果宿主平台只把附件正文放入模型上下文而不提供 `filepath`,模型应改为调用:

```text
validate_csv_content(fileName=<附件原始文件名>, csvContent=<完整原始 CSV 正文>)
```

`csvContent` 必须包含表头和全部数据行,不得总结、补写或改写。它可以是原始 CSV,
也可以是宿主平台解析附件后生成的 Markdown 表格;MCP 会按必填病历表头识别并
规范化表格,包括平台将数据行放在表头之前的情况。如果平台完全丢失表头,只有
Markdown 分隔行存在且每条记录严格匹配本服务规定的20列模板时,MCP才会补回
标准表头;列数不符或普通管道文本仍会拒绝。此兼容路径受模型上下文长度限制,
适合小型 CSV;大文件仍应优先使用 `validate_csv_from_url`。

## 运行 A2A Agent

```powershell
python mcp/a2a_server.py --host 127.0.0.1 --port 9999
```

端点:

- Agent Card:`http://127.0.0.1:9999/.well-known/agent-card.json`
- A2A JSON-RPC:`http://127.0.0.1:9999/a2a`

## 安全说明

- 病历查询连接以只读模式打开,SQL 查询也受 SQLite authorizer 限制。
- CSV 导入使用独立读写连接和固定参数化语句,不接受任意写 SQL。
- `eval_record` 仅接受 AST 白名单中的 Python 表达式,但它不是操作系统级
  强沙箱。
- 未配置 TLS、身份认证和请求限制时,不要把 HTTP MCP 或 A2A 服务直接暴露
  到公网。
- Agent 会把相关工具结果发送到 `LLM_BASE_URL`。接入真实病历前,请确认模型
  服务的数据隔离、访问控制、日志留存和合规策略。
- `.env` 已被 Git 忽略,请勿提交 API Key。目录内的 SQLite 文件是演示数据;
  替换为真实病历后应根据组织政策决定是否提交。

## 上传为独立 Git 仓库

```powershell
git init
git add .
git status
git commit -m "Initial health record agent"
```