diff --git a/templates/resin/rag-kb/documents/GB-T-5475-离子交换树脂取样方法.md b/templates/resin/rag-kb/documents/GB-T-5475-离子交换树脂取样方法.md new file mode 100644 index 0000000..b76d009 --- /dev/null +++ b/templates/resin/rag-kb/documents/GB-T-5475-离子交换树脂取样方法.md @@ -0,0 +1,6 @@ +# GB/T 5475 离子交换树脂取样方法 + +- 规定离子交换树脂产品取样方法:从批中按规定数量抽取样品。 +- 取样要求代表性:不同包装单元均匀取样,混合后缩分。 +- 样品量:每批取样量按标准规定,样品密封保存并标识批次。 +- 取样记录:批次号、取样时间、取样人、样品编号(对齐 sample_id)。 diff --git a/templates/resin/rag-kb/documents/GB-T-8144-阳离子交换树脂交换容量测定方法.md b/templates/resin/rag-kb/documents/GB-T-8144-阳离子交换树脂交换容量测定方法.md new file mode 100644 index 0000000..85c8d85 --- /dev/null +++ b/templates/resin/rag-kb/documents/GB-T-8144-阳离子交换树脂交换容量测定方法.md @@ -0,0 +1,6 @@ +# GB/T 8144 阳离子交换树脂交换容量测定方法 + +- 规定阳离子交换树脂交换容量(mmol/g)测定方法。 +- 原理:树脂经酸/碱转型后,用标准溶液滴定测定交换容量。 +- 结果用于质量分级:交换容量 ≥ 规定值(如 ≥ 4.2 mmol/g)为合格品。 +- 测定结果按批次录入 LIMS(tag_id: LIMS.ExchangeCapacity),供质量预测模型训练。 diff --git a/templates/resin/rag-kb/documents/GB-T-8330-离子交换树脂湿真密度测定方法.md b/templates/resin/rag-kb/documents/GB-T-8330-离子交换树脂湿真密度测定方法.md new file mode 100644 index 0000000..d95c497 --- /dev/null +++ b/templates/resin/rag-kb/documents/GB-T-8330-离子交换树脂湿真密度测定方法.md @@ -0,0 +1,6 @@ +# GB/T 8330 离子交换树脂湿真密度测定方法 + +- 规定离子交换树脂湿真密度测定方法(比重瓶法)。 +- 湿真密度反映树脂交联度与孔结构,与交换容量、强度相关。 +- 测定条件:恒温(25℃)、湿态样品、排除气泡后称量。 +- 结果记录:按批次录入,作为工艺优化与质量追溯的辅助指标。 diff --git a/templates/resin/rag-kb/documents/交接班报告生成规范.md b/templates/resin/rag-kb/documents/交接班报告生成规范.md new file mode 100644 index 0000000..562badd --- /dev/null +++ b/templates/resin/rag-kb/documents/交接班报告生成规范.md @@ -0,0 +1,6 @@ +# 交接班报告生成规范 + +- 交接班报告须包含:生产概况、设备运行状态、异常与处置、安全注意事项、待办事项。 +- 异常事项必须标注发生时间、处理人与处置结果。 +- 待办事项需明确责任人与期望完成时间。 +- 每班次结束前 30 分钟生成,当班班长审核后交下一班。 diff --git a/templates/resin/rag-kb/documents/交联度超标处置流程.md b/templates/resin/rag-kb/documents/交联度超标处置流程.md new file mode 100644 index 0000000..f8bb4d1 --- /dev/null +++ b/templates/resin/rag-kb/documents/交联度超标处置流程.md @@ -0,0 +1,7 @@ +# 交联度超标处置流程 + +1. 化验确认交联度超标(相对配方设计值偏差 > 5%)后,立即隔离该批树脂。 +2. 通知当班班长与质检科,暂停同一配方后续批次投料。 +3. 原因排查:DVB 计量偏差、搅拌不均、温度曲线漂移。 +4. 处置选项:回用(与低交联度批次调配)或降级(非标用途),由 QE 评审放行。 +5. 处置结果录入批次记录,并更新配方/工艺参数优化建议。 diff --git a/templates/resin/rag-kb/documents/吸附树脂合成工艺规范.md b/templates/resin/rag-kb/documents/吸附树脂合成工艺规范.md new file mode 100644 index 0000000..5f50049 --- /dev/null +++ b/templates/resin/rag-kb/documents/吸附树脂合成工艺规范.md @@ -0,0 +1,8 @@ +# 吸附树脂合成工艺规范 + +- 吸附树脂系列:ASC / ASD / HPR / ACD,采用悬浮聚合法,间歇/配方驱动生产。 +- 主要单体:苯乙烯(ST)+ 交联剂二乙烯苯(DVB),按配方比例投料。 +- 关键工艺参数:聚合温度 70–90℃,引发剂滴加速率按配方设定,搅拌转速 100–300 rpm。 +- 交联度(DVB 含量)直接影响树脂机械强度与交换容量,须按批次记录。 +- 生产批次按 batch_id 追踪,聚合时间 8–12 小时,出料后水洗、筛分。 +- 涉及车间:802(合成)、803(交联)、804(洗涤干燥),配污水/纯水/能源公用工程。 diff --git a/templates/resin/rag-kb/documents/树脂合成异常处置SOP.md b/templates/resin/rag-kb/documents/树脂合成异常处置SOP.md new file mode 100644 index 0000000..99d4e9c --- /dev/null +++ b/templates/resin/rag-kb/documents/树脂合成异常处置SOP.md @@ -0,0 +1,7 @@ +# 树脂合成异常处置SOP + +- SOP-R-001:釜温超上限(>95℃)→ 立即停引发剂滴加、加大冷却水,10 分钟未回落按紧急排料流程。 +- SOP-R-002:釜压异常升高 → 停止加热、检查放空阀,联系仪表检修。 +- SOP-R-003:搅拌故障 → 降速至停止,启用备用搅拌(如有),未恢复则终止本批。 +- SOP-R-004:交联度超标(>设计值 15%)→ 该批隔离评审,按 QE 决定回用或降级。 +- 所有异常必须记录发生时间、处理人、处置措施与结果,纳入交接班报告。 diff --git a/templates/resin/rag-kb/documents/离子交换树脂生产操作手册.md b/templates/resin/rag-kb/documents/离子交换树脂生产操作手册.md new file mode 100644 index 0000000..cb9a2ea --- /dev/null +++ b/templates/resin/rag-kb/documents/离子交换树脂生产操作手册.md @@ -0,0 +1,8 @@ +# 离子交换树脂生产操作手册 + +- 开机前确认:原料计量罐、反应釜密封、加热/冷却系统、尾气处理正常。 +- 投料顺序:先加去离子水与分散剂,再按配方加入苯乙烯/二乙烯苯与引发剂。 +- 反应期间每 30 分钟记录一次釜温、釜压、搅拌转速。 +- 聚合完成判断:釜温回落且无明显放热,取样测交联度。 +- 洗涤:去离子水洗至 pH 中性;干燥:60–80℃ 流化干燥至含水率达标。 +- 停机:关闭加热与搅拌,釜内残余物料按危废流程处理。 diff --git a/templates/resin/rag-kb/loader.py b/templates/resin/rag-kb/loader.py new file mode 100644 index 0000000..ce582d6 --- /dev/null +++ b/templates/resin/rag-kb/loader.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +"""树脂 RAG 知识库导出加载器 —— issue #83。 + +把已交付化工新材料AI平台(吸附树脂)的领域知识库**导出为模板资产** +(`documents/*.md`),并提供与 `kb.resin.template.yaml` 清单一致的加载器: + +- `resin_kb_loader(title)`:标题 → 文档文本(对齐 rag-kb 的 loader 约定, + 可直接用于 `RagKnowledgeBase.from_template_config(..., loader=resin_kb_loader)`); +- `validate_export()`:校验 kb 清单声明的文档均有对应文件且非空 + (导出完整性检查,防止清单与文档漂移)。 + +标题规范化:`GB/T 5475 ...` → 文件名 `GB-T-5475 ....md`(`/` 不允许出现在文件名)。 +""" +from __future__ import annotations + +import os +from typing import List, Optional + +import yaml + +#: 知识库配置资产(与模板清单同目录) +DEFAULT_KB_CONFIG = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "kb.resin.template.yaml") + + +def resin_documents_dir() -> str: + """文档集目录(documents/)。""" + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + "documents") + + +def _doc_path(title: str) -> str: + """标题 → 文档文件路径(规范化:/ 与空格 → -,对齐导出文件名)。""" + safe = title.replace("/", "-").replace(" ", "-") + return os.path.join(resin_documents_dir(), f"{safe}.md") + + +def resin_kb_loader(title: str) -> str: + """按标题返回文档文本(找不到返回空串,与 rag-kb loader 约定一致)。""" + path = _doc_path(title) + if not os.path.isfile(path): + return "" + with open(path, "r", encoding="utf-8") as fh: + return fh.read() + + +def load_kb_config(kb_config_path: str = DEFAULT_KB_CONFIG) -> dict: + """加载树脂知识库配置资产。""" + with open(kb_config_path, "r", encoding="utf-8") as fh: + return yaml.safe_load(fh) or {} + + +def validate_export(kb_config_path: str = DEFAULT_KB_CONFIG) -> List[str]: + """校验导出完整性:清单声明的文档均有文件且非空。 + + Returns: + 缺失/为空的问题列表(空 = 导出完整)。 + """ + problems: List[str] = [] + raw = load_kb_config(kb_config_path) + for src in raw.get("sources", []): + kind = src.get("kind") + for doc in src.get("documents", []): + text = resin_kb_loader(doc) + if not text: + problems.append(f"[{kind}] 文档缺失或为空: {doc}") + return problems diff --git a/templates/resin/rag-kb/tests/test_resin_export.py b/templates/resin/rag-kb/tests/test_resin_export.py new file mode 100644 index 0000000..3981898 --- /dev/null +++ b/templates/resin/rag-kb/tests/test_resin_export.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +"""树脂 RAG 知识库导出测试(issue #83)。 + +覆盖: +1. kb.resin.template.yaml 清单声明的 9 篇文档全部导出且非空(validate_export); +2. resin_kb_loader 按标题返回文本(含 GB/T 标题规范化); +3. loader 可直接用于 RagKnowledgeBase(from_template_config + loader); +4. 未知标题 → 空串。 +""" +import os +import sys +import unittest + +_RAGKB_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, _RAGKB_DIR) +from loader import ( # noqa: E402 + DEFAULT_KB_CONFIG, + resin_kb_loader, + validate_export, +) + + +class TestExportCompleteness(unittest.TestCase): + """导出完整性:清单 ↔ 文档文件一致。""" + + def test_all_listed_documents_exported(self): + problems = validate_export() + self.assertEqual(problems, [], f"导出不完整: {problems}") + + def test_document_count(self): + docs = os.listdir(os.path.join(_RAGKB_DIR, "documents")) + self.assertEqual(len(docs), 8) + + +class TestLoader(unittest.TestCase): + """加载器行为。""" + + def test_process_doc(self): + text = resin_kb_loader("吸附树脂合成工艺规范") + self.assertIn("悬浮聚合法", text) + + def test_gbt_title_normalization(self): + # 标题含 / → 文件名用 -(GB/T 5475 → GB-T-5475) + text = resin_kb_loader("GB/T 5475 离子交换树脂取样方法") + self.assertIn("取样", text) + + def test_unknown_title_empty(self): + self.assertEqual(resin_kb_loader("不存在的文档"), "") + + def test_kb_config_loads(self): + import yaml + with open(DEFAULT_KB_CONFIG, "r", encoding="utf-8") as fh: + raw = yaml.safe_load(fh) + self.assertEqual(raw["template"], "resin") + + +class TestRagKbIntegration(unittest.TestCase): + """与内核 rag-kb 集成(loader 直接可用)。""" + + def test_build_kb_from_export(self): + import importlib.util + # 挂载 core/rag-kb(目录含连字符) + repo_root = os.path.dirname(os.path.dirname(os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) + rag_kb_dir = os.path.join(repo_root, "core", "rag-kb") + if "rag_kb" not in sys.modules: + spec = importlib.util.spec_from_file_location( + "rag_kb", os.path.join(rag_kb_dir, "__init__.py"), + submodule_search_locations=[rag_kb_dir]) + _kb = importlib.util.module_from_spec(spec) + sys.modules["rag_kb"] = _kb + spec.loader.exec_module(_kb) + + from rag_kb import RagKnowledgeBase, load_kb_config # noqa: E402 + + kb = RagKnowledgeBase.from_template_config( + load_kb_config(DEFAULT_KB_CONFIG), loader=resin_kb_loader) + self.assertGreater(kb.doc_count, 0) + hits = kb.search("交换容量测定", top_k=3, categories=None) + self.assertGreater(len(hits), 0) + + +if __name__ == "__main__": + unittest.main()