Trong quy trình phát triển dựa trên đề xuất (Proposal-driven development) như OpenSpec, việc sử dụng các câu lệnh (prompt) tổng quát thường không đáp ứng được yêu cầu khắt khe của từng giai đoạn. Để giải quyết vấn đề này, việc triển khai các Agent chuyên biệt kết hợp với hệ thống mẫu tham số hóa là giải pháp tối ưu để đảm bảo AI tạo ra nội dung chất lượng cao và nhất quán.
Thách thức khi sử dụng Prompt đơn nhất
OpenSpec quản lý toàn bộ vòng đời của một thay đổi kỹ thuật, từ lúc hình thành ý tưởng đến khi triển khai thực tế. Qua thực tế vận hành tại dự án HagiCode, chúng tôi nhận thấy các vấn đề nghiêm trọng khi dùng chung một bộ prompt:
- Giai đoạn Explore dễ bị lạc đề do thiếu các điểm neo ngữ cảnh (context anchors).
- Chất lượng các tệp thành phẩm (artifacts) không ổn định:
design.md thiếu biểu đồ trực quan, proposal.md thiếu bảng thay đổi mã nguồn.
- Tệp
tasks.md thường xuyên bị trộn lẫn các lệnh Git (như git add, git commit) - vốn là việc của hệ thống, không phải của logic nghiệp vụ.
- Thiếu sự linh hoạt để điều chỉnh hành vi của AI theo từng kịch bản cụ thể của dự án.
Kiến trúc hệ thống Prompt phân lớp
Hệ thống OpenSpec bao gồm nhiều giai đoạn với mục tiêu hoàn toàn khác nhau:
Explore (thăm dò),
New (tạo mới),
FF (tạo nhanh artifact),
Apply (áp dụng mã nguồn), và
Archive (lưu trữ).
Để tối ưu, chúng tôi xây dựng một kiến trúc prompt dựa trên mẫu (template) sử dụng định dạng Handlebars (.hbs) và siêu dữ liệu JSON. Trung tâm của hệ thống là một Enum định nghĩa các kịch bản:
public enum AgentScenario
{
OpenSpecExplore, // Giai đoạn nghiên cứu giải pháp
OpenSpecNewProposal, // Khởi tạo đề xuất mới
OpenSpecFastForward, // Tạo nhanh các tài liệu kỹ thuật
OpenSpecApplyCode, // Thực thi thay đổi mã nguồn
OpenSpecSyncStatus // Đồng bộ trạng thái
}
Mỗi kịch bản này sẽ liên kết với một tệp mẫu riêng biệt, cho phép chúng ta nhúng các ràng buộc đặc thù mà không làm ảnh hưởng đến các giai đoạn khác.
Xử lý tham số động trong Prompt
Cốt lõi của sự linh hoạt nằm ở khả năng nạp tham số động vào lời nhắc tại thời điểm thực thi. Lớp
PromptEngine chịu trách nhiệm tổng hợp ngữ cảnh:
public async Task<string> GenerateFFPromptAsync(
string featureTitle,
string technicalScope,
string language = "vi-VN",
string? customInstructions = null)
{
var contextData = new Dictionary<string, object>
{
{ "featureTitle", featureTitle },
{ "technicalScope", technicalScope },
{ "strategicGuidance", ResolveGuidance(language, customInstructions) }
};
return await RenderTemplateAsync(
AgentScenario.OpenSpecFastForward,
contextData,
language);
}
Cách tiếp cận này cho phép hệ thống truyền tải các hướng dẫn chiến lược (như yêu cầu vẽ biểu đồ trình tự hoặc sơ đồ luồng dữ liệu) trực tiếp vào Prompt mà không cần sửa đổi tệp mẫu gốc.
Ràng buộc phạm vi nội dung nghiêm ngặt
Một trong những cải tiến quan trọng nhất là thiết lập "vùng cấm" cho AI, đặc biệt là đối với tệp
tasks.md. Chúng tôi sử dụng ngôn ngữ khẳng định mạnh mẽ (MUST/SHALL) trong Prompt để định hình đầu ra:
Quy tắc cho tasks.md:
- Bắt buộc: Các nhiệm vụ liên quan đến logic nghiệp vụ, phát triển API, unit test và cập nhật tài liệu kỹ thuật.
- Nghiêm cấm: Không bao gồm các thao tác quản lý phiên bản (Git workflow), các lệnh triển khai (Deployment) hoặc các bước vận hành hạ tầng.
Đối với
design.md, AI được yêu cầu bắt buộc phải cung cấp kiến trúc hệ thống dưới dạng Mermaid script để tăng khả năng trực quan hóa thay vì chỉ mô tả bằng văn bản thuần túy.
Điểm neo ngữ cảnh trong giai đoạn Explore
Giai đoạn thăm dò (Explore) thường bị AI hiểu lầm là giai đoạn viết tài liệu. Chúng tôi điều chỉnh Prompt để tập trung vào tư duy:
- Tư duy thay vì viết: Mục tiêu là thu thập thông tin và kiểm tra tính khả thi, không phải tạo ra tài liệu lưu trữ.
- Truyền tải thông tin: Kết quả của giai đoạn Explore phải là đầu vào cô đọng cho giai đoạn tạo Proposal sau đó.
- Phạm vi: Chỉ tập trung vào các vấn đề kỹ thuật chưa rõ ràng trong phạm vi đề xuất hiện tại.
Cấu hình hướng lập kế hoạch động
Hệ thống cho phép người dùng chọn "hướng lập kế hoạch" (Planning Directions) cho mỗi lần thực thi. Mỗi hướng sẽ kích hoạt một đoạn Prompt bổ sung tương ứng:
public static class PlanningDirectionRegistry
{
public static readonly DirectionDefinition[] Options =
{
new("ui-prototype", "Tập trung vào giao diện người dùng", "Thêm các mô tả chi tiết về component và state UI..."),
new("api-first", "Ưu tiên thiết kế API", "Yêu cầu định nghĩa đầy đủ cấu trúc Request/Response và mã lỗi..."),
new("data-integrity", "Đảm bảo tính toàn vẹn dữ liệu", "Phân tích các ràng buộc cơ sở dữ liệu và giao dịch...")
};
}
Khi một tùy chọn được chọn, đoạn mã Handlebars trong mẫu sẽ tự động kích hoạt:
{{#if strategyInstruction}}
### Hướng dẫn chiến lược bổ sung
{{{strategyInstruction}}}
{{/if}}
Việc tách biệt các Agent theo giai đoạn và áp dụng hệ thống mẫu tham số hóa giúp quy trình OpenSpec trở nên ổn định và hiệu quả hơn đáng kể. AI không còn mơ hồ về vai trò của mình, từ đó tạo ra những thành phẩm kỹ thuật có giá trị thực tiễn cao cho đội ngũ phát triển.