做agent-io这段时间,我越来越强烈地感觉到一件事:很多时候,真正劝退用户的,不是“大功能没有”,而是——明明只想做一件小事,却先要写一堆不重要的代码。这次更新,想解决的就是这个问题。事情的起因也很简单。前阵子,有小伙伴在issue里提了一个建议:https://githu
做 agent-io 这段时间,我越来越强烈地感觉到一件事:
很多时候,真正劝退用户的,不是“大功能没有”,而是 —— 明明只想做一件小事,却先要写一堆不重要的代码。
这次更新,想解决的就是这个问题。
事情的起因也很简单。
前阵子,有小伙伴在 issue 里提了一个建议:
https://github.com/lispking/agent-io/issues/1
能不能引入宏?这样定义工具会方便很多。
我看完第一反应就是:
对,这个需求太真实了。
因为在 Rust 里做 Agent,工具调用本来就是高频动作。
可如果每次加一个工具,都要先铺一层样板代码,那体验就会很割裂。
你明明只是想告诉 Agent:
结果却要先去写:
FunctionTool::new(...)Box::pin(...)这些东西不能说没必要。 但它们确实不该每次都让用户自己亲手写一遍。
所以这次,我们把它改了。
这次更新后,agent-io 支持直接用 #[tool] 宏来定义工具。
你可以这么写:
use agent_io::{tool, Agent, llm::ChatOpenAI};
use std::sync::Arc;
/// Get the current weather for a city
#[tool(location = "The city name to fetch weather for")]
async fn get_weather(location: String) -> agent_io::Result<String> {
Ok(format!("Weather in {location}: Sunny, 25°C"))
}
然后直接这样挂到 Agent 上:
let agent = Agent::builder()
.with_llm(Arc::new(llm))
.tool(get_weather())
.build()?;
是的,get_weather() 现在就能直接变成一个工具。
没有手写 schema。
没有自己包 Arc。
没有那层又一层的工具样板。
这件事最大的变化不是“代码更短了”,而是:
你终于可以把“定义工具”这件事,重新理解成“写一个普通的业务函数”。
这个感觉,差别其实很大。
因为“工具定义”不是一个低频边角需求。
在 Agent 开发里,它恰恰是最常发生的事之一。
你会一直写工具:
如果这件事每次都很重,用户就会越来越容易产生一种感觉:
框架能力挺强,但写起来总有点累。
而这种“累”,并不会出现在 README 的功能列表里。
它只会出现在真正开始写代码的那一刻。
所以这次我们做的,不只是加一个宏。
本质上是在做一件更重要的事:
把高频动作的摩擦降下来。
别看表面上只是一个 #[tool],背后其实是把一整段重复劳动接过去了。
你写在函数上的文档注释:
/// Get the current weather for a city
会直接变成工具 description。
这点很细,但特别有价值。 因为你写给人的说明,顺手也能成为写给模型的说明。
少写一次,信息也更统一。
比如:
#[tool(location = "The city name to fetch weather for")]
这里写的参数描述,会自动进入生成的 JSON Schema。
你不用再手动拼一大段 JSON。 函数签名负责结构,宏属性负责说明,代码本身就很像一份自然语言接口文档。
这次宏已经能自动处理一些最常见的类型:
String → "string"bool → "boolean"f32 / f64 → "number""integer"Vec<T> → "array"Option<T> → 自动识别成可选参数也就是说,很多以前必须手写 schema 的地方,现在都可以直接省掉了。
对于日常 Agent 工具来说,这已经能覆盖掉很大一部分场景。
这其实是最省脑子的部分。
宏会自动生成:
Tool trait 实现definition()execute()Arc<dyn Tool> 的构造器所以你看起来只是写了一个普通函数, 实际上已经把一整套工具接入链路都走完了。
我一直觉得,一个好用的 SDK,不是把所有能力都堆出来, 而是要不断追问一件事:
哪些复杂度,用户真的必须自己承担? 哪些复杂度,其实应该由框架接过去?
这次 #[tool] 的引入,就是很典型的一次“把复杂度往框架内部收”。
以前用户要自己写的很多内容,本质上并不属于业务逻辑。 它们只是“为了满足框架接入格式而存在”。
这种代码写一次还行,写十次就会让人烦。 而一旦让人烦,体验就开始打折。
所以与其让用户每次重复劳动, 不如让宏把这些固定模式吃掉。
这也是这次更新我最满意的地方:
它不是在炫技,而是在认真减少使用成本。
我一直不太喜欢一种更新方式: 功能上线了,但文档没动,示例没补,最后用户还得自己猜怎么用。
所以这次除了宏本身,还顺手把几件配套的事一起做了。
#[tool] 放进核心能力现在用户第一次打开项目 README,就能直接看到:
这比“功能已经有了,但你得翻提交记录才知道”要友好得多。
macro_tools 示例这次还加了一个可以直接跑的例子,专门演示宏定义工具。
这个示例不只是告诉你“语法长什么样”,而是把一整条使用链路串起来了:
这种例子对新用户特别重要。 因为很多时候,最好的文档不是长篇解释,而是一个“你复制就能跑”的工程示范。
如果只看提交内容,这次可以概括成:
#[tool] 宏但如果从更大的角度看,我更想说的是:
agent-io正在从“能用”,往“更顺手、更自然”去走。
这很重要。
因为一个框架能不能让人持续喜欢,不只看它支不支持多少 provider, 也不只看它功能列表有多长。
真正决定你会不会继续用它的,往往是这些瞬间:
这次更新,针对的就是这些“真实使用时刻”。
这次改动,最早只是从一个 issue 开始。
一个很简单、也很真诚的建议:
“想引入宏,这样方便使用。”
我很喜欢这种反馈。 因为它提醒我们,做工具和做框架,最终不是为了展示设计有多完整, 而是为了让真正用它的人,少一点别扭,多一点顺手。
所以这次,我们把那段最容易让人烦的样板代码,删掉了一大块。
现在你写的,不再是一层层工具包装。
你写的,就是一个普通的 Rust async fn。
然后它自然变成 Agent 能用的工具。
这件事,我觉得挺值。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!