func toolsCall()

in plugins/wasm-go/extensions/ai-agent/main.go [382:514]


func toolsCall(ctx wrapper.HttpContext, llmClient wrapper.HttpClient, llmInfo LLMInfo, jsonResp JsonResp, aPIsParam []APIsParam, aPIClient []wrapper.HttpClient, content string, rawResponse Response, log wrapper.Log) (types.Action, string) {
	dashscope.MessageStore.AddForAssistant(content)

	action, actionInput := outputParser(content, log)

	//得到最终答案
	if action == "Final Answer" {
		return types.ActionContinue, actionInput
	}
	count := ctx.GetContext(ToolCallsCount).(int)
	count++
	log.Debugf("toolCallsCount:%d, config.LLMInfo.MaxIterations=%d", count, llmInfo.MaxIterations)
	//函数递归调用次数,达到了预设的循环次数,强制结束
	if int64(count) > llmInfo.MaxIterations {
		ctx.SetContext(ToolCallsCount, 0)
		return types.ActionContinue, ""
	} else {
		ctx.SetContext(ToolCallsCount, count)
	}

	//没得到最终答案

	var urlStr string
	var headers [][2]string
	var apiClient wrapper.HttpClient
	var method string
	var reqBody []byte
	var maxExecutionTime int64

	for i, apisParam := range aPIsParam {
		maxExecutionTime = apisParam.MaxExecutionTime
		for _, tools_param := range apisParam.ToolsParam {
			if action == tools_param.ToolName {
				log.Infof("calls %s", tools_param.ToolName)
				log.Infof("actionInput: %s", actionInput)

				//将大模型需要的参数反序列化
				var data map[string]interface{}
				if err := json.Unmarshal([]byte(actionInput), &data); err != nil {
					log.Debugf("Error: %s", err.Error())
					return types.ActionContinue, ""
				}

				method = tools_param.Method

				// 组装 URL 和请求体
				urlStr = apisParam.URL + tools_param.Path

				// 解析URL模板以查找路径参数
				urlParts := strings.Split(urlStr, "/")
				for i, part := range urlParts {
					if strings.Contains(part, "{") && strings.Contains(part, "}") {
						for _, param := range tools_param.ParamName {
							paramNameInPath := part[1 : len(part)-1]
							if paramNameInPath == param {
								if value, ok := data[param]; ok {
									// 删除已经使用过的
									delete(data, param)
									// 替换模板中的占位符
									urlParts[i] = url.QueryEscape(value.(string))
								}
							}
						}
					}
				}

				// 重新组合URL
				urlStr = strings.Join(urlParts, "/")

				queryParams := make([][2]string, 0)
				if method == "GET" {
					for _, param := range tools_param.ParamName {
						if value, ok := data[param]; ok {
							queryParams = append(queryParams, [2]string{param, fmt.Sprintf("%v", value)})
						}
					}
				} else if method == "POST" {
					var err error
					reqBody, err = json.Marshal(data)
					if err != nil {
						log.Debugf("Error marshaling JSON: %s", err.Error())
						return types.ActionContinue, ""
					}
				}

				// 组装 headers 和 key
				headers = [][2]string{{"Content-Type", "application/json"}}
				if apisParam.APIKey.Name != "" {
					if apisParam.APIKey.In == "query" {
						queryParams = append(queryParams, [2]string{apisParam.APIKey.Name, apisParam.APIKey.Value})
					} else if apisParam.APIKey.In == "header" {
						headers = append(headers, [2]string{"Authorization", apisParam.APIKey.Name + " " + apisParam.APIKey.Value})
					}
				}

				if len(queryParams) > 0 {
					// 将 key 拼接到 url 后面
					urlStr += "?"
					for i, param := range queryParams {
						if i != 0 {
							urlStr += "&"
						}
						urlStr += url.QueryEscape(param[0]) + "=" + url.QueryEscape(param[1])
					}
				}

				log.Debugf("url: %s", urlStr)

				apiClient = aPIClient[i]
				break
			}
		}
	}

	if apiClient != nil {
		err := apiClient.Call(
			method,
			urlStr,
			headers,
			reqBody,
			func(statusCode int, responseHeaders http.Header, responseBody []byte) {
				toolsCallResult(ctx, llmClient, llmInfo, jsonResp, aPIsParam, aPIClient, content, rawResponse, log, statusCode, responseBody)
			}, uint32(maxExecutionTime))
		if err != nil {
			log.Debugf("tool calls error: %s", err.Error())
			proxywasm.ResumeHttpRequest()
		}
	} else {
		return types.ActionContinue, ""
	}

	return types.ActionPause, ""
}