V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
pmpmp
V2EX  ›  程序员

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

  •  
  •   pmpmp · 3 天前 · 898 次点击

    兄弟们,我又来了。前两天发了个帖子《 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, [email protected], 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 ,感谢~

    1 条回复    2025-11-28 10:42:50 +08:00
    GoldenMan
        1
    GoldenMan  
       2 天前   ❤️ 1
    很妙的想法,已 Star
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2365 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 15:34 · PVG 23:34 · LAX 07:34 · JFK 10:34
    ♥ Do have faith in what you're doing.