กฎให้คะแนนและการตรวจด้วย DeepSeek ของ 3D Warning
โน้ตนี้อธิบายว่า 3D Warning ตรวจจับความผิดปกติและทุจริต ภาพรวม (SYS-15 FraudWarning) ตัดสินความเสี่ยงอย่างไร: ด่านแรกเป็นกฎคำสำคัญฝั่งเซิร์ฟเวอร์ที่ไม่เสียโทเคน (evaluateActivityRisk) ด่านที่สองเป็นการส่งข้อความย่อให้ DeepSeek (runDeepSeekFraudInspection) ทุกอย่างอยู่ใน frontend/workmonitor/mongoAuditStore.ts และชนิดข้อมูลอยู่ใน fraudWarningData.ts
1. คะแนนจากการกระทำและช่องที่แก้
คะแนนเริ่มที่ 0 บวกตามกลุ่มที่เข้าเงื่อนไข (แต่ละกลุ่มนับครั้งเดียว) แล้วบีบให้อยู่ระหว่าง 5 ถึง 99
| กลุ่ม | เงื่อนไข | คะแนน | หมวด (fraudCategoryCode) |
|---|---|---|---|
| แก้แล้วปิดหนี | การกระทำ ABANDON_WITHOUT_SAVE หรือผลลัพธ์ ABANDONED_UNSAVED |
+65 | GHOST_EDIT_ABANDON |
| ยกเลิกหลังแก้ | CANCEL_EDIT หรือ CANCELLED_AFTER_EDIT และมีช่องที่แก้อย่างน้อย 1 |
+48 | PROBING_CANCEL_EDIT |
| ลบเอกสาร | DELETE_RECORD หรือ DELETED |
+72 | SUSPICIOUS_DELETION_VOID |
| เปิดส่องข้อมูล | CLOSE_AFTER_PEEKING, PEEKED_NO_SAVE หรือเลือกดูตั้งแต่ 4 รายการ |
+52 | DATA_HARVESTING_PEEK |
| ออกนอกระบบ | TAB_OUTSIDE_SYSTEM |
+22 | ไม่เปลี่ยนหมวด |
| บัญชีธนาคาร/ภาษี | ชื่อช่องหรือค่ามีคำเกี่ยวกับเลขบัญชี ธนาคาร พร้อมเพย์ ภาษี VAT หัก ณ ที่จ่าย | +32 | VENDOR_BANK_SWAP เมื่อเป็นแผนกจัดซื้อ |
| ราคา/ส่วนลด/ยอดเงิน | คำว่า ราคา ส่วนลด ยอดเงิน มูลค่า ต้นทุน และคำอังกฤษ price/discount/amount/cost | +26 | POS_PRICE_DISCOUNT_OVERRIDE เมื่อเป็นแผนก POS |
| สต็อก/จำนวน | คำว่า จำนวน คงเหลือ สต็อก ปรับปรุง คลัง ของเสีย ชำรุด และ qty/balance | +25 | WMS_SHRINKAGE_COVERUP เมื่อเป็นแผนกคลัง |
| วงเงินเครดิต | คำว่า เครดิต วงเงิน credit | +28 | AR_CREDIT_LIMIT_BYPASS |
| เงินเดือน/สิทธิ์ | คำว่า เงินเดือน ค่าแรง โอที สิทธิ์ ระดับ และ salary/permission/level/password | +35 | HR_PRIVILEGE_SALARY_TAMPER |
| น้ำมัน/ไมล์/ซ่อม | คำว่า น้ำมัน ไมล์ ซ่อม อะไหล่ และ fuel/mileage | +26 | TMS_FUEL_MAINTENANCE_SKIM |
- หมวดและหัวข้อไทยของกลุ่มช่องจะถูกตั้งเมื่อยังไม่มีหัวข้อจากกลุ่มการกระทำ (ยกเว้น
VENDOR_BANK_SWAPที่ตั้งทับเมื่อเป็นแผนกจัดซื้อ) ถ้าไม่เข้ากลุ่มใดเลยหัวข้อจะเป็นข้อความทั่วไป "พบพฤติกรรมผิดปกติใน <ชื่อหน้าจอ>" - ตัวจับคำทำงานกับ "ชื่อช่อง + ชื่อช่องที่แก้ + ค่าเดิม + ค่าใหม่" ที่แปลงเป็นตัวพิมพ์เล็กแล้ว ไม่รวมชื่อหน้าจอ ค่าที่ผู้ใช้พิมพ์จึงทำให้ติดธงได้ เช่น ชื่อสินค้าที่มีคำว่า "คลัง"
- ระดับความรุนแรง: 80 ขึ้นไป
CRITICAL, 60HIGH, 40MEDIUM, 20LOW, ต่ำกว่านั้นSAFE - ตัวอย่างจากสูตร: ปิดหนีโดยไม่บันทึก 65 (
HIGH); ลบเอกสาร 72 (HIGH); ยกเลิกหลังแก้ 48 (MEDIUM); บันทึกที่แก้เฉพาะช่องราคา 26 (LOW); บันทึกที่แก้ช่องธนาคารร่วมกับราคา 58; ปิดหนีที่มีช่องส่วนลดด้วย 91 (CRITICAL)
เมื่อไรถึงเกิดแจ้งเตือน
needsDeepSeekScan เป็นจริงเมื่อคะแนน 45 ขึ้นไป หรือเป็น ABANDON_WITHOUT_SAVE/DELETE_RECORD/CANCEL_EDIT ที่มีช่องแก้ หรือ SAVE_DOCUMENT ที่คะแนน 35 ขึ้นไป แต่ตอนสร้างแจ้งเตือนโค้ดเช็คทั้ง needsDeepSeekScan และคะแนน 45 ขึ้นไป ดังนั้นในทางปฏิบัติ "แจ้งเตือนเกิดเมื่อคะแนน 45 ขึ้นไปเท่านั้น" เงื่อนไขอื่นไม่ได้สร้างแจ้งเตือนเอง (บันทึกที่คะแนน 35-44 เก็บเป็นกิจกรรมอย่างเดียว) แจ้งเตือนที่ได้เป็นแบบ "ไฮบริด 0 โทเคน" ตามที่เล่าใน 3D Warning ตรวจจับความผิดปกติและทุจริต ภาพรวม (SYS-15 FraudWarning)
2. ตัวตรวจจับเหตุการณ์ฝั่งเบราว์เซอร์ มีผลต่อคะแนน
- ตัวแทนส่ง
selectedRecordsCountเท่ากับจำนวนรหัสแถว (ข้อความช่องแรกของแถว) ที่ไม่ซ้ำกันซึ่งเคยถูกคลิกบนหน้าจอนั้น และไม่ล้างค่าจนกว่าจะเปลี่ยนหน้าจอ ตามโค้ดกฎ "เลือกดู 4 รายการ" (+52) จึงติดกับ "ทุกการกระทำถัดไป" บนหน้าจอเดียวกัน รวมถึงการแก้ช่องและการบันทึก ทำให้หน้ารายการที่ผู้ใช้คลิกหลายแถวก่อนแก้ไขอาจเกิดแจ้งเตือนได้ง่าย (อ่านจากโค้ด ยังไม่ได้ทดลองรัน) - ตัวแทนแยก "ลบ" ด้วยปุ่มที่ข้อความเป็น
ลบ,ลบรายการ,ยืนยันลบ,ยกเลิกบิลหรือvoidแต่เงื่อนไข "ยกเลิก" (ประเภทCANCEL_EDIT/CLOSE_SCREEN) ถูกตรวจก่อน และคำว่า "ยกเลิก" ครอบคลุม "ยกเลิกบิล" ด้วย ตามลำดับเงื่อนไขในโค้ด ปุ่ม "ยกเลิกบิล" จึงไม่ถูกบันทึกเป็นDELETE_RECORD(ถ้าไม่มีช่องที่แก้ ผลคือCLOSE_SCREENปกติ) - สถานะ "บันทึกแล้ว" ตั้งทันทีที่คลิกปุ่มที่ข้อความตรงกับ "บันทึก/Save/ชำระเงิน/ยืนยันสั่งซื้อ" หรือกด Ctrl+S ขณะอยู่โหมดแก้ไข
- การทิ้งจอที่แก้ค้างโดยไม่บันทึก (
ABANDON_WITHOUT_SAVE) ตรวจตอนสลับหน้าจอ/ปิดแท็บ/ออกจากหน้า ถ้ามีช่องที่แก้หรืออยู่โหมดแก้ไข และยังไม่เคยกดบันทึก
3. การจัดแผนก (7 แผนก)
resolveFraudDepartment ต่อ path, โมดูล, รหัสเมนู และชื่อหน้าจอเป็นข้อความเดียว (ตัวพิมพ์เล็ก) แล้วเลือกแผนกแรกที่มีคำตรง ตามลำดับนี้
| ลำดับ | รหัสแผนก | ชื่อย่อบนจอ | คำที่ใช้จับ |
|---|---|---|---|
| 1 | DEP-POS |
ขายหน้าร้าน POS | /pos, pos, แคชเชียร์, ขายหน้าร้าน |
| 2 | DEP-PROCURE |
จัดซื้อ & เจ้าหนี้ | /procure, จัดซื้อ, เจ้าหนี้, ap_, po |
| 3 | DEP-WMS |
คลังสินค้า & สต็อก | /wms, คลัง, สต็อก, สินค้า, ic_ |
| 4 | DEP-TMS |
ขนส่ง & ฟลีทรถ | /tms, ขนส่ง, รถ |
| 5 | DEP-SALES |
ขายส่ง & ลูกหนี้ | /crm, /market, ลูกค้า, ลูกหนี้, ar_, ใบเสนอราคา, ขายส่ง |
| 6 | DEP-HR-IT |
บุคคล & ผู้ดูแลระบบ | พนักงาน, ผู้ใช้, สิทธิ์, เงินเดือน, erp_user, b102, config |
| 7 | DEP-FINANCE |
การเงิน & บัญชี | ไม่เข้าข้างบนเลย (ค่าตั้งต้น) |
ผลคือการประมาณจากข้อความ เช่น คำสั้น po ตรงกับคำอื่นที่มีตัวอักษรนี้ (เช่น report, export) หน้าจอที่ไม่เข้าลำดับใดจะตกเป็นแผนกการเงินทั้งหมด และชื่อผู้ใช้/แผนกจริงของบริษัทไม่ถูกนำมาใช้ แต่ละแผนกมีข้อความ "กฎและธงแดง" ที่แสดงบนแท็บ "ครบ 7 แผนก" ซึ่งเป็นคำอธิบายอย่างเดียว ไม่ได้ถูกนำไปคำนวณคะแนน
4. ด่านที่สอง: DeepSeek และการประหยัดโทเคน
ตัวกระตุ้น (triggeredBy): MANUAL_SCAN จากปุ่มบนจอ (สูงสุด 8 บันทึก, กรองตามแผนกที่เลือก), HIGH_RISK_TRIGGER จาก record_activity ที่ตัวแทนตั้ง autoRunDeepSeek และมีเหตุเสี่ยง (คะแนนเบื้องต้น 60 ขึ้นไป, ปิดหนี หรือลบ) สูงสุด 5 บันทึก, SIMULATION จากปุ่มจำลอง สูงสุด 4 บันทึก ค่าเริ่มต้นเมื่อไม่ระบุเลขเป็น 4
ชั้นที่ 1 (กรองฝั่งเซิร์ฟเวอร์ ไม่เสียโทเคน) อ่านบันทึกล่าสุด 80 แถว (กรองตามแผนกถ้าระบุ) เลือกเฉพาะที่คะแนนเบื้องต้น 35 ขึ้นไป หรือผลลัพธ์เป็นปิดหนี/ยกเลิกหลังแก้/ลบ/เปิดส่องแล้วไม่บันทึก หรือมีช่องที่แก้ แล้วตัดตามจำนวนสูงสุดข้างต้น ถ้าไม่มีบันทึกน่าสงสัยเลย โค้ดจะหยิบ 3 บันทึกล่าสุดของแผนกนั้นไปส่งแทน (จึงไม่ได้คัดทิ้งทั้งหมด) ตัวนับ "ตัดออก n จาก m บันทึก" บนจอคือผลต่างนี้
ชั้นที่ 2 (ข้อความย่อ) buildCompactTokenSavingDigest แปลงบันทึกเป็นหนึ่งบรรทัดต่อหนึ่งบันทึกคั่นด้วย |: รหัสบันทึก, รหัส(ชื่อ)พนักงาน, รหัสแผนก, [รหัสเมนู] พร้อมชื่อหน้าจอตัดที่ 28 ตัวอักษร, ประเภทการกระทำ, rec: รหัสเอกสาร, diff: ช่องที่แก้ไม่เกิน 3 ช่อง (ตัดรวมที่ 90 ตัวอักษร), out: ผลลัพธ์, และจำนวนวินาที ประมาณโทเคนด้วยอัตราส่วนตัวอักษร (ข้อความดิบหาร 2.6 ขั้นต่ำ 120; ข้อความย่อหาร 3.2 ขั้นต่ำ 30) ไม่ใช่ตัวนับโทเคนจริง
ชั้นที่ 3 (เรียกโมเดล) เมื่อมี env DEEPSEEK_API_KEY (ถ้าไม่มี โค้ดลองอ่านบรรทัดที่ขึ้นต้นด้วยชื่อตัวแปรนี้จากไฟล์ .env.local หรือ .env.deepseek ใน cwd หรือโฟลเดอร์แม่) จะ POST ไป https://api.deepseek.com/chat/completions ด้วยโมเดล deepseek-chat ส่งคีย์ในส่วนหัว Authorization อุณหภูมิ 0.1 max_tokens 800 ไม่สตรีม ข้อความระบบเป็นคำสั่งภาษาไทยคงที่ให้ตอบเป็นอาร์เรย์ JSON อย่างเดียว (โค้ดเรียกว่า prefix caching แต่เพียงรักษาข้อความระบบให้คงที่ ไม่ได้ตรวจว่าผู้ให้บริการใช้แคชจริง) ไม่ได้ตั้งเวลาหมดอายุให้คำขอนี้ คำตอบถูกอ่านโดยตัดข้อความตั้งแต่วงเล็บเหลี่ยมเปิดตัวแรกถึงวงเล็บเหลี่ยมปิดตัวสุดท้ายแล้วแปลงเป็น JSON
การแปลงคำตอบเป็นแจ้งเตือน: จับคู่กับบันทึกด้วยรหัส (ถ้าไม่ตรงใช้ลำดับ) ความรุนแรงรับเฉพาะ CRITICAL/HIGH/MEDIUM/LOW (ค่าอื่น ๆ ตกเป็น CRITICAL ถ้าคะแนนเบื้องต้น 80 ขึ้นไป ไม่เช่นนั้น HIGH) คะแนนบีบให้อยู่ที่ 40-99 แจ้งเตือนเดิมที่ตรงกัน (รหัสร่องรอยเดียวกัน หรือพนักงานคนเดียวกันกับรหัสเอกสารเดียวกัน ซึ่งรวมกรณีที่ทั้งคู่เป็น "-") จะถูกอัปเดตแทนการสร้างใหม่ โดยคงสถานะและหลักฐานเดิม แล้วเขียนเข้า MongoDB แบบ upsert ตั้ง fraudCategoryCode เป็น DEEPSEEK_AI_AUDIT และ detectedBy เป็น DEEPSEEK_AI
ถ้าเรียกไม่สำเร็จ (ไม่มีคีย์ คำตอบไม่ปกติ เครือข่ายล้ม หรืออ่านผลไม่ได้) โค้ดกลืนข้อผิดพลาดแล้วบันทึกรอบตรวจต่อ โดยชื่อโมเดลต่อท้าย (Hybrid Cache) ไม่มีแจ้งเตือนใหม่จาก AI แต่:
- ตัวเลขโทเคนเป็นค่าประมาณคงที่ (ข้อความย่อที่ประมาณ + 75 สำหรับพรอมต์ และ 165 สำหรับคำตอบ)
warningsGeneratedCountถูกตั้งเป็นจำนวนบันทึกที่ส่ง ทั้งที่ไม่ได้สร้างแจ้งเตือน- ค่าที่ route คืนเป็น "แจ้งเตือนอัปเดต" คือแจ้งเตือนล่าสุด 8 รายการในสำเนา RAM
- แถบข้อความบนจอยังพูดว่า "ใช้เพียง n Tokens (ประหยัดไป n%) พบ/อัปเดตแจ้งเตือน n รายการ" ซึ่งอ่านแล้วคล้ายว่าเรียก AI สำเร็จ
ตัวเลข "ประหยัด Token" คำนวณจากประมาณการ: ฐานเทียบ = ค่ามากกว่าระหว่าง (โทเคนรวม × 6) กับ (ประมาณการข้อความดิบ + 950) เปอร์เซ็นต์ที่ประหยัดบีบให้อยู่ระหว่าง 85.0 ถึง 98.5 เสมอ จึงไม่ใช่ค่าที่วัดได้จริงจากผู้ให้บริการ (ค่าเฉลี่ยรวมบนจอคำนวณจากผลรวมของรอบที่เก็บไว้ ถ้ายังไม่มีรอบเลยเป็นค่าคงที่ ดู 3D Warning ตรวจจับความผิดปกติและทุจริต ภาพรวม (SYS-15 FraudWarning)) ส่วนแจ้งเตือนไฮบริดที่สร้างทันทีระบุว่าประหยัด 320 โทเคนเป็นค่าคงที่ ข้อความ "ลดขนาดพรอมต์กว่าร้อยละ 92" บนแท็บ "ประหยัด Token" เป็นคำอธิบายที่เขียนไว้ ไม่ได้คำนวณจากข้อมูลจริง
ข้อมูลที่ออกไปยังภายนอก ข้อความย่อมีรหัสและชื่อพนักงาน แผนก หน้าจอ รหัสเอกสาร และค่าก่อน/หลังของช่องที่แก้ (ตัดความยาวแล้ว) ถูกส่งไปที่บริการ DeepSeek เมื่อมีคีย์ และการสแกนซ้ำจะส่งบันทึกชุดเดิมซ้ำเพราะไม่มีการกันส่งซ้ำ (ตัวแปร seenDigestHashes ถูกประกาศไว้แต่ไม่ได้ใช้) การทำเครื่องหมาย analyzedByDeepSeek ทำบนข้อมูลในหน่วยความจำ ไม่ได้สั่งอัปเดตใน MongoDB
5. การจำลองเหตุทุจริต
simulate_fraud_scenario สร้างเหตุการณ์ 4 ขั้นต่อหนึ่งแผนก (เปิดหน้าจอ, เลือก 3 รายการ, แก้ไข 2 ช่อง, ขั้นสุดท้าย) ด้วยตัวละครและเอกสารสมมติที่กำหนดไว้ในโค้ดคนละชุดต่อแผนก ขั้นสุดท้ายของแผนก POS, ขายส่ง, คลัง และบุคคลคือปิดหนีโดยไม่บันทึก แผนกจัดซื้อคือยกเลิกหลังแก้ แผนกการเงินคือลบเอกสาร และแผนกขนส่งคือบันทึก เขียนผ่านเส้นทางเดียวกับของจริง (insertActivityLogsToMongo) แล้วเรียก DeepSeek แบบ SIMULATION ถ้าไม่ระบุแผนกใช้ POS บันทึกจำลองใช้ traceId ขึ้นต้น trace-sim-<รหัสแผนก>- เป็นเครื่องหมายเดียว
ตามสูตรในหัวข้อ 1 สถานการณ์แผนกขนส่งได้คะแนน 26 ต่อบันทึก (เฉพาะกลุ่มน้ำมัน/ซ่อม) จึงไม่ก่อแจ้งเตือนจากกฎเอง ต้องพึ่ง DeepSeek ที่มีคีย์ และเมื่อไม่มีผลจาก AI ค่าที่คืนไปให้จอ (warningsCreated) จะเป็นแจ้งเตือนล่าสุดที่มีอยู่ซึ่งอาจไม่เกี่ยวกับการจำลองนั้น (อนุมานจากโค้ด ยังไม่ได้ทดลองรัน)
6. เปลี่ยนสถานะแจ้งเตือน
update_warning_status แก้สถานะทั้งในสำเนา RAM และใน MongoDB ($set ตามรหัสแจ้งเตือน) แล้วคืนภาพรวมใหม่ route ไม่ตรวจว่าค่าสถานะอยู่ในรายการที่อนุญาต และตอบ success แม้ MongoDB ไม่ตอบ (ฟิลด์ updated จะเป็นเท็จ แต่จอไม่ได้ใช้)
ที่มาในโค้ด
frontend/workmonitor/mongoAuditStore.ts:349-531ตัวให้คะแนน;:1313-1386สร้างแจ้งเตือนทันที;:1424-1451หาคีย์;:1453-1490ข้อความย่อ;:1507-1536ชั้นที่ 1;:1537-1720เรียกโมเดลและแปลงผล;:1722-1768บันทึกรอบตรวจ;:1774-2052จำลอง;:2130-2150เปลี่ยนสถานะ;:2308-2312ค่าเฉลี่ยประหยัดfrontend/workmonitor/fraudWarningData.ts:3-32ประเภทการกระทำและผลลัพธ์;:172-193โซนแผนก;:322-409จัดแผนกfrontend/src/app/api/fraudwarning/route.ts:108-121เงื่อนไขเรียก AI อัตโนมัติ;:136-185สแกน จำลอง เปลี่ยนสถานะfrontend/workmonitor/WorkstationTelemetryAgent.tsx:982-983นับแถวที่เลือก;:1008-1047ทิ้งจอ/เปิดส่อง;:1421-1475ปุ่มบันทึก ยกเลิก แก้ไข ลบ;:1492-1525คีย์ลัดfrontend/workmonitor/EmployeeFraudWarning3DScreen.tsx:205-232ปุ่มสั่งสแกนและข้อความสถานะ;:731-733ป้ายประหยัด Token;:1463-1486คำอธิบาย 3 ชั้น- ดูเพิ่ม: 3D Warning ตรวจจับความผิดปกติและทุจริต ภาพรวม (SYS-15 FraudWarning) · ตัวแทนบนเครื่องพนักงาน สัญญาณ WebRTC และจอผู้ดู (WorkMonitor telemetry) · API ตรวจสุขภาพ แผนที่ติดตาม และเส้นทางเฉพาะชั้น Next (Health, Monitormap, Next-only routes) · เช็กลิสต์ความปลอดภัย API: ตัวตน สิทธิ์ และช่องโหว่เว็บ (Secure API Checklist)