Agent之Function Call

LLM只知道根据一段token预测下一个token,给它一段话,它能一个token一个token地猜出一个回答来。但是现在Agent做到的事可不仅仅是回答问题,它还能自己读代码改代码,自己设计方案写成文档,自己检查写的代码有没有问题等等。从知道到做到的关键,就是 function call(又被称为tool call,工具调用),LLM会告诉客户端,应该调用什么工具,客户端调用对应的工具,来真正做到一些事情。

一、回答日期相关问题

LLM是无状态的,更没有独立的时钟,问它时间的问题,它就完全不知道了:

(用的上一篇文的练习代码,只是调用了DeepSeek API,并没有调用工具:github.com/rengmo/ai-p...

但是打开DeepSeek APP一问,回答得好好的:

可能是前端获取了会话时的时间信息,或者是后端调用了时间工具获取时间信息传给了模型。

这里并没有去看DeepSeek APP具体是怎么实现的。对于这次的练习来说,是在后端代码中加上了调用时间工具的逻辑,加上后就可以正常获得时间相关的答复了,这个工具是后端代码里实现的,不是LLM的能力。

  1. 首先定义工具的类型、名称和描述、工具的调用参数等信息:
jsx 复制代码
var toolDefs = []Tool{
	{
		Type: "function",
		Function: toolFunction{
			Name:        "get_current_time",
			Description: "获取当前日期、时间和星期。用户问「现在几点」「今天几号」时必须调用,不要编造。",
			Parameters: map[string]any{
				"type":       "object",
				"properties": map[string]any{},
			},
		},
	},
	...
  1. 然后在DeepSeek的API请求里面,作为Tools参数的值传递:
jsx 复制代码
body, _ := json.Marshal(apiChatRequest{Model: model, Messages: messages, Tools: toolDefs})
  1. 如果需要用到工具,模型会返回以下格式的数据(这个是DeepSeek API返回的,LLM本身可能只是返回某种格式的文本 ):
jsx 复制代码
tool_calls: [{ name: "get_current_time" }]
  1. 应用程序拿到数据之后,根据name信息,调用在应用程序代码里定义好的函数:
jsx 复制代码
result, _ := runTool(call.Function.Name, call.Function.Arguments)
jsx 复制代码
func runTool(name, argsJSON string) (ToolResult, error) {
	switch name {
	case "get_current_time":
		return ToolResult{Text: currentTimeText()}, nil
jsx 复制代码
func currentTimeText() string {
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	now := time.Now().In(loc)
	weekdays := []string{"日", "一", "二", "三", "四", "五", "六"}
	return fmt.Sprintf("%s 星期%s", now.Format("2006年1月2日 15:04:05"), weekdays[now.Weekday()])
}
  1. 拿到函数调用的结果后,把结果添加到message列表中,下一轮循环中,用包含tool调用结果的message列表,传给LLM,获取最终结果:
jsx 复制代码
			working = append(working, Message{
				Role:       "tool",
				ToolCallID: call.ID,
				Content:    result.Text,
			})
jsx 复制代码
	for step := 1; step <= 8; step++ {
		reply, u, err := chatOnce(working)
		
		...
		
		if len(reply.ToolCalls) == 0 {
			if reply.Content != "" {
				onEvent(map[string]string{"type": "delta", "content": reply.Content})
			}
			return working, total, nil
		}

这里比较关键的一点是ReAct,不是直接回答,而是推理(Reason)和 行动(Act)交替进行的多轮循环。

  • 观察(Observe)输入模型的内容。(观察 系统提示词 + 用户问题:"今天是几号,星期几?" )
  • 推理(Reason),LLM决定:先调用时间工具 get_current_time。
  • 行动(Act),应用执行对应的get_current_time函数。
  • 再观察(Observe),观察 系统提示词 + 用户问题 + get_current_time返回的时间数据。
  • 推理(Reason),LLM给到问题的答案。
  • 行动(Act),返回给用户答案。

上面这个是一个比较简单的例子,只循环了2次ReAct。

jsx 复制代码
func runAgent(messages []Message, onEvent SSEEventHandler) ([]Message, usageTotals, error) {
    working := append([]Message(nil), messages...)
    var total usageTotals

    for step := 1; step <= 8; step++ {                    // 上限 8 轮
        reply, u, err := chatOnce(working)                // 非流式,才能解析 tool_calls
        // ... 累加 token ...

        working = append(working, reply)

        if len(reply.ToolCalls) == 0 {                    // 模型不再调工具 → 正常结束
            if reply.Content != "" {
                onEvent(map[string]string{"type": "delta", "content": reply.Content})
            }
            return working, total, nil
        }

        for _, call := range reply.ToolCalls {
            onEvent(map[string]any{"type": "tool_start", "name": fn, "args": args})
            result, _ := runTool(call.Function.Name, call.Function.Arguments)
            onEvent(map[string]any{"type": "tool_done", "name": fn, "result": result.Text})
            if result.FileURL != "" {
                onEvent(map[string]any{"type": "file", "url": result.FileURL, "filename": result.FileName})
            }
            working = append(working, Message{Role: "tool", ToolCallID: call.ID, Content: result.Text})
        }
    }
    return working, total, fmt.Errorf("超过最大工具调用步数(8)")
}

这里设置的最大步骤为8次,目前这个循环是线性执行的非常简单的任务,如果是更复杂的任务,可以使用LangChain、LangGraph之类的实现加强版循环。

二、根据当前季节生成菜谱

根据当前季节,给到一个当季的菜谱PPT,用户可以直接下载。这个例子包含了文件处理能力,工具的实现要复杂一些,比上一个例子的ReAct循环次数会多一点,但主要思路还是一样的。

代码地址:github.com/rengmo/ai-p...

相关推荐
清酒难寻7 小时前
深度学习进阶(二十四)Swin 的二维 RPE
人工智能·深度学习
步步为营DotNet7 小时前
借助 Microsoft.Extensions.AI 与 ASP.NET Core 10 实现智能 Web 应用故障预测
人工智能·microsoft·asp.net
AI创界者7 小时前
零基础上手!ComfyUI + LTX-2.3 图生视频完整工作流搭建与调优指南(附避坑细节)
大数据·人工智能
怕浪猫7 小时前
AI图片工具到底有哪些?一份按能力维度整理的清单
人工智能
不听话坏7 小时前
Ignition篇(下 一) 动态执行前的事情
开发语言·前端·javascript
hongmai6668888 小时前
FH8856V310芯片详解:6M高清+0.5TOPS算力,赋能智能安防新方案
人工智能·单片机·嵌入式硬件·物联网·智能家居
一颗小树x8 小时前
NVIDIA Jetson Thor 运行 LLM / VLM:模型全整理与 vLLM 实践
人工智能·llm·jetson·vllm
每日综合8 小时前
蓝白风暴席卷BW2026!雷克沙展台首日燃情纪实
人工智能
To_OC8 小时前
手搓 LangChain 工具调用:原来 Agent 的核心逻辑,就是个 while 循环
人工智能·langchain·llm
Drgfd8 小时前
机器人从工厂走进生活:通用机器人开启大众商用时代
人工智能