วิธีสร้างระบบ RAG ด้วย Claude: AI ที่ทำงานบนข้อมูลของคุณเอง (คู่มือฉบับสมบูรณ์)

@undefinedKi
อังกฤษ2 วันที่ผ่านมา · 11 ก.ค. 2569
206K
150
25
16
575

TL;DR

คู่มือฉบับสมบูรณ์นี้อธิบายวิธีการสร้างระบบ Retrieval-Augmented Generation (RAG) แบบโลคัลโดยใช้ Claude ซึ่งช่วยให้ผู้ใช้สามารถสอบถามข้อมูลจากเอกสารของตนเองได้อย่างมีประสิทธิภาพ พร้อมทั้งลดค่าใช้จ่ายในการใช้ API

ถาม Claude เกี่ยวกับบริษัทของคุณ โน้ตของคุณ หรือไฟล์ของคุณ แล้วมันจะตอบไม่ได้เพราะมันไม่เคยเห็นข้อมูลเหล่านั้นมาก่อน มันรู้แค่สิ่งที่เรียนรู้จากข้อมูลฝึกฝน และข้อมูลของคุณไม่ได้อยู่ในนั้น

ระบบ RAG แก้ปัญหานี้ แทนที่จะตอบจากความจำ Claude จะค้นหาข้อมูลในเอกสารของคุณก่อน ดึงส่วนที่เกี่ยวข้องมา แล้วตอบจากสิ่งที่มันเจอจริงๆ ข้อมูลของคุณ เป็นแหล่งของมัน ไม่มีการเดาสุ่ม

ทำไมมันถึงดีกว่าการวางไฟล์ลงในแชทเฉยๆ:

ปรับขนาดได้ ฐานความรู้ทั้งหมดของคุณใส่ในแชทเดียวไม่พอ RAG เก็บทุกอย่างและดึงเฉพาะสิ่งที่แต่ละคำถามต้องการ

ถูกกว่า การวางไฟล์หมายความว่า Claude จะอ่านทั้งไฟล์ใหม่ทุกครั้งที่มีคำถาม RAG อ่านครั้งเดียว แล้วดึงเฉพาะส่วนที่เกี่ยวข้อง แทนที่จะส่งคู่มือขนาด 10,000 โทเค็นทุกครั้ง มันอาจส่งแค่ 500 โทเค็นของส่วนที่คุณต้องการ ระบบจริงลดการใช้โทเค็นได้ 80% หรือมากกว่า

แม่นยำกว่า ป้อนข้อความยาวๆ ให้โมเดล มันจะสูญเสียรายละเอียดตรงกลาง แต่ถ้าให้เฉพาะส่วนที่เกี่ยวข้อง คำตอบจะแม่นยำขึ้น

เป็นปัจจุบัน อัปเดตไฟล์ของคุณครั้งเดียว ระบบก็จะใช้เวอร์ชันล่าสุด ไม่ต้องวางไฟล์ใหม่

เมื่ออ่านคู่มือนี้จบ คุณจะได้ระบบ RAG ที่ทำงานกับไฟล์ของคุณเอง ทีละขั้นตอน โดยไม่ต้องมีปริญญาเอก

สิ่งที่คุณต้องมี

ก่อนจะแตะโค้ดใดๆ นี่คือรายการทั้งหมด ข่าวดี: เวอร์ชันนี้ใช้คีย์ API แค่หนึ่งอัน และทุกอย่างอื่นทำงานฟรีบนเครื่องของคุณ

1. Python 3.9 หรือใหม่กว่า เช็คว่ามีหรือไม่โดยเปิด Terminal (Terminal บน Mac, Command Prompt บน Windows) แล้วพิมพ์ python --version ถ้าเห็นเลขเช่น 3.11 ก็ใช้ได้ ถ้าไม่มี ให้ดาวน์โหลดจาก python.org แล้วติดตั้ง บน Windows ให้ติ๊ก "Add Python to PATH" ระหว่างติดตั้ง ไม่เช่นนั้นคำสั่งด้านล่างจะใช้ไม่ได้

2. Claude API key หนึ่งอัน พร้อมเครดิตเล็กน้อย นี่คือคีย์เดียวและเงินเดียวที่คู่มือนี้ต้องการ วิธีทำทีละขั้นตอน:

ไปที่ platform.claude.com แล้วล็อกอิน (หรือสมัคร) ที่นั่น

API ต้องมีเครดิตเป็นบวกจึงจะทำงานได้ ดังนั้นเติมเงินก่อน เมื่อระบบถาม ให้เลือกว่าเครดิตสำหรับตัวคุณหรือบริษัท จากนั้นคุณจะไปที่หน้าชำระเงิน เลือกตัวเลือก $5 "Starting out" นั้นเพียงพอแล้ว: ทุกอย่างอื่นในคู่มือนี้ฟรีและทำงานบนเครื่องของคุณ Claude จึงเป็นสิ่งเดียวที่เสียเงิน และแต่ละคำถามเสียเงินแค่เศษเสี้ยวเซนต์ เครดิตหมดอายุหลังจากซื้อ 1 ปี

หลังจากชำระเงิน คุณจะไปที่หน้า Console Dashboard คุณควรเห็นยอดเครดิต (เช่น $5.00) ที่มุมซ้ายบนใต้ "Organization credits"

จากนั้นรับคีย์ คลิก Get API key (มุมขวาบน) แล้ว Create Key ตั้งชื่ออะไรก็ได้ (เช่น my-rag-key) แล้วปล่อย workspace เป็น Default คลิก create แล้วคัดลอกสตริงที่แสดงขึ้นมา มันขึ้นต้นด้วย sk-ant- และคุณจะเห็นมันแค่ครั้งเดียว ดังนั้นวางไว้ที่ปลอดภัยสักครู่

นั่นคือการตั้งค่าทั้งหมด

ขั้นตอนที่ 1: เพิ่มคีย์และโหลดไฟล์ของคุณ

1. สร้างโฟลเดอร์โปรเจกต์ สร้างโฟลเดอร์ใหม่บน Desktop ชื่อ rag-project ทุกอย่างจะอยู่ที่นี่

2. เปิดเทอร์มินัล บน Mac: Cmd+Space, พิมพ์ Terminal, Enter บน Windows: ปุ่ม Start, พิมพ์ cmd, Enter

3. ชี้เทอร์มินัลไปที่โฟลเดอร์ของคุณ พิมพ์ cd แล้วเว้นวรรค จากนั้นลากโฟลเดอร์ rag-project ไปที่หน้าต่างเทอร์มินัลแล้วกด Enter ทุกคำสั่งด้านล่างจะรันจากในโฟลเดอร์นี้

bash
1cd Desktop/rag-project

4. ติดตั้งเครื่องมือ วางคำสั่งนี้ในเทอร์มินัลแล้วกด Enter (ครั้งแรกอาจใช้เวลาสักครู่):

bash
1pip install anthropic chromadb sentence-transformers pypdf python-dotenv

ถ้าเจอ pip: command not found ให้ใช้ pip3 แทน pip เมื่อเทอร์มินัลแสดงบรรทัดใหม่โดยไม่มีข้อผิดพลาดสีแดง แสดงว่าเสร็จ

5. สร้างไฟล์โค้ดของคุณ ใน rag-project สร้างไฟล์ว่างชื่อ rag.py เปิดด้วยโปรแกรมแก้ไขข้อความใดก็ได้

6. สร้างไฟล์คีย์ของคุณ ในโฟลเดอร์เดียวกัน สร้างไฟล์ชื่อ .env (ขึ้นต้นด้วยจุด ไม่มีชื่อข้างหน้า) วางข้อความนี้ลงไป โดยใส่คีย์จริงที่คุณสร้างตอนตั้งค่าไว้หลังเครื่องหมาย = ไม่มีช่องว่าง ไม่มีเครื่องหมายคำพูด:

text
1ANTHROPIC_API_KEY=sk-ant-paste-your-real-key-here

การเก็บคีย์ไว้ใน .env แทนที่จะใส่ในโค้ดจะป้องกันไม่ให้คีย์รั่วไหลถ้าคุณแชร์สคริปต์หรืออัปโหลดขึ้น GitHub

7. โหลดคีย์ ใส่โค้ดนี้ไว้ที่ด้านบนของ rag.py:

python
1import os
2from dotenv import load_dotenv
3
4load_dotenv() # อ่านไฟล์ .env ของคุณ
5api_key = os.getenv("ANTHROPIC_API_KEY")

8. สร้างฐานความรู้ของคุณ ใน rag-project สร้างโฟลเดอร์ชื่อ documents วางไฟล์ .txt, .md, หรือ .pdf ลงไป: โน้ตของคุณ, เอกสารผลิตภัณฑ์, บันทึกการประชุม, อะไรก็ได้

8.1. ถ้าคุณยังไม่มีไฟล์ ให้ใช้ไฟล์ทดสอบนี้ สร้างไฟล์ notes.txt ในโฟลเดอร์ documents แล้ววางข้อความนี้:

Project Northstar คือเครื่องมือภายในของเราสำหรับติดตามความคิดเห็นของลูกค้า เปิดตัวในเดือนมีนาคม 2026 และดูแลโดยทีมแพลตฟอร์ม วิศวกรนำคือ Dana Reyes ความคิดเห็นจะถูกตรวจสอบทุกวันศุกร์ Northstar แทนที่ระบบสเปรดชีตแบบเก่าที่เราใช้จนถึงปี 2025

ในตอนท้าย คุณจะถาม Claude เกี่ยวกับ Northstar และดูมันตอบจากไฟล์นี้โดยตรง

9. เพิ่มโค้ดที่อ่านไฟล์ของคุณ ใต้โค้ดจากขั้นตอนที่ 7 ใน rag.py:

python
1from pathlib import Path
2from pypdf import PdfReader
3
4def load_documents(folder="documents"):
5 docs = []
6 for file in Path(folder).iterdir():
7 if file.suffix in [".txt", ".md"]:
8 text = file.read_text(encoding="utf-8")
9 docs.append({"source": file.name, "text": text})
10 elif file.suffix == ".pdf":
11 reader = PdfReader(str(file))
12 text = "\n".join(page.extract_text() or "" for page in reader.pages)
13 docs.append({"source": file.name, "text": text})
14 return docs
15
16documents = load_documents()
17print(f"Loaded {len(documents)} document(s).")

10. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

คุณควรเห็น:

Loaded 1 document(s)

ถ้าคุณเห็น Loaded 0 document(s) แสดงว่าโฟลเดอร์ documents ว่างหรืออยู่ในตำแหน่งผิด มันต้องอยู่ภายใน rag-project โดยตรง ข้างๆ rag.py

ขั้นตอนที่ 2: แบ่งไฟล์ของคุณเป็น chunks

ตอนนี้แต่ละไฟล์เป็นข้อความก้อนใหญ่一塊 ก่อนที่เราจะค้นหามันได้ เราต้องตัดมันเป็นชิ้นเล็กๆ ที่เรียกว่า chunks นี่คือเหตุผล: เมื่อมีคนถามคำถาม ระบบจะหา chunks ที่ตรงกันและส่งเฉพาะส่วนนั้นไปให้ Claude ถ้า chunks ของคุณเป็นเอกสารทั้ง 50 หน้า คุณก็ส่งข้อมูลมากเกินไป ถ้าเป็นประโยคเดี่ยวๆ มันจะขาดบริบท ย่อหน้าสั้นๆ คือจุดที่เหมาะสม

1. เพิ่มโค้ดการแบ่ง chunk ใต้โค้ดจากขั้นตอนที่ 10 ใน rag.py:

python
1def chunk_text(text, chunk_size=500, overlap=100):
2 words = text.split()
3 chunks = []
4 start = 0
5 while start < len(words):
6 end = start + chunk_size
7 chunk = " ".join(words[start:end])
8 chunks.append(chunk)
9 start = end - overlap # ถอยหลังเล็กน้อยเพื่อให้ chunks ซ้อนทับกัน
10 return chunks

ตัวเลขสองตัวที่ต้องเข้าใจในภาษาธรรมดา:

  • chunk_size=500 หมายถึงแต่ละ chunk มีประมาณ 500 คำ ใหญ่พอที่จะเก็บแนวคิดหนึ่งๆ ได้ เล็กพอที่จะคงความแม่นยำ
  • overlap=100 หมายถึงแต่ละ chunk ซ้ำ 100 คำสุดท้ายของ chunk ก่อนหน้า สิ่งนี้สำคัญเพราะคำตอบอาจอยู่ตรงรอยต่อของสอง chunks พอดี ถ้าไม่มีการซ้อนทับ ประโยคที่ถูกแบ่งครึ่งอาจหายไป การซ้อนทับทำให้ไม่มีแนวคิดใดตกหล่น

2. แปลงทุกเอกสารเป็น chunks เพิ่มโค้ดนี้ด้านล่าง:

python
1all_chunks = []
2for doc in documents:
3 for chunk in chunk_text(doc["text"]):
4 all_chunks.append({"source": doc["source"], "text": chunk})
5
6print(f"Created {len(all_chunks)} chunk(s) from {len(documents)} document(s).")

สังเกตว่าแต่ละ chunk ติด source (ชื่อไฟล์ที่มันมา) มาด้วย เราจะเก็บสิ่งนี้ไว้ตลอดทาง เพื่อที่เมื่อ Claude ตอบในภายหลัง มันจะบอกคุณได้ว่าคำตอบมาจากไฟล์ไหน

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

คุณควรเห็นอะไรทำนองนี้:

Loaded 1 document(s).

Created 1 chunk(s) from 1 document(s).

ไฟล์ทดสอบเล็กๆ กลายเป็นแค่ chunk เดียวเพราะมันสั้น เอกสารจริงจะสร้างหลาย chunk ถ้าคุณวาง PDF ยาวๆ ในโฟลเดอร์ คุณอาจเห็นหลายสิบหรือหลายร้อย chunks ซึ่งเป็นสิ่งที่คุณต้องการ

ขั้นตอนที่ 3: เปลี่ยน chunks ของคุณเป็น embeddings

นี่คือขั้นตอนที่ทำให้คอมพิวเตอร์ค้นหาด้วยความหมายแทนคำที่ตรงกัน แต่ละ chunk จะถูกแปลงเป็นชุดตัวเลข (embedding) ที่จับความหมายของมัน Chunks ที่มีความหมายคล้ายกันจะมีตัวเลขที่คล้ายกัน ต่อมาเมื่อมีคำถามเข้ามา เราจะเปลี่ยนคำถามเป็นตัวเลขเช่นกันและหา chunks ที่ใกล้เคียงที่สุด

โมเดลที่ทำสิ่งนี้ทำงานบนเครื่องของคุณในเครื่อง本地 มันดาวน์โหลดครั้งเดียว จากนั้นทำงานออฟไลน์และฟรี และไฟล์ของคุณไม่เคยออกจากเครื่องของคุณ

1. โหลดโมเดล embedding ใต้โค้ดจากขั้นตอนที่ 2 ใน rag.py:

python
1from sentence_transformers import SentenceTransformer
2
3print("Loading the embedding model (first run downloads it, about 90 MB)...")
4embedder = SentenceTransformer("all-MiniLM-L6-v2")

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

2. แปลงทุก chunk เป็น embedding เพิ่มโค้ดนี้ด้านล่าง:

python
1chunk_texts = [chunk["text"] for chunk in all_chunks]
2embeddings = embedder.encode(chunk_texts)
3
4print(f"Created {len(embeddings)} embedding(s).")
5print(f"Each embedding is a list of {len(embeddings[0])} numbers.")

embedder.encode(...) รับ list ของข้อความ chunk แล้วคืนค่า embedding หนึ่งตัวต่อ chunk เท่านั้นเอง

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

ครั้งแรกจะหยุดชั่วคราวขณะดาวน์โหลดโมเดล จากนั้นคุณควรเห็นอะไรทำนองนี้:

Loaded 1 document(s).

Created 1 chunk(s) from 1 document(s).

Loading the embedding model (first run downloads it, about 90 MB)...

Created 1 embedding(s).

Each embedding is a list of 384 numbers.

บรรทัด "384 ตัวเลข" นั้นทำให้แนวคิดทั้งหมดเห็นภาพ: ข้อความของคุณกลายเป็นแถวตัวเลขที่คอมพิวเตอร์สามารถเปรียบเทียบได้ คุณไม่จำเป็นต้องอ่านหรือเข้าใจตัวเลขเหล่านั้นด้วยตนเอง ฐานข้อมูลในขั้นตอนต่อไปจะจัดการการเปรียบเทียบทั้งหมดให้คุณ

ถ้าการดาวน์โหลดล้มเหลวเนื่องจากข้อผิดพลาดการเชื่อมต่อ ให้รันคำสั่งอีกครั้ง มันจะเริ่มต่อจากจุดที่ค้างไว้

ขั้นตอนที่ 4: เก็บทุกอย่างในฐานข้อมูลเวกเตอร์ของคุณ

ตอนนี้เราจะใส่ chunks และ embeddings ของพวกมันลงใน Chroma ซึ่งเป็นฐานข้อมูลในเครื่องของคุณ นี่คือสิ่งที่ทำให้การค้นหาเร็ว: แทนที่จะเปรียบเทียบคำถามของคุณกับทุก chunk ด้วยมือทุกครั้ง Chroma เก็บพวกมันไว้พร้อมใช้งานและทำการจับคู่ให้คุณ มันบันทึกลงในโฟลเดอร์บนเครื่องของคุณ ดังนั้นคุณจึงสร้างมันเพียงครั้งเดียว

1. ตั้งค่าฐานข้อมูล ใต้โค้ดจากขั้นตอนที่ 3 ใน rag.py:

python
1import chromadb
2
3client = chromadb.PersistentClient(path="chroma_db")
4collection = client.get_or_create_collection("my_documents")

PersistentClient(path="chroma_db") บอกให้ Chroma บันทึกลงในโฟลเดอร์ชื่อ chroma_db (มันสร้างโดยอัตโนมัติ ข้างๆ สคริปต์ของคุณ) เนื่องจากบันทึกลงดิสก์ ข้อมูลของคุณจึงอยู่รอดหลังจากสคริปต์ทำงานเสร็จ collection คือกล่องที่มีชื่อที่ chunks ของคุณอาศัยอยู่

2. เพิ่ม chunks ของคุณลงในฐานข้อมูล เพิ่มโค้ดนี้ด้านล่าง:

python
1collection.add(
2 ids=[str(i) for i in range(len(all_chunks))],
3 embeddings=[emb.tolist() for emb in embeddings],
4 documents=[chunk["text"] for chunk in all_chunks],
5 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],
6)
7
8print(f"Stored {collection.count()} chunk(s) in the database.")

นี่คือสิ่งที่แต่ละบรรทัดส่งให้ Chroma ในภาษาธรรมดา: ids ให้ป้ายชื่อเฉพาะกับทุก chunk (0, 1, 2...), embeddings คือตัวเลขจากขั้นตอนที่ 3, documents คือข้อความ chunk จริงๆ, และ metadatas ติดชื่อไฟล์ไปด้วยเพื่อให้เราสามารถแสดงแหล่งที่มาได้ในภายหลัง Chroma เก็บทั้งสี่สิ่งนี้ไว้ด้วยกัน

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

คุณควรเห็น:

Stored 1 chunk(s) in the database.

สิ่งหนึ่งที่ควรรู้สำหรับภายหลัง ทุกครั้งที่คุณรันสคริปต์ตอนนี้ มันจะเพิ่ม chunks อีกครั้ง ดังนั้นจำนวนอาจเพิ่มขึ้น (1, แล้ว 2, แล้ว 3...) ในการรันซ้ำ ซึ่งไม่เป็นไรระหว่างที่เรากำลังสร้าง ถ้าต้องการเริ่มใหม่ ให้ลบโฟลเดอร์ chroma_db แล้วรันอีกครั้ง ในเวอร์ชันสุดท้ายเราจะจัดการอย่างถูกต้องเพื่อไม่ให้ซ้ำซ้อน

ขั้นตอนที่ 5: ค้นหาเอกสารของคุณ

นี่คือส่วน "retrieval" ของ RAG ตัว R ในชื่อ เราจะนำคำถาม เปลี่ยนเป็น embedding ด้วยวิธีเดียวกับที่ทำกับ chunks แล้วถาม Chroma ว่า chunks ไหนที่มีความหมายใกล้เคียงที่สุด chunks ที่ตรงกันเหล่านั้นคือสิ่งที่เราจะส่งให้ Claude ในขั้นตอนถัดไป

1. เพิ่มฟังก์ชันค้นหา ใต้โค้ดจากขั้นตอนที่ 4 ใน rag.py:

python
1def search(question, n_results=3):
2 question_embedding = embedder.encode([question])[0]
3 results = collection.query(
4 query_embeddings=[question_embedding.tolist()],
5 n_results=n_results,
6 )
7 return results

สิ่งที่ฟังก์ชันนี้ทำ ทีละบรรทัดในภาษาธรรมดา: มันเปลี่ยนคำถามเป็นตัวเลขด้วยโมเดลเดียวกับที่คุณใช้กับ chunks (สำคัญ เพราะทั้งคู่ต้องใช้ "ภาษาตัวเลข" เดียวกัน) จากนั้นถาม Chroma หา chunks ที่ใกล้เคียงที่สุด n_results=3 หมายถึง "ให้ chunks ที่เกี่ยวข้องมากที่สุด 3 อันมา" สามเป็นค่าเริ่มต้นที่ดี: มีบริบทเพียงพอ ไม่มากเกินไปจนเสียโทเค็น

2. ทดลองค้นหา เพิ่มโค้ดนี้ด้านล่างเพื่อทดสอบ:

python
1question = "Who runs Northstar and when is feedback reviewed?"
2results = search(question)
3
4for i, doc in enumerate(results["documents"][0]):
5 source = results["metadatas"][0][i]["source"]
6 print(f"\n--- Match {i+1} (from {source}) ---")
7 print(doc)

นี่จะรันคำถามจริงกับฐานข้อมูลของคุณและพิมพ์ chunks ที่พบ พร้อมชื่อไฟล์ที่มา

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

ด้วยไฟล์ทดสอบ Northstar คุณควรเห็นมันดึง chunk ที่ตรงกันกลับมา ประมาณนี้:

--- Match 1 (from notes.txt) ---

Project Northstar คือเครื่องมือภายในของเราสำหรับติดตามความคิดเห็นของลูกค้า เปิดตัวในเดือนมีนาคม 2026 และดูแลโดยทีมแพลตฟอร์ม วิศวกรนำคือ Dana Reyes ความคิดเห็นจะถูกตรวจสอบทุกวันศุกร์ Northstar แทนที่ระบบสเปรดชีตแบบเก่าที่เราใช้จนถึงปี 2025

สังเกตสิ่งที่เกิดขึ้น: คำถามของคุณใช้คำว่า "who runs" และ "reviewed" แต่ไฟล์บอกว่า "lead engineer" และ "reviewed every Friday" มันก็จับคู่ได้เพราะการค้นหาทำงานด้วยความหมาย ไม่ใช่คำที่ตรงกัน นั่นคือจุดรวมของ embeddings และนี่คือสาเหตุที่มันดีกว่าการค้นหาคำหลัก (Ctrl+F) ทั่วไปในไฟล์ของคุณ

ถ้าคุณมีไฟล์มากกว่านี้ คุณจะเห็น 3 chunks อันดับต้นๆ จากทั้งหมด เรียงตามความใกล้เคียง

ขั้นตอนที่ 6: ให้ Claude ตอบจากสิ่งที่มันพบ

นี่คือส่วน "generation" ตัว G ใน RAG เราจะนำ chunks จากขั้นตอนที่ 5 ส่งให้ Claude Opus 4.8 พร้อมกับคำถาม และบอกให้มันตอบโดยใช้เฉพาะบริบทนั้นเท่านั้น นี่คือสิ่งที่หยุดไม่ให้มันเดา: Claude ตอบจากไฟล์ของคุณ ไม่ใช่จากความจำของมันเอง และบอกคุณว่ามันใช้ไฟล์ไหน

1. เพิ่มฟังก์ชันตอบคำถาม ใต้โค้ดจากขั้นตอนที่ 5 ใน rag.py:

python
1import anthropic
2
3claude = anthropic.Anthropic(api_key=api_key)
4
5def answer(question):
6 results = search(question)
7 chunks = results["documents"][0]
8 sources = [m["source"] for m in results["metadatas"][0]]
9
10 context = ""
11 for i, chunk in enumerate(chunks):
12 context += f"[From {sources[i]}]\n{chunk}\n\n"
13
14 message = claude.messages.create(
15 model="claude-opus-4-8",
16 max_tokens=1024,
17 system=(
18 "You answer questions using only the context provided. "
19 "If the answer is not in the context, say you don't know. "
20 "Always mention which file your answer came from."
21 ),
22 messages=[
23 {
24 "role": "user",
25 "content": f"Context:\n{context}\nQuestion: {question}",
26 }
27 ],
28 )
29 return message.content[0].text

สิ่งที่เกิดขึ้นที่นี่ ในภาษาธรรมดา: เราค้นหา chunks ที่เกี่ยวข้อง รวม它们ไว้เป็นก้อน context เดียว (แต่ละส่วนมีป้ายชื่อไฟล์) จากนั้นส่งก้อนนั้นพร้อมกับคำถามไปยัง Claude คำสั่ง system คือส่วนสำคัญ มันบอก Claude สามอย่าง: ตอบจากบริบทเท่านั้น ยอมรับเมื่อไม่มีคำตอบอยู่ในนั้น และบอกชื่อไฟล์ต้นทาง สามกฎนี้คือสิ่งที่ทำให้คำตอบน่าเชื่อถือแทนที่จะถูกสร้างขึ้นมา

model="claude-opus-4-8" คือชื่อโมเดลที่แน่นอน (ใช้ขีดกลาง ไม่ใช่จุด) max_tokens=1024 จำกัดความยาวของคำตอบ

2. ถามคำถาม เพิ่มโค้ดนี้ด้านล่าง:

python
1question = "Who runs Northstar and when is feedback reviewed?"
2print(answer(question))

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

คุณควรได้คำตอบจริงที่สร้างจากไฟล์ของคุณ ประมาณนี้:

Dana Reyes เป็นวิศวกรนำที่ดูแล Project Northstar และความคิดเห็นจะถูกตรวจสอบทุกวันศุกร์ (ที่มา: notes.txt)

นั่นคือระบบ RAG ที่สมบูรณ์ทำงาน Claude ไม่เคยเห็นไฟล์นี้ระหว่างการฝึก มันไม่สามารถรู้ว่า Dana Reyes คือใคร แต่มันตอบถูกต้องและบอกคุณอย่างชัดเจนว่าคำตอบมาจากไหน ถามสิ่งที่ไม่มีในไฟล์ของคุณ มันจะบอกว่าไม่รู้ แทนที่จะสร้างคำตอบขึ้นมา การพูดว่า "ไม่รู้" นั้นเป็นคุณสมบัติ ไม่ใช่ความล้มเหลว: มันคือความแตกต่างระหว่างเครื่องมือที่คุณไว้ใจได้กับเครื่องมือที่เดาสุ่ม

ขั้นตอนที่ 7: ทำให้เป็นสิ่งที่คุณสามารถใช้ได้จริง

ตอนนี้คุณต้องแก้ไขโค้ดและรันสคริปต์ทั้งหมดใหม่ทุกครั้งที่ต้องการถามอะไร แย่กว่านั้น ทุกครั้งที่รัน มันจะอ่านไฟล์ของคุณใหม่และเพิ่ม它们ลงในฐานข้อมูลอีกครั้ง ทำให้ chunks สะสม มาลองแก้ไขทั้งสองอย่าง: สร้างฐานข้อมูลเพียงครั้งเดียว จากนั้นให้คุณถามคำถามเป็น loop โดยพิมพ์ลงในเทอร์มินัลโดยตรง

1. แก้ปัญหาการเพิ่มซ้ำ หาบล็อกจากขั้นตอนที่ 4 ที่เพิ่ม chunks (ส่วน collection.add(...)) แล้วแทนที่ด้วยเวอร์ชันนี้ ซึ่งจะสร้างฐานข้อมูลก็ต่อเมื่อมันว่างเปล่า:

python
1if collection.count() == 0:
2 collection.add(
3 ids=[str(i) for i in range(len(all_chunks))],
4 embeddings=[emb.tolist() for emb in embeddings],
5 documents=[chunk["text"] for chunk in all_chunks],
6 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],
7 )
8 print(f"Stored {collection.count()} chunk(s) in the database.")
9else:
10 print(f"Database already has {collection.count()} chunk(s), skipping rebuild.")

ตอนนี้งานหนัก (อ่านไฟล์, สร้าง embeddings, เติมฐานข้อมูล) จะเกิดขึ้นเฉพาะครั้งแรกเท่านั้น การรันครั้งต่อๆ ไปจะข้ามไปยังการตอบคำถามโดยตรง

2. เพิ่ม loop คำถาม ที่ด้านล่างสุดของ rag.py ให้แทนที่คำถามทดสอบเดี่ยวจากขั้นตอนที่ 6 ด้วยโค้ดนี้:

python
1print("\nAsk a question about your documents (or type 'quit' to exit).\n")
2
3while True:
4 question = input("You: ")
5 if question.lower() in ["quit", "exit"]:
6 break
7 print("\nClaude: " + answer(question) + "\n")

input("You: ") รอให้คุณพิมพ์คำถามแล้วกด Enter while True ให้มันทำงานต่อไปเพื่อให้คุณถามได้มากเท่าที่ต้องการ พิมพ์ quit เพื่อหยุด

3. รันมัน บันทึก rag.py จากนั้นในเทอร์มินัล:

bash
1python rag.py

ตอนนี้คุณสามารถพูดคุยกับไฟล์ของคุณได้:

Ask a question about your documents (or type 'quit' to exit).

You: ใครเป็นวิศวกรนำของ Northstar?

Claude: วิศวกรนำของ Project Northstar คือ Dana Reyes (ที่มา: notes.txt)

You: มันแทนที่อะไร?

Claude: Northstar แทนที่ระบบสเปรดชีตแบบเก่าที่ใช้จนถึงปี 2025 (ที่มา: notes.txt)

You: quit

นั่นคือระบบ RAG ที่เสร็จสมบูรณ์ของคุณ มันอ่านไฟล์ของคุณครั้งเดียว จดจำ它们 และตอบคำถามเกี่ยวกับ它们ตามต้องการ พร้อมแหล่งที่มาทุกครั้ง

สิ่งหนึ่งที่ควรรู้เมื่อคุณเพิ่มไฟล์ใหม่ เนื่องจากตอนนี้ฐานข้อมูลถูกสร้างเพียงครั้งเดียว การวางไฟล์ใหม่ใน documents จะไม่แสดงผลโดยอัตโนมัติ ในการโหลดไฟล์ใหม่ ให้ลบโฟลเดอร์ chroma_db แล้วรันสคริปต์หนึ่งครั้ง มันจะสร้างใหม่ทั้งหมดจากทุกอย่างในโฟลเดอร์

เสริม: ทำให้มีหน้าต่างแชทในเบราว์เซอร์ของคุณ

เทอร์มินัลใช้งานได้ แต่ถ้าคุณต้องการหน้าต่างแชทจริงๆ Streamlit จะเพิ่มให้ด้วยโค้ดประมาณ 20 บรรทัด

1. ติดตั้งมัน ในเทอร์มินัลของคุณ:

bash
1pip install streamlit

2. สร้าง app.py ในโฟลเดอร์เดียวกันแล้ววางโค้ดนี้ลงไป มันจะใช้ฟังก์ชัน answer จาก rag.py ของคุณซ้ำ:

python
1import streamlit as st
2from rag import answer
3
4st.title("Chat with your documents")
5
6if "history" not in st.session_state:
7 st.session_state.history = []
8
9question = st.chat_input("Ask about your files...")
10
11if question:
12 reply = answer(question)
13 st.session_state.history.append((question, reply))
14
15for q, a in st.session_state.history:
16 st.chat_message("user").write(q)
17 st.chat_message("assistant").write(a)

3. รันมัน ในเทอร์มินัลของคุณ (หมายเหตุ: ใช้ streamlit run ไม่ใช่ python):

bash
1streamlit run app.py

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

ข้อควรทราบ: เพื่อให้สิ่งนี้ทำงานได้ loop คำถามจากขั้นตอนที่ 7 ต้องไม่ทำงานเมื่อ import ให้ห่อ loop นั้นที่ด้านล่างของ rag.py ด้วย if __name__ == "__main__": เพื่อให้มันทำงานก็ต่อเมื่อคุณรัน rag.py โดยตรง ไม่ใช่ตอนที่ app.py import มัน

การให้มันตอบคำถามทั่วไปด้วย

ถ้าคุณต้องการให้มันตอบคำถามทั่วไปด้วย ตอนนี้ระบบตอบจากไฟล์ของคุณเท่านั้น ดังนั้นคำถามเช่น "เมืองหลวงของเวเนซุเอลาคืออะไร" จะได้คำตอบว่า "ไม่มีในเอกสาร" ทั้งที่ Claude รู้คำตอบ ถ้าคุณต้องการให้มันใช้ความรู้ของตัวเองเป็นตัวเลือกสำรอง ให้เปิด rag.py หาบล็อก system=(...) ในขั้นตอนที่ 6 แล้วสลับบรรทัดนี้:

python
1"ถ้าคำตอบไม่อยู่ในบริบท ให้บอกว่าคุณไม่รู้"

สำหรับอันนี้:

python
1"ถ้าคำตอบไม่อยู่ในบริบท ให้ตอบจากความรู้ทั่วไปของคุณ แต่ให้บอกว่าคุณกำลังทำเช่นนั้น"

บันทึกแล้วรันใหม่ ตอนนี้มันจะตอบจากไฟล์ของคุณก่อน และถ้าไฟล์ไม่ครอบคลุม ก็จะใช้ความรู้ทั่วไปแทน พร้อมบอกคุณว่ามันใช้ข้อมูลจากไหน

สรุป

คุณเพิ่งสร้างระบบ RAG ที่ใช้งานได้จริง มันอ่านไฟล์ของคุณเอง ค้นหาส่วนที่เกี่ยวข้อง และให้ Claude ตอบโดยอ้างอิงแหล่งที่มาที่แน่นอนทุกครั้ง โครงสร้างเดียวกันนี้ปรับขนาดได้จากโน้ตไม่กี่อันไปจนถึงฐานความรู้ทั้งหมดของคุณ

จากตรงนี้ คุณสามารถชี้ไปที่ไหนก็ได้: คลัง Obsidian ของคุณ, เอกสารงานของคุณ, งานวิจัยที่คุณบันทึกไว้ วางไฟล์ลงไป, สร้างใหม่ครั้งเดียว, แล้วเริ่มถาม ทุกสิ่งที่คุณเรียนรู้ที่นี่ — การแบ่งเป็นส่วน, การสร้าง embeddings, การค้นหา, การตอบ — คือแกนหลักเดียวกันกับเครื่องมือ "แชทกับเอกสารของคุณ" ทุกตัวที่คุณเคยเห็น

ถ้าสิ่งนี้มีประโยชน์ ไปที่โปรไฟล์ของฉันแล้วกดติดตาม ฉันเขียนเกี่ยวกับเทคโนโลยี, AI, และระบบที่ทำงานได้จริง

Ciao,

[@undefinedKi](https://x.com/@undefinedKi)

สร้างต่อใน YouMind

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
สำหรับครีเอเตอร์

เปลี่ยน Markdown ของคุณให้เป็นบทความ 𝕏 ที่สะอาดตา

เวลาคุณเผยแพร่งานเขียนยาวของตัวเอง การจัดรูปแบบรูปภาพ ตาราง และบล็อกโค้ดให้เข้ากับ 𝕏 นั้นน่าปวดหัว YouMind เปลี่ยนร่าง Markdown ทั้งฉบับให้เป็นบทความ 𝕏 ที่สะอาดตาและพร้อมโพสต์ทันที

ลอง Markdown เป็น 𝕏

แพตเทิร์นให้ถอดรหัสเพิ่มเติม

บทความไวรัลล่าสุด

สำรวจบทความไวรัลเพิ่มเติม