- LLM 0.26 là một bản phát hành lớn, cho phép kết nối các công cụ được tạo bằng hàm Python với các mô hình cục bộ OpenAI, Anthropic, Gemini và Ollama trong CLI cũng như thư viện Python
- Công cụ có thể được cài đặt dưới dạng plugin rồi gọi bằng
--tool/-T name_of_tool, hoặc có thể truyền trực tiếp hàm Python tạm thời từ dòng lệnh bằng --functions
- Ngoài các công cụ tích hợp sẵn
llm_version, llm_time, còn có các plugin simpleeval, QuickJS, SQLite, Datasette; mô hình có thể thử lại bằng cách truy vấn schema hoặc dùng biểu thức khác sau một lần gọi thất bại
model.chain() mới trong Python API phát hiện yêu cầu gọi công cụ, thực thi rồi truyền kết quả lại cho mô hình, đồng thời hỗ trợ cả hàm đồng bộ và công cụ asyncio
- Việc triển khai trở nên khả thi khi cách dùng công cụ/gọi hàm của nhiều nhà cung cấp đang hội tụ; các bước tiếp theo là mở rộng plugin, hỗ trợ thêm nhiều plugin mô hình và hỗ trợ client Model Context Protocol
Hỗ trợ chạy công cụ trong LLM 0.26
- LLM 0.26 bổ sung hỗ trợ công cụ, tính năng lớn nhất kể từ khi dự án bắt đầu
- Trong LLM CLI và thư viện Python, bạn có thể cấp cho các mô hình cục bộ OpenAI, Anthropic, Gemini và Ollama quyền truy cập vào các công cụ có thể biểu diễn bằng hàm Python
- Khi cài đặt plugin công cụ mới, bạn có thể gắn thêm chức năng mới vào mô hình đang sử dụng
- Có bốn cách sử dụng chính
- Cài đặt công cụ dạng plugin và tải bằng
--tool/-T name_of_tool
- Truyền trực tiếp mã hàm Python vào dòng lệnh bằng tùy chọn
--functions
- Trong Python API cũng có thể truyền công cụ theo kiểu
tools=[locals]
- Công cụ hoạt động trong cả ngữ cảnh bất đồng bộ và đồng bộ
Chạy công cụ trong CLI
- Có thể cài đặt LLM mới nhất bằng
uv tool install llm, và nâng cấp bản đã cài bằng uv tool upgrade llm
- Ví dụ dùng OpenAI: đặt API key bằng
llm keys set openai, rồi chạy như sau
llm --tool llm_version "What version?" --td
llm_version là công cụ demo đơn giản đi kèm LLM, và --tool llm_version sẽ phơi bày công cụ đó cho mô hình
- Có thể chỉ định
--tool nhiều lần, và cũng có thể dùng tùy chọn ngắn -T
--td là viết tắt của --tools-debug, in ra thông tin về lời gọi công cụ và phản hồi để bạn kiểm tra hoạt động bên trong
- Mô hình mặc định thường là
gpt-4o-mini; trong ví dụ, mô hình mặc định được đổi thành gpt-4.1-mini bằng llm models default gpt-4.1-mini
- Có thể chỉ định mô hình khác bằng tùy chọn
-m; cũng có ví dụ chạy o4-mini cùng công cụ tích hợp llm_time
llm --tool llm_time "What time is it?" --td -m o4-mini
- Phản hồi của công cụ
llm_time bao gồm các trường như utc_time, utc_time_iso, local_timezone, local_time, timezone_offset, is_dst
Hoạt động với nhiều mô hình và plugin
- Có thể dùng cùng mẫu lệnh với các plugin mô hình hỗ trợ công cụ
- Ví dụ Anthropic Claude Sonnet 4 dùng luồng sau
llm install llm-anthropic -U
llm keys set anthropic
llm --tool llm_version "What version?" --td -m claude-4-sonnet
- Ví dụ Google Gemini 2.5 Flash dùng luồng sau
llm install llm-gemini -U
llm keys set gemini
llm --tool llm_version "What version?" --td -m gemini-2.5-flash-preview-05-20
qwen3:4b chạy bằng Ollama cũng có thể chạy công cụ đơn giản
qwen3:4b là một mô hình nhỏ, dung lượng 2,6GB
ollama pull qwen3:4b
llm install 'llm-ollama>=0.11a0'
llm --tool llm_version "What version?" --td -m qwen3:4b
Plugin công cụ cho toán học, JavaScript, SQLite, Datasette
- Những tác vụ LLM yếu như nhân các số lớn có thể được bổ trợ bằng lời gọi công cụ
- llm-tools-simpleeval phơi bày thư viện simpleeval của Daniel Fairhead
- simpleeval là thư viện “Simple Safe Sandboxed Extensible Expression Evaluator for Python”
- Nó cung cấp một sandbox đủ chắc chắn để chạy các biểu thức Python đơn giản
- Ví dụ tính toán chạy như sau
llm install llm-tools-simpleeval
llm -T simple_eval 'Calculate 1234 * 4346 / 32414 and square root it' --td
- Trong lần chạy ví dụ, mô hình trước hết tính
1234 * 4346 / 32414 và nhận 165.45208860368976; sau đó thất bại vì sqrt(...) chưa được định nghĩa, rồi đổi sang ** 0.5 để nhận 12.862818066181678
- Có bốn plugin công cụ đã được công bố
- llm-tools-simpleeval: hỗ trợ biểu thức đơn giản như toán học
- llm-tools-quickjs: cung cấp quyền truy cập vào trình thông dịch JavaScript QuickJS chạy trong sandbox; môi trường được giữ lại giữa các lần gọi nên có thể tái sử dụng biến và hàm
- llm-tools-sqlite: cung cấp quyền truy cập truy vấn SQL chỉ đọc tới cơ sở dữ liệu SQLite cục bộ
- llm-tools-datasette: chạy truy vấn SQL trên một instance Datasette từ xa
Hộp công cụ Datasette và thử lại sau lỗi
- Plugin Datasette hoạt động dưới dạng hộp công cụ có nhiều công cụ bên trong
- Hộp công cụ là một plugin có thể cấu hình bằng constructor
- Ví dụ sử dụng như sau
llm install llm-tools-datasette
llm -T 'Datasette("https://datasette.io/content")' --td "What has the most stars?"
Datasette("https://datasette.io/content") truyền URL instance Datasette sẽ dùng cho plugin
- Ví dụ này nhắm tới cơ sở dữ liệu content đang vận hành website Datasette
- Mô hình thực hiện ba lần gọi
- Ban đầu, nó đoán
SELECT name, stars FROM repos ORDER BY stars DESC LIMIT 1 nhưng thất bại vì không có cột stars
- Sau khi nhận lỗi, nó dùng công cụ
Datasette_schema() để truy vấn schema cơ sở dữ liệu
- Dựa trên schema, nó lắp ghép và chạy truy vấn đúng
- Theo phản hồi cuối cùng, kho lưu trữ có nhiều sao nhất là
datasette, với 10.020 stars
Tạo công cụ tạm thời bằng --functions
- Tùy chọn
--functions cung cấp cách dùng công cụ tạm thời ít cấu trúc hơn so với plugin
- Nếu truyền trực tiếp một khối mã Python vào dòng lệnh, các hàm được định nghĩa trong đó sẽ trở thành công cụ mà mô hình có thể sử dụng
- Ví dụ công cụ tìm kiếm blog gọi trang tìm kiếm bằng
httpx và trả nguyên HTML về cho mô hình như sau
llm --functions '
import httpx
def search_blog(q):
"Search Simon Willison blog"
return httpx.get("https://simonwillison.net/search/", params={"q": q}).content
' --td 'Three features of sqlite-utils' -s 'use Simon search'
- Cách triển khai chỉ trả lại nguyên HTML của trang tìm kiếm nhưng vẫn hoạt động
- Do dòng GPT-4.1 xử lý được 1 triệu token, có thể xử lý cả HTML thô
- Cần thêm
use Simon search vào system prompt để mô hình dùng công cụ tìm kiếm được cung cấp thay vì tự trả lời
- Một công cụ tìm kiếm tốt hơn cần chỉ dẫn chi tiết hơn và các đoạn trích liên quan từ kết quả tìm kiếm
- Trong kết quả ví dụ, các tính năng của
sqlite-utils gồm kết hợp CLI với thư viện Python, tự động thêm cột thông qua alter=True, và hỗ trợ plugin
Hỗ trợ công cụ trong Python API
- LLM vừa là công cụ CLI vừa là thư viện Python, và trong 0.26, hỗ trợ công cụ cũng được thêm vào Python API
- Ví dụ giải bài toán đếm số chữ
r trong “strawberry” bằng công cụ dạng hàm
import llm
def count_char_in_text(char: str, text: str) -> int:
"How many times does char appear in text?"
return text.count(char)
model = llm.get_model("gpt-4.1-mini")
chain_response = model.chain(
"Rs in strawberry?",
tools=[count_char_in_text],
after_call=print
)
for chunk in chain_response:
print(chunk, end="", flush=True)
after_call=print là cách kiểm tra lời gọi công cụ, tương tự tùy chọn --td ở trên
- Phương thức mới
model.chain() giống model.prompt(), nhưng phát hiện yêu cầu gọi công cụ được trả về, thực thi, rồi gọi lại mô hình cùng kết quả
model.chain() có thể chạy nhiều phản hồi trước khi đưa ra câu trả lời cuối cùng
- Khi lặp qua
chain_response, có thể in streaming các token được trả về qua nhiều phản hồi
- Trong kết quả ví dụ, công cụ
count_char_in_text được gọi với char='r', text='strawberry', và dựa trên kết quả 3, nó trả lời rằng strawberry có 3 chữ r
- Thư viện Python cũng hỗ trợ
asyncio, và công cụ có thể là hàm async def
- Nếu mô hình yêu cầu nhiều công cụ bất đồng bộ cùng lúc, thư viện sẽ chạy chúng đồng thời bằng
asyncio.gather()
- Hộp công cụ cũng được hỗ trợ; truyền
tools=[Datasette("https://datasette.io/content")] vào chain() có hiệu quả giống --tool 'Datasette(...)' trong CLI
Bối cảnh triển khai và mẫu sử dụng công cụ
- Mẫu sử dụng công cụ là cách đã được theo dõi kể từ khi thấy trong ReAcT paper, công bố lần đầu vào tháng 10/2022
- Mẫu cơ bản rất đơn giản
- Cho mô hình biết có những công cụ có thể dùng
- Mô hình yêu cầu thao tác công cụ bằng cú pháp đặc biệt như JSON, XML hoặc
tool_name(arguments), rồi dừng lại
- Mã sẽ phân tích đầu ra đó và thực thi công cụ được yêu cầu
- Gửi prompt mới kèm kết quả thực thi tới mô hình
- Cách này hiện hoạt động với gần như mọi mô hình
- Nhiều mô hình đã được huấn luyện riêng để dùng công cụ, và cũng có các bảng xếp hạng như Berkeley Function-Calling Leaderboard
- OpenAI, Anthropic, Google, Mistral và Meta đều đưa tính năng dùng công cụ hoặc gọi hàm vào API của họ
- Ở phía mô hình cục bộ, Ollama đã bổ sung hỗ trợ công cụ vào năm ngoái, và server llama.cpp cũng có tính năng này
- LLM trước đó đã phát hành hỗ trợ schema vào tháng 2/2025, xem đây là một bước hướng tới hỗ trợ công cụ
- Thách thức là thiết kế một lớp trừu tượng hoạt động trên nhiều mô hình; khi cách làm của các nhà cung cấp hiện đã hội tụ rõ rệt, việc triển khai mới trở nên khả thi
- Workshop Building software on top of Large Language Models tại PyCon US là dịp giúp hoàn tất bản triển khai alpha, và cũng có phần tools trong tutorial
Agent và kế hoạch tiếp theo
- Dù vẫn có sự dè dặt với thuật ngữ “agents”, trong thế giới LLM khái niệm này đang hội tụ thành tools in a loop, và LLM 0.26 cũng gần với hình thức đó
- Nếu muốn tạo agent, LLM 0.26 có thể là điểm khởi đầu tốt
- Công việc tiếp theo được tổng hợp thành 13 issue trong LLM tools v2 milestone
- Chủ yếu là cải thiện hiển thị log chạy công cụ và những vấn đề nhỏ chưa chặn bản phát hành này
- Các issue bổ sung nằm dưới nhãn tools
- Plugin là lĩnh vực có tiềm năng được kỳ vọng nhất
- Đang trực tiếp sử dụng template cookiecutter llm-plugin-tools
- Có kế hoạch tạo tutorial liên quan
- Vẫn còn việc bổ sung hỗ trợ công cụ cho nhiều plugin mô hình hơn
- Hỗ trợ Model Context Protocol cũng nằm trong kế hoạch
- MCP đang nhanh chóng nổi lên như một cách tiêu chuẩn để mô hình truy cập công cụ
- Hai tuần trước, các API nhà cung cấp lớn chưa hỗ trợ trực tiếp, nhưng trong 8 ngày gần đây OpenAI, Anthropic và Mistral đã bổ sung
- Mục tiêu là để LLM hoạt động như một client MCP, giúp mọi người dễ dùng các server MCP do họ viết làm nguồn công cụ bổ sung cho LLM
1 bình luận
Ý kiến trên Hacker News
Trình kết xuất Markdown dạng streaming viết cho công cụ này cũng đáng xem: https://github.com/day50-dev/Streamdown
Bối cảnh nằm ở https://github.com/simonw/llm/issues/12, và tôi cũng dùng hằng ngày https://github.com/day50-dev/llmehelp, công cụ tmux được xây trên
llmcủa Simonllm: một plugin ZSH dùngzleđể biến tiếng Anh thành lệnh shell chỉ bằng một phím bấm: https://github.com/day50-dev/ZummonerHôm nay tôi cũng dùng kiểu như
$ git find out if abcdefg is a descendent of hijklmnop, và lệnhfor i in $(seq 1 6); do printf "%${i}sh${i}\n\n-----\n" | tr " " "#"; done | pv -bqL 30trong một bình luận khác vốn ban đầu là đầu vào gần với ngôn ngữ tự nhiên hơn nhiềuNhấn
ctrl-x xthì buffer được gửi tới OpenRouter và được thay thế bằng cú pháp đúng trong chưa đầy 1 giâyTôi đã mở issue: https://github.com/simonw/llm/issues/1112
Tôi có đưa vào vài lựa chọn thiết kế nhưng vẫn chưa thấy hoàn toàn đúng, nên muốn nhận phản hồi
llmcùng tô sáng cú pháp củabatCái này làm tăng mạnh nguy cơ tự bắn vào chân
Tài liệu https://llm.datasette.io/en/stable/tools.html có cảnh báo về prompt injection, nhưng tôi nghĩ kịch bản hợp lý hơn là tự mình gây hại cho mình
Ví dụ, nếu cấp cho công cụ quyền truy cập tài khoản chứng khoán để tự động hóa giao dịch, thì ngay cả khi không có prompt injection cũng không có cơ chế nào ngăn bot thực hiện các giao dịch ngu ngốc
Một khi bắt đầu gắn công cụ vào LLM, đặc biệt nếu các lệnh gọi công cụ đó đã được xác thực và còn có thể thay mặt bạn hành động, thì có quá nhiều con đường để mọi thứ đi sai
Phía MCP hiện đang trải qua điều này với tốc độ cực nhanh, và vụ GitHub MCP hôm qua cũng là một ví dụ: https://news.ycombinator.com/item?id=44097390
Tôi đã đưa cảnh báo lớn vào tài liệu, và cũng cẩn trọng chỉ phát hành các plugin công cụ ban đầu không thể gây thiệt hại. Vì vậy sandbox QuickJS và plugin SQLite đều chỉ đọc
llmvới tài khoản chứng khoán thì kẻ ngu không phải là bot mà là người đã nối nóTrên macOS thì không quá khó, nhưng hiện giờ thiếu công cụ tốt, dễ dùng. Claude Code đã bắt đầu dùng Seatbelt từng chút một để cải thiện trải nghiệm người dùng
Bạn có thể tự làm hại mình và có lẽ sẽ như vậy, nhưng việc không đánh giá các khả năng cũng là rủi ro. Người bình thường có xu hướng đánh giá thấp khả năng tự bắn vào chân, còn dân kỹ thuật lại có xu hướng đánh giá thấp rủi ro của việc không học các khả năng mới
Một năm trước tôi cũng đã để LLM chạy lệnh cục bộ trên laptop của mình; tôi xem nó là có rủi ro ở mức nào đó, nhưng chưa có chuyện gì gây hại xảy ra. Với prompt như
find out where I am and what weather it is going to be, dù vẫn có khả năng nó chạyrm -rf /, xác suất là rất thấpTuy vậy, giao giao dịch cổ phiếu cho LLM khi không biết nó quyết định thế nào thì theo tiêu chuẩn của tôi là quá rủi ro
Tôi thích ý tưởng agent chạy trong terminal của Warp Terminal, nhưng không thích mô hình kiểu Cursor: “cứ tin và trả tiền, prompt tốt và các lệnh gọi LLM để chúng tôi lo”
Vì vậy tôi đã tìm một agent terminal đơn giản dựa trên CLI để bù cho kỹ năng shell còn yếu, và kết hợp công cụ terminal với
llmtrông là một giải pháp nhẹ nhàngTôi hiểu rõ nguy cơ tự bắn vào chân là lớn, nên tò mò liệu có thể áp dụng cách hỏi quyền cho từng lệnh gọi công cụ như các agent khác không. Ví dụ kiểu “
llmsắp gọirm -rf ./*. Nhấn Y để xác nhận” thì có thể phần nào ngăn LLM chạy loạn trong terminal--full-autosaoNếu đang dùng
llm, Gtk-llm-chat cũng đáng xemNó tích hợp với công cụ dòng lệnh
llmvà desktop, cung cấp biểu tượng khay hệ thống cùng cửa sổ chat đẹp mắtGần đây đã phát hành 3.0.0 và cung cấp gói cho 3 hệ điều hành desktop lớn
Tôi xem bản phát hành này là một thành phần cốt lõi để mở khóa tiềm năng của LLM mà không bị giới hạn bởi các client hiện có
Sau khi 0.26 alpha ra mắt, tôi đã thử tạo plugin tương tác với MCP server nhưng khá khó. Đến giờ tôi đã kết nối được và lấy/ dùng động các công cụ, nhưng vẫn chưa truyền được tham số
Thư viện Python
mcpchính thức giả định khá chặt luồng chạyasyncio, kết nối đến server rồi kiểm tra các công cụ khả dụngTôi đang duy trì một plugin zsh/omz để hỗ trợ tự động hoàn thành bằng phím Tab cho CLI
llmgần như hoàn toàn bằng vibe coding, nhưng tốc độ phát hành tính năng mới nhanh quá nên khó theo kịpDù vậy, chỉ cần cỡ
llm -f README.md -f llm.plugin.zsh -f completions/_llm -f [https://simonwillison.net/2025/May/27/llm-tools/](<https://simonwillison.net/2025/May/27/llm-tools/>) "implement tab completions for the new tool plugins feature"là giải quyết được khoảng 90%Kho lưu trữ là https://github.com/eliyastein/llm-zsh-plugin, và vì tôi cố nhét càng nhiều tùy chọn và flag càng tốt nên hơi lộn xộn, rất muốn nhận phản hồi
Các thế hệ tương lai sẽ thắc mắc chúng ta đã làm việc bằng cách nào, giống như cách chúng ta nhìn các lập trình viên assembly và nghĩ “họ đã làm việc kiểu gì vậy?”
Tôi từng thắc mắc Claude Code biết và sử dụng công cụ như thế nào
Có thể cho LLM biết về công cụ và cách dùng, và wrapper thực thi có thể quản lý việc đó, nhưng tôi nghĩ Claude Code có những kỳ vọng rất cụ thể về API gọi công cụ mà wrapper dùng, và có lẽ đã được củng cố mạnh bằng hậu huấn luyện hoặc tinh chỉnh
Vì vậy tôi tự hỏi liệu các framework gọi công cụ của bên thứ ba dùng Claude có bất lợi hơn framework của chính Anthropic hay không
Riêng một vấn đề khác, như bài viết về “tấn công” GitHub MCP đã nói, LLM có thể bị lừa dùng toàn bộ quyền của thông tin xác thực. Vì GitHub có thông tin xác thực phân quyền chi tiết, và công ty chúng tôi cũng vậy, tôi mong ai đó thử một giao thức để wrapper tạo thông tin xác thực được phân quyền chi tiết rồi chuyển cho LLM
Tôi hình dung một cấu trúc trong đó ứng dụng được cấp thông tin xác thực mạnh hơn, còn LLM cấp dưới được huấn luyện để “yêu cầu quyền” cho một tác vụ hoặc tài nguyên cụ thể. Nếu người dùng cho phép, framework sẽ nhận thông tin xác thực có phạm vi giới hạn từ dịch vụ và dùng nó cho lệnh gọi công cụ
Cần một cấu hình công cụ khá tinh vi: công khai một công cụ “yêu cầu thông tin xác thực bổ sung”, và khi công cụ đó được gọi thì hỏi người dùng
Công cụ phải lưu giữ thông tin xác thực và tuyệt đối không chuyển token thật cho LLM; thay vào đó có thể chuyển một ký hiệu như
creds1và bảo LLM dùngcreds1cho các yêu cầu sauTôi tò mò không biết bạn đã xem bài báo này chưa. Nếu nó quan trọng như vẻ ngoài, tôi nghĩ chỉ số này đáng lẽ phải có trong mọi system card
Khi đánh giá 12 LLM phổ biến tuyên bố hỗ trợ ngữ cảnh từ 128K token trở lên, chúng hoạt động tốt ở ngữ cảnh ngắn nhưng hiệu năng giảm mạnh khi độ dài ngữ cảnh tăng. Ở mức 32K, 10 mô hình tụt xuống dưới 50% so với đường cơ sở độ dài ngắn; ngay cả GPT-4o, một ngoại lệ có hiệu năng tốt, cũng giảm từ 99,3% xuống 69,7%
https://arxiv.org/abs/2502.05167
Tôi được biết trong các needle in a haystack test gần đây, Gemini 2.5 Pro và Gemini 2.5 Flash vượt xa các mô hình khác, nên sẽ rất hay nếu chạy thử cả trên các mô hình đó
Tuần trước tôi đã làm một bản demo chatbot cho khách hàng dựa trên dữ liệu độc quyền bằng 0.26a0
Các thành phần cốt lõi tôi phải tự viết là system prompt, công cụ lấy dữ liệu bên ngoài và công cụ thực hiện tính toán; nhờ thư viện này, phần chức năng cốt lõi có thể được xây dựng rất dễ dàng
Trong demo, phần lớn công sức nằm ở việc làm hạ tầng kết nối: giữ được cuộc hội thoại, làm mới tự nhiên ngay cả khi trình duyệt được refresh do vấn đề kết nối, và tạo một UI web đẹp mắt để có thể bắt đầu phiên chat mới
Tôi không biết
after_call=print, nên đọc bài blog mới biết và thấy rất hayTôi dùng các công cụ của Simon hằng ngày
Nhờ pipe và khả năng chuyển đổi dễ dàng giữa Ollama cục bộ với mô hình từ xa, làm việc rất tiện