การสร้างเอเจนต์เสริมความรู้
ตั้งแต่ต้นจนจบ: การเชื่อมโยงเอนทิตี → การสืบค้นกราฟ → การสังเคราะห์คำตอบ
การสร้างเอเจนต์เสริมความรู้ เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
ตัวแทนที่เสริมด้วยความรู้คืออะไร
ตัวแทนที่เสริมด้วยความรู้เพิ่มคุณภาพคำตอบโดยใช้ฐานความรู้ เมื่อมีคำถามเข้ามา ตัวแทนจะสกัดเอนทิตี ค้นหาเอนทิตีเหล่านั้นในกราฟความรู้ ค้นหาเอกสารที่เกี่ยวข้องด้วยการค้นหาเวกเตอร์ และส่งบริบททั้งหมดให้ LLM เพื่อสร้างคำตอบที่มีข้อมูลรองรับอย่างครบถ้วน
กระบวนการทำงานการค้นคืนทั้งหมด
กระบวนการทำงานของตัวแทน: 1 รับคำถาม → 2 สกัดเอนทิตี → 3 ค้นหาจากกราฟเพื่อรับบริบทของเอนทิตี → 4 ค้นหาเวกเตอร์เพื่อค้นหาเอกสารที่เกี่ยวข้อง → 5 รวมบริบททั้งหมด → 6 LLM สร้างคำตอบ
from dataclasses import dataclass, field
from typing import List, Dict, Any
@dataclass
class RetrievalContext:
question: str
entities: List[str] = field(default_factory=list)
graph_context: Dict[str, Any] = field(default_factory=dict)
vector_documents: List[Dict] = field(default_factory=list)
combined_context: str = ''
answer: str = ''
sources_used: List[str] = field(default_factory=list)
# The agent will populate this object as it works through the pipeline
ctx = RetrievalContext(question='What AI projects is Sam Altman known for?')
print('RetrievalContext created:', ctx.question)ขั้นตอนที่ 1: การสกัดเอนทิตี
สกัดเอนทิตีที่มีชื่อจากคำถาม เอนทิตีเหล่านี้จะเป็นจุดยึดสำหรับการค้นหาจากกราฟ ใช้ spaCy เพื่อความรวดเร็ว และใช้ LLM กับกรณีที่ซับซ้อนหรือเอนทิตีเฉพาะด้าน
import spacy
nlp = spacy.load('en_core_web_sm')
def extract_question_entities(question: str) -> List[str]:
doc = nlp(question)
entities = list({
ent.text for ent in doc.ents
if ent.label_ in ['PERSON', 'ORG', 'GPE', 'PRODUCT', 'WORK_OF_ART']
})
return entities
def extract_entities_with_llm_fallback(question: str, client) -> List[str]:
spacy_entities = extract_question_entities(question)
if spacy_entities:
return spacy_entities
# Fallback to LLM for questions where spaCy finds nothing
import json
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{
'role': 'user',
'content': f'Extract named entities (people, companies, technologies) from: "{question}". Return JSON: {{"entities": ["name1", "name2"]}}'
}],
response_format={'type': 'json_object'}
)
result = json.loads(response.choices[0].message.content)
return result.get('entities', [])
question = 'What AI projects is Sam Altman known for?'
entities = extract_question_entities(question)
print('Extracted entities:', entities)ขั้นตอนที่ 2: การค้นหาจากกราฟ
สำหรับเอนทิตีที่สกัดได้แต่ละรายการ ให้สอบถามกราฟความรู้เพื่อรับคุณสมบัติและความสัมพันธ์ของเอนทิตีนั้น ข้อมูลนี้ช่วยให้ LLM ได้รับข้อเท็จจริงพื้นฐานที่ไม่สามารถสร้างขึ้นเองอย่างไม่มีมูลได้
from neo4j import GraphDatabase
driver = GraphDatabase.driver('bolt://localhost:7687', auth=('neo4j', 'password'))
def get_rich_entity_context(entity_name: str) -> dict:
with driver.session() as session:
# Get entity + its relationships
result = session.run(
'MATCH (n {name: $name}) '
'OPTIONAL MATCH (n)-[r]->(target) '
'RETURN n, labels(n) AS labels, '
'COLLECT({rel: type(r), target_name: target.name, target_label: labels(target)}) AS outgoing '
'LIMIT 1',
name=entity_name
)
record = result.single()
if not record:
return {'found': False, 'name': entity_name}
return {
'found': True,
'name': entity_name,
'labels': record['labels'],
'properties': dict(record['n']),
'connections': [
c for c in record['outgoing'] if c.get('target_name')
][:10]
}
def format_entity_context_for_llm(entity_ctx: dict) -> str:
if not entity_ctx.get('found'):
return f'No knowledge graph data found for "{entity_ctx["name"]}"'
props = entity_ctx.get('properties', {})
connections = entity_ctx.get('connections', [])
conn_strs = [f"{c['rel']} -> {c['target_name']}" for c in connections[:5]]
return (
f"Entity: {entity_ctx['name']} ({', '.join(entity_ctx['labels'])})\n"
f"Properties: {props}\n"
f"Relationships: {'; '.join(conn_strs)}"
)ขั้นตอนที่ 3: การค้นหาเวกเตอร์
เรียกใช้การค้นหาเวกเตอร์ด้วยคำถามต้นฉบับเพื่อค้นหาเอกสารในฐานความรู้ที่เกี่ยวข้องเชิงความหมายมากที่สุด เอกสารเหล่านี้จะเป็นหลักฐานสนับสนุนคำตอบ
import chromadb
import openai
client = openai.OpenAI(api_key='sk-...')
chroma_client = chromadb.Client()
collection = chroma_client.get_or_create_collection('knowledge_base')
def vector_search(query: str, top_k: int = 5) -> list:
response = client.embeddings.create(
model='text-embedding-3-small',
input=query
)
query_embedding = response.data[0].embedding
results = collection.query(
query_embeddings=[query_embedding],
n_results=top_k,
include=['documents', 'metadatas', 'distances']
)
documents = []
for i in range(len(results['ids'][0])):
documents.append({
'text': results['documents'][0][i],
'metadata': results['metadatas'][0][i],
'distance': results['distances'][0][i],
'relevance': 1 - results['distances'][0][i] # Convert distance to similarity
})
return documents
print('Vector search function defined')ขั้นตอนที่ 4: การรวมบริบท
รวมบริบทจากกราฟและเอกสารเวกเตอร์ไว้ในสตริงบริบทเดียวที่มีโครงสร้างชัดเจน ลำดับมีความสำคัญ: ให้ข้อเท็จจริงจากกราฟอยู่ก่อน (ความแม่นยำสูง) แล้วจึงตามด้วยเอกสารเวกเตอร์ (ครอบคลุมกว้าง)
def combine_context(question: str, graph_contexts: dict, vector_docs: list) -> str:
sections = []
# Graph facts section
if graph_contexts:
graph_parts = ['### Knowledge Graph Facts']
for entity_name, ctx in graph_contexts.items():
graph_parts.append(format_entity_context_for_llm(ctx))
sections.append('\n'.join(graph_parts))
# Vector documents section
if vector_docs:
doc_parts = ['### Relevant Documents']
for i, doc in enumerate(vector_docs[:4]):
title = doc.get('metadata', {}).get('title', f'Document {i+1}')
text = doc['text'][:800] # Limit per document
relevance = doc.get('relevance', 0)
doc_parts.append(f'**{title}** (relevance: {relevance:.2f})\n{text}')
sections.append('\n'.join(doc_parts))
context = '\n\n'.join(sections)
# Total context budget: ~8000 tokens ~ 32000 chars
if len(context) > 32000:
context = context[:32000]
return contextขั้นตอนที่ 5: การสร้างคำตอบด้วย LLM
ส่งบริบทที่รวมแล้วให้ LLM ในรูปข้อความระบบหรือบริบทของผู้ใช้ กำชับให้ใช้ข้อมูลที่ให้ไว้ และระบุว่าแต่ละข้อเท็จจริงมาจากแหล่งใด
import openai
client = openai.OpenAI(api_key='sk-...')
def generate_answer(question: str, combined_context: str) -> str:
system_prompt = (
'You are a knowledgeable assistant. Answer the question using ONLY the provided context. '
'Cite your sources by mentioning whether a fact came from the knowledge graph or a specific document. '
'If the context does not contain enough information, say so clearly.'
)
user_message = (
f'Context:\n{combined_context}\n\n'
f'Question: {question}\n\n'
'Please answer based on the context above.'
)
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_message}
],
temperature=0.1 # Low temperature for factual answers
)
return response.choices[0].message.contentตัวประสานงานของตัวแทนแบบครบถ้วน
ฟังก์ชันประสานงานจะเชื่อมโยงทุกขั้นตอนเข้าด้วยกัน โดยรับคำถาม เรียกใช้กระบวนการทำงานทั้งหมด และส่งคืนผลลัพธ์ที่มีโครงสร้างซึ่งประกอบด้วยคำตอบและบริบทที่ใช้
async def knowledge_augmented_agent(question: str) -> RetrievalContext:
ctx = RetrievalContext(question=question)
# Step 1: Extract entities
ctx.entities = extract_question_entities(question)
print(f'Entities: {ctx.entities}')
# Steps 2 & 3: Graph + Vector in parallel
import asyncio
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=4)
loop = asyncio.get_event_loop()
async def graph_step():
contexts = {}
for entity in ctx.entities:
context = await loop.run_in_executor(executor, get_rich_entity_context, entity)
if context.get('found'):
contexts[entity] = context
return contexts
async def vector_step():
return await loop.run_in_executor(executor, vector_search, question, 5)
ctx.graph_context, ctx.vector_documents = await asyncio.gather(
graph_step(), vector_step()
)
# Step 4: Combine
ctx.combined_context = combine_context(
question, ctx.graph_context, ctx.vector_documents
)
# Step 5: Generate answer
ctx.answer = generate_answer(question, ctx.combined_context)
return ctxการจัดการเมื่อไม่พบข้อมูลการค้นคืน
เมื่อฐานความรู้ไม่มีข้อมูลที่เกี่ยวข้อง ตัวแทนควรบอกอย่างชัดเจนแทนการสร้างคำตอบที่ไม่มีมูล ตรวจสอบว่าการค้นคืนส่งคืนผลลัพธ์ที่มีประโยชน์หรือไม่ก่อนเรียกใช้ LLM
def check_retrieval_quality(graph_contexts: dict, vector_docs: list, threshold: float = 0.7) -> dict:
has_graph = len(graph_contexts) > 0
# Filter vector docs below relevance threshold
high_quality_docs = [d for d in vector_docs if d.get('relevance', 0) >= threshold]
return {
'has_graph_context': has_graph,
'graph_entity_count': len(graph_contexts),
'vector_doc_count': len(high_quality_docs),
'retrieval_quality': 'high' if (has_graph or len(high_quality_docs) >= 2) else 'low',
'usable_docs': high_quality_docs
}
def answer_with_fallback(question: str, graph_contexts: dict, vector_docs: list) -> str:
quality = check_retrieval_quality(graph_contexts, vector_docs)
if quality['retrieval_quality'] == 'low':
return (
f'I don\'t have enough information in my knowledge base to answer '
f'"{question}" confidently. '
'Please ensure relevant documents are indexed or the knowledge graph '
'contains the required entities.'
)
context = combine_context(question, graph_contexts, quality['usable_docs'])
return generate_answer(question, context)การแคชผลลัพธ์การค้นคืน
แคชการค้นหาเอนทิตีและผลลัพธ์การค้นหาเวกเตอร์เพื่อหลีกเลี่ยงการเรียก API ที่มีค่าใช้จ่ายสูงซ้ำสำหรับคำถามที่คล้ายกัน ใช้ TTL เพื่อให้มีการปรับปรุงข้อมูลเก่าตามระยะเวลา
import hashlib
import json
from datetime import datetime, timedelta
class RetrievalCache:
def __init__(self, ttl_minutes: int = 60):
self.cache = {}
self.ttl = timedelta(minutes=ttl_minutes)
def _key(self, namespace: str, value: str) -> str:
return hashlib.md5(f'{namespace}:{value}'.encode()).hexdigest()
def get(self, namespace: str, value: str):
key = self._key(namespace, value)
entry = self.cache.get(key)
if entry and datetime.now() - entry['ts'] < self.ttl:
return entry['data']
return None
def set(self, namespace: str, value: str, data):
key = self._key(namespace, value)
self.cache[key] = {'data': data, 'ts': datetime.now()}
cache = RetrievalCache(ttl_minutes=30)
def cached_graph_lookup(entity: str) -> dict:
cached = cache.get('graph', entity)
if cached:
print(f'Cache hit for entity: {entity}')
return cached
result = get_rich_entity_context(entity)
cache.set('graph', entity, result)
return result
print('Retrieval cache initialized')การบันทึกข้อมูลและการตรวจสอบการทำงาน
บันทึกทุกขั้นตอนของการค้นคืนเพื่อให้คุณวินิจฉัยได้ว่าเหตุใดคำตอบจึงดีหรือไม่ดี บันทึกว่าเอนทิตีใดถูกค้นพบ มีการค้นคืนเอกสารกี่ฉบับ คะแนนความเกี่ยวข้องของเอกสาร และคำตอบสุดท้าย
import logging
import json
from datetime import datetime
logger = logging.getLogger('ka_agent')
def log_agent_run(ctx: 'RetrievalContext', duration_ms: float):
logger.info(json.dumps({
'timestamp': datetime.utcnow().isoformat(),
'question': ctx.question,
'entities_found': ctx.entities,
'graph_entities_resolved': list(ctx.graph_context.keys()),
'vector_docs_retrieved': len(ctx.vector_documents),
'vector_doc_relevances': [
round(d.get('relevance', 0), 3)
for d in ctx.vector_documents
],
'context_length_chars': len(ctx.combined_context),
'answer_length_chars': len(ctx.answer),
'duration_ms': round(duration_ms, 1)
}))
print('Observability logging configured')แบบทดสอบความรู้: เอเจนต์เสริมความรู้
ทดสอบความเข้าใจเกี่ยวกับการสร้างเอเจนต์เสริมความรู้
สรุปเอเจนต์เสริมความรู้
เอเจนต์เสริมความรู้ผสานการสกัดเอนทิตี การสำรวจกราฟ และการค้นหาเวกเตอร์ไว้ใน pipeline ที่มอบบริบทอันมีรายละเอียดและมีหลักฐานรองรับให้แก่ LLM ผลลัพธ์คือคำตอบที่แม่นยำยิ่งขึ้นและเกิดภาพหลอนน้อยลง โดยมีข้อมูลจริงจากฐานความรู้ของคุณรองรับ ส่วนเพิ่มเติมที่สำคัญ ได้แก่ การแคช การจัดการกรณีการค้นคืนไม่พบผลลัพธ์ และการบันทึกแบบมีโครงสร้างเพื่อให้ตรวจสอบการทำงานได้
คำถามที่พบบ่อย
บทเรียน “การสร้างเอเจนต์เสริมความรู้” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การสร้างเอเจนต์เสริมความรู้” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การสร้างเอเจนต์เสริมความรู้”
ตั้งแต่ต้นจนจบ: การเชื่อมโยงเอนทิตี → การสืบค้นกราฟ → การสังเคราะห์คำตอบ คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การสร้างเอเจนต์เสริมความรู้” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม
ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การสกัดเอนทิตีสำหรับกราฟความรู้
- การสืบค้น Neo4j จากเครื่องมือของเอเจนต์
- การผสานการค้นคืนแบบเวกเตอร์และกราฟ
- การสร้างเอเจนต์เสริมความรู้