ใส่ด่านตัดสินใจใน LangChain agent ด้วย Jev พร้อมจุดที่ tutorial พลาด
ใส่ด่านตัดสินใจใน LangChain agent ด้วย Jev พร้อมจุดที่ tutorial พลาด โดย Nokka (นก-กา) | 19 กันยายน 2026 บทความนี้เขียนโดย AI (โมเดล deepseek-v4.1-flash ของผู้ให้บริการ ollama-cloud) ผ่าน Hermes Agent จาก Nous Researc
ใส่ด่านตัดสินใจใน LangChain agent ด้วย Jev พร้อมจุดที่ tutorial พลาด
โดย Nokka (นก-กา) | 19 กันยายน 2026
บทความนี้เขียนโดย AI (โมเดล deepseek-v4.1-flash ของผู้ให้บริการ ollama-cloud) ผ่าน Hermes Agent จาก Nous Research ตรวจสอบและเรียบเรียงโดย Nokka ข้อความในเครื่องหมายคำพูดที่เป็นคำแปลเป็นคำแปลของผม ไม่ใช่สำเนาต้นฉบับ
LangChain agent ทำงานเป็นวงจรตายตัว คือ LLM คิด เลือก tool เรียก tool ดูผลลัพธ์ แล้ววนกลับไปคิดใหม่ จนได้คำตอบ [1]
⇒ ปัญหาของวงจรนี้คือมันปฏิบัติกับทุกคำขอเหมือนกันหมด คำถามง่าย ๆ อย่าง "นโยบายคืนสินค้าคืออะไร" ก็วิ่งเข้าโมเดลตัวแพงเท่ากับงานหลายขั้นตอนที่ซับซ้อน [1]
และที่อันตรายกว่าคือ tool อย่าง delete_user_account ทำงานด้วยความลื่นไหลพอ ๆ กับ search_faq การเรียกฟังก์ชันที่โมเดลคิดผิดเพียงครั้งเดียวจึงสร้างผลกระทบที่ย้อนกลับไม่ได้ โดยไม่มีจุดตรวจในโค้ดเลย [1]
บทความนี้จะพาไปดูวิธีวาง ชั้นตัดสินใจ ลงในวงจรนี้ด้วย Jev จาก TypeSafe และชี้จุดที่ tutorial ต้นทางเขียนผิด ซึ่งผมตรวจพบตอนลองติดตั้งจริง
ต้นทางที่ผมอ่าน และเหตุผลที่บทความนี้ต่างจากต้นทาง
SitePoint เผยแพร่ tutorial เรื่อง "Build a Safer AI Agent Harness with Jev and LangChain" เมื่อ 18 กันยายน 2026 [1]
ตัวบทความสอนสร้าง harness ที่ใช้ Jev สองจุด เป็นด่านตัดสินใจในวงจร LangChain [1]
⇒ ผมอ่านแล้วเห็นว่าแนวคิดถูกและมีประโยชน์ แต่ตอนตรวจโค้ด ผมพบว่าโค้ดในบทความเรียกใช้ API ที่ไม่มีอยู่จริงในแพ็กเกจ บทความนี้จึงยึดโครงแนวคิดจาก SitePoint และแก้โค้ดให้ตรงกับ API จริง
ภาพที่ 1: ตำแหน่งที่ Jev เข้าไปวางในวงจรของ agent
แนวคิด: สองด่านตัดสินใจ
SitePoint เสนอให้วาง Jev สองจุดในวงจร [1]
ด่านที่หนึ่ง · Request Router จำแนกว่าคำขอซับซ้อนแค่ไหน แล้วเลือกชั้นของโมเดลให้เหมาะ คำถาม FAQ ธรรมดาวิ่งเข้าโมเดลเล็ก ส่วนงานหลายขั้นตอนวิ่งเข้าโมเดลใหญ่ [1]
ด่านที่สอง · Tool Gate ประเมิน tool ที่ LLM เสนอ ก่อนที่จะลงมือเรียกจริง tool ความเสี่ยงต่ำผ่านได้เลย ความเสี่ยงปานกลางผ่านได้แต่ต้องบันทึก Log ไว้ ความเสี่ยงสูงถูกปฏิเสธและส่งต่อให้คน [1]
และ SitePoint ย้ำจุดสำคัญว่าทั้งสองด่าน แทนที่ if/else แบบกระจัดกระจายด้วยการตัดสินใจที่มี type ตรวจสอบได้ ทำให้บันทึกทดสอบ และรีวิวแยกจากเหตุผลของ agent ได้ [1]
จุดที่ต้องแก้: from jev import Decision ไม่มีอยู่จริง
SitePoint เขียนไว้ในหัวข้อ Prerequisites และในโค้ดตัวอย่างว่า [1]
from jev import Decision
⇒ ผมติดตั้งแพ็กเกจแล้วเปิดซอร์สโค้ดดูโดยตรง ไม่มีคลาสชื่อ Decision อยู่ในโมดูลเลย [2]
รายการที่โมดูลส่งออกจริงคือ fn, JevFn, AsyncJevFn, BaseModel, decide, adecide, state_payload, builder และผลการค้นหาคำว่า Decision ทั้งไฟล์ได้ผลลัพธ์เป็นศูนย์ [2]
SitePoint เองก็เขียนกำกับไว้สั้น ๆ ในหัวข้อ Prerequisites ว่า "If the package is unavailable or its API differs from what is shown here" แล้วเสนอให้เปลี่ยนไปใช้ instructor หรือ marvin ถือว่าเขาระวังไว้แล้ว แต่โค้ดที่ให้มาก็ยังเรียก API ที่ไม่มี [1]
ภาพที่ 2: การแปลงชนิด field ที่ดึงจากซอร์สโค้ดของแพ็กเกจ
API จริงของแพ็กเกจ jev
แพ็กเกจ jev บน PyPI อธิบายตัวเองว่าเป็น "Decorator that compiles Python function definitions into Jev (TypeSafe System One) queries" และพึ่งพา typesafe-sdk 0.6.0 ขึ้นไป อยู่เบื้องหลัง [2][3]
⇒ หัวใจของมันคือ docstring ของฟังก์ชันคือ Jinja2 template ตอนเรียก มันจะ render docstring ด้วยค่าที่ส่งเข้ามา แล้วส่งเป็น state ให้ Jev [2]
และ return annotation ต้องเป็นคลาสลูกของ pydantic.BaseModel โดยแต่ละ field กลายเป็นคำถามหนึ่งข้อ และ type ของ field เป็นตัวกำหนดชนิดคำถามอัตโนมัติ [2]
| ชนิด field ใน Python | กลายเป็นคำถามแบบ | ได้ค่ากลับมาเป็น |
|---|---|---|
bool |
Noul | probability_yes >= 0.5 |
Literal[...] หรือ Enum
|
Choice | ป้ายที่ถูกเลือก |
int ที่มี Field(ge, le)
|
Score | ค่าในช่วงที่กำหนด |
float ที่มี Field(ge, le)
|
Score | ค่าที่ประมาณจากระดับ |
และ description ของ field กลายเป็นคำสั่งของคำถามนั้น [2]
ทางเลือกที่ไม่ต้องใช้ decorator คือเรียก decide(state, Model) ตรง ๆ สำหรับกรณีที่ต้องการ input ก้อนเดียวออกมาเป็น struct เดียว [2]
ส่วนรายละเอียดของ primitive ทั้งสามแบบ มีอธิบายครบในเอกสารทางการของ TypeSafe [4]
from typing import Literal
from jev import decide, BaseModel
class TicketReview(BaseModel):
category: Literal["billing", "technical", "sales"]
urgent: bool
review = decide(ticket_text, TicketReview)
⇒ หรือจะสืบทอดจาก jev.BaseModel เพื่อได้เมธอด .decide(state) ติดมา และคำถามจะถูก compile ครั้งเดียวตอนประกาศคลาส [2]
เอาไปวางในวงจรจริง
เมื่อเข้าใจ API จริงแล้ว โครงของ harness จะเขียนแบบนี้ ตรงตามแนวคิดของ SitePoint แต่ใช้ชื่อที่ถูก
from jev import decide, BaseModel
from typing import Literal
class RouteDecision(BaseModel):
"""ตัดสินว่าคำขอนี้ควรใช้โมเดลชั้นไหน"""
tier: Literal["small", "large"]
class GateDecision(BaseModel):
"""ตัดสินว่า tool ที่เสนอควรถูกเรียกหรือไม่"""
risk: Literal["low", "medium", "high"]
def route(user_query: str):
return decide(user_query, RouteDecision).tier
def gate(tool_name: str, args: dict):
state = f"tool: {tool_name}\nargs: {args}"
return decide(state, GateDecision).risk
จากนั้นในวงจร LangChain เอา route() ไปเลือกโมเดลตอนต้น และเอา gate() ไปคั่นก่อนเรียก tool ทุกครั้ง โค้ดส่วนที่ตัดสินใจจะกลายเป็นฟังก์ชันที่ทดสอบแยกได้ ไม่ปนกับ prompt
จุดที่ SitePoint เตือนเอง และผมเห็นด้วย
SitePoint เขียนหมายเหตุเรื่องต้นทุนไว้ตรง ๆ ว่า ทั้ง router และ gate เพิ่มการเรียก LLM หนึ่งครั้งต่อหนึ่งคำขอ การประหยัดจะเกิดขึ้นก็ต่อเมื่อส่วนที่ประหยัดจากคำขอที่ถูกเบี่ยงไปโมเดลเล็ก มากกว่าต้นทุนที่เพิ่มมา [1]
ผู้เขียนแนะนำให้วัดต้นทุนการ route ต่อคำขอ เทียบกับส่วนต่างราคาโมเดลต่อคำขอ เพื่อหาจุดคุ้มทุนของตัวเอง และระบุว่าถ้าปริมาณน้อย ค่า overhead อาจแพงกว่าที่ประหยัดได้ [1]
⇒ ผมคิดว่านี่คือคำเตือนที่ตรงที่สุดในบทความต้นทาง และหลายคู่มือไม่พูด เพราะมันบอกว่าเครื่องมือนี้ไม่ใช่คำตอบสำเร็จรูป ต้องคำนวณก่อนใช้
ค่าใช้จ่ายจริงที่ควรรู้
SitePoint ยกตัวอย่างว่า gpt-4o-mini ถูกกว่า gpt-4o ประมาณ 30 เท่าต่อโทเคน input และแนะนำให้เช็คราคาปัจจุบันจากหน้า pricing ของ OpenAI เอง เพราะราคาเปลี่ยนได้ [1]
ผมคิดว่าจุดที่ควรสังเกตคือ ตัวเลขนี้เป็นของโมเดลรุ่นก่อน การเทียบชั้นโมเดลในปี 2026 ต้องดูราคาปัจจุบันของแต่ละเจ้า ไม่ใช่ยึดตัวอย่างจากบทความ
ภาพที่ 3: จุดที่ tutorial ต้นทางเขียนผิด
สิ่งที่ผมเห็นจากเรื่องนี้
ถ้าอ่านแค่แนวคิด บทความของ SitePoint ก็ให้ประโยชน์ครบ แต่สิ่งที่ทำให้ผมต้องเขียนชิ้นนี้คือ โค้ดใน tutorial เรียก API ที่ไม่มีอยู่ [1][2]
⇒ ปัญหานี้ไม่ได้เกิดกับ SitePoint ที่เดียว แต่เกิดกับ tutorial สาย AI ทั้งวง เพราะไลบรารีเปลี่ยน API เร็วกว่าที่บทความจะตามทัน และ AI ที่ช่วยเขียนบทความก็มีแนวโน้มสร้างโค้ดที่ "ดูน่าเชื่อ" มากกว่าโค้ดที่รันได้ [2]
วิธีป้องกันที่ได้ผลที่สุดคือ เปิดซอร์สโค้ดของแพ็กเกจดู __all__ ก่อนเชื่อ tutorial ซึ่งใช้เวลาไม่ถึงหนึ่งนาที และประหยัดเวลาไล่บั๊กได้หลายชั่วโมง [2]
ส่วนแนวคิดของ SitePoint ยังใช้ได้ตามเดิม เพราะการวางชั้นตัดสินใจที่มี type ตรวจสอบได้ ระหว่าง LLM กับโค้ด เป็นรูปแบบที่แก้ปัญหาจริง ทั้งเรื่องต้นทุนและเรื่องความปลอดภัย [1]
ถ้าคุณจะลองทำตาม ลำดับที่ผมแนะนำคือ ติดตั้งแพ็กเกจด้วย pip install jev ก่อน แล้วรันคำสั่งสั้น ๆ เพื่อตรวจว่ารายชื่อที่ส่งออกตรงกับที่คุณจะเรียก ทำแบบนี้กับทุก tutorial ที่ให้โค้ดกับไลบรารีที่คุณยังไม่คุ้น [2]
จากนั้นค่อยเริ่มจากด่านเดียว คือ Tool Gate ก่อน เพราะด่านนี้ให้ผลทางความปลอดภัยที่วัดได้ทันที ส่วน Request Router ค่อยเพิ่มเมื่อคุณมีข้อมูลจริงว่าคำขอแบบไหนควรไปโมเดลไหน [1]
แหล่งอ้างอิง
[1] SitePoint — "Build a Safer AI Agent Harness with Jev and LangChain", 18 กันยายน 2026 — https://www.sitepoint.com/build-safer-ai-agent-harness-jev-langchain/
[2] PyPI — แพ็กเกจ jev 0.3.0 (ซอร์สโค้ด jev.py), กันยายน 2026 — https://pypi.org/project/jev/
[3] PyPI — แพ็กเกจ typesafe-sdk (Python SDK สำหรับ TypeSafe AI), กันยายน 2026 — https://pypi.org/project/typesafe-sdk/
[4] TypeSafe AI — เอกสารทางการ (primitives, patterns, confidence), กันยายน 2026 — https://docs.typesafe.ai/
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.

