# -*- coding: utf-8 -*- """端到端联调用例(Issue #86)—— 跨链路集成场景。 验证数据流(采集 → 落库)与问答流(RAG → LLM 网关)的协同: 基于真实采集到的工艺数据,构建 RAG 知识并完成一次安全问答闭环, 模拟「采集 → 沉淀知识 → 智能问答」的完整业务闭环。 """ from __future__ import annotations # 引导加载四个内核模块(必须在测试导入前执行) import tests.e2e._bootstrap # noqa: F401 import tempfile import unittest from collector.engine import CollectorEngine from collector.metrics import HealthMetrics from collector.spool import SpoolStore from drivers import SimulatorDriver from point_dict.loader import Point, PointDict from data_bus import BatchWriter, MemorySink from rag_kb import RagKnowledgeBase, build_document from llm_gateway import ( GatewayResult, LLMGateway, LocalBackend, PromptRegistry, RouteTarget, ) def _collect_samples(tmpdir: str): """跑一轮采集,返回(落库样本, 引擎健康度)。""" points = [ Point(device_id="CLF-01", point_id="CLF-01.TEMP", name="1#炉温", unit="℃", data_type="float", sample_rate=1000, quality_code=True, row_number=2), Point(device_id="CLF-01", point_id="CLF-01.PRES", name="1#炉压", unit="kPa", data_type="float", sample_rate=1000, quality_code=True, row_number=3), ] pd = PointDict(points) spool = SpoolStore(spool_dir=tmpdir, cache_limit_bytes=8 * 1024 * 1024) metrics = HealthMetrics() engine = CollectorEngine( point_dict=pd, driver_slots=[("simulator", SimulatorDriver(), [])], spool=spool, metrics=metrics, interval_ms=1000, max_pending=100_000, ) engine.collect_once() sink = MemorySink() writer = BatchWriter(sink=sink, batch_size=10, flush_interval=0.0) for s in spool.pending_records(): writer.push(s) spool.ack(s) writer.flush() return sink.rows, metrics class CrossPipelineE2ETest(unittest.TestCase): """采集 → 落库 → 知识沉淀 → 安全问答 全业务闭环。""" def test_collect_then_query_closed_loop(self) -> None: """采集数据落库后,结合工艺知识库完成一次安全问答。""" tmpdir = tempfile.mkdtemp(prefix="iaop_cross_") # 1) 数据流:采集并落库 rows, metrics = _collect_samples(tmpdir) self.assertEqual(len(rows), 2) # 2 点位全部落库 self.assertTrue(metrics.meets_sla()) # 采集健康度达标 point_ids = {r["point_id"] for r in rows} self.assertEqual(point_ids, {"CLF-01.TEMP", "CLF-01.PRES"}) # 2) 知识沉淀:工艺知识库(模拟由采集数据衍生的工艺规范) kb = RagKnowledgeBase() kb.add_documents([ build_document( title="1#氯化炉工艺卡", text="1#氯化炉(CLF-01)正常炉温 850~920℃,炉压 0.2~0.4MPa。" "CLF-01.TEMP 与 CLF-01.PRES 为关键监控点位。", category="process", ), ]) # 3) 问答流:基于知识库回答工艺问题 prompts = PromptRegistry() prompts.update(name="qa", version="1.0.0", text="工业 AI 助手回答:{query}") gateway = LLMGateway(prompts=prompts, prompt_name="qa", local=LocalBackend(echo_context=True)) query = "CLF-01 的关键监控点位和正常炉温范围?" hits = kb.search(query, top_k=3) sources = [h.chunk.title for h in hits] self.assertGreater(len(sources), 0) result = gateway.ask(query, rag_context=sources, confidence=0.95) self.assertIsInstance(result, GatewayResult) self.assertEqual(result.route.target, RouteTarget.LOCAL) self.assertFalse(result.needs_human) # 回答引用了知识库来源 self.assertIn(sources[0], result.answer) if __name__ == "__main__": unittest.main()