更新各项内容
This commit is contained in:
@@ -179,11 +179,12 @@ type mcpToolSummary struct {
|
||||
|
||||
const maxToolRounds = 6
|
||||
|
||||
// aiUsage 一次 AI 调用的 token 用量
|
||||
// aiUsage 一次 AI 调用的 token 用量与真实模型
|
||||
type aiUsage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
Model string `json:"model,omitempty"` // 接口实际返回的使用模型(如网关把 auto 解析后的真实模型)
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
}
|
||||
|
||||
// add 累加用量
|
||||
@@ -201,6 +202,21 @@ func (u aiUsage) total() int {
|
||||
return u.PromptTokens + u.CompletionTokens
|
||||
}
|
||||
|
||||
// usedMcpTool 单次调用中实际使用的 MCP 工具(含所属服务器)
|
||||
type usedMcpTool struct {
|
||||
ServerID uint64 `json:"server_id"`
|
||||
ServerName string `json:"server_name"`
|
||||
ToolName string `json:"tool_name"`
|
||||
}
|
||||
|
||||
// aiToolUsage 一次 AI 工具循环调用的元信息:用了什么模型、调用了哪些 MCP 工具
|
||||
type aiToolUsage struct {
|
||||
Model string `json:"model"` // 实际使用的模型
|
||||
Rounds int `json:"rounds"` // 工具循环轮数
|
||||
Tools []usedMcpTool `json:"tools"` // 实际被调用的 MCP 工具(去重)
|
||||
ToolCount int `json:"tool_count"` // 工具调用种类数
|
||||
}
|
||||
|
||||
// sanitizeToolName 工具名规范化为 OpenAI 允许的字符集
|
||||
func sanitizeToolName(s string) string {
|
||||
var sb strings.Builder
|
||||
@@ -346,24 +362,56 @@ func buildMcpAssistantMessage(text string, pending []pendingToolCall) openaiMess
|
||||
return msg
|
||||
}
|
||||
|
||||
// runToolLoop 运行带已启用 MCP 工具的完整对话循环(非流式),返回最终文本。
|
||||
// runToolLoop 运行带已启用 MCP 工具的完整对话循环(非流式),返回最终文本与调用元信息。
|
||||
// 供聊天非流式接口与智能生成等模块复用。
|
||||
func runToolLoop(claims *jwtutil.Claims, provider models.BackendAiProvider, model string, systemPrompt string, messages []openaiMessage) (string, error) {
|
||||
func runToolLoop(claims *jwtutil.Claims, provider models.BackendAiProvider, model string, systemPrompt string, messages []openaiMessage) (string, *aiToolUsage, error) {
|
||||
// 智能生成等内部调用:默认注入全部已启用的 MCP 工具
|
||||
llmTools, refMap, _, _ := collectMcpTools(claims, true, nil)
|
||||
usage := &aiToolUsage{Model: model}
|
||||
usedSet := make(map[string]bool)
|
||||
addUsed := func(key string) {
|
||||
ref, ok := refMap[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id := fmt.Sprintf("%d:%s", ref.ServerID, ref.ToolName)
|
||||
if usedSet[id] {
|
||||
return
|
||||
}
|
||||
usedSet[id] = true
|
||||
usage.Tools = append(usage.Tools, usedMcpTool{
|
||||
ServerID: ref.ServerID,
|
||||
ServerName: ref.ServerName,
|
||||
ToolName: ref.ToolName,
|
||||
})
|
||||
}
|
||||
|
||||
current := messages
|
||||
rounds := 0
|
||||
for {
|
||||
rounds++
|
||||
text, pending, _, err := callAITools(provider, model, systemPrompt, current, llmTools)
|
||||
text, pending, roundUsage, err := callAITools(provider, model, systemPrompt, current, llmTools)
|
||||
// 用接口实际返回的真实模型覆盖占位符(如 auto),若网关未回传则保留请求模型
|
||||
if roundUsage.Model != "" {
|
||||
usage.Model = roundUsage.Model
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
usage.Rounds = rounds
|
||||
usage.ToolCount = len(usage.Tools)
|
||||
return "", usage, err
|
||||
}
|
||||
for _, pc := range pending {
|
||||
addUsed(pc.Name)
|
||||
}
|
||||
if len(pending) == 0 {
|
||||
return text, nil
|
||||
usage.Rounds = rounds
|
||||
usage.ToolCount = len(usage.Tools)
|
||||
return text, usage, nil
|
||||
}
|
||||
if rounds >= maxToolRounds {
|
||||
return text, nil
|
||||
usage.Rounds = rounds
|
||||
usage.ToolCount = len(usage.Tools)
|
||||
return text, usage, nil
|
||||
}
|
||||
current = append(current, buildMcpAssistantMessage(text, pending))
|
||||
toolMsgs := executePendingTools(claims, pending, refMap, nil, nil)
|
||||
@@ -923,6 +971,7 @@ func callAITools(provider models.BackendAiProvider, model string, systemPrompt s
|
||||
return "", nil, aiUsage{}, fmt.Errorf("API返回错误状态 %d: %s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
var result struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Message struct {
|
||||
Content string `json:"content"`
|
||||
@@ -955,6 +1004,10 @@ func callAITools(provider models.BackendAiProvider, model string, systemPrompt s
|
||||
TotalTokens: result.Usage.TotalTokens,
|
||||
}
|
||||
}
|
||||
// 接口实际返回的使用模型(网关可能将 auto 解析为具体模型)
|
||||
if result.Model != "" {
|
||||
usage.Model = result.Model
|
||||
}
|
||||
content := result.Choices[0].Message.Content
|
||||
var pending []pendingToolCall
|
||||
for _, tc := range result.Choices[0].Message.ToolCalls {
|
||||
@@ -1029,6 +1082,8 @@ func callAITools(provider models.BackendAiProvider, model string, systemPrompt s
|
||||
TotalTokens: result.Usage.InputTokens + result.Usage.OutputTokens,
|
||||
}
|
||||
}
|
||||
// Anthropic 响应不回传模型名,记录请求使用的模型
|
||||
usage.Model = model
|
||||
var text strings.Builder
|
||||
var pending []pendingToolCall
|
||||
for _, block := range result.Content {
|
||||
@@ -1129,6 +1184,7 @@ func callOpenAIStreamTools(provider models.BackendAiProvider, model string, syst
|
||||
break
|
||||
}
|
||||
var chunk struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Delta struct {
|
||||
Content string `json:"content"`
|
||||
@@ -1152,11 +1208,12 @@ func callOpenAIStreamTools(provider models.BackendAiProvider, model string, syst
|
||||
return "", nil, aiUsage{}, fmt.Errorf(chunk.Error.Message)
|
||||
}
|
||||
if chunk.Usage != nil {
|
||||
usage = aiUsage{
|
||||
PromptTokens: chunk.Usage.PromptTokens,
|
||||
CompletionTokens: chunk.Usage.CompletionTokens,
|
||||
TotalTokens: chunk.Usage.TotalTokens,
|
||||
}
|
||||
usage.PromptTokens = chunk.Usage.PromptTokens
|
||||
usage.CompletionTokens = chunk.Usage.CompletionTokens
|
||||
usage.TotalTokens = chunk.Usage.TotalTokens
|
||||
}
|
||||
if chunk.Model != "" {
|
||||
usage.Model = chunk.Model
|
||||
}
|
||||
if len(chunk.Choices) == 0 {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user