
一、整体架构:用 Go 做“中心大脑”
您提出的四层架构非常经典且精准。我们的核心就是要把 Go 语言的优势发挥在「业务服务层」和「集成层」,让它成为整个 AI 流程中那个最可靠、最高效的“交通枢纽”和“中央处理器”。
1.1 架构全景图(文字版)
为了便于理解,我们先在脑海里构建一张架构图:
+----------------+ +----------------+ +----------------+ +------------------+
| 采集层 | | AI 处理层 | | 业务服务层 | | 集成层 |
| (IoT / App) |----->| (ASR / LLM) |<----->| (Go Core) |----->| (HIS / EMR / FHIR)|
| | | | | | | |
| - 诊室麦克风 | | - 科大讯飞/阿里 | | - gateway | | - HL7 v2/v3 |
| - 医生 App | | - OpenAI GPT-4 | | - session | | - FHIR R4 |
| - 患者小程序 | | - 自研医疗模型 | | - nlp-service | | - 厂商私有 API |
| - 床旁交互屏 | | | | - emr-service | | |
| - 护士工作站 | | | | - audit | | |
+----------------+ +----------------+ +----------------+ +------------------+
^ ^ ^ ^
| 音频流 | 文本/结构化数据 | RPC/HTTP 请求 | API 调用
| | | |
(医院内网 / 专线) (GPU 集群 / 公有云) (K8s 集群 / 裸金属) (医院数据中心)
1.2 Go 的定位与核心价值
为什么 Go 是这个“中心大脑”的最佳人选?
高并发与高性能:一个中型医院可能有上百个诊室、上千个床位同时进行语音采集。Go 的 Goroutine 和 Channel 模型能以极低的资源开销处理成千上万的并发连接(WebSocket 长连接、gRPC 流),这是 Python 等传统解释型语言难以比拟的。微服务生态的天然契合:Go 的编译速度快、部署简单(单一二进制文件)、内置强大网络库。结合 、
gRPC、
protobuf 等框架,可以轻松构建出清晰、高效、松耦合的微服务集群。稳定性与可维护性:Go 的静态类型和强约束语法,能在编译期发现大量潜在错误。这对于需要 7×24 小时稳定运行、且不容有失的医疗系统至关重要。一个由 Go 构建的后端,在长期维护上具有显著优势。“胶水语言”的卓越能力:AI 模型(ASR, NLP)多半是 Python 实现,集成层(HIS/EMR)接口五花八门。Go 极其擅长作为“胶水”,通过标准化的 HTTP/gRPC 客户端与这些异构系统进行高效、可靠的通信,并进行协议转换、数据聚合与分发。
Gin
1.3 数据流与控制流
让我们以一个数据包的生命周期,来理解 Go 在其中的作用:
数据上行:
->
音频数据 -> (WebSocket) ->
采集层 (Go, 负载均衡、鉴权) -> (gRPC Streaming) ->
gateway-service (Go, 音频缓冲、转发) -> (HTTP/WebSocket) ->
asr-adapter-service (ASR 引擎)。
AI 处理层
AI 处理与下行:
->
ASR 引擎 -> (HTTP Callback/gRPC Stream) ->
转写文本结果 (Go, 组装结果、时间戳对齐) -> (消息队列) ->
asr-adapter-service (Go, 持久化存储) &
session-service (Go, 拉取对话,触发 NLP)。
nlp-medical-service
业务处理与集成:
->
nlp-medical-service ->
调用 LLM API ->
LLM 返回结构化病历 (Go, 校验、模板渲染) ->
nlp-medical-service ->
gateway-service (医生编辑) ->
前端展示 ->
医生提交 ->
gateway-service (Go, 调用 HIS/EMR API) ->
emr-integration-service。
HIS/EMR 系统
在这个流程中,Go 服务负责了几乎所有的状态管理、流程编排、数据校验、协议转换和外部系统集成工作,是整个系统的“骨架”和“神经中枢”。
二、核心模块设计(Go 为主的服务拆分)
将庞大的系统拆分为职责单一的小服务,是“真落地”的第一步。这便于团队并行开发、独立部署和故障隔离。
2.1 服务拆分粒度原则
按业务能力拆分:每个服务对应一个明确的业务领域(如“会话管理”、“EMR集成”)。数据所有权分离:每个服务拥有自己独立的数据库表,避免跨服务的数据库直接耦合。高内聚、低耦合:服务内部逻辑紧密关联,服务间通过明确的 API(gRPC/REST)通信。可独立运维:单个服务可以独立进行扩缩容、升级和监控。
2.2 详细服务设计
1.
gateway-service (API 网关)
gateway-service
职责:
系统的统一入口,所有来自外部的请求都必须经过它。身份认证与鉴权(验证医生/护士的 Token)。权限控制(基于 Casbin,判断该医生是否有权限访问该患者的数据)。请求路由(将 的请求转发给
/api/v1/sessions)。流量控制与熔断(防止恶意请求或后端服务雪崩)。请求/响应日志记录。
session-service
Go 技术选型:
Web 框架: 或
Gin。性能极高,中间件生态丰富。
Echo 社区更大,文档更全,可作为首选。鉴权:
Gin 库处理 JWT Token 的生成和解析。权限控制:
golang-jwt/jwt。强大的访问控制框架,支持 ACL、RBAC 等多种模型。我们可以将权限策略(如
Casbin)存储在数据库或配置文件中。服务发现:如果使用 Kubernetes,可以利用其 Service 机制。如果是裸机部署,可以引入
doctor, 123, patient, 456, read 或
Consul。限流熔断:
Etcd 或
go-kratos/aegis。
sentinel-golang
核心接口示例:
// POST /api/v1/sessions
// Body: {"patientId": "P12345", "deptId": "D01", "type": "outpatient"}
// Response: {"sessionId": "sess_xxx", "wsUrl": "wss://.../asr/sess_xxx"}
func (h *SessionHandler) CreateSession(c *gin.Context) { ... }
// WebSocket /ws/asr/{sessionId}
// 用于前端建立音频流长连接
func (h *ASRHandler) HandleAudioStream(c *gin.Context) { ... }
2.
session-service (会话 & 录音管理)
session-service
职责:
核心业务实体管理:(问诊/查房会话)、
Session(对话轮次)、
Turn(录音文件)。创建、查询、更新、结束会话。绑定会话与参与者(医生、患者、科室)。管理录音文件元数据(存储地址、时长、格式、MD5)。与
Recording 协同,将转写结果与
asr-adapter-service 关联。
Turn
Go 技术选型:
框架:。作为内部核心服务,gRPC 的高性能和强类型接口是首选。数据库:
gRPC。强一致性,支持 JSONB 字段,非常适合存储半结构化的转写文本和病历草稿。ORM:
PostgreSQL。功能强大,社区活跃,能极大提高数据库操作效率。消息队列:
GORM 或
NATS。用于接收来自
RabbitMQ 的转写结果,实现异步处理,避免阻塞。
asr-adapter-service
Proto 定义示例:
service SessionService {
rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse);
rpc GetSession(GetSessionRequest) returns (GetSessionResponse);
rpc EndSession(EndSessionRequest) returns (EndSessionResponse);
rpc AddTranscription(AddTranscriptionRequest) returns (google.protobuf.Empty);
}
message Session {
string session_id = 1;
string doctor_id = 2;
string patient_id = 3;
string department_id = 4;
SessionType type = 5; // OUTPATIENT, INPATIENT_WARD
google.protobuf.Timestamp start_time = 6;
google.protobuf.Timestamp end_time = 7;
SessionStatus status = 8; // ACTIVE, ENDED, PROCESSING, COMPLETED
}
3.
asr-adapter-service (语音识别适配层)
asr-adapter-service
职责:
纯粹的适配器,不执行任何 ASR 算法。通过 WebSocket 接收来自前端的音频流。处理音频格式转换(如前端 WebM 转 PCM)、分帧。建立与 ASR 引擎(云服务或自建)的连接。流量整形与容错:
将前端音频流可靠地转发给 ASR 引擎。处理 ASR 引擎的部分转写结果和最终结果。实现断线重连、心跳检测机制。当 ASR 引擎响应超时或失败时,发送错误通知。
将带有时间戳的最终转写文本,通过消息队列发送给 。
session-service
Go 技术选型:
WebSocket:,事实上的标准库。并发模型:每个 WebSocket 连接一个 Goroutine,完美适配 Go 模型。HTTP 客户端:标准库
gorilla/websocket 或
net/http(如果对性能有极致要求)。配置管理:
fasthttp,用于动态加载不同 ASR 厂商的 API Key、URL 等配置。
Viper
核心逻辑伪代码:
func (s *ASRServer) HandleWS(w http.ResponseWriter, r *http.Request) {
conn, _ := upgrader.Upgrade(w, r, nil)
defer conn.Close()
// 1. 从路径中获取 sessionId
sessionId := ...
// 2. 建立 ASR 引擎连接
asrClient := s.createASRClient(sessionId)
// 3. 启动两个 goroutine 进行双向数据转发
// a) 读取前端音频,发给 ASR
go func() {
for {
messageType, audioData, err := conn.ReadMessage()
if err != nil { break }
asrClient.SendAudio(audioData)
}
}()
// b) 读取 ASR 结果,发给前端并持久化
for {
result, err := asrClient.ReceiveResult()
if err != nil { break }
// 发送实时字幕给前端
conn.WriteJSON(result)
// 如果是最终结果,发给 session-service
if result.IsFinal {
s.producer.Publish("transcription.queue", result)
}
}
}
4.
nlp-medical-service (医疗文本理解 & 病历草稿生成)
nlp-medical-service
职责:
LLM/医疗 NLP 模型的“指挥官”。订阅 发出的“会话结束”消息。拉取完整的对话文本(带说话人标签 D/P)。Prompt Engineering:构建高质量的 Prompt,指导 LLM 生成符合医学规范的病历。Prompt 中应包含:
session-service
角色定义:“你是一名资深内科医生…”任务描述:“根据以下医患对话,生成一份结构化的门诊病历初稿…”输出格式要求:严格的 JSON Schema,定义 ,
chief_complaint 等字段。对话原文。(可选)患者历史信息摘要。
history_of_present_illness
调用 LLM API(OpenAI, Azure, 或自建模型)。结果后处理与校验:
解析 LLM 返回的 JSON。如果解析失败,进行重试或降级处理。根据内置规则进行校验(例如,主诉不能为空,诊断编码格式正确)。进行术语标准化(如“感冒” -> “上呼吸道感染”)。将结构化结果存入数据库,并更新 状态为
session。
COMPLETED
Go 技术选型:
LLM 客户端: 库(如果使用 OpenAI 系列),或自行封装 HTTP Client 调用其他 API。JSON Schema 校验:
go-openai 库,可以验证 LLM 返回的 JSON 是否符合预期结构。规则引擎:可以自己实现一个简单的
jsonschema 来做术语标准化,复杂场景可引入
map[string]func(string)string。消息队列消费者:
go-ruleengine (NATS Streaming) 或
stan.go (RabbitMQ)。
amqp
接口抽象(关键!):
为了方便切换不同的 LLM 供应商,定义接口是必须的:
type LLMClient interface {
GenerateEMR(ctx context.Context, req *GenerateEMRRequest) (*GenerateEMRResponse, error)
}
type GenerateEMRRequest struct {
Dialog []Turn `json:"dialog"`
TemplateID string `json:"template_id"` // "outpatient_respiratory"
PatientInfo *PatientInfo `json:"patient_info,omitempty"`
}
type GenerateEMRResponse struct {
Draft *EMRDraft `json:"draft"`
Raw string `json:"raw"` // LLM 原始返回,用于调试
}
// --- 实现一个 OpenAI 客户端 ---
type OpenAIClient struct {
client *openai.Client
}
func (c *OpenAIClient) GenerateEMR(ctx context.Context, req *GenerateEMRRequest) (*GenerateEMRResponse, error) {
// 1. 构建 Prompt
prompt := buildPrompt(req)
// 2. 调用 OpenAI API
resp, err := c.client.CreateChatCompletion(...)
// 3. 解析响应,填充 GenerateEMRResponse
// ...
}
// --- 在 service 中使用 ---
type NLPService struct {
llmClient LLMClient
}
// 通过依赖注入,可以轻松切换 LLMClient 的具体实现
5.
emr-integration-service (EMR 集成服务)
emr-integration-service
职责:
“翻译官”和“信使”,负责将我们的数据“翻译”成目标系统能懂的语言,并可靠地送达。维护与多家医院、多种 EMR 系统的适配器。字段映射:将我们的 JSON 结构,映射到目标 EMR 的字段。这通常需要复杂的配置。协议转换:处理 REST API、SOAP、HL7 v2 MLLP、FHIR 等不同协议。调用 EMR 接口并处理响应。提供“补偿机制”:如果写入失败(如 EMR 系统维护),自动重试,或转为人工介入任务。记录每一次调用的详细日志和回执。
EMRDraft
Go 技术选型:
HTTP/REST 客户端: 或
net/http。SOAP 客户端:
resty。HL7 v2:
github.com/hooklift/gowsoapservice 或更专业的库(可能需要寻找或自建)来解析和构建 HL7 消息。FHIR:
github.com/ghetzel/go-stockutil/stringutil 提供了数据结构定义,配合 HTTP 客户端使用。任务调度与重试:
github.com/samply/golang-fhir-models 或
Asynq。将 EMR 写入操作作为异步任务,支持延时重试、死信队列。
go-queue
核心设计 – 适配器模式:
type EMRWriter interface {
Write(ctx context.Context, session *Session, draft *EMRDraft) (*WriteResult, error)
GetCapabilities() *Capabilities // 返回该EMR支持的功能
}
// 为某家医院的某款EMR实现适配器
type VendorAEMRWriter struct {
baseURL string
apiKey string
mapping map[string]string // 字段映射配置
}
func (w *VendorAEMRWriter) Write(ctx context.Context, session *Session, draft *EMRDraft) (*WriteResult, error) {
// 1. 根据 mapping 将 draft 转换为 VendorA API 需要的格式
payload := w.mapToVendorAFormat(session, draft)
// 2. 调用 VendorA 的 REST API
resp, err := resty.R().SetBody(payload).Post(w.baseURL + "/api/emr")
// 3. 解析响应,返回统一的 WriteResult
return &WriteResult{Success: err == nil, Message: resp.String()}, nil
}
// --- 在 service 中使用 ---
type IntegrationService struct {
// 假设我们对接多家医院
writers map[string]EMRWriter // key: hospital_id
}
6.
audit-log & monitoring-service (审计 / 日志 / 监控)
audit-log & monitoring-service
职责:
数据不可篡改的记录者。所有关键操作(创建会话、生成病历、写入EMR)都必须有日志。接收来自其他服务的审计事件(通过消息队列)。将审计日志写入独立的、高可靠性的存储(如 Elasticsearch 或专门的日志数据库)。监控大脑:
自身暴露 接口(Prometheus 格式)。聚合其他服务的指标。配置告警规则(如病历生成失败率超过5%)。
/metrics
Go 技术选型:
日志库: 或
zap。高性能、结构化日志,是 Go 服务首选。Metrics 库:
zerolog。业界标准。存储:
prometheus/client_golang。强大的日志检索和分析能力,配合
Elasticsearch 做可视化。Tracing:
Kibana。实现分布式链路追踪,定位性能瓶颈和错误根源。
OpenTelemetry-Go
三、Go 技术选型(总览与深化)
这一节,我们汇总并深化之前提到的技术选型,形成一个完整的技术栈。
| 分类 | 技术 | 选择理由 | Go 相关库/工具 |
|---|---|---|---|
| Web/API | REST API | 对外提供标准接口,前端调用方便 | / |
| gRPC | 内部服务间通信,高性能、强类型 | , |
|
| WebSocket | 实时音频流、实时字幕推送 | |
|
| 数据存储 | SQL (PostgreSQL) | 存储核心业务数据,支持事务和JSONB | , |
| Cache (Redis) | 存储会话状态、Token、热点数据 | |
|
| Object Storage (MinIO/S3) | 存放海量录音文件,成本低,易扩展 | (兼容MinIO) |
|
| Message Queue (NATS) | 服务解耦,异步处理,削峰填谷 | , |
|
| Search/Log (Elasticsearch) | 存储和检索审计日志、对话文本 | |
|
| AI 对接 | LLM Client | 调用大模型生成病历 | , |
| ASR Client | 适配多种语音识别服务 | , |
|
| 可观测性 | Metrics (Prometheus) | 收集系统性能指标,监控告警 | |
| Logging (Loki/ELK) | 集中式日志管理 | , |
|
| Tracing (Jaeger) | 分布式链路追踪 | |
|
| 安全/配置 | Auth (JWT) | 无状态认证 | |
| Permission (RBAC) | 细粒度权限控制 | |
|
| Config (Viper) | 管理不同环境下的配置 | |
|
| 部署 | Container (Docker) | 环境一致性,方便打包分发 | , |
| Orchestration (K8s) | 自动化部署、扩缩容、故障自愈 | Kubernetes | |
| CI/CD | 自动化测试、构建、部署 | , |