excel-normalizer
by climik
README.md
<div align="center">
English | **[çŽä˝ä¸ć](README.zh-CN.md)**
# đ Excel Semantic Normalizer
**Any Excel, any headers, any column order â canonical fields â Canonical JSON â template export**
Maps columns by *meaning*, not position. Hybrid retrieval, LLM adjudication, template export, and a human-feedback loop â with graceful offline fallback.

</div>
---
## ⨠Why this exists
Procurement / supply-chain spreadsheets in the wild use whatever headers each vendor likes: `ĺĺ` / `č´§çŠĺç§°` / `Product` / `Name` all mean the same thing, and column order changes per file. This service parses any `.xlsx`, resolves each column to one of **19 canonical fields** (product, price, supplier, material, âŚ), and outputs clean, typed, validated **Canonical JSON** â plus it can render results back into your own Excel templates.
## đ Quick start
**Requirements:** Python ⼠3.10. API keys optional â the service runs fully offline.
```bash
git clone <repo-url>
cd excel-normalizer
pip install -r requirements.txt # or: pip install -e ".[dev]"
cp .env.example .env # optional: add API keys for higher accuracy
uvicorn app.main:app --reload --port 8000
```
Then:
- Interactive API docs: <http://127.0.0.1:8000/docs>
- Health check: <http://127.0.0.1:8000/health>
- Live dashboard: <http://127.0.0.1:8000/dashboard>
### One-call example
```bash
curl -X POST "http://127.0.0.1:8000/v1/table/normalize" \
-F "file=@tests/sample_éč´ćçť.xlsx"
```
```python
import requests
resp = requests.post(
"http://127.0.0.1:8000/v1/table/normalize",
files={"file": open("tests/sample_éč´ćçť.xlsx", "rb")},
)
data = resp.json()
# data["columns"] â per-column canonical field mapping + confidence
# data["records"] â normalized, typed records with _meta.issues traceability
```
## đ§ How it works
| Phase | What it does | Status |
|-------|--------------|--------|
| **P1 Rule engine** | Header exact/fuzzy alias matching + Hungarian assignment + row alignment + type coercion | â
|
| **P2 Hybrid retrieval** | Dense + BM25 + rule recall â RRF fusion â reranker (remote bge-m3 or local fallback) | â
|
| **P3 LLM adjudication** | Strict-JSON LLM verdict on low-confidence columns, with conservative overturn guard | â
|
| **P4 Template export** | Parse `{{}}`/`${}`/`[[]]` placeholders, reverse-bind fields, render styled xlsx back | â
|
| **P5 Feedback loop** | Human corrections â SQLite â auto-reflow into knowledge base with regression gate + auto-rollback | â
|
| **MCP server** | 9 tools for Claude / Cursor / Windsurf integration | â
|
**Accuracy** (130 unseen-header eval set, anti-cheat verified zero overlap): offline **96.2%** â remote reranker **96.9%** â with LLM adjudication **98.5%**.
**Graceful degradation:** no API keys â local char-ngram + rapidfuzz. Keys configured â remote embeddings/rerank with retry-then-fallback. LLM never overrides a confident mapping unless strictly more confident.
## đ Canonical fields (19)
`product_name`* ¡ `origin` ¡ `price` ¡ `quantity` ¡ `spec` ¡ `delivery_date` ¡ `supplier` ¡ `sku` ¡ `unit` ¡ `amount` ¡ `material` ¡ `weight` ¡ `tax_rate` ¡ `delivery_location` ¡ `payment_method` ¡ `remark` ¡ `freight` ¡ `vessel` ¡ `warehouse`
\* required. Full dictionary with 500+ aliases: [docs/SCHEMA_DICTIONARY.md](docs/SCHEMA_DICTIONARY.md)
## đ API overview
| Group | Endpoints |
|-------|-----------|
| Normalize | `POST /v1/table/normalize` ¡ batch `/v1/table/batch` |
| Schema | `GET /v1/schema` ¡ `GET/POST /v1/domains` (multi-domain) |
| Export | `POST /v1/export/template/parse` ¡ `/bind` ¡ `/render` ¡ one-shot `/v1/export` |
| Feedback | `POST /v1/feedback` ¡ `/stats` ¡ `/apply` ¡ `/rollback` |
| Observability | `/v1/observations` ¡ `/v1/drift/report` ¡ `/v1/eval/trend` ¡ `/v1/cache/stats` |
| Rules | `GET/POST/DELETE /v1/rules` |
Full examples: [docs/USAGE.md](docs/USAGE.md) (Chinese).
## đ§Ş Testing & evaluation
```bash
pytest tests/ -v # 146 tests, fully offline
pytest tests/eval/test_p2_accuracy.py # regression gate: âĽ85% + anti-cheat
python -m tests.eval.run_p2_eval # per-field accuracy report
python -m tests.eval.kb_expand_llm --dry-run --llm # LLM alias expansion with 4-gate safety
```
## đł Docker
```bash
docker compose up --build
```
## đ¤ Contributing
PRs welcome. Requirements: tests for new features, full `pytest` green, and knowledge-base additions must pass the anti-cheat (zero eval overlap) + no-accuracy-regression gates.
1. Fork â branch (`feat/xxx`) â commit â PR
## đ License
[MIT](LICENSE)
## đ More
- [ĺŽć´ä˝żç¨ćšćĄ / Full usage guide (Chinese)](docs/USAGE.md)
- [Field dictionary snapshot](docs/SCHEMA_DICTIONARY.md)
- [P2 evaluation report](tests/eval/P2_EVAL_REPORT.md)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues