0Pricing
AI Agents · บทเรียน

การผสานการค้นหาเว็บกับ RAG

การดึงข้อมูลแบบผสม: ที่จัดเก็บเวกเตอร์ในเครื่อง + การค้นหาเว็บแบบสดเพื่อคำตอบที่เป็นปัจจุบัน

การผสานการค้นหาเว็บกับ RAG เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

ความท้าทายของการดึงข้อมูลแบบผสม

เอเจนต์ในโลกจริงส่วนใหญ่ต้องใช้ความรู้สองประเภท: ความรู้เฉพาะด้านแบบคงที่ (เอกสารของบริษัท คู่มือผลิตภัณฑ์ และนโยบาย) และ ข้อมูลปัจจุบัน (ข่าว ราคาปัจจุบัน และเหตุการณ์ล่าสุด)

ที่จัดเก็บเวกเตอร์ภายในเครื่องจัดการความรู้ประเภทแรก ส่วนการค้นหาเว็บจัดการประเภทหลัง การรวมทั้งสองแบบเข้าด้วยกันทำให้ได้ประโยชน์สูงสุดจากแต่ละด้าน

สถาปัตยกรรม: RAG ภายในเครื่อง + การค้นหาเว็บ

ระบบแบบผสมจะส่งต่อคำถามแต่ละข้อไปยังแหล่งดึงข้อมูลที่เหมาะสม:

  • ที่จัดเก็บเวกเตอร์ (RAG) — เอกสารที่ทำดัชนีแล้ว ความรู้ที่คงที่ และข้อมูลส่วนตัว
  • การค้นหาเว็บ — เหตุการณ์ปัจจุบัน การเผยแพร่ล่าสุด และข้อมูลแบบปัจจุบัน
  • ทั้งสองแหล่ง — เมื่อคำถามต้องใช้บริบทจากเอกสารและข้อมูลปัจจุบัน
class HybridRetrievalAgent:
    def __init__(self, vector_store, search_client):
        self.vector_store = vector_store  # e.g., ChromaDB or FAISS
        self.search_client = search_client  # e.g., TavilyClient

    def answer(self, question):
        route = self.classify_question(question)

        if route == 'static':
            context = self.vector_store.query(question, n_results=5)
        elif route == 'current':
            context = self.web_search(question)
        else:  # 'both'
            local = self.vector_store.query(question, n_results=3)
            web = self.web_search(question)
            context = local + web

        return self.generate_answer(question, context)

if __name__ == '__main__':
    class DemoAgent(HybridRetrievalAgent):
        def classify_question(self, question):
            return 'static' if 'company' in question.lower() else 'current'
        def web_search(self, question):
            return [('Web result about ' + question, {})]
        def generate_answer(self, question, context):
            return f'Answer using {len(context)} context item(s).'

    class FakeVectorStore:
        def query(self, question, n_results=5):
            return [('Local doc snippet', {})]

    agent = DemoAgent(FakeVectorStore(), search_client=None)
    print(agent.answer('What is our company policy on refunds?'))

ตัวจำแนกสำหรับการกำหนดเส้นทาง

ตัวจำแนกจะตัดสินใจว่าจะใช้แหล่งดึงข้อมูลใด คุณอาจสร้างเป็นการเรียกใช้ LLM ชุดกฎจากคำสำคัญ หรือตัวจำแนกขนาดเล็กที่ผ่านการฝึก เอเจนต์กำหนดเส้นทางที่ใช้ LLM มีความยืดหยุ่นมากที่สุด

ROUTING_PROMPT = '''Classify this question into one of three categories:
- "static": answered from company documents, product docs, or stable technical knowledge
- "current": requires up-to-date information (news, prices, recent events, latest releases)
- "both": needs both company context and current information

Question: {question}

Respond with exactly one word: static, current, or both.'''

def classify_question(question):
    response = llm_call(ROUTING_PROMPT.format(question=question))
    route = response.strip().lower()
    if route not in ('static', 'current', 'both'):
        return 'both'  # safe default
    return route

การตั้งค่าที่จัดเก็บเวกเตอร์ภายในเครื่อง

สำหรับฐานความรู้แบบคงที่ ให้ใช้ ChromaDB ซึ่งเป็นฐานข้อมูลเวกเตอร์ขนาดเล็กที่ทำงานอยู่ภายในกระบวนการ ทำดัชนีเอกสารของคุณเพียงครั้งเดียว แล้วจึงค้นหาเมื่อโปรแกรมทำงาน

ติดตั้งด้วย pip install chromadb openai

import chromadb
from chromadb.utils import embedding_functions
import os

client = chromadb.PersistentClient(path='./vector_db')

ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key=os.getenv('OPENAI_API_KEY'),
    model_name='text-embedding-3-small'
)

collection = client.get_or_create_collection(
    name='company_docs',
    embedding_function=ef
)

def index_document(doc_id, text, metadata=None):
    collection.add(
        ids=[doc_id],
        documents=[text],
        metadatas=[metadata or {}]
    )

def local_retrieve(question, n_results=5):
    results = collection.query(
        query_texts=[question],
        n_results=n_results
    )
    return list(zip(results['documents'][0], results['metadatas'][0]))

การดึงข้อมูลจากการค้นหาเว็บ

เส้นทางการค้นหาเว็บใช้ Tavily เพื่อดึงข้อมูลปัจจุบัน จัดรูปแบบผลลัพธ์ให้สอดคล้องกัน เพื่อให้สามารถรวมกับผลลัพธ์ RAG ภายในเครื่องในโครงสร้างคำสั่งเดียวกันได้

from tavily import TavilyClient
import os

tavily = TavilyClient(api_key=os.getenv('TAVILY_API_KEY'))

def web_retrieve(question, n_results=3):
    results = tavily.search(
        query=question,
        max_results=n_results,
        search_depth='basic'
    )
    # Normalize to same format as local results
    return [
        (
            r['content'][:600],  # text
            {'source': r['url'], 'title': r['title'], 'type': 'web'}  # metadata
        )
        for r in results.get('results', [])
    ]

การรวมผลลัพธ์ภายในเครื่องและจากเว็บ

เมื่อใช้ทั้งสองแหล่ง ให้รวมผลลัพธ์และติดป้ายกำกับแหล่งที่มาของแต่ละรายการ วิธีนี้ช่วยให้ LLM ให้น้ำหนักผลลัพธ์ได้อย่างเหมาะสม โดยใช้เอกสารภายในเครื่องสำหรับข้อเท็จจริงเฉพาะของบริษัท และใช้เว็บสำหรับข้อมูลปัจจุบัน

def merge_results(local_results, web_results):
    merged = []

    for text, meta in local_results:
        merged.append({
            'content': text,
            'source': meta.get('source', 'internal document'),
            'type': 'local',
            'title': meta.get('title', 'Company Document')
        })

    for text, meta in web_results:
        merged.append({
            'content': text,
            'source': meta.get('source', 'web'),
            'type': 'web',
            'title': meta.get('title', 'Web Result')
        })

    return merged

def format_merged_for_prompt(merged_results):
    parts = []
    for i, r in enumerate(merged_results, 1):
        tag = '[INTERNAL]' if r['type'] == 'local' else '[WEB]'
        parts.append(f'[{i}] {tag} {r["title"]}\n{r["content"]}')
    return '\n\n'.join(parts)

if __name__ == '__main__':
    local = [('Refunds are processed within 5 business days.', {'source': 'handbook', 'title': 'Refund Policy'})]
    web = [('Company X reported Q2 earnings today.', {'source': 'reuters.com', 'title': 'Q2 Earnings'})]
    merged = merge_results(local, web)
    print(format_merged_for_prompt(merged))

การตรวจจับคำถามที่ไวต่อข้อมูลปัจจุบัน

นอกเหนือจากตัวจำแนกที่ใช้ LLM แล้ว ให้ใช้หลักเกณฑ์จากคำสำคัญเพื่อตรวจจับคำถามที่ต้องใช้ข้อมูลปัจจุบัน วิธีนี้ทำงานได้เร็วกว่าและหลีกเลี่ยงการเรียกใช้ LLM เพิ่มสำหรับกรณีที่เห็นได้ชัด

CURRENT_EVENTS_SIGNALS = [
    'latest', 'current', 'today', 'now', 'recent',
    'this week', 'this month', 'this year',
    'just released', 'new version', 'updated',
    'price', 'stock', 'news', 'announcement',
    '2024', '2025'
]

STATIC_SIGNALS = [
    'how does', 'what is', 'explain', 'tutorial',
    'documentation', 'our product', 'company policy',
    'internal', 'handbook'
]

def fast_route(question):
    lower = question.lower()
    current_score = sum(1 for s in CURRENT_EVENTS_SIGNALS if s in lower)
    static_score = sum(1 for s in STATIC_SIGNALS if s in lower)

    if current_score > static_score:
        return 'current'
    elif static_score > current_score:
        return 'static'
    else:
        return 'both'

if __name__ == '__main__':
    for q in ['What is our company handbook policy on PTO?', 'What is the latest stock price today?']:
        print(f'{fast_route(q)!r} <- "{q}"')

การจัดการความขัดแย้งระหว่างแหล่งข้อมูล

ความขัดแย้งเกิดขึ้นเมื่อเอกสารภายในเครื่องระบุอย่างหนึ่ง แต่ผลลัพธ์จากเว็บระบุอีกอย่างหนึ่ง ตัวอย่างเช่น เอกสารราคาภายในของคุณระบุว่าเดือนละ $50 แต่ผลลัพธ์จากเว็บระบุว่าราคาเปลี่ยนเป็นเดือนละ $80

กำชับให้ LLM แจ้งความขัดแย้งและเลือกใช้แหล่งข้อมูลจากเว็บสำหรับข้อเท็จจริงที่เปลี่ยนแปลงตามเวลา

HYBRID_ANSWER_PROMPT = '''You are answering a question using two types of sources:
- [INTERNAL] sources: company documents (may be outdated)
- [WEB] sources: current web information

For factual claims about current state (prices, versions, availability):
  PREFER [WEB] sources over [INTERNAL] ones.
For company-specific processes, policies, and architecture:
  PREFER [INTERNAL] sources.

If sources conflict, note the discrepancy in your answer.

Sources:
{sources}

Question: {question}
Answer:'''

def generate_hybrid_answer(question, merged_results):
    sources_text = format_merged_for_prompt(merged_results)
    return llm_call(HYBRID_ANSWER_PROMPT.format(
        sources=sources_text,
        question=question
    ))

การตรวจจับความล้าสมัยของเอกสารภายในเครื่อง

เอกสารภายในเครื่องจะล้าสมัยไปตามเวลา ควรเพิ่มการตรวจสอบความล้าสมัย หากเอกสารภายในเครื่องเก่ากว่าเกณฑ์ที่กำหนด ให้เสริมด้วยการค้นหาเว็บ แม้ว่าตัวกำหนดเส้นทางจะแยกประเภทคำถามเป็น “คงที่” ก็ตาม

from datetime import datetime, timedelta

STALENESS_THRESHOLD_DAYS = 90

def check_staleness(metadata):
    indexed_at = metadata.get('indexed_at')
    if not indexed_at:
        return False  # unknown age — assume fresh
    indexed_date = datetime.fromisoformat(indexed_at)
    age = datetime.now() - indexed_date
    return age > timedelta(days=STALENESS_THRESHOLD_DAYS)

def smart_retrieve(question, route):
    local_results = []
    web_results = []

    if route in ('static', 'both'):
        local_results = local_retrieve(question, n_results=4)
        # Check if any local results are stale
        stale = any(check_staleness(meta) for _, meta in local_results)
        if stale:
            print('Stale local docs — adding web search')
            web_results = web_retrieve(question, n_results=2)

    if route in ('current', 'both'):
        web_results = web_retrieve(question, n_results=3)

    return merge_results(local_results, web_results)

การให้คะแนนความเชื่อมั่น

แนบคะแนนความเชื่อมั่นให้กับบริบทที่ดึงมาแต่ละส่วน แหล่งข้อมูลที่มีความเชื่อมั่นสูง (ใหม่ โดเมนน่าเชื่อถือ และมีความคล้ายคลึงของเวกเตอร์ฝังตัวสูง) ควรได้รับน้ำหนักมากกว่าในคำตอบสุดท้าย

def score_result(result, query_embedding):
    score = 0.5  # base score

    # Recency bonus for web results
    if result.get('type') == 'web':
        pub_date = result.get('published_date', '')
        if '2024' in pub_date or '2025' in pub_date:
            score += 0.2

    # Embedding similarity to query
    if result.get('content'):
        result_emb = embed(result['content'][:500])
        sim = cosine_similarity(query_embedding, result_emb)
        score += sim * 0.3

    # Domain authority
    from urllib.parse import urlparse
    domain = urlparse(result.get('source', '')).netloc
    if any(auth in domain for auth in ['docs.', 'developer.', 'official.']):
        score += 0.1

    return min(score, 1.0)

กระบวนการดึงข้อมูลแบบผสมครบวงจร

เมื่อนำทุกอย่างมารวมกัน: กำหนดเส้นทางอย่างรวดเร็ว → ดึงข้อมูลอย่างชาญฉลาดจากแหล่งใดแหล่งหนึ่งหรือทั้งสองแหล่ง → เสริมข้อมูลที่ล้าสมัย → รวมผลลัพธ์ → ให้คะแนน → จัดรูปแบบ → สร้างคำตอบ

def hybrid_answer(question):
    # 1. Route (fast heuristic first, LLM fallback for ambiguous)
    route = fast_route(question)
    if route == 'both':
        route = classify_question(question)  # LLM for ambiguous cases

    print(f'Route: {route}')

    # 2. Retrieve
    merged = smart_retrieve(question, route)

    if not merged:
        return 'I could not find relevant information to answer your question.'

    # 3. Generate
    answer = generate_hybrid_answer(question, merged)
    return answer

# Usage
print(hybrid_answer('What is our refund policy?'))   # -> static/local
print(hybrid_answer('What is GPT-4 pricing today?')) # -> current/web

ตรวจสอบความรู้

เมื่อใดที่เอเจนต์ดึงข้อมูลแบบผสมควรเลือกผลการค้นหาจากเว็บมากกว่าผลลัพธ์จากเอกสารภายในเครื่อง

ทบทวน: การรวมการค้นหาเว็บเข้ากับ RAG

การดึงข้อมูลแบบผสมรวม ที่จัดเก็บเวกเตอร์ภายในเครื่อง (สำหรับความรู้แบบคงที่ ข้อมูลส่วนตัว หรือความรู้เฉพาะด้าน) เข้ากับ การค้นหาเว็บ (สำหรับข้อมูลปัจจุบันและข้อมูลสาธารณะ) ตัวจำแนกสำหรับกำหนดเส้นทางจะส่งคำถามแต่ละข้อไปยังแหล่งข้อมูลที่เหมาะสม หรือส่งไปทั้งสองแหล่งเมื่อจำเป็น

เทคนิคสำคัญ ได้แก่ การกำหนดเส้นทางอย่างรวดเร็วด้วยหลักเกณฑ์จากสัญญาณคำสำคัญ การตรวจจับความล้าสมัยของเอกสารภายในเครื่อง คำสั่งสำหรับจัดการความขัดแย้งในคำสั่ง และการให้คะแนนความเชื่อมั่นเพื่อกำหนดน้ำหนักของบริบทที่ดึงมา

คำถามที่พบบ่อย

บทเรียน “การผสานการค้นหาเว็บกับ RAG” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การผสานการค้นหาเว็บกับ RAG” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การผสานการค้นหาเว็บกับ RAG”

การดึงข้อมูลแบบผสม: ที่จัดเก็บเวกเตอร์ในเครื่อง + การค้นหาเว็บแบบสดเพื่อคำตอบที่เป็นปัจจุบัน คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การผสานการค้นหาเว็บกับ RAG” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม

ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Tavily และ SerpAPI สำหรับการค้นหาของตัวแทน
  2. การจัดอันดับและกรองผลการค้นหา
  3. รูปแบบลูปการวิจัยเชิงลึก
  4. การผสานการค้นหาเว็บกับ RAG
← กลับไปที่ AI Agents