
Cách xây dựng AI Agents vào năm 2026 (Khóa học toàn diện)
AI features
- Views
- 696K
- Likes
- 368
- Reposts
- 68
- Comments
- 22
- Bookmarks
- 1.2K
TL;DR
Một bài phân tích kỹ thuật chuyên sâu về môi trường thực thi agentic-harness, giải thích cách sử dụng kiến trúc ba lớp, sandbox từ xa và nén ngữ cảnh để xây dựng các AI agents bền bỉ, sẵn sàng cho môi trường sản xuất.
Reading the TIẾNG VIỆT translation
đây là sự thật mà không ai nói với những người xây dựng AI.
hầu hết họ đang xây dựng các bản demo
tất cả những gì bạn cần xây dựng là
một tác nhân AI cấp sản xuất
TLDR; nếu bạn không muốn đọc, hãy đưa link này cho tác nhân của bạn và hỏi nó các câu hỏi: ➡️https://github.com/codejunkie99/agentic-harness
đây là tweet đã khởi đầu tất cả
vấn đề là hầu hết các kỹ sư AI không có ý tưởng rõ ràng về việc thực sự nên xây dựng cái gì khi họ quyết định nghiêm túc với các tác nhân.
một số người với tới LangChain vì các bản demo đa tác nhân trông sạch sẽ trên YouTube, và dành hai tuần tiếp theo để vật lộn với Python interop và async runtime mismatches trước khi vứt bỏ toàn bộ.
một số cố gắng xây dựng một lớp orchestration tùy chỉnh từ đầu: một vòng lặp, một kho lưu trữ phiên, một trình lắp ráp ngữ cảnh và không bao giờ hoàn thành tác nhân thực sự vì cơ sở hạ tầng đã ngốn hết thời gian.
những người khác sao chép ví dụ webhook hello-world, nhận lại phản hồi JSON, cho rằng họ hiểu hệ thống, và tung ra thứ gì đó sẽ hỏng ngay lần đầu tiên một phiên chạy quá mười phút, một remote sandbox bị sập giữa tác vụ, hoặc cửa sổ ngữ cảnh bị đầy mà không có compaction được cấu hình.
kết quả thường giống nhau: rất nhiều đường ống, không có tác nhân sản xuất, và không có mô hình tinh thần về một runtime tác nhân sản xuất thực sự trông như thế nào.
nếu mục tiêu của bạn là xây dựng và tung ra các tác nhân thực sự vào năm 2026, bạn không cần phải học sáu framework.
bạn cần hiểu một runtime đủ sâu để sở hữu một tác nhân sản xuất từ handler đến triển khai.
điều đó có nghĩa là học cách:
- kết nối kiến trúc ba lớp để logic handler của bạn tồn tại qua các lần thay đổi nhà cung cấp và thay đổi mục tiêu mà không cần chạm vào mã tác nhân
- sử dụng sessions và tasks một cách chính xác để các công việc dài không làm ô nhiễm ngữ cảnh của chính chúng
- viết roles và skills để định hình hành vi của mô hình mà không cần biên dịch lại bất cứ thứ gì
- cấu hình compaction để các phiên chạy trong hai giờ không bắt đầu hallucinating ở giờ đầu tiên
- trỏ HttpSessionEnv vào các remote sandbox để binary chạy cục bộ trong khi thực thi chạy trên Linux
- chọn đúng build target: native, node, hoặc Cloudflare, mà không cần viết lại bất kỳ logic tác nhân nào giữa chúng
- tạo connectors thay vì viết adapters bằng tay, và hiểu tại sao sự khác biệt đó lại quan trọng dưới tải thực tế
hướng dẫn này là một bài phân tích kỹ thuật đầy đủ được xây dựng từ codebase agentic-harness thực tế, sáu tuần xây dựng và phá vỡ các tác nhân thực tế với nó, và các chế độ lỗi tốn nhiều thời gian gỡ lỗi nhất.
bài viết dài hơn 4.000 TỪ và lấy từ repo và tài liệu trực tiếp, không phải các bản tóm tắt gián tiếp hay các ví dụ cấp demo.
nhưng giá trị thực sự của nó là mỗi phần đều có một đoạn mã hoạt động, một lời giải thích rõ ràng về lý do quyết định được đưa ra, và chế độ lỗi chính xác bạn sẽ gặp phải nếu bỏ qua nó.
bằng cách đó, khi bạn đọc xong, bạn có thể sở hữu một tác nhân sản xuất từ đầu đến cuối, từ handler đầu tiên đến sandbox đến công việc CI chạy nó mà không cần giám sát.
xây dựng sự hiểu biết này đã mất hơn 6 TUẦN làm việc hàng ngày với codebase, hầu hết là gỡ lỗi những thứ trông có vẻ đúng trước khi chúng hỏng trong điều kiện thực tế.
bây giờ hãy bắt đầu. ⬇️
Hình dạng của Dự án
hai crates. một binary. mọi execution target là một lựa chọn cấu hình, không phải là một bản viết lại.
- SDK là một thư viện bạn kéo vào bất kỳ dự án rust nào. CLI bao bọc nó. tác nhân của bạn là một binary rust bắt đầu bằng use agentic_harness::prelude::*.
- cargo build là toàn bộ pipeline. không có bundler. không có bước transpile. không có runtime ngôn ngữ trên máy đích. một executable độc lập cộng với một manifest.json.
ràng buộc thiết kế đã thúc đẩy mọi thứ: cùng một binary tác nhân sẽ chạy trên laptop của bạn ở chế độ tương tác, trong một công việc GitHub Actions clone một repo mới, chống lại một remote E2B sandbox qua HTTP, và trên ranh giới Cloudflare Worker mà không thay đổi một dòng logic tác nhân nào giữa chúng.
mọi quyết định trong codebase này tồn tại để tôn vinh ràng buộc đó.
3 lớp và lý do mỗi lớp tồn tại
mô hình tinh thần là ba vòng tròn đồng tâm. biết mỗi ranh giới nằm ở đâu sẽ giúp bạn tiết kiệm thời gian gỡ lỗi hơn bất cứ thứ gì khác trong hướng dẫn này.
mã rust của bạn là vòng tròn ngoài cùng.
- bạn viết handlers. handlers nhận một AgentContext. chúng gọi sessions. sessions gọi mô hình, đọc file, ghi file, chạy lệnh shell, sinh ra tasks, kết nối đến MCP servers.
- bạn không bao giờ chạm trực tiếp vào HTTP client. bạn không bao giờ phân tích phản hồi mô hình trực tiếp. SDK xử lý cả hai.
harness là vòng tròn ở giữa.
- nó quản lý registry tác nhân, định tuyến danh tính theo đường dẫn URL, xử lý session persistence qua các lần gọi, context compaction khi sessions phát triển, role và skill discovery, model selection precedence, và trait ModelClient trung lập với nhà cung cấp.
- điều đó cho phép bạn chuyển đổi Anthropic sang OpenAI sang một instance Ollama cục bộ mà không cần chạm vào mã handler.
- harness là thứ làm cho logic tác nhân của bạn có thể tái sử dụng qua các nhà cung cấp và mục tiêu.
- nó cũng là nơi tất cả những thứ hỏng trong sản xuất được xử lý: trạng thái phiên, tràn ngữ cảnh, lỗi nhà cung cấp, thứ tự yêu cầu đồng thời.
execution targets là vòng tròn bên trong.
- local filesystem. CI checkout. HttpSessionEnv trỏ đến Daytona hoặc E2B. Cloudflare Worker boundary.
- harness không quan tâm bạn đang sử dụng cái nào. handlers của bạn cũng vậy. chúng gọi session.shell() và session.write() và harness dịch những thứ đó sang bất cứ thứ gì target bên dưới cần.
- sự tách biệt này là toàn bộ vấn đề. khi E2B phát hành một phiên bản API mới, bạn cập nhật connector, không phải logic tác nhân của bạn.
- khi Anthropic tung ra claude-opus-4-7, bạn cập nhật runtime.json, không phải handlers của bạn. vòng tròn ngoài cùng giữ sạch sẽ vì vòng tròn ở giữa hấp thụ tất cả sự xáo trộn.
runtime config: file kiểm soát lớp mô hình
trước khi bạn viết một handler nào, bạn cần runtime.json trong workspace của mình.
đặt file này tại .agentic-harness/config.json hoặc tại gốc workspace dưới dạng agentic-harness.json. load_workspace_context() sẽ tự động nhặt nó.
lựa chọn mô hình tại runtime tuân theo thứ tự ưu tiên sau:
- PromptOptions::model(...): ghi đè cho mỗi lần gọi
- metadata mô hình của role đã chọn: mặc định cho mỗi role
- defaultModel từ runtime config: mặc định của workspace
điều cần hiểu: ID mô hình phải được đăng ký trước khi bạn có thể sử dụng nó. openaiCompatibleModels là danh sách harness sử dụng để kết nối client chat-completions tích hợp sẵn. nếu mô hình của bạn không có trong danh sách đó, bạn sẽ nhận được một lỗi rõ ràng khi khởi động thay vì một lỗi khó hiểu giữa phiên.
đối với các gateway tương thích OpenAI, cấu hình trông giống nhau. trỏ baseUrl vào gateway của bạn:
- không bao giờ viết một API key chữ vào runtime.json. sử dụng apiKeyEnv và giữ key thực tế trong môi trường của bạn.
- harness đọc biến môi trường tại thời điểm yêu cầu, không phải lúc khởi động — điều này có nghĩa là bạn có thể xoay vòng keys mà không cần khởi động lại server.
Danh tính tác nhân là một đường dẫn URL, không phải tra cứu registry
đây là quyết định thiết kế đầu tiên làm tôi ngạc nhiên. bây giờ tôi nghĩ nó là đúng.
không có hệ thống ID tác nhân. không có khóa registry. không có UUID bạn tự tạo. danh tính tác nhân của bạn là POST /agents/<tên>/<id>.
- harness xử lý tất cả việc ghi sổ trạng thái phiên đằng sau URL đó.
- lý do điều này hoạt động: mọi caller trong mọi hệ thống đều đã biết cách xây dựng một ID có ý nghĩa từ ngữ cảnh. một số PR. một ID chạy. một dấu thời gian kết hợp với tên tác vụ. một handle người dùng.
- bạn không cần một endpoint tạo phiên. bạn không cần lưu trữ ID phiên riêng biệt. URL là phiên.
handler tác nhân ở phía rust gọi ctx.id() để lấy bất kỳ ID nào mà caller cung cấp:
Sessions: ngữ cảnh thực thi có trạng thái
một session không chỉ là một chuỗi hội thoại. nó là ngữ cảnh thực thi đầy đủ cho một lần gọi tác nhân.
nó chứa:
- lịch sử tin nhắn với mô hình
- truy cập file workspace (đọc, ghi, chỉnh sửa, grep, glob, stat, readdir)
- thực thi shell với kiểm soát cwd và env
- đăng ký công cụ (MCP servers, custom tools)
- role được gán và lớp phủ system prompt của nó
- ngân sách compaction và watermark lịch sử
bạn nhận được một session bằng cách gọi ctx.session_with_id() với bất kỳ ID nào có ý nghĩa:
- sessions tồn tại qua các lần gọi HTTP khi bạn sử dụng cùng một ID. gọi cùng một endpoint tác nhân ba lần với cùng một ID phiên, mô hình thấy cả ba lần trao đổi như một cuộc trò chuyện liên tục.
- lịch sử tích lũy tự động. bạn không quản lý nó.
- đây là thứ làm cho các quy trình làm việc nhiều bước có thể thực hiện được mà không cần tự quản lý trạng thái. bạn tiếp tục gọi session.prompt() và harness xử lý mọi thứ khác.
khi bạn cần truyền một lượng lớn ngữ cảnh cùng với một prompt, hãy đọc file và định dạng nó nội tuyến:
session quản lý việc đếm token để bạn không vô tình làm tràn cửa sổ ngữ cảnh giữa cuộc trò chuyện. khi bạn gần đến ngân sách, compaction sẽ kích hoạt. thêm về điều đó trong một phần sau.
Tasks: các phiên con tập trung giữ cho phiên cha sạch sẽ
- đây là nguyên thủy mà tôi ước mình đã hiểu từ ngày đầu tiên. nó là sự khác biệt giữa các tác nhân duy trì sự mạch lạc qua các công việc dài và các tác nhân bắt đầu hallucinating giữa chừng.
- một task là một phiên con dùng một lần. lịch sử mới. workspace được chia sẻ. trả về kết quả cho phiên cha. lịch sử của phiên cha không bao giờ thấy bất kỳ suy luận trung gian nào của task.
- task nghiên cứu chạy trong sự cô lập. toàn bộ chuỗi suy luận của nó.
- mọi quan sát trung gian mà mô hình đưa ra về mã, mọi "khoan, để tôi kiểm tra file này nữa", vẫn ở bên trong task.
- phiên cha nhận được một bản tóm tắt sạch sẽ. đó là tất cả những gì nó thấy.
- tại sao điều này quan trọng trong thực tế: khi bạn chạy phân tích khám phá trực tiếp bên trong một phiên dài, lịch sử sẽ đầy với các lệnh gọi công cụ trung gian, câu trả lời một phần, và suy luận mô hình về những thứ không còn liên quan nữa.
- mô hình neo vào nhiễu đó khi không nên. compaction cuối cùng kích hoạt và làm mất ngữ cảnh bạn thực sự cần. tasks là bản sửa chữa phẫu thuật.
quy tắc: nếu bài toán con có một kết quả rõ ràng và không cần lịch sử hội thoại của phiên cha để hoàn thành, hãy biến nó thành một task. ngưỡng cho "biến nó thành một task" thấp hơn bạn nghĩ.
đối với phân tích song song trên một codebase: mẫu cartographer, phân tán tasks và thu thập kết quả:
mỗi task đều sạch sẽ. mỗi task tập trung vào chính xác một thư mục. phiên cha thu thập kết quả và viết tài liệu cuối cùng.
nếu bạn có 12 module, bạn chạy 12 task tập trung, mỗi task bắt đầu với không có hành lý từ các task khác.
Roles và Skills: định hình hành vi mà không cần biên dịch lại
- roles sống trong .agentic-harness/roles/. skills sống trong .agents/skills/. cả hai đều được tự động phát hiện khi harness khởi động.
- roles là các lớp phủ system prompt được phạm vi cho một lần gọi. được áp dụng tại thời điểm gọi và bị loại bỏ sau đó. chúng không tồn tại trong lịch sử tin nhắn. chúng không tích lũy qua các lần gọi.
chuỗi ưu tiên: call role > session role > agent role > không có role.
- frontmatter mô hình là tùy chọn nhưng hữu ích. nó cho phép bạn định tuyến các role cụ thể đến các mô hình cụ thể.
- role explainer của bạn chạy trên claude-sonnet-4-6 vì tốc độ và chi phí. role security-auditor của bạn chạy trên claude-opus-4-7 vì độ sâu. bạn cấu hình điều này một lần trong file role và không bao giờ nghĩ về nó nữa.
- skills là các file mô tả hành vi mà mô hình đọc khi bắt đầu một phiên.
- chúng là các file markdown trong .agents/skills/. harness tìm thấy chúng tự động. bạn không đăng ký chúng ở bất cứ đâu.
công dụng thực tế: một thư viện skills cùng với codebase của bạn mô tả cách bạn làm việc. định dạng commit message, thư viện ưa thích, quy ước đặt tên migration, mẫu thiết kế API, yêu cầu kiểm thử.
mô hình đọc điều này trước mỗi phiên. bạn chỉnh sửa markdown. hành vi cập nhật ở lần chạy tiếp theo. không cần biên dịch lại.
mô hình đọc điều này. nó viết các commit phù hợp với quy ước của bạn. bạn không nhắc nhở nó mỗi phiên. bạn duy trì một file.
Vòng lặp tác nhân coding chi tiết đầy đủ
vòng lặp tác nhân coding là trường hợp sử dụng chính mà CLI được xây dựng xung quanh. nó cũng là nơi nhiều thứ nhất có thể sai nếu bạn cấu hình sai nó.
lệnh đầy đủ với tất cả các tùy chọn quan trọng:
mỗi flag làm gì và tại sao nó quan trọng:
- --workspace . đặt thư mục gốc. tất cả các thao tác file được sandbox ở đây. tác nhân không thể đọc hoặc ghi bên ngoài đường dẫn này, được thực thi ở cấp harness — không phải bằng cách tin tưởng mô hình tự giới hạn.
- --llm auto chọn mô hình từ defaultModel trong runtime config của bạn. sử dụng --llm anthropic/claude-opus-4-7 cho các tác vụ phức tạp cần suy luận sâu, hoặc --llm anthropic/claude-sonnet-4-6 để lặp lại nhanh hơn.
- --deny-path là một khối cứng. nó khớp theo kiểu tiền tố, vì vậy --deny-path config/ bao gồm mọi thứ dưới config/. kiểm tra workspace của bạn trước lần chạy đầu tiên và liệt kê mọi đường dẫn chứa bí mật hoặc cấu hình sản xuất — không chỉ .env.
- --approve-dependencies cho phép sửa đổi Cargo.toml mà không cần bước phê duyệt của con người. bỏ qua điều này nếu bạn muốn xem xét mọi crate mới trước khi nó được thêm vào.
- --commit tự động stage tất cả các thay đổi và commit chúng khi kết thúc một lần chạy thành công với thông điệp bạn cung cấp. không có flag này, các thay đổi sẽ đến dưới dạng sửa đổi chưa được stage để bạn xem xét.
- --pr mở một pull request từ commit. yêu cầu trạng thái git sạch sẽ trước khi chạy và một nhánh thực sự, không phải detached HEAD.
bản thân vòng lặp: Inspect → Brief → LLM + Tools → Edit + Test → Commit · PR.
- inspect: đọc cấu trúc workspace, tải skills và roles, xác định các file có khả năng liên quan nhất đến prompt.
- viết sự hiểu biết của nó vào coding-brief.md trước khi chạm vào bất kỳ mã nào.
- brief: mô hình cam kết với một kế hoạch. bạn có thể đọc .agentic-harness/runs/<id>/coding-brief.md giữa lúc chạy để xem nó đã quyết định gì.
- nếu brief trông sai, hãy giết lần chạy. sẽ rẻ hơn để khởi động lại với một prompt rõ ràng hơn là để tác nhân thực thi một kế hoạch tồi.
- LLM + tools: vòng lặp chỉnh sửa-kiểm tra. mô hình thực hiện các thay đổi, chạy bộ kiểm tra, đọc đầu ra, thực hiện thêm các thay đổi. lặp lại cho đến khi các bài kiểm tra vượt qua, giới hạn lặp lại bị chạm, hoặc nó quyết định tác vụ đã hoàn thành.
commit · PR: stage, commit, push, mở PR với diff đính kèm.
mỗi lần chạy ghi sáu artifact vào .agentic-harness/runs/<id>/:
- coding-brief.md: kế hoạch mà tác nhân đã cam kết trước khi viết bất kỳ mã nào
- summary.md: tài khoản có thể đọc được của con người về những gì đã làm, những gì đã thử, và tại sao
- run.json: siêu dữ liệu có cấu trúc: mô hình đã sử dụng, tổng thời gian, số lượng token đầu vào/đầu ra, số lần lặp, trạng thái thoát cuối cùng
- events.jsonl: mọi lệnh gọi công cụ theo thứ tự với đầy đủ đầu vào và đầu ra, để gỡ lỗi những gì đã sai
- diff.patch: diff hoàn chỉnh của tất cả các thay đổi file
- checks.json: kết quả kiểm tra và lint cuối cùng đã xác định thành công hay thất bại
Mẹo cần nhớ
- coi những thứ này như các bản ghi có cấu trúc, không phải đầu ra tạm thời. tôi commit các artifact chạy vào repo cho bất kỳ tác vụ nào tôi cần có thể tái tạo.
- chỉ riêng run.json (2KB) cho bạn biết mô hình, chi phí token, và liệu nó có thành công hay không. events.jsonl cho bạn biết chính xác tác nhân đã làm gì và theo thứ tự nào khi bạn cần gỡ lỗi một lần chạy tồi.
đối với CI, mẫu là:
HttpSessionEnv: chạy binary cục bộ, thực thi từ xa
- đây là khả năng tôi mất nhiều thời gian nhất để hiểu đầy đủ. tôi sử dụng nó trên hầu hết mọi tác vụ chạm đến cơ sở hạ tầng bây giờ.
- binary tác nhân chạy trên máy của bạn hoặc trong CI. các thao tác filesystem và shell thực thi bên trong một remote sandbox.
- tác nhân không biết hoặc quan tâm nó đang ở môi trường nào.
sử dụng agentic_harness::HttpSessionEnv;
giao thức dây là JSON qua HTTP. mọi thao tác:
- exec
- read
- write
- edit
- grep
- glob
- stat
- readdir
- mkdir
- rm có một hình dạng yêu cầu/phản hồi được xác định.
bất kỳ sandbox nào triển khai giao thức này đều hoạt động như một target HttpSessionEnv.
để kết nối một sandbox được đặt tên:
các connector tích hợp sẵn xử lý boilerplate xác thực và vòng đời cho Vercel Sandbox, Daytona và E2B:
- trường hợp sử dụng cụ thể tôi sử dụng cái này nhiều nhất: tái tạo lỗi CI trong một môi trường Linux sạch sẽ.
- tác nhân clone repo tại hash commit bị lỗi chính xác, chạy lệnh kiểm tra bị lỗi chính xác, đọc đầu ra đầy đủ, chẩn đoán lỗi và viết một báo cáo.
- tôi đọc báo cáo. tôi không bao giờ chạm vào máy cục bộ của mình. sandbox bị loại bỏ khi phiên kết thúc.
hiệu suất mà không ai cảnh báo bạn: mọi lệnh gọi shell qua HttpSessionEnv là một vòng lặp mạng. các vòng lặp chặt: chỉnh sửa, kiểm tra, kiểm tra đầu ra, chỉnh sửa: tích lũy độ trễ nhanh chóng.
một vòng lặp 40 lần lặp mất 5 giây cục bộ sẽ mất vài phút khi chạy trên remote sandbox nếu mỗi lần lặp thực hiện ba lệnh gọi shell riêng biệt.
sửa chữa: gộp công việc shell vào các script.
một lần gọi mỗi lần lặp thay vì ba. viết script một lần, chạy nó nhiều lần. sự khác biệt về độ trễ trên một vòng lặp 40 lần lặp là có thật.
Build targets: cùng một codebase, ba hình dạng triển khai
native là mặc định. một binary. một manifest. không có gì khác trên máy đích. chạy ở bất cứ đâu có thể thực thi một binary Linux gốc.
node dành cho các nền tảng lưu trữ yêu cầu một entrypoint Node. bản build tạo ra server.mjs khởi động binary rust gốc như một tiến trình con và proxy HTTP đến nó. logic tác nhân vẫn chạy dưới dạng rust. lớp Node là một shim HTTP 30 dòng.
Cloudflare dành cho triển khai biên.
- bản build tạo ra một file ranh giới Worker và liên kết một bộ điều hợp ứng dụng tương thích với Worker.
- handlers biên dịch sang WASM thông qua WASM JSON ABI.
- các ràng buộc Durable Object xử lý session persistence qua Cloudflare KV.
ràng buộc quan trọng về Cloudflare: Workers không hỗ trợ các lệnh shell chạy dài. chúng không có filesystem thực sự.
chúng không hỗ trợ cargo hoặc bất kỳ công cụ xây dựng nào. --target cloudflare dành cho xử lý webhook, route metadata, các endpoint kiểm soát nhỏ và định tuyến Durable Object, không phải cho công việc coding.
đối với bất cứ thứ gì cần chạy cargo test, hãy ủy quyền cho một tiến trình gốc hoặc remote sandbox.
ma trận quyết định thực tế:
- gửi một tác nhân dưới dạng API mà các dịch vụ khác gọi → native đằng sau nginx hoặc một nền tảng được quản lý
- lưu trữ trên Railway, Render hoặc một nền tảng mong đợi Node → node
- ingestion webhook, định tuyến nhẹ, quản lý trạng thái Durable Object → cloudflare
- mọi thứ khác → native
Đầu ra có hướng dẫn schema: struct rust được kiểu từ phản hồi mô hình
yêu cầu mô hình trả về JSON và hy vọng nó làm được là một nửa giải pháp.
việc harness trích xuất, xác thực và deserialize nó thành struct rust của bạn là giải pháp đầy đủ.
mô hình có thể trả về văn xuôi suy luận cùng với payload đã được kiểu trong cùng một phản hồi. harness trích xuất khối kết quả giữa các dấu hiệu
- --RESULT_START--- và ---RESULT_END---. bạn nhận được một struct rust. an toàn kiểu tại thời điểm biên dịch từ đầu ra mô hình đến logic handler của bạn.
- schema làm hai việc: nó cho mô hình biết hình dạng nào cần tạo ra, và nó cung cấp cho harness thứ gì đó để xác thực trước khi deserialization.
- nếu mô hình trả về thứ gì đó không khớp với schema, bạn nhận được PromptError::SchemaValidationFailed thay vì một panic ba call sites sau đó khi bạn truy cập một trường bị thiếu.
Công cụ MCP: vươn ra ngoài sandbox
khi tác nhân cần các khả năng vượt quá file và shell, connect_mcp là cửa thoát hiểm.
tác nhân nhận được toàn bộ bộ công cụ của MCP server. không có định nghĩa công cụ nào để viết. mô tả đến từ server. mô hình quyết định khi nào gọi công cụ nào dựa trên những mô tả đó.
bạn có thể kết nối nhiều MCP server với một phiên:
- mô hình gọi các công cụ dựa trên mô tả của chúng. một mô tả mơ hồ như "tìm kiếm sentry" được gọi không nhất quán.
- một mô tả nói "gọi cái này trước khi trả lời bất kỳ câu hỏi nào về lỗi, sự cố hoặc vấn đề sản xuất" được gọi một cách đáng tin cậy.
- nếu bạn kiểm soát MCP server, hãy viết các mô tả mang tính chỉ định: cho mô hình biết khi nào nên gọi, không chỉ những gì nó trả về.
Connectors: tạo adapters thay vì viết chúng
thay vì viết mã adapter bằng tay chống lại một API không quen thuộc, hãy đưa một công thức connector cho tác nhân coding của bạn:
- công thức connector là một mô tả có cấu trúc về API sandbox và hợp đồng SessionEnv mà nó cần thỏa mãn.
- tác nhân coding đọc nó, viết module adapter rust, xử lý xác thực, bao bọc vòng đời nhà cung cấp và hiển thị nó dưới dạng HttpSessionEnv.
- bạn xem xét diff. bạn merge nó. adapter sống trong dự án của bạn. bây giờ nó là mã của bạn.
tôi đã kết nối Daytona sử dụng cái này trong khoảng 20 phút bao gồm toàn bộ chu trình xem xét. tác nhân đã có đúng định dạng header auth ngay từ lần đầu tiên.
viết adapter từ đầu dựa trên tài liệu Daytona sẽ mất gần hết một buổi chiều và ít nhất hai giả định sai về luồng refresh token.
khi connector được tạo:
Nén tự động: xử lý các phiên dài mà không mất ngữ cảnh
các phiên chạy dài tích lũy lịch sử.
cuối cùng chúng làm tràn cửa sổ ngữ cảnh của mô hình.
harness xử lý việc này tự động, nhưng bạn cần cấu hình nó đúng cách nếu không bạn sẽ mất ngữ cảnh đúng vào thời điểm sai.
context_window_tokens là tổng ngân sách cho phiên.
- reserve_tokens là thứ bạn giữ lại cho phản hồi của mô hình. giới hạn hiệu quả cho lịch sử là context_window_tokens - reserve_tokens.
- keep_recent_messages là số lượng tin nhắn ở phần đuôi luôn được giữ nguyên văn bất kể nén.
khi lịch sử vượt quá ngân sách, harness yêu cầu mô hình tóm tắt mọi thứ giữa system prompt và phần đuôi được giữ lại.
bản tóm tắt đó thay thế phần giữa. các tin nhắn ở đuôi vẫn còn nguyên vẹn. phiên đã nén nhỏ hơn và lần gọi tiếp theo phù hợp trong ngân sách.
sự đánh đổi là có thật: các bản tóm tắt mất đi độ chính xác. một quyết định cụ thể được đưa ra 50 tin nhắn trước: "chúng tôi đã chọn authlib vì nó là thư viện duy nhất hỗ trợ PKCE hoạt động với mô hình middleware của axum": có thể tồn tại dưới dạng "chúng tôi đã chọn authlib cho xác thực" trong bản tóm tắt.
nếu độ chính xác đó là quan trọng cho các quyết định sau này trong phiên, hãy lưu trữ nó một cách rõ ràng:
- viết các quyết định vào file. files tồn tại qua quá trình nén. mô hình có thể đọc lại chúng theo yêu cầu. lịch sử không cần phải mang mọi thứ nếu workspace làm được.
- chạy agentic-harness doctor để xem cửa sổ ngữ cảnh thực tế được báo cáo của mô hình bạn. đặt context_window_tokens thành 80-90% giá trị đó.
- bộ đếm token không hoàn toàn chính xác ở phía mô hình và một lần đọc file lớn có thể đẩy bạn vượt quá nếu bạn đang ở mức 99%.
Những điều cần chú ý
- Ô nhiễm lịch sử phiên
- vấn đề: phân tích khám phá bên trong một phiên dài làm nhiễu các prompt sau đó với nhiễu từ giai đoạn khám phá
- sửa chữa: sử dụng tasks. lịch sử task không bao giờ chạm vào phiên cha. ngưỡng cho "biến nó thành một task" thấp hơn bạn nghĩ
- Bất ngờ về thứ tự ưu tiên role
- vấn đề: một role ở cấp độ gọi làm lu mờ role phiên. mô hình hành xử khác với mong đợi và bạn không biết tại sao
- sửa chữa: session role đặt danh tính. call role thu hẹp trọng tâm. chúng xếp lớp — call role thêm vào, nó không nên hủy bỏ
- Khoảng trống --deny-path
- vấn đề: bạn từ chối .env. bí mật của bạn cũng sống trong .env.local và config/staging.yaml. tác nhân đọc một trong số chúng
- sửa chữa: từ chối các tiền tố, không phải tên file. --deny-path config/ bao gồm mọi thứ bên dưới nó
- Detached HEAD trong CI
- vấn đề: tác nhân chỉnh sửa, kiểm tra vượt qua, commit thất bại — vì không có nhánh để commit vào
- sửa chữa: git checkout -b agent-run-$RUN_ID trước khi gọi harness
- Độ trễ HttpSessionEnv trong các vòng lặp chặt
- vấn đề: 40 lần lặp với ba lần gọi shell mỗi lần là vài phút độ trễ mạng thuần túy
- sửa chữa: viết agent-check.sh làm mọi thứ trong một lần gọi. một lần gọi mỗi lần lặp
- Đánh giá thấp ngân sách ngữ cảnh
- vấn đề: compaction kích hoạt giữa tác vụ. mô hình mất kế hoạch và bắt đầu ứng biến từ bản tóm tắt
- sửa chữa: chạy agentic-harness doctor để lấy cửa sổ thực tế. đặt ngân sách thành 80-90% của nó
- Runtime config được tải sau khi đăng ký handler
- vấn đề: một handler chạy trước khi
load_workspace_context(). không có model nào được đăng ký. lỗi trông không giống vấn đề cấu hình chút nào - khắc phục: luôn gọi
load_workspace_context()trongapp()trước khi kết nối bất kỳ agent nào
- --llm tự động thay đổi giữa các lần chạy
- vấn đề:
defaultModelbị cập nhật. hai lần chạy cách nhau sáu tháng không thể so sánh được. bạn không thể tái tạo lần chạy đầu tiên - khắc phục: ghim model trong
runtime.jsoncho bất cứ thứ gì cần khả năng tái tạo
- xóa các artifact của lần chạy
- vấn đề: bạn dọn dẹp thư mục
runs/bằng một rule gitignore. ba tuần sau bạn cần tái tạo một hồi quy và mọi thứ đã biến mất - khắc phục: commit các artifact của lần chạy cho bất kỳ tác vụ nào bạn cần tái tạo.
run.jsonchỉ 2KB. hãy giữ nó
những điều tôi sẽ làm khác
- chạy hướng dẫn agentic-harness trước khi động vào bất cứ thứ gì.
- viết test cấp session trước khi viết logic handler.
- sử dụng tasks cho mọi thứ có sub-deliverable.
- ghim model ngay từ lần chạy nghiêm túc đầu tiên.
- lưu quyết định vào file, không lưu trong lịch sử session.
- gộp các thao tác shell ngay từ đầu khi sử dụng sandbox từ xa.
kết luận
hầu hết các framework agent đều là wrapper xung quanh một lời gọi API. đây là một runtime.
một wrapper giải quyết "làm cho model phản hồi." một runtime giải quyết "đưa agent lên production và giữ nó hoạt động sau khi model thay đổi, sau khi sandbox thay đổi, sau khi codebase thay đổi, sau khi session chạy hai giờ và tràn context window."
kiến trúc 3 lớp
- code của bạn
- harness
- execution target
là thứ làm cho điều đó khả thi. bạn viết handlers. harness hấp thụ toàn bộ độ phức tạp vận hành. execution target là một lựa chọn cấu hình.
những thứ không thay đổi: logic handler, cấu trúc session, mẫu task, định nghĩa role, file skill. những thứ thay đổi: models, providers, nhà cung cấp sandbox, deploy targets.
kiến trúc được thiết kế để những thứ thay đổi không bao giờ chạm vào những thứ không thay đổi.
đó là sự đặt cược. đó là sự đặt cược đúng đắn.
hy vọng bạn thích đọc bài viết này và khám phá cách tôi xây dựng cho agents nói chung ❣️
Tuyên bố miễn trừ trách nhiệm
Bài viết này được tác giả nghiên cứu và viết, được biên tập bởi Minimax-M2.7. Ảnh thumbnail được lấy từ Pinterest.
Harrison Chase "memory should be open!" —
[https://x.com/hwchase17/status/2046308913939919232Harrison](https://x.com/hwchase17/status/2046308913939919232Harrison)
Chase :"Your Harness, Your Memory" —
[https://www.langchain.com/blog/your-harness-your-memory](https://www.langchain.com/blog/your-harness-your-memory)
Vivek Trivedi :"The Anatomy of an Agent Harness" —
[https://www.langchain.com/blog/the-anatomy-of-an-agent-harness](https://www.langchain.com/blog/the-anatomy-of-an-agent-harness)


