# 矿井主通风机业务与模拟联调 当前启动、月度和 API 批次已切换为[固定工作流](MAIN_FAN_WORKFLOW.md):沿用监测预处理,Python/GLM OCR 及失败页 Qwen 补识别后,由 Qwen3.5 一次生成研判 JSON。工作流以告警准确率优先,开停、风量按冻结规则判断,性能粗略核对;仅三类均无可用数据时为 `UNDETERMINED`,有数据且无确凿异常为 `NORMAL`(未发现确凿异常)。新工作流默认只保存本地结果。下文的 Skill/沙箱说明仍适用于原有自由对话及兼容入口,不再描述批次的模型执行方式和结论口径。 ## 批次固定首步:获取并筛选煤矿目录 批次入口先由宿主代码调用 `GET /system/user/deptTree`,递归提取煤矿叶节点,只保留名称末尾括号中的完整状态为“正常生产煤矿”的记录。支持中文、英文括号,保留原始 `label`;“非正常生产煤矿”、停产、建设及未标注状态的煤矿不会进入筛选清单。父部门即使名称中包含该状态也不作为煤矿。 该步骤使用 `MAIN_FAN_AGENT_CODE` 和配置的网关连接,不调用模型。每条煤矿记录保存 `dept_id`(原节点 `id`)、`mine_id`(原 `mineId`,保持字符串和前导零)、去除末尾状态后的 `mine_name`、`production_status`、原始标签、父部门、部门路径与 `raw_node`。`mine_id` 对应单矿任务输入的 `mine_code`。身份缺失、部门层级冲突或重复身份会明确报错,不能静默遗漏或重复执行煤矿。 通过业务管理 API 调用,无需模型参数或请求体,认证使用本服务的 `BUSINESS_API_TOKEN`: ```bash curl --request POST \ --url http://127.0.0.1:8080/api/business-agents/main_fan/mine-catalog \ --header 'Authorization: Bearer ' ``` 成功返回 HTTP 201,包含 `snapshot_id`、固定阶段 `mine_discovery`、节点及煤矿数量、生产状态分布,以及仅包含正常生产煤矿的 `mines`。`GET /api/business-agents/main_fan/mine-catalog` 查看最近一次成功清单;`GET /api/business-agents/main_fan/mine-catalog/{snapshot_id}` 重读指定批次清单,均使用相同业务认证。 也可在项目根目录直接执行固定首步,不启动网页、模型、任务队列或结果推送: ```bash .venv/bin/python scripts/refresh_main_fan_mines.py ``` 每次成功查询生成独立快照,保存在 `data/main_fan/mine_catalog//`:`request.json` 为查询元数据,`dept-tree.json` 为完整上游业务响应,`all-mines.json` 为全部煤矿,`selected-mines.json` 为筛选后冻结清单。文件不包含网关凭证。失败不会覆盖上一次成功清单;成功但没有符合条件的煤矿会明确返回0条。 此目录入口只负责煤矿发现、筛选和清单保存;逐矿判识由下述本地批次使用冻结的`snapshot_id`与`mines`执行。 ## 单矿主通风机判识 当期切换与风量采用已确定的数据源:安全监控1010提供开停与切换,重大设备仅提供风量。批次计划和历史查询校验只接受 `safety/fan_state`、`equipment/flow`,并将 `source_policy`写入取数回执及详情文件;重大设备运行状态不参与切换判断。按原编号做两路风量时间对照,未确认的设备对应关系仍如实标注;窗口缺样不改用重大设备开停否定安监事件。 旧批次重新分析时按当前口径筛选历史计划,原件和缓存索引保留;覆盖统计及Agent摘要只包含本轮选中测点的日段,已有成功原件继续复用。 `main_fan` 在我们自己的框架内执行,陕西局网关负责对外管理和转发。默认联合研判同矿井、同组的两台主通风机,比较检测报告参数、实测性能数据,以及有真实时序输入时的切换前后稳定风量和风压。 业务 Skill 为 `skills/main-fan-equivalent-capability-assessment/SKILL.md`。当前模式是沙箱自主探索:Agent 自行编写文档解析、取证、计算、图表和报告生成代码,在会话沙箱中执行,根据真实输出修改方法。原七阶段、文档识别器、事实登记器和独立计算器不在默认工具集中;旧服务仅用于显式兼容脚本。 用1237已有真实原件回放固定发现和预处理,生成可直接给Agent的数据(无新接口或模型调用): ```bash .venv/bin/python scripts/preview_main_fan_data.py \ --capture outputs/main_fan_history_checks/20261021_094202_1237 \ --output outputs/main_fan_prepared/1237_fixed_code ``` ## 沙箱自主探索工作台 当前注册十二个入口,其中四个批次工具只接受宿主绑定的本地批次: | 工具 | 职责 | |---|---| | `prepare_main_fan_task` | 绑定身份,导出原始输入、规则和交付 schema | | `fetch_main_fan_data` | 通过宿主网关取得原件、设备与运行记录 | | `query_fan_sensor_realtime` | 按部门、可选类型及分页参数查询安全监控实时传感器列表 | | `query_fan_timeseries` | 按测点、部门与起止时间查询历史曲线,保存完整原始及规范化数据 | | `query_fan_equipment_realtime` | 查询重大设备主通风机系统的实时设备及测点数据,固定devOfSystem=30 | | `query_fan_equipment_timeseries` | 按重大设备测点及时间范围取得电机、温度等历史曲线,保留原始字段 | | `run_python` | 隔离执行 Agent 自己编写的代码,保存真实输出和执行记录 | | `submit_main_fan_report` | 核验交付文件与执行记录、映射平台格式并归档 | | `submit_main_fan_mine_plan` | 根据本矿真实实时列表提交通风井及测点计划 | | `query_main_fan_history` | 按计划测点向前按天回查40天,逐段保存原件并补取失败段 | | `fetch_main_fan_reports` | 正式模式经资料采集API和挂载目录复制PDF,模拟模式下载测试原件;按hash加载同矿同井成功档案 | | `archive_main_fan_report` | 核验成功沙箱输出及全页覆盖声明,保存可复用的报告提取 | ### 本地批次联调 井级结果中的 `details.switch_analysis` 必须交付状态、方法、具体事件和限制;每个候选或确认事件保留实际停启/状态变化时刻、间隔与原件依据,不能用运行占比代替事件识别。原输出目录加 `--reassess` 可重新分析,旧结果保存在井级 `revisions/`。同一批次、同一井、相同计划和时段的已取原件经hash校验后复用,不再次请求;不同月度批次重新获取当期数据。 重大设备历史已适配实际 `data[].sbNodeCurve[]`,其中 `dataTime` 是采样时间,`realData` 是原始值;保留原始响应,额外提供规范时间、样本数和结构预览。未知结构的采样数量为 `null`,空数组为0,全0曲线仍是已有历史数据。井级 `details.flow_analysis` 必须提供状态和原始样本数,不能将已有嵌套采样误报为空数据。 报告档案采用 `main-fan-agent-report-v2-page-reads`,除页数声明外还核对 `page_sources`:每个物理页对应独立文件、实际成功的 `read_file` 读取及当时指纹,扫描页必须为图片。只读10页却声明26页会被拒绝;旧版缺乏读取核验的档案不再复用。合订PDF中的每份报告仍需由Agent逐份整理,读取记录不代替业务内容的正确性检查。 全局运行能力保留显式开关:CLI需同时指定 `--all-producing-mines --allow-large-batch`;网页或月度服务需启用 `MAIN_FAN_BATCH_ALLOW_ALL=1`、将 `MAIN_FAN_BATCH_MAX_MINES` 设为0或足够大的上限,再请求 `all_producing_mines=true` 或配置 `MAIN_FAN_BATCH_DEPT_IDS=all`。分日模式按每日矿数校验上限,每月仍使用当次筛选后的完整清单。 框架筛选正常生产煤矿后,只处理显式选择的部门。固定代码先查询安全监控与重大设备实时列表、取齐分页,按1010和30101选择开停及风量测点,校验矿井身份并保存计划。宿主按天取齐40天数据、整理开停区间、切换候选及风量窗口,并完成报告接口和hash核验,随后才调用Agent。同一矿仍复用一个`main_fan`会话,各井分别提交报告。Agent直接读取`prepare_main_fan_task`返回的`prepared_data.agent_data_path`,不再自主规划监测取数;井级同时导出`给Agent的数据.json`。 固定代码按实际类型选择安监开停和重大设备风量测点。振动、电流等不能声明为开停;请求传入类型筛选后仍须核对实际返回的类型。风机号不能当作通风井号,缺少井级证据时保留一个带“待确认”的候选主备系统。开停和风量使用相同截止时刻、按连续24小时窗口回查40天。批量传输不会消耗160个Agent步骤,每段仍独立保存原件和指纹;`coverage_complete`只表示请求归档覆盖;固定程序同时整理切换候选和前后稳定窗口,Agent核对有效性并作业务分析。 历史取数完成后,逐日请求、响应及完整数据经校验压缩为一个 `history-evidence.zip`;索引持久化后才清理本轮暂存JSON。按日记录和指纹仍可恢复,重试仅补失败段,同批次重分析复用ZIP。Agent默认只读精简的`history-agent.json`:矿井、查询范围、开停区间、风量概况、切换候选及前后风量和必要限制。nodeId、调试计数、质量明细、规则副本及完整窗口计算移到`history-state-details.json`;仅在需要时通过`details_path`复核。默认区间省略gaps字段;具体中断与缺测按需读取详情,详细观测段仍保留`observed_parts`。原始0/1状态码、一级/二级及不同系统不自动合为整机。摘要超过20个状态段或变化时保留截断标志,完整内容按details路径读取;当前本地批次按用户指定的单点法:原机以切换范围起点前1小时、新机以终点后1小时为目标,在各自一侧取最接近目标的一条。保留实际时间和原值,不求平均,不使用分钟完整率或CV门槛;原机值大于0且单位可比时计算按原编号的单点变化,缺失或原机为0时不算比例。单点变化不宣称连续稳定工况。 切换候选按同组内每个安监测点的首末净状态变化确定方向,交接时某一级短暂“开→停→开”不会再导致整组候选被跳过;一级/二级的原始变化仍保留在详情中,不自动确认整机状态。前后风量写入`给Agent的数据.json`的`switch_candidates[].flow`和`comparison.before_value/after_value`。日志`history.prepared`记录预处理版本与候选数量,便于确认镜像已更新。 用已核验的1219单矿原件生成同格式输入示例(只读原业务库,不请求模型/网关): ```bash .venv/bin/python scripts/compact_main_fan_history.py \ --batch-dir outputs/main_fan_batches/20261020_1219_preview ``` 示例在 `verification/agent-history/`,包括Agent入口、精细区间、详细索引、一个压缩证据包及可读说明。此回放不改写此前原件和业务库;新批次自动使用压缩流程。 正式报告通过资料采集API核对矿井身份、同步类型、单矿单期采集并轮询任务,再从只读挂载目录复制到workspace。配置、目录范围及容器部署见 [DOCKER.md](DOCKER.md)。模拟模式仍可使用 `GET /api/main-fan/reports` 和 `GET /api/main-fan/report-files/{hash}.pdf`,原封不动返回既有测试原件,标记 `is_mock` 和 `test_fixture_not_mine_report`,不能改写为其他矿的正式报告。宿主按矿、井级内部标识、实际SHA256和提取版本保存成功档案。复用时将验证后的事实导出为当前可信输入;不同矿井、更新原件、损坏档案及未完成提取均不命中。当期时序始终重新查询。 配置 `MAIN_FAN_BATCH_CRON=0 2 1 * *`、`MAIN_FAN_BATCH_SPREAD_DAYS=20` 后,北京时间每月1日02:00冻结当月清单,均分为20份,每天02:00只投递一份,余数分到前几天。`MAIN_FAN_BATCH_DEPT_IDS=all`、`MAIN_FAN_BATCH_ALLOW_ALL=True`、`MAIN_FAN_BATCH_MAX_MINES=0` 覆盖全部正常生产煤矿。建议 `MAIN_FAN_BATCH_RUN_ON_STARTUP=False`,重启仅续跑已投递的中断份额;停机漏掉的份额按顺序每天一份顺延,不集中补跑。未配置分日天数或设为1时保留每月整批方式。计划、恢复、失败处理和保护上限详见[部署说明](DOCKER.md#每月启动分20天研判)。 如另需服务启动就跑前N矿,设 `MAIN_FAN_BATCH_RUN_ON_STARTUP=True`、`MAIN_FAN_BATCH_STARTUP_MAX_MINES=2`。每次重启都会额外触发独立启动批次。可设置 `MAIN_FAN_BATCH_STARTUP_EXCLUDE_DEPT_IDS=[1045,1106]`,只对独立启动批次先排除这些部门,再按数量上限选矿,未配置时不排除;不改变月度计划范围。 独立脚本默认最多两个矿、回查40天,使用独立业务库和checkpoint,关闭结果平台与自动推送。保留现有模型、网关配置: ```bash .venv/bin/python scripts/run_main_fan_batch.py \ --dept-id 1219 \ --mock-pdf '/实际路径/通风机性能测定报告.pdf' \ --output outputs/main_fan_batches/local-preview ``` 重复 `--dept-id` 可选择第二个矿;`--snapshot-id` 使用已有冻结清单,默认重新获取部门树;`--cutoff '2026-10-20 15:00:00'` 指定北京时间回放。再次指定原输出目录可恢复未完成任务,沿用原清单和截止时刻。大批次必须显式添加 `--allow-large-batch`,联调不使用该选项。 文件夹中包含 `index.html`、`batch.json`、冻结清单、每矿发现记录、每井的 `报告.md`、`结果.json`、`待审阅报文.json`、`来源与覆盖.json`、`推送记录.json`,以及工作区、查询原件和Python执行记录。正式批次先保存本地结果,再按 `RESULT_PLATFORM_ENABLED` 总开关决定是否进入持久化推送队列;失败重试不重新调用Agent。上述隔离预览脚本始终关闭推送。 已归档的单候选系统可单独运行本地开发核验,不需要模型或网关: ```bash .venv/bin/python scripts/verify_main_fan_batch.py \ --batch-dir outputs/main_fan_batches/20261020_1219_preview ``` 核验先校对请求、响应及数据的SHA256,再统计正常开停标签、时间冲突和候选变化,以及实际嵌套风量的原始/去重样本、空段、0值及采样间隔,输出 `verification/本地数据核验.html`、Markdown和JSON。此脚本只适用于每矿一个已保存候选系统,多井需另行逐井核验;它不提交Agent结果,也不代替业务判识。2026-10-20真实联调仅涉及1219一个矿:280/280分段成功;风量1124条全0,安全监控存在两组配对变化候选。旧AI初稿的风量遗漏已作废标记;补充Agent复核因模型服务403待权限恢复。正式交付先查看本地核验报告。 可选网页入口需启用 `MAIN_FAN_LOCAL_BATCH_ENABLED=1`,配置 `MAIN_FAN_MOCK_REPORT_PDF` 和网关。`POST /api/business-agents/main_fan/batches` 接受 `dept_ids`(默认最多两个)及可选 `snapshot_id/cutoff/lookback_days/user_name`;`GET /api/business-agents/main_fan/batches/{batch_id}` 查询状态和输出目录,均使用 `BUSINESS_API_TOKEN`。服务关闭时取消任务并保存中断状态。 月度入口使用相同本地队列,在批次开关开启且配置 `MAIN_FAN_BATCH_CRON` 后装载。按月份及矿井范围生成幂等键,重复触发复用原计划或原整批结果;分日模式每份有独立批次标识,已完成时不重跑。新计划只由月度触发器创建,日调度和启动恢复只处理已有计划。独立模拟PDF服务可运行 `scripts/mock_main_fan_reports.py --pdf '/实际路径/通风机性能测定报告.pdf'`,仅监听 `127.0.0.1:8768`;批次脚本通过进程内ASGI HTTP调用同一接口。 `query_fan_timeseries` 固定代理 `GET /aqjk/alarmcurve/getCurveByNodeId`。模型传 `workflow_id`、`node_id`、`dept_id`、`start_time`、`end_time`,宿主使用 `SHANXI_GATEWAY_URL/TOKEN` 和本业务的 `MAIN_FAN_AGENT_CODE` 发请求;不把凭证交给模型。时间支持带时区的 ISO 格式或 `YYYY-MM-DD HH:MM:SS`,统一转为 Asia/Shanghai,默认单次最多24小时、200000个采样点,分别由 `MAIN_FAN_SERIES_MAX_HOURS`、`MAIN_FAN_SERIES_MAX_SAMPLES` 配置。可重复分段查询,不静默截断或降采样。 例如先准备实际矿井任务,再调用: ```json { "workflow_id": "准备工具返回的实际ID", "node_id": "61082101917301KG1010016D09", "dept_id": "1117", "start_time": "2026-10-19 11:00:00", "end_time": "2026-10-19 11:11:00" } ``` 该编号来自对方提供的朱盖塔矿“主通1一级”接口样例,不预设为其他矿井或整机状态。工具返回记录数、实际时间范围、重复/倒序/窗口外/空值计数、少量预览及完整文件路径;所有收到的采样按原顺序保留。每次查询分别存档请求、上游原始业务响应、规范化数据,加入本轮指纹和报告证据来源。模型用返回的 `python_paths.data` 在沙箱读取完整 JSON;切换识别及工况计算仍由模型自编代码完成。空数据明确为0条,失败返回 `ok=false` 及可恢复反馈。配置的网关需支持这个真实曲线接口,本地旧模拟网关的批量资料路由不等同于该接口。 四个真实接口分别接入,均使用配置的网关地址、凭证和 `MAIN_FAN_AGENT_CODE`: | Agent工具 | 上游GET路径 | 参数 | |---|---|---| | query_fan_sensor_realtime | /aqjk/node/getNodeRealtimeDetails | dept_id、page_num、page_size、可选sensor_type_list;宿主生成索引键值对数组 | | query_fan_timeseries | /aqjk/alarmcurve/getCurveByNodeId | dept_id、node_id、start_time、end_time;上游nodeIds复数 | | query_fan_equipment_realtime | /zdsb/sbnode/selectSbNodeList | dept_id、page_num、page_size;宿主固定devOfSystem=30 | | query_fan_equipment_timeseries | /zdsb/sbnode/selectSbNodeCurve | dept_id、node_id、start_time、end_time;上游nodeId单数 | 四个工具均需先准备本轮workflow_id。实时列表工具一次一页,默认page_num=1、page_size=100;每页请求上限由 `MAIN_FAN_REALTIME_MAX_PAGE_SIZE` 控制,默认1000。可传 `sensor_type_list=["0005","0013","1001","1010","0001,0011,0043","0002"]` 原样复现给定筛选;未传时不添加类型筛选。识别到rows/total时返回row_count、total、has_more、next_page,未识别的结构明确标记未知,不伪装成空列表。超过预览预算时省略preview,完整数据仍保留。 实时列表与重大设备曲线的完整data_path JSON在payload下保留上游业务响应;不猜测重大设备的风量、温度、单位或采样字段。Agent用Python读取真实字段后决定解析方法。重大设备实时列表示例为dept_id="1219";其曲线示例为dept_id="1237"、node_id="6108310185903000MN30102100000000000064"、start_time="2026-10-20 06:20:49"、end_time="2026-10-20 09:20:49"。这些示例部门不自动绑定为同矿,重大设备编码也不直接用于安全监控曲线。是否存在主通风机风量测点须看实际返回中的测点、单位及设备关联,接口接入本身不证明存在风量。 每次查询均归档请求、完整响应和数据文件,准备工具返回sensor_queries/equipment_queries/timeseries_queries;全部文件支持来源哈希核验及报告引用。只使用实时/时序入口的任务无需强制调用批量fetch_main_fan_data。请求失败返回可恢复错误,不以空数据代替失败。 文档文本提取、页面渲染和字段整理使用 Agent 自编 Python;扫描件由它渲染 PNG 后通过通用 `read_file` 看图。中间事实结构由 Agent 自行设计,不再登记到固定 DocumentFacts。标准库、numpy、pypdfium2、Pillow 和 cv2 已可使用;沙箱不联网、不注入服务端凭证、不运行时安装依赖。业务要求、单位和阈值写在 Skill 与本轮冻结的 `rules.json`。 macOS 使用 sandbox-exec,Linux 需要 bubblewrap;缺少沙箱时报不可执行,不回退为宿主裸执行。默认单次最长120秒、单会话最多40次。配置项为 `AGENT_PYTHON_MAX_SECONDS`、`AGENT_PYTHON_MEMORY_MB`(仅Linux地址空间)、`AGENT_PYTHON_MAX_RUNS`。 Agent 用代码生成 Markdown、AgentAssessment JSON 及需要的附件,声明 `output_paths` 并带 `workflow_id` 执行。提交时必须关联本轮成功 `run_id`,报告、JSON、附件的哈希必须仍与声明输出一致。宿主只核验身份、原件与可信输入指纹、来源归属、文件格式及执行记录,不重新 OCR、不复算窗口或核心指标、不改写结论和正文。业务结论和平台摘要来自 Agent 的 JSON;算法与文字正确性由 Agent 按 Skill 核对,执行成功本身不证明结论正确。 原始输入、规则版本、实际操作和最终结果保存在 `main_fan_workflows`、`main_fan_analyses`。派生文件在会话 `/workspace/`,宿主另存受保护的代码快照和执行凭据。工作台显示实际操作,不预设七阶段;探索步骤预算默认480,由 `AGENT_EXPLORATION_RECURSION_LIMIT` 控制。详细交付格式见 Skill 下的 `references/workbench.md`。 ## 2026-10-19 改造验证 563 项离线回归通过,包含实际 macOS 沙箱中的 PDF 渲染、库加载、文件/网络隔离、执行失败反馈、输出哈希、来源归属,以及真实 Agent 图的写代码/执行/提交与协调委派。网关取数连接保留,默认测试 API 使用完整 Agent。 真实 `qwen3.5-35b-a3b` 扫描件实测中,Agent 自行渲染并查看原页、编写解析/切换分析/报告脚本,得到合成样例的6分钟切换间隔、3800→3610 m³/min稳定风量与5%差异。测试程序随后直接提交其未修改的脚本输出,宿主归档及平台映射通过;这一步不是模型自行完成提交。 最后模型调用被提供方 `429 insufficient_quota / Allocated quota exceeded` 中断,文本PDF最终用例与扫描PDF的完整自动提交尚未完成验收。扫描件报告仍发现电动机型号字段吞入下一行、Measured误标为折算两处质量问题;已补强Skill自检,因配额不足未复测。不得将上述记录表述为真实业务准确性已经验收。 机器可读记录位于 `data/main_fan_live/20261019_sandbox_verification.json`,含测试范围、已验证指标、已知问题及恢复配额后的复测命令。测试使用独立SQLite和checkpoint、合成PDF与时序,未连接真实取数网关或推送正式结果。 ## 网页报告输出 最终答复是主通风机与备用风机一致性整体报告,重点对比两台风机测定报告中同叶片角度、同频率的绘图数据表,计算风压、功率和效率差异,逐工况给出指标、阈值和结论,无需重新画曲线。风量点不同只在共同实测范围内插值对齐,不按原表行号比较,禁止外推。型号、额定功率和电动机配置作为基础核对,不能代替性能通过;角度或频率未知时不判同工况一致。 性能表默认阈值为风压平均偏差≤5%、最大偏差≤10%、功率最大偏差≤10%、效率最大差≤5个百分点;每台同工况至少4个有效测定点,共同风量范围覆盖双方各自范围至少80%。数据条件满足且指标均不超限才通过,超限列明差异,不足标记无法判断。具体计算和记录要求见Skill的`references/performance-tables.md`,阈值取本轮冻结规则,不由Agent临时决定。 综合核心项为报告性能表格一致性、切换间隔和前后监测风量差。核心项证据充分且通过才为`NORMAL`;有效性能指标或监测指标一般超限为`WARNING`;达到另行配置且证据有效的严重线才为`ABNORMAL`;关键对照不足为`UNDETERMINED`。监测证据不足时仍保留已完成的报告性能对比结论。基础铭牌一致、历史检测合格和任务执行成功均不能代替本次性能通过。 报告使用 m³/min、Pa、kW 和 Hz,效率差用百分点,保持原始值与换算依据。当前本地批次的开停、切换和监测风量直接采用`prepared_data.agent_data_path`(导出为`给Agent的数据.json`)中的`states`、`switch_candidates.flow/comparison`及`notes`,不重新取数、定位切换、选择风量点或计算稳定窗口。候选`time_range`不作准确切换耗时,零值保留,缺失或编号对应限制照实呈现;仅摘要截断时读取剩余整理明细。模拟数据明确标注来源。 规则和阈值配置位于 `examples/main_fan/equivalence_rules.json`,可通过 `MAIN_FAN_RULES_FILE` 替换。初始配置来自需求中的建议值,不表述为法规限值。执行状态与综合判定状态分开:部分项目有结果时报告只陈述这些结果,Agent 应在交付 JSON 中明确未评估项目和证据限制。 自主模式的提取事实与计算细节保存在 `assessment.agent_result.details`,来源引用在 `assessment.evidence`,代码执行记录在 `assessment.exploration`;不再由固定算法生成 `analysis`。网页工具详情默认折叠,文件来源保留显示;最终正文恢复 Agent 实际提交的原件。 单机报告版本的历史实测记录位于 `data/main_fan_live/20261014_report_style_5eda22f9/`;该版本当时只分析1号,当前默认流程已改为双机。显式设置 `paired_assessment=false` 仅用于旧单机接口兼容。 双机版本网页实测记录:`data/main_fan_live/20261014_pair_verified_20a71196/`。实际请求为“审核众源煤业主通风机及备用是否一致”,从原件模拟网关下载26页PDF和两张系统截图,16次实际视觉模型调用(含一次图注分类回读),未使用旧缓存或人工转录。两台各44行数据,88行公式检查;30、40、50Hz均形成共同风量区间的数据比较。截图识别出另一矿井的设备与风速,未混入本矿井报告。正文约2500字符(含Markdown表格,不计SVG编码),只含有数据的参数、性能和实际资料差异章节。 文档识别对风量原文单位、轴功率含义和表图工况分类进行一致性检查,疑似记录自动放大回读原页;仍缺明确单位或轴功率依据的行退出计算,原值不按公式反算修正。缓存升级至v4,旧识别记录不自动复用。 ## 历史服务的资料与实测记录 下列原件核验和历史实测基于此前固定服务,不代表本次自主探索改造已完成模型联调或测试。 当前目录有一份 26 页扫描 PDF,`风机数据截图/` 子目录内有两张裁切截图。原件模拟网关递归建立文件索引。PDF 包含陕西众源煤业集团有限公司的 1、2 号主通风机报告,编号分别以 `0003`、`0004` 结尾;检验日期为 2022-03-18、2022-03-17,报告所载建议下次日期为 2025-03-17、2025-03-16。 截图属于神木市瓷窑塔矿业有限公司,不能与上述报告合并。只转录完整可见行:设备 `00000540` 为“地面主通二号二级/开”,`00000541`、`00000542` 为“一号一级/停”“一号二级/停”;开停截图未提供采集时间。测点 `2000000TJFJ010038` 为“一号风机风速”,值 0 m/s、状态正常,时间 2026-10-13 10:13:00;下方温度行不完整,未转录。 [reviewed_sample.json](../examples/main_fan/reviewed_sample.json) 是旧离线联调夹具,保存人工核验转录、原文件 SHA256 和 PDF 物理页码;生产自动识别和原件实测不加载它。`mock-ci`、`mock-zy` 是模拟编码,实际矿井编码未知。 1 号报告物理页 8 的现场运行表工况静压为 523.57 Pa、效率 64.77%;物理页 11 曲线图注为 511.73 Pa、63.30%。自动识别把图注与数据表分开保存,保留两处差异供复核。旧离线夹具只覆盖身份和各 7 行 30 Hz 现场数据。 ## 规则边界 | 输入 | 实际处理 | |---|---| | 矿井编码或名称冲突 | 排除记录;不模糊合并公司名称 | | 名称、编号、位置线索 | 同矿井风机号形成 candidate;PLC1、一级/二级不作风机号 | | 人工确认关联 | 须提供 point_code、fan_id、confirmed_by、reason;冲突仍阻断 | | 重复、断线、缺时、过期、未来时间、未知单位 | 保留发现,排除当前计算 | | 单点风速 | 不直接换算风量;缺断面与平均风速时说明缺件 | | “风压”、电输入功率 | 不冒充风机静压增量或轴功率 | | 当前效率 | 唯一、已确认关联、有效且同步的 Q、静压、轴功率才计算 | | 历史报告 | 同矿井且对应目标风机时复核同一行,不混用现场和折算值 | | 建议检验日期已过 | 提示核查更新报告,不直接认定当前不合格或违法 | | 开停快照 | 不根据单台/单级停机推断全矿停风或备用能力 | 静压效率:`eta = Q[m3/s] × p_static[Pa] / (1000 × P_shaft[kW]) × 100%`。支持显式 m3/min→m3/s、kPa→Pa、W→kW 换算。不把静压功率/电输入功率误称为风机轴功率效率。 时效默认 600 秒,同步窗口 120 秒,分别配置 `MAIN_FAN_FRESHNESS_SECONDS`、`MAIN_FAN_SYNC_SECONDS`;它们是数据筛查参数,不是工程安全限值。无时区时间按 Asia/Shanghai 解释。`evaluation_time` 可指定回放时刻并保存在输入;默认当前时间。 `status=completed` 表示初步筛查完成;`judgment_scope=preliminary_screening`,工程状态保持 `review_required` 或 `insufficient_data`,不自动生成红橙黄蓝等级。没有任何本矿井可用资料时为 `needs_materials`。结果保存 `main_fan_analyses`,发布时恢复数据库原件;模型不能改写工具数值后推送。队列工具使用服务端输入,不能自行增加人工绑定。 历史规范仅作为原报告事实保留。当前规程基准核对了应急管理部第 17 号令、2026-02-01 施行的[《煤矿安全规程》](https://www.mem.gov.cn/gk/zfxxgkpt/fdzdgknr/gz11/202508/t20250804_553279.shtml)第 177–179 条。连续运转、备用、改造/换叶片后测定、反风能力需要配置、试验或过程记录;样例不足以核验,不用历史条款数值做当前自动执法判定。规则核对日期:2026-10-14。 ## 使用真实模型识别原件 ```bash .venv/bin/python scripts/run_main_fan_document.py \ --document '/Users/gaoshutao/Project/煤科/陕西局代码/测试数据/通风机性能测定报告.pdf' \ --mine-name '陕西众源煤业集团有限公司' --fan-name '1号通风机' --agent ``` 默认 `document_cache_policy=refresh`,创建独立 SQLite 与 checkpoint,调用实际 `main_fan` LangGraph 和业务工具。矿井名称只用于任务身份筛查,不作为识别模型的预填答案。不导入人工转录,不使用已有识别缓存,不调用真实陕西局网关。原件复制到本会话附件目录,并验证运行前后的 SHA256 一致。 结果保存在新的 `data/main_fan_live//result.json`。`--agent` 使用自主模式,Agent 自行选择证据范围,脚本、事实、选页审计及模型原始响应留在本会话 `workspace/exploration//`,受保护的代码快照留在同会话 `python_runs/`。不加 `--agent` 才直接调用历史固定服务,其 `document/<附件标识>/facts.json`、`audit.json` 和 `call-*.json` 保存自动提取与逐页调用记录。`--output` 可指定新目录,已有实测数据库不覆盖。 2026-10-14 在本机配置的 `deepseek-flash` 上完成真实模型验证: | 输入与记录目录 | 实际识别 | 业务执行 | |---|---|---| | 原始 `通风机性能测定报告.pdf` → `data/main_fan_live/20261014_scan_verified/` | 26 页全部走视觉、13 次实际识别调用;2 份报告、88 行性能数据、2 条数字图注 | 实际 `main_fan` 图;目标 1 号风机 44 行公式复核,最大效率偏差 0.0071 个百分点 | | 合成带文本层 PDF → `data/main_fan_live/20261014_native_verified/` | 1 页走文本、1 次实际识别调用,未发送图片 | 实际 `main_fan` 图;合成数据复核 60% | 原件 SHA256 为 `46eb24ec6c0f23907b4dc6f67af00f7907fa15d7acbf04ce13d142a247cfc92a`,扫描识别调用用量合计 76,071 tokens。两种实测均为 `cache_used=false`、`transcript_seed_loaded=false`,数据库缓存方法为 `automatic_extraction`;缓存是在本次完整识别后才写入的。文本 PDF 是独立软件测试输入,不是把原扫描报告转录后冒充可解析 PDF。混合 PDF 另有逐页路由回归测试。 抽查原件物理页 3、8、11、16、21、24 的身份、日期、运行工况和图注。保留 1 号表格 523.57 Pa/64.77% 与图注 511.73 Pa/63.30% 的原件差异;这属于资料复核,不能用历史“合格”结论证明当前设备状态。完整事实未逐字人工验收。全量 381 项测试、结构自检、依赖锁及打包白名单校验通过。 ## 离线模拟全链路 ```bash .venv/bin/python scripts/demo_main_fan.py \ --data-dir '/Users/gaoshutao/Project/煤科/陕西局代码/测试数据' ``` 该兼容脚本用两个临时本地端口启动取数网关和独立结果接收平台,在新的 `data/main_fan_demo//` 创建业务 SQLite、checkpoint、接收平台 SQLite 和 `result.json`,不读取/修改本机 `.env`。会话附件进入框架 `data/workspaces/`,不改变原测试目录。脚本显式调用历史固定服务,经持久化队列、HTTP下载和独立结果推送进行离线联调;不调用外部模型或真实陕西局服务,也不验证自主探索能力。 默认三场景:`original` 原截图与异矿报告,`report` 原报告历史复核,`synthetic` 合成量及合成确认(60 m3/s、500 Pa、50 kW→60%)。随后验证本地业务 stop/start,检查网关没有收到注册、上传或结果推送请求。第三种来源标注 synthetic。可用 `--scenario original|report|synthetic` 单独运行;`--output` 指定新目录,已有演示库不覆盖。 原 PDF 按真实哈希核验,离线识别用人工转录缓存。这验证业务与计算,不能替代视觉模型识别质量验收。 ## 单独启动模拟网关 ```bash .venv/bin/python scripts/mock_shaanxi_gateway.py \ --data-dir '/Users/gaoshutao/Project/煤科/陕西局代码/测试数据' --port 8766 --raw-files ``` 默认仅监听 127.0.0.1。`--raw-files` 按当前测试目录直接建立 PDF/图片原件索引,不加载人工转录 JSON;文档中的事实由业务智能体实际识别。启动脚本另外加载下述两份明确合成的时序JSON及其风机身份,不从人工转录夹具填充报告或监测值。原件变动后需重启网关刷新索引。不加该参数则沿用旧离线夹具。业务配置示例: ```dotenv SHANXI_GATEWAY_ENABLED=1 SHANXI_GATEWAY_URL=http://127.0.0.1:8766 SHANXI_GATEWAY_TOKEN=mock-internal-token SHANXI_FILE_BASE_URL=http://127.0.0.1:8766 MAIN_FAN_AGENT_CODE=mock-main-fan MAIN_FAN_DEVICES_PATH=/mock-platform/main-fan/devices MAIN_FAN_POINTS_PATH=/mock-platform/main-fan/points MAIN_FAN_REPORTS_PATH=/mock-platform/main-fan/reports MAIN_FAN_SERIES_PATH=/mock-platform/main-fan/series MAIN_FAN_OPERATION_LOG_PATH=/mock-platform/main-fan/states ``` 这些路径、凭证只属于模拟服务。取数身份在网关中预先授权,应用无需运行时注册。真实路径、字段和取数编码需同事提供,没有预设陕西局存在 `/mock-platform/` 接口。 结果接收另起 `.venv/bin/python scripts/mock_result_platform.py`(8767端口),配置 `RESULT_PLATFORM_ENABLED/URL/TOKEN` 后独立推送。正式结果格式、四种编码及推送查询接口见 [独立结果平台](BUSINESS_AGENTS.md#独立结果平台)。 ### 开停与风量模拟时序(10分钟、10%规则) 新增 `examples/main_fan/mock_switch_states.json` 和 `examples/main_fan/mock_airflow_series.json`,统一声明 `schema_version=1`、`data_kind=synthetic`、矿井身份、时区、采样间隔和 `series`。每条曲线有 `fan_id`、`samples=[{"observed_at":"带时区时间","value":数值}]`。开停曲线是整机级,`state_scope=whole_fan`,0为停、1为开;风量曲线另有 `sensor_id`、`unit=m3/min` 和显式的 `mapping_confirmed`。两台设备绑定 `mock-zy` 下的 `mock-report-fan-1`、`mock-report-fan-2`。 默认回放2026-10-14 09:20至11:00,每60秒一条,每类数据两台各101点。1号10:00停机,2号10:06启动,双停过渡6分钟。1号切换前风量均值3800 m³/min,2号切换后3610 m³/min,差异5%;停机零值和启动过渡也保留在原始曲线中。 网关代理 `/mock-platform/main-fan/states` 返回整机状态记录,`/mock-platform/main-fan/series` 返回两条风量序列;智能体从状态变化识别切换,不使用预写的切换结论。原始JSON可查看 `http://127.0.0.1:8766/mock/main-fan/curves/switch-states` 和 `/mock/main-fan/curves/airflow`。更换样例目录使用 `--operating-data-dir`。 两项规则独立计算:原机停机至另一台启动的非负间隔 ≤10分钟;`|切换后稳定均值-切换前稳定均值| / 切换前稳定均值 ×100% ≤10%`。等于边界也通过;涨幅、降幅均按绝对差异判断。先启动再停机时另外记录重叠时长,停机等待间隔计0。前窗口取首次切换动作前5分钟之外的30分钟,后窗口取切换完成后5分钟之外的30分钟,默认有效样本完整率≥90%、变异系数≤5%。切换前风量为0不形成比例结论。 整机未知、冲突或连续状态记录缺测超过两个采样间隔时,不跨缺口推断停启时间;未来样本不能补足窗口。没有可比风量时仍能单独保留已证明的切换间隔;只有切换时刻而无停机、启动时刻的旧记录,不自动判定间隔合格。两项通过只表示本次切换与风量规则通过,不替代整体同等能力判识。上述10分钟/10%为本次用户指定业务规则,规则版本随配置归档。 本地网页测试可打开 `http://127.0.0.1:8080/`,在新会话选择“矿井主通风机研判智能体”,只需发送“审核众源煤业1号主通风机”。无需填写矿井编码、工具名或网关/缓存参数,也无需手动上传:业务工具从模拟网关取得目录和文件鉴权,直连下载原 PDF,实际调用视觉模型重新识别。网页聊天的结果保存在当前应用 SQLite;平台推送另由业务任务 API 或网关事件任务触发。 ### 简短请求与目标身份目录 `prepare_main_fan_task` 支持目标文字或 `{"target":"众源煤业1号主通风机"}`;业务任务 API 的 input 同样只需一个 target 字段。target 也可以是已配置的风机标识。完整 MainFanInput 协议仍兼容。 ```json {"input":{"target":"众源煤业1号主通风机"},"publish":false} ``` 服务端设置示例: ```dotenv MAIN_FAN_TARGETS_FILE=examples/main_fan/mock_targets.json MAIN_FAN_DEFAULT_DOCUMENT_CACHE_POLICY=refresh ``` 目标目录保存矿井名称、经明确配置的别名、矿井编码、风机名称及标识;不包含报告日期、监测数值或人工转录。当前测试目录配置众源煤业 1、2 号风机。自然语言前后缀和“一号/1号/1#”可归一化,但不会模糊搜索公司名称或根据文件内容猜平台编码。未唯一匹配时先询问业务对象,未确定身份前不会取数或分析。 简短请求未带本地材料且网关启用时自动 `use_gateway=true`,默认刷新原件识别。明确提供的附件和显式 `use_gateway`/`document_cache_policy` 会保留;关闭全局网关不会暗中启用 Mock。完整协议输入维持原默认值,队列以服务端已验证输入为准,模型不能通过改写目标或人工绑定改变任务。 测试目标目录明确绑定 `http://127.0.0.1:8766`;真实环境需配置正式身份目录并更新其网关绑定,不能把 Mock 编码用于真实网关。新增矿井在目录里增加身份与别名即可,不需要修改业务 prompt。地址和凭证仍由服务配置决定,用户一句话不能切换网关环境。 2026-10-14 已在运行中的网页仅发送“审核众源煤业1号主通风机”验证完整链路,无附件、无技术参数:自动网关取数和直连下载,实际识别 26 页、13 次文档模型调用,对目标风机复核 44 行;`cache_used=false`、`transcript_seed_loaded=false`。实测记录位于 `data/main_fan_live/20261014_short_request/`,网页会话为 `0a159bbb91dc43c39f9296f35c0fa821`。目标文字、单 target 对象及模型偶尔返回的 JSON 字符串都可解析;全量 411 项测试通过。 用网关内部 Bearer 和 `X-Agent-Code: mock-main-fan` 向 `POST /mock/tasks` 发送 `{"scenario":"original","taskId":"local-001"}`,产生 `main_fan.judgment` 事件。须业务端已注册并订阅;单独启动模式使用你配置的真实语言模型,演示脚本才是离线编排器。 管理模拟:`GET /openapi/agent/manage?agentCode=mock-main-fan&action=stop&token=mock-platform-manage-token`,action 支持 start/stop/restart/status/logs。停止关闭 SSE,重连返回 403;本业务执行和推送暂停,启动后恢复订阅。对方真实网关的指标/日志口径仍按双方约定。 ## 历史固定服务的 PDF 识别实现(自主模式不调用) 独立 PDFium 进程逐页判别,不按文件名选择识别方式: | 页面 | 处理 | |---|---| | 可读、完整的文本层 | 直接提取文本,由 `MAIN_FAN_TEXT_MODEL` 整理结构;不发送图片 | | 空文本、损坏字符、零散水印或大图配少量文字 | 渲染为 PNG,由 `MAIN_FAN_VISION_MODEL` 读取原页 | | 曲线图页 | 走视觉;仅录印出的工况数字,不猜曲线像素 | | 文本与扫描页混合 | 分别按每页路由,合并到同一文档事实 | 默认最多 32 页,扫描页最长边 2200px,截图最长边 1700px;同一路由每批 2 页。文字模型默认沿用主模型,视觉模型必须明确配置且支持图像。以下设置可独立指定模型、接口和凭证;留空的接口/凭证沿用项目设置: ```dotenv MAIN_FAN_VISION_MODEL=deepseek-flash MAIN_FAN_TEXT_MODEL=deepseek-flash MAIN_FAN_VISION_BASE_URL= MAIN_FAN_VISION_API_KEY= MAIN_FAN_TEXT_BASE_URL= MAIN_FAN_TEXT_API_KEY= MAIN_FAN_DOCUMENT_TIMEOUT=180 MAIN_FAN_DOCUMENT_BATCH_PAGES=2 MAIN_FAN_DOCUMENT_MAX_TOKENS=20000 MAIN_FAN_MAX_DOCUMENT_PAGES=32 MAIN_FAN_ALLOW_MANUAL_CACHE=0 ``` DeepSeek 的文档识别显式关闭思考,使用 JSON 输出模式。视觉与文本分别计入 `main_fan_ocr`、`main_fan_document_text`,不作为主回答流输出,也不重复计费;部署时配置对应模型单价,提供方没有 usage 时标记 estimate。 提取结果校验 schema、PDF 物理页、日期标签及单位,按报告编号累积。JSON/schema 结构异常会重新读取同批原页一次,仍失败则报缺件;截断输出直接报错,避免把不完整结果缓存为成功。数据行或图注的数值与其印出的效率相差超过 0.5 个百分点时,扫描页增加原图放大区域,再读一次;公式只触发复读,不反算覆盖数字,仍不一致则保留原值并提示核验。每次复读同样记录实际模型响应与用量。 旧服务缓存版本为 `main-fan-facts-v4`,支持 `document_cache_policy=auto|refresh|off`;自主工作台只保留本轮识别,不读写共享缓存。不同页出现真实字段冲突时清除该字段并保留候选值与页码;当前自主模式由 Agent 修改自己的提取代码和派生事实后重新执行,代码快照和交付文件哈希留作追溯。制造年月不能替代检验日期,缺 Q/静压/轴功率的行不进入公式复核。 证据方法区分 `text_extraction`、`model_extraction`,保留源文件 SHA256、原文和物理页码。自动识别仍需核对原页,公式正确不证明 OCR 数字正确。业务结果以 SQLite 原件为准,Agent 的代码、派生输出和报告都有各自记录;当前自主模式的平台编码来自 Agent 脚本交付,不由宿主重新计算。 ## 旧人工夹具的显式导入 离线调试需要时可以导入已核验的转录: ```bash .venv/bin/python scripts/import_main_fan_facts.py \ --document '/Users/gaoshutao/Project/煤科/陕西局代码/测试数据/通风机性能测定报告.pdf' \ --facts examples/main_fan/reviewed_sample.json ``` 该命令写当前配置的本地库,验证哈希、物理页码和原文,不修改原件。自动识别默认拒绝该缓存;旧 `demo_main_fan.py` 才显式开启人工缓存。其他人工转录使用 `DocumentFacts` JSON 和 `--sha256` 原文档哈希。 JSON 适配在 `extensions/main_fan/data_source.py`,目前接受 `items`、`data.items` 或 `data` 数组;字段使用业务 schema 的 snake_case/明确声明的 camelCase。真实响应不同需增加明确映射。报告目录只需文件 ID、版本和下载路径,由我们取鉴权、直连下载、存文件和索引;模拟网关拒绝拿内部凭证直接下载。 ## 代码与验证 - `algorithms/main_fan/`:事实 schema、确定性关联/筛查/计算、隔离 PDF 渲染。 - `extensions/main_fan/`:工具、网关 JSON 适配、文档提取、结果和提示词。 - `db/main_fan_store.py`:文档缓存、分析原件;`business_files` 存队列下载索引。 - `mock_gateway/main_fan.py`:取数鉴权、JSON代理和文件系统;旧协议接口仅保留兼容测试。 - `extensions/result_platform/`:独立结果平台的地址、鉴权、回执校验及客户端。 - `mock_result_platform/`:独立结果接收服务,SQLite存档、幂等及冲突检测。 - `tests/test_result_platform.py`:四种编码契约、实际规则映射、取数/结果解耦、重试、导出及所有权校验。 - `tests/test_main_fan.py`:原始数据边界、量纲/身份/时效/同步、真实图、PDF 渲染、原件及模拟网关。 - `tests/test_main_fan_documents.py`:文本/扫描/混合路由、真实调用协议、缓存隔离、字段冲突与截断处理;其中 PDF 是合成测试输入,不是原报告转录。 运行 `.venv/bin/python -m pytest` 和隔离存储配置下的 `scripts/selfcheck.py`,无需真实网关或模型。