Files
LaodingBot/internal/agent/react_parser.go

62 lines
2.0 KiB
Go

package agent
import (
"encoding/json"
"fmt"
"strings"
)
// reactDecision 是 LLM 在统一 ReAct 循环中返回的结构化 JSON 决策。
// 每轮 LLM 调用都返回这个结构,由 agent 判断是否继续循环。
type reactDecision struct {
// Thought 是 LLM 的当前推理过程描述
Thought string `json:"thought"`
// Action 是需要调用的工具名称(如 "file"、"shell"、"web_search"),不需要工具时为 "none" 或空
Action string `json:"action"`
// ActionInput 是传给工具的输入参数,可以是字符串或结构化对象
ActionInput json.RawMessage `json:"action_input"`
// IsFinalAnswer 标记本轮是否为最终回答。true 表示 ReAct 循环结束。
IsFinalAnswer bool `json:"is_final_answer"`
// FinalAnswer 当 IsFinalAnswer 为 true 时,包含给用户的最终回复内容
FinalAnswer *string `json:"final_answer"`
}
// GetActionInputString 将 ActionInput 转为字符串,用于传递给工具的 Call 方法。
// 如果 ActionInput 是 JSON 字符串则去掉引号;如果是对象/数组则保持 JSON 原文。
func (d *reactDecision) GetActionInputString() string {
if len(d.ActionInput) == 0 {
return ""
}
// 尝试解析为字符串
var s string
if err := json.Unmarshal(d.ActionInput, &s); err == nil {
return s
}
// 非字符串则直接返回 JSON 原文
return strings.TrimSpace(string(d.ActionInput))
}
func parseDecision(raw string) (reactDecision, error) {
raw = normalizeJSON(raw)
start := strings.Index(raw, "{")
end := strings.LastIndex(raw, "}")
if start < 0 || end < start {
return reactDecision{}, fmt.Errorf("no json object found")
}
raw = raw[start : end+1]
var out reactDecision
if err := json.Unmarshal([]byte(raw), &out); err != nil {
return reactDecision{}, err
}
return out, nil
}
func normalizeJSON(raw string) string {
raw = strings.TrimSpace(raw)
raw = strings.TrimPrefix(raw, "```json")
raw = strings.TrimPrefix(raw, "```")
raw = strings.TrimSuffix(raw, "```")
return strings.TrimSpace(raw)
}