mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 01:18:08 +00:00
fix: deduplicate assistant content in multi-tool turns to prevent premature loop exit
- process.go: only emit resp.Content on the first tool call per batch, subsequent assistant messages use empty content (serialized as null) - provider.go: MarshalJSON outputs null content when empty with tool_calls to comply with DeepSeek/OpenAI expected format - output.go: update parameter interface (payload/type/meta) to match tool definitions (uncommitted from previous refactor)
This commit is contained in:
@ -47,8 +47,10 @@ func (m Message) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
if len(m.Blocks) > 0 {
|
||||
raw["content"] = m.Blocks
|
||||
} else {
|
||||
} else if m.Content != "" || len(m.ToolCalls) == 0 {
|
||||
raw["content"] = m.Content
|
||||
} else {
|
||||
raw["content"] = nil
|
||||
}
|
||||
if m.ReasoningContent != "" {
|
||||
raw["reasoning_content"] = m.ReasoningContent
|
||||
|
||||
@ -11,25 +11,46 @@ import (
|
||||
|
||||
func (a *Agent) executeOutputSendTool(tc agentAPI.ToolCall) string {
|
||||
channel := strings.TrimPrefix(tc.Name, "output_send__")
|
||||
if channel == "" {
|
||||
return "工具名称格式: output_send__{channel}"
|
||||
}
|
||||
content, _ := tc.Arguments["content"].(string)
|
||||
if content == "" {
|
||||
return "content 不能为空"
|
||||
payload, _ := tc.Arguments["payload"].(string)
|
||||
rawType, _ := tc.Arguments["type"].(string)
|
||||
if channel == "" || payload == "" || rawType == "" {
|
||||
return "工具名称格式: output_send__{channel},payload 和 type 不能为空"
|
||||
}
|
||||
meta, _ := tc.Arguments["meta"].(string)
|
||||
|
||||
caps := a.io.GetChannelCapabilities(channel)
|
||||
if caps == 0 {
|
||||
return fmt.Sprintf("通道 [%s] 不存在或不可用。可用输出工具列表见 output_list_channels", channel)
|
||||
}
|
||||
switch rawType {
|
||||
case "text":
|
||||
if !caps.Supports(agentIO.CapText) {
|
||||
return fmt.Sprintf("通道 [%s] 不支持文本输出(能力: %s)", channel, caps.String())
|
||||
}
|
||||
case "voice", "audio":
|
||||
if !caps.Supports(agentIO.CapAudio) {
|
||||
return fmt.Sprintf("通道 [%s] 不支持语音输出(能力: %s)", channel, caps.String())
|
||||
}
|
||||
case "image":
|
||||
if !caps.Supports(agentIO.CapImage) {
|
||||
return fmt.Sprintf("通道 [%s] 不支持图片输出(能力: %s)", channel, caps.String())
|
||||
}
|
||||
case "file":
|
||||
if !caps.Supports(agentIO.CapFile) {
|
||||
return fmt.Sprintf("通道 [%s] 不支持文件输出(能力: %s)", channel, caps.String())
|
||||
}
|
||||
}
|
||||
|
||||
if !caps.Supports(agentIO.CapText) {
|
||||
return fmt.Sprintf("通道 [%s] 不支持文本输出(能力: %s)", channel, caps.String())
|
||||
args := map[string]interface{}{
|
||||
"payload": payload,
|
||||
"type": rawType,
|
||||
}
|
||||
if meta != "" {
|
||||
args["meta"] = meta
|
||||
}
|
||||
|
||||
stageCtx := &sdk.StageContext{
|
||||
FinalText: content,
|
||||
FinalText: payload,
|
||||
Phase: sdk.StageBeforeOutput,
|
||||
}
|
||||
a.runStage(sdk.StageBeforeOutput, stageCtx)
|
||||
@ -39,11 +60,7 @@ func (a *Agent) executeOutputSendTool(tc agentAPI.ToolCall) string {
|
||||
if stageCtx.FinalText == "" {
|
||||
return "输出被插件清空"
|
||||
}
|
||||
|
||||
args := map[string]interface{}{
|
||||
"payload": stageCtx.FinalText,
|
||||
"type": "text",
|
||||
}
|
||||
args["payload"] = stageCtx.FinalText
|
||||
|
||||
if dev := a.io.GetDevice(channel); dev != nil {
|
||||
result, err := dev.Execute("output", args)
|
||||
@ -53,7 +70,7 @@ func (a *Agent) executeOutputSendTool(tc agentAPI.ToolCall) string {
|
||||
return fmt.Sprintf("已通过 [%s] 通道发送: %v", channel, result)
|
||||
}
|
||||
|
||||
a.io.EmitTextTo("agent_io", channel, stageCtx.FinalText)
|
||||
a.io.EmitTextTo("agent_io", channel, payload)
|
||||
return fmt.Sprintf("已通过 [%s] 通道发送", channel)
|
||||
}
|
||||
|
||||
|
||||
@ -179,6 +179,7 @@ func (a *Agent) process(input string, stageCtx *sdk.StageContext) (response stri
|
||||
return resp.Content, toolsUsed, nil
|
||||
}
|
||||
|
||||
contentOnce := true
|
||||
for _, tc := range resp.ToolCalls {
|
||||
if len(a.interceptCh) > 0 {
|
||||
for _, interrupt := range a.drainInterrupts() {
|
||||
@ -203,7 +204,7 @@ func (a *Agent) process(input string, stageCtx *sdk.StageContext) (response stri
|
||||
stageCtx.ToolResults = nil
|
||||
if a.runStage(sdk.StageBeforeToolcall, stageCtx) {
|
||||
result := fmt.Sprintf("工具 %s 已被插件拒绝", tc.Name)
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", Content: resp.Content, ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgs = append(msgs, agentAPI.Message{Role: "tool", ToolCallID: tc.ID, Content: result})
|
||||
a.publishEvent(events.EventToolCall, map[string]interface{}{
|
||||
"tool": tc.Name,
|
||||
@ -219,7 +220,7 @@ func (a *Agent) process(input string, stageCtx *sdk.StageContext) (response stri
|
||||
if pluginName != "" && !a.pluginHealth.isHealthy(pluginName) {
|
||||
result := fmt.Sprintf("插件 %s 处于崩溃状态,已跳过执行,等待自动恢复重载", pluginName)
|
||||
log.Printf("[agent] skip tool %s: plugin %s unhealthy", tc.Name, pluginName)
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", Content: resp.Content, ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgs = append(msgs, agentAPI.Message{Role: "tool", ToolCallID: tc.ID, Content: result})
|
||||
continue
|
||||
}
|
||||
@ -238,7 +239,12 @@ func (a *Agent) process(input string, stageCtx *sdk.StageContext) (response stri
|
||||
argsJSON, _ := json.Marshal(tc.Arguments)
|
||||
a.recordToolCall(tc.Name, string(argsJSON), result)
|
||||
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", Content: resp.Content, ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgContent := ""
|
||||
if contentOnce {
|
||||
msgContent = resp.Content
|
||||
contentOnce = false
|
||||
}
|
||||
msgs = append(msgs, agentAPI.Message{Role: "assistant", Content: msgContent, ToolCalls: []agentAPI.ToolCall{tc}})
|
||||
msgs = append(msgs, agentAPI.Message{Role: "tool", ToolCallID: tc.ID, Content: result})
|
||||
|
||||
a.publishEvent(events.EventToolCall, map[string]interface{}{
|
||||
|
||||
Reference in New Issue
Block a user