Một model AI mà không viết bài, không sinh code, hỏi cũng không giải thích reasoning cho mình nghe. Nhưng nó lại có thể tham gia điều khiển một agent thao tác trên hệ thống.
Nghe hơi ngược với những gì mình thường mong đợi ở một model mới nhỉ.
Jev khiến mình muốn nhìn kỹ hơn vào một việc khá quen: bên trong một AI Agent, thực ra mình đang giao những loại công việc nào cho model? Viết một đoạn nội dung, lên kế hoạch xử lý sự cố, chọn một file để đọc, hay xác định nút tiếp theo cần click là những việc rất khác nhau. Khi ghép chúng vào một agent loop, sự khác biệt đó đôi khi bị che đi bởi một lời gọi model chung.
Bài này đi từ cơ chế của Jev đến cách chia việc trong agent. Phần mình thấy đáng bàn nhất nằm ở chỗ: khi có thêm một model chuyên cho những quyết định nhỏ, người xây hệ thống sẽ tổ chức công việc khác đi thế nào?
Jev là gì, và vì sao lại gọi là System One Model?
Jev là model của TypeSafe AI, được thiết kế để nhận state và các câu hỏi có phạm vi xác định, rồi trả về quyết định cùng xác suất. Ứng dụng dùng những kết quả đó để phân loại, chọn hướng xử lý hoặc điều khiển workflow.
Cái tên mượn từ cách con người tư duy
Nếu từng đọc "Thinking, Fast and Slow" của Daniel Kahneman, bạn sẽ thấy System 1 và System 2 khá quen. Một bên là kiểu tư duy nhanh, trực giác; bên kia cần sự tập trung và suy luận có chủ đích.
TypeSafe mượn cách gọi System One để nhấn mạnh vai trò của Jev: những phán đoán nhanh, tập trung vào một câu hỏi cụ thể. Jev hiện nhận đầu vào dạng text, bao gồm thông tin được tổ chức thành JSON. Giới thiệu System One của TypeSafe
Một ví dụ để hình dung ngay
Giả sử hệ thống nhận được ticket:
Em đã hủy gói nhưng vẫn bị trừ tiền, giờ còn không đăng nhập được.
Trước khi soạn câu trả lời cho khách, phần mềm đã có một loạt việc cần quyết định. Ticket nên chuyển cho đội nào? Có liên quan thanh toán không? Có cần xử lý thêm vấn đề tài khoản không?
Những câu hỏi ấy là chỗ mình có thể thử đặt Jev vào. Còn nội dung phản hồi gửi khách sẽ được viết ở một bước riêng, sau khi hệ thống biết cần xử lý những gì.
Khác biệt nằm ở cơ chế tạo ra kết quả
LLM autoregressive sinh token như thế nào?
Với LLM autoregressive, quá trình tạo output diễn ra tuần tự. Model dùng context để dự đoán token tiếp theo, bổ sung token đó vào chuỗi rồi tiếp tục. Token có thể là một phần của từ, dấu câu hoặc thành phần khác của văn bản.
Nếu viết gọn, quá trình đó trông như sau:
flowchart LR
C["Context"] --> P["Dự đoán token tiếp theo"]
P --> A["Nối token vào chuỗi"]
A --> E{"Đã gặp token kết thúc?"}
E -- "Chưa" --> P
E -- "Rồi" --> O["Output hoàn chỉnh"]
Ở mỗi bước, những token đã sinh trở thành một phần thông tin cho bước kế tiếp. Các kỹ thuật như KV cache giúp tái sử dụng tính toán, nhưng vẫn giữ quan hệ phụ thuộc trong chuỗi output. Tài liệu Transformers về autoregressive generation
Khả năng tạo chuỗi linh hoạt này là lý do cùng một model có thể viết bài, sinh code và tạo tool call. Đó là một giao diện rất rộng: gần như thứ gì biểu diễn được bằng text cũng có thể đưa qua nó.
Khi phần mềm chỉ cần một nhãn
Bây giờ quay lại ticket lúc nãy. Với bước phân loại đội xử lý, output cần lấy về có thể chỉ là billing.
Mình có thể yêu cầu LLM trả một JSON nhỏ. Structured output giúp ràng buộc format và các giá trị hợp lệ; với model autoregressive, quá trình tạo output vẫn đi qua việc sinh token.
Ở đây cần phân biệt hai chuyện: kết quả có hình dạng gì và model tạo ra kết quả bằng cách nào. Một response được đóng gói thành JSON có thể xuất phát từ những cơ chế rất khác nhau.
Jev đánh giá các câu hỏi có sẵn
Theo TypeSafe, Jev dùng kiến trúc và cơ chế sampling phục vụ việc trả quyết định có kiểu dữ liệu, với khả năng xuất các kết quả song song. Đây là điểm khác biệt ở tầng model so với việc lần lượt sinh chuỗi trả lời. Bài giới thiệu Jev
Ở phía lập trình, cách dùng cũng thay đổi. Mình chuẩn bị state, đặt câu hỏi, mô tả những đáp án hợp lệ rồi sử dụng kết quả trong code.
| Góc nhìn | LLM autoregressive | Jev |
|---|---|---|
| Output phục vụ ứng dụng | Văn bản, code, tool call, structured output | Lựa chọn, điểm số, xác suất theo câu hỏi |
| Cơ chế đang xét | Sinh chuỗi token có phụ thuộc tuần tự | Đánh giá câu hỏi để trả quyết định có kiểu |
| Việc developer cần chuẩn bị | Context, yêu cầu, schema hoặc tool definition | State, câu hỏi, tiêu chí và không gian đáp án |
| Phần phù hợp để đưa vào ví dụ ticket | Soạn phản hồi, phân tích tình huống phức tạp | Phân loại, đánh giá từng yếu tố, chọn hướng xử lý |
Điều này gợi ra một cách tối ưu khá cụ thể: rà lại những bước đang dùng model sinh text, xem bước nào thực sự chỉ cần một quyết định có giới hạn.
Jev được training theo hướng nào?
RLCD đặt mục tiêu vào quyết định và xác suất
TypeSafe mô tả RLCD, viết đầy đủ là Reinforcement Learning for Calibrated Decisions, như một hướng post-training cho model. Mục tiêu là trả quyết định cùng xác suất phản ánh được kết quả thực tế.
| Hướng post-training | Tín hiệu tối ưu được nhấn mạnh |
|---|---|
| RLHF | Phản hồi và sự ưu tiên của con người |
| RLVR | Reward dựa trên kết quả có thể kiểm chứng |
| RLCD của TypeSafe | Quyết định và xác suất được calibration |
Đây là cách TypeSafe giải thích hướng training; tài liệu này chưa phải công thức đầy đủ về dữ liệu và reward để tái tạo Jev. AI primer của TypeSafe
Vì sao calibration lại có ích?
Hãy lấy một ví dụ minh họa: trong 100 dự đoán cùng được gán xác suất khoảng 80%, kết quả tương ứng xảy ra khoảng 80 lần. Đó là điều mình mong đợi ở một model được calibration tốt.
Nếu con số model trả về bám được thực tế như vậy, ứng dụng có thêm cơ sở để phân chia mức tự động hóa. Những trường hợp rõ có thể đi tiếp; những trường hợp mơ hồ được chuyển sang một bước xử lý khác.
Mình thích nhìn vấn đề này qua chi phí của một quyết định sai. Chuyển nhầm ticket sang đội khác và tự động hoàn tiền nhầm là hai hậu quả rất khác nhau. Khi thiết kế workflow, ngưỡng chấp nhận cho hai hành động đó cũng phải khác.
Jev hoạt động với 3 kiểu câu hỏi chính
Choice: chọn trong một tập phương án
Choice phù hợp khi ứng dụng đã xác định được những kết quả có thể xảy ra. Với ticket, đó có thể là billing, technical, sales và other.
Response có lựa chọn đứng đầu, phân bố xác suất giữa các phương án và confidence. Tài liệu hiện tại cho phép tối đa 255 phương án trong một câu hỏi Choice. Tài liệu Choice
Phần khó khi áp dụng thường nằm ở cách phân chia danh mục. Nếu billing và account cùng nhận xử lý vấn đề subscription, mình cần mô tả ranh giới rõ. Hai nhãn chồng lấn sẽ làm đầu ra khó sử dụng dù model hiểu nội dung ticket.
Score: đánh giá theo một thang mức độ
Score dùng cho các tiêu chí có thứ tự, chẳng hạn mức độ bức xúc của khách hàng. Mình định nghĩa các mức rồi để model đánh giá đầu vào theo thang đó. Kết quả có thể là giá trị nằm giữa các mức, cùng phân bố xác suất và confidence. Tài liệu Score
Đặt câu hỏi "khách đang bức xúc ở mức nào" sẽ dễ sử dụng hơn một điểm tổng hợp mơ hồ như "ticket này có vấn đề nghiêm trọng không". Mức bức xúc, tác động kinh doanh và tình trạng kỹ thuật có thể cần ba tiêu chí riêng.
Noul: xác suất cho một điều kiện yes/no
Noul trả về một giá trị từ 0 đến 1 cho điều kiện được hỏi. Ví dụ: khách có yêu cầu hoàn tiền không? Nội dung có nhắc đến giao dịch trùng lặp không? Noul không có trường confidence riêng. Tài liệu Noul
Với tình huống ticket đang xét, có thể phân chia như sau:
| Điều cần biết | Kiểu câu hỏi | Ứng dụng dùng vào việc gì? |
|---|---|---|
| Đội nào nhận xử lý chính? | Choice | Route ticket |
| Khách đang bức xúc ở mức nào? | Score | Hỗ trợ sắp thứ tự xử lý |
| Khách có nhắc tới khoản thu sau khi hủy gói? | Noul | Kích hoạt bước kiểm tra giao dịch |
| Khách có báo lỗi đăng nhập? | Noul | Tạo nhánh xử lý tài khoản |
Các câu hỏi độc lập có thể cùng đánh giá một state trong một request. TypeSafe khuyến nghị cách phân rã này rồi kết hợp kết quả bằng logic ứng dụng. Tài liệu giới thiệu API
Với ticket lúc đầu, một request như vậy có thể hình dung thế này:
flowchart TD
T["Ticket của khách"] --> S["State của một request"]
S --> Q1["Choice: đội nào xử lý chính?"]
S --> Q2["Score: khách bức xúc ở mức nào?"]
S --> Q3["Noul: bị thu tiền sau khi hủy gói?"]
S --> Q4["Noul: có báo lỗi đăng nhập?"]
Q1 --> L["Logic ứng dụng kết hợp kết quả"]
Q2 --> L
Q3 --> L
Q4 --> L
L --> R1["Route ticket"]
L --> R2["Sắp thứ tự xử lý"]
L --> R3["Kiểm tra giao dịch"]
L --> R4["Nhánh xử lý tài khoản"]
Từ câu hỏi nhỏ đến một agent thao tác trên browser
Browser Use phân chia công việc ra sao?
Project jev-ultrafast của Browser Use có một cách triển khai khá trực quan. Hệ thống quan sát trang và dựng danh sách thao tác cùng element khả dụng. Jev chọn operation và target; code thực thi. Khi operation là TYPE_TEXT, một model sinh text riêng cung cấp nội dung cần nhập. Project jev-ultrafast
flowchart TD
O["Quan sát trang"] --> B["Dựng tập lựa chọn từ trạng thái hiện tại"]
B --> J["Jev chọn operation và target"]
J --> T{"Operation là TYPE_TEXT?"}
T -- "Có" --> G["Model sinh text viết nội dung cần nhập"]
T -- "Không" --> X{"Target còn đúng trên trang?"}
G --> X
X -- "Còn" --> D["Code thực thi thao tác"]
X -- "Trang đã đổi" --> O
D --> O
Trong code của project, các câu hỏi về operation và target được đưa vào cùng request. Sau khi có đáp án, ứng dụng chỉ sử dụng target tương ứng với operation đã chọn. Đây là cách hỏi trước nhiều khả năng rồi dùng kết quả cần thiết. Implementation trong model.py
Danh sách lựa chọn thay đổi theo môi trường
Điểm đáng chú ý là danh sách action được dựng từ trạng thái hiện tại. Qua một trang khác, agent nhận một tập lựa chọn khác.
Mình thấy cách này làm rõ trách nhiệm giữa những thành phần trong hệ thống. Bộ quan sát phải nhận diện đúng những gì đang có. Model phải chọn phương án phù hợp với mục tiêu. Executor phải kiểm tra rồi thao tác đúng đối tượng.
Nếu nút đã biến mất trong lúc chờ model, executor cần báo trạng thái thay đổi để agent quan sát lại. Thêm một câu prompt thật dài cũng không xử lý thay được công việc đó.
Model sinh text nằm ở nhánh cần nội dung
Với thao tác click, đầu ra có thể chỉ là một ID trong danh sách. Với thao tác điền form, ứng dụng còn cần nội dung để nhập. Nhánh gọi model sinh text xuất hiện đúng tại điểm này.
Đây là ví dụ rõ về việc ghép các khả năng AI khác nhau trong cùng một agent. Chọn và viết có liên quan, nhưng chúng có thể được tổ chức thành hai bước riêng.
Nếu xây một support agent, mình sẽ bắt đầu từ đâu?
Phần này là một thiết kế minh họa để áp dụng các ý trên vào ticket lúc đầu.
Chuẩn bị state từ dữ liệu cần thiết
Ứng dụng lấy nội dung ticket cùng dữ liệu mà người dùng đã được xác thực là có quyền truy cập. Nếu câu hỏi liên quan tới thanh toán, state cần thông tin giao dịch phù hợp. Nếu câu hỏi chỉ là xác định chủ đề, nội dung ticket có thể đã đủ.
Mình sẽ tránh mặc định gửi toàn bộ lịch sử tài khoản cho mọi câu hỏi. Cần thông tin gì để quyết định thì chuẩn bị thông tin đó.
Tách phân loại khỏi xử lý nghiệp vụ
Jev có thể giúp nhận diện rằng ticket liên quan đến khoản thu sau khi hủy gói. Bước tiếp theo của hệ thống là đối chiếu trạng thái subscription và giao dịch thật.
Việc hoàn tiền sẽ đi qua quy tắc nghiệp vụ: giao dịch thuộc tài khoản nào, đã hoàn trước đó chưa, ai có quyền phê duyệt. Sau khi có kết quả xử lý, LLM mới soạn phản hồi phù hợp cho khách.
| Bước | Thành phần chịu trách nhiệm trong ví dụ |
|---|---|
| Lấy ticket và dữ liệu tài khoản | Backend |
| Phân loại và nhận diện các vấn đề | Jev |
| Tra giao dịch, kiểm tra điều kiện | Code nghiệp vụ |
| Xử lý trường hợp thiếu dữ liệu | Hỏi thêm hoặc chuyển người |
| Soạn nội dung phản hồi | LLM sinh text |
| Lưu kết quả và lịch sử xử lý | Backend |
Dùng confidence để chọn bước tiếp theo
Choice và Score có confidence được tính từ phân bố xác suất. Giá trị đó khác với việc lấy đơn giản xác suất của phương án đứng đầu. Tài liệu TypeSafe khuyến nghị chọn ngưỡng theo domain và kiểm tra bằng dữ liệu của ứng dụng. Tài liệu Confidence
Với ticket có hai vấn đề cùng lúc, mình còn muốn biết hệ thống có phát hiện đủ cả hai không. Một nhãn chính chính xác nhưng bỏ sót lỗi đăng nhập vẫn khiến khách phải liên hệ thêm lần nữa.
Vì vậy, thiết kế câu hỏi và định nghĩa "xử lý đúng" cần đi cùng nhau ngay từ đầu.
Ghép các bước lại, đường đi của một ticket trông như sau:
flowchart TD
A["Backend lấy ticket và dữ liệu được phép dùng"] --> J["Jev phân loại và nhận diện vấn đề"]
J --> C{"Confidence đạt ngưỡng của hành động?"}
C -- "Đạt" --> B["Code nghiệp vụ tra giao dịch, kiểm tra điều kiện"]
C -- "Chưa đạt" --> H["Hỏi thêm khách hoặc chuyển người"]
B --> K{"Đủ dữ liệu để xử lý?"}
K -- "Đủ" --> W["LLM soạn phản hồi cho khách"]
K -- "Thiếu" --> H
W --> S["Backend lưu kết quả và lịch sử xử lý"]
H --> S
Hệ thống AI Agent sẽ thay đổi ở đâu?
Công việc được phân chia chi tiết hơn
Từ những ví dụ này, mình nghiêng về hướng các agent sẽ có thêm những thành phần chuyên trách. Có bước cần tạo nội dung dài, có bước cần tìm kiếm, có bước cần phán đoán nhanh trên dữ liệu vừa nhận.
Một decision model tạo thêm lựa chọn cho người thiết kế. Chỗ nào lặp nhiều, đầu ra có giới hạn và đo được chất lượng thì đáng đem ra thử trước.
Thiết kế state trở thành một phần quan trọng của ứng dụng
Khi model chỉ trả một quyết định nhỏ, chất lượng của dữ liệu đưa vào hiện ra rất rõ. Mình có thể kiểm tra lại chính state, tập phương án và kết quả từng lần gọi để tìm lỗi.
Một ticket bị route sai có thể do danh mục chồng lấn, thiếu dữ liệu hoặc bản thân model đánh giá sai. Những nguyên nhân đó dẫn tới các cách sửa khác nhau. Ghi lại đủ thông tin giúp mình tìm đúng chỗ cần thay đổi.
Agent cần đường phục hồi cụ thể
Một hệ thống dùng nhiều quyết định nhỏ cũng có nhiều điểm cần xử lý khi chưa đủ thông tin. Mình muốn mỗi điểm có một bước tiếp theo rõ: đọc thêm dữ liệu, hỏi người dùng, gọi model có khả năng reasoning tốt hơn, hoặc dừng để người phụ trách xem.
Chẳng hạn agent bị kẹt ở một form nhiều bước. Sau vài lần quan sát mà trạng thái không tiến triển, ứng dụng có thể chuyển toàn bộ tình huống sang bước lập kế hoạch lại. Việc chuyển đó cần được thiết kế như một phần bình thường của workflow.
Đo hiệu quả trên cả tác vụ
Một quyết định nhanh hơn có thể giúp agent giảm thời gian chờ. Nhưng nếu quyết định đó làm agent đi sai hướng, phần tiết kiệm sẽ bị dùng vào những lượt sửa sai tiếp theo.
Mình sẽ theo dõi những kết quả gắn với công việc thực tế:
| Chỉ số | Câu hỏi cần trả lời |
|---|---|
| Tỷ lệ hoàn thành đúng | Agent có thực sự giải quyết yêu cầu không? |
| Thời gian toàn tác vụ | Từ lúc nhận yêu cầu đến lúc có kết quả mất bao lâu? |
| Chi phí toàn tác vụ | Tổng các lần gọi, retry và fallback là bao nhiêu? |
| Tỷ lệ chuyển người | Phần việc nào hệ thống chưa tự xử lý được? |
| Lỗi trên nhóm tự xử lý | Những quyết định được cho đi tiếp có đáng tin không? |
Với ví dụ support, mình sẽ bắt đầu bằng một tập ticket đã biết kết quả, gồm cả tiếng Việt, câu ngắn, nhiều vấn đề và thiếu thông tin. Giữ một phần dữ liệu riêng để kiểm tra sau khi đã chỉnh câu hỏi hoặc ngưỡng.
So sánh với cách đang chạy và một baseline đơn giản sẽ giúp mình biết phần cải thiện đến từ đâu. Có những trường hợp chỉ cần sửa taxonomy hoặc thêm một quy tắc là đã giải quyết được lỗi.
Bắt đầu bằng một quyết định mà mình hiểu rõ
Nếu muốn thử Jev trong một hệ thống đang có, mình sẽ chọn một bước nhỏ với đầu vào và đầu ra rõ ràng. Phân loại ticket là một ví dụ; chọn handler cho một yêu cầu cũng là một ví dụ khác.
Đọc lại những lần model chọn sai, mình có thể học được khá nhiều về chính ứng dụng: danh mục có đang lẫn lộn không, dữ liệu nào thực sự cần thiết, trường hợp nào nên chuyển người ngay từ đầu.
Khi bước đó làm việc ổn, mình mới cân nhắc mở rộng sang vị trí khác. Cách này giúp mỗi lần thêm một thành phần đều gắn với một vấn đề cụ thể đã quan sát được.
Điều khiến Jev đáng tìm hiểu, với mình, nằm ở cách nó buộc người xây agent phải gọi tên rõ từng phần việc. Chọn, viết, suy luận, kiểm tra và thực thi có thể được phân chia, đo đạc rồi ghép lại theo nhiều cách.
Model đang cho mình thêm những mảnh ghép khá hay. Phần thú vị tiếp theo là tìm ra cách ghép chúng để cả hệ thống thực sự làm được việc.