模型优化
This commit is contained in:
+18
-1
@@ -19,7 +19,8 @@ import (
|
||||
|
||||
const (
|
||||
defaultActivationPrompt = `判断用户问题是否需要联网搜索。
|
||||
仅当问题涉及实时信息、新闻、价格、当前版本、近期事件、政策、网页资料核验,或用户明确要求“查一下/搜索/联网/最新”时调用 search。
|
||||
当问题涉及实时信息、新闻、价格、当前版本、近期事件、政策、网页资料核验,或用户明确要求“查一下/搜索/联网/最新”时调用 search。
|
||||
当用户询问“历史上的今天”、某日期历史事件、需要按当前日期动态确定查询词的常识资料时,也应调用 search;如果联网无结果,主模型会回退到自身知识库回答并说明来源。
|
||||
普通知识、闲聊、代码推理、已有上下文可回答的问题不要调用。`
|
||||
defaultBaseURL = "https://api.duckduckgo.com/"
|
||||
defaultTimeout = 10
|
||||
@@ -287,6 +288,22 @@ func BuildErrorContext(query string, err error) string {
|
||||
return fmt.Sprintf("工具路由尝试联网搜索但失败。用户问题:%s\n错误:%v\n请向用户说明联网搜索失败,不要编造搜索结果。", query, err)
|
||||
}
|
||||
|
||||
func BuildFallbackContext(config ProfileConfig, query string, routeReason string, err error) string {
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "工具路由尝试联网搜索,但没有可用的搜索结果。当前搜索源: %s(%s)。\n", config.Name, config.Provider)
|
||||
fmt.Fprintf(&b, "搜索时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(&b, "搜索词: %s\n", query)
|
||||
if strings.TrimSpace(routeReason) != "" {
|
||||
fmt.Fprintf(&b, "调用原因: %s\n", strings.TrimSpace(routeReason))
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(&b, "搜索结果状态: %v\n", err)
|
||||
}
|
||||
fmt.Fprintln(&b, "请改用模型自身知识库回答用户问题,并在回答开头或结尾明确说明:本次联网搜索未获得可用结果,以下内容来自模型训练数据/内置知识,可能不是最新或完整信息。")
|
||||
fmt.Fprintln(&b, "不要伪造网页链接或声称已由搜索结果证实;涉及时效性、争议性或不确定细节时要提示用户核验。")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
return Config{
|
||||
Enabled: true,
|
||||
|
||||
@@ -2,6 +2,7 @@ package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -66,6 +67,15 @@ func TestBuildResultContext(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFallbackContext(t *testing.T) {
|
||||
text := BuildFallbackContext(ProfileConfig{Name: "duckduckgo", Provider: "duckduckgo"}, "历史上的今天都发生了什么?", "需要查询当天历史事件", errors.New("未搜索到相关网页结果"))
|
||||
for _, want := range []string{"没有可用的搜索结果", "历史上的今天", "需要查询当天历史事件", "模型训练数据/内置知识", "不要伪造网页链接"} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("fallback context missing %q:\n%s", want, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuckDuckGoSearchParsesResults(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("q") != "golang" {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const ActivationPrompt = "提供当前日期、时间和常用时间范围。当用户问题包含今天、明天、昨天、本周、本月、本年、最近、日程安排等相对时间表达时,应先调用此工具;如果后续还需要查数据库,可继续调用 sql。"
|
||||
const ActivationPrompt = "提供当前日期、时间和常用时间范围。当用户问题包含今天、今日、明天、昨天、本周、本月、本年、最近、历史上的今天、日程安排等相对时间表达时,应先调用此工具;如果后续还需要联网搜索或查数据库,可继续调用 search 或 sql。"
|
||||
|
||||
type Range struct {
|
||||
Start time.Time
|
||||
|
||||
@@ -42,7 +42,8 @@ const (
|
||||
JSON 格式:{"tools":[{"name":"工具名称","reason":"..."}],"reason":"..."}
|
||||
工具名称必须来自“可用工具”列表。
|
||||
可以选择多个工具,工具会按配置顺序依次执行;后面的工具可以使用前面工具写入的上下文。
|
||||
如果用户问题包含今天、明天、昨天、本周、本月、本年、最近等相对时间,并且还需要查询数据库,请同时选择 time 和 sql。
|
||||
如果用户问题包含今天、今日、明天、昨天、本周、本月、本年、最近等相对时间,且还需要调用 search 或 sql,必须同时选择 time,并让 time 排在这些工具之前。
|
||||
例如“历史上的今天都发生了什么”应选择 time 和 search:先获取今天的绝对日期,再搜索当天历史事件;如果联网无结果,主模型会回退到自身知识库回答并说明来源。
|
||||
例如“本月有什么日程安排”应选择 time 和 sql:先获取本月绝对日期范围,再查询日程表。
|
||||
如果无需工具,返回 {"tools":[],"reason":"..."}。
|
||||
只选择确实必要的工具。`
|
||||
@@ -1104,8 +1105,8 @@ func runSearchTool(ctx context.Context, state *searchagent.State, messages []Cha
|
||||
}
|
||||
if len(results) == 0 {
|
||||
err := errors.New("未搜索到相关网页结果")
|
||||
emit(chatSSEFrame{Type: "trace", Tool: "search", Stage: "results", Status: "error", Message: err.Error()})
|
||||
return prependHiddenContext(messages, searchagent.BuildErrorContext(query, err)), nil
|
||||
emit(chatSSEFrame{Type: "trace", Tool: "search", Stage: "results", Status: "warning", Message: "未搜索到相关网页结果,将使用模型知识库回答"})
|
||||
return prependHiddenContext(messages, searchagent.BuildFallbackContext(profile, query, routeReason, err)), nil
|
||||
}
|
||||
emit(chatSSEFrame{Type: "trace", Tool: "search", Stage: "results", Status: "success", Message: fmt.Sprintf("联网搜索完成,找到 %d 条结果", len(results)), Data: map[string]any{"provider": profile.Provider, "count": len(results)}})
|
||||
return prependHiddenContext(messages, searchagent.BuildResultContext(profile, query, results, routeReason)), nil
|
||||
@@ -1130,6 +1131,7 @@ func enrichMessagesWithRoutedTools(ctx context.Context, chatProfile *OpenAIProfi
|
||||
return messages, err
|
||||
}
|
||||
selected := filterToolSelections(decision, tools, toolRouterState.cfg.Tools)
|
||||
selected = ensureTimeSelectionForRelativeQuery(selected, tools, toolRouterState.cfg.Tools, latestUserQuery(messages))
|
||||
if len(selected) == 0 {
|
||||
emit(chatSSEFrame{Type: "trace", Tool: "tool_router", Stage: "route", Status: "success", Message: "工具路由结果:无需调用工具", Data: map[string]any{"reason": decision.Reason}})
|
||||
return messages, nil
|
||||
@@ -1239,6 +1241,47 @@ func filterToolSelections(decision ToolRoutingDecision, tools map[string]ChatToo
|
||||
selected[item.Name] = item
|
||||
}
|
||||
}
|
||||
return orderToolSelections(selected, order)
|
||||
}
|
||||
|
||||
func ensureTimeSelectionForRelativeQuery(selected []ToolSelection, tools map[string]ChatTool, order []ToolRouteConfig, query string) []ToolSelection {
|
||||
if !containsRelativeTime(query) || hasToolSelection(selected, "time") || (!hasToolSelection(selected, "search") && !hasToolSelection(selected, "sql")) {
|
||||
return selected
|
||||
}
|
||||
if _, ok := tools["time"]; !ok {
|
||||
return selected
|
||||
}
|
||||
withTime := make(map[string]ToolSelection, len(selected)+1)
|
||||
for _, item := range selected {
|
||||
withTime[item.Name] = item
|
||||
}
|
||||
withTime["time"] = ToolSelection{Name: "time", Reason: "问题包含相对日期,需要先获取当前日期"}
|
||||
return orderToolSelections(withTime, order)
|
||||
}
|
||||
|
||||
func containsRelativeTime(query string) bool {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
return false
|
||||
}
|
||||
for _, keyword := range []string{"今天", "今日", "明天", "昨天", "本周", "这周", "本月", "这个月", "本年", "今年", "最近", "历史上的今天"} {
|
||||
if strings.Contains(query, keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasToolSelection(selected []ToolSelection, name string) bool {
|
||||
for _, item := range selected {
|
||||
if item.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func orderToolSelections(selected map[string]ToolSelection, order []ToolRouteConfig) []ToolSelection {
|
||||
result := make([]ToolSelection, 0, len(selected))
|
||||
for _, item := range order {
|
||||
if selection, ok := selected[item.Name]; ok {
|
||||
|
||||
@@ -129,6 +129,41 @@ func TestFilterToolSelections(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureTimeSelectionForRelativeSearch(t *testing.T) {
|
||||
tools := map[string]ChatTool{
|
||||
"time": fakeChatTool{name: "time", enabled: true},
|
||||
"search": fakeChatTool{name: "search", enabled: true},
|
||||
}
|
||||
selected := ensureTimeSelectionForRelativeQuery(
|
||||
[]ToolSelection{{Name: "search", Reason: "查询历史事件"}},
|
||||
tools,
|
||||
[]ToolRouteConfig{{Name: "time"}, {Name: "search"}, {Name: "sql"}},
|
||||
"历史上的今天都发生了什么?",
|
||||
)
|
||||
if len(selected) != 2 || selected[0].Name != "time" || selected[1].Name != "search" {
|
||||
t.Fatalf("unexpected selected tools: %#v", selected)
|
||||
}
|
||||
if !strings.Contains(selected[0].Reason, "相对日期") {
|
||||
t.Fatalf("unexpected time reason: %#v", selected[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureTimeSelectionSkipsOrdinarySearch(t *testing.T) {
|
||||
tools := map[string]ChatTool{
|
||||
"time": fakeChatTool{name: "time", enabled: true},
|
||||
"search": fakeChatTool{name: "search", enabled: true},
|
||||
}
|
||||
selected := ensureTimeSelectionForRelativeQuery(
|
||||
[]ToolSelection{{Name: "search", Reason: "查询资料"}},
|
||||
tools,
|
||||
[]ToolRouteConfig{{Name: "time"}, {Name: "search"}},
|
||||
"查一下 Go 语言官网",
|
||||
)
|
||||
if len(selected) != 1 || selected[0].Name != "search" {
|
||||
t.Fatalf("unexpected selected tools: %#v", selected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTimeToolAddsHiddenDateRanges(t *testing.T) {
|
||||
messages := []ChatMessage{{Role: "user", Content: "本月有什么日程安排"}}
|
||||
withTime, err := runTimeTool(context.Background(), messages, "需要日期范围", func(chatSSEFrame) {})
|
||||
|
||||
Reference in New Issue
Block a user