
LLM Và Nghịch Lý Chuyên Môn: Vì Sao AI Càng Mạnh, Domain Expertise Càng Đắt Giá?
- luandnh
- Ai tools , System design
- 3 tháng 10, 2026
Mục lục
Cái gap CSS ngày xưa và cái gap backend bây giờ
Thập niên 2010, nếu bạn có lỗ hổng kỹ năng - kiểu không biết viết CSS - bạn có đúng hai lựa chọn: nhờ một đồng nghiệp giỏi hơn, hoặc cầu trời rằng có ai đó đã đăng đúng câu trả lời cho đúng vấn đề của bạn lên Stack Overflow.
Giờ thì khác. Ai cũng viết được CSS “tạm ổn” bằng cách quăng task cho một con LLM. LLM biến tất cả mọi người thành generalist.
Chính vì vậy, rất nhiều người cho rằng chẳng có kỹ năng gì đặc biệt trong việc làm việc với LLM. Muốn có sản phẩm mà LLM có thể giao - toán cấp PhD, code máy tính chạy được nhưng đôi khi nhạt nhẽo, hay văn kiểu LinkedIn gượng gạo - cứ hỏi thẳng. Vì ai cũng đang nói chuyện với cùng một đám model, nên “prompt engineer giỏi” nhận kết quả y hệt người lần đầu chạm vào LLM.
Điều này sai. Kỹ năng quan trọng nhất khi prompt chính là chuyên môn (expertise) trong đúng cái domain bạn đang prompt.
Sean Goedecke, một engineer ở GitHub, viết bài “LLMs reward expertise” tháng 7/2026. Bài leo top Hacker News với 1416 điểm và gần 600 comment. Không phải vì nó hứa hẹn điều gì mới mẻ, mà vì nó gọi tên một thứ nhiều người cảm thấy nhưng chưa diễn đạt được: AI càng mạnh, khoảng cách giữa người giỏi và người dở trong cùng một domain càng rộng ra, chứ không phải thu hẹp lại.
Terence Tao và cái ChatGPT mình không bao giờ chạm tới
Ví dụ đẹp nhất là cuộc trò chuyện của Terence Tao với ChatGPT về phản ví dụ mới được tìm ra cho Jacobian Conjecture.
Nhắc lại bối cảnh một chút cho anh em chưa theo dõi. Jacobian Conjecture phát biểu đại khái: nếu một ánh xạ đa thức trên trường số phức có Jacobian là hằng số khác không (tức là locally invertible ở mọi điểm), thì nó invertible toàn cục (có nghịch đảo cũng là đa thức). Mọi người tin nó đúng suốt hơn 80 năm. Tháng 7/2026, người ta tìm ra phản ví dụ ở chiều 3 - dùng một hệ AI tên Fable - và conjecture sụp đổ ở chiều 3 trở lên (chiều 2 vẫn còn mở).
Tao viết một bài “digestion” để tiêu hóa cái phản ví dụ đó. Một phần trong quá trình đó là đối thoại với ChatGPT.
Sean quan sát cuộc đối thoại đó và rút ra vài đặc điểm của cách prompt của Tao:
- Tin nhắn của Tao rất ngắn và đi thẳng vào vấn đề. Ông không trả lời từng điểm một của model, chỉ nhắm vào cái gist.
- Output của model súc tích hơn hẳn so với khi một người thường như Sean hỏi GPT-5.6 về toán. Bằng cách phát tín hiệu “tôi là chuyên gia”, Tao đẩy model sang mode “nói chuyện với nhà toán học”, chứ không phải mode “giảng giải cho người mới”.
- Tao push back khi output trông sai, nhưng không đối đầu trực tiếp. Ông nói kiểu “cái này trông phức tạp hơn tôi hy vọng” thay vì “sai rồi”.
- Tao tự đề xuất hướng đi. Ông gần như không bao giờ nhận lời khuyên của model về việc nên đi tiếp ở đâu.
Nhưng đây là điểm mấu chốt, và cũng là điểm mà mấy bài “10 mẹo prompt như chuyên gia” bỏ qua: bạn không thể prompt như Tao chỉ bằng cách bắt chước bốn mẹo trên. Chìa khóa nằm ở việc thực sự hiểu toán - biết rút ra ý tưởng cốt lõi từ một đoạn trả lời dài ngoằng, biết đề xuất cách tiếp cận khác, biết cái gì “trông lạ”.
Sean nói thẳng: “Tao là nhà toán học giỏi hơn tôi là lập trình viên.” Nhưng cơ chế thì y hệt trong công việc của Sean. Nếu bạn có một “theory of the codebase” tốt - một mô hình mental đủ chắc về hệ thống mình sở hữu - bạn có thể ép LLM mạnh hơn nhiều so với khi bạn mù tịt. Vì bạn có cảm giác riêng về một lời giải tốt trông như thế nào, bạn mới nói được: “không, tôi nghĩ chỗ này đơn giản hơn được”, hoặc “nhưng mình đã làm X rồi mà nhỉ?”, hoặc “diễn đạt bài toán này bằng mấy khái niệm quen thuộc được không?”.
Hai vòng lặp: một cái xoáy, một cái tiến
Để thấy cơ chế này rõ hơn, hình dung hai vòng lặp prompt. Đây là chỗ mà “expertise” không phải một cảm giác mơ hồ, mà là một cơ chế phản hồi (feedback loop) cụ thể.
graph LR
subgraph "Non-Expert Loop"
N1["`Prompt mơ hồ
'nó bị lỗi gì đó'`"] --> N2["`LLM trả lời dài dòng
không đánh giá được đúng sai`"]
N2 --> N3["`Đọc, thử, rối
hỏi thêm feature`"]
N3 --> N1
end
subgraph "Expert Loop"
E1["`Prompt ngắn, đúng thuật ngữ
'race ở map này, dùng errgroup'`"] --> E2["`LLM trả lời súc tích
đúng mode chuyên gia`"]
E2 --> E3["`Chỉ ra chỗ 'trông sai'
đề xuất hướng đi riêng`"]
E3 --> E1
end
Vòng bên trái không hội tụ. Bạn không có tiêu chí để biết output đúng hay sai, nên mỗi lần lặp bạn chỉ thêm rối. Vòng bên phải hội tụ, vì mỗi vòng bạn bơm thêm một ràng buộc (constraint) dựa trên hiểu biết thật của mình.
Có một comment trên Hacker News minh họa vòng bên trái cực kỳ rõ. Một anh tên krisoft kể: bạn gái anh ấy (không có nền tảng software engineering) muốn làm một single-page web app đơn giản. Anh bảo cô thử tự làm bằng LLM, để anh ngồi xem. Anh tưởng AI viết code sẽ không thành vấn đề, chỉ tò mò xem AI có nhận ra người dùng là novice không.
Kết quả: cô ấy không có cả vốn từ để yêu cầu AI viết code. Cô đi vòng quanh việc brainstorm feature, càng lúc càng rối. Sau 1 tiếng rưỡi và rất nhiều message, họ dừng thí nghiệm. Trong khi chính anh krisoft, nếu prompt, sẽ chỉ cần một câu: “Viết cho tôi một trang HTML làm X, Y, Z.” Sự khác biệt duy nhất là vocabulary.
Đây là insight đau lòng: khoảng cách về từ vựng kỹ thuật (technical vocabulary) ngăn cái vòng lặp phản hồi khép kín. Bạn không biết hỏi “làm sao để làm ra cái tôi muốn”, nên bạn hỏi “tôi nên làm gì tiếp”, và thế là rơi vào vòng xoáy.
Đổ vào backend: technical vocabulary là tiền
Ngày xưa, một backend engineer nắm chắc thuật ngữ như “race condition”, “critical section”, “compare-and-swap”, “backpressure”, “head-of-line blocking” là người có lợi thế. Với LLM, lợi thế đó không biến mất - nó nhân lên.
Thử một ví dụ cụ thể ai cũng từng gặp: fetch dữ liệu từ N URL song song.
Người không nắm context, goroutine, sync sẽ prompt: “Viết code Go để tải nhiều URL cùng lúc cho nhanh.” LLM trả về một mớ kiểu:
func FetchAll(urls []string) []string {
var results []string
for _, u := range urls {
go func(u string) {
resp, _ := http.Get(u) // không timeout, không context
body, _ := io.ReadAll(resp.Body)
results = append(results, string(body)) // data race
}(u)
}
return results // return ngay, goroutine leak
}
Code này trông đúng. Nó chạy. Nó trả về dữ liệu. Và nó sai theo ít nhất năm cách: data race trên results, không giới hạn số goroutine (N = 10.000 URL là bạn tự bắn vào chân), không timeout, không xử lý status code, và leak toàn bộ body chưa đóng.
Một engineer có chuyên môn nhìn cái này là thấy ngay. Nhưng quan trọng hơn: anh ta biết biến chuyên môn đó thành ràng buộc trong prompt. Không phải “viết cho tôi code tải song song”, mà là “dùng errgroup.WithContext, giới hạn concurrency ở mức X, tôn trọng cancellation từ context, đừng để leak goroutine”.
- Go (Naive)
- Go (Expert Guided)
// Prompt: "Write Go code to fetch many URLs concurrently"
// Output của LLM khi người dùng không nêu ràng buộc.
func FetchAll(urls []string) []string {
var results []string
for _, u := range urls {
go func(u string) {
resp, _ := http.Get(u)
body, _ := io.ReadAll(resp.Body)
results = append(results, string(body)) // DATA RACE
}(u)
}
return results // trả về trước khi goroutine xong; leak
}
// Prompt: "errgroup.WithContext, bounded concurrency, honor ctx
// cancellation, pre-allocate, cap body size, no leaked goroutines"
func FetchAll(ctx context.Context, client *http.Client, urls []string, limit int) ([]string, error) {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(limit) // bounded concurrency, chống bão goroutine
results := make([]string, len(urls)) // 1 lần cấp phát, không append
for i, u := range urls {
i, u := i, u
g.Go(func() error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return fmt.Errorf("build request %s: %w", u, err)
}
resp, err := client.Do(req) // client có Timeout sẵn
if err != nil {
return fmt.Errorf("get %s: %w", u, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("get %s: status %d", u, resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBody))
if err != nil {
return fmt.Errorf("read %s: %w", u, err)
}
results[i] = string(body) // index riêng biệt -> không race
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
Cả hai đoạn code đều “do AI viết”. Nhưng đoạn thứ hai chỉ tồn tại được vì người prompt biết mình cần gì. LLM không tự nghĩ ra g.SetLimit, không tự thêm io.LimitReader, không tự nhớ http.NewRequestWithContext. Nó có biết, nhưng nó không biết là bạn cần.
Tip
Mẹo thực dụng: khi prompt code Go, hãy nêu tên thư viện và primitive cụ thể (errgroup, context.WithTimeout, sync.Pool, semaphore.Weighted) chứ đừng mô tả ý định chung chung. Bạn đang không dạy AI code, bạn đang thu hẹp không gian tìm kiếm của nó về đúng vùng bạn muốn.
pprof: chỗ chuyên môn ăn tiền rõ nhất
Có một loại câu hỏi mà người thiếu chuyên môn không bao giờ biết để hỏi: câu hỏi về bằng chứng.
Một junior gặp endpoint chậm sẽ prompt: “Làm sao để code Go nhanh hơn?” LLM sẽ trả về một danh sách generic: dùng cache, tránh allocate, dùng goroutine, giảm syscall. Nghe hợp lý. Toàn bộ đều là suy đoán.
Một senior sẽ prompt khác hẳn: “Tôi có pprof profile này, hàm renderMetric chiếm 43% CPU. Đây là source. Tối ưu mà không đổi hành vi.” Anh ta không hỏi “làm sao nhanh hơn” - anh ta hỏi về một hàm cụ thể, đã được đo bằng dữ liệu thật.
import (
"log"
"net/http"
_ "net/http/pprof" // đăng ký handler vào DefaultServeMux
)
// Bật endpoint debug trên cổng riêng, KHÔNG expose ra ngoài.
func startDebugServer() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
}
Sau đó lấy profile:
go tool pprof -top -cum http://localhost:6060/debug/pprof/profile?seconds=30
go tool pprof -http=:8081 cpu.prof # xem flame graph
Note
Điểm khác biệt không nằm ở chỗ senior biết lệnh go tool pprof. LLM cũng biết. Điểm khác biệt là senior biết rằng không được tối ưu mà chưa đo - và biết giao cho LLM một profile cụ thể kèm ràng buộc “không đổi hành vi”, thay vì để nó đoán mò.
Chuyện tương tự với memory allocation. Người có chuyên môn hiểu rằng append không pre-allocate sẽ gây copy nhiều lần, rằng interface boxing tạo allocation ẩn, rằng sync.Pool hợp lý cho object ngắn hạn tần suất cao nhưng có cost riêng. Họ biến hiểu biết đó thành prompt:
// Naive: append không pre-allocate -> reallocation + copy liên tục
func Process(items []Item) []Result {
var out []Result
for _, it := range items {
out = append(out, transform(it)) // mỗi lần vượt cap là cấp phát lại
}
return out
}
// Expert guided: cấp phát đúng một lần, truyền con trỏ tránh copy struct lớn
func Process(items []Item) []Result {
out := make([]Result, 0, len(items)) // 1 allocation, cap chính xác
for i := range items {
out = append(out, transform(&items[i])) // không copy Item lớn
}
return out
}
Hai phiên bản này chênh nhau có thể 30-50% thời gian chạy trên hot path. Người không biết thì không nghĩ tới; LLM không tự đề xuất vì nó không biết datasize của bạn.
Bức tranh lớn: điểm nghẽn đã chuyển sang phía con người
Ghép tất cả lại, Sean rút ra một kết luận mà mình thấy đúng cho cả backend lẫn mọi domain khác:
Với rất nhiều task, con người là điểm nghẽn, không phải model. Phần khó nằm ở việc truyền đạt cho model chính xác loại giải pháp mà con người muốn. Thông tin đã “nằm trong model” rồi, nhưng cần một người rất giỏi mới moi nó ra được.
Hình dung nó như một cái phễu.
flowchart TD
subgraph "Trước LLM"
A["`Cú pháp & API
Cần người biết viết`"] --> B["`Thiết kế hệ thống
Cần người biết muốn gì`"]
end
subgraph "Sau LLM"
C["`Cú pháp & API
LLM làm phần lớn`"] --> D["`Prompt + đánh giá output
Điểm nghẽn MỚI: biết diễn đạt
và biết cái gì sai`"]
end
Trước đây, phần lớn giá trị của một engineer nằm ở tầng dưới cùng của phễu: biết cú pháp, biết API, viết ra được code chạy. LLM ăn gần hết tầng đó. Nhưng khi tầng đó bị tự động hóa, giá trị dịch chuyển lên tầng trên: biết mình muốn gì, biết đánh giá output, biết cái gì “trông sai”. Và cái tầng trên này lại chính là thứ không thể tự động hóa bằng cách bắt chước mẹo prompt, vì nó cần hiểu biết hệ thống thật.
Đây cũng khớp với một điều mình đã viết nhiều lần: các bài toán system design bị chi phối bởi chi tiết cụ thể (concrete specifics), không phải nguyên lý chung chung. Biết CAP Theorem hay Saga Pattern là tốt, nhưng đọc hiểu codebase của mình, biết service nào đang gọi service nào, transaction nào đang giữ lock gì - cái đó mới giúp bạn hỏi LLM được những câu cụ thể như Tao hỏi ChatGPT về toán của ông ấy.
Mặt trái: “lòng tự an ủi” và cái bẫy chứng minh từ giai thoại
Nếu mọi thứ chỉ dừng ở đây thì bài viết này nghe quá dễ chịu. Và đó chính xác là điều mình cần cảnh giác.
Trên Hacker News, một số commenter chỉ ra rào cản trí tuệ với luận điểm của Sean: nó an ủi một nhóm người cụ thể là developer, và chúng ta có xu hướng tin những luận điểm khiến mình thấy an toàn về tương lai. Mình đồng ý. “Chuyên môn vẫn quan trọng” là một luận điểm dễ tin vì nó dễ chịu. Nhưng khả năng nó đúng không cao hơn chỉ vì nó dễ chịu.
Thứ hai, bằng chứng cho “expertise thắng” phần lớn là giai thoại (anecdote), và giai thoại cắt cả hai chiều. Cái comment krisoft (bạn gái không prompt nổi) được đặt cạnh một comment khác của nvrmndmnm với bằng chứng ngược lại: bạn gái anh này, một hair stylist, không có nền tảng code, đã tự cài Arch Linux + Hyprland, ricing đủ kiểu, chạy được Steam + Portal, rồi tự build một Telegram bot hoạt động chỉ với free-tier Gemini và một key Kimi. Zero expertise, kết quả thật.
Vậy cái phân biệt hai case là gì? Comment cgannett gói gọn: người bạn của krisoft không có vốn từ để hỏi, nhưng chính việc moi vốn từ đó ra từ AI cũng là một kỹ năng. Bạn gái của nvrmndmnm không phải chuyên gia, nhưng cô ấy biết hỏi “web page được làm từ gì”, “mình có thể dùng mấy viên gạch đó để build app riêng không”. Đó là một kiểu tinkerer - sự tò mò + khả năng đặt câu hỏi - thứ mà dân kỹ thuật hay coi là hiển nhiên nhưng thực ra là một văn hóa, một tập quán, không phải bản năng.
Và cuối cùng là điểm phản biện mạnh nhất: OpenAI (và các lab khác) tìm ra mấy phát hiện toán học kia bằng prompt không chuyên nghiệp - họ không cần expertise để con model đề xuất. Sean phản hồi lại đúng chỗ: OpenAI có một team nhà toán học chuyên môn kiểm tra và lọc các đề xuất của model, và bạn hiện tại không thể bỏ bước đó. Nói cách khác, expertise không nhất thiết nằm ở người prompt, nhưng nó phải nằm ở đâu đó trong pipeline để con số nghe được tin được. Đó là điểm tinh tế: câu hỏi không phải “AI có cần expert không”, mà là “expert ở khâu nào”.
Vậy làm gì với điều này
Nếu luận điểm đúng, thì vài hệ quả thực dụng:
Đừng học prompt engineering, hãy đào sâu domain. Mấy khóa “10 kỹ thuật prompt nâng cao” có giá trị thấp. Thứ trả tiền là bạn hiểu sâu cái hệ thống bạn đang sở hữu, hiểu đủ để đặt câu hỏi mà AI không biết để trả lời.
Xây “theory of the codebase”. Không phải là thuộc lòng code, mà là có mental model đủ chắc về luồng dữ liệu, các ràng buộc ngầm, các invariant. Có cái đó rồi, bạn mới dùng được chiêu của Tao: đề xuất hướng đi riêng, không nhận lời khuyên của model.
Dùng AI như một sparring partner, không phải oracle. Tao không nhận advice của ChatGPT về việc đi đâu tiếp. Ông dùng nó như một bức tường để ném ý tưởng vào. Với backend, nghĩa là: bạn đề xuất thiết kế, AI phản biện, bạn phán xét phản biện, bạn chốt. Vai trò phán xử không bao giờ được giao phó.
Với cái mình chưa là expert, hãy thừa nhận và đi vòng. Sean nói thẳng: ai cũng phải pha trộn hai chế độ, vì mình có chuyên môn ở vùng này và mù tịt ở vùng khác. Khi bạn mù tịt, bạn phải cling vào LLM và nhận diện mình đang nằm trong vòng xoáy - và lúc đó, việc đúng đắn là đi tìm một chuyên gia con người, chứ không phải hỏi con model thêm 40 lần.
Cuối cùng, giữ một chút hoài nghi thái độ. Có thể khi chúng ta làm ra đủ nghiên cứu để khẳng định chắc “expertise vẫn thắng”, mọi thứ lại thay đổi dưới chân chúng ta một lần nữa. Nhưng cho đến lúc đó, bằng chứng - dù là giai thoại - nghiêng về một hướng: cùng một con model, người hiểu hệ thống hỏi được câu hỏi tốt hơn, và câu hỏi tốt hơn cho ra kết quả tốt hơn. AI không san phẳng cuộc chơi. Nó chỉ đổi chỗ đặt trọng số.
Warning
Đừng dùng bài này để tự trấn an (“tôi là senior, tôi an toàn”). Cái an ủi thì dễ tin. Hãy đọc nó như một giả thuyết cần tự kiểm chứng: lần tới khi bạn thấy mình đang đi trong vòng lặp brainstorm với AI, hỏi bản thân xem bạn thiếu expertise, hay chỉ thiếu một chuyên gia con người.


