惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
月光博客
月光博客
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
O
OpenAI News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Know Your Adversary
Know Your Adversary
Last Week in AI
Last Week in AI
S
Securelist
Engineering at Meta
Engineering at Meta
博客园 - 司徒正美
P
Privacy & Cybersecurity Law Blog
T
Tailwind CSS Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
U
Unit 42
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hugging Face - Blog
Hugging Face - Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
Lohrmann on Cybersecurity
IT之家
IT之家
Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
S
Security @ Cisco Blogs
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
人人都是产品经理
人人都是产品经理
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 码甲哥不卷

WorkBuddy已经成国民应用了,我们day0支持了腾讯混元3大模型 1分钟买不了吃亏系列: nginx动态域名解析 3min手搓一个帮助文档,很合理吧! 哈哈哈哈哈打不过我吧,没有办法我(vllm)就是这么强大! GLM模型这么火,咱们用vllm也咧一个呗! 同样都是九年义务教育,他知道的AI算力科普好像比我多耶 higress 这个中登才是AI时代的心头好 MetalLB才是给Ingress这个老登做负重前行的那个男人 超性感的轻量级openclaw平替,我给nanobot打call 还有比ollama更傻瓜式的大模型本地部署方式吗 ? 🔎我不允许谁还分不清这三种watch机制的区别 云原生AI算力平台的架构解读 🚀糟糕,我实现的k8s informer好像是依托答辩 🎉在k8s调度的花园里面挖呀挖 🎉卷不过AI算法, AI工程化或许是一个出路 我是新来的,我需要知道这些吗?网关上的限流器 新来的外包,在大群分享了它的限流算法的实现 新来的外包,限流算法用的这么6 面试总被追问k8s调度器工作原理, 收藏 == 学废 kong网关反向代理grpc请求 幂等的双倍快乐,你值得拥有 JWT 这点小秘密,你们肯定知道! Go动态感知资源变更的技术实践,你指定用过!
我不允许谁还不清楚function call在AI-Agent领域中打手的地位
神仙别打架 · 2026-01-29 · via 博客园 - 码甲哥不卷

前文提要:还有比ollama更傻瓜式的大模型本地部署方式吗 ?

1.function calling 底层工作原理

大模型重塑了我们与软件应用的交互方式, 其中最重要的特性就是 function calling 。

一种利用结构化输入/输出在LLM和编程应用之间建立桥梁的方式。

不管是当前火热的AI-agent还是MCP,了解function calling底层工作原理都至关重要,特别是request和response payload。

回顾上文我们与qwen大模型的对话:

what is the temperature in the capital of china today?

image

一个由LLM驱动的应用, 回答这个问题,应用与LLM经历了三次对话。

① 第一次请求payload:

  • message:标准的历史对话, 提供上下文context
  • tools:一系列的工具函数定义, 提供给LLM来选择

LLM的响应payload:

  • tool_calls.function.name: LLM选中的工具函数get_currentDate
  • tool_calls.id: 由LLM为这个函数指定的id,后面会用到

② 第二次request:

  • messages.role.assistant: 告诉LLM我们这次请求包含了上次function calling的结果
  • messages.role.tool: 上次function calling执行的结果2026-01-24

LLM的响应payload:

如第一次类似:
本例也有tool_calls:包含LLM选中的函数get_temperature() 和所有的参数beijing 2026-01-24

③ 应用最后一次请求,包含所有信息

LLM推理认为不再需要外部工具,不再返回tool_calls,给出结合外部工具的对话结果。


从三次请求对话来看, LLM在三次对话的响应中体现了它的思考和逻辑步骤,应用持续被LLM引导做出行动,同时LLM也持续对应用的行为做出进一步观察和思考。

2. agent的实现原理

大模型是 AI 的大脑,其核心是理解自然语言,并做出回应,(文本)大模型本身只能接收一段文本,然后输出一段文本。

而当你希望大模型能使用一些工具自行获取所需的信息、执行一些动作,就需要使用 Tool 来实现了,拥有了 Tool 的大模型就像是拥有了手脚,可以和当下已有的 IT 基础设施进行交互。

RAG给LLM装上实时知识外挂, 通过将信息检索与文本生成结合,让模型能引用外部权威信息生成回答,既保证了时效性,又提升了准确性。

字节开源的Eino标榜的优势在于:

  1. LangChain,LlamaIndex等主流框架虽然起源自python强大的AI生态,但是也继承了python“弱类型检查”和“长期维护成本高”的诟病。 Eino作为golang下的开源agent开发框架,规避了这一问题。

  2. 另一方面, 借助字节系在agent领域的工程化实践,Eino既封装了领域内不变的通用核心和最佳实践,也能敏捷的反映业内技术动向。

Eino框架结构图:

image

Eino 有三大稳定内核: compose编排、components组件,common公共库。

组件一抹多,是原子能力的最小单位, 编排对这些组件进行组合、串连。

3. 中国首都今天的天气怎么样? 我母鸡啊

我们使用Eino框架来实现本文的题目:
what is the temperature in the capital of china today

按照我们的分析,
从LLM的视角,要回答这个问题,经历了“思考-行动-观察-思考” 循环, 这是一个ReAct模式的agent。

下面基于阿里百炼千问大模型,实现了天气对话,请自行从阿里百炼平台申请的apiKey替换到54行。

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/cloudwego/eino-ext/components/model/qwen"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/components/tool/utils"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/schema"
)

// what is  the temperature in the capital of china today

type weatherReqParam struct {
	City string `json:"city"  jsonschema:"description=the name of the city"`
	Date string `json:"date"  jsonschema:"description=the date in the format of YYYY-MM-DD, please use the result from get_currentDate tool"`
}

func GetTemperatureFunc(_ context.Context, p weatherReqParam) (float64, error) {
	// 这里直接mock一个温度值,实际应用中应该替换为真实的API调用
	return 32, nil
}

func GetCurrentDateFunc(_ context.Context, _ struct{}) (string, error) {
	return time.Now().Format("2006-01-02"), nil
}

func of[T any](t T) *T {
	return &t
}

func main() {
	getDateTool, err := utils.InferTool("get_currentDate", "Get the current date", GetCurrentDateFunc)
	if err != nil {
		panic(err)
	}
	getTemperatureTool, err := utils.InferTool("get_temperature", "Get the temperature in the capital of china", GetTemperatureFunc)
	if err != nil {
		panic(err)
	}

	// 初始化 tools
	weatherTools := []tool.BaseTool{
		getDateTool,
		getTemperatureTool,
	}

	apiKey := os.Getenv("DASHSCOPE_API_KEY")
	apiKey = "{}"  // 在阿里百炼平台申请签名
	modelName := os.Getenv("MODEL_NAME")
	modelName = "qwen3-max"
	chatModel, err := qwen.NewChatModel(context.Background(), &qwen.ChatModelConfig{
		BaseURL:     "https://dashscope.aliyuncs.com/compatible-mode/v1",
		APIKey:      apiKey,
		Timeout:     0,
		Model:       modelName,
		MaxTokens:   of(2048),
		Temperature: of(float32(0.7)),
		TopP:        of(float32(0.7)),
	})
	if err != nil {
		log.Fatalf("NewChatModel of qwen failed, err=%v", err)
	}

	var ctx = context.Background()
	// 获取工具信息并绑定到 ChatModel
	toolInfos := make([]*schema.ToolInfo, 0, len(weatherTools))
	for _, tool := range weatherTools {
		info, err := tool.Info(ctx)
		if err != nil {
			log.Fatal(err)
		}
		toolInfos = append(toolInfos, info)
	}
	err = chatModel.BindTools(toolInfos)
	if err != nil {
		log.Fatal(err)
	}

	// 创建 tools 节点
	weatherToolsNode, err := compose.NewToolNode(context.Background(), &compose.ToolsNodeConfig{
		Tools: weatherTools,
	})
	if err != nil {
		log.Fatal(err)
	}

	// 构建基于 Graph 的 Agent,实现 ReAct 模式(自动执行工具并生成自然语言回复)

	// 定义状态,用于保存对话历史
	type appState struct {
		messages []*schema.Message
	}

	graph := compose.NewGraph[[]*schema.Message, *schema.Message](
		compose.WithGenLocalState(func(ctx context.Context) *appState {
			return &appState{}
		}),
	)

	// 添加 ChatModel 节点
	err = graph.AddChatModelNode("qwen_chat_model", chatModel,
		compose.WithStatePreHandler(func(ctx context.Context, input []*schema.Message, state *appState) ([]*schema.Message, error) {
			// 如果是第一次进入(从 Start),将输入(用户问题)添加到历史
			if len(state.messages) == 0 {
				state.messages = append(state.messages, input...)
			}
			// 始终将完整的历史记录作为 ChatModel 的输入
			return state.messages, nil
		}),
		compose.WithStatePostHandler(func(ctx context.Context, output *schema.Message, state *appState) (*schema.Message, error) {
			// 将 ChatModel 的输出(可能是工具调用或最终回复)添加到历史
			state.messages = append(state.messages, output)
			return output, nil
		}),
	)
	if err != nil {
		log.Fatal(err)
	}

	// 添加 Tools 节点
	err = graph.AddToolsNode("agent_tools", weatherToolsNode,
		compose.WithStatePostHandler(func(ctx context.Context, output []*schema.Message, state *appState) ([]*schema.Message, error) {
			// 将工具执行结果添加到历史
			state.messages = append(state.messages, output...)
			return output, nil
		}),
	)
	if err != nil {
		log.Fatal(err)
	}

	// 添加边和分支
	_ = graph.AddEdge(compose.START, "qwen_chat_model")

	// 如果有工具调用,流转到 tools;否则结束
	_ = graph.AddBranch("qwen_chat_model", compose.NewGraphBranch(func(ctx context.Context, msg *schema.Message) (string, error) {
		if len(msg.ToolCalls) > 0 {
			return "agent_tools", nil
		}
		return compose.END, nil
	}, map[string]bool{"agent_tools": true, compose.END: true}))

	// 工具执行完后,回流到 chat_model 生成回复
	_ = graph.AddEdge("agent_tools", "qwen_chat_model")

	// 编译运行
	agent, err := graph.Compile(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// 运行示例
	resp, err := agent.Invoke(ctx, []*schema.Message{
		{
			Role:    schema.User,
			Content: "what is the temperature in the capital of china today? ",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	// 输出结果
	fmt.Println(resp.Content)
}

一开始我参照的官网的chain编排Eino组件,但是得到的结果是数字“32”, 并不是LLM对话的类人语言。

使用Trae的编码agent,20s就帮我改成了Graph形式的正确编码, 输出:

The temperature in Beijing, the capital of China, today (January 28, 2026) is a warm 32°C! That’s quite hot for this time of year—make sure to stay hydrated and cool if you’re out and about!

本例实际也可以使用Chain来完成,chain是一种特殊的、简化的graph,chain不能回头,本例需要手动串起来,Chain更适合确定性的编排。

Graph就像一个自动化的流水线(loop),工人是 chat_model 和 tools :

本例的Graph图如下,读者可以结合源代码理解。

image

  1. START -> chat_model
    • ChatModel 拿到用户问题,思考后说:“我需要查日期。”(输出包含 ToolCall)
  2. chat_model -> tools (由 Branch 判断)
    • Tools 听到指令,去查日期,得到 "2026-01-24"。
  3. tools -> chat_model (由 AddEdge 强制流转)
    • ChatModel 再次被唤醒。这次它的输入包含了之前的历史(用户问题 + 刚才的 ToolCall + 刚才的结果)。
    • 它再次思考:“我知道日期了,现在我要查北京的温度。”(输出包含 ToolCall)
  4. chat_model -> tools (再次循环)
    • Tools 去查温度,得到 "32度"。
  5. tools -> chat_model (再次循环)
    • ChatModel 再次被唤醒。现在的输入更丰富了(所有历史)。
    • 它再次思考:“日期有了,温度也有了,我可以直接回答用户了。”(输出 不包含 ToolCall)
  6. chat_model -> END (由 Branch 判断)
    • 循环结束。

最后我们重温全文,function calling(function tools)在agent中的作用:
在由LLM驱动的agent应用中,function calling(function tools)作为LLM的手脚,让LLM具备使用工具从外部获取最新信息并指导应用行为的能力,这一过程由结构化的输入输出参数来传递。