Điểm khác biệt lớn nhất giữa một QA senior dùng AI để điều tra bug trong 5 phút và một người ngồi đợi AI "đoán mò" suốt 30 phút không nằm ở việc ai giỏi viết prompt hơn — mà nằm ở việc ai biết cách chọn đúng log, cắt đúng đoạn, và trình bày đúng cấu trúc để đưa cho AI.
Log, trace (dấu vết thực thi phân tán), và bối cảnh tái hiện lỗi (reproduction context) là "nguyên liệu thô" cho mọi phân tích bug bằng AI. Nhưng nguyên liệu thô không đồng nghĩa với việc cứ đổ hết vào là AI làm việc tốt. LLM (Large Language Model — mô hình ngôn ngữ lớn) có context window (không gian ngữ cảnh — số token tối đa nó xử lý được trong một lượt) hữu hạn, và quan trọng hơn: khả năng suy luận của nó tỷ lệ nghịch với lượng nhiễu (noise) trong dữ liệu bạn đưa vào. Bài này sẽ đi từ cách chọn lọc log, định dạng chúng thành evidence block (khối bằng chứng) mà AI đọc hiệu quả, đến cách tóm tắt distributed trace và kết hợp nhiều nguồn bằng chứng vào một prompt hành động được. Toàn bộ nội dung đi kèm ví dụ prompt thực tế bạn có thể áp dụng ngay với một tình huống lỗi thanh toán (payments) cụ thể.
Nên đưa log nào vào prompt phân tích bug bằng AI — và cắt gọt thế nào?
Sai lầm phổ biến nhất khi QA bắt đầu dùng AI để phân tích bug là tư duy "cho nhiều dữ liệu thì AI phân tích tốt hơn". Thực tế ngược lại: log dư thừa làm loãng tín hiệu (signal), khiến AI phải "lọc" giữa hàng nghìn dòng vô nghĩa để tìm ra vài dòng thật sự liên quan — và nó có thể lọc sai, hoặc tệ hơn, bị cắt cụt log giữa chừng vì vượt context window trước khi chạm tới đoạn quan trọng nhất.
Ba nguyên tắc dưới đây giúp bạn xác định "vừa đủ" log cần đưa vào.
"Minimum log volume"
Nguyên tắc đầu tiên: chỉ đưa lượng log tối thiểu cần thiết để AI xác nhận hoặc loại trừ một giả thuyết cụ thể — không đưa "tất cả log có thể liên quan". Trước khi copy-paste, tự hỏi: "Nếu tôi chỉ có 50 dòng, 50 dòng nào sẽ giúp tôi trả lời câu hỏi đang điều tra?". Với một lỗi timeout thanh toán, 50 dòng đó thường là: log ngay trước khi request bắt đầu, log tại điểm request bị treo/lỗi, và vài dòng log INFO xung quanh cho ngữ cảnh — không phải toàn bộ access log của service trong 2 giờ.
Cách áp dụng thực tế: dùng grep, jq, hoặc query trên Datadog/CloudWatch/Kibana để lọc theo request_id, trace_id, hoặc correlation_id trước khi đưa cho AI. Một request_id duy nhất xuyên suốt log của nhiều service là "sợi chỉ" giúp bạn cắt đúng lát log cần thiết, thay vì đưa nguyên khối log của cả service.
"Full execution context of the failing operation"
Nguyên tắc thứ hai tưởng như mâu thuẫn với nguyên tắc trên, nhưng thực ra bổ trợ: trong phạm vi request/operation đang lỗi, phải lấy đầy đủ, không cắt xén nửa vời. Nghĩa là: nếu bạn đã xác định request_id req-8821 là request lỗi, hãy lấy toàn bộ log của request đó — từ lúc vào entry point tới lúc kết thúc (thành công hoặc lỗi) — xuyên suốt mọi service nó đi qua, không chỉ lấy dòng log ERROR cuối cùng.
Lý do: root cause thường nằm ở một dòng INFO hoặc DEBUG "vô hại" trước dòng ERROR vài bước, ví dụ dòng log ghi nhận một giá trị cấu hình bất thường, hoặc một retry đã âm thầm xảy ra trước khi lỗi cuối cùng lộ ra. Nếu bạn chỉ đưa dòng ERROR, AI sẽ suy luận trên phần ngọn của vấn đề chứ không thấy được phần gốc.
"Starting from the last known-good state"
Nguyên tắc thứ ba: mốc thời gian bắt đầu của đoạn log nên là thời điểm cuối cùng hệ thống còn hoạt động bình thường, không phải thời điểm lỗi xuất hiện. Nếu lỗi xảy ra lúc 14:23:10, đừng cắt log bắt đầu từ 14:23:05 — hãy lùi về mốc mà bạn biết chắc hệ thống vẫn khỏe, ví dụ request thành công gần nhất trước đó, hoặc mốc deploy/thay đổi config gần nhất. Khoảng đệm này giúp AI thấy được "điểm chuyển pha" từ trạng thái tốt sang trạng thái lỗi, một tín hiệu suy luận cực kỳ mạnh mà log chỉ chụp đúng lúc lỗi không bao giờ cho thấy được.
Log trimming procedure
Kết hợp ba nguyên tắc trên thành một quy trình cắt gọt log (log trimming) 5 bước bạn có thể lặp lại cho mọi bug:
- Xác định anchor (điểm mốc): request_id, trace_id, user_id, hoặc timestamp lỗi được báo cáo.
- Xác định time window: lùi về mốc "last known-good" (thường 5-15 phút trước lỗi, hoặc trước request thành công gần nhất cùng loại), kéo tới khi lỗi kết thúc/được retry xong.
- Lọc theo anchor trên toàn bộ service liên quan trong window đó — dùng
grep <request_id>hoặc query filter trên công cụ log tập trung. - Tóm tắt phần lặp lại: nếu có log lặp (ví dụ retry loop in ra 400 dòng giống nhau), nén thành một dòng dạng
[LOG PATTERN] "Connection timeout" repeated 400x between 14:22:58–14:23:40. - Giữ nguyên vẹn phần độc nhất: mọi dòng log khác nhau về nội dung (không lặp) trong window đã lọc — không tóm tắt tiếp, vì đây thường là nơi chứa manh mối.
Mẹo: Trước khi paste bất kỳ log nào vào AI, tự trả lời câu hỏi "Log này giúp tôi xác nhận hay loại trừ giả thuyết gì?" — nếu không trả lời được, đừng đưa vào; nó chỉ tốn context window và làm loãng tín hiệu thật.
Application Logs
Application log (log ứng dụng) là lớp log gần nhất với business logic — nơi bạn thấy được request được xử lý ra sao, exception nào được throw, biến nào có giá trị bất thường. Đây thường là lớp bằng chứng đầu tiên và quan trọng nhất trong bất kỳ điều tra bug nào, vì nó nói "ngôn ngữ" của code, không phải "ngôn ngữ" của hạ tầng.
Khi thu thập application log, ưu tiên theo thứ tự: (1) exception/stack trace đầy đủ (không cắt), (2) log ERROR/WARN trong window đã xác định, (3) log INFO đánh dấu các bước quan trọng của luồng nghiệp vụ (ví dụ "payment authorized", "webhook received"), (4) log DEBUG chỉ khi hai lớp trên chưa đủ để xác nhận giả thuyết.
Một lỗi thường gặp: QA chỉ copy dòng có chữ "ERROR" mà bỏ qua log của service gọi đến hoặc bị gọi. Với hệ thống microservices, application log của một service riêng lẻ hiếm khi kể hết câu chuyện — bạn cần log của ít nhất service gây lỗi và service gọi trực tiếp nó.
Mẹo: Nếu framework của bạn hỗ trợ structured logging (log dạng JSON có field cố định thay vì chuỗi text tự do), luôn lọc theo field level và request_id bằng công cụ (jq, Kibana query) trước khi copy — đừng đọc bằng mắt và chọn tay, vì bạn sẽ dễ bỏ sót dòng WARN "vô hại" nhưng lại là manh mối quan trọng nhất.
Làm sao định dạng log context để AI đọc mà không lãng phí context window?
Định dạng (formatting) quyết định AI "tiêu hóa" dữ liệu nhanh hay chậm, đúng hay sai — không kém gì nội dung log. Một log block không có cấu trúc buộc AI phải tự phân tách (parse) trong đầu, tốn token suy luận và dễ nhầm lẫn ranh giới giữa các nguồn log khác nhau. Ngược lại, một log block có header rõ ràng, timestamp thống nhất, và annotation (chú thích) tại điểm quan trọng giúp AI "nhìn" ra pattern gần như ngay lập tức — giống cách một QA senior lướt log cũng tìm ranh giới và điểm bất thường trước khi đọc kỹ.
Use header labels for each log section
Mỗi khối log nên có một header rõ ràng nêu: nguồn (service/component nào), loại log (application/database/access/gateway), và time window. Không có header, AI (và cả người review sau này) phải đoán dòng log này từ đâu ra — đặc biệt nguy hiểm khi bạn ghép log của nhiều service có timestamp gần giống nhau.
Format header khuyến nghị:
[N] <Loại log> — <tên service> (<time window>, <timezone>)
Ví dụ: [1] Application Log — payments-service (2024-03-15 14:22:55 – 14:23:50 UTC). Đánh số [N] giúp bạn tham chiếu ngược lại nguồn cụ thể trong phần hỏi AI ("dựa vào [1] và [3], ...") mà không cần lặp lại toàn bộ log.
Mẹo: Luôn ghi rõ timezone trong mọi header, kể cả khi tất cả log cùng timezone — đây là nguyên nhân số một khiến AI (và người) suy luận sai thứ tự sự kiện khi log từ nhiều hệ thống khác nhau (một số dùng UTC, một số dùng local time server).
Evidence Block
"Evidence block" (khối bằng chứng) là cách tổ chức nhiều nguồn log liên quan đến cùng một sự cố thành một khối duy nhất, có đánh số, để đưa vào một prompt. Đây là bước nâng cấp từ "dán log rời rạc" lên "trình bày bằng chứng có cấu trúc" — sự khác biệt quyết định giữa một câu trả lời AI mơ hồ và một câu trả lời AI trích dẫn chính xác dòng nào gây ra vấn đề.
Dưới đây là ví dụ evidence block thực tế cho một bug: thanh toán bị timeout ngẫu nhiên trên payments-service trong giờ cao điểm.
[1] Application Log — payments-service (2024-03-15 14:22:55 – 14:23:50 UTC)
14:22:55.102 INFO [req-88213] PaymentController - received charge request, order_id=ORD-55291, amount=249000, method=CARD
14:22:55.140 INFO [req-88213] PaymentGatewayClient - calling external gateway, endpoint=/v2/charge, timeout=2000ms
14:22:57.145 WARN [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, retrying (attempt 1/3)
14:22:57.150 INFO [req-88213] PaymentGatewayClient - calling external gateway, endpoint=/v2/charge, timeout=2000ms
14:22:59.155 WARN [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, retrying (attempt 2/3)
14:22:59.160 INFO [req-88213] PaymentGatewayClient - calling external gateway, endpoint=/v2/charge, timeout=2000ms
14:23:01.170 ERROR [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, max retries exceeded
14:23:01.175 ERROR [req-88213] PaymentController - charge failed, order_id=ORD-55291, reason=GATEWAY_TIMEOUT
14:23:01.180 WARN [req-88213] ConnectionPoolManager - pool "gateway-http-pool" utilization at 98% (49/50 connections in use)
[2] Database Slow Query Log — payments-db (same time window)
2024-03-15 14:22:56 UTC [duration: 1842ms] SELECT * FROM payment_transactions WHERE order_id = 'ORD-55291' FOR UPDATE
2024-03-15 14:22:58 UTC [duration: 2103ms] SELECT * FROM payment_transactions WHERE order_id = 'ORD-55288' FOR UPDATE
2024-03-15 14:23:00 UTC [duration: 1977ms] SELECT * FROM payment_transactions WHERE order_id = 'ORD-55302' FOR UPDATE
2024-03-15 14:23:02 UTC [lock_wait: 1600ms] UPDATE payment_transactions SET status='FAILED' WHERE order_id = 'ORD-55291'
[3] HTTP Access Log — api-gateway (same time window)
14:22:55 "POST /api/v1/checkout/charge HTTP/1.1" 200 -> upstream: payments-service, upstream_time=6070ms
14:22:56 "POST /api/v1/checkout/charge HTTP/1.1" 200 -> upstream: payments-service, upstream_time=5890ms
14:22:58 "POST /api/v1/checkout/charge HTTP/1.1" 504 -> upstream: payments-service, upstream_time=timeout
14:23:01 "POST /api/v1/checkout/charge HTTP/1.1" 504 -> upstream: payments-service, upstream_time=timeout
Ba khối trên kể một câu chuyện liên kết: request thanh toán bị retry 3 lần vì external gateway timeout, đồng thời database đang có FOR UPDATE lock kéo dài gần 2 giây mỗi query trên bảng payment_transactions — nghĩa là connection pool tới gateway HTTP đang cạn (98%) trong khi database cũng đang bị nghẽn do lock contention. Đây chính là kiểu tương quan (correlation) AI phát hiện rất nhanh nếu bạn trình bày ba nguồn log cạnh nhau với timestamp thống nhất, nhưng dễ bị bỏ sót nếu bạn chỉ đưa một nguồn.
Normalize log timestamps
Trước khi đưa vào prompt, chuẩn hóa toàn bộ timestamp về cùng một format và cùng timezone (khuyến nghị UTC, format ISO 8601 HH:mm:ss.SSS). Nếu một service log theo local time (GMT+7) còn service khác log UTC, hãy tự quy đổi trước — đừng để AI tự đoán, vì nó có thể quy đổi sai lệch múi giờ hoặc bỏ qua chênh lệch, dẫn tới kết luận sai về thứ tự sự kiện nhân quả.
Replace long log prefixes with abbreviations
Nhiều framework log kèm prefix rất dài lặp lại ở mọi dòng (ví dụ 2024-03-15T14:22:55.102+00:00 [payments-service-prod-pod-7f8c9d] [thread-42] [trace_id=a1b2c3d4e5f6...]). Prefix này chiếm rất nhiều token nhưng gần như không đổi giữa các dòng. Hãy rút gọn: nêu rõ trong header rằng "tất cả dòng dưới đây thuộc pod X, trace_id Y", sau đó chỉ giữ phần timestamp ngắn + level + message trong từng dòng, như ví dụ evidence block trên. Việc này có thể giảm 40-60% số token của một đoạn log dài mà không mất thông tin thật sự cần thiết.
Use inline annotations for key events
Khi một dòng log đặc biệt quan trọng, thêm annotation ngay sau dòng đó bằng comment, ví dụ:
14:23:01.180 WARN [req-88213] ConnectionPoolManager - pool "gateway-http-pool" utilization at 98%
Annotation không thay đổi nội dung log gốc (giữ tính trung thực của bằng chứng) nhưng hướng sự chú ý của AI đúng vào điểm bạn nghi ngờ — tương tự cách bạn highlight bằng bút dạ quang khi review log trên màn hình.
Chunk very long evidence into sequential prompts
Khi tổng evidence vượt quá khả năng xử lý hợp lý trong một lượt (ví dụ log trải dài nhiều giờ, nhiều service, hàng chục nghìn dòng dù đã lọc), đừng cố nhồi tất cả vào một prompt duy nhất. Chia theo giai đoạn thời gian hoặc theo tầng hệ thống, đưa từng phần theo thứ tự, và yêu cầu AI giữ lại "working hypothesis" (giả thuyết đang xét) sau mỗi phần trước khi nhận phần tiếp theo.
Ví dụ chia làm 2 phần cho một cửa sổ log dài 40 phút:
Part 1: System Context and Symptom Description
- Symptom: payments-service returns 504 for ~8% of charge requests during peak hour
- Time window under investigation: 14:10 – 14:50 UTC, 2024-03-15
- I will provide evidence in 2 parts due to length. Please build a running
hypothesis after each part and tell me what additional evidence you'd need.
Part 1: Application Logs (first half of relevant window)
[dán log từ 14:10 đến 14:30]
Sau khi AI phản hồi giả thuyết sơ bộ ở phần 1, bạn tiếp tục gửi phần 2 (14:30–14:50) kèm câu "Here is Part 2, please refine or confirm your hypothesis". Cách chunk tuần tự này giữ được chất lượng suy luận ổn định thay vì một prompt khổng lồ khiến AI "lướt qua" phần giữa.
Mẹo: Định dạng evidence block nhất quán (header đánh số, timestamp chuẩn hóa, annotation) không chỉ giúp AI đọc tốt hơn — nó còn trở thành một artifact review được ngay, bạn có thể paste thẳng vào bug ticket hoặc Slack thread mà đồng nghiệp đọc hiểu ngay không cần hỏi lại "log này của service nào vậy?".
Làm sao tóm tắt distributed trace hiệu quả cho AI phân tích?
Distributed trace — dữ liệu từ Jaeger, Zipkin, Datadog APM, hay OpenTelemetry — là nguồn bằng chứng mạnh nhất cho các bug liên quan tới latency và lỗi xảy ra ở hệ thống nhiều service, vì nó cho thấy chính xác request đi qua đâu và tốn bao nhiêu thời gian ở mỗi bước. Nhưng raw trace JSON export ra thường dài hàng nghìn dòng với rất nhiều field metadata (service.version, k8s.pod.name, http.user_agent...) mà AI không cần để suy luận root cause. Dán nguyên JSON vào prompt là cách nhanh nhất để lãng phí 80% context window vào field vô dụng.
Giải pháp là tóm tắt trace thành 3 phần cốt lõi trước khi đưa cho AI: trace summary (tổng quan), critical path waterfall (biểu đồ thác nước đường găng), và error span details (chi tiết span lỗi).
What to extract from a distributed trace
Từ một trace gốc, chỉ trích các field sau cho mỗi span (đơn vị thực thi trong trace, ví dụ một lần gọi service hoặc một query):
span_id(rút gọn, ví dụ chỉ giữ 6 ký tự cuối) vàparent_span_idđể AI dựng lại quan hệ cha-con.service.namevàoperation.name(ví dụpayments-service: POST /v2/charge).duration(thời gian thực thi span đó, tính riêng — không bao gồm span con).status(ok/error) vàerror.messagenếu có.- Tối đa 2-3 tag nghiệp vụ liên quan trực tiếp tới bug (ví dụ
retry.count,db.rows_affected) — bỏ toàn bộ tag hạ tầng không liên quan.
Mẹo: Nếu công cụ trace của bạn (Datadog, Honeycomb, Jaeger UI) có tính năng export "flame graph" hoặc "waterfall view" dạng text/CSV, dùng nó thay vì export raw JSON — hầu hết đã tự động lược field không cần thiết và giữ đúng cấu trúc phân cấp bạn cần.
Trace Summary
Ví dụ trace summary cho một request thanh toán chậm bất thường (tổng 6.8 giây, ngưỡng chấp nhận là 800ms):
TRACE SUMMARY
trace_id: 9f3a...c221 (shortened)
Root operation: POST /api/v1/checkout/charge
Total duration: 6,842ms (SLA threshold: 800ms — 8.5x over)
Total spans: 14
Services involved: api-gateway, checkout-api, payments-service, fraud-check-service, payments-db
Status: ERROR (root span marked error=true, downstream 504 to client)
Mẹo: Luôn ghi kèm SLA threshold hoặc baseline duration ngay trong trace summary (như dòng "Total duration: 6,842ms (SLA: 800ms)") — nếu chỉ đưa số tuyệt đối, AI không có cơ sở để đánh giá mức độ nghiêm trọng và có thể bỏ qua một con số thực ra rất bất thường.
Critical Path Waterfall
Critical path (đường găng) là chuỗi span quyết định tổng thời gian, loại bỏ các span chạy song song không ảnh hưởng tới tổng thời gian. Trình bày dạng thác nước với indent thể hiện quan hệ cha-con và duration riêng của từng span:
CRITICAL PATH WATERFALL (indent = parent-child, duration = self time)
api-gateway: POST /checkout/charge ................................ 6,842ms
checkout-api: handleCharge() ..................................... 6,790ms
payments-service: chargeCard() ................................. 6,650ms
fraud-check-service: evaluateRisk() [span_id: ...a12f] ......... 210ms
payments-db: SELECT ... FOR UPDATE [span_id: ...b88e] ......... 1,980ms <- outlier
payments-service: callGateway() attempt 1 [span_id: ...c40d] . 2,010ms (timeout)
payments-service: callGateway() attempt 2 [span_id: ...d91a] . 2,015ms (timeout)
payments-service: callGateway() attempt 3 [span_id: ...e02b] . 445ms (success)
Waterfall này lập tức cho thấy 3 điểm bất thường: query FOR UPDATE tốn 1,980ms (bình thường dưới 50ms), và hai lần gọi gateway đầu tiên timeout hết 2s mỗi lần trước khi lần thứ 3 thành công — tổng hai yếu tố này chiếm hơn 6 giây trong tổng 6.8 giây.
Mẹo: Khi dựng waterfall bằng tay từ trace UI, chỉ liệt kê span nằm trên critical path (span quyết định tổng thời gian) — bỏ hẳn các span song song không ảnh hưởng tới tổng duration ra khỏi bảng, thay vào đó ghi một dòng tóm tắt riêng như "3 parallel spans, max 180ms, not on critical path" để AI không nhầm chúng vào chuỗi nhân quả chính.
Error Span Details
Với riêng các span có status error, trích chi tiết đầy đủ hơn vì đây là nơi root cause thường lộ rõ nhất:
ERROR SPAN DETAILS
Span: payments-service: callGateway() attempt 1 [span_id: ...c40d]
duration: 2,010ms
status: error
error.message: "context deadline exceeded"
tags: retry.count=1, gateway.endpoint=/v2/charge, connection.pool_utilization=0.94
Span: payments-service: callGateway() attempt 2 [span_id: ...d91a]
duration: 2,015ms
status: error
error.message: "context deadline exceeded"
tags: retry.count=2, gateway.endpoint=/v2/charge, connection.pool_utilization=0.98
Reading trace timing patterns
Khi đọc (và hướng dẫn AI đọc) trace timing, có vài pattern kinh điển nên nhận diện ngay:
- Chuỗi span timeout giống nhau về duration (ví dụ cả hai lần gọi đều ~2,000ms — đúng bằng giá trị timeout cấu hình) là dấu hiệu request đang bị timeout ở đúng ngưỡng cấu hình, chứ không phải xử lý tự nhiên chậm dần — hướng nghi ngờ nên đổ vào tầng network/downstream service, không phải logic xử lý.
- Một span con chiếm phần lớn duration của span cha (self time cao bất thường so với các lần chạy khác) là "outlier span" — ưu tiên điều tra span đó trước, như query
FOR UPDATE1,980ms trong ví dụ trên so với baseline dưới 50ms. connection.pool_utilizationtăng dần qua các attempt retry (0.94 → 0.98) là dấu hiệu retry storm tự khuếch đại — mỗi lần retry giữ thêm connection, làm pool cạn nhanh hơn, dẫn tới càng retry càng chậm.- Span song song (sibling span) có tổng duration lớn hơn span cha là dấu hiệu chúng chạy đồng thời (không phải tuần tự) — quan trọng để AI không tính nhầm rằng chúng cộng dồn vào critical path.
Mẹo: Luôn tính và ghi rõ baseline "duration bình thường" cạnh mỗi số liệu bất thường trong trace summary (ví dụ "1,980ms (baseline: <50ms)") — con số tuyệt đối không có ý nghĩa với AI nếu nó không biết đâu là bình thường, đâu là bất thường.
Làm sao kết hợp nhiều nguồn bằng chứng vào một prompt AI hành động được?
Bug thực tế hiếm khi được giải quyết chỉ bằng một nguồn dữ liệu. Một điều tra hoàn chỉnh thường cần kết hợp: application log, stack trace, trace summary, kết quả query database, và cấu hình môi trường — tất cả trong cùng một prompt để AI thấy được toàn cảnh và tìm tương quan giữa các nguồn. Thử thách là giữ prompt đủ có cấu trúc để AI không nhầm lẫn nguồn nào nói gì, trong khi vẫn nằm trong giới hạn context window hợp lý.
The combined evidence prompt structure
Cấu trúc chuẩn cho một combined evidence prompt gồm 4 phần theo thứ tự: (1) bối cảnh và câu hỏi tổng quan, (2) evidence registry (danh mục bằng chứng, liệt kê nguồn nào có gì trước khi đi vào chi tiết), (3) từng khối bằng chứng đánh số, (4) yêu cầu phân tích cụ thể ở cuối. Đặt yêu cầu phân tích ở cuối (không phải đầu) giúp AI đọc hết bằng chứng trước khi hình thành kết luận, giảm thiên vị "chốt sớm" (anchoring) vào giả thuyết đầu tiên.
Dưới đây là ví dụ đầy đủ cho bug thanh toán timeout đã dùng xuyên suốt bài này:
Bug Investigation Context
Ticket: PAY-4471 — Intermittent 504 timeout on checkout charge during peak hours
Reported by: Customer Support (12 user complaints in 30 minutes)
Environment: production, ap-southeast-1
First observed: 2024-03-15 14:20 UTC, still recurring, ~8% of charge requests affected
Evidence Registry
[1] Application Logs — payments-service, 14:22:55–14:23:50 UTC
[2] Stack Trace — from Sentry, error PAY-CHARGE-TIMEOUT
[3] Distributed Trace Summary — trace_id 9f3a...c221 (representative slow request)
[4] Database Query Result — connection pool stats snapshot, 14:23:00 UTC
[5] Environment Configuration — payments-service, gateway client settings
[1] Application Logs
14:22:55.102 INFO [req-88213] PaymentController - received charge request, order_id=ORD-55291
14:22:57.145 WARN [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, retrying (1/3)
14:22:59.155 WARN [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, retrying (2/3)
14:23:01.170 ERROR [req-88213] PaymentGatewayClient - gateway call timed out after 2000ms, max retries exceeded
[2] Stack Trace
com.payments.gateway.GatewayTimeoutException: context deadline exceeded after 2000ms
at com.payments.gateway.GatewayHttpClient.execute(GatewayHttpClient.java:142)
at com.payments.gateway.PaymentGatewayClient.charge(PaymentGatewayClient.java:88)
at com.payments.PaymentController.handleCharge(PaymentController.java:55)
Caused by: java.util.concurrent.TimeoutException: connection acquisition timeout,
pool="gateway-http-pool", active=49, max=50, pending=17
[3] Distributed Trace Summary
Total duration: 6,842ms (SLA: 800ms)
Critical path: fraud-check (210ms) -> db lock wait (1,980ms, baseline <50ms) ->
gateway attempt 1 (2,010ms, timeout) -> attempt 2 (2,015ms, timeout) -> attempt 3 (445ms, ok)
[4] Database Query Result
SELECT count(*) as active_connections, max_pool_size
FROM pg_stat_pool_snapshot WHERE service = 'payments-service';
-- active_connections=49, max_pool_size=50 (98% utilization at 14:23:00 UTC)
[5] Environment Configuration
gateway.http.pool.max_connections=50
gateway.http.pool.acquisition_timeout_ms=2000
gateway.http.retry.max_attempts=3
gateway.http.retry.backoff=none <- no jitter/backoff configured
gateway.http.circuit_breaker.enabled=false
Analysis Request
Based on all evidence above:
1. What is the most likely root cause of the intermittent 504 timeouts?
2. Explain how the database lock wait (evidence [3]) and connection pool
exhaustion (evidence [2], [4]) are related, if at all.
3. Why does this only happen during peak hours and not consistently?
4. Recommend an immediate mitigation (low risk, deployable within the hour)
and a permanent fix. Cite which evidence number supports each conclusion.
Với cấu trúc này, một AI phân tích tốt sẽ liên kết được: config không có backoff/jitter (evidence [5]) khiến 3 lần retry gọi gateway dồn dập gần nhau, mỗi lần giữ một connection trong pool tới 2 giây (evidence [2], [4]) — khi traffic tăng vào giờ cao điểm, số request đồng thời cần connection tăng theo, pool nhanh chóng đạt 98% (evidence [4]), làm các request khác phải chờ acquisition timeout, tạo hiệu ứng domino. Đồng thời AI có thể chỉ ra db lock wait 1,980ms (evidence [3]) là một yếu tố khuếch đại độc lập, không phải nguyên nhân chính — vì nó chỉ chiếm ~30% tổng thời gian trong khi phần lớn (hơn 4 giây) nằm ở hai lần gateway timeout.
Managing evidence volume in combined prompts
Khi số nguồn bằng chứng vượt quá 5-6 khối, cân nhắc các kỹ thuật giảm tải: (1) tóm tắt các khối ít quan trọng hơn thành 2-3 dòng kết luận thay vì log đầy đủ (ví dụ "[6] Redis cache metrics: hit rate normal 94%, not a contributing factor — summary only, full data available on request"); (2) đặt các khối quan trọng nhất lên đầu vì một số model có xu hướng chú ý nhiều hơn tới đầu và cuối prompt (hiệu ứng "lost in the middle" — model dễ bỏ sót thông tin nằm ở giữa một prompt dài); (3) nếu dùng công cụ agentic có khả năng tự truy vấn (như Claude Code với quyền đọc log/database trực tiếp), để AI tự pull evidence bổ sung khi cần thay vì bạn đoán trước và nhồi hết vào prompt ban đầu.
Iterative evidence addition
Trong nhiều trường hợp, bạn không có đủ evidence ngay từ đầu — kết quả phân tích đầu tiên của AI sẽ chỉ ra "cần thêm dữ liệu X để xác nhận". Đây là quy trình lành mạnh, không phải dấu hiệu prompt ban đầu tệ. Khi AI trả lời "để xác nhận giả thuyết về lock contention, cần xem thêm log slow query đầy đủ của bảng payment_transactions trong window này", bạn bổ sung đúng phần đó vào cùng thread hội thoại (giữ nguyên context cũ, không mở prompt mới từ đầu), rồi hỏi tiếp "Với dữ liệu bổ sung này, giả thuyết ban đầu của bạn thay đổi thế nào?".
Cách làm việc iterative (lặp lại theo vòng) này phản ánh đúng cách một QA senior điều tra thật: đưa ra giả thuyết sớm dựa trên dữ liệu có sẵn, xác định thiếu gì để xác nhận, rồi đi lấy đúng phần đó — không cố gắng thu thập "mọi thứ có thể" trước khi bắt đầu suy luận.
Mẹo: Khi bổ sung evidence theo vòng lặp, luôn yêu cầu AI nói rõ giả thuyết đã thay đổi ra sao so với vòng trước ("what changed in your conclusion and why") — điều này buộc AI thể hiện rõ quá trình suy luận, giúp bạn phát hiện ngay nếu nó đang "chữa cháy" bằng cách diễn giải lại dữ liệu mới để khớp với giả thuyết cũ thay vì thật sự cập nhật suy luận.
Những điểm chính
- Chọn log theo ba nguyên tắc: minimum volume theo đúng giả thuyết đang xét, full execution context của riêng operation lỗi, và bắt đầu từ mốc last known-good state — không phải "đưa mọi log có thể liên quan".
- Dùng quy trình log trimming 5 bước (anchor, time window, filter, tóm tắt phần lặp, giữ nguyên phần độc nhất) để cắt gọt log một cách có hệ thống, lặp lại được cho mọi bug.
- Định dạng evidence block với header đánh số
[N], timestamp chuẩn hóa cùng timezone, rút gọn prefix log dài, và annotation tại điểm quan trọng — giúp AI đọc nhanh và chính xác hơn, đồng thời tạo ra artifact review được ngay. - Khi evidence quá dài, chunk theo giai đoạn và yêu cầu AI giữ "running hypothesis" xuyên suốt các phần, thay vì nhồi tất cả vào một prompt khổng lồ.
- Tóm tắt distributed trace thành 3 phần: trace summary (tổng quan), critical path waterfall (đường găng có self time từng span), và error span details (chi tiết span lỗi) — đừng dán raw JSON.
- Nhận diện các pattern timing kinh điển trong trace: duration giống nhau ở nhiều span (dấu hiệu timeout theo ngưỡng cấu hình), outlier span, pool utilization tăng dần qua các lần retry, và span song song không cộng dồn vào critical path.
- Kết hợp nhiều nguồn bằng chứng theo cấu trúc: bối cảnh + evidence registry + các khối đánh số + yêu cầu phân tích ở cuối cùng — đặt câu hỏi ở cuối để tránh AI chốt giả thuyết sớm trước khi đọc hết bằng chứng.
- Bổ sung evidence theo vòng lặp trong cùng thread hội thoại khi AI chỉ rõ cần thêm dữ liệu gì, và luôn yêu cầu AI giải thích rõ giả thuyết thay đổi thế nào sau mỗi lần bổ sung.