Files
aichat/agents/time/time.go
T
2026-06-11 18:04:47 +08:00

120 lines
3.9 KiB
Go

package timeagent
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
)
const (
ToolName = "time"
ActivationPrompt = "提供当前日期、时间和常用时间范围。当用户问题包含今天、今日、明天、昨天、本周、本月、本年、最近、历史上的今天、日程安排等相对时间表达时,应先调用此工具;如果后续还需要联网搜索或查数据库,可继续调用 search 或 sql。"
)
type ToolArgs struct {
Reason string `json:"reason"`
}
type Range struct {
Start time.Time
End time.Time
}
type Context struct {
Now time.Time
Today Range
Tomorrow Range
Yesterday Range
ThisWeek Range
ThisMonth Range
ThisYear Range
}
func ToolDefinition(description string) *model.Tool {
description = strings.TrimSpace(description)
if description == "" {
description = ActivationPrompt
}
return &model.Tool{
Type: model.ToolTypeFunction,
Function: &model.FunctionDefinition{
Name: ToolName,
Description: description,
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"reason": map[string]any{
"type": "string",
"description": "调用时间工具的原因。",
},
},
},
},
}
}
func ExecuteTool(args string, now time.Time) (string, error) {
var parsed ToolArgs
if strings.TrimSpace(args) != "" {
if err := json.Unmarshal([]byte(args), &parsed); err != nil {
return "", fmt.Errorf("解析时间工具参数失败: %w", err)
}
}
return BuildContext(Resolve(now), parsed.Reason), nil
}
func Resolve(now time.Time) Context {
loc := now.Location()
today := Range{Start: startOfDay(now), End: startOfDay(now).AddDate(0, 0, 1)}
tomorrowStart := today.End
tomorrow := Range{Start: tomorrowStart, End: tomorrowStart.AddDate(0, 0, 1)}
yesterdayEnd := today.Start
yesterday := Range{Start: yesterdayEnd.AddDate(0, 0, -1), End: yesterdayEnd}
weekdayOffset := (int(now.Weekday()) + 6) % 7
weekStart := today.Start.AddDate(0, 0, -weekdayOffset)
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, loc)
yearStart := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, loc)
return Context{
Now: now,
Today: today,
Tomorrow: tomorrow,
Yesterday: yesterday,
ThisWeek: Range{Start: weekStart, End: weekStart.AddDate(0, 0, 7)},
ThisMonth: Range{Start: monthStart, End: monthStart.AddDate(0, 1, 0)},
ThisYear: Range{Start: yearStart, End: yearStart.AddDate(1, 0, 0)},
}
}
func BuildContext(ctx Context, routeReason string) string {
var b strings.Builder
b.WriteString("时间工具结果。请优先使用这里的绝对日期解释用户问题中的相对时间,不要自行猜测当前日期。\n")
fmt.Fprintf(&b, "当前本地日期时间:%s\n", ctx.Now.Format("2006-01-02 15:04:05 MST"))
fmt.Fprintf(&b, "今天:%s\n", FormatSQLRange(ctx.Today))
fmt.Fprintf(&b, "明天:%s\n", FormatSQLRange(ctx.Tomorrow))
fmt.Fprintf(&b, "昨天:%s\n", FormatSQLRange(ctx.Yesterday))
fmt.Fprintf(&b, "本周:%s\n", FormatSQLRange(ctx.ThisWeek))
fmt.Fprintf(&b, "本月:%s\n", FormatSQLRange(ctx.ThisMonth))
fmt.Fprintf(&b, "本年:%s\n", FormatSQLRange(ctx.ThisYear))
b.WriteString("SQL 日期过滤建议:对日程/事件类表使用半开区间,例如 event_time >= start AND event_time < end;如果实际字段名不同,必须使用 schema 中存在的时间字段。\n")
if strings.TrimSpace(routeReason) != "" {
b.WriteString("激活原因:" + strings.TrimSpace(routeReason) + "\n")
}
return b.String()
}
func FormatDate(t time.Time) string {
return t.Format("2006-01-02")
}
func FormatSQLRange(r Range) string {
return fmt.Sprintf("start=%s, end_exclusive=%s", r.Start.Format("2006-01-02 15:04:05"), r.End.Format("2006-01-02 15:04:05"))
}
func startOfDay(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, 0, 0, 0, 0, t.Location())
}