คำตอบสั้น
ฉันจะสร้าง deep research agent ใน Python ได้อย่างไร
เขียน loop ที่ถาม LLM ให้สร้างคำค้นหา รันแต่ละคำผ่าน web search API ที่คืนเนื้อหาหน้าเว็บ เก็บแต่ละแหล่งไว้ครั้งเดียว ถามคำค้นหาติดตามผล แล้วเขียนคำตอบที่มีการอ้างอิง ด้วย LLMWebSearch การค้นหาแต่ละครั้งจะคืน 3 หน้าแรกเป็น Markdown ในราคาหนึ่งเครดิต สคริปต์ด้านล่างรันหกการค้นหาต่อหนึ่งคำถาม เริ่มต้นที่ US$1.20/1K การค้นหา ไม่ต้อง KYC จ่ายด้วยคริปโต
Agent จะทำอะไร
แพ็กเกจ
ถามโมเดลให้สร้างคำค้นหาสั้น ๆ สี่คำที่ครอบคลุมคำถามด้วยกัน
ค้นหา
รันแต่ละคำค้นหาด้วย
read=3เพื่อให้ได้สามหน้าแรกกลับมาเป็น Markdownเก็บแต่ละแหล่งไว้ครั้งเดียว
เก็บผลลัพธ์ตาม URL และข้ามหน้าที่เก็บไปแล้วหรือไม่มีข้อความ
มองหาช่องว่าง
แสดงชื่อเรื่องที่พบ到目前为止ให้โมเดลดู แล้วถามคำค้นหาใหม่สองคำเกี่ยวกับสิ่งที่ยังขาด
เขียน
ใส่หมายเลขแหล่งข้อมูลสูงสุด 12 แหล่ง แล้วขอคำตอบที่อ้างอิงเป็น [1], [2]
ต้องเตรียมอะไรก่อนเริ่ม
สามอย่าง:
- API key ของ LLMWebSearch ใน environment variable
LLMWEBSEARCH_API_KEYkey จะปรากฏในแดชบอร์ดหลังซื้อแพ็กแรก - chat endpoint ที่รองรับ OpenAI ตัวใดก็ได้ ตั้งค่าด้วย
LLM_BASE_URL,LLM_API_KEYและLLM_MODELทั้งโมเดลแบบ hosted และเซิร์ฟเวอร์ในเครื่องใช้ได้ - Python 3.9 หรือใหม่กว่า พร้อมแพ็กเกจ
requests
สคริปต์ใช้ HTTP ธรรมดาสำหรับทั้งสองบริการ คุณจึงสลับโมเดลได้โดยไม่ต้องแก้โค้ดค้นหา
import os
import requests
SEARCH = "https://llmwebsearch.com/api/v1/search"
SEARCH_HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
# Any OpenAI-compatible chat endpoint works here: set your own base URL, key and model name.
LLM_URL = os.environ.get("LLM_BASE_URL", "https://llm.example.com/v1") + "/chat/completions"
LLM_HEADERS = {"Authorization": "Bearer " + os.environ.get("LLM_API_KEY", "")}
MODEL = os.environ.get("LLM_MODEL", "your-model-name")
def ask_llm(prompt):
body = {"model": MODEL, "messages": [{"role": "user", "content": prompt}]}
resp = requests.post(LLM_URL, headers=LLM_HEADERS, json=body, timeout=120)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
def plan_queries(question, n=4):
text = ask_llm(f"Write {n} short web search queries, one per line, to answer: {question}")
queries = [line.strip("-*0123456789. ").strip() for line in text.splitlines()]
return [q for q in queries if q][:n]
def search(query):
body = {"q": query, "max_results": 5, "read": 3, "max_chars": 5000}
resp = requests.post(SEARCH, headers=SEARCH_HEADERS, json=body, timeout=60)
resp.raise_for_status()
return resp.json()["results"]
def research(question, rounds=2):
sources = {} # url -> result, so the same page is never cited twice
queries = plan_queries(question)
for _ in range(rounds):
for query in queries:
for r in search(query):
if r.get("content_status") == "ok" and r["url"] not in sources:
sources[r["url"]] = r
notes = "\n".join(f"- {r['title']}" for r in sources.values())
queries = plan_queries(f"{question}\nAlready found:\n{notes}\nWhat is still missing?", n=2)
numbered = list(sources.values())[:12]
context = "\n\n".join(
f"[{i}] {r['title']} ({r['url']})\n{r['content']}" for i, r in enumerate(numbered, 1)
)
prompt = (
"Answer the question using only the sources below. Cite them as [1], [2]. "
"Say so when the sources disagree or do not cover a point.\n\n"
f"Question: {question}\n\nSources:\n{context}"
)
return ask_llm(prompt), [r["url"] for r in numbered]
if __name__ == "__main__":
answer, urls = research("What changed in the EU rules for general-purpose AI models in 2025?")
print(answer)
for i, url in enumerate(urls, 1):
print(f"[{i}] {url}")แต่ละส่วนทำงานอย่างไร
plan_queries ขอคำค้นหาสั้น ๆ จากโมเดล บรรทัดละคำ และตัดเครื่องหมาย list ออก คำค้นหาสั้น ๆ เข้ากับวิธีที่ search engine จัดอันดับหน้าเว็บได้ดีกว่าคำถามเต็มประโยค
search เรียก POST https://llmwebsearch.com/api/v1/search ด้วย max_results=5, read=3 และ max_chars=5000 ผลลัพธ์มีห้ารายการ โดยสามรายการแรกมีเนื้อหาหน้าเว็บอยู่ใน content
research รันเป็นรอบ ๆ เก็บแหล่งข้อมูลใน dictionary ที่ใช้ URL เป็น key หน้าที่พบจากสองคำค้นหาจึงนับครั้งเดียว เก็บเฉพาะผลลัพธ์ที่ content_status เท่ากับ ok เพราะคำตอบต้องอ้างอิงจากข้อความที่โมเดลอ่านได้
หลังแต่ละรอบ โมเดลจะเห็นชื่อเรื่องที่เก็บได้到目前为止และเสนอคำค้นหาใหม่สองคำสำหรับสิ่งที่ยังขาด prompt สุดท้ายใส่หมายเลขแหล่งข้อมูลสูงสุดสิบสองแหล่งและกำหนดกติกาการอ้างอิง
หนึ่งรายงานมีค่าใช้จ่ายเท่าไร
หกการค้นหาต่อคำถามด้วยการตั้งค่าเริ่มต้น: สี่ครั้งในรอบแรกและสองครั้งในรอบที่สอง เมื่อ read อยู่ที่ 3 นั่นคือหกเครดิต ค้นหา 25,000 ครั้ง ที่ US$41.25 จึงครอบคลุมมากกว่า 4,000 รายงาน
การเรียกโมเดลมีค่าใช้จ่ายเพิ่มบนบิล LLM ของคุณ: การเรียกวางแผนสามครั้งและการเรียกคำตอบยาวหนึ่งครั้งต่อรายงาน การเรียกคำตอบมีเนื้อหาสูงสุดสิบสองหน้าครั้งละ 5,000 ตัวอักษร ดังนั้นลด max_chars ลงถ้าโมเดลของคุณมีราคาแพง
ทำอย่างไรให้ค้นได้ลึกขึ้น?
เปลี่ยนตัวเลข ไม่ใช่โครงสร้าง:
- เพิ่มรอบ
rounds=4ให้โมเดลตามช่องว่างเพิ่มอีกสองชุด - เพิ่มคำค้นหาต่อรอบ ปรับ
nในคำสั่งหาช่องว่างจาก 2 เป็น 4 - จำกัดแหล่งข้อมูลให้แคบลง เพิ่ม
include_domainsใน body ของการค้นหาเพื่อเจาะเว็บไซต์ทางการหรือวิชาการ - เอาเฉพาะเนื้อหาใหม่ เพิ่ม
"time_range": "month"หรือ"topic": "news" - เพิ่มหน้าเว็บต่อคำค้นหา
read=5อ่านห้าหน้า ที่ 2 เครดิตต่อการค้นหา
เพิ่มกฎหยุดก่อนจะเพิ่มขีดจำกัด: จบลูปเมื่อรอบหนึ่งได้แหล่งข้อมูลใหม่น้อยกว่าสองแหล่ง
ทำอย่างไรให้เอเจนต์ปลอดภัยและสุภาพ?
ถือว่าทุกหน้าเว็บเป็นข้อความที่ไม่น่าไว้วางใจ พรอมป์ต์สุดท้ายบอกโมเดลให้ใช้แหล่งข้อมูลเป็นหลักฐานเท่านั้น; เพิ่มบรรทัดชัดเจนว่าต้องเพิกเฉยต่อคำสั่งที่อยู่ในแหล่งข้อมูล ห้ามให้แหล่งข้อมูลทริกเกอร์ tool call หรือการเขียนไฟล์
ฝั่งเว็บ ตัวอ่านของเราเคารพ robots.txt: แนะนำตัวว่าเป็น LLMWebSearch ทำตามกฎที่เว็บไซต์เขียนไว้สำหรับมันหรือสำหรับ robots ทั้งหมด และละเว้นเนื้อหาของหน้าใดก็ตามที่มันไม่ได้รับอนุญาตให้อ่าน หน้าที่ปฏิเสธการเข้าถึงจะส่งกลับมาโดยไม่มีเนื้อหา และสคริปต์ก็ข้ามไปเลย เมื่อเว็บไซต์ปิดกั้นตัวอ่านของเรา ผลลัพธ์จะกลับมาโดยไม่มีเนื้อหาและมีสถานะของเว็บไซต์: เราไม่แก้ CAPTCHA ไม่ลงชื่อเข้าใช้บัญชี และไม่หลบเลี่ยงการบล็อกหรือ paywall
มีอะไรที่อาจผิดพลาดได้?
มีสามอย่างที่เจอตอนทดสอบ:
- คำค้นหากำกวม ถ้าแผนอ่อน ทุกรอบจะเจอหน้าเดิม ขอให้โมเดลสร้างคำค้นหาที่ใช้ชื่อและวันที่เฉพาะเจาะจง
- แหล่งข้อมูลที่มีข้อความน้อยเกินไป บางหัวข้ออยู่หลัง paywall ขยายคำค้นหาหรืออนุญาตผลลัพธ์มากขึ้น
- พรอมป์ต์ยาวเกินไป หน้าที่ยาวสิบสองหน้าอาจเกิน context ของโมเดลเล็ก ลด
max_charsลงหรือเก็บไว้แปดแหล่ง
ล็อกทุกคำค้นหาและ URL ที่ได้มา การอ่านล็อกนั้นคือวิธีที่เร็วที่สุดในการปรับพรอมป์ต์วางแผน
เอเจนต์วิจัยเชิงลึก: คำถามที่พบบ่อย
รันด้วยโมเดลโลคอลได้ไหม?
ได้ ถ้าเซิร์ฟเวอร์โลคอลรองรับรูปแบบ OpenAI chat-completions ตั้ง LLM_BASE_URL เป็นที่อยู่ของมัน และ LLM_MODEL เป็นชื่อโมเดล
แปลงเป็น MCP tool ได้ไหม?
ได้ ห่อ research() ใน MCP tool หรือให้เอเจนต์ของคุณใช้ search tool ธรรมดาจาก หน้า MCP แล้วปล่อยให้มันรันลูปเอง
ทำไมไม่ถาม search API ให้ตอบคำถามโดยตรงเลย?
เพราะ LLMWebSearch ไม่ได้เขียนคำตอบ API ส่งคืนแหล่งข้อมูล ไม่ใช่คำตอบที่เขียนไว้: โมเดลของคุณเองอ่านหน้าเว็บ เขียนคำตอบ และอ้างอิง URL
ดูได้อย่างไรว่าแต่ละการค้นหาใช้ไปเท่าไร?
ทุกการตอบกลับมี credits_used และ GET https://llmwebsearch.com/api/v1/credits แสดงยอดคงเหลือที่เหลืออยู่โดยไม่มีค่าใช้จ่าย