CWICR 成本计算器:透明的建筑估算 - Openclaw Skills

作者:互联网

2026-03-25

AI教程

什么是 CWICR 成本计算器?

CWICR 成本计算器是一款专门的基于资源的估算工具,旨在解决建筑预算中的“黑箱”问题。通过利用数据驱动建设 (DDC) CWICR 方法,该技能将物理定额(如人工工时和材料数量)与波动的市场价格分开。这种分离确保了估算不仅准确,而且完全可审计,并能随着价格波动轻松更新。

作为 Openclaw Skills 更广泛生态系统的一部分,此计算器为开发商和工程师提供了一种程序化的方式,将成本分解为细颗粒度的组成部分:人工、材料、设备、管理费和利润。它旨在处理复杂的数据集,并为每一次计算提供可追溯的逻辑,使其成为需要在建筑项目中实现完全财务透明度的利益相关者的必备资产。

下载入口:https://github.com/openclaw/skills/tree/main/skills/datadrivenconstruction/cwicr-cost-calculator

安装与下载

1. ClawHub CLI

从源直接安装技能的最快方式。

npx clawhub@latest install cwicr-cost-calculator

2. 手动安装

将技能文件夹复制到以下位置之一

全局模式 ~/.openclaw/skills/ 工作区 /skills/

优先级:工作区 > 本地 > 内置

3. 提示词安装

将此提示词复制到 OpenClaw 即可自动安装。

请帮我使用 Clawhub 安装 cwicr-cost-calculator。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。

CWICR 成本计算器 应用场景

  • 为单个建筑工作项创建详细的成本分解。
  • 从大量工作项列表中生成全面的项目估算。
  • 与来自 BIM 模型或 Excel 表格的工程量清单 (QTO) 数据集成。
  • 执行区域价格调整,以规范不同地理位置的估算。
  • 比较不同的估算版本,以跟踪预算变化并识别成本驱动因素。
CWICR 成本计算器 工作原理
  1. 此 Openclaw Skills 工具首先加载经过验证的 CWICR 方法数据,通常以 Parquet 或 CSV 等格式存储。
  2. 用户输入特定的工程项目代码以及从项目计划中得出的相关工程量。
  3. 计算器对拥有超过 55,000 个工作项的数据库进行查找,以检索物理定额和基准费率。
  4. 它通过将数量乘以特定的人工、材料和设备定额来计算直接成本。
  5. 间接成本(包括管理费和利润率)根据可配置的项目费率进行应用。
  6. 最终输出生成为结构化的成本分解或面向利益相关者的高级汇总报告。

CWICR 成本计算器 配置指南

要使用此技能,请确保您已准备好所需的 Python 环境。使用以下命令安装核心数据处理依赖项:

pip install pandas numpy

然后,您可以通过加载 CWICR 数据文件并将其传递给 CWICRCostCalculator 类来初始化计算器,开始您的估算工作。

CWICR 成本计算器 数据架构与分类体系

该技能利用结构化的数据层级来确保每次计算都是可追溯的。主要数据对象包括:

对象 描述
CostComponent 定义成本类型(人工、材料、设备、管理费、利润)。
CostBreakdown 单个工作项的详细记录,包括单价和状态。
CostSummary 整个项目的汇总视图,包括货币和类别总计。
CostStatus 跟踪计算状态(已计算、已估算、缺少数据或错误)。

name: "cwicr-cost-calculator" description: "Calculate construction costs using DDC CWICR resource-based methodology. Break down costs into labor, materials, equipment with transparent pricing." homepage: "https://datadrivenconstruction.io" metadata: {"openclaw":{"emoji":"??","os":["darwin","linux","win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":["python3"]}}}

CWICR Cost Calculator

Business Case

Problem Statement

Traditional cost estimation often produces "black box" estimates with hidden markups. Stakeholders need:

  • Transparent cost breakdowns
  • Traceable pricing logic
  • Auditable calculations
  • Resource-level detail

Solution

Resource-based cost calculation using CWICR methodology that separates physical norms (labor hours, material quantities) from volatile prices, enabling transparent and auditable estimates.

Business Value

  • Full transparency - Every cost component visible
  • Auditable - Traceable calculation logic
  • Flexible - Update prices without changing norms
  • Accurate - Based on 55,000+ validated work items

Technical Implementation

Prerequisites

pip install pandas numpy

Python Implementation

import pandas as pd
import numpy as np
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
from datetime import datetime


class CostComponent(Enum):
    """Cost breakdown components."""
    LABOR = "labor"
    MATERIAL = "material"
    EQUIPMENT = "equipment"
    OVERHEAD = "overhead"
    PROFIT = "profit"
    TOTAL = "total"


class CostStatus(Enum):
    """Cost calculation status."""
    CALCULATED = "calculated"
    ESTIMATED = "estimated"
    MISSING_DATA = "missing_data"
    ERROR = "error"


@dataclass
class CostBreakdown:
    """Detailed cost breakdown for a work item."""
    work_item_code: str
    description: str
    unit: str
    quantity: float

    labor_cost: float = 0.0
    material_cost: float = 0.0
    equipment_cost: float = 0.0
    overhead_cost: float = 0.0
    profit_cost: float = 0.0

    unit_price: float = 0.0
    total_cost: float = 0.0

    labor_hours: float = 0.0
    labor_rate: float = 0.0

    resources: List[Dict[str, Any]] = field(default_factory=list)
    status: CostStatus = CostStatus.CALCULATED

    def to_dict(self) -> Dict[str, Any]:
        return {
            'work_item_code': self.work_item_code,
            'description': self.description,
            'unit': self.unit,
            'quantity': self.quantity,
            'labor_cost': self.labor_cost,
            'material_cost': self.material_cost,
            'equipment_cost': self.equipment_cost,
            'overhead_cost': self.overhead_cost,
            'profit_cost': self.profit_cost,
            'total_cost': self.total_cost,
            'status': self.status.value
        }


@dataclass
class CostSummary:
    """Summary of cost estimate."""
    total_cost: float
    labor_total: float
    material_total: float
    equipment_total: float
    overhead_total: float
    profit_total: float

    item_count: int
    currency: str
    calculated_at: datetime

    breakdown_by_category: Dict[str, float] = field(default_factory=dict)


class CWICRCostCalculator:
    """Resource-based cost calculator using CWICR methodology."""

    DEFAULT_OVERHEAD_RATE = 0.15  # 15% overhead
    DEFAULT_PROFIT_RATE = 0.10   # 10% profit

    def __init__(self, cwicr_data: pd.DataFrame,
                 overhead_rate: float = None,
                 profit_rate: float = None,
                 currency: str = "USD"):
        """Initialize calculator with CWICR data."""
        self.data = cwicr_data
        self.overhead_rate = overhead_rate or self.DEFAULT_OVERHEAD_RATE
        self.profit_rate = profit_rate or self.DEFAULT_PROFIT_RATE
        self.currency = currency

        # Index data for fast lookup
        self._index_data()

    def _index_data(self):
        """Create index for fast work item lookup."""
        if 'work_item_code' in self.data.columns:
            self._code_index = self.data.set_index('work_item_code')
        else:
            self._code_index = None

    def calculate_item_cost(self, work_item_code: str,
                            quantity: float,
                            price_overrides: Dict[str, float] = None) -> CostBreakdown:
        """Calculate cost for single work item."""

        # Find work item in database
        if self._code_index is not None and work_item_code in self._code_index.index:
            item = self._code_index.loc[work_item_code]
        else:
            # Try partial match
            matches = self.data[
                self.data['work_item_code'].str.contains(work_item_code, case=False, na=False)
            ]
            if matches.empty:
                return CostBreakdown(
                    work_item_code=work_item_code,
                    description="NOT FOUND",
                    unit="",
                    quantity=quantity,
                    status=CostStatus.MISSING_DATA
                )
            item = matches.iloc[0]

        # Get base costs
        labor_unit = float(item.get('labor_cost', 0) or 0)
        material_unit = float(item.get('material_cost', 0) or 0)
        equipment_unit = float(item.get('equipment_cost', 0) or 0)

        # Apply price overrides if provided
        if price_overrides:
            if 'labor_rate' in price_overrides:
                labor_norm = float(item.get('labor_norm', 0) or 0)
                labor_unit = labor_norm * price_overrides['labor_rate']
            if 'material_factor' in price_overrides:
                material_unit *= price_overrides['material_factor']
            if 'equipment_factor' in price_overrides:
                equipment_unit *= price_overrides['equipment_factor']

        # Calculate component costs
        labor_cost = labor_unit * quantity
        material_cost = material_unit * quantity
        equipment_cost = equipment_unit * quantity

        # Direct costs
        direct_cost = labor_cost + material_cost + equipment_cost

        # Overhead and profit
        overhead_cost = direct_cost * self.overhead_rate
        profit_cost = (direct_cost + overhead_cost) * self.profit_rate

        # Total
        total_cost = direct_cost + overhead_cost + profit_cost

        # Unit price
        unit_price = total_cost / quantity if quantity > 0 else 0

        return CostBreakdown(
            work_item_code=work_item_code,
            description=str(item.get('description', '')),
            unit=str(item.get('unit', '')),
            quantity=quantity,
            labor_cost=labor_cost,
            material_cost=material_cost,
            equipment_cost=equipment_cost,
            overhead_cost=overhead_cost,
            profit_cost=profit_cost,
            unit_price=unit_price,
            total_cost=total_cost,
            labor_hours=float(item.get('labor_norm', 0) or 0) * quantity,
            labor_rate=float(item.get('labor_rate', 0) or 0),
            status=CostStatus.CALCULATED
        )

    def calculate_estimate(self, items: List[Dict[str, Any]],
                          group_by_category: bool = True) -> CostSummary:
        """Calculate cost estimate for multiple items."""

        breakdowns = []
        for item in items:
            code = item.get('work_item_code') or item.get('code')
            qty = item.get('quantity', 0)
            overrides = item.get('price_overrides')

            breakdown = self.calculate_item_cost(code, qty, overrides)
            breakdowns.append(breakdown)

        # Aggregate totals
        labor_total = sum(b.labor_cost for b in breakdowns)
        material_total = sum(b.material_cost for b in breakdowns)
        equipment_total = sum(b.equipment_cost for b in breakdowns)
        overhead_total = sum(b.overhead_cost for b in breakdowns)
        profit_total = sum(b.profit_cost for b in breakdowns)
        total_cost = sum(b.total_cost for b in breakdowns)

        # Group by category if requested
        breakdown_by_category = {}
        if group_by_category:
            for b in breakdowns:
                # Extract category from work item code prefix
                category = b.work_item_code.split('-')[0] if '-' in b.work_item_code else 'Other'
                if category not in breakdown_by_category:
                    breakdown_by_category[category] = 0
                breakdown_by_category[category] += b.total_cost

        return CostSummary(
            total_cost=total_cost,
            labor_total=labor_total,
            material_total=material_total,
            equipment_total=equipment_total,
            overhead_total=overhead_total,
            profit_total=profit_total,
            item_count=len(breakdowns),
            currency=self.currency,
            calculated_at=datetime.now(),
            breakdown_by_category=breakdown_by_category
        )

    def calculate_from_qto(self, qto_df: pd.DataFrame,
                          code_column: str = 'work_item_code',
                          quantity_column: str = 'quantity') -> pd.DataFrame:
        """Calculate costs from Quantity Takeoff DataFrame."""

        results = []
        for _, row in qto_df.iterrows():
            code = row[code_column]
            qty = row[quantity_column]

            breakdown = self.calculate_item_cost(code, qty)
            result = breakdown.to_dict()

            # Add original QTO columns
            for col in qto_df.columns:
                if col not in result:
                    result[f'qto_{col}'] = row[col]

            results.append(result)

        return pd.DataFrame(results)

    def apply_regional_factors(self, base_costs: pd.DataFrame,
                               region_factors: Dict[str, float]) -> pd.DataFrame:
        """Apply regional adjustment factors."""
        adjusted = base_costs.copy()

        if 'labor_cost' in adjusted.columns and 'labor' in region_factors:
            adjusted['labor_cost'] *= region_factors['labor']

        if 'material_cost' in adjusted.columns and 'material' in region_factors:
            adjusted['material_cost'] *= region_factors['material']

        if 'equipment_cost' in adjusted.columns and 'equipment' in region_factors:
            adjusted['equipment_cost'] *= region_factors['equipment']

        # Recalculate totals
        adjusted['direct_cost'] = (
            adjusted.get('labor_cost', 0) +
            adjusted.get('material_cost', 0) +
            adjusted.get('equipment_cost', 0)
        )
        adjusted['total_cost'] = adjusted['direct_cost'] * (1 + self.overhead_rate) * (1 + self.profit_rate)

        return adjusted

    def compare_estimates(self, estimate1: CostSummary,
                         estimate2: CostSummary) -> Dict[str, Any]:
        """Compare two cost estimates."""
        return {
            'total_difference': estimate2.total_cost - estimate1.total_cost,
            'total_percent_change': (
                (estimate2.total_cost - estimate1.total_cost) /
                estimate1.total_cost * 100 if estimate1.total_cost > 0 else 0
            ),
            'labor_difference': estimate2.labor_total - estimate1.labor_total,
            'material_difference': estimate2.material_total - estimate1.material_total,
            'equipment_difference': estimate2.equipment_total - estimate1.equipment_total,
            'item_count_difference': estimate2.item_count - estimate1.item_count
        }


class CostReportGenerator:
    """Generate cost reports from calculations."""

    def __init__(self, calculator: CWICRCostCalculator):
        self.calculator = calculator

    def generate_summary_report(self, items: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Generate summary cost report."""
        summary = self.calculator.calculate_estimate(items)

        return {
            'report_date': datetime.now().isoformat(),
            'currency': summary.currency,
            'total_cost': round(summary.total_cost, 2),
            'breakdown': {
                'labor': round(summary.labor_total, 2),
                'material': round(summary.material_total, 2),
                'equipment': round(summary.equipment_total, 2),
                'overhead': round(summary.overhead_total, 2),
                'profit': round(summary.profit_total, 2)
            },
            'percentages': {
                'labor': round(summary.labor_total / summary.total_cost * 100, 1) if summary.total_cost > 0 else 0,
                'material': round(summary.material_total / summary.total_cost * 100, 1) if summary.total_cost > 0 else 0,
                'equipment': round(summary.equipment_total / summary.total_cost * 100, 1) if summary.total_cost > 0 else 0,
            },
            'item_count': summary.item_count,
            'by_category': summary.breakdown_by_category
        }

    def generate_detailed_report(self, items: List[Dict[str, Any]]) -> pd.DataFrame:
        """Generate detailed line-item report."""
        results = []

        for item in items:
            code = item.get('work_item_code') or item.get('code')
            qty = item.get('quantity', 0)

            breakdown = self.calculator.calculate_item_cost(code, qty)
            results.append(breakdown.to_dict())

        df = pd.DataFrame(results)

        # Add totals row
        totals = df[['labor_cost', 'material_cost', 'equipment_cost',
                     'overhead_cost', 'profit_cost', 'total_cost']].sum()
        totals['description'] = 'TOTAL'
        totals['work_item_code'] = ''

        df = pd.concat([df, pd.DataFrame([totals])], ignore_index=True)

        return df


# Convenience functions
def calculate_cost(cwicr_data: pd.DataFrame,
                   work_item_code: str,
                   quantity: float) -> float:
    """Quick cost calculation."""
    calc = CWICRCostCalculator(cwicr_data)
    breakdown = calc.calculate_item_cost(work_item_code, quantity)
    return breakdown.total_cost


def estimate_project_cost(cwicr_data: pd.DataFrame,
                         items: List[Dict[str, Any]]) -> Dict[str, Any]:
    """Quick project cost estimate."""
    calc = CWICRCostCalculator(cwicr_data)
    report = CostReportGenerator(calc)
    return report.generate_summary_report(items)

Quick Start

import pandas as pd
from cwicr_data_loader import CWICRDataLoader

# Load CWICR data
loader = CWICRDataLoader()
cwicr = loader.load("ddc_cwicr_en.parquet")

# Initialize calculator
calc = CWICRCostCalculator(cwicr)

# Calculate single item
breakdown = calc.calculate_item_cost("CONC-001", quantity=150)
print(f"Total: ${breakdown.total_cost:,.2f}")
print(f"  Labor: ${breakdown.labor_cost:,.2f}")
print(f"  Material: ${breakdown.material_cost:,.2f}")
print(f"  Equipment: ${breakdown.equipment_cost:,.2f}")

Common Use Cases

1. Project Estimate

items = [
    {'work_item_code': 'CONC-001', 'quantity': 150},
    {'work_item_code': 'EXCV-002', 'quantity': 200},
    {'work_item_code': 'REBAR-003', 'quantity': 15000}  # kg
]

summary = calc.calculate_estimate(items)
print(f"Project Total: ${summary.total_cost:,.2f}")

2. QTO Integration

# Load BIM quantities
qto = pd.read_excel("quantities.xlsx")

# Calculate costs
costs = calc.calculate_from_qto(qto,
    code_column='work_item',
    quantity_column='quantity'
)
print(costs[['description', 'quantity', 'total_cost']])

3. Regional Adjustment

# Apply Berlin pricing
berlin_factors = {
    'labor': 1.15,      # 15% higher labor
    'material': 0.95,   # 5% lower materials
    'equipment': 1.0
}

adjusted = calc.apply_regional_factors(costs, berlin_factors)

Resources

  • GitHub: OpenConstructionEstimate-DDC-CWICR
  • DDC Book: Chapter 3.1 - Construction Cost Estimation