Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx skills add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill document-classification-nlpgit clone --depth 1 https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_ConstructionWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/document-classification-nlp)<a href="https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/document-classification-nlp"><img src="https://agentmods.dev/badge/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/document-classification-nlp/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/document-classification-nlp"><img src="https://agentmods.dev/badge/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/document-classification-nlp.svg" alt="Reviewed on agentmods" width="80" height="20"></a>- NVIDIA SkillSpector pass
What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00034 | $0.03614 |
| Opus 5.5 | $0.00014 | $0.01446 |
| Sonnet 5.5 | $0.00007 | $0.00723 |
| Haiku 4.5 | $0.00003 | $0.00361 |
Grade A, and why
document-classification-nlp scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 1mo ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
Copies of this mod
1 near-identical copy found in the catalogue:
- document-classification-nlp — 100% identical, 0 lines differ
How it starts
The opening of the file, as written. The whole thing — 452 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Document Classification with NLP
Overview
This skill implements NLP-based document classification and information extraction for construction projects. Automate document sorting, key term extraction, and content analysis.
Document Types:
- RFIs (Requests for Information)
- Submittals and shop drawings
- Change orders and variations
- Specifications and standards
- Contracts and agreements
- Safety reports and permits
Quick Start
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
import pandas as pd
# Sample training data
documents = [
("Please clarify the steel reinforcement spacing for the foundation slab", "RFI"),
("Attached shop drawing for HVAC ductwork layout", "Submittal"),
("Additional cost for unforeseen soil conditions", "Change Order"),
("Fire-rated wall assembly specification Section 09 21 16", "Specification"),
]
texts, labels = zip(*documents)
# Train classifier
classifier = Pipeline([
('tfidf', TfidfVectorizer(max_features=1000, ngram_range=(1, 2))),
('clf', MultinomialNB())
])
classifier.fit(texts, labels)
# Classify new document
new_doc = "Request to approve substitution of specified light fixtures"
prediction = classifier.predict([new_doc])[0]
print(f"Classification: {prediction}") # Output: Submittal
Advanced Classification System
Document Classifier Class
import re
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
from typing import List, Dict, Tuple, Optional
import spacy
from dataclasses import dataclass
@dataclass
class ClassificationResult:
document_id: str
predicted_class: str
confidence: float
alternative_classes: List[Tuple[str, float]]
extracted_entities: Dict[str, List[str]]
keywords: List[str]
class ConstructionDocumentClassifier:
"""Classify and analyze construction documents"""
# Document type patterns
DOCUMENT_PATTERNS = {
'RFI': [
r'request\s+for\s+information',
r'clarification\s+(needed|required|requested)',
r'please\s+(clarify|confirm|advise)',
r'question\s+(regarding|about)',
r'rfi\s*#?\d*'
],
'Submittal': [
r'submittal',
r'shop\s+drawing',
r'product\s+data',
r'sample\s+submission',
r'approval\s+request',
r'material\s+submission'
],
'Change Order': [
r'change\s+order',
r'variation\s+order',
r'cost\s+(increase|adjustment|addition)',
r'scope\s+change',
r'additional\s+work',
r'unforeseen\s+conditions'
],
'Specification': [
r'section\s+\d{2}\s+\d{2}\s+\d{2}',
r'specification',
r'performance\s+requirement',
r'material\s+standard',
r'quality\s+standard'
],
'Safety Report': [
r'incident\s+report',
r'safety\s+(inspection|violation|observation)',
r'hazard\s+(identification|assessment)',
r'near\s+miss',
r'osha',
r'jha|jsa'
],
'Contract': [
r'contract\s+agreement',
r'terms\s+and\s+conditions',
r'scope\s+of\s+work',
r'payment\s+terms',
r'warranty\s+provision'
]
}
def __init__(self, use_spacy: bool = True):
self.classifier = None
self.vectorizer = None
self.label_encoder = LabelEncoder()
if use_spacy:
try:
self.nlp = spacy.load("en_core_web_sm")
except:
self.nlp = None
else:
self.nlp = None
def train(self, documents: List[str], labels: List[str]) -> Dict:
"""Train the document classifier"""
# Encode labels
y = self.label_encoder.fit_transform(labels)
# Create pipeline
self.classifier = Pipeline([
('tfidf', TfidfVectorizer(
max_features=5000,
ngram_range=(1, 3),
stop_words='english',
sublinear_tf=True
)),
('clf', LinearSVC(C=1.0, class_weight='balanced'))
])
# Train
self.classifier.fit(documents, y)
# Cross-validation
scores = cross_val_score(self.classifier, documents, y, cv=5)
return {
'accuracy_mean': scores.mean(),
'accuracy_std': scores.std(),
'classes': list(self.label_encoder.classes_)
}
def classify(self, document: str) -> ClassificationResult:
"""Classify a single document"""
if self.classifier is None:
# Use rule-based classification if no model trained
return self._rule_based_classify(document)
# Get prediction
prediction = self.classifier.predict([document])[0]
predicted_class = self.label_encoder.inverse_transform([prediction])[0]
# Get confidence scores
decision_scores = self.classifier.decision_function([document])[0]
probs = self._softmax(decision_scores)
alternatives = [
(self.label_encoder.inverse_transform([i])[0], float(probs[i]))
for i in np.argsort(probs)[::-1][1:4]
]
# Extract entities and keywords
entities = self._extract_entities(document)
keywords = self._extract_keywords(document)
return ClassificationResult(
document_id="",
predicted_class=predicted_class,
confidence=float(probs[prediction]),
alternative_classes=alternatives,
extracted_entities=entities,
keywords=keywords
)
def _rule_based_classify(self, document: str) -> ClassificationResult:
"""Rule-based classification using patterns"""
doc_lower = document.lower()
scores = {}
for doc_type, patterns in self.DOCUMENT_PATTERNS.items():
score = sum(
1 for pattern in patterns
if re.search(pattern, doc_lower)
)
scores[doc_type] = score
if max(scores.values()) == 0:
predicted = 'Other'
confidence = 0.5
else:
predicted = max(scores, key=scores.get)
confidence = scores[predicted] / len(self.DOCUMENT_PATTERNS[predicted])
return ClassificationResult(
document_id="",
predicted_class=predicted,
confidence=confidence,
alternative_classes=[],
extracted_entities=self._extract_entities(document),
keywords=self._extract_keywords(document)
)
def _extract_entities(self, document: str) -> Dict[str, List[str]]:
"""Extract named entities from document"""
entities = {
'dates': [],
'organizations': [],
'people': [],
'monetary': [],
'references': []
}
# Date patterns
date_pattern = r'\d{1,2}[/-]\d{1,2}[/-]\d{2,4}'
entities['dates'] = re.findall(date_pattern, document)
# Money patterns
money_pattern = r'\$[\d,]+(?:\.\d{2})?'
entities['monetary'] = re.findall(money_pattern, document)
# Reference numbers
ref_pattern = r'(?:RFI|CO|SI|PR)[-#]?\s*\d+'
entities['references'] = re.findall(ref_pattern, document, re.IGNORECASE)
# Use spaCy for NER if available
if self.nlp:
doc = self.nlp(document)
for ent in doc.ents:
if ent.label_ == 'ORG':
entities['organizations'].append(ent.text)
elif ent.label_ == 'PERSON':
entities['people'].append(ent.text)
return entities
def _extract_keywords(self, document: str, top_n: int = 10) -> List[str]:
"""Extract key terms from document"""
# Construction-specific terms
construction_terms = [
'concrete', 'steel', 'reinforcement', 'foundation', 'structural',
'hvac', 'plumbing', 'electrical', 'mechanical', 'architectural',
'specification', 'drawing', 'detail', 'schedule', 'submittals',
'rfi', 'change order', 'delay', 'inspection', 'approval'
]
doc_lower = document.lower()
found_terms = [term for term in construction_terms if term in doc_lower]
return found_terms[:top_n]
def _softmax(self, x: np.ndarray) -> np.ndarray:
"""Convert decision scores to probabilities"""
exp_x = np.exp(x - np.max(x))
return exp_x / exp_x.sum()
def batch_classify(self, documents: List[str]) -> pd.DataFrame:
"""Classify multiple documents"""
results = [self.classify(doc) for doc in documents]
return pd.DataFrame([{
'Predicted_Class': r.predicted_class,
'Confidence': r.confidence,
'Keywords': ', '.join(r.keywords),
'Dates_Found': ', '.join(r.extracted_entities['dates']),
'References_Found': ', '.join(r.extracted_entities['references'])
} for r in results])
What ships with it
2 files beside SKILL.md in the same directory: the scripts, references and assets a skill reads on demand. Not counted in the per-session cost; read them before you install if any of them is executable.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 1mo ago First seen · 452 lines · 34 tokens per session scan A 7a5035b2dbd4
document-classification-nlp is a skill published in the GitHub repository datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction (374 stars, last pushed 8d ago), licensed MIT. It adds 34 tokens to every session and 3,614 once invoked, about $0.0001 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-09-03.
Other skills, from other repositories
read-book
When you want to read and extract structured notes from a book — PDF, EPUB, MOBI, markdown, .txt, pasted text, or URL to a public-domain work. Reads in chunks (by chapter when a TOC exists, by 50-page blocks otherwise), extracts per-chapter TL;DR + key concepts + quotes + action items + frameworks, and offers to…
google-apps-script
Build Google Apps Script automation for Sheets and Workspace. Custom menus, triggers (onEdit / time-driven / form submit), dialogs, sidebars, email batches, PDF export, external API. Use whenever the user wants to automate a Google Sheet, build a Sheets menu / sidebar / dialog, hit a Sheets row from email or a…
executive-summary
Redacta un resumen ejecutivo para cualquier documento, informe o propuesta. Úsalo cuando te pidan redactar un resumen ejecutivo, resumen gerencial, briefing o one-pager para stakeholders senior. Produce un resumen estructurado que directivos ocupados puedan leer en menos de 3 minutos y sobre el que puedan actuar.
invoice-generator
Create a professional, complete invoice for a client or customer. Use when asked to write an invoice, create a bill, draft a freelance/contractor invoice, or set up an invoice template. Produces a clear invoice — your and the client's details, a unique number, line items with quantities/rates, subtotal/tax/total…
dataset-datasheet
Document a dataset so others know what it is, how it was made, and when not to use it. Use when asked to write a datasheet for a dataset, document training/eval data, or assess whether a dataset is fit for a use. Produces a datasheet — motivation, composition, collection process, preprocessing, recommended uses &…
model-card
Document a deployed ML/AI model so others can use it responsibly. Use when asked to write a model card, document a model's intended use and limitations, or prepare an AI model for review/launch. Produces a complete model card — intended use, training data, evaluation metrics across slices, limitations, ethical…