Node.js后端集成Baichuan-M2-32B医疗问答API指南

📅 发布时间:2026/7/6 6:16:10 👁️ 浏览次数:
Node.js后端集成Baichuan-M2-32B医疗问答API指南
Node.js后端集成Baichuan-M2-32B医疗问答API指南1. 为什么选择Baichuan-M2-32B构建医疗问答服务在医疗健康领域准确、可靠且响应迅速的AI辅助系统正变得越来越重要。Baichuan-M2-32B作为百川智能推出的医疗增强推理模型专为真实世界的医疗推理任务设计它基于Qwen2.5-32B基座通过创新的大型验证器系统进行深度优化在HealthBench评测中取得了60.1分的优异成绩超越了众多开源及部分闭源模型。与通用大模型不同Baichuan-M2-32B在临床诊断思维、患者交互能力和医学常识方面表现出色。它能理解复杂的症状描述识别潜在的疾病关联并以专业但易懂的方式提供参考信息。更重要的是它支持4-bit量化在RTX4090单卡上即可高效部署这对需要控制硬件成本的医疗应用开发团队来说是个关键优势。Node.js作为现代Web后端开发的主流选择凭借其非阻塞I/O特性和丰富的生态系统非常适合构建高并发、低延迟的API服务。将Baichuan-M2-32B与Node.js结合不仅能快速搭建起一个稳定可靠的医疗问答接口还能轻松集成JWT鉴权、流式响应等现代Web开发必备功能为后续构建患者管理平台、在线问诊系统或健康知识库打下坚实基础。2. 环境准备与模型服务部署2.1 Node.js安装及环境配置在开始集成之前确保你的开发环境已正确配置。Node.js版本建议使用18.x或20.x长期支持版本这些版本对现代JavaScript特性和异步处理提供了最佳支持。首先检查当前Node.js和npm版本node --version npm --version如果需要安装或升级推荐使用nvmNode Version Manager来管理多个Node.js版本# macOS/Linux安装nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # Windows用户可下载Node.js官方安装包或使用nvm-windows安装完成后创建项目目录并初始化mkdir medical-ai-api cd medical-ai-api npm init -y安装核心依赖npm install express axios cors helmet morgan jsonwebtoken bcryptjs dotenv npm install --save-dev nodemon2.2 模型服务部署方案选择Baichuan-M2-32B有多种部署方式根据你的硬件条件和性能需求选择最适合的方案方案一vLLM高性能推理服务推荐vLLM是目前最高效的LLM推理引擎之一特别适合生产环境。它支持Baichuan-M2-32B的GPTQ-Int4量化版本能在单张RTX4090上实现高吞吐量。安装vLLM并启动服务pip install vllm --pre --extra-index-url https://wheels.vllm.ai/nightly # 启动API服务使用GPTQ量化版本 vllm serve baichuan-inc/Baichuan-M2-32B-GPTQ-Int4 \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --max-model-len 32768 \ --enable-prefix-caching方案二Xinference轻量级部署Xinference提供了更友好的CLI界面适合快速验证和中小规模部署pip install xinference # 启动Xinference服务 xinference-local --host 0.0.0.0 --port 9997 # 在另一个终端中注册并启动模型 xinference launch --model-engine vllm \ --model-name Baichuan-M2 \ --size-in-billions 32 \ --model-format gptq \ --quantization Int4方案三本地Transformers加载开发调试用对于开发和调试阶段可以直接使用Transformers库加载模型但请注意这需要大量GPU显存from transformers import AutoTokenizer, AutoModelForCausalLM import torch model AutoModelForCausalLM.from_pretrained( baichuan-inc/Baichuan-M2-32B, trust_remote_codeTrue, device_mapauto, torch_dtypetorch.float16 ) tokenizer AutoTokenizer.from_pretrained(baichuan-inc/Baichuan-M2-32B)无论选择哪种方案最终目标都是获得一个可通过HTTP访问的OpenAI兼容API端点如http://localhost:8000/v1/chat/completions。3. RESTful API接口设计与实现3.1 核心路由与请求结构我们采用Express框架构建RESTful API遵循清晰的资源命名规范。医疗问答服务的核心是/api/v1/medical/chat端点它接受标准的聊天消息格式。创建主应用文件app.jsconst express require(express); const cors require(cors); const helmet require(helmet); const morgan require(morgan); const { createMedicalChatRouter } require(./routes/medicalChat); const app express(); const PORT process.env.PORT || 3000; // 安全中间件 app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: [self], scriptSrc: [self, unsafe-inline], styleSrc: [self, unsafe-inline] } } })); // CORS配置 app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(,) || [http://localhost:3000], credentials: true })); // 日志记录 app.use(morgan(combined)); // 解析JSON请求体 app.use(express.json({ limit: 10mb })); app.use(express.urlencoded({ extended: true, limit: 10mb })); // 健康检查端点 app.get(/health, (req, res) { res.json({ status: ok, timestamp: new Date().toISOString(), version: 1.0.0 }); }); // 医疗问答路由 app.use(/api/v1/medical, createMedicalChatRouter()); // 404处理 app.use(*, (req, res) { res.status(404).json({ error: Route not found }); }); // 全局错误处理 app.use((err, req, res, next) { console.error(Unhandled error:, err); res.status(500).json({ error: Internal server error }); }); app.listen(PORT, () { console.log(Medical AI API server running on port ${PORT}); }); module.exports app;3.2 医疗问答路由实现创建routes/medicalChat.js文件实现核心的问答逻辑const express require(express); const axios require(axios); const { verifyToken } require(../middleware/auth); const { validateMedicalRequest } require(../middleware/validation); const createMedicalChatRouter () { const router express.Router(); // POST /api/v1/medical/chat - 处理医疗问答请求 router.post(/chat, verifyToken, validateMedicalRequest, async (req, res) { try { const { messages, stream false, max_tokens 2048, temperature 0.3 } req.body; // 构建vLLM兼容的请求体 const vllmRequestBody { model: baichuan-m2-32b, messages: messages.map(msg ({ role: msg.role, content: msg.content })), stream, max_tokens, temperature, top_p: 0.95, presence_penalty: 0.1, frequency_penalty: 0.1 }; // 调用vLLM服务 const vllmResponse await axios.post( http://localhost:8000/v1/chat/completions, vllmRequestBody, { headers: { Content-Type: application/json, Authorization: Bearer ${process.env.VLLM_API_KEY || dummy-key} }, timeout: 300000 // 5分钟超时医疗推理可能需要较长时间 } ); // 处理流式响应 if (stream) { res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); res.setHeader(Connection, keep-alive); vllmResponse.data.on(data, (chunk) { try { const data chunk.toString().trim(); if (data.startsWith(data:)) { const jsonStr data.substring(5).trim(); if (jsonStr jsonStr ! [DONE]) { const parsed JSON.parse(jsonStr); const content parsed.choices?.[0]?.delta?.content || ; if (content) { res.write(data: ${JSON.stringify({ content })}\n\n); } } } } catch (e) { console.error(Error parsing stream chunk:, e); } }); vllmResponse.data.on(end, () { res.write(data: [DONE]\n\n); res.end(); }); vllmResponse.data.on(error, (err) { console.error(Stream error:, err); res.status(500).json({ error: Stream processing error }); }); return; } // 处理普通响应 const { choices } vllmResponse.data; const responseMessage choices?.[0]?.message?.content || 抱歉我无法生成回答。; // 添加医疗免责声明 const finalResponse ${responseMessage}\n\n **医疗免责声明**本回答仅供参考不能替代专业医疗诊断和治疗建议。如有紧急健康问题请立即联系医生或前往医院就诊。; res.json({ success: true, message: finalResponse, usage: vllmResponse.data.usage || {}, timestamp: new Date().toISOString() }); } catch (error) { console.error(Medical chat error:, error.response?.data || error.message); if (error.response?.status 429) { return res.status(429).json({ error: 请求过于频繁请稍后再试 }); } if (error.code ECONNREFUSED || error.code ETIMEDOUT) { return res.status(503).json({ error: 模型服务暂时不可用请检查vLLM服务状态 }); } res.status(500).json({ error: 处理医疗问答请求时发生错误 }); } }); return router; }; module.exports { createMedicalChatRouter };3.3 请求验证与安全中间件创建middleware/validation.js确保传入的医疗请求符合规范const { body, validationResult } require(express-validator); // 验证医疗问答请求 exports.validateMedicalRequest [ body(messages) .isArray({ min: 1, max: 20 }) .withMessage(messages必须是包含1-20条消息的数组), body(messages.*.role) .isIn([user, assistant, system]) .withMessage(消息角色必须是user、assistant或system之一), body(messages.*.content) .isString() .isLength({ min: 1, max: 4000 }) .withMessage(消息内容长度必须在1-4000字符之间), body(messages.*.content) .custom((content) { // 检查是否包含明显的医疗紧急情况关键词 const emergencyKeywords [ 胸痛, 呼吸困难, 昏迷, 抽搐, 大出血, 严重过敏, 中风, 心梗, 休克, 窒息, 中毒, 自杀, 自残 ]; const hasEmergency emergencyKeywords.some(keyword content.includes(keyword) || content.toLowerCase().includes(keyword.toLowerCase()) ); if (hasEmergency) { throw new Error(检测到紧急医疗情况请立即拨打急救电话或前往医院); } return true; }), body(stream) .optional() .isBoolean() .withMessage(stream参数必须是布尔值), body(max_tokens) .optional() .isInt({ min: 128, max: 8192 }) .withMessage(max_tokens必须在128-8192之间), body(temperature) .optional() .isFloat({ min: 0.0, max: 1.0 }) .withMessage(temperature必须在0.0-1.0之间), // 最终验证 (req, res, next) { const errors validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ error: 请求参数验证失败, details: errors.array() }); } next(); } ];4. JWT鉴权与用户认证系统4.1 JWT令牌生成与验证医疗数据敏感性要求严格的访问控制。我们使用JWTJSON Web Token实现无状态认证确保只有授权用户才能访问医疗问答API。创建middleware/auth.jsconst jwt require(jsonwebtoken); const bcrypt require(bcryptjs); const { promisify } require(util); // JWT密钥应从环境变量读取生产环境务必使用强密钥 const JWT_SECRET process.env.JWT_SECRET || medical-ai-secret-key-change-in-production; const JWT_EXPIRE process.env.JWT_EXPIRE || 30d; // 生成JWT令牌 exports.generateToken (userId, username, role user) { return jwt.sign( { userId, username, role }, JWT_SECRET, { expiresIn: JWT_EXPIRE } ); }; // JWT验证中间件 exports.verifyToken async (req, res, next) { try { let token; // 从Authorization头获取token if ( req.headers.authorization req.headers.authorization.startsWith(Bearer) ) { token req.headers.authorization.split( )[1]; } // 从cookie获取token可选 if (!token req.cookies req.cookies.token) { token req.cookies.token; } // 如果没有token返回401 if (!token) { return res.status(401).json({ error: 访问被拒绝缺少认证令牌 }); } // 验证token const decoded await promisify(jwt.verify)(token, JWT_SECRET); // 将解码后的用户信息附加到请求对象 req.user decoded; next(); } catch (error) { console.error(JWT验证错误:, error); if (error.name TokenExpiredError) { return res.status(401).json({ error: 认证令牌已过期请重新登录 }); } if (error.name JsonWebTokenError) { return res.status(401).json({ error: 无效的认证令牌 }); } res.status(401).json({ error: 认证失败 }); } }; // 角色权限检查中间件 exports.requireRole (...roles) { return (req, res, next) { if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 权限不足您没有访问此资源的权限 }); } next(); }; };4.2 用户认证路由实现创建routes/auth.js处理用户注册、登录和令牌刷新const express require(express); const bcrypt require(bcryptjs); const { generateToken, verifyToken } require(../middleware/auth); const { body, validationResult } require(express-validator); const createAuthRouter () { const router express.Router(); // POST /api/v1/auth/register - 用户注册 router.post(/register, [ body(username).isLength({ min: 3, max: 30 }).withMessage(用户名长度必须在3-30个字符之间), body(email).isEmail().normalizeEmail().withMessage(请输入有效的邮箱地址), body(password).isLength({ min: 8 }).withMessage(密码至少需要8个字符), body(confirmPassword).custom((value, { req }) { if (value ! req.body.password) { throw new Error(两次输入的密码不一致); } return true; }) ], async (req, res) { try { const errors validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ error: 注册信息验证失败, details: errors.array() }); } const { username, email, password } req.body; // 这里应该连接数据库检查用户是否已存在 // 为简化示例我们模拟一个内存中的用户存储 const existingUser mockUsers.find(u u.email email); if (existingUser) { return res.status(400).json({ error: 该邮箱已被注册 }); } // 密码哈希 const salt await bcrypt.genSalt(12); const hashedPassword await bcrypt.hash(password, salt); // 创建新用户实际项目中应保存到数据库 const newUser { id: Date.now().toString(), username, email, password: hashedPassword, role: user, createdAt: new Date().toISOString() }; mockUsers.push(newUser); // 生成JWT令牌 const token generateToken(newUser.id, newUser.username, newUser.role); // 设置HTTP-only cookie前端无需手动处理token res.cookie(token, token, { httpOnly: true, secure: process.env.NODE_ENV production, maxAge: 30 * 24 * 60 * 60 * 1000, // 30天 sameSite: strict }); res.status(201).json({ success: true, message: 用户注册成功, user: { id: newUser.id, username: newUser.username, email: newUser.email, role: newUser.role } }); } catch (error) { console.error(注册错误:, error); res.status(500).json({ error: 注册过程中发生错误 }); } }); // POST /api/v1/auth/login - 用户登录 router.post(/login, [ body(email).isEmail().normalizeEmail().withMessage(请输入有效的邮箱地址), body(password).exists().withMessage(密码不能为空) ], async (req, res) { try { const errors validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ error: 登录信息验证失败, details: errors.array() }); } const { email, password } req.body; // 查找用户实际项目中应查询数据库 const user mockUsers.find(u u.email email); if (!user) { return res.status(401).json({ error: 邮箱或密码错误 }); } // 验证密码 const isMatch await bcrypt.compare(password, user.password); if (!isMatch) { return res.status(401).json({ error: 邮箱或密码错误 }); } // 生成JWT令牌 const token generateToken(user.id, user.username, user.role); // 设置HTTP-only cookie res.cookie(token, token, { httpOnly: true, secure: process.env.NODE_ENV production, maxAge: 30 * 24 * 60 * 60 * 1000, sameSite: strict }); res.json({ success: true, message: 登录成功, user: { id: user.id, username: user.username, email: user.email, role: user.role } }); } catch (error) { console.error(登录错误:, error); res.status(500).json({ error: 登录过程中发生错误 }); } }); // POST /api/v1/auth/logout - 用户登出 router.post(/logout, (req, res) { // 清除cookie res.clearCookie(token); res.json({ success: true, message: 已成功登出 }); }); // GET /api/v1/auth/me - 获取当前用户信息 router.get(/me, verifyToken, (req, res) { res.json({ success: true, user: { id: req.user.userId, username: req.user.username, role: req.user.role, exp: req.user.exp } }); }); return router; }; // 模拟用户存储实际项目中应使用数据库 const mockUsers []; module.exports { createAuthRouter };5. 流式响应处理与用户体验优化5.1 流式响应的完整实现流式响应对于医疗问答场景至关重要它能让用户在模型生成答案的过程中就看到部分内容显著提升感知响应速度。我们已经在医疗问答路由中实现了基本的流式处理现在需要完善前端友好的响应格式。创建utils/streamHandler.js/** * 流式响应处理器 * 将vLLM的SSE流转换为前端友好的格式 */ class StreamHandler { constructor(res) { this.res res; this.isFirstChunk true; } /** * 初始化流式响应头 */ init() { this.res.setHeader(Content-Type, text/event-stream); this.res.setHeader(Cache-Control, no-cache); this.res.setHeader(Connection, keep-alive); this.res.setHeader(X-Accel-Buffering, no); // Nginx兼容 } /** * 发送数据块 * param {string} content - 要发送的内容 * param {Object} metadata - 可选的元数据 */ send(content, metadata {}) { try { const data { content, timestamp: new Date().toISOString(), ...metadata }; // 第一个块添加特殊标识 if (this.isFirstChunk) { data.firstChunk true; this.isFirstChunk false; } this.res.write(data: ${JSON.stringify(data)}\n\n); } catch (error) { console.error(流式响应写入错误:, error); this.res.write(event: error\n); this.res.write(data: ${JSON.stringify({ error: error.message })}\n\n); } } /** * 发送完成事件 */ complete() { this.res.write(event: complete\n); this.res.write(data: {status: completed}\n\n); this.res.end(); } /** * 发送错误事件 * param {string} errorMessage - 错误信息 */ error(errorMessage) { this.res.write(event: error\n); this.res.write(data: {error: ${errorMessage}}\n\n); this.res.end(); } } module.exports { StreamHandler };更新医疗问答路由以使用新的流式处理器// 在medicalChat.js中添加对StreamHandler的引用 const { StreamHandler } require(../utils/streamHandler); // 在流式响应处理部分替换为 if (stream) { const streamHandler new StreamHandler(res); streamHandler.init(); vllmResponse.data.on(data, (chunk) { try { const data chunk.toString().trim(); if (data.startsWith(data:)) { const jsonStr data.substring(5).trim(); if (jsonStr jsonStr ! [DONE]) { const parsed JSON.parse(jsonStr); const content parsed.choices?.[0]?.delta?.content || ; if (content) { streamHandler.send(content, { finish_reason: parsed.choices?.[0]?.finish_reason }); } } } } catch (e) { console.error(Error parsing stream chunk:, e); streamHandler.error(解析流式响应时发生错误); } }); vllmResponse.data.on(end, () { streamHandler.complete(); }); vllmResponse.data.on(error, (err) { console.error(Stream error:, err); streamHandler.error(流式响应处理失败); }); return; }5.2 前端集成示例为了展示如何在前端使用这个流式API这里提供一个简单的HTML页面示例!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title医疗AI助手/title style .chat-container { max-width: 800px; margin: 0 auto; padding: 20px; } .message { margin-bottom: 15px; padding: 10px; border-radius: 8px; } .user-message { background-color: #e3f2fd; margin-left: 20%; } .ai-message { background-color: #f5f5f5; margin-right: 20%; } .typing-indicator { display: inline-block; margin-left: 10px; } .typing-indicator span { display: inline-block; width: 8px; height: 8px; background-color: #666; border-radius: 50%; margin-right: 4px; animation: typing 1.4s infinite; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } keyframes typing { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } /style /head body div classchat-container h1 医疗AI助手/h1 div idchat-messages/div div input typetext iduser-input placeholder描述您的症状或健康问题... stylewidth: 70%; padding: 10px; button onclicksendMessage()发送/button /div /div script async function sendMessage() { const input document.getElementById(user-input); const messagesDiv document.getElementById(chat-messages); if (!input.value.trim()) return; // 显示用户消息 messagesDiv.innerHTML div classmessage user-message${input.value}/div; input.value ; // 显示AI正在思考的指示器 messagesDiv.innerHTML div classmessage ai-messageAI正在思考中... div classtyping-indicatorspan/spanspan/spanspan/span/div/div; messagesDiv.scrollTop messagesDiv.scrollHeight; try { const response await fetch(/api/v1/medical/chat, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer YOUR_JWT_TOKEN // 实际项目中应从cookie或localStorage获取 }, body: JSON.stringify({ messages: [{ role: user, content: input.value }], stream: true }) }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const reader response.body.getReader(); const decoder new TextDecoder(); let accumulatedContent ; while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); const lines chunk.split(\n); for (const line of lines) { if (line.startsWith(data: )) { try { const data JSON.parse(line.substring(6)); if (data.content) { accumulatedContent data.content; // 更新AI消息显示 const aiMessage messagesDiv.lastChild; aiMessage.innerHTML AI: ${accumulatedContent} div classtyping-indicatorspan/spanspan/spanspan/span/div; messagesDiv.scrollTop messagesDiv.scrollHeight; } } catch (e) { console.error(解析数据块错误:, e); } } } } // 移除打字指示器 const aiMessage messagesDiv.lastChild; aiMessage.innerHTML AI: ${accumulatedContent}; } catch (error) { console.error(发送消息错误:, error); const aiMessage messagesDiv.lastChild; aiMessage.innerHTML AI: 对不起处理您的请求时发生了错误。${error.message}; } } // 支持回车发送 document.getElementById(user-input).addEventListener(keypress, (e) { if (e.key Enter) { sendMessage(); } }); /script /body /html6. 生产环境部署与监控6.1 Docker容器化部署为了确保开发、测试和生产环境的一致性我们使用Docker进行容器化部署。创建DockerfileFROM node:20-alpine # 设置工作目录 WORKDIR /app # 复制package.json和package-lock.json以利用Docker缓存 COPY package*.json ./ # 安装依赖 RUN npm ci --onlyproduction # 复制应用代码 COPY . . # 创建非root用户提高安全性 RUN addgroup -g 1001 -f nodejs adduser -S nextjs -u 1001 # 切换到非root用户 USER nextjs # 暴露端口 EXPOSE 3000 # 启动应用 CMD [npm, start]创建docker-compose.yml用于多服务编排version: 3.8 services: # 医疗AI API服务 medical-api: build: . restart: unless-stopped ports: - 3000:3000 environment: - NODE_ENVproduction - PORT3000 - JWT_SECRET${JWT_SECRET} - JWT_EXPIRE30d - ALLOWED_ORIGINShttp://localhost:3000,https://yourdomain.com - VLLM_API_KEY${VLLM_API_KEY} depends_on: - vllm-service networks: - medical-network # vLLM模型服务 vllm-service: image: vllm/vllm-cpu:latest restart: unless-stopped ports: - 8000:8000 command: vllm serve baichuan-inc/Baichuan-M2-32B-GPTQ-Int4 --host 0.0.0.0 --port 8000 --tensor-parallel-size 1 --max-model-len 32768 --enable-prefix-caching volumes: - ./models:/root/.cache/huggingface/hub environment: - HF_HOME/root/.cache/huggingface/hub deploy: resources: limits: memory: 24g cpus: 4 networks: - medical-network # Redis缓存可选用于会话存储 redis: image: redis:7-alpine restart: unless-stopped ports: - 6379:6379 networks: - medical-network networks: medical-network: driver: bridge6.2 日志与监控配置创建utils/logger.js实现结构化日志记录const winston require(winston); const { format } winston; // 创建日志格式 const logFormat format.combine( format.timestamp({ format: YYYY-MM-DD HH:mm:ss }), format.errors({ stack: true }), format.json() ); // 创建日志器 const logger winston.createLogger({ level: process.env.LOG_LEVEL || info, format: logFormat, defaultMeta: { service: medical-ai-api }, transports: [ // 控制台日志 new winston.transports.Console({ format: format.combine( format.colorize(), format.simple() ) }), // 文件日志生产环境 ...(process.env.NODE_ENV production ? [ new winston.transports.File({ filename: logs/error.log, level: error }), new winston.transports.File({ filename: logs/combined.log }) ] : []) ] }); // 请求日志中间件 exports.requestLogger (req, res, next) { const start Date.now(); res.on(finish, () { const duration Date.now() - start; const logData { method: req.method, url: req.url, status: res.statusCode, duration: ${duration}ms, ip: req.ip, userAgent: req.get(User-Agent), referer: req.get(Referer) }; if (res.statusCode 400) { logger.error(Request failed, logData); } else { logger.info(Request completed, logData); } }); next(); }; module.exports logger;6.3 性能优化与最佳实践在生产环境中还需要考虑以下优化措施数据库连接池如果应用需要访问数据库使用连接池管理数据库连接const { Pool } require(pg); const pool new Pool({ connectionString: process.env.DATABASE_URL, max: 20, // 最大连接数 min: 5, // 最小连接数 idleTimeoutMillis: 30000, // 空闲连接超时 connectionTimeoutMillis: 2000, // 连接超时 }); // 使用连接池执行查询 exports.query (text, params) pool.query(text, params);缓存策略对常见医疗问题的回答进行缓存减少模型调用频率const NodeCache require(node-cache); const cache new NodeCache({ stdTTL: 3600, checkperiod: 120 }); // 缓存1小时 // 在医疗问答路由中添加缓存逻辑 const cacheKey medical