เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก
ทำความเข้าใจว่าเหตุใดการบันทึกข้อมูลแบบดั้งเดิมจึงไม่เพียงพอสำหรับแอปพลิเคชัน LLM ข้อมูลใดที่จำเป็นต่อการวินิจฉัยความล้มเหลวในกระบวนการ RAG และเอเจนต์ และรูปแบบข้อมูลการติดตามเป็นอย่างไร
เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
ความท้าทายเฉพาะของการแก้ไขข้อบกพร่อง LLM
ซอฟต์แวร์แบบดั้งเดิมทำงานล้มเหลวอย่างเป็นแบบแผน กล่าวคือ เมื่อได้รับข้อมูลนำเข้าเดียวกัน ก็จะสร้างผลลัพธ์เดิมเสมอ และการติดตามสแตกจะชี้ไปยังบรรทัดที่ล้มเหลวโดยตรง แต่แอปพลิเคชัน LLM ละเมิดข้อสมมติเหล่านี้ พรอมต์เดียวกันอาจสร้างผลลัพธ์ต่างกันในการเรียกแต่ละครั้ง ความล้มเหลวมักเกิดขึ้นโดยไม่ส่งสัญญาณใด ๆ (ได้คำตอบผิดแทนที่จะเกิดข้อยกเว้น) และสาเหตุอาจซ่อนอยู่ในพรอมต์ที่ใช้ก่อนหน้านั้น 5 ขั้นตอนในห่วงโซ่ เครื่องมือการบันทึกและการแก้ไขข้อบกพร่องมาตรฐานจึงไม่ได้ออกแบบมาสำหรับกรณีนี้
ความไม่เป็นแบบกำหนดทำให้การจำลองปัญหาทำได้ยาก
โดยค่าเริ่มต้น ผลลัพธ์ของ LLM ไม่เป็นแบบกำหนด แม้ตั้งค่า temperature=0 พรอมต์เดียวกันก็อาจสร้างผลลัพธ์ที่แตกต่างกันเล็กน้อย เนื่องจากการประมวลผลเป็นชุดและความแม่นยำทางตัวเลข ซึ่งหมายความว่าข้อบกพร่องจะเกิดขึ้นเป็นครั้งคราว: พรอมต์ที่ล้มเหลว 20% ของเวลาอาจผ่านชุดการทดสอบของคุณได้ หากคุณเรียกใช้เพียงครั้งเดียว การจำลองความล้มเหลวเฉพาะครั้งจำเป็นต้องบันทึกข้อมูลนำเข้าที่แน่นอน พารามิเตอร์ของโมเดล และผลลัพธ์ ณ เวลาที่เกิดความล้มเหลว ไม่ใช่บันทึกเพียงข้อมูลนำเข้าเท่านั้น
import json
import time
def logged_llm_call(client, messages, model, temperature, **kwargs):
request_id = f'{int(time.time() * 1000)}-{id(messages)}'
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
**kwargs
)
# Log EVERYTHING needed to reproduce this exact call
log_entry = {
'request_id': request_id,
'model': model,
'temperature': temperature,
'messages': messages,
'response': response.choices[0].message.content,
'finish_reason': response.choices[0].finish_reason,
'usage': response.usage.model_dump(),
'timestamp': time.time()
}
write_to_trace_store(log_entry)
return responseความล้มเหลวที่ไม่ส่งสัญญาณ: ผิดแต่ไม่เสีย
ความล้มเหลวของ LLM ที่ตรวจจับได้ยากที่สุดคือ ความล้มเหลวที่ไม่ส่งสัญญาณ: การเรียก API สำเร็จ (HTTP 200 และไม่มีข้อยกเว้น) แต่คำตอบผิด แต่งขึ้น ไม่ครบถ้วน หรือไม่ตรงประเด็น แอปพลิเคชันของคุณจะประมวลผลคำตอบผิดนั้นต่ออย่างราบรื่นและส่งกลับให้ผู้ใช้ โดยไม่มีข้อบ่งชี้ว่ามีสิ่งใดล้มเหลว การตรวจสอบระบบแบบดั้งเดิมที่เฝ้าดูเฉพาะข้อยกเว้นและรหัสข้อผิดพลาดจะไม่พบปัญหาเหล่านี้ คุณจึงต้องตรวจสอบคุณภาพของผลลัพธ์ในเชิงความหมาย
# This succeeds with HTTP 200 but returns wrong information
response = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': 'What is the boiling point of water at sea level?'}]
)
output = response.choices[0].message.content
# response.status_code: None (not relevant - always 200 if we got here)
# No exception thrown
# But if output is '90 degrees Celsius', it is WRONG and your app will serve bad data
# You need semantic validation:
def validate_boiling_point_answer(text: str) -> bool:
return '100' in text # Rough check - real validation is more sophisticatedห่วงโซ่หลายขั้นตอน: ผิดพลาดที่จุดใด
ในกระบวนการ RAG หรือห่วงโซ่ของเอเจนต์ การล้มเหลวของการตอบกลับสุดท้ายอาจย้อนกลับไปถึงขั้นตอนการดึงข้อมูลที่ส่งคืนส่วนข้อมูลไม่เกี่ยวข้อง ซึ่งสาเหตุของขั้นตอนนั้นอาจย้อนกลับไปถึงกลยุทธ์การแบ่งส่วนที่แยกประโยคสำคัญออกเป็นสองส่วน และสาเหตุของเรื่องนี้อาจย้อนกลับไปถึงโมเดลการฝังเวกเตอร์ที่จัดการคำศัพท์ทางเทคนิคได้ไม่ดี หากไม่มี การติดตามร่องรอยแยกตามแต่ละขั้นตอน คุณจะเห็นเพียงคำตอบสุดท้ายที่ผิด โดยไม่มีทางระบุได้ว่าขั้นตอนไหนเป็นผู้สร้างข้อผิดพลาด
# Without tracing: you see only the final wrong answer
def rag_pipeline_naive(query):
chunks = retrieve(query) # step 1 - might return bad chunks
context = format_context(chunks) # step 2 - might truncate key info
answer = generate(query, context) # step 3 - LLM gets bad context
return answer # WRONG - but why?
# With tracing: you can see each step's input and output
def rag_pipeline_traced(query):
with trace_span('retrieve') as span:
chunks = retrieve(query)
span.set_attribute('num_chunks', len(chunks))
span.set_attribute('top_chunk_score', chunks[0]['score'] if chunks else 0)
with trace_span('format_context') as span:
context = format_context(chunks)
span.set_attribute('context_length', len(context))
with trace_span('generate') as span:
answer = generate(query, context)
span.set_attribute('answer_length', len(answer))
return answer # Now you can diagnose: was retrieve the problem?ความประหลาดใจจากจำนวนโทเค็นและค่าใช้จ่าย
หากไม่มีการวัดและบันทึกข้อมูล จำนวนโทเค็นและค่าใช้จ่ายจะมองไม่เห็นจนกว่าจะได้รับใบเรียกเก็บเงินรายเดือน พรอมต์ระบบที่ขยายจาก 500 เป็น 5,000 โทเค็นเนื่องจากการมองข้าม ฟังก์ชันดึงข้อมูลที่ส่งคืน 20 ส่วนแทนที่จะเป็น 5 ส่วน หรือห่วงโซ่ที่เรียกใช้ LLM 100 ครั้งแทนที่จะเป็น 10 ครั้ง ล้วนทำให้ค่าใช้จ่ายเพิ่มขึ้นหลายเท่าโดยไม่มีสัญญาณใด ๆ ให้วัดและบันทึกการเรียกใช้ LLM ทุกครั้ง เพื่อบันทึกจำนวนโทเค็นของพรอมต์ จำนวนโทเค็นของผลลัพธ์ และค่าใช้จ่ายโดยประมาณ ทำให้มองเห็นความผิดปกติได้แบบเรียลไทม์
COST_PER_1K = {'gpt-4o': {'input': 0.005, 'output': 0.015},
'gpt-4o-mini': {'input': 0.000150, 'output': 0.000600}}
def compute_cost(model: str, usage) -> float:
pricing = COST_PER_1K.get(model, {'input': 0.005, 'output': 0.015})
input_cost = (usage.prompt_tokens / 1000) * pricing['input']
output_cost = (usage.completion_tokens / 1000) * pricing['output']
return input_cost + output_cost
def instrumented_call(client, model, messages):
response = client.chat.completions.create(model=model, messages=messages)
cost = compute_cost(model, response.usage)
# Alert if single call is unexpectedly expensive
if cost > 0.10: # more than 10 cents for one call
print(f'WARNING: Expensive LLM call: ${cost:.4f} ({response.usage.prompt_tokens} prompt tokens)')
metrics.record('llm_cost_usd', cost, tags={'model': model})
metrics.record('llm_prompt_tokens', response.usage.prompt_tokens)
return responseเวลาแฝง: ขั้นตอนไหนช้า
ผู้ใช้รับรู้เวลาแฝงของ LLM เป็นเวลารอเพียงก้อนเดียว แต่ในความเป็นจริงเวลานี้เป็นผลรวมของหลายขั้นตอน ได้แก่ การค้นหาฐานข้อมูลเวกเตอร์ การดึงเอกสาร การประกอบพรอมต์ การเรียก API ผ่านเครือข่าย การสร้างโทเค็น และการแยกวิเคราะห์ผลตอบกลับ หากไม่มีการจับเวลาแยกตามแต่ละขั้นตอน คุณจะไม่ทราบว่าการตอบกลับที่ช้าเกิดจากตัวดึงข้อมูลที่ช้าหรือการเรียกใช้ LLM ที่ช้า ให้วัดเวลาแฝงของแต่ละขั้นตอนเพื่อระบุคอขวดที่แท้จริง
import time
from contextlib import contextmanager
@contextmanager
def timed(name: str, metrics_client):
start = time.monotonic()
try:
yield
finally:
elapsed_ms = (time.monotonic() - start) * 1000
metrics_client.histogram(f'step_latency_ms', elapsed_ms, tags={'step': name})
if elapsed_ms > 2000: # flag steps taking more than 2 seconds
print(f'SLOW STEP [{name}]: {elapsed_ms:.0f}ms')
# Usage
def rag_with_timing(query, metrics):
with timed('embed_query', metrics):
query_embedding = embed(query)
with timed('vector_search', metrics):
chunks = vector_db.search(query_embedding, top_k=5)
with timed('llm_generate', metrics):
answer = generate(query, chunks)
return answerข้อมูลที่จำเป็นจริง ๆ
ในการวินิจฉัยความล้มเหลวของแอปพลิเคชัน LLM คุณต้องเก็บและจัดเก็บข้อมูลต่อไปนี้: พรอมต์ข้อมูลนำเข้าทั้งหมด (ระบบและข้อความทั้งหมด), โมเดลและพารามิเตอร์ที่ใช้ (temperature, max_tokens), ผลลัพธ์ทั้งหมด, จำนวนโทเค็นและค่าใช้จ่ายโดยประมาณ, เวลาแฝงของแต่ละขั้นตอน, การเรียกใช้เครื่องมือและผลลัพธ์ของเครื่องมือ และ รหัสเซสชันหรือรหัสคำขอที่เชื่อมโยงทุกขั้นตอนของคำขอผู้ใช้หนึ่งรายการเข้าด้วยกัน นี่คือชุดข้อมูลการติดตามร่องรอยขั้นต่ำที่ใช้งานได้
from dataclasses import dataclass, field
from typing import Optional
import time
@dataclass
class LLMTrace:
request_id: str
session_id: str
step_name: str
model: str
temperature: float
system_prompt: str
user_messages: list[dict]
response: str
finish_reason: str
prompt_tokens: int
completion_tokens: int
cost_usd: float
latency_ms: float
tool_calls: list[dict] = field(default_factory=list)
error: Optional[str] = None
timestamp: float = field(default_factory=time.time)
def is_anomalous(self) -> bool:
return (
self.cost_usd > 0.10 or
self.latency_ms > 10000 or
self.finish_reason == 'length' or # was cut off
self.error is not None
)การเชื่อมโยงร่องรอยด้วยรหัสคำขอ
คำขอผู้ใช้หนึ่งรายการอาจกระตุ้นการเรียกใช้ LLM 10 ครั้งผ่านบริการต่าง ๆ หากไม่มี รหัสเชื่อมโยงที่ส่งต่อไปตลอดทุกขั้นตอน คุณจะไม่สามารถจัดกลุ่มการเรียกเหล่านี้ให้เป็นร่องรอยเดียวกันได้ ให้สร้างรหัสคำขอที่ไม่ซ้ำกันที่จุดเริ่มต้นของคำขอผู้ใช้ทุกครั้ง แล้วส่งรหัสดังกล่าวไปกับการเรียกใช้ LLM การค้นหาฐานข้อมูล และข้อความบันทึกทุกครั้ง วิธีนี้ทำให้สามารถสร้างเส้นทางการทำงานทั้งหมดของคำขอผู้ใช้เฉพาะรายการใด ๆ ขึ้นใหม่ได้
import uuid
from contextvars import ContextVar
# Thread-safe request ID propagation using context variables
request_id_var: ContextVar[str] = ContextVar('request_id', default='unknown')
def handle_user_request(query: str):
# Set request ID at the entry point
req_id = str(uuid.uuid4())[:8]
request_id_var.set(req_id)
return rag_pipeline(query)
def get_current_request_id() -> str:
return request_id_var.get()
# Every LLM call logs with the same request_id
def log_llm_call(model, prompt, response):
logger.info('LLM call', extra={
'request_id': get_current_request_id(), # automatically correlates all calls
'model': model,
'prompt_length': len(prompt),
'response_length': len(response)
})องค์ประกอบการสังเกตการณ์ LLM
องค์ประกอบการสังเกตการณ์ LLM มีสามชั้น: การบันทึกเก็บระเบียนที่มีโครงสร้างของการเรียกใช้ LLM ทุกครั้ง (LangSmith, Langfuse และบันทึกแบบกำหนดเอง) เมตริกติดตามตัวเลขรวมตามช่วงเวลา ได้แก่ จำนวนคำขอ เวลาแฝงเฉลี่ย อัตราข้อผิดพลาด และค่าใช้จ่ายรายวัน การติดตามร่องรอยบันทึกห่วงโซ่เหตุและผลของขั้นตอนต่าง ๆ ภายในคำขอเดียวกัน เสาหลักทั้งสามนี้ช่วยให้คุณมองเห็นระบบเพียงพอที่จะวินิจฉัยความล้มเหลว ตรวจจับการถดถอย และปรับปรุงประสิทธิภาพ
การแจ้งเตือนเมื่อคุณภาพลดลง
ต่างจากซอฟต์แวร์แบบดั้งเดิมที่ข้อผิดพลาดเป็นแบบทวิภาค (ทำงาน/เสีย) คุณภาพของ LLM จะค่อย ๆ ลดลง การเปลี่ยนพรอมต์อาจทำให้คุณภาพคำตอบลดจาก 85% เหลือ 70% โดยไม่มีข้อยกเว้นเกิดขึ้น ให้ตรวจสอบคุณภาพด้วยการประเมินอัตโนมัติ (การให้คะแนนโดย LLM ในฐานะผู้ตัดสิน) กับตัวอย่างคำตอบจากระบบจริงทุกวัน แจ้งเตือนเมื่อคะแนนคุณภาพเฉลี่ยแบบเลื่อนลดลงต่ำกว่าเกณฑ์ ก่อนที่ผู้ใช้จะเริ่มร้องเรียน
เริ่มต้นแนวปฏิบัติด้านการสังเกตการณ์ระบบ
อย่ารอให้เกิดเหตุขัดข้องในระบบจริงก่อนจึงเพิ่มการสังเกตการณ์ระบบ ให้เริ่มด้วยสามขั้นตอนพื้นฐาน: (1) บันทึกการเรียกใช้ LLM ทุกครั้งพร้อมข้อมูลนำเข้า ผลลัพธ์ และจำนวนโทเค็นทั้งหมดลงในฐานข้อมูลหรือไฟล์ (2) กำหนดรหัสคำขอให้กับการโต้ตอบของผู้ใช้ทุกครั้งและใส่รหัสดังกล่าวในรายการบันทึกทั้งหมด และ (3) เพิ่มการจับเวลาแยกตามแต่ละขั้นตอนในกระบวนการของคุณ เพียงสามสิ่งนี้ก็ช่วยให้งานแก้ไขข้อบกพร่อง 80% แก้ได้ภายในไม่กี่นาทีแทนที่จะเป็นหลายชั่วโมง
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับสาเหตุที่แอปพลิเคชัน LLM แก้ไขข้อบกพร่องได้ยากจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า ความไม่เป็นแบบกำหนดทำให้ข้อบกพร่องของ LLM เกิดขึ้นเป็นครั้งคราวและจำลองปัญหาได้ยาก หากไม่เก็บบริบทของคำขอทั้งหมด ความล้มเหลวที่ไม่ส่งสัญญาณ (คำตอบผิดจากการเรียก API ที่สำเร็จ) จะหลบเลี่ยงการตรวจสอบข้อผิดพลาดแบบดั้งเดิมและจำเป็นต้องตรวจสอบคุณภาพเชิงความหมาย และ การติดตามร่องรอยแยกตามแต่ละขั้นตอนพร้อมการเชื่อมโยงด้วยรหัสคำขอคือสิ่งขั้นต่ำที่จำเป็นต่อการวินิจฉัยความล้มเหลวในกระบวนการ RAG และกระบวนการของเอเจนต์แบบหลายขั้นตอน บทถัดไป เราจะนำการติดตามร่องรอยมาใช้งานด้วย LangSmith
เรียนรู้ Python ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก”
ทำความเข้าใจว่าเหตุใดการบันทึกข้อมูลแบบดั้งเดิมจึงไม่เพียงพอสำหรับแอปพลิเคชัน LLM ข้อมูลใดที่จำเป็นต่อการวินิจฉัยความล้มเหลวในกระบวนการ RAG และเอเจนต์ และรูปแบบข้อมูลการติดตามเป็นอย่างไร คุณปฏิบัติ AI Engineering Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Engineering Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม
ได้ บทเรียน AI Engineering Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดแอป LLM จึงแก้ไขข้อผิดพลาดได้ยาก
- การติดตามด้วย LangSmith
- Langfuse สำหรับการสังเกตการณ์ที่ไม่ขึ้นกับโมเดล
- การแจ้งเตือนเวลาแฝง ต้นทุน และคุณภาพที่ลดลง