Chữ ký hợp lệ không phải là được phép
Mandate đã ký và khoản tiền đã settle vẫn không trả lời câu người vận hành thật sự hỏi: hôm nay agent này có được tiêu khoản này, cho payee này không. Thứ tự các bước kiểm và chỗ đặt cổng duyệt mới là thứ bạn xây.
Giả sử sáng nay tiền đi hai lần, và dòng log nào cũng ghi một chữ: verified.
Request có chữ ký. Mandate kiểm xong. Khoản tiền đã settle. Không bước nào trong pipeline báo lỗi, nên không ai bị gọi dậy. Rồi người vận hành mở run ra, hỏi đúng một câu họ cần biết — agent này có được tiêu chừng này, cho payee này, hôm nay không — và công cụ trả về một danh sách những thứ có thể đã sai. Không phải "được". Cũng không phải "không được".
Chữ ký chưa bao giờ là câu hỏi đó. Nó chỉ là thứ khiến ta thôi không hỏi nữa.
"Verified" trả lời một câu hẹp hơn ta tưởng
Chữ ký hợp lệ nghĩa là request đến nguyên vẹn và do người giữ khoá gửi. Điều đó đáng có, và trong một agent stack thì khá dễ làm ra. Nó không nói gì về việc người giữ khoá đó có được phép làm đúng request này, trên đúng tài khoản này, với đúng số tiền này, vào đúng lúc này hay không.
Chuyện này cũ trong thanh toán, nhưng hay bị làm phẳng trong pipeline agent: một dấu tích xanh được dùng thay cho ba sự thật khác nhau — request không bị sửa; mandate có bao khoản đó; có người đã duyệt. Chỉ cái thứ ba là thứ kiểm soát mà tuần sau bạn còn chỉ được vào khi ai đó hỏi "ai quyết". Cách đặt vấn đề này được viết gọn ở một bài riêng: chữ ký hợp lệ không đồng nghĩa với được phép.
Verified là câu nói về đường truyền và khoá. Allowed là câu nói về thẩm quyền. Hệ thống ghi cái đầu rồi ngầm hiểu là cái sau không nói dối — nó chỉ trả lời một câu không ai hỏi, trong khi câu thật vẫn bỏ ngỏ.
Thứ tự các bước kiểm mới là thứ bạn xây
Khi đã nhận ra chữ ký chỉ là đầu vào chứ không phải phán quyết, câu hỏi thiết kế đổi hẳn. Không còn là "kiểm được mandate không" — kiểm được — mà là "các bước kiểm chạy theo thứ tự nào, và khi một bước nói không thì tôi nhận được gì". Chính thứ tự đó là sản phẩm, đúng như luận điểm trong The order of the checks is the product.
Một agent chi tiền đứng trước năm câu hỏi, mỗi câu có câu trả lời riêng:
- Ai đang hỏi. Agent nào, run nào, thuộc người vận hành nào.
- Được làm gì. Tool nào và động từ nào — đọc số dư là tool khác với gửi tiền, và bước kiểm phải nằm ở tool thứ hai.
- Cho ai. Payee hay đích đến, so với thứ đã được ghi ra trước khi run bắt đầu, không phải suy ra từ chính request.
- Bao nhiêu. Số tiền, và tổng đã tiêu trong cùng khung thời gian, vì ba khoản mỗi khoản đều dưới hạn mức thì cộng lại vẫn có thể vượt.
- Khi nào. Hạn, khung giờ, ngày. Mandate đúng tuần trước không phải bằng chứng cho sáng nay.
Thứ tự quan trọng vì mỗi bước thu hẹp bước sau, và vì vị trí của một bước quyết định xem nó có chặn được gì không. Kiểm số tiền đặt sau lệnh settle thì chỉ còn là biên lai. Kiểm payee chỉ chạy khi agent xin xác nhận thì chỉ là gợi ý. Còn bước kiểm rẻ và dễ chịu nhất — chữ ký hợp lệ — không được là bước duy nhất làm cả chuỗi dừng lại. Đặt nó đầu tiên cũng được, miễn đừng để nó return sớm.
Nửa còn lại của thứ tự là thứ người vận hành nhìn thấy. Một checker đổ ra mọi cảnh báo có thể có rồi để người ta tự ghép thành quyết định là đang chuyển việc, không phải làm việc. Cuối chuỗi nên là một câu có hoặc không, kèm lý do, đọc được lúc 7 giờ 40 sáng khi người ta chưa tỉnh.
Nhãn không phải là con số
Cùng một lỗi, ở chỗ không có gì được ký. Một integration được mô tả là chỉ-đọc, mô tả đó nằm trong file cấu hình, và mọi thứ phía sau coi nó là thuộc tính của hệ thống. Tới lúc ai đó thật sự đếm capability thì con số không khớp với nhãn — như trường hợp một MCP server y tế mà tuyên bố chỉ-đọc và kết quả đếm capability không đi cùng nhau (5 capabilities counted, 0 write).
Đọc kỹ chỗ này, vì bài học dễ nhất là bài học sai. Bài học không phải "đếm ra 0 nên yên tâm". Mà là con số 0 đó phải do đo mà ra, không phải do đọc mô tả mà suy ra. Nếu cổng duyệt của bạn đọc nhãn thì nhãn chính là toàn bộ mô hình bảo mật của bạn, và chưa ai audit một cái nhãn bao giờ.
Làm thật thì: suy quyền từ việc tool làm được gì, suy lại khi tool đổi, và giữ kết quả suy ra ở chỗ cổng đọc được. "Chỉ-đọc" trong tài liệu và "chỉ-đọc" trong grant của runner là hai vật thể khác nhau. Chỉ một trong hai từ chối được một lệnh ghi.
Một call fail để lại gì
Dạng thứ ba của cùng lỗi, và là dạng sống sót qua buổi review sự cố. Một call fail — timeout, lỗi, bị kill giữa run — và người vận hành kết luận là không có gì xảy ra. Thường là có: một record được tạo, một job bị claim, một dòng tồn tại mà ai đó phải đối soát. Call fail không phải call rỗng, đúng như bài What a failed call leaves behind đặt ra: hai agent, record của chúng, và một câu hỏi tách riêng — ai đếm được chúng.
Với agent chi tiền, điều đó nghĩa là log phải phân biệt lần thử và hệ quả. "Call fail" và "không ghi gì" là hai khẳng định khác nhau, và thường chỉ một cái đúng. Nếu bạn không trả lời được "hôm nay agent này thử trả bao nhiêu lần, bao nhiêu lần landed" mà không phải grep ba service, thì bạn không có con số; không có con số thì không có hạn mức.
Đặt cổng duyệt ở đâu trong box tự host
Cổng thuộc về ranh giới tool, trước khi có hệ quả, ở chỗ cả năm sự thật trên vẫn còn trong tay: mission, tool được grant, tham số, người gọi, đồng hồ. Qua vạch đó rồi thì bạn đang viết lịch sử, không phải viết chính sách.
Từ đó ra một danh sách ngắn, không hào nhoáng, cho agent tự host:
- Danh sách payee đọc từ cùng chỗ đọc grant, để chỉ có một câu trả lời, không phải hai.
- Hạn mức tiền đi kèm khung thời gian, và tổng luỹ kế trong khung đếm từ hệ quả, không phải từ lần thử.
- Tool chuyển tiền là tool riêng, khác tool đọc số dư, với grant riêng.
- Bước duyệt chặn call, không chạy đua với call. Call đứng chờ thì review được; call cảnh báo rồi đi tiếp là đã xong rồi.
- Mỗi lần từ chối ghi lại bước nào từ chối và nó thấy giá trị gì, để tuần sau tái lập được câu trả lời.
Và một thói quen quan trọng hơn cả mấy dòng trên: lý do hiện cho người duyệt phải nêu payee, số tiền, và tiền đi đâu. Một nút "Allow once" trong hộp thoại dạy người ta bấm cho xong. Một dòng nêu đúng *payee mới, thêm từ hôm qua, không có trong danh sách* thì người ta đọc.
Soot làm gì trong chuyện này
Soot đang ở bản preview, và làm đúng một việc hẹp trong lập luận trên: một Soot nhận mission và tập tool được grant, và một tool có thể được khai báo là cần operator duyệt. Khi đó call đứng chờ trong Nest tới khi có người quyết, chứ không chạy xong rồi báo cáo. soot check --show đọc grant hiện tại của Soot, nên câu "agent này được chạm vào gì" là một lệnh, không phải một cuộc phỏng vấn.
# xem Soot này đang thực sự được grant gì
soot check --showSoot Forge là catalog pack — skill và tool để cài — và cài một pack không tự grant tool bên trong nó. Sự tách bạch đó là cùng một ý ở tầng khác: catalog nói cái gì tồn tại, grant quyết agent này được làm gì, và người duyệt quyết hôm nay chuyện gì xảy ra.
Soot chưa công bố giá, không có gói trả tiền, không có hosted service; chạy Soot tốn đúng chi phí provider và máy hoặc VM của bạn. Điều Soot không làm, hôm nay, là tự quyết hộ bạn một mandate có tốt hay không. Nó là chỗ để quyết định được đưa ra và đọc lại được.
Câu hỏi đáng thiết kế cho
Không phải "cái này có được ký không". Cũng không phải "đã settle chưa". Mà là *ai quyết định việc này được phép, dựa trên cái gì, và mai có chỉ lại được không*. Toàn bộ bài này — thứ tự các bước kiểm, con số phía sau cái nhãn, khác biệt giữa call fail và call rỗng, cổng đặt trước hệ quả — chỉ là cách để câu đó trả lời được bằng một câu thay vì một danh sách.
Bắt đầu từ tool có thể chuyển tiền, và viết ra năm câu trả lời cho nó trước khi grant nó cho bất cứ thứ gì.