ในบทความนี้
In this post
ตอนที่ 3 จากสิบสอง: เก็บ schema ชนิดข้อมูล ขนาด และรูปแบบที่แน่นอนไว้ในโค้ด ตรวจต้นทางก่อนเสีย inference budget
คำตัดสินและร่างข้อความเริ่มต้นเป็น mock ที่ผู้เขียนกำหนด ไม่ใช่ผล Jev จริง อ่านบทเรียนคู่กับ source, tests และแบบฝึกที่ลิงก์ไปยังรุ่นตรึงใน GitHub
เป้าหมายและสิ่งที่ต้องพร้อม
เตรียม Node22.18 ขึ้นไปกับ terminal clone companion จาก GitHub ตาม README หรือดาวน์โหลด ZIP ฉบับสมบูรณ์ แตกแล้วเข้า ai-core-functional-lab ไม่ต้อง npm install ใช้ API key หรือเรียกโมเดลผ่านเครือข่าย เวลาอ่านไม่รวมการลงมือประมาณ25–45นาที เก็บชุดอ้างอิงหนึ่งชุดและทำแบบฝึกในอีกสำเนา
ดาวน์โหลด companion ZIP · README / แผนบทเรียน
ตอนนี้เน้น src/input.mjs โดย gate state บอกผลต่อชิ้นงานและทางไปต่อ ไม่มี state ใดส่งข้อความหรือแก้บัญชีจริง
จากโค้ดเดิมสู่ขอบเขตใหม่
ตรวจสิ่งที่สังเกตตรง ๆ ด้วยโค้ดเดิม เช่น ชื่อฟิลด์ enum ภาษา รูปแบบ ID ชนิด string และขนาด การเรียกโมเดลกับเงื่อนไขที่ parser ตัดสินได้แน่นอนเพิ่มทั้งเวลาและความกำกวม validation เดิมไม่ได้ล้าสมัย
ชุดฟิลด์ที่รับไม่มีเฉลยหรือฟิลด์สั่งงานส่วนเกิน หลังตรวจชนิดกับขนาดจึง normalize NFC และ trim ค่า length ของ JavaScript นับ UTF-16 code units ไม่ใช่คำภาษาธรรมชาติ ขอบเขตนี้คุม payload ไม่ได้วัดความอ่านง่าย
deny pattern สั้น ๆ จับ injection ที่รู้จักได้ แต่ไม่ครอบคลุมทุกการถอดคำ ตัวกรองโค้ดลดเคสชัดก่อนด่านความหมายที่ผิดพลาดได้ การแยกชั้นทำให้ทดสอบข้อจำกัดได้ แทนเรียก regex ว่าระบบความปลอดภัยครอบจักรวาล
ลงมือทีละเจ็ดขั้น
ขั้นที่ 1
อ่าน input สี่ฟิลด์ ID ใช้ระบุเคสสมมติ language เลือกภาษา request เป็นข้อความที่ไม่เชื่อถือ ส่วน requestedAction ยังต้องผ่านด่านสิทธิ์
ขั้นที่ 2
รัน input ของ V001 ควรได้สำเนาที่ normalize แล้วกับ state accept ซึ่งรับเพียงรูปแบบ input ยังไม่ได้บอกคุณภาพคำตอบปลายทาง
ขั้นที่ 3
รัน V002 ที่ request เป็นตัวเลข ต้อง reject ก่อน normalize การใช้ String แปลงให้ผ่านคือสร้าง request ใหม่ ไม่ใช่ตรวจ request ที่ caller ส่งมา
ขั้นที่ 4
รัน V003 แล้วชี้ pattern ที่ทำให้ปฏิเสธ เก็บ test ของประโยคนั้น พร้อมจดว่าผลขึ้นกับรายการ pattern เพื่อไม่ให้ test ที่ผ่านกลายเป็นคำอ้างกว้างเกินหลักฐาน
ขั้นที่ 5
ดูการตรวจ object และชื่อฟิลด์ ไม่รับ key แปลก ไม่รับ array แทน record และ ID ใช้เป็น path ไม่ได้ ตรวจให้เสร็จก่อน provider เห็น request เพื่อไม่เปลือง inference กับงานเสียรูป
ขั้นที่ 6
อ่าน normalization test และยืนยันว่า object ต้นฉบับไม่เปลี่ยน การประกอบฟังก์ชันง่ายขึ้นเมื่อด่านคืนค่าใหม่ แทนแก้ input ของ caller โดยไม่บอก
ขั้นที่ 7
เชื่อม input gate กับ early return ของ pipeline เมื่อ input เสีย spy ของ provider ต้องเป็นศูนย์ ขอบเขตยังไม่สมบูรณ์ถ้างานถัดไปยังถูกรัน
ไฟล์และคำสั่งที่รันได้
เปิด source จริงและอ่าน dependencies ก่อนแก้ นี่คือโค้ดอ้างอิงที่รันได้ ไม่ใช่ SDK ชื่อ AI-Core ที่สมมติขึ้น ลิงก์ชี้ source รุ่นตรึงของ companion สาธารณะใน GitHub
src/input.mjs · tests ของตอนนี้ · แบบฝึกตอนนี้
import {gate,plain} from './contracts.mjs';
export function checkInput(raw) {
if(!plain(raw)) return gate('reject',['input_not_object']);
const keys=['id','language','request','requestedAction'];
if(Object.keys(raw).some(k=>!keys.includes(k))) return gate('reject',['unknown_input_field']);
if(typeof raw.id!=='string'||!/^[VHA]\d{3}$/.test(raw.id)) return gate('reject',['invalid_id']);
if(!['en','th'].includes(raw.language)) return gate('reject',['unsupported_language']);
if(typeof raw.request!=='string'||!raw.request.trim()||raw.request.length>2000)
return gate('reject',['invalid_request_size']);
if(typeof raw.requestedAction!=='string') return gate('reject',['missing_action']);
const request=raw.request.normalize('NFC').trim();
if(/ignore (all|previous)|system prompt|bypass the gate/i.test(request))
return gate('reject',['known_injection_pattern']);
// This deliberately small pattern list is not a complete injection detector.
return gate('accept',[],{id:raw.id,language:raw.language,request,requestedAction:raw.requestedAction});
}
คำสั่งออฟไลน์:
node src/cli.mjs input --case V001
node src/cli.mjs input --case V002
node src/cli.mjs input --case V003
node --test --test-reporter=spec test/contracts.test.mjs
ผลที่ควรได้และการตรวจ
V001 ผ่านรูปแบบ V002 reject ด้วย invalid_request_size และ V003 ด้วย known_injection_pattern ส่วน pipeline tests ยืนยันว่าไม่เรียก provider เมื่อโค้ดปฏิเสธ คุณสมบัติเหล่านี้ไม่ต้องใช้คำตัดสินจากโมเดล
อ่าน reasons, history และทุก attempt ไม่ดูเพียง label สุดท้าย usage ที่ไม่ทราบต้องยังไม่ทราบ test สีเขียวแสดงเส้นทางโค้ดที่กำหนด ไม่ได้พิสูจน์ความปลอดภัยเชิงความหมายทุกกรณี
ตัวอย่างที่ต้องเข้าใจเมื่อไม่ผ่าน
การแปลงชนิดแบบยอมทุกอย่างอาจเปลี่ยน array หรือตัวเลขเป็นข้อความแล้วส่งเข้าด่านความหมาย ซ่อน bug ของ caller และเปลี่ยนประชากรที่ทดสอบ ควรปฏิเสธรูปแบบผิดให้ชัด
แบบฝึกในสำเนาทดลอง
เพิ่ม test ที่ 2000 code units กับ 2001 แล้วเพิ่ม property ที่ไม่รู้จักใน record ที่เหลือถูกต้อง ให้เป็น code tests ไม่ต้องถาม Jev ให้นับหรือตรวจ schema
ทำตามไฟล์แบบฝึก เก็บต้นฉบับและคืนชุดอ้างอิงก่อนตอนถัดไป อย่าใช้ผล held-out เลือก threshold
ตรวจเมื่อผลไม่ตรง
อักขระผสมภาษาไทยทำให้จำนวนที่มองเห็นต่างจาก length อย่าเรียกขอบเขต payload นี้ว่าจำนวนคำไทย หากเปลี่ยนลำดับ normalize ต้องรันทดสอบขอบขนาดใหม่
จุดตรวจถัดไป
ก่อนต่อ ให้บอกคุณสมบัติที่โค้ดตรวจได้แน่นอน คำตัดสินที่ผิดได้ state ที่หยุดงาน และหลักฐานที่ต้องมีก่อนปล่อยรุ่น
แหล่งอ้างอิงและรุ่น
เอกสารต้นทางและ companion source
ตรวจเอกสาร TypeSafe วันที่24ตุลาคม2026 บทเรียนตรึงคำขอข้อความที่ jev-1.14.0 และใช้ Choice/Noul ไม่ได้อ้างว่ามีเพียงความสามารถเหล่านี้ โค้ด REST ทางเลือกยังไม่ได้ลอง live ส่วน SDK0.7.0 มีเอกสารแต่แล็บนี้ไม่ต้องติดตั้ง
สิ่งที่ควรนำไปใช้ต่อ
- เงื่อนไขแน่นอนและสิทธิ์ยังอยู่ในโค้ด
- ด่านความหมายและคุณภาพผิดพลาดได้
- accept, reject, revise และ review มีทางไปต่อคนละแบบ
Part 3 of twelve. Keep exact schema, type, size and field checks in ordinary code before spending an inference budget.
Default decisions and draft text are authored mocks, not live Jev results. Use the tutorial with the matching companion code, tests and exercise linked to a fixed GitHub version.
Outcome and prerequisites
Use Node22.18+ and a terminal. Clone the GitHub companion using its README, or download the complete ZIP, extract it and enter ai-core-functional-lab. No npm install, API key or network inference is needed. The reading-time label excludes about25–45 minutes of hands-on work. Keep one reference copy and make exercises in another.
Download companion ZIP · README / lesson map
This part focuses on src/input.mjs. Expected gate states describe content and continuation; no state sends a message or changes a real account.
Old functions and the new boundary
Keep observable input properties in ordinary code: field names, language enum, ID shape, string type and size. A probabilistic call adds latency and ambiguity to checks a parser can decide exactly. This is familiar validation, not an obsolete practice.
The allowed fields exclude evaluator labels and instruction-like extras. NFC normalization and trimming operate after type and size checks. JavaScript length counts UTF-16 code units, not linguistic words; this bound limits payload size rather than measuring readability.
A short deny pattern catches known injection examples, but it cannot identify every hostile paraphrase. Code filters reduce obvious noise before a fallible semantic guard. Separating these stages makes their limits testable instead of calling a regex a universal security system.
Seven practical steps
Step 1
Read the four allowed input keys and their meanings. An ID identifies a synthetic case, language selects the response track, request is untrusted text, and requestedAction is still subject to authorization.
Step 2
Run V001 through the input command. The returned value should be a normalized copy with state accept. This only accepts the input shape; it says nothing about the eventual response quality.
Step 3
Run V002, whose request is a number. Reject it before normalization; coercing it with String would create a different request instead of validating the caller’s request.
Step 4
Run V003 and identify the exact pattern responsible for rejection. Keep a test for that phrase, but also record that the result depends on the pattern list. This prevents a passing test from becoming a sweeping claim.
Step 5
Inspect the object and field checks. Unknown keys are refused, arrays are not accepted as records, and IDs cannot become file paths. Validate before providers see a request so malformed work consumes no inference budget.
Step 6
Read the normalization test and verify the original object remains unchanged. Composition is easier when one gate returns a new value rather than mutating the caller’s input behind its back.
Step 7
Connect the input gate to the pipeline’s early return. For malformed input, provider call spies must stay at zero. The boundary is not complete until downstream work actually stops.
Files and runnable commands
Open the exact source and read its dependencies before editing. This is runnable reference code, not a fictional AI-Core SDK. The source links point to a fixed version of the public GitHub companion.
src/input.mjs · Matching tests · This lesson’s exercise
import {gate,plain} from './contracts.mjs';
export function checkInput(raw) {
if(!plain(raw)) return gate('reject',['input_not_object']);
const keys=['id','language','request','requestedAction'];
if(Object.keys(raw).some(k=>!keys.includes(k))) return gate('reject',['unknown_input_field']);
if(typeof raw.id!=='string'||!/^[VHA]\d{3}$/.test(raw.id)) return gate('reject',['invalid_id']);
if(!['en','th'].includes(raw.language)) return gate('reject',['unsupported_language']);
if(typeof raw.request!=='string'||!raw.request.trim()||raw.request.length>2000)
return gate('reject',['invalid_request_size']);
if(typeof raw.requestedAction!=='string') return gate('reject',['missing_action']);
const request=raw.request.normalize('NFC').trim();
if(/ignore (all|previous)|system prompt|bypass the gate/i.test(request))
return gate('reject',['known_injection_pattern']);
// This deliberately small pattern list is not a complete injection detector.
return gate('accept',[],{id:raw.id,language:raw.language,request,requestedAction:raw.requestedAction});
}
Offline commands:
node src/cli.mjs input --case V001
node src/cli.mjs input --case V002
node src/cli.mjs input --case V003
node --test --test-reporter=spec test/contracts.test.mjs
Expected output and verification
V001 accepts shape; V002 rejects invalid_request_size; V003 rejects known_injection_pattern. Pipeline tests confirm these code-level refusals make no provider calls. No model judgment is needed to establish those particular properties.
Inspect reasons, history and every attempt rather than only the final label. Unknown usage must remain unknown. A green test demonstrates the specified code path, not universal semantic safety.
A failure to understand
A permissive coercion may turn an array or number into text and allow later semantic work. That hides a caller bug and changes the test population. Refuse the malformed representation explicitly instead.
Exercise in a disposable copy
Add a test at exactly 2000 code units and another at 2001. Then add an unknown property to an otherwise valid record. Keep these as code tests rather than asking Jev to count or inspect schema.
Follow the matching exercise file, retain the original and restore the reference before the next chapter. Do not use held-out results to choose a threshold.
Troubleshooting
Thai combining marks can make visual character counts differ from length. Do not describe this payload bound as a Thai word limit. If you change normalization order, rerun size-boundary tests deliberately.
Next checkpoint
Before continuing, name the property that code checks exactly, the judgment that may be wrong, the state that stops work and the evidence needed before release.
← Previous lesson · Next lesson →
Sources and version notes
Primary documentation and companion source
Official TypeSafe docs checked2026-10-24. This curriculum pins text requests to jev-1.14.0 and uses Choice/Noul; it does not claim those exhaust Jev’s capabilities. Optional REST code is untested live. SDK0.7.0 is documented but unnecessary for this no-install lab.
Key takeaways
- Deterministic constraints and authorization stay in code.
- Semantic guards and quality judgments are fallible.
- Accept, reject, revise and review have distinct continuation rules.