Một buổi agent ăn hết context

Hôm trước mình giao cho Claude Code một task khá đời thường: tìm tất cả chỗ trong codebase đang gọi một helper cũ tên formatPrice, rồi thay bằng phiên bản mới có thêm currency option. Codebase Astro + một ít Vue + một ít script Node, không lớn nhưng cũng không nhỏ.

Agent làm đúng cái nó được train để làm. Nó gọi Bash chạy grep -rn formatPrice src/. Ra 12 match ở 9 file. Sau đó nó Read từng file. Mỗi file 300 tới 800 dòng. Đến file thứ 6 thì context window đầy. Nó tự compact, mất nửa context gốc, rồi tiếp tục. Khi quay lại file thứ nhất để áp dụng thay đổi, nó quên signature mới mà chính nó vừa thiết kế. Mình phải nhắc lại.

Task xong, nhưng tốn gấp 4 lần token so với mức đáng lẽ phải tốn. Nếu bạn đang chạy agent trên Sonnet với cap input mỗi message, hoặc đang trả tiền theo token, vòng lặp này là khoản chi phí thật. Mình ngồi xem usage log thấy 80% token đi vào việc đọc file mà phần lớn nội dung không liên quan câu hỏi gốc.

Đây là vấn đề mà Semble nhắm vào.

Grep cho người, grep cho agent

Nói thẳng: grep với ripgrep là tool tuyệt vời. Mình dùng rg hàng ngày, sub-second response trên repo vài chục nghìn file, không thay được. Nhưng grep được thiết kế cho con người: trả về một list match, người mở editor scroll quanh nó, đọc context, đóng lại, hỏi tiếp.

Agent thì khác. Agent thấy 12 match, nó không có editor để scroll, nó chỉ có một option duy nhất: gọi Read để lấy nguyên file vào context. Mà file 600 dòng thì 600 dòng nó phải nuốt, dù câu trả lời nằm ở 15 dòng. Nhân với 12 file là một câu chuyện token rất khác.

Có người sẽ nói: “thì train agent biết dùng grep -A 10 -B 10 đi”. Mình thử rồi. Nó dùng được, nhưng:

  1. Context 10 dòng quá ngắn để hiểu function dài.
  2. Context 50 dòng đôi khi vẫn cắt giữa block.
  3. Nếu match nằm trong một class method, agent vẫn cần đọc signature của class ở đầu file.
  4. Quan trọng nhất: agent không biết tự stop. Nó cứ thấy match là Read cho chắc.

Hôm 2026-05-18 Semble lên top Show HN. Tag line đập thẳng: “Code search for agents that uses 98% fewer tokens than grep”. Số 98% đáng nghi ngờ, nhưng cái thái độ “agent là first-class user” thì mình thấy đúng hướng.

Semble làm gì, từ góc nhìn user

Theo README, Semble không phải là grep replacement nói chung. Nó là một code search backend được tối ưu cho việc trả lời câu hỏi của agent.

Stack ngắn gọn (mình paraphrase từ README):

  1. Chunk code theo tree-sitter, biết được boundary của function, class, block.
  2. Build hai index song song: static embedding (model nhỏ potion-code-16M từ Model2Vec) cho semantic match, và BM25 cho lexical match trên identifier.
  3. Query thì merge hai kết quả bằng Reciprocal Rank Fusion, rerank theo vài signal: definition boost, file coherence, downrank file test và shim.
  4. Trả về chunk, không trả về file. Mỗi chunk đã đủ context để hiểu, không cần đọc thêm.

Toàn bộ chạy trên CPU, không cần GPU, không cần API key. README claim index một repo trung bình mất ~250ms, query ~1.5ms. Mình chưa benchmark độc lập, nên trích nguyên văn thay vì khẳng định.

Phần quan trọng cho agent: “uses 98% fewer tokens on average”. Theo README, con số này được đo bằng cách so sánh char count của (file đầy đủ chứa match) với char count của (snippet Semble trả về), chia cho 4 để ra token estimate. README tự nhận đây là “conservative estimate”. Nói cách khác: con số 98% không phải so với grep đơn thuần, mà so với pattern grep-rồi-read-full-file mà phần lớn agent đang làm.

Plug vào Claude Code

Setup MCP một dòng:

claude mcp add semble -s user -- uvx --from "semble[mcp]" semble

uvx là CLI của uv, nếu chưa có thì pip install uv hoặc dùng installer chính thức. Lần đầu chạy nó sẽ pull package về cache, sau đó MCP server boot trong khoảng 2 giây trên máy mình.

Mở Claude Code, gõ /mcp xem list. Nếu Semble xuất hiện với status connected, agent đã có thêm 2 tool mới (search và find-related). Mình kiểm tra bằng cách hỏi nguyên văn:

“Find where formatPrice is defined and all its callers using semble”

Lần này agent không grep nữa. Nó gọi tool semble_search. Trả về 4 chunk: 1 chunk định nghĩa, 3 chunk caller. Mỗi chunk ~40 dòng có sẵn context. Agent đọc xong là biết đủ để bắt tay vào edit, không cần Read thêm file nào.

Mình chưa đo chính xác token saving trên codebase của mình, vì chạy task thật không có baseline lặp lại. Nhưng nhìn rough trên usage panel của Claude Code: task tương tự trước đó tốn ~45k token input, lần này ~8k. Số liệu của bạn sẽ khác, codebase và task khác nhau, đừng tin con số của mình.

Mấy chỗ Semble không thay được grep

Sau vài ngày dùng, đây là những điểm mình note ra. Một số trùng với comment trên thread HN, một số mình tự gặp.

Index có thể stale. README ghi local path được watch và re-index tự động, nhưng nếu bạn git checkout sang branch khác, hoặc rebase, hoặc một agent khác sửa file ở worktree song song, có khả năng search trả về chunk của state cũ. Mình chưa gặp lỗi nghiêm trọng, nhưng đã thấy 1 lần agent search ra một function mà branch hiện tại đã xoá. Workaround tạm: restart MCP server sau khi switch branch lớn.

Agent không tin tool mới. Đây là pattern thú vị mình thấy trên thread HN, và mình confirm: Claude Code, sau khi nhận kết quả Semble, thỉnh thoảng vẫn tự gọi Bash chạy rg để “double-check”. Một commenter HN gọi thẳng: “models are so heavily RL’d with grep that they do not trust results in other forms”. Cho tới khi model mới được train với Semble trong tool set, hành vi này còn. Cách giảm: prompt agent rõ “trust semble results, do not double-check with grep unless semble returns empty”.

Exact string thì vẫn cần grep. Nếu bạn cần tìm chính xác một literal, ví dụ một typo trong error message, hoặc một config key, semantic search có thể trả về chunk gần đúng nhưng không đúng. README cũng nói rõ: “Use grep only when you need exhaustive literal matches”. Mình giữ cả hai trong tool set, không bỏ ripgrep.

False positive ở multi-language repo. Codebase mình có Astro (TS + JSX), Vue (TS + template), MDX (content), một ít Python script ở scripts/. Semble trả về kết quả tốt cho TS và Python. MDX thì kém, chunk bị cắt vô lý ở mấy chỗ frontmatter và component import. README ghi train cụ thể trên 6 ngôn ngữ trong số “63 repos in 19 languages”. Đoán: ngôn ngữ chính (Python, JS, TS, Go, Rust, Java) sẽ tốt; ngôn ngữ đuôi sẽ tuỳ.

Speed end-to-end không phải lúc nào cũng thắng. Một commenter HN benchmark trên Pi: token ít hơn nhưng wall-clock chậm gấp đôi grep. Lý do: index lần đầu mất công, query thì nhanh nhưng có overhead MCP roundtrip. Nếu bạn chỉ chạy 1 query rồi tắt agent, grep vẫn nhanh hơn. Nếu agent chạy 30 query trong 1 session dài, Semble thắng dễ.

Khi nào dùng, khi nào không

Đây là cách mình đang phân chia:

Dùng Semble khi:

  • Task chạy trên Claude Code hoặc một agent khác có MCP. Token budget là yếu tố quyết định.
  • Codebase đủ lớn để pattern grep-rồi-read-full-file gây stress context. Threshold của mình rough là repo > 50 file source.
  • Task cần nhiều query (refactor, audit, “tìm tất cả chỗ dùng X”), không phải single shot.

Vẫn dùng rg khi:

  • Mình tự search trong terminal. Mắt người scroll nhanh, không cần snippet.
  • Cần literal exact (error message, config key, một sha cụ thể).
  • Cần regex phức tạp Semble không hỗ trợ.
  • Task agent chỉ có 1 search rồi xong. Overhead MCP không bù lại được.

Hỏi ngược lại

Workflow agent của bạn đang đụng vấn đề token budget chưa? Hay vẫn còn dưới threshold để cảm thấy?

Nếu bạn đã thử Semble (hoặc một tool cùng họ như codegraph, codebase-memory-mcp, ck), mình tò mò nghe đoạn so sánh thật trên codebase của bạn, đặc biệt mấy con số token trước/sau. Tag mình trên LinkedIn hoặc gửi email là xinchao@nghia-pham.com.

Bản thân mình, mình sẽ giữ Semble trong tool set Claude Code thêm 2 tuần nữa, xem có lần nào nó thật sự gây phiền (index stale, false positive ở MDX) đáng để gỡ ra không. Nếu sau 2 tuần vẫn còn ở đó, coi như cú plug thành công.

Tham khảo: github.com/MinishLab/semble, Show HN thread 2026-05-18.