Harness bắt đầu từ chỗ nào
Vòng lặp chỉ là bộ điều phối. Thứ biến nó thành harness là bốn mảnh cắm rời được, và trong một harness thật cả bốn đều là plugin thay được từ cấu hình.
Vòng lặp không phải harness
Bài trước kết thúc ở chỗ vòng lặp dài 101 dòng và phần dôi ra là những chỗ nó từng vỡ. Nhưng 101 dòng đó vẫn chưa phải một harness.
Vòng lặp làm đúng một việc: điều phối. Nó gọi model, chạy tool, lặp. Nó không biết lịch sử được ghi ở đâu, cũng không biết tool đến từ đâu. Nó không biết ai giữ ngân sách ngữ cảnh, và không biết ai cho phép một lời gọi chạy.
Bốn câu hỏi đó là bốn mảnh. Trong một harness thật, cả bốn đều là plugin — nghĩa là cả bốn đều thay được mà không sửa vòng lặp.
Bốn mảnh, và chúng nằm ở đâu
Trong harness tôi đọc, mỗi profile khởi động bằng cách nạp 94 dòng cấu hình plugin (gọi tắt là row) từ một bundle nền. Bốn mảnh cốt lõi nằm rải trong danh sách đó, mỗi mảnh mang một id riêng để có thể ghi đè (patch) qua cấu hình:
| Mảnh | Mục cấu hình (Row) | Nhiệm vụ |
|---|---|---|
| Sổ ghi bền | session, session-persistence-jsonl | Lưu mọi sự kiện model từng thấy |
| Sổ đăng ký tool | tool-* (17 mục) | Khai báo tool nào tồn tại |
| Ngân sách ngữ cảnh | compaction-basic | Giữ request vừa cửa sổ |
| Tầng quyền | approval, permission, sandbox | Quyết định cái gì được chạy |
Bốn cái tên, bốn mục cấu hình độc lập, không cái nào nằm cứng trong thân vòng lặp.
Đó là điểm mấu chốt. Vòng lặp chỉ gọi ctx.tools.execute() mà không cần biết tool đó là lệnh bash nội bộ hay một MCP server ở nửa vòng trái đất. Nó ghi sự kiện vào session mà không bận tâm session đang nằm trên RAM hay file JSONL dưới đĩa.
Thay một mảnh mà không sửa code
Đây là chỗ luận đề trở nên kiểm chứng được thay vì chỉ nghe hợp lý.
Mỗi mục cấu hình đều có một id. Muốn đổi hành vi của một plugin, bạn viết một bản vá (patch) trỏ vào đúng id đó. Bản vá sẽ thay toàn bộ cấu hình của plugin thay vì gộp lẫn lộn — nhờ vậy kết quả luôn xác định và không phụ thuộc thứ tự đọc file.
# Row gốc trong bundle chỉ có tên, không có config:# - id: compaction-basic# name: '@deepseek-ai/dsh-compaction-basic'
- id: compaction-basic config: thresholdRatio: 0.8 # mặc định của package retainRatio: 0.16 # phần hội thoại gần nhất giữ nguyên vănHai khóa đó đều là tham số thật của package, và giá trị trên là mặc định của tác giả bundle. Cái bạn viết vào patch là của bạn.
Và tài liệu kiến trúc nói thẳng hệ quả:
There is no privileged core to patch: you extend dsh by mounting a plugin beside the others, and registrations are effects that unwind when their plugin unloads.— dsh, docs/architecture.md
Hai vế, và vế thứ hai quan trọng ngang vế thứ nhất. Plugin đăng ký thứ gì thì khi gỡ nó ra, thứ đó tự rút lại. Không còn tool mồ côi, không còn listener treo lại sau khi plugin chết. Đây là điều kiện để việc thay mảnh diễn ra lúc chạy, chứ không chỉ lúc build.
338 package, 70 tên tool
Bây giờ tới con số làm rõ cả bài.
Harness tôi đọc có 338 package trong workspace, trải trên 55 nhóm package, và 14.004 file được track. Nhưng bề mặt mà model nhìn thấy chỉ là 70 tên tool trong catalog.
Khoảng cách đó không phải lãng phí. Nó chính là định nghĩa của harness.
Một harness tốt giữ cho bề mặt hướng ra model nhỏ và ổn định, trong khi khối đứng sau lớn và thay đổi được. Model thấy một danh sách tool ổn định. Phía sau, mỗi tool trong đó có thể đến từ bundle nền, từ plugin ngoài cây, hoặc từ một MCP server. Model không phân biệt được, và không cần phân biệt.
Nếu bề mặt đó lớn lên theo khối đứng sau, bạn không có harness. Bạn có một đống package lộ ra ngoài.
Xem dữ liệu diagram
type: architecture
title: Cây plugin của một harness
desc: Từ 338 package workspace, một profile cắm 94 row plugin; bốn mảnh lõi nằm rải trong đó và đều thay được từ cấu hình
nodes:
- id: profile
label: Profile (web, headless, sdk)
kind: external
- id: bundle
label: Bundle nền, 94 row
kind: input
- id: log
label: Sổ ghi bền
kind: store
focal: true
- id: registry
label: Sổ đăng ký tool
focal: true
- id: budget
label: Ngân sách ngữ cảnh
- id: perm
label: Tầng quyền
kind: security
- id: surface
label: 70 tên tool model thấy
kind: external
edges:
- [profile, bundle]
- [bundle, log]
- [bundle, registry]
- [bundle, budget]
- [bundle, perm]
- [registry, surface]
- [perm, surface]Bốn profile, một bundle nền
Một chi tiết đáng chú ý: các profile không phải bốn bản sao. Chúng xếp lớp trên cùng một bundle nền — dsh-base, 94 row — rồi mỗi profile thêm phần của mình.
| Profile | Số row riêng | Thêm gì |
|---|---|---|
| web | 112 | Ứng dụng trình duyệt |
| sdk-minimal | 32 | Cây SDK tối giản, cố ý không dùng bundle nền |
| sdk | 7 | Server JSON-RPC |
| headless | 5 | Runner một lần, không server |
| acp | 5 | Server tự động hoá |
Nhìn headless: năm row. Một harness chạy một lần rồi thoát chỉ cần thêm năm dòng cấu hình lên trên bundle nền. Không có nhánh if (headless) nào trong vòng lặp.
Đó là bài kiểm tra tôi dùng cho mọi thiết kế harness: thêm một chế độ chạy mới tốn bao nhiêu dòng trong lõi? Nếu câu trả lời lớn hơn không, các mảnh chưa thật sự rời.
Làm được ngay
- Kể tên bốn mảnh trước khi viết vòng lặpChốt sổ ghi, sổ đăng ký, ngân sách, và quyền là bốn interface riêng. Nếu bạn chưa gọi tên được chúng, vòng lặp sẽ tự nuốt chúng.
- Cho mỗi mảnh một cái tên trong cấu hìnhMột
idổn định cho mỗi thành phần, để patch trỏ vào được. Không có tên thì không có chỗ để thay. - Đo bề mặt model thấy, và giữ nó nhỏĐếm số tool và số trường model nhìn thấy. Con số đó nên tăng chậm hơn nhiều so với số package đứng sau.
- Thử gỡ một plugin xem có sạch khôngCắm rồi gỡ. Nếu còn tool mồ côi hay listener treo, việc đăng ký chưa phải một effect rút lại được.
Nguyên tắc
Vòng lặp là phần dễ nhất của một harness, và cũng là phần ít quyết định nhất. Thứ quyết định là bốn mảnh quanh nó có cắm rời được hay không.
Nếu bạn không thay được model adapter mà không sửa vòng lặp, bạn chưa có harness. Bạn có một vòng lặp với đèn báo.— tusgino
- Vòng lặp chỉ điều phối; harness là sổ ghi, sổ đăng ký, ngân sách ngữ cảnh và tầng quyền.
- Cho mỗi mảnh một
idổn định trong cấu hình, để thay hành vi bằng patch thay vì sửa mã nguồn. - Giữ bề mặt hướng ra model nhỏ và ổn định: 338 package đứng sau, 70 tên tool model thấy.
- Đăng ký phải là một effect rút lại được, nếu không thì gỡ plugin sẽ để lại rác.
- Phép thử cuối: thêm một chế độ chạy mới tốn bao nhiêu dòng trong lõi? Câu trả lời đúng là không dòng nào.