license: apache-2.0
base_model: Qwen/Qwen3.5-9B
tags:
- accounting
- thai
- bookkeeping
- structured-output
- qwen3.5
- image-text-to-text
- rag
language:
- th
- en
pipeline_tag: image-text-to-text
extra_gated_heading: Accept the terms before using TAI-1 (Thai Account Assistant)
extra_gated_prompt: >
TAI-1 produces DRAFT accounting entries. Its tax rules and account mappings
have NOT been
reviewed by a licensed Thai accountant or auditor, and its outputs may be
wrong. By requesting
access you agree that:
(1) You will have every output REVIEWED by a licensed bookkeeper (ผู้ทำบัญชี)
or auditor
(ผู้สอบบัญชี) before it is posted to a ledger, filed with the Revenue Department, or used
in any financial statement.
(2) You will not treat the model's outputs as tax or legal advice, and will
not present them to
any third party as the opinion of a licensed professional.
(3) You will supply the applicable tax rules through the prompt (retrieval)
rather than relying
on rates memorised in the weights — Thai tax rates and filing forms change by law.
(4) You understand that I C Develop Co., Ltd. accepts no liability for use
that violates these
terms.
TAI-1 สร้าง "ร่าง" การลงบัญชี
เกณฑ์ภาษีและการจับคู่ผังบัญชียังไม่ผ่านการตรวจสอบโดยผู้ทำบัญชี
หรือผู้สอบบัญชีที่มีใบอนุญาต ผลลัพธ์อาจผิดได้
การขอสิทธิ์เข้าใช้งานถือว่าท่านยอมรับว่า
(1) จะให้ผู้ทำบัญชีหรือผู้สอบบัญชีตรวจสอบทุกผลลัพธ์ก่อนบันทึกลงสมุดบัญชี
ยื่นต่อกรมสรรพากร
หรือนำไปใช้ในงบการเงิน
(2) จะไม่ถือว่าผลลัพธ์เป็นความเห็นทางภาษีหรือกฎหมาย
และจะไม่นำเสนอต่อบุคคลอื่นในลักษณะที่
ทำให้เข้าใจว่าเป็นความเห็นของผู้มีวิชาชีพ
(3) จะส่งเกณฑ์ภาษีที่ใช้บังคับเข้ามาทาง prompt แทนการพึ่งอัตราที่ฝังอยู่ใน
weights เพราะอัตรา
ภาษีและแบบยื่นของไทยเปลี่ยนตามกฎหมาย
(4) บริษัท ไอ ซี ดีเวลลอป จำกัด
ไม่รับผิดชอบต่อการใช้งานที่ขัดกับเงื่อนไขข้างต้น
extra_gated_fields:
I accept all 4 terms and will have a licensed accountant review every output: checkbox
Organization / intended use: text
TAI-1 — Thai Account Assistant (Qwen3.5 9B) · build v2b-coa (2026-08-04)
Draft-grade by design: every output must be reviewed by a licensed Thai bookkeeper or auditor before it is used — accepting that condition is what the access gate asks of you. The team's reading of the underlying tax texts has itself not been signed off by a licensed professional.
A bookkeeping assistant for Thai accounting firms, fine-tuned from
Qwen/Qwen3.5-9B (Apache-2.0).
1. What it does
TAI-1 does not write debit/credit lines. It reads a document and returns a posting template code plus parameters; the ICD-ACC posting engine renders the journal entry. The answer space is closed — 61 templates, 218 accounts — so every axis is scored in code. No LLM judge is used anywhere in this evaluation.
NEW in this build — firm chart-of-accounts mapping (coa_mapping_draft)
Maps an accounting firm's own account names (their phrasing, abbreviations, even typos) to the standard chart. Ground truth: 102 pairs individually verified by a licensed bookkeeper on a signed review form; 6 further pairs the reviewer rejected are excluded everywhere.
Measured head-to-head on those 102 pairs — this capability is why the build exists, and the previous build simply does not have it:
| prompt setup | this build | previous build (no CoA training) |
|---|---|---|
| account catalog in prompt (bare) | 99.0% (101/102) | 32.4% (33/102) |
| + 8 similar verified examples | 82.4% (84/102) | 52.0% (53/102) |
Integration rules (measured, not stylistic) — full guide in INTEGRATION-ICD-ACC.md in this
repo:
- Names an accountant already confirmed → exact lookup, no model call.
- Unseen names → call the model bare with the account block, exactly in its trained format. Do not add few-shot examples on top of this build — it scores lower with them (the table above is the measurement).
- Schema-validate: the answer must be a code from the catalog; reject anything else.
- The 99% column is on vocabulary this build was trained on (two firms). A new firm's unseen phrasings will score between the two columns until its own verified store grows — the lookup-first loop in the integration guide is how accuracy climbs without retraining.
2. Getting the most out of it
2.1 You must put the law in the prompt. This is not optional.
Measured on the tax axis: 100% with the rules supplied, 36.7% without. The model was deliberately trained to apply rules it is given rather than recall them, because Thai tax rates and filing forms change by statute. A model that remembers them is wrong the day the law changes and nobody notices.
The knowledge base is at icdevelop/tai1-kb (separate
access request). Without it you get answers worse than the numbers below, and confidently wrong.
2.2 Retrieve the way the training data was built
This build was trained with legal context in every record, assembled by a specific rule. Serving a differently-shaped context puts the prompt outside the distribution the model saw — that cost the previous build 14 points (100% to 86%).
| Question about | Send |
|---|---|
| Input VAT | ประกาศอธิบดีฯ ฉ.17 + ฉ.42 — whole documents (~17k chars) |
| Withholding tax | ท.ป.4/2528 — whole document (~34k chars) |
| Prohibited expenses / add-back | ประมวลรัษฎากร ม.65 ตรี — the whole (1)–(20) list |
| Financial-statement line items | ประกาศ DBD 2566 — top-4 chunks (~11k chars) |
Send whole documents for the tax questions. Chunk-level retrieval scored 13 points worse even when it retrieved the correct clause, because most of these questions are answered by a rule's absence: "not prohibited" is only knowable if you can see the entire prohibition list. Partial retrieval cannot tell "not on the list" from "not sent to me".
The DBD axis is the exception and proves the principle — there the candidate answers are already
in the prompt, so chunk retrieval is fine. The question type decides the retrieval mode, not
the document size. kb_retrieve.py ships with the KB; use it as-is.
2.3 Settings that must match training
| Value | If you deviate | |
|---|---|---|
| chat template | qwen3_nothink |
thinking mode measured 18.0% — tokens go to reasoning and the JSON never arrives |
| context window | at least 24,576 tokens | longest prompt measured 21,175 tokens; shorter truncates the statute silently |
| temperature | 0 or near 0 | output is JSON checked by code |
2.4 Supply business type as its own fact
The passenger-car input VAT exception turns on the taxpayer's own line of business (dealers, rental firms and motor insurers may claim). Leaving it inside prose is not enough:
ประเภทธุรกิจของกิจการผู้เสียภาษี: ให้เช่ารถยนต์
2.5 Ask for the clause, not just the answer
On the add-back axis, requiring the sub-section alongside the answer raised accuracy from 67.9%
to 75.0%. More importantly, wrong answers become visibly wrong — a reviewer sees
"audit fee → 65ตรี(4) entertainment" and rejects it instantly, rather than a bare true that
reads as authoritative. When the item genuinely is on the list, the cited sub-section is right
16 times out of 17.
ตอบ: {"add_back": true หรือ false, "clause": "<เช่น 65ตรี(6)>" หรือ null}
2.6 Optional documents — what each one buys, and how to add it
The model degrades gracefully: with no reference documents at all it still answers, just far worse and with misplaced confidence. Each document you install lifts a specific axis by a measured amount. You do not need all of them. Install what matches the work you do.
| Document | Lifts | Measured effect | Ships with the KB? |
|---|---|---|---|
| ประกาศอธิบดีฯ ฉ.17 + ฉ.42 | input VAT | tax axis 36.7% → 100% | ✅ yes |
| ท.ป.4/2528 | withholding rates and forms | same axis, same lift | ✅ yes |
| คู่มือเอกสารประกอบการลงบัญชี (RD) | required supporting documents | evidence axis 0/60 → 10/60 exact, recall 0.47 → 0.77 | ✅ yes |
| ประกาศ DBD 2566 | financial-statement line items | statement axis 85.3% → 97.1% | ✅ yes |
| ประมวลรัษฎากร ม.65 ตรี | prohibited expenses / add-back | add-back axis 53.6% → 75.0% | ✅ yes |
| TFRS for NPAEs | recognition, measurement, provisions | ⚠️ not measured — see below | ❌ you must supply it |
Adding a document we cannot ship
TFRS for NPAEs belongs to TFAC and we have no right to redistribute it. Most Thai accounting firms already hold a licensed copy. To add yours:
# 1. extract, with the Thai-character sentinel check
python3 extract_reference.py --pdf /your/path/tfrs-npaes.pdf --out reference-docs/tfrs-npaes.txt
# 2. chunk it into the KB (the config entry already exists)
python3 build_reference_kb.py
Then make the retriever aware of it — it is deliberately not wired up, so that a missing document can never silently shorten anyone's context:
KIND_DOCS["recognition"] = ("tfrs-npaes",)
⚠️ Do not trust the extraction without checking. Thai PDFs lose characters several ways and one of them is unrecoverable. Ours measured 8.3% tone marks, 9.3% vowel signs, zero private-use-area characters. If yours differs a lot, the extraction is broken — do not load it.
⚠️ We publish no number for TFRS because we have never used it. It has never been placed in any prompt, so claiming a lift would be an invention. Measure it on your own data before relying on it. Everything needed to do that ships with the KB.
2.7 The model tells you what it is missing — and this makes it better, not worse
When a question needs a document that is not installed, the serving code states that fact and the model reports it back alongside its answer:
ตอบ: {"add_back": true หรือ false, "clause": "<เช่น 65ตรี(6)>" หรือ null,
"missing_reference": ["<ชื่อเอกสารที่ขาด>"] หรือ []}
kb_retrieve.missing_docs() computes the gap by comparing what a question type needs against
what is loaded, so it fires only when something is genuinely absent.
Measured on the add-back axis with ม.65 ตรี removed from the KB:
| accuracy | flags the gap | |
|---|---|---|
| document missing, model not told | 53.6% | 0 / 28 |
| document missing, told "answer with what you have" | 32.1% | 26 / 28 |
| document missing, told as a plain fact | 60.7% | 23 / 28 |
| document present | 75.0% | — |
⚠️ The wording is the whole thing. Phrasing the notice as an instruction to hold back cost 21 points — the model reported the gap correctly and then stopped trying. Stating the absence as a fact and asking it to answer as well as it can, noting what would confirm the answer, beats saying nothing at all by 7 points while still flagging 23 of 28 cases.
So an installation missing an optional document is not merely tolerated — as long as the serving layer declares the gap plainly, the model does better than if it had never been told. Do not rewrite that notice into a hedge.
3. Cautions
- Output is a draft. A licensed bookkeeper or auditor must review it before anything is posted, filed, or placed in a financial statement. It is not tax or legal advice.
- Tax criteria come from Revenue Department texts, but our reading of them is not signed off. Clause citations exist so a reviewer can check the reading instead of trusting it.
- ⚠️ Known bias: showing the model a prohibition list makes it over-apply that list. Observed on three separate axes. On add-back, ordinary deductible costs — office rent, factory electricity, raw materials, audit fees — are flagged for add-back 7 times in 10. Treat any "prohibited" answer as a candidate for review, never as a conclusion.
- The add-back and statement axes have never been trained. They are measurements of base behaviour published for transparency, not capabilities. Do not build on them yet.
- The pnd3/pnd53 form mapping has no cited source. It appears in none of the fetched documents; it is a team convention, labelled as uncited inside the prompt, and needs sign-off.
- Rates have a time dimension. The 1.0% e-withholding rate expired 31 December 2025. Back-dated documents need the text in force on their own date.
- Do not rely on rates baked into the weights. The model recalls roughly 37% of tax knowledge unaided, by design.
4. Results
Measured with the KB in the prompt and retrieval matching training.
| แกน | ผล | จำนวนเคส |
|---|---|---|
| จับคู่ผังบัญชี | 96.0% | 200 |
| รายจ่ายบวกกลับ ม.65 ตรี — รายงานอย่างเดียว ไม่เคยเทรน | 64.3% | 28 |
| คำนวณจำนวนเงิน | 98.7% | 150 |
| รู้ว่าเมื่อไหร่ข้อมูลไม่พอ — รายงานอย่างเดียว | 86.1% | 101 |
| เอกสารประกอบที่กฎหมายต้องการ | 18.3% | 60 |
| รายการย่อของงบการเงิน (DBD) — รายงานอย่างเดียว ไม่เคยเทรน | 97.1% | 34 |
| ภาษี (ข้อยกเว้น) — รายงานอย่างเดียว ไม่ใช่ gate | 95.2% | 21 |
| ภาษี (เคสธรรมดา) | 98.0% | 150 |
| เลือก template ถูกต้อง | 100.0% | 200 |
| อ่านใบกำกับภาษีจากภาพสแกน (hard-scan 200 ภาพ ครบทุก field) | 77.5% | 200 |
Small axes (21–34 cases) are report-only — one case moves them 3–5 points. The hard gates are template ≥90, account ≥80, ordinary tax ≥95, amounts ≥98, schema violations = 0; this build passes all of them.
5. How the evaluation works
- Everything is scored in code. The label space is closed, so answers compare directly.
- Templates are split by hash into train and holdout, and the corpus is built only from the train side, so holdout numbers measure generalisation. 26 of 61 templates are held out.
- Two-layer decontamination. Corpus phrase banks are disjoint from the benchmark, and every record is then re-checked by token overlap; the build fails if anything crosses the threshold.
- Boolean axes carry control cases and are reported per side. The exceptions axis has 6 controls in 21, add-back has 10 in 28. Without them add-back would read 18/18 and the conclusion would be exactly backwards — see caution 3.
- Small axes are report-only. At 21–34 cases one case is 3–5 points; use them for direction and for reading individual failures, never as a gate.
- The image axis is mandatory for quantised builds. Text benchmarks cannot detect a damaged vision tower — a model with no image processor at all still passes every text axis.
6. The knowledge base is a separate download
Not bundled with the weights. It lives at
icdevelop/tai1-kb, also private and gated.
It holds 98 chunks from five Revenue Department documents plus the DBD notification — laws, Director-General notifications and official manuals, which under section 7 of the Thai Copyright Act B.E. 2537 are not copyright works and may be redistributed.
⚠️ TFRS for NPAEs is deliberately excluded. It belongs to the Federation of Accounting
Professions (TFAC) and is not a government work under section 7; a private repository is access
control, not a licence. No number reported here depends on it — it has never been placed in
any prompt, which is checkable three ways and documented in the KB repo. If you need it, the KB
ships ADD-YOUR-OWN-TFRS.md explaining how to add your own licensed copy with the same
extraction scripts.
7. Training data
3,866 records, one epoch, every tax description textually distinct. Two sources:
- 3,764 synthetic records built from the posting-template catalog, the chart of accounts, and the statutes — the same generator family as the previous build.
- 102 real chart-of-account names from two Thai accounting firms, each mapping verified by a licensed bookkeeper before entering the corpus. These are account names only (e.g. "ค่าเสื่อมราคาสะสม-ยานพาหนะ") — no customer documents, no transactions, no amounts, no personal data; firm identity is not recoverable from them.
I C Develop Co., Ltd. · public release under continuing review — the access gate's accountant-review condition applies to every use