Tool: web_search — ค้นเว็บพร้อมกรองความเกี่ยวข้องหลายชั้นก่อนให้โมเดลเห็น

ภาพประกอบ: Tool: web_search — ค้นเว็บพร้อมกรองความเกี่ยวข้องหลายชั้นก่อนให้โมเดลเห็น

บทความ Tool: write_file พูดถึงการเขียนไฟล์ในเครื่อง บทความนี้ข้ามไปที่ tool เดียวใน 6 ตัวที่ออกไปนอกเครื่องweb_search

Tool นี้คืออะไร

ค้นอินเทอร์เน็ตหาข้อมูลที่ไม่มีในเครื่อง (ข่าว, ราคาปัจจุบัน, ข้อเท็จจริงที่เปลี่ยนแปลงได้) คืนผลเป็นสรุปสั้นๆที่ทุกบรรทัดต้องแนบ source URL กำกับ — นี่เป็น hard requirement ที่ระบุไว้ตรงๆ ในคอมเมนต์ต้นไฟล์ว่าเป็นคำขอของผู้ใช้จริง: “จุดประสงค์ของ tool นี้คือ สรุป+อ้างอิง เสมอ ไม่ใช่แค่สรุปเฉยๆ”

จุดที่ต่างจาก tool ค้นเว็บทั่วไป: tool นี้ไม่ไปโหลดเนื้อหาเต็มของหน้าเว็บผลลัพธ์เลย ใช้แค่ title/snippet ที่ search engine ให้มา เหตุผลด้านความปลอดภัยตรงๆ: ผลค้นหาเป็นข้อมูลที่ไม่น่าเชื่อถือ (untrusted) การให้ agent ไปเปิด URL ต่อเองจะเปิดช่องให้เป็น SSRF (Server-Side Request Forgery) surface ได้

Agent เรียก tool นี้เมื่อไร

Docstring เน้นย้ำว่าอะไรก็ตามที่เปลี่ยนแปลงตามเวลาต้องเรียก tool นี้เสมอ แม้จะรู้สึกเหมือนตอบได้จากความรู้เดิม:

❌ "วันนี้อากาศเป็นไง" -> ห้ามตอบจากความรู้ทั่วไปเรื่องภูมิอากาศ
✅ ต้องเรียก web_search(query="...") ก่อนเสมอสำหรับคำถามแบบนี้

และห้ามใช้แทน rag_search — คำขอหาไฟล์ในเครื่องไม่ใช่งานของ tool นี้แม้ index ในเครื่องยังไม่มีก็ตาม

Layer การกรอง 1: ตัดคำฟุ่มเฟือยและปรับ query ให้ตรงเจตนา

ก่อนยิงค้นจริง มีการสร้าง query variant หลายแบบ เช่น _simplified_query_variant (ตัดคำอย่าง “ช่วยค้นหา”, “พร้อมแหล่งที่มา” ออก เพราะเป็น instruction ไม่ใช่คำค้นจริง) และ _feature_query_variant (แปลงคำถามธรรมชาติอย่าง “มีฟีเจอร์ใหม่อะไรบ้าง” ให้เป็นรูปแบบที่ search engine ค้นได้ดีกว่า เช่น เติม “what’s new” หรือสร้าง site:docs.python.org ตรงสำหรับคำถามเรื่องเวอร์ชัน Python)

Layer การกรอง 2: ตรวจ relevance ด้วย character trigram

ผลลัพธ์ที่ได้กลับมาไม่ได้เชื่อ ranking ของ search engine ตรงๆ — มีการคำนวณ relevance score เทียบ query กับ title/snippet ของแต่ละผลลัพธ์ด้วยวิธี character trigram overlap (ตัด query กับ text เป็นชุดตัวอักษร 3 ตัวติดกัน แล้วเทียบว่าซ้อนทับกันแค่ไหน) — เลือกวิธีนี้เพราะทำงานได้กับภาษาไทย (ไม่มี word boundary แบบภาษาอังกฤษ), อังกฤษ, ตัวเลข และข้อความผสม โดยไม่ต้องโหลด embedding model เพิ่ม (คงหลักการประหยัด RAM แบบเดียวกับ rag_search)

มีเงื่อนไข hard-reject หลายแบบที่ทำให้คะแนนเป็น 0 ทันที ไม่ว่า trigram จะซ้อนทับกันแค่ไหนก็ตาม:

  • ปีไม่ตรง — ค้นข้อมูลปี 2569 แต่ผลลัพธ์ระบุปี 2567 ชัดเจน → ตัดทิ้ง (แปลงปี พ.ศ./ค.ศ. ให้เทียบกันได้ก่อน)
  • เวอร์ชันไม่ตรง — ค้น Python 3.12 แต่ผลลัพธ์พูดถึง 3.9 → ตัดทิ้ง
  • คำถามเรื่อง deadline แต่ผลลัพธ์ไม่มีหลักฐานเรื่องกำหนดเวลาเลย → ตัดทิ้ง
  • คำถามเรื่องราคาแต่ title ไม่มีคำว่าราคา/price เลย → ตัดทิ้ง
  • คำถามขอข้อมูล “ล่าสุด/วันนี้” แต่ title เป็นบทความคาดการณ์อนาคต (forecast) → ตัดทิ้ง เพราะเป็นคนละประเภทคำตอบ

Layer การกรอง 3: ให้โมเดิลตัวเดียวกันช่วยตัดสิน (batch semantic filter)

หลังกรองด้วย trigram แล้ว ยังมีชั้นสุดท้ายที่ใช้โมเดล LLM ตัวเดียวกันที่คุยกับผู้ใช้อยู่แล้ว (ไม่โหลดโมเดลเพิ่ม) มาช่วยตัดสินแบบ batch — ส่ง title/snippet ของผลลัพธ์ที่เหลือทั้งหมดในรอบเดียว (ไม่ใส่ URL เข้าไปด้วย เพราะให้โมเดลตัดสินจากเนื้อหาเท่านั้น การอ้างอิง URL เป็นหน้าที่ของโค้ดต่างหาก) ให้ตอบ YES/NO ทีละข้อว่าเนื้อหานี้ตรงกับหัวข้อ+เจตนาของ query จริงหรือไม่ ผลลัพธ์ parse ด้วย regex ที่เข้มงวด — ถ้า parse ไม่ได้หรือรูปแบบผิด ระบบ fail closed (ถือว่าทุกอย่างถูกปฏิเสธ ไม่ใช่ยอมรับทุกอย่าง) เพื่อไม่ให้ผลลัพธ์ที่ไม่ผ่านการตรวจสอบหลุดไปถึงคำตอบสุดท้าย

กรองหน้า “ไม่ใช่เนื้อหา” ทิ้งด้วย

ก่อนถึงชั้น relevance มีการกรองผลลัพธ์ที่เป็นหน้า navigation ทิ้งก่อน เช่นหน้า login ของ Google, หน้า account, หรือ YouTube channel page ทั่วไปที่ไม่มีเนื้อหาจริงให้อ้างอิง — และมีการกรองเนื้อหาสำหรับผู้ใหญ่ (adult content) ออกด้วยรายชื่อโดเมน/คำที่รู้จัก

กรองซ้ำโดเมนเดียวกัน

ผลลัพธ์สุดท้ายที่ส่งให้โมเดลอ่านจะถูก dedupe ตามโดเมน — ไม่ให้เว็บเดียวกันครองผลลัพธ์ทั้งหมด กระจายแหล่งที่มาให้หลากหลายมากกว่าจะได้คำตอบที่รอบด้านกว่า

แหล่งที่มาถูกต่อท้ายโดยโค้ด ไม่ใช่โมเดล

จุดที่ตรงกับหลักการ “โค้ด ไม่ใช่ prompt” ที่พบซ้ำในหลายจุดของ AGENT LITE: URL ปลายทางถูกแปะต่อท้ายทุกบรรทัดผลลัพธ์โดยโค้ดเป็น [source: URL] — ไม่ใช่หวังว่าโมเดลจะจำมาแนบเอง เพราะทดลองแล้วพบว่าบางครั้งโมเดลสรุปถูกแต่ลืมแนบลิงก์ reminder ที่แนบมากับผลลัพธ์ครั้งแรกในแต่ละ turn ก็บอกโมเดลตรงๆ ว่า “อย่าพิมพ์ URL เองในคำตอบ เพราะโค้ดจะแนบให้อัตโนมัติแล้ว”

ทำไมเรื่องนี้ถึงสำคัญ

web_search เป็น tool ที่รับข้อมูลจากภายนอกที่ไม่น่าเชื่อถือเข้ามาในระบบ (ผลค้นหาอาจถูกออกแบบมาให้เข้าใจผิดหรือหลอกโมเดลได้) — ทีมพัฒนาจึงลงทุนสร้างชั้นกรองถึง 3 ชั้นก่อนให้โมเดลเห็นผลลัพธ์เลย (lexical trigram → hard-reject rules → LLM batch classify) แทนที่จะเชื่อ ranking ของ search engine ตรงๆ หรือปล่อยให้โมเดลกรองเอง — ผลคือคำตอบที่อ้างอิงแหล่งจริง ตรงประเด็น และมี URL กำกับเสมอ

อ่านเพิ่มเติม

ความคิดเห็น

กำลังโหลดความคิดเห็น...