更新各项内容

This commit is contained in:
2026-09-11 00:22:49 +08:00
parent 1304e71b7a
commit b9cdabe045
24 changed files with 3109 additions and 469 deletions
+72 -15
View File
@@ -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