From fc73dd1aca80676dcaf99866da0fa1de207f9f66 Mon Sep 17 00:00:00 2001 From: gr Date: Tue, 27 Oct 2026 15:31:48 +0800 Subject: [PATCH] =?UTF-8?q?AI=20=E5=9B=9E=E5=A4=8D=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=94=A8=E8=8D=AF=E9=80=9F=E8=A7=88=EF=BC=8C=E4=B8=AD=E8=8D=AF?= =?UTF-8?q?/=E8=A5=BF=E8=8D=AF=E8=8D=AF=E5=90=8D=E6=91=98=E5=87=BA?= =?UTF-8?q?=E5=B9=B6=E9=AB=98=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ai_consult_drug_names:中药饮片、方剂/中成药、西药显式词表,长词优先识别,西药按通用名去重 - AI 回复标题下新增“用药速览”卡片:中药绿、西药蓝色块 - 正文药名同色浅底加粗高亮,剂量数字不着色;Markdown 回退路径同样保留底色 - 新增药名识别测试,更新药物剂量着色测试 Co-Authored-By: Claude Opus 5.5 --- .../ui/dialogs/ai_consult.py | 112 ++++++++++++++++ .../ui/dialogs/ai_consult_drug_names.py | 121 ++++++++++++++++++ .../ui/dialogs/ai_consult_metric_text.py | 17 ++- app/tests/test_ai_consult_drug_names.py | 29 +++++ app/tests/test_ai_consult_metric_text.py | 12 +- 5 files changed, 285 insertions(+), 6 deletions(-) create mode 100644 app/src/doctor_workstation/ui/dialogs/ai_consult_drug_names.py create mode 100644 app/tests/test_ai_consult_drug_names.py diff --git a/app/src/doctor_workstation/ui/dialogs/ai_consult.py b/app/src/doctor_workstation/ui/dialogs/ai_consult.py index f566e2d43..9ad112149 100644 --- a/app/src/doctor_workstation/ui/dialogs/ai_consult.py +++ b/app/src/doctor_workstation/ui/dialogs/ai_consult.py @@ -63,6 +63,7 @@ from shiboken6 import isValid from .. import icons from ..chat_items import parse_chat_item as _parse_chat_item from ..diagnosis_drawer import CaseGrid, _RemoteImageButton +from ..diagnosis_index_widgets import FlowWidget from ..diagnosis_media import ( ImagePreviewDialog, is_previewable_image, @@ -100,6 +101,7 @@ from .ai_consult_metric_state import ( combined_metric_state, risk_color, ) +from .ai_consult_drug_names import TCM as DRUG_TCM, WESTERN as DRUG_WESTERN, summarize_drugs from .ai_consult_metric_text import MetricTextState, metric_text_context, metric_text_html from .ai_consult_report_visuals import ( ReportVisual, @@ -812,6 +814,51 @@ QDialog#AiConsultDialog QLabel#AiConsultSideRequestStatus[tone="failed"] { background-color: #FFF1F3; border-color: #F5C2C7; } +QDialog#AiConsultDialog QFrame#AiConsultDrugStrip { + background-color: #FFFFFF; + border: 1px solid #E3E6EF; + border-radius: 12px; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugStripTitle { + color: #1A1C1F; + background-color: transparent; + font-size: 14px; + font-weight: 600; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugStripMeta { + color: #8A8B8D; + background-color: transparent; + font-size: 12px; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugKind { + min-width: 40px; + max-width: 40px; + min-height: 26px; + max-height: 26px; + color: #FFFFFF; + border-radius: 6px; + font-size: 12px; + font-weight: 600; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugKind[kind="tcm"] { background-color: #1F7A4D; } +QDialog#AiConsultDialog QLabel#AiConsultDrugKind[kind="western"] { background-color: #3347B8; } +QDialog#AiConsultDialog QLabel#AiConsultDrugChip { + min-height: 24px; + padding: 0 10px; + border-radius: 7px; + font-size: 14px; + font-weight: 700; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugChip[kind="tcm"] { + color: #1F7A4D; + background-color: #EAF7F0; + border: 1px solid #BFE6D0; +} +QDialog#AiConsultDialog QLabel#AiConsultDrugChip[kind="western"] { + color: #3347B8; + background-color: #EEF1FA; + border: 1px solid #C9D1F3; +} QDialog#AiConsultDialog QLabel#AiConsultSideRequestStatus[tone="cancelled"] { color: #9A5B00; background-color: #FFF7E6; @@ -2682,6 +2729,9 @@ def _highlight_document_metrics(document: QTextDocument) -> None: style = QTextCharFormat() style.setForeground(foreground) style.setFontWeight(fragment.charFormat().fontWeight()) + background = fragment.charFormat().background() + if background.style() != Qt.BrushStyle.NoBrush: + style.setBackground(background) cursor.setPosition(fragment.position()) cursor.setPosition(fragment.position() + fragment.length(), QTextCursor.MoveMode.KeepAnchor) cursor.mergeCharFormat(style) @@ -2712,6 +2762,59 @@ def split_report_lead(item: str) -> tuple[str, str]: return label, body +class _DrugSummaryStrip(QFrame): + """用药速览:把回复中提到的中药、西药摘成色块,医生不读正文也能一眼看到用药。""" + + def __init__(self, summary: Mapping[str, Sequence[str]], parent: QWidget | None = None) -> None: + super().__init__(parent) + self.setObjectName("AiConsultDrugStrip") + self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum) + _style_surface(self) + layout = QVBoxLayout(self) + layout.setContentsMargins(14, 11, 14, 12) + layout.setSpacing(8) + + total = sum(len(names) for names in summary.values()) + head = QHBoxLayout() + head.setContentsMargins(0, 0, 0, 0) + head.setSpacing(8) + title = QLabel("用药速览", self) + title.setObjectName("AiConsultDrugStripTitle") + head.addWidget(title) + count = QLabel(f"共 {total} 种,正文中同色高亮", self) + count.setObjectName("AiConsultDrugStripMeta") + head.addWidget(count) + head.addStretch(1) + layout.addLayout(head) + + for kind, label in ((DRUG_TCM, "中药"), (DRUG_WESTERN, "西药")): + names = list(summary.get(kind) or ()) + if not names: + continue + row = QHBoxLayout() + row.setContentsMargins(0, 0, 0, 0) + row.setSpacing(10) + tag = QLabel(label, self) + tag.setObjectName("AiConsultDrugKind") + tag.setProperty("kind", kind) + tag.setAlignment(Qt.AlignmentFlag.AlignCenter) + row.addWidget(tag, 0, Qt.AlignmentFlag.AlignTop) + chips = FlowWidget(self, horizontal_spacing=6, vertical_spacing=6) + for name in names: + chip = QLabel(name, chips) + chip.setObjectName("AiConsultDrugChip") + chip.setProperty("kind", kind) + chip.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + chips.flow.addWidget(chip) + row.addWidget(chips, 1) + layout.addLayout(row) + + @staticmethod + def for_text(text: str, parent: QWidget | None = None) -> "_DrugSummaryStrip | None": + summary = summarize_drugs(text) + return _DrugSummaryStrip(summary, parent) if any(summary.values()) else None + + class _StructuredReportPanel(QWidget): """Scan-first report for any sectioned reply. @@ -3645,6 +3748,7 @@ class _ChatBubble(QWidget): parent=self._bubble_frame, ) self._reset_clinical_panel() + drug_strip = _DrugSummaryStrip.for_text(self._raw_payload) if panel is None: document = self.body.document() layout_enabled = document.isLayoutEnabled() @@ -3660,9 +3764,17 @@ class _ChatBubble(QWidget): visual = _report_visual_widget(report_visuals, self._bubble_frame) self._standalone_visuals.append(visual) self._inner.insertWidget(1, visual) + if drug_strip is not None: + drug_strip.setParent(self._bubble_frame) + self._standalone_visuals.append(drug_strip) + self._inner.insertWidget(1, drug_strip) return False if report_visuals: panel.layout().insertWidget(1, _report_visual_widget(report_visuals, panel)) + # 用药速览紧跟标题行,排在图表与正文之前。 + if drug_strip is not None: + drug_strip.setParent(panel) + panel.layout().insertWidget(1, drug_strip) panel.setParent(self._bubble_frame) self._clinical_panel = panel self._role_name.hide() diff --git a/app/src/doctor_workstation/ui/dialogs/ai_consult_drug_names.py b/app/src/doctor_workstation/ui/dialogs/ai_consult_drug_names.py new file mode 100644 index 000000000..93b4f9c1c --- /dev/null +++ b/app/src/doctor_workstation/ui/dialogs/ai_consult_drug_names.py @@ -0,0 +1,121 @@ +"""从 AI 回复中摘出药名(中药 / 西药),供用药速览与正文高亮使用。 + +只用显式词表匹配,不按“…汤/…片”等后缀猜测,宁可漏标也不把普通词误标成药。 +匹配按长词优先,所以「六味地黄丸」不会被拆成「地黄」。 +""" + +from __future__ import annotations + +import re +from typing import NamedTuple + +TCM = "tcm" +WESTERN = "western" + +# 中药绿、西药蓝;与侧栏状态卡的完成/进行中色一致。 +DRUG_COLORS = { + TCM: ("#1F7A4D", "#EAF7F0", "#BFE6D0"), + WESTERN: ("#3347B8", "#EEF1FA", "#C9D1F3"), +} +DRUG_STYLES = { + kind: f"color: {fg}; background-color: {bg}; font-weight: 700;" + for kind, (fg, bg, _border) in DRUG_COLORS.items() +} + +# 常用中药饮片(两字及以上,避免单字误配)。 +_TCM_HERBS = """ +黄芪 党参 人参 西洋参 太子参 红参 白术 苍术 茯苓 茯神 土茯苓 猪苓 泽泻 薏苡仁 甘草 炙甘草 +当归 川芎 白芍 赤芍 熟地黄 生地黄 熟地 生地 地黄 山茱萸 山萸肉 牡丹皮 丹皮 丹参 三七 红花 桃仁 +黄连 黄芩 黄柏 栀子 知母 石膏 天花粉 葛根 麦冬 天冬 玉竹 黄精 石斛 沙参 北沙参 南沙参 百合 +五味子 枸杞子 女贞子 墨旱莲 菟丝子 桑椹 覆盆子 金樱子 芡实 怀山药 鸡内金 神曲 麦芽 山楂 +陈皮 青皮 枳实 枳壳 厚朴 半夏 法半夏 姜半夏 竹茹 干姜 炮姜 桂枝 肉桂 附子 制附子 +柴胡 升麻 防风 荆芥 羌活 独活 秦艽 威灵仙 桑枝 桑叶 菊花 薄荷 蝉蜕 牛蒡子 连翘 金银花 蒲公英 +板蓝根 大青叶 鱼腥草 败酱草 白花蛇舌草 夏枯草 决明子 车前子 车前草 木通 通草 滑石 萹蓄 瞿麦 +茵陈 金钱草 虎杖 大黄 芒硝 火麻仁 郁李仁 杏仁 苦杏仁 桔梗 紫菀 款冬花 百部 川贝母 浙贝母 贝母 +瓜蒌 全瓜蒌 薤白 远志 酸枣仁 柏子仁 夜交藤 首乌藤 合欢皮 龙骨 牡蛎 珍珠母 磁石 天麻 钩藤 石决明 +地龙 全蝎 蜈蚣 僵蚕 水蛭 土鳖虫 鸡血藤 牛膝 怀牛膝 川牛膝 杜仲 续断 桑寄生 狗脊 骨碎补 补骨脂 +淫羊藿 仙茅 巴戟天 肉苁蓉 锁阳 益智仁 乌药 小茴香 吴茱萸 木香 香附 郁金 延胡索 川楝子 佛手 +砂仁 白豆蔻 豆蔻 草豆蔻 藿香 佩兰 苍耳子 辛夷 白芷 细辛 麻黄 紫苏 苏叶 紫苏叶 苏子 紫苏子 +乌梅 五倍子 诃子 浮小麦 麻黄根 山茱萸肉 何首乌 制何首乌 阿胶 鹿角胶 龟甲 鳖甲 玄参 牛黄 冰片 +麝香 苏合香 石菖蒲 益母草 泽兰 王不留行 莪术 三棱 乳香 没药 五灵脂 蒲黄 茜草 白茅根 侧柏叶 +仙鹤草 艾叶 地榆 槐花 小蓟 大蓟 白及 荷叶 绞股蓝 桑白皮 地骨皮 青蒿 银柴胡 胡黄连 白薇 +苦参 白鲜皮 地肤子 蛇床子 黄药子 海藻 昆布 荔枝核 橘核 鬼箭羽 翻白草 玉米须 冬瓜皮 赤小豆 +""".split() + +# 常用方剂与中成药(含糖尿病常用)。 +_TCM_FORMULAS = """ +六味地黄丸 知柏地黄丸 杞菊地黄丸 麦味地黄丸 金匮肾气丸 济生肾气丸 桂附地黄丸 左归丸 右归丸 +玉泉丸 消渴丸 参芪降糖颗粒 津力达颗粒 天芪降糖胶囊 渴乐宁胶囊 金芪降糖片 降糖舒胶囊 糖脉康颗粒 +芪蛭降糖胶囊 木丹颗粒 通心络胶囊 复方丹参滴丸 银杏叶片 血塞通 血栓通 丹参酮 脑心通胶囊 +补中益气丸 补中益气汤 归脾丸 归脾汤 逍遥丸 逍遥散 加味逍遥丸 丹栀逍遥散 柴胡疏肝散 四逆散 +四君子汤 六君子汤 香砂六君子汤 参苓白术散 理中丸 附子理中丸 四物汤 八珍汤 十全大补汤 当归补血汤 +生脉饮 生脉散 玉女煎 白虎加人参汤 白虎汤 葛根芩连汤 黄连解毒汤 半夏泻心汤 大柴胡汤 小柴胡汤 +血府逐瘀汤 补阳还五汤 桃红四物汤 二陈汤 温胆汤 黄连温胆汤 天麻钩藤饮 镇肝熄风汤 酸枣仁汤 +天王补心丹 一贯煎 增液汤 沙参麦冬汤 益胃汤 竹叶石膏汤 五苓散 猪苓汤 真武汤 实脾饮 防己黄芪汤 +玉屏风散 桂枝汤 麻黄汤 银翘散 桑菊饮 藿香正气散 藿香正气水 保和丸 平胃散 二妙丸 三妙丸 四妙丸 +当归四逆汤 黄芪桂枝五物汤 独活寄生汤 身痛逐瘀汤 大补阴丸 二至丸 交泰丸 安宫牛黄丸 浓缩水丸 +""".split() + +# 西药(以内分泌、心血管、常见合并用药为主)。 +_WESTERN = """ +二甲双胍 盐酸二甲双胍 格列美脲 格列齐特 格列吡嗪 格列本脲 格列喹酮 瑞格列奈 那格列奈 阿卡波糖 +伏格列波糖 米格列醇 吡格列酮 罗格列酮 西格列汀 沙格列汀 维格列汀 利格列汀 阿格列汀 达格列净 +恩格列净 卡格列净 艾托格列净 利拉鲁肽 司美格鲁肽 度拉糖肽 艾塞那肽 利司那肽 贝那鲁肽 替尔泊肽 +胰岛素 门冬胰岛素 赖脯胰岛素 谷赖胰岛素 甘精胰岛素 地特胰岛素 德谷胰岛素 精蛋白锌胰岛素 +重组人胰岛素 生物合成人胰岛素 预混胰岛素 硝苯地平 氨氯地平 左旋氨氯地平 非洛地平 尼群地平 +贝尼地平 厄贝沙坦 缬沙坦 氯沙坦 替米沙坦 坎地沙坦 奥美沙坦 沙库巴曲缬沙坦 依那普利 贝那普利 +培哚普利 卡托普利 福辛普利 美托洛尔 比索洛尔 卡维地洛 阿替洛尔 氢氯噻嗪 吲达帕胺 呋塞米 +螺内酯 托拉塞米 阿托伐他汀 瑞舒伐他汀 辛伐他汀 普伐他汀 匹伐他汀 依折麦布 非诺贝特 苯扎贝特 +阿司匹林 氯吡格雷 替格瑞洛 华法林 利伐沙班 达比加群 硫酸氢氯吡格雷 甲钴胺 依帕司他 硫辛酸 +α-硫辛酸 胰激肽原酶 羟苯磺酸钙 前列地尔 贝前列素钠 别嘌醇 非布司他 苯溴马隆 碳酸氢钠 +左甲状腺素 甲巯咪唑 丙硫氧嘧啶 奥美拉唑 泮托拉唑 雷贝拉唑 埃索美拉唑 雷尼替丁 法莫替丁 +多潘立酮 莫沙必利 二甲硅油 乳果糖 阿莫西林 头孢呋辛 左氧氟沙星 莫西沙星 阿奇霉素 布洛芬 +对乙酰氨基酚 塞来昔布 加巴喷丁 普瑞巴林 碳酸钙 骨化三醇 维生素D 维生素B12 叶酸 硝酸甘油 +单硝酸异山梨酯 曲美他嗪 地高辛 胺碘酮 坦索罗辛 非那雄胺 西地那非 他达拉非 +""".split() + +# 西药常带剂型后缀;一起摘出,展示时保留原文写法。 +_WESTERN_FORM = r"(?:缓释片|控释片|肠溶片|分散片|片|缓释胶囊|肠溶胶囊|胶囊|注射液|注射剂|笔|颗粒)?" + + +class DrugMention(NamedTuple): + start: int + end: int + # 去掉剂型后缀的药名,用于速览去重;高亮范围仍覆盖原文中的完整写法。 + name: str + kind: str + + +def _alternation(names: list[str]) -> str: + return "|".join(re.escape(name) for name in sorted(set(names), key=len, reverse=True)) + + +# 单一正则、长词优先:中成药 > 西药 > 饮片,保证一个位置只被归入一类。 +_DRUG_RE = re.compile( + rf"(?P{_alternation(_TCM_FORMULAS)})" + rf"|(?P(?P{_alternation(_WESTERN)}){_WESTERN_FORM})" + rf"|(?P{_alternation(_TCM_HERBS)})" +) + + +def find_drug_mentions(text: str) -> list[DrugMention]: + """返回正文中每一处药名的位置与类别(不重叠、按出现顺序)。""" + + mentions: list[DrugMention] = [] + for match in _DRUG_RE.finditer(str(text or "")): + if match.group("western"): + mentions.append(DrugMention(match.start(), match.end(), match.group("western_base"), WESTERN)) + else: + mentions.append(DrugMention(match.start(), match.end(), match.group(), TCM)) + return mentions + + +def summarize_drugs(text: str) -> dict[str, list[str]]: + """按类别去重汇总药名,保留首次出现的顺序。""" + + summary: dict[str, list[str]] = {TCM: [], WESTERN: []} + for mention in find_drug_mentions(text): + names = summary[mention.kind] + if mention.name not in names: + names.append(mention.name) + return summary diff --git a/app/src/doctor_workstation/ui/dialogs/ai_consult_metric_text.py b/app/src/doctor_workstation/ui/dialogs/ai_consult_metric_text.py index 3c00b2f9e..b79d179d3 100644 --- a/app/src/doctor_workstation/ui/dialogs/ai_consult_metric_text.py +++ b/app/src/doctor_workstation/ui/dialogs/ai_consult_metric_text.py @@ -15,6 +15,8 @@ from contextvars import ContextVar from dataclasses import dataclass from html import escape +from .ai_consult_drug_names import DRUG_STYLES, find_drug_mentions + from .ai_consult_metric_state import ( ABNORMAL_COLOR, NORMAL_COLOR, @@ -393,18 +395,23 @@ def metric_text_html(text: str, *, metric_label: str = "") -> str: if metric_label and _MISSING_PLACEHOLDER_RE.fullmatch(text): spans.append((0, len(text), UNKNOWN_COLOR)) + # 药名高亮:中药绿、西药蓝,浅底加粗,让医生在正文里一眼看到用药。 + styled = [(start, end, f"color: {color}; font-weight: 600;") for start, end, color in spans] + styled.extend( + (mention.start, mention.end, DRUG_STYLES[mention.kind]) + for mention in find_drug_mentions(text) + ) + # Prefer a full reading over a contained unitless value, then consume each # original character once. Generated markup is never scanned by a regex. - spans.sort(key=lambda span: (span[0], -(span[1] - span[0]))) + styled.sort(key=lambda span: (span[0], -(span[1] - span[0]))) pieces = [''] position = 0 - for start, end, color in spans: + for start, end, style in styled: if start < position: continue pieces.append(escape(text[position:start])) - pieces.append( - f'{escape(text[start:end])}' - ) + pieces.append(f'{escape(text[start:end])}') position = end pieces.append(escape(text[position:])) pieces.append("") diff --git a/app/tests/test_ai_consult_drug_names.py b/app/tests/test_ai_consult_drug_names.py new file mode 100644 index 000000000..07935e302 --- /dev/null +++ b/app/tests/test_ai_consult_drug_names.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from doctor_workstation.ui.dialogs.ai_consult_drug_names import ( + TCM, + WESTERN, + find_drug_mentions, + summarize_drugs, +) + + +def test_formula_wins_over_contained_herb() -> None: + mentions = find_drug_mentions("可参考六味地黄丸加减") + assert [(m.name, m.kind) for m in mentions] == [("六味地黄丸", TCM)] + + +def test_western_dosage_form_is_highlighted_but_deduplicated_by_base_name() -> None: + text = "在用二甲双胍胶囊,二甲双胍早晚各1片,加用阿卡波糖" + mentions = find_drug_mentions(text) + assert text[mentions[0].start:mentions[0].end] == "二甲双胍胶囊" + assert summarize_drugs(text) == {TCM: [], WESTERN: ["二甲双胍", "阿卡波糖"]} + + +def test_herbs_and_western_drugs_are_split_by_kind() -> None: + summary = summarize_drugs("黄芪、丹参益气活血;格列美脲注意低血糖;黄芪可加量") + assert summary == {TCM: ["黄芪", "丹参"], WESTERN: ["格列美脲"]} + + +def test_plain_text_and_diet_words_are_not_drugs() -> None: + assert summarize_drugs("建议多吃山药、大枣,少吃甜食,规律运动") == {TCM: [], WESTERN: []} diff --git a/app/tests/test_ai_consult_metric_text.py b/app/tests/test_ai_consult_metric_text.py index d3d3ca87c..9ee46a5ed 100644 --- a/app/tests/test_ai_consult_metric_text.py +++ b/app/tests/test_ai_consult_metric_text.py @@ -118,7 +118,6 @@ def test_current_readings_after_targets_do_not_keep_reference_color() -> None: "诊单ID:123456;patient_id=1501;流水ABC123456;电话13800138000", "2026-08-18,2026/08/19,08:30,第1章,1. 检查,2. 处理", "病程3年,持续2周,随访7天,每次30分钟", - "二甲双胍500mg,每日2次;胰岛素10U;阿司匹林100 mg", "剂量0.2mg/kg、浓度5mg/mL,外用2%利多卡因", "统计显示80%的患者改善20%,目标完成率90%,报销比例50%", "BMI2026-08-18;eGFR2026/08/19;heart_rate2026-08-18", @@ -131,6 +130,17 @@ def test_unrelated_numbers_and_drug_doses_remain_unemphasized(text: str) -> None assert "color:" not in rendered +def test_drug_names_are_highlighted_but_their_doses_stay_plain() -> None: + text = "二甲双胍500mg,每日2次;胰岛素10U;阿司匹林100 mg;黄芪30g" + rendered = metric_text_html(text) + for name in ("二甲双胍", "胰岛素", "阿司匹林"): + assert f'background-color: #EEF1FA; font-weight: 700;">{name}' in rendered + assert 'background-color: #EAF7F0; font-weight: 700;">黄芪' in rendered + for dose in ("500mg", "10U", "100 mg", "30g"): + assert f'font-weight: 600;">{dose}' not in rendered + assert f'font-weight: 700;">{dose}' not in rendered + + def test_explicit_statuses_are_emphasized_without_classifying_the_numbers() -> None: document = _document("血糖6.0 mmol/L未达标;血压140/90mmHg正常;血糖偏高;检查异常") assert _color(document, "6.0 mmol/L") == "#BD4B58"