Menu

Prompt AI viết code: cách nhận về code chạy được

Prompt viết code hiệu quả khi nó đọc như một bản đặc tả nhỏ: ngôn ngữ và phiên bản, đầu vào và đầu ra, các trường hợp biên, và các test mà code phải vượt qua. Hãy yêu cầu từng bước nhỏ một.

Mọi prompt bên dưới đều có thể chỉnh sửa: thay đổi nó, rồi mở trong ChatGPT, Claude hoặc ứng dụng AI khác.

Một prompt viết code là một bản đặc tả. Model chưa thấy dự án của bạn, không biết bạn chạy phiên bản ngôn ngữ nào, và không thể hỏi điều gì nên xảy ra khi đầu vào rỗng. Bất cứ điều gì prompt bỏ ngỏ, nó sẽ lấp bằng lựa chọn phổ biến nhất trong dữ liệu huấn luyện, và lựa chọn phổ biến nhất thường không phải là lựa chọn của bạn. Các prompt dưới đây để model phải đoán ít thứ hơn.

Viết đặc tả trước khi viết code

Khối dưới đây yêu cầu một hàm Python nhỏ. Mỗi phần của prompt trả lời một câu hỏi mà nếu không, model sẽ tự trả lời thay bạn. Hãy lần lượt tắt từng phần và hình dung câu trả lời khi thiếu chúng: thiếu ràng buộc, bạn có thể nhận về một thư viện bên thứ ba; thiếu ngữ cảnh, model phải đoán thế nào là đầu vào hợp lệ; thiếu định dạng, bạn có thể không nhận được test nào.

Hàm từ đặc tả
Fill in
Parts
Bạn là một lập trình viên Python cẩn thận, chuyên viết các hàm nhỏ có test.
Viết hàm `parse_duration(text)` chuyển một chuỗi thời lượng thành số giây.
Đầu vào hợp lệ có dạng "1h30m", "45m", "2h" và "90s": giờ, phút và giây, mỗi phần đều tùy chọn, luôn theo đúng thứ tự đó. "1h30m" trả về 5400. Mọi thứ khác, kể cả chuỗi rỗng, đều không hợp lệ.
Python 3.11, chỉ dùng thư viện chuẩn. Raise ValueError cho đầu vào không hợp lệ, kèm đầu vào trong thông báo lỗi. Không chấp nhận số thập phân như "1.5h".
Đưa hàm trước, sau đó là các test pytest bao quát mọi ví dụ ở trên cùng ba đầu vào không hợp lệ. Không giải thích quá một câu.
Try it
Example replyReplies vary between models and runs.

Hàm khớp ba phần tùy chọn theo thứ tự và từ chối trường hợp khớp mà cả ba phần đều rỗng.

import re

_PATTERN = re.compile(r"(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?")

def parse_duration(text: str) -> int:
    match = _PATTERN.fullmatch(text)
    if not match or not any(match.groups()):
        raise ValueError(f"invalid duration: {text!r}")
    hours, minutes, seconds = (int(g) if g else 0 for g in match.groups())
    return hours * 3600 + minutes * 60 + seconds
import pytest
from duration import parse_duration

@pytest.mark.parametrize("text, expected", [
    ("1h30m", 5400), ("45m", 2700), ("2h", 7200), ("90s", 90),
])
def test_valid(text, expected):
    assert parse_duration(text) == expected

@pytest.mark.parametrize("text", ["", "1m1h", "1.5h"])
def test_invalid(text):
    with pytest.raises(ValueError):
        parse_duration(text)

Bốn chi tiết trong prompt đó làm phần lớn công việc:

  • Ví dụ kèm đáp án. "1h30m trả về 5400" là một test mà model có thể dùng để tự kiểm tra code của nó, và nó xóa mọi nghi ngờ về đơn vị.
  • Phiên bản ngôn ngữ và các thư viện được phép. Thiếu chúng, bạn có thể nhận về một thư viện bạn chưa cài hoặc cú pháp mới hơn trình thông dịch của bạn.
  • Thế nào là không hợp lệ, và khi đó phải làm gì. Model dễ bỏ qua việc xử lý lỗi khi không ai yêu cầu.
  • Test trong câu trả lời. Chúng biến "trông có vẻ đúng" thành thứ bạn có thể chạy. Nếu một test thất bại, bạn dán lỗi đó trở lại, một câu hỏi tiếp theo tốt hơn nhiều so với "nó không chạy".

Phép kiểm tra any(match.groups()) cũng đáng để ý: riêng pattern thì vẫn khớp với chuỗi rỗng, vì mọi phần đều tùy chọn. Chính dòng về chuỗi rỗng trong prompt khiến trường hợp đó xuất hiện trong code và trong test.

Nêu phiên bản, công nghệ và những gì đã có

Model nghiêng về phong cách phổ biến nhất trong dữ liệu huấn luyện. Với JavaScript, điều đó có thể là require của CommonJS trong một dự án dùng ES modules; với Python, có thể là API của một thư viện đã thay đổi (Pydantic 1 so với 2 là trường hợp hay gặp); và với bất kỳ framework nào thay đổi nhanh, có thể là cách viết từ hai phiên bản lớn trước. Thường chỉ một dòng là đủ sửa: "Node 22, ES modules, không TypeScript" hoặc "React 19, function component, CSS module thuần".

Khi bạn thêm code vào một dự án có sẵn, model cần thấy những phần mà code mới sẽ chạm tới. Hãy dán chữ ký hàm mà nó phải gọi, dạng dữ liệu nó sẽ nhận, và một tệp có sẵn cho thấy các quy ước của bạn. "Dùng helper db.query(sql, params) trong tệp bên dưới" sẽ cho ra code ăn khớp; thiếu nó, bạn có thể nhận về một kết nối cơ sở dữ liệu mới viết từ đầu. Bỏ ra những tệp không liên quan đến thay đổi, vì mỗi dòng không liên quan là thứ model có thể tìm cách dùng lại.

Yêu cầu từng bước nhỏ một

Lỗi phổ biến nhất trong "vibe coding", tức xây dựng một ứng dụng bằng cách mô tả nó cho AI, là đòi cả ứng dụng trong một lần. Model phải chọn framework, cơ sở dữ liệu, cấu trúc thư mục và cả chục tính năng trong một câu trả lời, mà một câu trả lời hiếm khi chứa nổi code chạy được cho tất cả, nên thứ thường nhận về chỉ là một bộ khung. Hãy so sánh hai tab.

Xây cho tôi một ứng dụng todo có tài khoản người dùng, cơ sở dữ liệu, nhắc việc và chế độ tối.
Try it
Example replyReplies vary between models and runs.

Đây là một ứng dụng todo full-stack dùng React, Node.js với Express và MongoDB.

Cấu trúc dự án

todo-app/
  client/   (React front end)
  server/   (Express API, auth, reminders)

server/index.js

const express = require("express");
const mongoose = require("mongoose");
const app = express();
app.use(express.json());
// ... auth routes, todo routes and the reminder scheduler go here

Để xác thực, bạn có thể thêm JWT bằng jsonwebtoken, còn để nhắc việc thì dùng một scheduler như node-cron. Cho tôi biết nếu bạn muốn tôi viết tiếp phần nào.

Câu trả lời đầu tiên không sai, nhưng nó chỉ là bộ khung: nó chọn ba công nghệ thay bạn và để phần việc thật thành các dòng chú thích. Câu trả lời thứ hai đủ ngắn để đọc, chạy được ngay khi bạn mở tệp, và cho bạn một nền tảng chạy được cho bước 2 ("giờ hãy lưu danh sách vào localStorage để nó còn sau khi tải lại trang"). Mỗi bước đủ nhỏ để khi có gì hỏng, bạn biết thay đổi nào gây ra.

Đây là prompt chaining làm bằng tay: đầu ra của một yêu cầu trở thành điểm khởi đầu của yêu cầu tiếp theo. Hãy dán phiên bản hiện tại của tệp vào mỗi bước mới, để model sửa đúng đoạn code bạn đang có chứ không phải đoạn code nó nhớ là đã viết.

Yêu cầu kế hoạch trước một thay đổi lớn

Với mọi thứ lớn hơn một hàm, hãy yêu cầu kế hoạch trước và code sau: "Liệt kê các tệp bạn sẽ thay đổi và mỗi thay đổi làm gì. Chưa viết code." Một kế hoạch đọc nhanh và sửa nhanh. Nếu nó đề xuất một dependency mới mà bạn không muốn, hoặc bỏ sót một tệp bạn biết là có liên quan, bạn sửa điều đó trong một câu thay vì phát hiện ra nó giữa ba trăm dòng code.

Kiểm tra những gì nhận về

Code được tạo ra thường hỏng theo vài cách dễ đoán, và mỗi cách đều có một thói quen viết prompt để bắt được:

  • API bịa ra. Model có thể gọi một hàm hoặc import một package không tồn tại, chỉ vì cái tên nghe hợp lý. Hãy tra cứu những import lạ trước khi cài; ảo giác AI giải thích vì sao điều này xảy ra.
  • Trường hợp biên bị bỏ qua. Code chạy tốt với trường hợp thông thường nhưng crash với một list rỗng. Liệt kê các trường hợp biên trong prompt và yêu cầu test là cách sửa rẻ nhất.
  • Thay đổi âm thầm. Khi bạn nhờ sửa trong một tệp dài, model có thể đổi tên hoặc sắp xếp lại những đoạn code bạn không hề hỏi tới. Hãy thêm "chỉ thay đổi những gì cần thiết và liệt kê mọi thay đổi bạn đã làm".

Khi code chạy nhưng hoạt động sai, hãy chuyển sang prompt gỡ lỗi: prompt để gỡ lỗi nói rõ nên dán những gì. Trước khi merge bất cứ thứ gì quan trọng, một lượt nữa với prompt review code có thể bắt được những vấn đề mà prompt viết code chưa nghĩ tới.

Câu hỏi thường gặp

Prompt tốt nhất để viết code với ChatGPT hoặc Claude là gì?

Không có prompt thần kỳ nào cả. Những prompt hiệu quả đọc như một bản đặc tả ngắn: ngôn ngữ và phiên bản, code nhận vào gì và trả về gì, hai hoặc ba đầu vào ví dụ kèm đầu ra, các trường hợp biên, và những gì code không được dùng. Kết thúc bằng "viết thêm test cho các trường hợp này" cho bạn cách kiểm tra câu trả lời thay vì tin tưởng nó.

Prompt vibe coding là gì?

"Vibe coding" là cách xây dựng phần mềm chủ yếu bằng việc mô tả điều bạn muốn cho AI và chấp nhận code nó viết, thường không đọc kỹ. Những prompt giúp một dự án vibe coding tiếp tục chạy được là các prompt nhỏ: mỗi yêu cầu một tính năng, nói rõ những gì đã có, và yêu cầu chạy hoặc test kết quả trước khi đi tiếp. Những yêu cầu lớn làm tất cả cùng lúc là chỗ các dự án này hay vỡ.

Có nên nói cho AI biết dùng phiên bản ngôn ngữ lập trình nào không?

Có. Ngôn ngữ và thư viện thay đổi giữa các phiên bản, và nếu không được nói, model sẽ viết theo phong cách phổ biến nhất trong dữ liệu huấn luyện của nó, có thể cũ hơn môi trường của bạn. Nêu rõ phiên bản ("Python 3.12", "React 19 với function component", "Node 22, ES modules") giúp tránh những câu trả lời dựa trên API mà bạn không có.

Có thể tin code do AI viết không?

Hãy đối xử với nó như code của một đồng nghiệp mới: có lẽ gần đúng, đôi khi sai theo những cách trông có vẻ đúng. Chạy nó, test với các trường hợp biên bạn quan tâm, và đọc kỹ mọi phần liên quan tới tiền bạc, bảo mật hay dữ liệu người dùng. Model cũng có thể bịa ra hàm hoặc package không tồn tại, nên hãy kiểm tra những import lạ trước khi cài bất cứ thứ gì.

Vì sao code do AI viết bị hỏng khi dự án lớn dần?

Model chỉ thấy những gì có trong cuộc trò chuyện. Khi dự án lớn lên, nó không còn thấy những tệp không được đưa cho nó, và lấp chỗ trống bằng phỏng đoán về tên, cấu trúc và các quyết định trước đó. Hãy dán các tệp liên quan, nêu rõ các quy ước dự án tuân theo, và giữ mỗi yêu cầu chỉ một thay đổi.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu