应该是最“极简”的 LLM 工具调用了:你写的函数、类对象就是工具,不用学任何新东西

2025 年 11 月 27 日
 pmpmp

兄弟们,我又来了。前两天发了个帖子《 MCP 到底是个什么鬼?》,没想到对大家有点小帮助,非常开心。

用 LLM 的时候,工具调用确实是一个很烦的事情(它不难,就是很繁琐),要么你在某个框架里面,把你的工具封装成 MCP 服务,要么你在某个框架里面用它的机制包装(比如加一个装饰器 @tool ),五花八门。

有没有简单一点的办法呢?有的,比如最近的 lang 家的 deepagents 里面的方式就已经接近优雅了,它可以让你直接使用自己写的函数,函数(工具)本身你想怎么写怎么写,但是,他又搞了个 middleware 的 tool ,看着又很反直觉了,又得学怎么搞,本来挺清爽的,又搞复杂了。

好麻烦,不就是给 LLM 传工具么?为什么来来回回搞得这么复杂呢?有必要么?

于是,我前两天更新了一下我的库 👉👉👉:chak ( https://github.com/zhixiangxue/chak-ai ),就干一件事情:让你自己写的函数、对象,都能传给 LLM ,让它当工具用,而你不需要写任何额外的东西,连 @tool 这样的装饰器都不需要,就像这样:

# 🚩🚩🚩 这个函数你该怎么写就怎么写,不需要很麻烦的封装成什么 MCP Server ,不需要加装饰器,不需要任何植入
def get_weather(city: str) -> str:
    """查询城市天气"""
    return requests.get(f'xxxx.com?city={city}').text

conv = chak.Conversation("openai/gpt-4o", tools=[get_weather]) # ⬅️ 直接把函数传进来就行了
response = conv.send("北京天气怎么样?")

或者这样:

from pydantic import BaseModel, Field

class UserInput(BaseModel):
    name: str = Field(description="User's full name")
    email: str = Field(description="User's email address")
    age: int = Field(description="User's age")

class UserOutput(BaseModel):
    id: int
    name: str
    status: str = "active"

# 🚩🚩🚩 你的工具的输入输出只要是可以序列化的就行,不必是简单类型,也不需要装饰任何东西
def create_user(user: UserInput) -> UserOutput:
    """Create a new user"""
    return UserOutput(id=123, name=user.name, status="active")

conv = chak.Conversation(
    "openai/gpt-4o",
    tools=[create_user] # ⬅️ 直接传进来
)

response = await conv.asend("Create a user: John Doe, john@example.com, 30 years old")

如果你喜欢把工具封装到一个类里面(这很常见对吧),你传给 LLM 一个对象也是可以的,Chak 会帮你保持对象的状态,调用完成后,你的对象甚至是带有状态更新的,相当于你可以喂给 LLM 一个你的工作流,而这个工作流就是一个普通的对象,像这样:

# 定义一个购物车
class ShoppingCart:
    def __init__(self):
        self.items = []
        self.discount = 0
    
    def add_item(self, name: str, price: float, quantity: int = 1):
        """Add item to cart"""
        self.items.append({"name": name, "price": price, "quantity": quantity})
    
    def apply_discount(self, percent: float):
        """Apply discount percentage"""
        self.discount = percent
    
    def get_total(self) -> float:
        """Calculate total price"""
        subtotal = sum(item["price"] * item["quantity"] for item in self.items)
        return subtotal * (1 - self.discount / 100)

cart = ShoppingCart()

conv = chak.Conversation(
    "openai/gpt-4o",
    tools=[cart]  # ⬅️ 直接把这个对象作为工具
)

# 让 LLM 帮你在这个购物车里面做一些操作
response = await conv.asend(
    "Add 2 iPhones at $999 each, then apply 10% discount and tell me the total"
)

# 🚩🚩🚩 LLM 折腾完了之后,你的购物车里面的数据就更新了,你都不需要解析什么 LLM 的输入(因为很多时候 LLM 输出的 Json 未必准确,有各种乌七八糟的问题),chak 自动帮你把这事情干完了
print(cart.items)     # [{'name': 'iPhone', 'price': 999, 'quantity': 2}]
print(cart.discount)  # 10
print(cart.get_total())  # 1798.2

所以,“工具调用非得用 MCP 、学 MCP 吗?” —— 其实大可不必(当然 Chak 也是支持 MCP 的,用起来也非常简单)

让 LLM 可以调用工具,就应该这么简单才丝滑,对吧...

最后说两句

工具调用本应该是助力 LLM 变得更强的,现在很多时候成了开发者的负担。如果你也烦透了这事,真心可以试试 chak:它把这些破事都打包了,你只管写业务代码,非常轻量级,依赖非常少。

项目地址👉👉👉: https://github.com/zhixiangxue/chak-ai

各位帮忙使用一下,好用的话帮我点个⭐啊,随时提 bug 、issue ,感谢~

2592 次点击
所在节点    程序员
1 条回复
GoldenMan
2025 年 11 月 28 日
很妙的想法,已 Star

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1175461

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX