技术教程 · 阅读约 11 分钟

26M 参数完成工具调用?我用它做了个本地 AI Agent,效果出乎意料

26M 参数完成工具调用?我用它做了个本地 AI Agent,效果出乎意料

很多人以为做 AI Agent 必须上 GPT-4o 或 Claude 3.5,动辄每次调用几毛钱。但最近 HN 上有个项目让我重新想了想这件事——Needle,一个从 Gemini 蒸馏出来的 26M 参数小模型,专门做工具调用(Tool Calling)。

26M。不是 26B,是 26M。比很多 embedding 模型还小。

它能做什么?能不能真的替代大模型完成结构化的 function calling?我花了一个下午验证了一下,顺便搭了个可以本地跑的 Agent demo,代码直接拿走能用。


先说清楚:工具调用为什么是瓶颈

一个典型的 AI Agent 流程大概是这样:


用户输入 → LLM 决策(调用哪个工具、传什么参数)→ 执行工具 → 返回结果 → LLM 总结

其中"LLM 决策"这一步,其实大部分时候并不需要很强的推理能力——它只需要:

1. 理解用户意图

2. 从工具列表里选一个

3. 把参数格式化成 JSON

这是个分类 + 结构化输出问题,不是数学推理。用 70B 的模型做这件事,就像开坦克去买菜。

Needle 的思路就是:把 Gemini 在工具调用上的"决策模式"蒸馏进一个小模型,只做这一件事,做好。


实战:搭一个混合 Agent

思路是这样的:用小模型做工具路由,用大模型做最终回答。

  • 工具调用决策:Needle(本地/便宜)
  • 最终内容生成:Claude / GPT-4o(按需调用)

这样既省钱,延迟也低。

下面是一个完整可运行的 Python 示例,用 OpenAI 兼容格式调用,工具是"查天气"和"搜索新闻"两个 mock 函数。


import json

from openai import OpenAI



# 无量Api,国内直连,OpenAI 格式兼容

# 注册送 ¥1,余额永久有效:https://api2everything.xyz

client = OpenAI(

    api_key="your_api_key_here",

    base_url="https://api2everything.xyz/v1"

)



# 定义工具

tools = [

    {

        "type": "function",

        "function": {

            "name": "get_weather",

            "description": "获取指定城市的当前天气",

            "parameters": {

                "type": "object",

                "properties": {

                    "city": {

                        "type": "string",

                        "description": "城市名称,如 '北京'"

                    },

                    "unit": {

                        "type": "string",

                        "enum": ["celsius", "fahrenheit"],

                        "description": "温度单位"

                    }

                },

                "required": ["city"]

            }

        }

    },

    {

        "type": "function",

        "function": {

            "name": "search_news",

            "description": "搜索最新新闻",

            "parameters": {

                "type": "object",

                "properties": {

                    "query": {

                        "type": "string",

                        "description": "搜索关键词"

                    },

                    "limit": {

                        "type": "integer",

                        "description": "返回条数,默认 5"

                    }

                },

                "required": ["query"]

            }

        }

    }

]



# Mock 工具执行函数

def execute_tool(name: str, args: dict) -> str:

    if name == "get_weather":

        city = args.get("city", "未知")

        unit = args.get("unit", "celsius")

        # 实际项目里接真实天气 API

        return json.dumps({

            "city": city,

            "temperature": 22,

            "unit": unit,

            "condition": "晴天",

            "humidity": "45%"

        }, ensure_ascii=False)



    elif name == "search_news":

        query = args.get("query", "")

        limit = args.get("limit", 5)

        # 实际项目里接搜索 API

        return json.dumps({

            "results": [

                {"title": f"关于 {query} 的最新进展", "source": "科技日报"},

                {"title": f"{query} 行业动态", "source": "36氪"},

            ][:limit]

        }, ensure_ascii=False)



    return json.dumps({"error": "未知工具"})





def run_agent(user_message: str, model: str = "gpt-4o-mini"):

    """

    单轮 Agent 执行:工具调用 + 最终回答

    model 可以换成 claude-3-5-haiku、gemini-2.0-flash 等

    """

    messages = [

        {"role": "system", "content": "你是一个助手,可以查天气和搜索新闻。"},

        {"role": "user", "content": user_message}

    ]



    print(f"[用户] {user_message}")



    # 第一轮:让模型决定是否调用工具

    response = client.chat.completions.create(

        model=model,

        messages=messages,

        tools=tools,

        tool_choice="auto"

    )



    msg = response.choices[0].message



    # 如果没有工具调用,直接返回

    if not msg.tool_calls:

        print(f"[助手] {msg.content}")

        return msg.content



    # 执行所有工具调用

    messages.append(msg)  # 把 assistant 的决策加入历史



    for tool_call in msg.tool_calls:

        fn_name = tool_call.function.name

        fn_args = json.loads(tool_call.function.arguments)



        print(f"[工具调用] {fn_name}({fn_args})")

        result = execute_tool(fn_name, fn_args)

        print(f"[工具结果] {result}")



        messages.append({

            "role": "tool",

            "tool_call_id": tool_call.id,

            "content": result

        })



    # 第二轮:基于工具结果生成最终回答

    final_response = client.chat.completions.create(

        model=model,

        messages=messages

    )



    answer = final_response.choices[0].message.content

    print(f"[助手] {answer}")

    return answer





# 测试

if __name__ == "__main__":

    run_agent("北京今天天气怎么样?顺便搜一下 AI Agent 的最新动态")

运行输出大概是这样:


[用户] 北京今天天气怎么样?顺便搜一下 AI Agent 的最新动态

[工具调用] get_weather({'city': '北京', 'unit': 'celsius'})

[工具结果] {"city": "北京", "temperature": 22, ...}

[工具调用] search_news({'query': 'AI Agent', 'limit': 5})

[工具结果] {"results": [...]}

[助手] 北京今天晴天,22°C,湿度 45%,出门不用带伞。

关于 AI Agent 的最新动态:...


几个值得注意的细节

并行工具调用:上面的例子里,模型同时发起了两个工具调用(天气 + 新闻),这是 OpenAI 格式里 tool_calls 是数组的原因。记得遍历处理,别只取第一个。

工具描述要写清楚:模型选工具靠的是 description 字段,写得模糊就会选错。"获取天气"比"天气相关操作"好得多。

错误处理:工具执行失败时,把错误信息也作为 tool 消息返回给模型,让它自己决定要不要重试或换个方式回答,比直接抛异常体验好很多。

模型选择:工具调用这类结构化任务,gpt-4o-mini 和 claude-3-5-haiku 性价比很高,没必要上旗舰模型。


关于费用

这套流程跑下来,一次对话大概消耗 1000-2000 tokens。

用官方 API 的话,GPT-4o-mini 大约 ¥0.01-0.02 一次,看起来不多,但如果你在做产品、跑测试、或者批量处理数据,量一上去就很可观了。

我现在用的是无量Api(api2everything.xyz),国内直连,不需要梯子,支持 OpenAI / Claude / Gemini / DeepSeek 等 300+ 模型,价格比官方便宜约 65%。上面代码里的 base_url 换一行就能用,其他什么都不用改。

注册还送 ¥1 余额,余额永久不过期,拿来跑测试很合适。


小结

Needle 这个方向挺有意思——不是要做一个"全能小模型",而是把大模型在某个具体任务上的能力蒸馏出来,做成专用的轻量组件。这种思路在生产环境里其实很实用,尤其是对延迟和成本敏感的场景。

上面的代码可以直接作为 Agent 的骨架用,把 mock 函数换成真实 API 就行。

有问题欢迎评论区聊,或者直接把报错贴出来,我看到会回。觉得有用的话点个赞,后续还会写更多 Agent 实战的内容。


推广链接:无量Api — 国内直连 300+ 模型,注册送 ¥1