ใส่ด่านตัดสินใจใน LangChain agent ด้วย Jev พร้อมจุดที่ tutorial พลาด
ปัญหาของ LangChain Agent ปัจจุบัน
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 จริง
แนวคิด: สองด่านตัดสินใจ
SitePoint เสนอให้วาง Jev สองจุดในวงจร [1]
ด่านที่หนึ่ง · Request Router - จำแนกว่าคำขอซับซ้อนแค่ไหน แล้วเลือกชั้นของโมเดลให้เหมาะ คำถาม FAQ ธรรมดาวิ่งเข้าโมเดลเล็ก ส่วนงานหลายขั้นตอนวิ่งเข้าโมเดลใหญ่ [1]
ด่านที่สอง · Tool Gate - ประเมิน tool ที่ LLM เสนอ ก่อนที่จะลงมือเรียกจริง [1]
- ความเสี่ยงต่ำ: ผ่านได้เลย
- ความเสี่ยงปานกลาง: ผ่านได้แต่ต้องบันทึก Log ไว้
- ความเสี่ยงสูง: ถูกปฏิเสธและส่งต่อให้คน
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]
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:
| ชนิด 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 ต้องดูราคาปัจจุบันของแต่ละเจ้า ไม่ใช่ยึดตัวอย่างจากบทความ
จุดที่ tutorial ต้นทางเขียนผิด
สิ่งที่ทำให้ต้องเขียนบทความนี้คือ โค้ดใน 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]
แหล่งอ้างอิง
- SitePoint - "Build a Safer AI Agent Harness with Jev and LangChain", 18 กันยายน 2026 - https://www.sitepoint.com/build-safer-ai-agent-harness-jev-langchain/
- PyPI - แพ็กเกจ jev 0.3.0 (ซอร์สโค้ด jev.py), กันยายน 2026 - https://pypi.org/project/jev/
- PyPI - แพ็กเกจ typesafe-sdk (Python SDK สำหรับ TypeSafe AI), กันยายน 2026 - https://pypi.org/project/typesafe-sdk/
- TypeSafe AI - เอกสารทางการ (primitives, patterns, confidence), กันยายน 2026 - https://docs.typesafe.ai/
Comments
No comments yet. Start the discussion.