quote-mcp-server
README.md
# 🏗️ decoration-quote-engine
> 装修硬装报价引擎 · 已落地生产验证:DXF 墙线拓扑重建 → 工程量提取 → 确定性报价 → HTTP / MCP 服务
     
配套智能体项目:[interior-ai-agent](https://github.com/zeqikong-ctrl/interior-ai-agent)(LangGraph 编排层,经 MCP 调用本引擎与软装目录)
软装单价目录(独立仓库):[soft-mcp-server](https://github.com/zeqikong-ctrl/soft-mcp-server)
---
## ✨ 它解决什么问题
装修报价长期依赖人工量房 + Excel 套价,存在三个痛点:
1. **慢且不可复现** —— 同一张户型图,不同设计师出价差异大,客户难以比价。
2. **黑盒** —— 客户看不到「面积怎么来、单价怎么定」,信任成本高。
3. **LLM 幻觉** —— 直接让大模型算钱,金额随温度波动、无法审计。
本引擎用**确定性代码**替代人工套价:几何算量 + 规则报价全部可复现、可审计,仅在「风格识别」「置信度不足」等少数卡点引入人工确认(HITL)。
## 📦 它做什么(真实案例 · 南京·新块1)
输入一张**量房后的原始墙体图(DWG→DXF,无任何闭合房间线)**,输出可直接给客户看的硬装 + 软装报价:
```
新块1.dxf(套内 60.48㎡,两室两厅)
→ 墙线 topo 重建,自动围合 7 个房间 + 干湿区分类
餐厅/主卧/客厅/次卧=dry,厨房/卫生间/阳台独立分区
→ 门 5 / 窗 5 / 垭口 1 自动识别
→ 硬装报价 标准档 ¥94,806.79(70 项明细)
→ 软装预算 奶油风格 标准档 ¥109,100
→ 客户整装预算 ≈ ¥203,907(硬装 + 软装)
```
**全程零 LLM 调用**——算量与算钱都是确定性代码,同一输入永远得到同一输出。
## 🧱 架构(四层)
```
┌─────────────────────────────────────────────────────────────┐
│ 编排层 interior-ai-agent (LangGraph) │
│ intent 解析 / 多轮改价 / HITL 人工确认 / 案例图检索 │
└───────────────┬───────────────────────────┬─────────────────┘
│ MCP stdio │ MCP stdio
▼ ▼
┌───────────────────────────┐ ┌─────────────────────────────┐
│ 硬装引擎 (本仓库) │ │ 软装目录 soft-mcp-server │
│ parse_dxf │ │ soft_catalog │
│ compute_quote │ │ compute_soft_budget │
│ generate_report │ └─────────────────────────────┘
└───────────────┬───────────┘
│ 内部 importlib 加载 soft_prices.py
▼
┌───────────────────────────┐
│ A 层 src/service.py │ FastAPI:/quote /soft /health /reports
│ + Docker / docker-compose │ 可被任意前端 / 系统直接 HTTP 调用
└───────────────────────────┘
```
## 🧠 核心设计
### 1. 墙线拓扑重建(src/agents/hard/takeoff.py)
原始墙体图往往没有闭合的房间多段线。本引擎用 `shapely.polygonize` + `networkx`
从纯墙线段自动围合房间,再按文字标注 / 面积启发式判定房间功能(厨房 / 卫生间 / 阳台),
输出带**置信度**的工程量 JSON:
- `ROOM` 图层闭合多段线优先(confidence 0.90)
- 无 ROOM 图层时走 `wall_polygonize` 墙线围合(confidence 0.75)
- confidence < 0.70 触发人工核对(HITL)
### 2. 规则报价引擎(src/agents/hard/cost.py)
单价表按 `zone`(dry/wet/kitchen/balcony/all)× `basis`(计量依据)驱动,内置一线业务规则:
| 规则 | 说明 |
|---|---|
| 门按套计 | 木门 1298 元/套;门套按 `2×高+宽` 算延米,入户门单边、垭口双边 |
| 瓷砖品牌统一 | 同空间墙/地砖自动选同一品牌(两种品牌有 200 元小单费) |
| 地面材质分支 | 铺瓷砖→含铺贴人工、免地垫宝;铺地板→需地垫宝找平、踢脚线赠送 |
| 水电分档 | 1室1厅≤60平 8500;2室以上≤75平 10000;>75平按建筑面积×140(建面=套内/0.75) |
| 石膏板平顶 | 只算衣柜柜头:投影面积×(柜深/柜高),非全屋满吊 |
| 橱柜/定制柜估算 | 按套内面积-户型对照表自动估延米/投影面积,可用实测值覆盖 |
| 提问点机制 | 层高、卧室瓷砖还是地板、下沉找平——计算前必须确认 |
### 3. 档次分档(tier)—— 激活「预算调高 / 调低」
单价表(`config/unit_price_nanjing_mq.csv`)每行带 `tier` 列:`最简` / `标准` / `轻奢` / `通用`(不分档)。
- `标准` 为锚点(即原南京美全价目表基线);`最简` / `轻奢` 缺项时自动回退到 `标准`,保证任何档位都能算全。
- 分档价差覆盖:木门、强化地板、瓷砖(300/800 系列)、钛合金门、集成吊顶、开关插座、定制柜等主材。
- 语义归一:`低档/经济`→最简,`中档/性价比`→标准,`高档/豪华`→轻奢。
- 同一套工程量下,三档真实金额(新块1):**最简 ¥83,735 / 标准 ¥94,807 / 轻奢 ¥107,596**。
### 4. MCP Server(src/mcp_server/server.py)
引擎以 MCP stdio 协议对外暴露 3 个工具,可被 LangGraph Agent、Claude Desktop、Cursor 等任意 MCP Host 调用:
| Tool | 输入 → 输出 |
|---|---|
| `parse_dxf` | DXF 路径 → 工程量 JSON + confidence + 房间摘要 |
| `compute_quote` | takeoff JSON + ProjectConfig(层高/地面材质/品牌偏好/tier)→ 报价 JSON |
| `generate_report` | 报价 JSON → 打印友好 HTML |
### 5. A 层 HTTP 服务(src/service.py)
FastAPI 把引擎封装成 HTTP 接口,软装目录经 `importlib` 按路径加载(规避双 `src` 包命名冲突),
输出自动落盘到 `output/`,HTML 报价单通过 `/reports` 静态挂载直接预览。
## 📁 项目结构
```
decoration-quote-engine/
├── src/
│ ├── core/ # 数据模型 / DXF IO / 几何工具 / 配置
│ │ ├── models.py # Takeoff / HardQuote / ProjectState
│ │ ├── dxf_io.py
│ │ ├── geometry.py
│ │ └── config/ # 单价表加载
│ ├── agents/hard/
│ │ ├── takeoff.py # DXF 解析 + 墙线拓扑重建 + 工程量提取
│ │ ├── cost.py # 规则报价引擎(ProjectConfig + zone/basis 匹配)
│ │ └── quote_report.py # 报价 JSON → HTML
│ ├── mcp_server/server.py # quote-mcp-server(FastMCP stdio)
│ └── service.py # A 层 FastAPI:/quote /soft /health /reports
├── config/
│ └── unit_price_template.csv # 单价表格式模板(真实价目表不入库)
├── output/ # 运行产物(takeoff/quote/报价单 JSON+HTML)
├── tests/ # pytest(takeoff 回归 + MCP 全链路冒烟)
├── Dockerfile # python:3.13-slim
├── docker-compose.yml # 挂载 soft-mcp-server,端口 8000
├── .dockerignore
├── requirements.txt # fastapi / uvicorn / ezdxf / shapely / networkx
├── docs/
│ ├── deploy.md # 部署与端点说明
│ └── case_study.md # 落地复盘(真实数据)
└── 装修报价智能体_技术方案文档.md
```
## 🚀 快速开始
### A. CLI 跑通整条链路
```bash
pip install -r requirements.txt
# 1) DXF → 工程量
python -m src.agents.hard.takeoff path/to/户型.dxf output/takeoff.json
# 2) 工程量 → 硬装报价(需按 config/unit_price_template.csv 准备单价表)
python -m src.agents.hard.cost output/takeoff.json config/你的单价表.csv output/quote.json
# 2b) 带档次:--tier 最简/标准/轻奢
python -m src.agents.hard.cost output/takeoff.json --tier 轻奢 output/quote_lux.json
# 3) 报价 → HTML 报价单
python src/agents.hard/quote_report.py output/quote.json output/quote.html
# 直接起 MCP Server(stdio)
quote-mcp-server
# 测试
pytest tests/ -q
```
### B. HTTP 服务(A 层)
```bash
# 设置软装目录仓库路径(docker-compose 通过 SOFT_REPO_PATH 注入)
export SOFT_REPO_PATH=/path/to/soft-mcp-server
uvicorn src.service:app --host 0.0.0.0 --port 8000
```
| 端点 | 方法 | 说明 |
|---|---|---|
| `/health` | GET | 健康检查 |
| `/quote` | POST | `multipart/form-data`:dxf 文件 + `style`/`tier`/`floor_height`/`city`,返回硬装报价 JSON + `report_url` |
| `/soft` | POST | JSON:`{style, tier, rooms?}`,返回软装预算 JSON |
| `/reports/*` | GET | 已生成的 HTML 报价单(静态目录) |
```bash
# 调硬装报价
curl -F "dxf=@新块1.dxf" -F "style=奶油" -F "tier=标准" http://localhost:8000/quote
# 调软装预算
curl -X POST http://localhost:8000/soft -H "Content-Type: application/json" \
-d '{"style":"奶油","tier":"标准"}'
```
### C. Docker 部署
```bash
# 在 docker-compose.yml 中设置 SOFT_REPO_PATH 指向本地 soft-mcp-server
docker compose up --build
# → http://localhost:8000/health
```
详见 [docs/deploy.md](docs/deploy.md)。
## 🧾 真实落地效果(新块1 · 南京)
| 维度 | 结果 |
|---|---|
| 套内面积 | 60.48 ㎡ |
| 自动围合房间 | 7 间(4 dry + 厨房 + 卫生间 + 阳台) |
| 门窗识别 | 门 5 / 窗 5 / 垭口 1 |
| 解析方式 / 置信度 | wall_polygonize / 0.75(触发人工核对提示) |
| 硬装(标准档) | ¥94,806.79(70 项) |
| 软装(奶油·标准档) | ¥109,100 |
| **客户整装预算** | **≈ ¥203,907** |
硬装三档:最简 ¥83,735 / 标准 ¥94,807 / 轻奢 ¥107,596
软装五风格(标准档):现代简约 ¥98,190 / 奶油 ¥109,100 / 中古 ¥114,555 / 法式 ¥125,465 / 新中式 ¥130,920
完整复盘见 [docs/case_study.md](docs/case_study.md),全部原始产物在 `output/`。
## ⚠️ 已知边界
- 单价表基于南京市场(2026),其他城市需替换 `config/` 下的价目表
- 房间围合依赖墙线基本闭合;低置信度结果会在 JSON 中给出 warnings,由上层做人工确认(HITL)
- 橱柜延米 / 衣柜投影面积默认按户型规则估算,精确报价需实测值覆盖
- A 层服务通过 `importlib` 按文件路径加载软装目录的 `soft_prices.py`,需正确设置 `SOFT_REPO_PATH`
## 📜 License
MIT © 2026
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues