如何在大模型API调用中实现function calling配合流式输出

在上一篇文章,MCP-HOST:简易版的Claude Code命令行工具中,虽然MCP-HOST实现了在与大模型交互中使用MCP server的功能,但是缺乏流式输出的能力,导致在处理稍微复杂的交互中,不能确定任务是否在进行中,还是响应超时了。所以我就在想如何让它的输出方式变为流式输出,这样就可以实时掌握输出的情况了,当然调用MCP server的场景还是需要等其处理完才能知道结果。


什么是Function Calling

简单来讲,Function Calling (函数调用) 让大模型(如DeepSeek、CHATGPT等)在对话过程中智能识别用户需求,并决定是否需要调用外部工具/函数来完成特定任务。从之前的几篇MCP的文章中,我们已经了解到 MCP-Client 和 MCP-Server 通过 List_Tools 和 Call_Tools 这两个接口来查看/使用工具。也就是说相对于 Function Calling 来说,MCP协议解耦了工具的使用,不像使用 Function Calling 那样,你需要在事先在你的Agent中定义好工具,如果后续需要使用新的工具,你需要重新定义Agent,这会带来很多麻烦。

有了上面的说明,你就可以理解,通过 MCP-ClientMCP-Server 来使用 Tools 和使用 Function Calling 其实本质上差不多,所以接下来我们就一起看看如何使用 Function Calling配合流式输出。


定义工具

下面是一段获取当前时间 Tool 的代码:

go 复制代码
// 示例工具函数
func handleToolCall(toolCall ToolCall) string {
	switch toolCall.Function.Name {
	case ToolNameGetTime:
		return time.Now().Format("2006-01-02 15:04:05")
	default:
		return "Tool not found"
	}
}

流式输出

下面是一段流式输出的代码,具体使用 Tool 的逻辑在 handleResponse 中。

go 复制代码
func (c *DeepSeekClient) ChatStream(messages []Message, tools []Tool, handleResponse func(StreamResponse)) error {
	requestBody := map[string]any{
		"model":    "deepseek-chat",
		"messages": messages,
		"stream":   true,
	}
	if len(tools) > 0 {
		requestBody["tools"] = tools
	}

	bodyBytes, _ := json.Marshal(requestBody)
	req, _ := http.NewRequest("POST", APIEndpoint, bytes.NewReader(bodyBytes))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+c.apiKey)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		line := scanner.Bytes()
		if len(line) == 0 {
			continue
		}

		var response StreamResponse
		if err := json.Unmarshal(line[6:], &response); err != nil { // 跳过"data: "前缀
			continue
		}

		handleResponse(response)
	}

	return nil
}

调用 Tool

下面代码是每次流式响应的时候需要回调处理 Tool 的逻辑。

go 复制代码
	handleResponse := func(response StreamResponse) {
		if len(response.Choices) == 0 {
			return
		}

		delta := response.Choices[0].Delta

		// 处理工具调用
		if len(delta.ToolCalls) > 0 {
			for _, toolCall := range delta.ToolCalls {
                if toolCall.Type != "function" {
                    continue
                }
				result := handleToolCall(toolCall)
				fmt.Printf("\n[工具调用] 结果: %s\n", result)
			}
		}

		// 处理常规输出
		if delta.Content != "" {
			fmt.Print(delta.Content)
		}
	}

总结

本文简单的介绍了Function CallingMCP 之间的区别,实现了一个简单的在流式输出中回调工具的示例,完整代码在demo_for_ai中。

相关推荐
武子康3 分钟前
第一次用 SGLang:把本地模型接进聊天应用
人工智能·llm·agent
Geek-Chow1 小时前
MCP 模型上下文协议:八、深入传输层 · stdio 与 Streamable HTTP
人工智能·mcp
Web极客码2 小时前
Pydantic 校验通过不等于答案正确:如何识别 LLM 的语义错误
服务器·人工智能·ai·llm
流浪0012 小时前
大模型技术全景(六):AI 的“逐字接龙“——Token 与自回归生成背后的真相
开发语言·人工智能·llm
米小虾14 小时前
告别逐 token 蹦字:扩散语言模型(dLLM)到底能不能终结自回归?
人工智能·llm
米小虾14 小时前
一周 AI 观察(9.1–9.7):模型能力开始"过剩",行业真正卷的是落地
人工智能·llm
AI技术新视界16 小时前
符号诞生之前:视觉通用智能如何开启 AGI 的物理进化之路
人工智能·llm·agi
asaotomo16 小时前
从抓包插件到浏览器安全 Agent:Hx0 鹰眼 v1.0.6,正式接入 MCP
安全·渗透测试·agent·浏览器插件·ai工具·mcp
AI技术新视界17 小时前
失控的认知外包:大语言模型如何像病毒般入侵人类思维与社会生态
人工智能·llm·认知科学
打破砂锅问到底00717 小时前
115 行把 Qwen3-8B 跑进浏览器:WebLLM 本地推理实战
人工智能·ai·llm