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, ""
}