กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML
ใช้การแบ่งส่วนเฉพาะทางกับโค้ด Python ด้วยตัวแบ่งฟังก์ชันที่อาศัย AST กับ HTML ด้วยตัวแยกวิเคราะห์ที่รู้จักแท็ก และกับ Markdown ด้วยลำดับชั้นของหัวข้อ
กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดการแบ่งส่วนทั่วไปจึงล้มเหลวกับเอกสารเฉพาะทาง
การแบ่งส่วนโดยอาศัยข้อความได้รับการออกแบบมาสำหรับร้อยแก้ว แต่ข้อมูลในโลกจริงยังมี ซอร์สโค้ด หน้า HTML และ เอกสาร Markdown การแบ่งโค้ดที่ขอบเขตอักขระคงที่อาจตัดฟังก์ชันตรงกลางเนื้อความ ทำให้ส่วนย่อยนั้นใช้ค้นคืนไม่ได้ เอกสารเฉพาะทางต้องใช้เครื่องมือแบ่งส่วนที่เข้าใจโครงสร้างภายใน ไม่ใช่พิจารณาเพียงความยาว
การแบ่งส่วนโค้ด Python โดยอาศัย AST
ต้นไม้ไวยากรณ์นามธรรม (AST) ของไฟล์ Python บันทึกฟังก์ชัน คลาส และโมดูลทุกส่วนเป็นโหนดที่มีโครงสร้าง การไล่ดู AST ช่วยให้คุณแยกฟังก์ชันหรือเมธอดแต่ละรายการเป็นส่วนย่อยของตนเอง โดยคงลายเซ็น เอกสารกำกับ และเนื้อความไว้ด้วยกัน PythonCodeTextSplitter ของ LangChain ใช้แนวทางนี้ภายใน
import ast
import textwrap
def extract_functions(source_code: str) -> list[dict]:
tree = ast.parse(source_code)
chunks = []
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
start = node.lineno - 1
end = node.end_lineno
lines = source_code.splitlines()[start:end]
chunks.append({
'name': node.name,
'code': '\n'.join(lines),
'start_line': node.lineno,
})
return chunksการแบ่งส่วนตามขอบเขตคลาส
สำหรับฐานโค้ดเชิงวัตถุ การแบ่งส่วนที่ ระดับคลาส มักดีกว่าการแบ่งที่ระดับฟังก์ชัน ส่วนย่อยระดับคลาสยังคงความสัมพันธ์ระหว่างเมธอดกับสถานะร่วมที่เมธอดเหล่านั้นใช้งาน คุณสามารถรวมเอกสารกำกับของคลาสและเนื้อความของเมธอดทั้งหมดไว้เป็นส่วนย่อยเดียว จากนั้นสร้างส่วนย่อยที่ละเอียดขึ้นสำหรับเมธอดขนาดยาวเท่านั้น
from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=1000,
chunk_overlap=100,
)
with open('my_module.py', 'r') as f:
source = f.read()
chunks = python_splitter.create_documents([source])
print(f'Created {len(chunks)} code chunks')การเพิ่มข้อมูลกำกับโค้ดให้ส่วนย่อย
ส่วนย่อยของโค้ดดิบจะมีประโยชน์มากน้อยเพียงใดขึ้นอยู่กับ ข้อมูลกำกับ เมื่อจัดเก็บส่วนย่อยของโค้ดในฐานข้อมูลเวกเตอร์ ให้ระบุเส้นทางไฟล์ ชื่อฟังก์ชัน ภาษาการเขียนโปรแกรม และช่วงบรรทัด ข้อมูลกำกับนี้ช่วยให้เครื่องมือค้นคืนกรองตามภาษาหรือไฟล์ และช่วยให้ LLM อ้างอิงตำแหน่งต้นฉบับที่แน่นอนในคำตอบ
from langchain_core.documents import Document
def chunk_python_file(filepath: str) -> list[Document]:
with open(filepath) as f:
source = f.read()
functions = extract_functions(source) # from previous example
docs = []
for fn in functions:
docs.append(Document(
page_content=fn['code'],
metadata={
'source': filepath,
'function': fn['name'],
'language': 'python',
'start_line': fn['start_line'],
}
))
return docsHTML: ให้ความสำคัญกับโครงสร้างมากกว่าอักขระ
เอกสาร HTML มีโครงสร้างลำดับชั้นที่ประกอบด้วยหัวเรื่อง ส่วน ย่อหน้า และรายการ การแบ่ง HTML ตามจำนวนอักขระมักตัดผ่านแท็ก ทำให้เกิดส่วนย่อยที่มีรูปแบบไม่ถูกต้อง แนวทางที่เหมาะสมคือแยกวิเคราะห์ HTML ด้วยตัวแยกวิเคราะห์ที่เหมาะสม เช่น BeautifulSoup แล้วแยกองค์ประกอบที่มีความหมายเชิงความหมาย เช่น แท็ก <article> <section> และ <p>
from bs4 import BeautifulSoup
def chunk_html_by_section(html: str) -> list[dict]:
soup = BeautifulSoup(html, 'html.parser')
chunks = []
for tag in soup.find_all(['h1', 'h2', 'h3', 'p', 'li']):
text = tag.get_text(separator=' ', strip=True)
if len(text) > 40: # skip trivial fragments
chunks.append({
'tag': tag.name,
'text': text,
})
return chunksการแบ่งส่วน HTML ตามลำดับชั้นของหัวเรื่อง
กลยุทธ์ HTML ที่ซับซ้อนขึ้นจะจัดกลุ่มเนื้อหาตามหัวเรื่องที่อยู่ใกล้ที่สุด ย่อหน้าและรายการทุกส่วนที่ตามหลังหัวเรื่อง <h2> จะอยู่ในส่วนนั้น การจัดกลุ่มข้อความร่วมกับหัวเรื่องหลักช่วยรักษาบริบทของหัวข้อ ซึ่งย่อหน้าเดี่ยวจะสูญเสียไปหากแยกออกมา HTMLHeaderTextSplitter ของ LangChain ทำงานนี้โดยอัตโนมัติ
from langchain_text_splitters import HTMLHeaderTextSplitter
headers_to_split_on = [
('h1', 'Header 1'),
('h2', 'Header 2'),
('h3', 'Header 3'),
]
splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
with open('page.html') as f:
html = f.read()
sections = splitter.split_text(html)
for sec in sections[:3]:
print(sec.metadata)
print(sec.page_content[:200])
print('---')Markdown: การรักษาลำดับชั้นของหัวเรื่อง
เอกสาร Markdown จัดระเบียบด้วยหัวเรื่อง # ## และ ### MarkdownHeaderTextSplitter จะแบ่งส่วนที่ขอบเขตหัวเรื่องและจัดเก็บลำดับชั้นของหัวเรื่องไว้ในข้อมูลกำกับ นั่นหมายความว่าส่วนย่อยทุกส่วนทราบเส้นทางหัวเรื่องทั้งหมดของตนเอง ซึ่งช่วยเพิ่มความเกี่ยวข้องของบริบทที่ค้นคืนได้อย่างมากเมื่อผู้ใช้ถามเกี่ยวกับส่วนใดส่วนหนึ่งของเอกสาร
from langchain_text_splitters import MarkdownHeaderTextSplitter
headers_to_split_on = [
('#', 'H1'),
('##', 'H2'),
('###', 'H3'),
]
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
with open('README.md') as f:
markdown = f.read()
docs = md_splitter.split_text(markdown)
for doc in docs[:2]:
print('Metadata:', doc.metadata)
print('Content:', doc.page_content[:300])
print()การแบ่งส่วนครั้งที่สองหลังแบ่งตามหัวเรื่อง
หลังจากแบ่งตามหัวเรื่องแล้ว แต่ละส่วนอาจยังยาวเกินขีดจำกัดโทเคนของโมเดลเวกเตอร์แทนความหมาย รูปแบบที่แนะนำคือ การแบ่งสองขั้นตอน ขั้นแรกแบ่งตามลำดับชั้นของหัวเรื่องเพื่อรักษาบริบทเชิงความหมาย จากนั้นใช้เครื่องมือแบ่งส่วนตามอักขระกับส่วนที่เกินขีดจำกัดขนาดส่วนย่อย วิธีนี้ทำให้ไม่มีส่วนย่อยใดใหญ่เกินไป พร้อมทั้งรักษาข้อมูลกำกับของหัวเรื่องไว้
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
header_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[('#', 'H1'), ('##', 'H2')]
)
char_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
)
with open('docs.md') as f:
md = f.read()
header_chunks = header_splitter.split_text(md)
final_chunks = char_splitter.split_documents(header_chunks)
print(f'{len(final_chunks)} final chunks produced')การแบ่งส่วน PDF โดยคำนึงถึงตาราง
PDF ที่แยกข้อความด้วยเครื่องมืออย่าง PyMuPDF หรือ pdfplumber มักสูญเสียโครงสร้างตาราง ทำให้ได้แถวข้อความที่อ่านไม่รู้เรื่อง เพื่อจัดการปัญหานี้ ให้ใช้ตัวแยกวิเคราะห์ PDF ที่คำนึงถึงเค้าโครง ซึ่งตรวจจับกรอบขอบเขตตารางและแปลงเป็นรูปแบบ Markdown หรือ CSV ก่อนแบ่งส่วน ให้ถือว่าตารางแต่ละตารางเป็นส่วนย่อยเดียว พร้อมข้อมูลกำกับที่มีโครงสร้างเพื่อระบุว่าเป็นตาราง ไม่ใช่ร้อยแก้ว
import pdfplumber
def extract_pdf_chunks(pdf_path: str) -> list[dict]:
chunks = []
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages):
# Extract tables separately
for table in page.extract_tables():
rows = ['|'.join(str(c) for c in row) for row in table]
chunks.append({
'type': 'table',
'content': '\n'.join(rows),
'page': page_num + 1,
})
# Extract prose text
text = page.extract_text()
if text:
chunks.append({'type': 'text', 'content': text, 'page': page_num + 1})
return chunksการตรวจจับภาษาสำหรับคลังข้อมูลหลายรูปแบบ
ฐานความรู้ระดับองค์กรมักผสมไฟล์หลายประเภท เช่น สคริปต์ Python เอกสาร API ใน HTML บันทึกสถาปัตยกรรมใน Markdown และข้อมูลส่งออกใน CSV กระบวนการแบ่งส่วนที่มีความทนทานควร ตรวจจับประเภทไฟล์ จากนามสกุลหรือประเภท MIME แล้วส่งเอกสารแต่ละรายการไปยังเครื่องมือแบ่งส่วนเฉพาะทางที่เหมาะสม วิธีนี้ช่วยหลีกเลี่ยงการใช้ตรรกะการแบ่งโค้ดกับร้อยแก้ว หรือในทางกลับกัน
from pathlib import Path
def route_document(filepath: str) -> list[dict]:
ext = Path(filepath).suffix.lower()
if ext == '.py':
return chunk_python_file(filepath)
elif ext in ('.html', '.htm'):
with open(filepath) as f:
return chunk_html_by_section(f.read())
elif ext == '.md':
# use MarkdownHeaderTextSplitter
return chunk_markdown(filepath)
elif ext == '.pdf':
return extract_pdf_chunks(filepath)
else:
# fallback: plain text recursive splitter
return chunk_plain_text(filepath)การรักษาบริบทด้วยบรรทัดโดยรอบ
เมื่อแบ่งโค้ดตามฟังก์ชัน การรวม บริบทโดยรอบ สองสามบรรทัดมักมีประโยชน์ เช่น คำสั่งนำเข้าที่ด้านบนของไฟล์ หรือคำจำกัดความของคลาสที่มีเมธอดนั้น บริบทนี้ช่วยให้ LLM เข้าใจว่ามีไลบรารีใดพร้อมใช้งาน และฟังก์ชันมีบทบาทอย่างไรภายในคลาสที่กว้างขึ้น จึงช่วยปรับปรุงคุณภาพของคำตอบที่สร้างขึ้น
def chunk_with_imports(source_code: str, fn_node, lines: list[str]) -> str:
# Gather top-of-file imports (first block before first non-import)
import_lines = []
for line in lines:
stripped = line.strip()
if stripped.startswith('import ') or stripped.startswith('from '):
import_lines.append(line)
elif stripped and not stripped.startswith('#'):
break
fn_body = '\n'.join(lines[fn_node.lineno - 1:fn_node.end_lineno])
return '\n'.join(import_lines) + '\n\n' + fn_bodyตรวจสอบความเข้าใจ
ทดสอบความเข้าใจเกี่ยวกับกลยุทธ์การแบ่งส่วนเฉพาะเอกสารจากบทเรียนนี้
สรุปบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า การแบ่งส่วนโดยอาศัย AST รักษาขอบเขตฟังก์ชันและคลาสของ Python HTMLHeaderTextSplitter และ MarkdownHeaderTextSplitter เคารพลำดับชั้นของหัวเรื่องเพื่อรักษาบริบทไว้กับส่วนของหัวเรื่องนั้น และ แนวทางสองขั้นตอน (แบ่งตามหัวเรื่องแล้วแบ่งตามอักขระ) จัดการส่วนที่มีขนาดใหญ่เกินไปโดยไม่สูญเสียข้อมูลกำกับเชิงโครงสร้าง ต่อไป เราจะสำรวจการค้นหาแบบผสมที่รวมการค้นคืนแบบหนาแน่นและแบบเบาบาง
คำถามที่พบบ่อย
บทเรียน “กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML”
ใช้การแบ่งส่วนเฉพาะทางกับโค้ด Python ด้วยตัวแบ่งฟังก์ชันที่อาศัย AST กับ HTML ด้วยตัวแยกวิเคราะห์ที่รู้จักแท็ก และกับ Markdown ด้วยลำดับชั้นของหัวข้อ คุณปฏิบัติ AI Engineering Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Engineering Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม
ได้ บทเรียน AI Engineering Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดการแบ่งส่วนแบบง่ายจึงทำลายการดึงข้อมูล
- การแบ่งส่วนเชิงความหมายด้วยความคล้ายคลึงของเวกเตอร์ฝัง
- การดึงข้อมูลแบบส่วนแม่-ส่วนลูกและจากเล็กไปใหญ่
- กลยุทธ์เฉพาะเอกสารสำหรับโค้ดและ HTML