Metadata-Version: 2.4
Name: atlisp-dwg-kb
Version: 0.2.0
Summary: Ontology-based CAD DWG knowledge graph system
License-Expression: MIT
Project-URL: Homepage, https://github.com/atlisp/dwg-knowledge-base
Project-URL: Repository, https://github.com/atlisp/dwg-knowledge-base
Keywords: cad,dwg,knowledge-graph,ontology,rdf,bim
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: rdflib>=7.0
Requires-Dist: networkx>=3.0
Requires-Dist: scipy>=1.10
Requires-Dist: fastapi>=0.100
Requires-Dist: uvicorn>=0.20
Requires-Dist: pydantic>=2.0
Provides-Extra: dwg
Requires-Dist: ezdxf>=1.0; extra == "dwg"
Provides-Extra: render
Requires-Dist: ezdxf>=1.0; extra == "render"
Requires-Dist: matplotlib>=3.7; extra == "render"
Requires-Dist: Pillow>=9.0; extra == "render"
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: ui
Requires-Dist: gradio>=4.0; extra == "ui"
Provides-Extra: neo4j
Requires-Dist: neo4j>=5.0; extra == "neo4j"
Provides-Extra: watch
Requires-Dist: psutil>=5.9; extra == "watch"
Provides-Extra: service
Requires-Dist: pywin32>=306; sys_platform == "win32" and extra == "service"
Provides-Extra: tray
Requires-Dist: pystray>=0.19; extra == "tray"
Requires-Dist: Pillow>=9.0; extra == "tray"
Provides-Extra: all
Requires-Dist: ezdxf>=1.0; extra == "all"
Requires-Dist: matplotlib>=3.7; extra == "all"
Requires-Dist: Pillow>=9.0; extra == "all"
Requires-Dist: mcp>=1.0; extra == "all"
Requires-Dist: neo4j>=5.0; extra == "all"
Requires-Dist: psutil>=5.9; extra == "all"
Requires-Dist: pywin32>=306; sys_platform == "win32" and extra == "all"
Requires-Dist: pystray>=0.19; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# atlisp-dwg-kb

基于本体(Ontology)的 CAD 图纸知识库管理系统。将历史 DWG 图纸转化为可检索、可追溯的知识图谱。

## 功能

- **DWG 元数据提取**: 从 DWG 文件提取图层、块、文字标注、**尺寸标注**等元数据（支持多进程并行、断点续传）
- **天正/代理对象诊断**: 提取时统计 `ACAD_PROXY_*` 与未知实体占比，输出 `risk=low/medium/high` 解析风险报告
- **视觉渲染轨**: DWG/DXF → PNG/SVG（ezdxf drawing + matplotlib），供视觉大模型理解场景
- **双轨校验**: 视觉模型断言 vs 结构化硬数据比对——**冲突时一律以数据为准**并输出预警（如视觉说“办公室”、数据写“会议室”）；每条断言附 `evidence`（证据来源+命中值），报告与提取 JSON 均带 `schema_version`
- **确定性比例尺**: 图签/文本中的 `1:50`、`1/4"=1'-0"`、`NTS` 仅用正则解析（`ratio`/`kind`），不按比例永不参与量化；`$INSUNITS` 附加 `unit`/`value_mm`
- **图号互引 / 矢量连通**: 图内文字提取 `建施-03`、`3/A-501` 等互引（build 时按全库图号建 `references` 边）；线段端点容差并查集把 block 串成 `connected_to` 边（P&ID/电气回路，confidence=1.0）
- **待审清单**: 解析风险 high/medium、代理对象过多、校验冲突自动进 `review` 队列（只生成清单，不自动处理）
- **金标评测**: `verify --eval tests/fixtures/verify_eval` 零容忍回归——`accuracy` 必须为 1.0
- **OWL 本体**: 形式化知识表示（含 Mechanical/Process 专业、VerificationReport 校验报告）
- **RDF 三元组存储**: rdflib 图存储，规模随图纸量增长
- **自动推断**: 设计演进链、跨专业关联、共享构件
- **SPARQL 查询**: 支持自然语言关键词和 SPARQL 查询
- **REST API**: FastAPI 服务端 (20+ 端点，含 `/render`、`/verify`、`/review_queue`)
- **Web 管理界面**: FastAPI 内置浏览器界面 (`/ui`)，零额外依赖
- **MCP Server**: `read_dwg` / `render_dwg` / `diagnose_dwg` / `verify_dwg` / `review_queue` / `query_kb` / `search_drawings` 工具，供 Claude Desktop、Cursor 等 AI 客户端调用
- **系统托盘**: Windows 托盘管理器 (服务控制/统计/查询/日志)
- **Neo4j 导出**: 可选导出到 Neo4j 图数据库

## 对设计人员的价值

十年积累的图纸散落在几百个项目目录里，找一张图要翻目录、记文件名、问老同事。本系统把这些 DWG 变成**可提问的档案库**：

### 1. 秒级找图，告别翻目录

```
"列出所有暖通图纸"
"2023 年港达项目有哪些结构图"
"所有节点详图"
```

按专业（建筑/结构/暖通/电气/给排水）、项目、年份、图纸类型直接筛选，不再依赖个人记忆和目录命名习惯。

### 2. 版本与审图轮次自动追溯

系统自动识别 `_t3`、`(1)`、日期等版本标记，以及"意见/审图/二次意见/答复"等审图轮次关键词，还原每张图的**设计演进链**：

- 这个节点大样改过几版？每版属于哪一轮审查？
- 初版和最终版之间图纸经历了什么过程？

### 3. 跨专业关联一目了然

自动发现图纸间的坐标/引用关系和共享构件——做专业协调时，快速定位"这张建筑图对应哪些结构图、机电图"，减少扯皮和漏配。

### 4. 标准件与详图复用

识别跨项目重复使用的图块（标准节点、设备图例、大样），新项目直接参考过往做法，**不再重复画详图**。

### 5. 团队经验沉淀

历史方案（绿建、人防、消防等专题图纸）从"存在某人电脑里"变成全团队可检索的素材库，人员流动不再带走经验。

> 定位：它是**图纸档案的检索与追溯工具**，不替代 CAD——设计、绘图仍在 CAD 中完成，本系统解决"找、查、追、复用"的问题。

## 安装

```bash
# 本地开发（全部依赖）
pip install -e ".[all]"

# 按需选择 extras
#   [dwg]     ezdxf (DWG 提取)
#   [render]  ezdxf + matplotlib + Pillow (视觉渲染轨)
#   [mcp]     mcp (MCP Server，供 AI 客户端调用)
#   [neo4j]   neo4j (图数据库导出)
#   [watch]   psutil (CPU 空闲自动构建)
#   [service] pywin32 (Windows 服务)
#   [tray]    pystray + Pillow (Windows 系统托盘)
#   [ui]      gradio (旧版 Web 界面，已弃用)
#   [all]     以上全部（不含已弃用的 gradio）
```

安装后提供 `atlisp-dwg-kb` 命令；未安装时也可从仓库根直接运行：

```bash
PYTHONPATH=src python -m atlisp_dwg_kb.cli <command>
```

系统依赖：DWG 提取（`atlisp-dwg-kb extract`）需要 `[dwg]` extra（ezdxf），且需要 **dwg2dxf**（libredwg）——不在 Python 依赖里，需在系统 PATH 中（或 Windows 手动放入包内 `atlisp_dwg_kb/bin/`，该目录被 gitignore、PyPI 包不含）：

```bash
# Ubuntu/Debian
sudo apt install libredwg-tools
# Windows：从 GNU libredwg（如 msys2 构建）获取 dwg2dxf.exe 及依赖 DLL，
# 加入 PATH，或放入 site-packages/atlisp_dwg_kb/bin/（_find_dwg2dxf 优先查找该目录）
```

## 数据处理流程

```
raw/ (DWG 源) ──atlisp-dwg-kb extract──▶ dwg-extract/ (JSON+MD)   【精确数据轨】
                                               │
                          atlisp-dwg-kb render │ (可选) PNG/SVG     【视觉理解轨】
                                               │      │
                          atlisp-dwg-kb verify ◀──────┘ → 校验报告 (数据为准+预警)
                                               │
                              atlisp-dwg-kb build
                                               │
                                    ~/.dwg-kb/ (RDF + 图谱)
                                               │
                     query / stats / API(:8260) / UI(:8260/ui) / mcp / export-neo4j
```

## 使用

### 1. 提取 DWG 元数据

```bash
# 试跑 50 个文件
atlisp-dwg-kb extract raw --limit 50

# 全量提取（默认 8 进程并行，--workers/-w 可调）
atlisp-dwg-kb extract raw -w 8

# 断点续传（跳过 dwg-extract/.processed.json 中已处理的文件）
atlisp-dwg-kb extract raw --resume

# 只处理某年份子目录 / 指定输出目录
atlisp-dwg-kb extract raw --project 2025
atlisp-dwg-kb extract raw -o /data/dwg-extract
```

- 源目录需按年份组织（2016–2025），项目名取自年份目录的下一层
- 输出为每张图一个 `<stem>.json` + `<stem>.md`，保留源目录结构
- 另有 `extraction.log` 与 `summary.json` 记录运行日志和统计

#### 元数据内容

每张 DWG 经 `dwg2dxf` → DXF → `ezdxf` 提取，JSON 主要字段：

| 字段 | 内容 |
|------|------|
| `source_file` | 源 DWG 路径 |
| `layers` | 图层：名称、颜色、线型、开关/冻结/锁定、描述 |
| `blocks` | 块定义（排除 `*` 内部块）：名称 + 属性 tag/text |
| `texts` / `text_items` | 模型空间 + 块定义内 TEXT/MTEXT 全文（单条上限 10 万字符；`text_items` 带图层+坐标，最多 10 万条，供定位分析） |
| `text_paragraphs` | 按阅读顺序（上→下、左→右）聚类的文字段落，便于还原**工程说明**等密集多行文本 |
| `layer_text_stats` | 各图层文字数量分布 |
| `block_attribs` | 块属性实例（block/tag/value） |
| `title_block` | 图签/标题栏：图号、图名、工程名称、建设单位、比例、日期、阶段、工号、版次、设计/制图/校对/审核等 |
| `dimensions` | 尺寸标注：实测值 + 图层 + 坐标（工程尺寸硬证据） |
| `diagnostics` | 解析诊断：`ACAD_PROXY_*` 代理对象数、未知实体类型、`risk=low/medium/high`、dwg2dxf 转换警告 |
| `xrefs` | 外部参照 XREF（名称+路径），用于图纸间真实关联 |
| `extents` | 模型空间图形范围（`$EXTMIN/$EXTMAX`，回退 bbox） |
| `hatches` | 填充数量 + 图案 top20 |
| `media` | 光栅图像、OLE 对象计数 |
| `entity_stats` | 实体类型统计（LINE/INSERT/…） |
| `layouts` / `layout_texts` | 布局名 + 图纸空间文字全文（图签常在布局中） |
| `*_count` | 层/块/文字计数 |

文本尽量完整：内容理解暂不依赖几何曲线，工程说明等密集文字不做条数/短截断（仅防病态超长的安全上限）；Markdown 同步输出全部段落与 `text_items` 定位。

下游用途：专业识别（图层名+文字关键词）、本体 RDF 填充、版本/审图轮次（文件名关键词 + `title_block.revision`）、跨专业关联（`xrefs` + 共享图层/块）、解析风险评估（`diagnostics`）。

### 1.5 双轨校验（读懂图纸的智能体核心）

核心思路：**不迷信视觉模型**——精确数据轨（ezdxf）是硬证据，视觉轨（渲染图 + VLM）只负责理解场景，冲突时以数据为准并预警。

```bash
# 视觉渲染轨：DWG/DXF → PNG/SVG（[render] extra）
atlisp-dwg-kb render raw/2025/某项目 -f png --limit 20
atlisp-dwg-kb render 某图.dxf -f svg
atlisp-dwg-kb render 某图.dxf --tiles   # 额外输出 VLM 瓦片(1536+192重叠)+全局缩略+tiles.json

# 解析风险（天正/代理对象）
atlisp-dwg-kb diagnose raw/2025/某项目/含代理图.dwg
atlisp-dwg-kb diagnose raw/2025/某项目/dwg-extract

# 双轨校验：渲染图 → 视觉模型断言 → 与结构化数据比对 → 报告
atlisp-dwg-kb verify dwg-extract/某图.json          # 报告写到旁边 <stem>.verify.json
atlisp-dwg-kb verify 某图.dwg --question "这是什么功能房间？"
atlisp-dwg-kb verify --eval tests/fixtures/verify_eval   # 金标评测，accuracy<1.0 退出码 1

# 待审清单（高风险/代理多/校验冲突，只生成不处理）
atlisp-dwg-kb review dwg-extract --json
# UI 双轨校验页 / API GET /review_queue / MCP review_queue 同源
```

校验规则（`verify.py`）：

| 断言类型 | 比对对象 | 冲突处理 |
|---------|---------|---------|
| `room_name` | `texts` 中的房间/功能词 | 视觉说“办公室”、数据有“会议室” → **corrected + 预警** |
| `title_field` | `title_block` 图签字段 | 图签硬数据胜出 |
| `discipline` | 图层+文字关键词判定 | 关键词判定胜出 |
| `number` | `dimensions` / `extents` | 数值不命中 → **不采信**（unverifiable） |

置信度 = `(confirmed + 0.3×corrected) / 总断言数`，写入本体 `cad:confidence_score`。

**VLM 接入**：默认 `mock`（离线确定性，测试/演示用，支持 `<image>.vlm.json` sidecar 注入断言）。接真实服务时：

```bash
export DWG_KB_VLM_PROVIDER=openai_compat          # 任意 OpenAI 兼容端点
export DWG_KB_VLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
export DWG_KB_VLM_MODEL=qwen2.5-vl-plus
export DWG_KB_VLM_API_KEY=sk-...
# 需要: pip install openai
```

> 中文渲染提示：PNG 中文需要系统 CJK 字体（Debian/Ubuntu: `sudo apt install fonts-noto-cjk`），代码会自动探测 Noto/文泉驿/微软雅黑。

### 1.6 MCP Server（AI 客户端调用读图能力）

```bash
pip install -e ".[mcp]"
atlisp-dwg-kb mcp    # stdio transport
```

Claude Desktop / Cursor 配置：

```json
{
  "mcpServers": {
    "atlisp-dwg-kb": { "command": "atlisp-dwg-kb", "args": ["mcp"] }
  }
}
```

工具一览（二进制不进上下文，图片落盘返回路径）：

| 工具 | 说明 |
|------|------|
| `read_dwg(path)` | 结构化元数据（图层/块/文字/尺寸/图签/诊断）——硬证据，优先采信 |
| `render_dwg(path, format)` | 渲染 PNG/SVG，返回文件路径 |
| `diagnose_dwg(path)` | 天正/代理对象解析风险 |
| `verify_dwg(path, question?)` | 双轨校验报告 |
| `review_queue(limit?)` | 待审图纸清单（风险/校验冲突） |
| `query_kb(question)` | 知识库自然语言查询 |
| `search_drawings(keyword?, discipline?, project?)` | 按条件列图 |

### 2. 构建知识库

```bash
atlisp-dwg-kb build --extract-dir raw/dwg-extract
```

产物（默认写入 `~/.dwg-kb/`）：
- `rdf/knowledge.trig` — RDF 三元组
- `graph/knowledge.json` — NetworkX 图谱 + 社区检测
- `graph/graph.html` — 自包含交互式图谱（Sigma.js/WebGL，可直接浏览器打开；构建期算好节点坐标，图结构未变时复用 `graph/layout.json` 缓存，打开即渲染）

### 3. 查询

```bash
atlisp-dwg-kb query "列出所有暖通图纸"
atlisp-dwg-kb stats
```

### 4. 启动服务

```bash
atlisp-dwg-kb serve          # API: http://localhost:8260  (自动打开管理界面 /ui)
atlisp-dwg-kb serve --no-browser   # 仅启动服务，不打开浏览器
atlisp-dwg-kb all            # 同 serve（启动 API + 内置浏览器界面）
atlisp-dwg-kb ui             # 旧版 Gradio 界面 http://localhost:7860 (已弃用)
atlisp-dwg-kb export-neo4j   # 导出 Neo4j Cypher 到 ~/.dwg-kb/graph/
```

浏览器打开 `http://localhost:8260/ui` 即为管理界面，包含 8 个标签页：

| 标签页 | 功能 | 对应 API |
|--------|------|----------|
| 统计概览 | 三元组/图纸/专业/图层统计 | `GET /stats` |
| 智能查询 | 自然语言或 SPARQL 查询 | `POST /query` |
| 图纸浏览 | 按专业筛选图纸 | `GET /drawings` |
| 设计演进 | 追踪图纸版本演进链 | `GET /drawings/{id}/evolution`、`GET /evolution/chains` |
| 跨专业关联 | 关联查询、协调矩阵、共享图层 | `GET /cross_ref/*` |
| 图谱可视化 | 内嵌 Sigma.js/WebGL 交互图谱 | `GET /graph/html`（带 ETag，未变更时 304） |
| 双轨校验 | 渲染预览 + 视觉/数据比对报告 | `POST /drawings/{id}/render`、`POST /drawings/{id}/verify` |
| 构建知识库 | 选择目录与提取方式，无元数据则先提取再构建 | `POST /build`、`GET /fs/list` |

- 管理界面为原生 HTML/CSS/JS，由 FastAPI 直接提供，**无需 gradio 等额外依赖**。
- 「构建知识库」标签页通过「选择目录…」弹出服务端目录浏览器（`GET /fs/list`）选择目录，再选择提取方式后点击「开始构建」，调用 `POST /build?path=<dir>&mode=<mode>`：
  - `auto`（默认）— 目录含元数据（`*.json`）或其 `dwg-extract/` 含元数据 → 直接构建；只含 `*.dwg` → 先提取（`--resume`）再构建；
  - `resume` — 强制提取，跳过 `.processed.json` 中已处理的文件（**补齐缺失/不完整的结果**）；
  - `full` — 强制重新提取所有 DWG（忽略 `.processed.json`，耗时较长）；
  - `build` — 只构建，不提取。
  - 目录既无元数据也无 `*.dwg` → 返回 400 并提示。
- 构建为**后台任务**：`POST /build` 立即返回，页面通过 `GET /build/status` 每秒轮询，实时显示阶段（提取中/构建中/完成/失败）、提取进度条（`[已完成/总数]`）与日志；刷新页面后会自动恢复显示正在进行的任务。
- `POST /build` 仍接受旧参数 `?extract_dir=<path>`；`path` 留空则依次尝试 `DWG_EXTRACT_DIR`、`./dwg-extract`、`~/.dwg-kb/dwg-extract`。
- 交互式 API 文档：`http://localhost:8260/docs`。

### 5. 管理多个 DWG 目录

**合并为一个知识库**：各目录分别提取到不同输出目录，构建时指向它们的共同父目录。`build` 会递归加载所有 `*.json`，同名图纸按源文件绝对路径区分，不会互相覆盖：

```bash
atlisp-dwg-kb extract /data/root1 -o /kb/extract/root1
atlisp-dwg-kb extract /data/root2 -o /kb/extract/root2
atlisp-dwg-kb build --extract-dir /kb/extract        # 合并 root1 + root2
```

**各自独立知识库**：用 `DWG_KB_DATA_DIR` 隔离存储（build / query / stats / serve / ui 需带同一变量）：

```bash
DWG_KB_DATA_DIR=~/.dwg-kb/root1 atlisp-dwg-kb build --extract-dir /data/root1/dwg-extract
DWG_KB_DATA_DIR=~/.dwg-kb/root1 atlisp-dwg-kb query "列出所有暖通图纸"
```

注意：不要让两个源目录提取到同一个 `-o` 目录（每张图输出为 `<相对目录>/<stem>.json`，相对结构重叠时会互相覆盖）。

### 6. 空闲时自动构建（autobuild）

常驻守护：定时采样 CPU，连续多次低于阈值（默认 30%）且源目录有更新时，自动执行 `extract --resume` + `build` 全流程。适合放在夜间/空闲时段自动跟进新图纸。

```bash
# 需要 [watch] extra (psutil) 与 [dwg] extra (ezdxf)
atlisp-dwg-kb autobuild raw

# 自定义：采样 30s，CPU<20%，连续 3 次空闲触发，仅提取 2025 年
atlisp-dwg-kb autobuild raw --interval 30 --cpu-threshold 20 --idle-checks 3 --project 2025

# 只跑一轮（等到空闲构建一次即退出），适合 cron/计划任务
atlisp-dwg-kb autobuild raw --once
```

- 触发条件：`knowledge.trig` 不存在，或源 `*.dwg` / 提取 `*.json` 的 mtime 晚于上次构建
- `--interval` 既是 CPU 采样窗口也是检查周期；需连续 `--idle-checks` 次空闲才触发（默认 3 次 × 60s ≈ 3 分钟）
- 后台/服务场景用 `--log-file <path>` 追加带时间戳的日志
- 按 `Ctrl-C` 退出

## Windows 后台运行（服务）

在 Windows 上把 `autobuild` 注册为服务：开机自启、崩溃重启、无需登录。两种方式任选。

### 方式一：内置命令（pywin32，推荐）

```powershell
pip install -e ".[dwg,watch,service]"     # [service] = pywin32
# 以管理员 PowerShell 运行：
atlisp-dwg-kb service install D:\dwg --interval 60 --cpu-threshold 30 --idle-checks 3 --log-file D:\dwg\dwg-kb-service.log

atlisp-dwg-kb service status
atlisp-dwg-kb service stop
atlisp-dwg-kb service start
atlisp-dwg-kb service uninstall
```

- `service install` 把运行参数写入 `%PROGRAMDATA%\atlisp-dwg-kb\service.json`，再注册并启动服务（`SERVICE_AUTO_START`）。
- 需管理员权限；若 pywin32 的服务宿主未注册，运行 `python Scripts\pywin32_postinstall.py -install`。
- 仅 Windows 可用，其他平台会提示 `Windows service is only supported on Windows`。

### 方式二：NSSM 脚本

1. 下载 [NSSM](https://nssm.cc/download)，将 `nssm.exe` 加入 PATH。
2. `pip install -e ".[dwg,watch]"`。
3. 管理员 PowerShell 运行仓库脚本：

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\windows\install-service.ps1 -DwgRoot D:\dwg
```

管理：

```cmd
nssm status atlisp-dwg-kb
nssm restart atlisp-dwg-kb
powershell -ExecutionPolicy Bypass -File .\scripts\windows\install-service.ps1 -Uninstall
```

注意：提取依赖 `dwg2dxf.exe`（libredwg）——需在 PATH 中，或手动放入包内 `atlisp_dwg_kb/bin/`（PyPI 包不含二进制）。

### 方式三：系统托盘图标（交互式管理）

在 Windows 系统托盘显示图标，提供图形化管理界面：

```powershell
pip install -e ".[dwg,watch,tray]"        # [tray] = pystray + Pillow

# 启动托盘图标（前台运行）
atlisp-dwg-kb tray D:\dwg

# 最小化到托盘启动
atlisp-dwg-kb tray D:\dwg --background
```

功能：
- **服务控制**: 启动/停止/重启 autobuild 服务
- **状态监控**: 实时显示 CPU 使用率、服务状态、最后构建时间
- **手动触发**: 立即执行一次构建
- **日志查看**: 弹窗显示构建日志
- **配置管理**: 查看/修改服务配置
- **知识库统计**: 显示图纸数量、专业分布
- **快速查询**: 通过自然语言查询知识库

## Linux 后台运行（systemd 服务）

在 Linux 上把 `autobuild` 注册为系统级 systemd 服务：开机自启、崩溃自动重启、日志进 journald。以 CPU 空闲守护方式常驻（与 Windows service 行为一致）。

```bash
pip install -e ".[dwg,watch]"      # [watch] = psutil，autobuild 必需
# 系统需安装 libredwg 的 dwg2dxf（在 PATH 中）

sudo ./scripts/linux/install-service.sh --root /media/data/Private/Design
```

安装脚本渲染 `scripts/linux/atlisp-dwg-kb.service` 模板为 `/etc/systemd/system/atlisp-dwg-kb.service` 并 `enable --now`。可选参数：

```bash
sudo ./scripts/linux/install-service.sh \
  --root /media/data/Private/Design \     # DWG 源根目录（默认即此值）
  --user vitalgg \                        # 运行用户（默认: sudo 调用者）
  --extract-dir /var/lib/atlisp-dwg-kb/dwg-extract \  # 提取输出（默认，不污染原始数据目录）
  --interval 60 --cpu-threshold 30 --idle-checks 3 \
  --workers 4 --project 2025 \
  --serve --port 8322                     # 同时安装 API+界面服务 atlisp-dwg-kb-serve
```

管理：

```bash
systemctl status atlisp-dwg-kb            # 构建守护（autobuild）
systemctl status atlisp-dwg-kb-serve      # API + 界面
journalctl -u atlisp-dwg-kb -f            # 跟踪构建日志
journalctl -u atlisp-dwg-kb-serve -f      # 跟踪服务日志
sudo systemctl restart atlisp-dwg-kb-serve
sudo ./scripts/linux/install-service.sh --uninstall   # 卸载两个单元
```

- 知识库输出：`/var/lib/atlisp-dwg-kb/`（`rdf/knowledge.trig`、`graph/`，由 `StateDirectory` 管理）。
- 提取输出默认在 `/var/lib/atlisp-dwg-kb/dwg-extract/`，避免向原始数据目录写入；改 `--extract-dir` 需重新运行安装脚本。
- 运行期环境变量（Neo4j / VLM 密钥等）写入 `/etc/default/atlisp-dwg-kb`，改后 `systemctl restart`。
- 改单元级参数（间隔、阈值等）：重新运行安装脚本，或 `sudo systemctl edit atlisp-dwg-kb`。

### 数据目录统一（重要）

两个 systemd 单元都固定 `DWG_KB_DATA_DIR=/var/lib/atlisp-dwg-kb`、`DWG_EXTRACT_DIR=/var/lib/atlisp-dwg-kb/dwg-extract`，因此**界面/查询读到的就是守护构建的全量知识库**。手工运行 CLI（`query`/`stats`/`build`/`serve` 等）默认使用 `~/.dwg-kb`，要读同一份数据请显式指定：

```bash
export DWG_KB_DATA_DIR=/var/lib/atlisp-dwg-kb
export DWG_EXTRACT_DIR=/var/lib/atlisp-dwg-kb/dwg-extract
atlisp-dwg-kb query "会议室在哪些图纸上出现"
```

全量库（约 550 万三元组）由 rdflib 加载需 **~10 分钟 / ~7.5GB 内存**：服务启动后在后台预热，期间 `GET /health` 的 `rdf_loaded=false`（就绪探针）；重建后同样在后台重载，请求始终不阻塞（对外提供旧快照，`rdf_stale/rdf_reloading` 可观测）。


## 配置

通过环境变量配置（见 `src/atlisp_dwg_kb/config.py`）：

| 变量 | 说明 | 默认值 |
|------|------|--------|
| `DWG_KB_DATA_DIR` | 知识库存储目录 | `~/.dwg-kb` |
| `DWG_EXTRACT_DIR` | DWG 提取结果目录（build 默认读取） | `./dwg-extract` |
| `DWG_KB_API_KEY` | 设置后 API 需带 Key（`X-API-Key`/`Bearer`/`?key=`）；浏览器资源除外 | (空 = 不鉴权) |
| `DWG_KB_ALLOWED_ROOTS` | `/fs/list` 与 `/build` 的路径白名单（`:` 分隔） | (空 = 不限制) |
| `DWG_KB_RATE_LIMIT` | 每 IP 每分钟请求上限（0=关闭） | `600` |
| `DWG_KB_MAX_BODY_BYTES` | POST 请求体上限（超出 413） | `2097152` |
| `DWG_KB_SPARQL_MAX_CHARS` | SPARQL 查询长度上限 | `20000` |
| `DWG_KB_SPARQL_DEFAULT_LIMIT` | 无 `LIMIT` 时自动追加（`ASK` 除外） | `1000` |
| `DWG_KB_LOG_LEVEL` | 日志级别 `DEBUG/INFO/WARNING/ERROR` | `INFO` |
| `DWG_KB_LOG_FORMAT` | 日志格式 `console` / `json` | `console` |
| `DWG_KB_RENDER_TIMEOUT` | 单图渲染硬超时（秒，超出杀子进程返 504） | `120` |
| `DWG_KB_VLM_PROVIDER` | 视觉模型：`mock` / `openai_compat` | `mock` |
| `DWG_KB_VLM_BASE_URL` | OpenAI 兼容端点 | (空) |
| `DWG_KB_VLM_MODEL` | 模型名（如 `qwen2.5-vl-plus`） | (空) |
| `DWG_KB_VLM_API_KEY` | API Key | (空) |
| `DWG_KB_RENDER_MAX_EDGE` | PNG 长边像素上限 | `4000` |
| `NEO4J_URI` | Neo4j 连接地址 | `bolt://localhost:7687` |
| `NEO4J_USER` | Neo4j 用户名 | `neo4j` |
| `NEO4J_PASSWORD` | Neo4j 密码 | (空) |

```bash
# 自定义知识库存储目录
export DWG_KB_DATA_DIR=/my/data
atlisp-dwg-kb build --extract-dir /my/dwg-extract
```

注意：仓库内的 `data/` 是本地构建产物（已 gitignore），不是运行时默认路径。

## REST API

默认监听 `0.0.0.0:8260`，管理界面 `/ui`，`/health` 可用于探活。列表接口统一返回分页信封
`{items, total, offset, limit}`（`limit` 上限 1000）。

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 探活/就绪：版本、RDF 是否加载与是否滞后（`rdf_loaded/rdf_stale/rdf_reloading`）、graph 是否已构建 |
| GET | `/stats` | 统计（图纸/图层/块/校验数等） |
| GET | `/drawings?discipline=&project=&limit=&offset=` | 图纸分页列表 |
| GET | `/projects` `/layers` `/blocks` | 项目/图层/块（分页信封） |
| GET | `/drawings/{id}/evolution` | 单图演进链 |
| GET | `/evolution/chains` | 全部演进链摘要（版本迭代、审图轮次） |
| GET | `/drawings/{id}/cross_ref` | 单图跨专业引用 |
| GET | `/cross_ref/{bridges,matrix,shared_layers}` | 跨专业桥接/协同矩阵/共享图层 |
| GET | `/search?q=&limit=` | 文本检索 |
| POST | `/query` | 自然语言或 SPARQL（只读，长度/LIMIT 受限） |
| POST | `/drawings/{id}/render?format=png\|svg[&background=true]` | 渲染（`background` 返回 `job_id`） |
| POST | `/drawings/{id}/verify?question=[&background=true]` | 双轨校验（数据为准） |
| GET | `/jobs/{job_id}` | 异步作业状态（`running/ok/error` + `result`） |
| GET | `/renders/{filename}` | 取回渲染图 |
| GET | `/review_queue?limit=` | 待审清单（只生成不处理） |
| GET | `/graph/html` | 知识图谱可视化（ETag/304） |
| GET | `/fs/list?path=` | 目录选择器（受白名单约束） |
| POST | `/build?path=&mode=auto\|resume\|full\|build` | 后台提取+构建，`GET /build/status` 轮询（含 `run_id`） |

鉴权（可选）与限流见 [SECURITY.md](SECURITY.md)；大图渲染建议用 `background=true` + 轮询 `/jobs/{id}`，
避免长请求被浏览器/代理超时中断。


## 命令一览

| 命令 | 说明 |
|------|------|
| `atlisp-dwg-kb extract <dwg-root> [--limit N] [--resume] [--project YYYY] [-w N] [-o DIR]` | 从 DWG 提取元数据（含尺寸与解析诊断） |
| `atlisp-dwg-kb render <path> [-f png\|svg] [--limit N] [--resume] [-w N] [--tiles]` | 渲染 DWG/DXF 为图片（视觉轨；`--tiles` 输出 VLM 瓦片） |
| `atlisp-dwg-kb verify <path> [--question Q] [-o DIR] [--eval DIR]` | 双轨校验（视觉模型 vs 硬数据，数据为准；`--eval` 金标评测） |
| `atlisp-dwg-kb diagnose <path>` | 天正/代理对象解析风险报告 |
| `atlisp-dwg-kb review [dir] [--json] [-n N]` | 待审清单（风险/代理/校验冲突，只生成不处理） |
| `atlisp-dwg-kb mcp` | 启动 MCP Server（stdio，供 Claude/Cursor 调用） |
| `atlisp-dwg-kb build --extract-dir <dir>` | 从提取结果构建知识库 |
| `atlisp-dwg-kb autobuild <dwg-root> [--interval N] [--cpu-threshold P] [--idle-checks K] [--once] [--log-file F]` | CPU 空闲时自动提取+构建 |
| `atlisp-dwg-kb service {install,uninstall,start,stop,status}` | Windows 服务管理 (pywin32) |
| `atlisp-dwg-kb query "..."` | 命令行查询 |
| `atlisp-dwg-kb stats` | 显示统计 |
| `atlisp-dwg-kb serve [--port N] [--no-browser]` | 启动 FastAPI 服务并打开管理界面 (默认 8260) |
| `atlisp-dwg-kb ui [--port N]` | 启动 Gradio 界面 (默认 7860，已弃用) |
| `atlisp-dwg-kb all [--port N] [--no-browser]` | 启动 API + 内置浏览器界面 |
| `atlisp-dwg-kb export-neo4j` | 导出 Neo4j Cypher |
| `atlisp-dwg-kb tray <dwg-root> [--background]` | 启动系统托盘图标管理器 (Windows) |

## 项目结构

```
dwg-knowledge-base/
├── src/
│   └── atlisp_dwg_kb/          # 核心包
│       ├── extract.py          # DWG 元数据提取 (DWG→DXF→JSON/MD，含尺寸+代理诊断)
│       ├── render.py           # 视觉渲染轨 (DXF→PNG/SVG)
│       ├── verify.py           # 双轨校验引擎 (VLM 断言 vs 硬数据，数据为准)
│       ├── vision/             # VLM 抽象层 (mock 默认 + openai_compat 骨架)
│       ├── mcp_server.py       # MCP Server (read_dwg/render_dwg/verify_dwg…)
│       ├── ontology/           # 本体(ttl) + RDF 填充 + SPARQL 查询
│       ├── graph/              # 图谱构建 + 演进追踪 + 跨专业 + Neo4j 导出
│       ├── api/                # FastAPI REST (分页/异步作业/健康检查)
│       ├── ui/static/          # 浏览器管理界面 (HTML/CSS/JS，无框架)
│       ├── fsutil.py           # 原子写 + 跨进程文件锁 (构建/提取串行化)
│       ├── logging_setup.py    # 统一日志 (stdout=数据，stderr=日志)
│       ├── bin/                # 可选: 手动放置 dwg2dxf.exe (gitignore，PyPI 不含)
│       ├── tray.py             # 系统托盘图标管理器 (Windows)
│       ├── cli.py              # CLI 入口
│       └── config.py           # 配置 + 领域关键词表 + VLM/渲染/安全配置
├── tests/                      # pytest 测试 (208 cases + 金标评测)
├── scripts/                    # Windows/Linux 服务安装脚本
├── Makefile                    # 常用开发任务 (lint/test/cov/build/serve)
├── CONTRIBUTING.md / SECURITY.md
├── raw -> /media/data/...      # 本机 DWG 档案符号链接（勿提交）
├── data/                       # 本地构建产物（gitignore）
└── pyproject.toml
```

## 本体类层次

```
owl:Thing
├── Project
├── Drawing
│   ├── ArchitecturalDrawing
│   ├── StructuralDrawing
│   ├── HVACDrawing
│   ├── ElectricalDrawing
│   ├── DetailDrawing
│   ├── GeneralDrawing
│   ├── MechanicalDrawing
│   └── ProcessDrawing
├── Layer
├── Block
│   ├── BuildingComponent
│   └── Equipment
├── ReviewRound
├── Discipline
├── TextAnnotation
└── VerificationReport          # 双轨校验报告 (confidence_score/warning_count)
```

## 依赖

- Python 3.10+
- rdflib (RDF 处理)
- networkx + scipy (图算法 + 构建期力导向布局)
- fastapi + uvicorn (API 服务)
- ezdxf (可选, DWG 提取)
- matplotlib + Pillow (可选 `[render]`, 视觉渲染轨)
- mcp (可选 `[mcp]`, MCP Server)
- pystray + Pillow (可选, Windows 系统托盘)
- gradio (可选, 旧版 Web 界面，已弃用)
- dwg2dxf (libredwg, DWG 转 DXF；系统级安装，PyPI 包不含二进制)
