This project implements an LLM-powered email extraction system for freight forwarding pricing enquiries. The objective is to extract structured shipment information from unstructured emails and evaluate accuracy against a provided ground truth dataset.
The final solution combines LLM-based raw extraction with deterministic post-processing logic to handle domain-specific noise (especially port names and abbreviations).
Final overall accuracy on provided dataset: 91.43%
pip install -r requirements.txt
python src/extract.py # Generates output.json
python src/evaluate.py # Prints accuracy metricsThis follows an extraction approach that combines an LLM for signal extraction with deterministic post-processing for precision. The LLM is deliberately constrained to extract raw, unnormalized facts such as port mentions, incoterms, quantities, and dangerous goods indicators. This minimizes hallucinations and keeps the model’s role focused on language understanding rather than business logic.
All domain-specific rules such as UN/LOCODE resolution, product line inference, unit conversions, incoterm defaults, and dangerous goods negation are implemented deterministically. A dedicated port resolution layer handles abbreviations, aliases, and noisy port mentions using cannonical mappings and fallback rules. This separation of concerns proved handful in improving accuracy, especially for ports.
The system performs schema validation using Pydantic and includes fallback handling for malformed LLM outputs. This design ensures reproducible results, clear failure modes, and strong generalization to all kinds of emails.
Description: Initial prompt asking the LLM to extract structured fields directly.
Issues observed:
-
Port codes often missing or incorrect
-
LLM returned raw port names instead of UN/LOCODEs
-
Product line accuracy extremely low
Example failures:
EMAIL_004: destination extracted as "Chennai" instead of INMAA
EMAIL_012: "SHA" not recognized as Shanghai
Changes:
-
Added explicit business rules to the prompt
-
Listed valid incoterms
-
Clarified India import/export logic
Remaining issues:
-
Port abbreviations
(SHA, MAA, SIN)still failed -
Multi-word ports like "Xingang / Tianjin" inconsistent
-
Product line failures due to unresolved ports
Example:
EMAIL_017: origin extracted as "SHA" but failed downstream resolution
Key realization: Prompt tuning alone cannot reliably normalize real-world port mentions.
Changes:
-
Simplified prompt to extract raw facts only
-
Moved normalization into deterministic Python logic
Implemented a robust PortResolver:
-
Alias mapping (SHA → CNSHA, MAA → INMAA)
-
Substring matching
-
India fallback rules
-
Canonicalized port names using port_codes_reference.json
This shift eliminated most remaining errors and generalized well to unseen data.
Final results from evaluate.py:
OVERALL ACCURACY: 91.43%
product_line : 88%
origin_port_code : 86%
destination_port_code : 96%
incoterm : 96%
cargo_weight_kg : 82%
cargo_cbm : 92%
is_dangerous : 100%Issue: Phrases like "non-hazardous" incorrectly flagged as dangerous goods. Explicit negation detection overrides keyword matches.
