Skip to content

Tool Plane: 30 câu hỏi đã tự đặt và tự trả lời ​

Đặc tả 0.1 để lại nhiều chỗ chưa quyết. Trang này ghi lại ba mươi câu hỏi đã tự đặt trong lúc dựng bản MVP, đề xuất cho từng câu, câu trả lời đã chọn, và thứ đã thật sự làm trong mã.

Ghi lại vì một lý do: câu trả lời thì đọc được trong mã, còn những phương án đã loại bỏ thì không. Phiên sau nhìn vào mã sẽ thấy kết quả mà không thấy vì sao, rồi "sửa" đúng thứ đã được cân nhắc kỹ.

Ký hiệu: ✅ đã làm · 📋 đã quyết, chưa làm · ⏸ cố ý hoãn.


Phần I: Ranh giới kiến trúc ​

1. Tool Plane có được suy luận không, dù chỉ một chút? ​

Đề xuất. (a) Không, tuyệt đối. (b) Cho phép một chút, ví dụ tự chọn nhánh gốc khi agent không nói.

Trả lời: (b), nhưng chỉ với mặc định tất định. ✅ github.create_branch tự tra nhánh mặc định khi thiếu from. Đó là tra cứu, không phải suy luận: cùng đầu vào luôn cho cùng kết quả và không cần gọi model. Ranh giới là gọi model: Tool Plane không bao giờ gọi một model. Ngày nó gọi là ngày nó thành một agent, và lúc đó không còn ai canh nó.

2. Tool Plane nên là một Worker riêng hay một domain trong api.conan.school? ​

Đề xuất. (a) Domain trong API sẵn có, dùng lại bindings, ít hạ tầng. (b) Worker riêng.

Trả lời: (b) Worker riêng. ✅ api.conan.school giữ chứng thư cho hàng chục việc và có bề mặt HTTP rất rộng. Đặt lớp quản trị vào đó nghĩa là mọi lỗ hổng ở bất kỳ route nào cũng thành lỗ hổng của lớp quản trị. Một Worker riêng có một danh sách secret nhỏ và hai bề mặt.

3. Kiến trúc dùng MCP hay REST? ​

Đề xuất. (a) MCP. (b) REST + mô tả OpenAPI. (c) Cả hai.

Trả lời: (a) MCP là bề mặt cho agent. ✅ MCP đã cho sẵn khám phá tool, schema đầu vào và kết quả có cấu trúc, đúng ba thứ cần. /admin là REST vì người đọc nó.

4. Registry nên nằm trong mã hay trong cơ sở dữ liệu? ​

Đề xuất. (a) Chỉ mã. (b) Chỉ D1, đổi tool không cần deploy. (c) Chia đôi.

Trả lời: (c), và sự bất đối xứng là điểm chính. ✅ Mã sở hữu tool là gì (schema, executor). D1 giữ trạng thái vận hành (enabled, requires_approval). (b) nghe hấp dẫn cho tới khi nhận ra nó nghĩa là một hàng trong bảng cấp được năng lực mà không lượt review nào thấy.

5. Đồng bộ registry thì có ghi đè enabled không? ​

Đề xuất. (a) Ghi đè hết, mã là sự thật. (b) Không ghi đè enabled và requires_approval.

Trả lời: (b). ✅ Người vận hành tắt một tool giữa sự cố, rồi lượt deploy sau lặng lẽ bật lại, đó đúng là loại hỏng mà hệ vẫn trông như đang chạy bình thường.

6. Máy chính sách nên đọc D1 trực tiếp hay là hàm thuần? ​

Đề xuất. (a) Đọc thẳng, ít lớp. (b) Tách: một hàm thuần, một lớp nạp dữ liệu.

Trả lời: (b). ✅ Nhờ vậy toàn bộ bảng quyết định, 17 bài kiểm, chạy được không cần cơ sở dữ liệu, không cần Worker. Một lớp phân quyền không kiểm được là một lớp không ai dám sửa, và một lớp không ai dám sửa sẽ dần bị đi vòng.

7. Có nên có POST /tools/:name/execute cho tiện ngoài MCP? ​

Trả lời: không. ✅ Một đường vào thứ hai là một đường vòng qua chính sách đang chờ được ai đó dùng "chỉ lần này thôi". Mọi thứ đi qua invoke.ts.


Phần II: Phân quyền ​

8. Mặc định khi không có luật nào khớp? ​

Trả lời: từ chối. ✅ Có bài kiểm riêng (no grant is a deny). Đây là câu hỏi duy nhất trong danh sách không có phương án thay thế đáng cân nhắc.

9. Chính sách allow có nới được một deny không? ​

Đề xuất. (a) Có, luật cụ thể hơn thắng. (b) Không, không bao giờ.

Trả lời: (b). ✅ Với (a), một chính sách viết cẩu thả có thể cấp quyền. Ở đây, chính sách chỉ siết được. Nới lỏng chỉ xảy ra bằng cách sửa grant, một hành động có chủ đích, nhắm vào một agent cụ thể.

10. Scope khớp bằng regex hay glob thô sơ? ​

Trả lời: glob thô sơ. ✅ Chỉ *, tiền tố, khớp đúng, danh sách phẩy. Một regex lấy từ cơ sở dữ liệu là mã chạy được lấy từ cơ sở dữ liệu. Có bài kiểm khẳng định .* được coi là chuỗi thường và không khớp mọi thứ.

11. Lượt gọi không có tài nguyên thì grant có phạm vi có khớp không? ​

Trả lời: không. ✅ resource = null chỉ khớp *. Nếu không, một tool có resourceOf viết sai sẽ lặng lẽ thoát mọi ràng buộc phạm vi, hỏng mà mọi bài kiểm vẫn xanh.

12. Nhiều grant cùng khớp thì cái nào thắng? ​

Đề xuất. (a) Cái đầu tiên. (b) Chặt nhất thắng. (c) Cụ thể nhất thắng, deny tường minh thắng tất.

Trả lời: (c). ✅ (b) làm scope hẹp trở nên vô dụng, cấp thêm một quyền hẹp lại không có tác dụng gì. (c) cho deny tường minh sức mạnh tuyệt đối mà vẫn để quyền hẹp có nghĩa.

13. Môi trường lấy từ đối số hay từ định nghĩa tool? ​

Trả lời: từ định nghĩa tool. ✅ environmentOf là một hàm trong mã. cloudflare.deploy_production luôn trả production. Nếu môi trường là một trường đối số, agent tự khai môi trường của chính nó, và mọi chính sách theo môi trường thành lời đề nghị.

14. Có nên có "chế độ chỉ đọc" toàn cục? ​

Trả lời: có, nhưng bằng chính sách. 📋 Một hàng policies với effect = deny, tool_scope = *, đặt vào lúc cần rồi tắt đi. Không cần cờ riêng; cơ chế đã đủ diễn đạt.

15. Giới hạn tần suất theo agent, theo tool, hay theo cặp? ​

Trả lời: theo cặp, 60/phút. ✅ Theo agent thì một tool đọc ồn ào chặn mất tool ghi quan trọng. Theo tool thì một agent kẹt vòng lặp làm nghẽn cả các agent khác.


Phần III: Bí mật ​

16. Có bao giờ nên đưa một token phạm vi hẹp, sống ngắn cho agent không? ​

Đề xuất. (a) Không bao giờ. (b) Có, nếu hẹp và hết hạn nhanh.

Trả lời: (a) không bao giờ. ✅ Một token vào ngữ cảnh model là một token đã rò: nó vào prompt, vào log nhà cung cấp, vào bản ghi hội thoại. Không thu hồi được thứ model đã đọc. "Hẹp và ngắn" chỉ làm nhỏ thiệt hại, không đổi bản chất.

17. GitHub App hay personal access token? ​

Trả lời: GitHub App. ✅ Phạm vi theo cài đặt, token hết hạn một giờ, thu hồi được mà không đụng tài khoản của người nào. PAT gắn với một con người, người đó nghỉ việc là hệ thống chết.

18. Cache token cài đặt ở đâu? ​

Đề xuất. (a) KV. (b) Biến trong module isolate. (c) Không cache.

Trả lời: (b). ✅ Cache trong isolate, mất khi isolate chết, không bao giờ được lưu xuống đĩa. KV nghĩa là chứng thư nằm yên ở một chỗ khác, thêm một chỗ để rò, đổi lấy vài trăm mili-giây.

19. Có che bí mật trong thông báo lỗi không, hay tin vào API bên ngoài? ​

Trả lời: che, ở lớp chuẩn hoá lỗi. ✅ Bài kiểm này đã bắt lỗi thật khi viết: normalizeHttpError ban đầu không che, và một thân lỗi GitHub chứa ghp_… đã đi thẳng được tới agent. Che ở đúng chỗ mọi executor phải đi qua.

20. redactDeep che theo hình dạng giá trị hay theo tên khoá? ​

Trả lời: cả hai, nhưng tên khoá là chính. ✅ Che theo hình dạng sẽ trượt những bí mật không có hình dạng, một mật khẩu cơ sở dữ liệu trông y hệt một từ thường.

21. Ghi đối số vào vết kiểm toán thì ghi gì? ​

Trả lời: hình dạng, không phải giá trị. ✅ <string:5120>, <array:3>. Nội dung file và thân PR không thuộc về một bảng kiểm toán, và thứ ai đó giấu trong đó cũng vậy.


Phần IV: Duyệt và việc chạy lâu ​

22. require_approval nên chặn request hay đỗ lại? ​

Đề xuất. (a) Giữ request mở, hỏi người. (b) Đỗ lại, trả approval_id.

Trả lời: (b). ✅ (a) đòi một con người trả lời trong vài giây, nếu không request chết và agent thấy một timeout, thông tin tệ nhất có thể trả về. (b) cho agent biết chính xác chuyện gì đang xảy ra và làm gì tiếp.

23. Một phê duyệt dùng được mấy lần? ​

Trả lời: đúng một. ✅ Tiêu thụ khi dùng, bằng UPDATE … WHERE status='approved' rồi kiểm số hàng đổi. Nếu không, một lượt deploy production đã duyệt có thể bị phát lại thành ba lượt, và bản ghi kiểm toán sẽ trông đúng cả ba lần.

24. Phê duyệt có gắn với tài nguyên và môi trường không? ​

Trả lời: có, chặt. ✅ Duyệt deploy api-conan-school không duyệt deploy thứ khác. Thiếu bất kỳ điều kiện nào đều nghĩa là "không có phê duyệt", không bao giờ là "gần đúng".

25. Phê duyệt hết hạn sau bao lâu? ​

Trả lời: 60 phút. ✅ Một phê duyệt cho "deploy bản này" mất nghĩa khi "bản này" đã đổi. Một cron dọn dẹp đánh dấu các phê duyệt quá hạn.

26. Việc chạy lâu: Workflow, Durable Object, hay waitUntil? ​

Đề xuất. (a) Cloudflare Workflow. (b) Durable Object. (c) waitUntil + bảng operations.

Trả lời: (c) cho bây giờ, (a) khi cần nhiều bước. ✅ Đặc tả nói "đừng thêm hạ tầng khi chưa có nhu cầu cụ thể". Việc chạy lâu duy nhất hiện có, kích hoạt workflow CI rồi đọc lượt chạy, là hai lượt gọi HTTP. Một Cloudflare Workflow cho hai lượt gọi là hạ tầng để chiêm ngưỡng. Giao diện (operation_id + plane.get_operation) không đổi khi chuyển sang Workflow, nên chuyển sau không tốn gì.

27. Agent xem được operation của agent khác không? ​

Trả lời: không. ✅ Truy vấn lọc theo agent_id. Danh sách operation của một agent khác là thông tin về việc đang chạy trong hệ, không có lý do nào để lộ.


Phần V: Đường ranh của Conan ​

28. cloudflare.deploy_* nên gọi API Cloudflare hay kích hoạt CI? ​

Đề xuất. (a) Gọi thẳng API Cloudflare, trực tiếp, nhanh. (b) workflow_dispatch lên GitHub Actions.

Trả lời: (b). ✅ Luật nền tảng là không deploy từ máy. Một Worker deploy hộ agent chính là deploy từ máy, chỉ khác chỗ đặt cái máy. Đi qua CI giữ một đường deploy duy nhất, một chỗ xem lịch sử, một chỗ hỏng để sửa. Chưa xong (rà soát 03.09.2026): chỉ ba workflow của tool-plane có workflow_dispatch; bảy workflow mà DEPLOY_WORKFLOWS trỏ tới (deploy-api/admin/docs/www/com/mam/mentors.yml) chưa có, nên cloudflare.deploy_* hiện không kích được chúng.

Hệ quả cố ý: Worker không có trong DEPLOY_WORKFLOWS thì không deploy được qua Tool Plane.

29. Có tool nào cho agent ghi nội dung khoá học không? ​

Trả lời: không, và đây là câu hỏi quan trọng nhất trong danh sách. ✅ conan.* chỉ đọc; executor không có đường ghi nào. Một tool như conan.create_lesson sẽ là đường vòng qua hàng rào còn lại của nội dung viết tay, vào repo trước, D1 sau (luật nền), và qua POST /ops/audit/:slug lẫn course_content_runs.

Nguy hiểm ở chỗ nó trông rất hợp lý, nhất là từ 03.09.2026 khi ai viết nội dung cũng được. "Agent cần ghi kết quả nó sinh ra" là một câu đúng trong mọi hệ khác. Ở đây agent ghi bằng migration trong git và mở PR, dấu vết, review, CI. Tool Plane không được là cửa sau đó.

30. conan.* gọi REST API hay dùng binding D1? ​

Trả lời: binding D1. ✅ Không đúc token, không egress, không giới hạn tần suất, và câu truy vấn nằm ngay trong repo thay vì ẩn sau một lượt gọi HTTP, nghĩa là nó bị review.


Bài học sau khi chạy thật ​

Ba mươi câu trên là những gì tự đặt ra trước khi hệ chạy. Mục này là thứ chỉ lộ ra sau, và nó không nằm trong danh sách nào cả.

Một bộ kiểm xanh có thể đang chứng minh nhầm thứ ​

installationToken() trả GITHUB_PAT trước nếu có. Suốt giai đoạn cả PAT lẫn GitHub App cùng tồn tại, việc kiểm "đọc repo qua GitHub" xanh đều, nhưng nó chứng minh PAT chạy được, chứ không phải App. Đúng lúc xoá PAT là hỏng, và khó truy vì bộ kiểm "đã xanh từ lâu".

Không có bài kiểm nào sai. Chỗ sai là câu kết luận rút ra từ nó.

Chữa bằng cách bắt bộ kiểm tự khai: nó đọc tên secret trên Worker (wrangler secret list chỉ trả tên, không bao giờ trả giá trị) rồi in ra ngay trước các dòng PASS:

Chứng thư GitHub đang dùng: GitHub App (không có GITHUB_PAT)

Nguyên tắc rộng hơn một lần hỏng: khi có hai đường và một đường được ưu tiên ngầm, bộ kiểm phải nói nó vừa đi đường nào, không để người đọc suy ra từ việc nó xanh.

Khoá GitHub App: tự chuyển, đừng bắt người chạy openssl ​

GitHub phát khoá App ở PKCS#1; WebCrypto chỉ đọc PKCS#8. Cách rẻ nhất là báo lỗi rõ rồi bảo người chạy openssl pkcs8 -topk8. Đã chọn cách đắt hơn: Worker tự chuyển (security/pkcs.ts). Lý do là một câu hỏi đơn giản, mỗi lần xoay khoá, ai sẽ nhớ bước đó? Một bước tay mà quên được là một bước sẽ bị quên.

Bài kiểm sinh khoá lúc chạy rồi đối chiếu từng byte với bản PKCS#8 do Node xuất ra; không khoá riêng nào vào repo. Có cả khoá 4096 bit, vì độ dài từ 128 trở lên phải mã hoá DER dạng dài, sai chỗ đó thì 2048 vẫn chạy mà 4096 thì hỏng.

Token mất thì phải cấp lại được: mà ban đầu không có đường nào ​

Tài liệu hứa "mất thì cấp lại, không lấy lại được", nhưng không có endpoint nào cấp lại, và tạo trùng id thì ném 500 từ một UNIQUE violation. POST /admin/agents/:id/rotate lấp chỗ đó, và cũng là đường thu hồi khi một token rò.

Một lời hứa trong tài liệu mà mã không giữ là một lỗi, không phải một thiếu sót nhỏ.

Việc còn lại ​

ViệcTrạng thái
Vòng lặp đầu-cuối đã chạy✅ smoke-test-tool-plane xanh 6/6 qua mcp.conan.school bằng GitHub App (run 33583032491), kiến trúc đã được xác nhận
Bộ kiểm tự nói nó chạy bằng chứng thư nào✅ đọc tên secret trên Worker và in ra trước sáu dòng PASS, xem "Bài học sau khi chạy thật" bên dưới
Worker deploy qua CI✅ mcp.conan.school (và conan-tool-plane.dac2205.workers.dev), main xanh
D1 + migration✅ tạo bằng scripts/tool-plane-setup.sh trên máy có wrangler login (token CI không có quyền D1)
Secret, registry, engineering-agent chỉ đọc✅ workflow provision-tool-plane (run 33551288878); token ở job summary
Chứng thư GitHub cho plane✅ GitHub App (App 4797567, installation 158327219). GITHUB_PAT đã xoá. Worker tự chuyển khoá PKCS#1 → PKCS#8 nên nạp thẳng file .pem GitHub cấp
Route mcp.conan.school✅ Workers Custom Domain (không phải [[routes]]), gắn qua API từ máy đã wrangler login (02.09.2026). Không khai trong wrangler.toml vì token CI thiếu quyền zone; custom domain là đối tượng cấp account nên deploy sau không đụng tới.
Giao diện duyệt trong cụm admin📋
Chuyển việc chạy lâu sang Cloudflare Workflow⏸ khi có việc nhiều hơn hai bước
Đo lường dùng tool, đánh giá agent⏸ khi có nhiều hơn một agent

Đọc tiếp ​

Tài liệu nội bộ nền tảng Conan School.