完善日程的ai接口
This commit is contained in:
@@ -33,6 +33,14 @@ func FunctionToolSchemas(configs []ToolConfig) []FunctionToolSchema {
|
|||||||
tools = append(tools, timeFunctionToolSchema())
|
tools = append(tools, timeFunctionToolSchema())
|
||||||
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
||||||
tools = append(tools, opsAIAssistantScheduleQuerySchema())
|
tools = append(tools, opsAIAssistantScheduleQuerySchema())
|
||||||
|
case "ops_ai_assistant_calendar_list":
|
||||||
|
tools = append(tools, opsAIAssistantCalendarListSchema())
|
||||||
|
case "ops_ai_assistant_schedule_create":
|
||||||
|
tools = append(tools, opsAIAssistantScheduleCreateSchema())
|
||||||
|
case "ops_ai_assistant_schedule_update":
|
||||||
|
tools = append(tools, opsAIAssistantScheduleUpdateSchema())
|
||||||
|
case "ops_ai_assistant_schedule_delete":
|
||||||
|
tools = append(tools, opsAIAssistantScheduleDeleteSchema())
|
||||||
case "ops_ai_assistant_current_user":
|
case "ops_ai_assistant_current_user":
|
||||||
tools = append(tools, opsAIAssistantCurrentUserSchema())
|
tools = append(tools, opsAIAssistantCurrentUserSchema())
|
||||||
case "ops_ai_assistant_purchase_query":
|
case "ops_ai_assistant_purchase_query":
|
||||||
@@ -64,6 +72,14 @@ func ExecuteFunctionTool(ctx context.Context, runtime FunctionToolRuntime, name
|
|||||||
return json.Marshal(result)
|
return json.Marshal(result)
|
||||||
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
||||||
return executeOpsAIAssistantScheduleQuery(ctx, runtime, rawArgs)
|
return executeOpsAIAssistantScheduleQuery(ctx, runtime, rawArgs)
|
||||||
|
case "ops_ai_assistant_calendar_list":
|
||||||
|
return executeOpsAIAssistantCalendarList(ctx, runtime, rawArgs)
|
||||||
|
case "ops_ai_assistant_schedule_create":
|
||||||
|
return executeOpsAIAssistantScheduleCreate(ctx, runtime, rawArgs)
|
||||||
|
case "ops_ai_assistant_schedule_update":
|
||||||
|
return executeOpsAIAssistantScheduleUpdate(ctx, runtime, rawArgs)
|
||||||
|
case "ops_ai_assistant_schedule_delete":
|
||||||
|
return executeOpsAIAssistantScheduleDelete(ctx, runtime, rawArgs)
|
||||||
case "ops_ai_assistant_current_user":
|
case "ops_ai_assistant_current_user":
|
||||||
return executeOpsAIAssistantCurrentUser(ctx, runtime, rawArgs)
|
return executeOpsAIAssistantCurrentUser(ctx, runtime, rawArgs)
|
||||||
case "ops_ai_assistant_purchase_query":
|
case "ops_ai_assistant_purchase_query":
|
||||||
|
|||||||
@@ -114,6 +114,35 @@ type ScheduleCalendar struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CalendarListOutput struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Color string `json:"color,omitempty"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
CanEdit bool `json:"canEdit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateScheduleInput struct {
|
||||||
|
CalendarID uint
|
||||||
|
Title string
|
||||||
|
StartDate string
|
||||||
|
EndDate string
|
||||||
|
ScheduleType string
|
||||||
|
IsPublic bool
|
||||||
|
Remark string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateScheduleInput struct {
|
||||||
|
EventID uint
|
||||||
|
Title string
|
||||||
|
StartDate string
|
||||||
|
EndDate string
|
||||||
|
ScheduleType string
|
||||||
|
IsPublic bool
|
||||||
|
Remark string
|
||||||
|
}
|
||||||
|
|
||||||
type ScheduleEvent struct {
|
type ScheduleEvent struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
CalendarID uint `json:"calendar_id"`
|
CalendarID uint `json:"calendar_id"`
|
||||||
@@ -138,6 +167,10 @@ type ScheduleQuery struct {
|
|||||||
|
|
||||||
type ScheduleProvider interface {
|
type ScheduleProvider interface {
|
||||||
QuerySchedules(ctx context.Context, query ScheduleQuery) ([]ScheduleEvent, error)
|
QuerySchedules(ctx context.Context, query ScheduleQuery) ([]ScheduleEvent, error)
|
||||||
|
ListCalendars(ctx context.Context, userID uint) ([]CalendarListOutput, error)
|
||||||
|
CreateScheduleEvent(ctx context.Context, userID uint, input CreateScheduleInput) (*ScheduleEvent, error)
|
||||||
|
UpdateScheduleEvent(ctx context.Context, userID uint, input UpdateScheduleInput) (*ScheduleEvent, error)
|
||||||
|
DeleteScheduleEvent(ctx context.Context, userID uint, eventID uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseProvider interface {
|
type PurchaseProvider interface {
|
||||||
@@ -388,3 +421,306 @@ func executeOpsAIAssistantScheduleQuery(ctx context.Context, runtime FunctionToo
|
|||||||
"events": events,
|
"events": events,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CalendarListArgs struct {
|
||||||
|
Action string `json:"action,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleEventCreateArgs struct {
|
||||||
|
Action string `json:"action,omitempty"`
|
||||||
|
CalendarID uint `json:"calendar_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
StartDate string `json:"start_date"`
|
||||||
|
EndDate string `json:"end_date"`
|
||||||
|
ScheduleType string `json:"schedule_type,omitempty"`
|
||||||
|
IsPublic bool `json:"is_public,omitempty"`
|
||||||
|
Remark string `json:"remark,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleEventUpdateArgs struct {
|
||||||
|
Action string `json:"action,omitempty"`
|
||||||
|
EventID uint `json:"event_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
StartDate string `json:"start_date"`
|
||||||
|
EndDate string `json:"end_date"`
|
||||||
|
ScheduleType string `json:"schedule_type,omitempty"`
|
||||||
|
IsPublic bool `json:"is_public,omitempty"`
|
||||||
|
Remark string `json:"remark,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleEventDeleteArgs struct {
|
||||||
|
Action string `json:"action,omitempty"`
|
||||||
|
EventID uint `json:"event_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantCalendarListSchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_calendar_list",
|
||||||
|
Description: "只读工具:查询当前用户可见的 OPS 日历列表。创建/修改日程前如用户未提供 calendar_id,应先调用本工具让用户选择日历。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"action": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"list"},
|
||||||
|
"description": "固定为 list。",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": []string{"action"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantScheduleCreateSchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_schedule_create",
|
||||||
|
Description: "写入工具:在指定 OPS 日历中创建日程。仅当用户明确要求新增/创建/添加日程且已提供日历 ID、标题和明确日期范围时调用;相对日期需先调用 time 获取 start_date/end_date。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": scheduleEventWriteProperties(),
|
||||||
|
"required": []string{"action", "calendar_id", "title", "start_date", "end_date"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantScheduleUpdateSchema() FunctionToolSchema {
|
||||||
|
props := scheduleEventWriteProperties()
|
||||||
|
props["event_id"] = map[string]interface{}{"type": "integer", "description": "要修改的日程事件 ID。"}
|
||||||
|
delete(props, "calendar_id")
|
||||||
|
props["action"] = map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"update"},
|
||||||
|
"description": "固定为 update。",
|
||||||
|
}
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_schedule_update",
|
||||||
|
Description: "写入工具:修改指定 OPS 日程事件。仅当用户明确要求修改/更新已有日程且已提供事件 ID、标题和明确日期范围时调用;相对日期需先调用 time 获取 start_date/end_date。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": props,
|
||||||
|
"required": []string{"action", "event_id", "title", "start_date", "end_date"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantScheduleDeleteSchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_schedule_delete",
|
||||||
|
Description: "写入工具:删除指定 OPS 日程事件。仅当用户明确要求删除/移除日程且已提供事件 ID 时调用。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"action": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"delete"},
|
||||||
|
"description": "固定为 delete。",
|
||||||
|
},
|
||||||
|
"event_id": map[string]interface{}{"type": "integer", "description": "要删除的日程事件 ID。"},
|
||||||
|
},
|
||||||
|
"required": []string{"action", "event_id"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scheduleEventWriteProperties() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"action": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"create"},
|
||||||
|
"description": "固定为 create。",
|
||||||
|
},
|
||||||
|
"calendar_id": map[string]interface{}{"type": "integer", "description": "目标日历 ID。"},
|
||||||
|
"title": map[string]interface{}{"type": "string", "description": "日程标题。"},
|
||||||
|
"start_date": map[string]interface{}{"type": "string", "description": "开始日期,格式 YYYY-MM-DD。"},
|
||||||
|
"end_date": map[string]interface{}{"type": "string", "description": "结束日期,格式 YYYY-MM-DD。"},
|
||||||
|
"schedule_type": map[string]interface{}{"type": "string", "enum": []string{"", "work", "duty", "exam", "standby", "personal_holiday", "public_holiday"}, "description": "日程类型,默认 work。"},
|
||||||
|
"is_public": map[string]interface{}{"type": "boolean", "description": "是否公开,默认 false。"},
|
||||||
|
"remark": map[string]interface{}{"type": "string", "description": "备注。"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantCalendarList(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args CalendarListArgs
|
||||||
|
if len(rawArgs) > 0 {
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.Action != "" && args.Action != "list" {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "ops_ai_assistant_calendar_list 仅允许 list 查询操作",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if runtime.UserID <= 0 {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": false,
|
||||||
|
"message": "需要登录才能查询日历列表。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if registeredScheduleProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "日历服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
calendars, err := registeredScheduleProvider.ListCalendars(ctx, runtime.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if calendars == nil {
|
||||||
|
calendars = []CalendarListOutput{}
|
||||||
|
}
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": true,
|
||||||
|
"count": len(calendars),
|
||||||
|
"calendars": calendars,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantScheduleCreate(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args ScheduleEventCreateArgs
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.Action != "" && args.Action != "create" {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "ops_ai_assistant_schedule_create 仅允许 create 操作",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := validateScheduleWrite(runtime.UserID, args.Title, args.StartDate, args.EndDate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.CalendarID <= 0 {
|
||||||
|
return nil, fmt.Errorf("calendar_id is required")
|
||||||
|
}
|
||||||
|
if registeredScheduleProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "日历服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
event, err := registeredScheduleProvider.CreateScheduleEvent(ctx, runtime.UserID, CreateScheduleInput{
|
||||||
|
CalendarID: args.CalendarID,
|
||||||
|
Title: args.Title,
|
||||||
|
StartDate: args.StartDate,
|
||||||
|
EndDate: args.EndDate,
|
||||||
|
ScheduleType: args.ScheduleType,
|
||||||
|
IsPublic: args.IsPublic,
|
||||||
|
Remark: args.Remark,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": true,
|
||||||
|
"event": event,
|
||||||
|
"message": "日程已创建。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantScheduleUpdate(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args ScheduleEventUpdateArgs
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.Action != "" && args.Action != "update" {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "ops_ai_assistant_schedule_update 仅允许 update 操作",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if args.EventID <= 0 {
|
||||||
|
return nil, fmt.Errorf("event_id is required")
|
||||||
|
}
|
||||||
|
if err := validateScheduleWrite(runtime.UserID, args.Title, args.StartDate, args.EndDate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if registeredScheduleProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "日历服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
event, err := registeredScheduleProvider.UpdateScheduleEvent(ctx, runtime.UserID, UpdateScheduleInput{
|
||||||
|
EventID: args.EventID,
|
||||||
|
Title: args.Title,
|
||||||
|
StartDate: args.StartDate,
|
||||||
|
EndDate: args.EndDate,
|
||||||
|
ScheduleType: args.ScheduleType,
|
||||||
|
IsPublic: args.IsPublic,
|
||||||
|
Remark: args.Remark,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": true,
|
||||||
|
"event": event,
|
||||||
|
"message": "日程已更新。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantScheduleDelete(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args ScheduleEventDeleteArgs
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.Action != "" && args.Action != "delete" {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "ops_ai_assistant_schedule_delete 仅允许 delete 操作",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if runtime.UserID <= 0 {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": false,
|
||||||
|
"message": "需要登录才能删除日程。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if args.EventID <= 0 {
|
||||||
|
return nil, fmt.Errorf("event_id is required")
|
||||||
|
}
|
||||||
|
if registeredScheduleProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "日历服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := registeredScheduleProvider.DeleteScheduleEvent(ctx, runtime.UserID, args.EventID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"loggedIn": true,
|
||||||
|
"event_id": args.EventID,
|
||||||
|
"message": "日程已删除。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateScheduleWrite(userID uint, title string, startDate string, endDate string) error {
|
||||||
|
if userID <= 0 {
|
||||||
|
return fmt.Errorf("需要登录才能操作日程")
|
||||||
|
}
|
||||||
|
if title == "" {
|
||||||
|
return fmt.Errorf("title is required")
|
||||||
|
}
|
||||||
|
start, err := time.Parse("2006-01-02", startDate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid start_date: %w", err)
|
||||||
|
}
|
||||||
|
end, err := time.Parse("2006-01-02", endDate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid end_date: %w", err)
|
||||||
|
}
|
||||||
|
if end.Before(start) {
|
||||||
|
return fmt.Errorf("end_date must be after start_date")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,7 +108,11 @@ func ConfigAllInit() error {
|
|||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
||||||
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
||||||
|
{Name: "ops_ai_assistant_calendar_list", Enabled: true, Description: "查询当前用户可见的 OPS 日历列表。"},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_create", Enabled: true, Description: "创建 OPS 日历日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_update", Enabled: true, Description: "修改 OPS 日历日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_delete", Enabled: true, Description: "删除 OPS 日历日程。"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func handleChat(ctx *gin.Context) {
|
|||||||
toolNames = append(toolNames, tool.Function.Name)
|
toolNames = append(toolNames, tool.Function.Name)
|
||||||
}
|
}
|
||||||
emitTrace("function_tools", "prepare", "success", "已启用 Function Calling 工具", map[string]interface{}{"tools": toolNames})
|
emitTrace("function_tools", "prepare", "success", "已启用 Function Calling 工具", map[string]interface{}{"tools": toolNames})
|
||||||
openaiMsgs = append([]openaiMessage{{Role: "system", Content: "可用工具使用规则:当用户询问“我是谁”“当前登录用户是谁”“我的用户信息”等当前身份问题时,调用 ops_ai_assistant_current_user;工具返回 loggedIn=true 时按工具结果回答当前用户信息,返回 loggedIn=false 时说明不知道并提示需要登录才能获取信息。当用户询问采购订单列表、采购订单详情、采购状态或数量统计时,调用 ops_ai_assistant_purchase_query;该工具只允许查询,禁止新增、修改、删除采购数据。当用户询问本月、今天、本周、下周等相对日期的日程时,先调用 time 获取明确 start_date/end_date,再调用 ops_ai_assistant_schedule_query 查询日程。不要臆造工具结果中不存在的信息。"}}, openaiMsgs...)
|
openaiMsgs = append([]openaiMessage{{Role: "system", Content: "可用工具使用规则:当用户询问“我是谁”“当前登录用户是谁”“我的用户信息”等当前身份问题时,调用 ops_ai_assistant_current_user;工具返回 loggedIn=true 时按工具结果回答当前用户信息,返回 loggedIn=false 时说明不知道并提示需要登录才能获取信息。当用户询问采购订单列表、采购订单详情、采购状态或数量统计时,调用 ops_ai_assistant_purchase_query;该工具只允许查询,禁止新增、修改、删除采购数据。当用户询问本月、今天、本周、下周等相对日期的日程时,先调用 time 获取明确 start_date/end_date,再调用 ops_ai_assistant_schedule_query 查询日程。用户询问有哪些日历或创建日程但未提供 calendar_id 时,调用 ops_ai_assistant_calendar_list 获取可用日历;用户明确要求新增/修改/删除日程时,分别调用 ops_ai_assistant_schedule_create、ops_ai_assistant_schedule_update、ops_ai_assistant_schedule_delete,写入工具必须基于用户明确指令和明确日期,不要自行猜测日历或事件 ID。不要臆造工具结果中不存在的信息。"}}, openaiMsgs...)
|
||||||
var toolExecuted bool
|
var toolExecuted bool
|
||||||
openaiMsgs, toolExecuted, err = runOpenAIToolLoop(ctx.Request.Context(), profile, openaiMsgs, functionTools, currentUser, tracker, emitTrace)
|
openaiMsgs, toolExecuted, err = runOpenAIToolLoop(ctx.Request.Context(), profile, openaiMsgs, functionTools, currentUser, tracker, emitTrace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -111,7 +111,11 @@ func ensureBuiltinAIChatTools() error {
|
|||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。", SortOrder: 0},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。", SortOrder: 0},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。", SortOrder: 5},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。", SortOrder: 5},
|
||||||
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。", SortOrder: 8},
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。", SortOrder: 8},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。", SortOrder: 10},
|
{Name: "ops_ai_assistant_calendar_list", Enabled: true, Description: "查询当前用户可见的 OPS 日历列表。", SortOrder: 10},
|
||||||
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。", SortOrder: 12},
|
||||||
|
{Name: "ops_ai_assistant_schedule_create", Enabled: true, Description: "创建 OPS 日历日程。", SortOrder: 14},
|
||||||
|
{Name: "ops_ai_assistant_schedule_update", Enabled: true, Description: "修改 OPS 日历日程。", SortOrder: 16},
|
||||||
|
{Name: "ops_ai_assistant_schedule_delete", Enabled: true, Description: "删除 OPS 日历日程。", SortOrder: 18},
|
||||||
}
|
}
|
||||||
for _, builtin := range builtins {
|
for _, builtin := range builtins {
|
||||||
var existing TabAIChatTool
|
var existing TabAIChatTool
|
||||||
@@ -208,7 +212,11 @@ func seedAIChatConfigFromYAMLIfEmpty() error {
|
|||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
||||||
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
||||||
|
{Name: "ops_ai_assistant_calendar_list", Enabled: true, Description: "查询当前用户可见的 OPS 日历列表。"},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_create", Enabled: true, Description: "创建 OPS 日历日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_update", Enabled: true, Description: "修改 OPS 日历日程。"},
|
||||||
|
{Name: "ops_ai_assistant_schedule_delete", Enabled: true, Description: "删除 OPS 日历日程。"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, tool := range tools {
|
for i, tool := range tools {
|
||||||
|
|||||||
@@ -179,23 +179,208 @@ func (calendarScheduleProvider) QuerySchedules(ctx context.Context, query agents
|
|||||||
|
|
||||||
result := make([]agents.ScheduleEvent, 0, len(events))
|
result := make([]agents.ScheduleEvent, 0, len(events))
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
result = append(result, agents.ScheduleEvent{
|
result = append(result, calendarScheduleEventToAgent(event))
|
||||||
ID: event.ID,
|
}
|
||||||
CalendarID: event.CalendarID,
|
return result, nil
|
||||||
UserID: event.UserID,
|
}
|
||||||
Title: event.Title,
|
|
||||||
StartDate: event.StartDate,
|
func (calendarScheduleProvider) ListCalendars(ctx context.Context, userID uint) ([]agents.CalendarListOutput, error) {
|
||||||
EndDate: event.EndDate,
|
select {
|
||||||
ScheduleType: event.ScheduleType,
|
case <-ctx.Done():
|
||||||
IsPublic: event.IsPublic,
|
return nil, ctx.Err()
|
||||||
Remark: event.Remark,
|
default:
|
||||||
AccessNote: event.AccessNote,
|
}
|
||||||
CanEdit: event.CanEdit,
|
|
||||||
|
var calendars []TabCalendar
|
||||||
|
if err := models.DB.Where("deleted_at IS NULL AND (is_public = ? OR user_id = ?)", true, userID).Order("created_at DESC").Find(&calendars).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]agents.CalendarListOutput, 0, len(calendars))
|
||||||
|
for _, cal := range calendars {
|
||||||
|
result = append(result, agents.CalendarListOutput{
|
||||||
|
ID: cal.ID,
|
||||||
|
Name: cal.Name,
|
||||||
|
Description: cal.Description,
|
||||||
|
Color: cal.Color,
|
||||||
|
IsPublic: cal.IsPublic,
|
||||||
|
CanEdit: canModifyCalendar(userID, cal.UserID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (calendarScheduleProvider) CreateScheduleEvent(ctx context.Context, userID uint, input agents.CreateScheduleInput) (*agents.ScheduleEvent, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar := TabCalendar{}
|
||||||
|
if err := models.DB.Where("id = ? AND deleted_at IS NULL", input.CalendarID).First(&calendar).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !calendar.IsPublic && !canModifyCalendar(userID, calendar.UserID) {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
startDate, err := time.Parse("2006-01-02", input.StartDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
endDate, err := time.Parse("2006-01-02", input.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if input.ScheduleType == "" {
|
||||||
|
input.ScheduleType = "work"
|
||||||
|
}
|
||||||
|
|
||||||
|
event := TabCalendarEvent{
|
||||||
|
CalendarID: input.CalendarID,
|
||||||
|
UserID: userID,
|
||||||
|
Title: input.Title,
|
||||||
|
StartDate: &startDate,
|
||||||
|
EndDate: &endDate,
|
||||||
|
ScheduleType: input.ScheduleType,
|
||||||
|
IsPublic: input.IsPublic,
|
||||||
|
Remark: input.Remark,
|
||||||
|
}
|
||||||
|
if err := models.DB.Create(&event).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newContent, _ := json.Marshal(event)
|
||||||
|
models.DB.Create(&TabCalendarLog{CalendarID: event.CalendarID, EventID: event.ID, UserID: userID, ActionType: "create_event", NewContent: string(newContent), Remark: "AI 助手创建日程"})
|
||||||
|
|
||||||
|
return calendarEventToAgent(event, userID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (calendarScheduleProvider) UpdateScheduleEvent(ctx context.Context, userID uint, input agents.UpdateScheduleInput) (*agents.ScheduleEvent, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
oldEvent := TabCalendarEvent{}
|
||||||
|
if err := models.DB.Where("id = ?", input.EventID).First(&oldEvent).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !canModifyCalendarEvent(userID, oldEvent) {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
startDate, err := time.Parse("2006-01-02", input.StartDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
endDate, err := time.Parse("2006-01-02", input.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if input.ScheduleType == "" {
|
||||||
|
input.ScheduleType = "work"
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := map[string]interface{}{
|
||||||
|
"title": input.Title,
|
||||||
|
"start_date": &startDate,
|
||||||
|
"end_date": &endDate,
|
||||||
|
"schedule_type": input.ScheduleType,
|
||||||
|
"is_public": input.IsPublic,
|
||||||
|
"remark": input.Remark,
|
||||||
|
}
|
||||||
|
if err := models.DB.Model(&oldEvent).Updates(updates).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldContent, _ := json.Marshal(oldEvent)
|
||||||
|
newContent, _ := json.Marshal(updates)
|
||||||
|
models.DB.Create(&TabCalendarLog{CalendarID: oldEvent.CalendarID, EventID: oldEvent.ID, UserID: userID, ActionType: "update_event", OldContent: string(oldContent), NewContent: string(newContent), Remark: "AI 助手更新日程"})
|
||||||
|
|
||||||
|
var event TabCalendarEvent
|
||||||
|
if err := models.DB.Where("id = ?", input.EventID).First(&event).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return calendarEventToAgent(event, userID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (calendarScheduleProvider) DeleteScheduleEvent(ctx context.Context, userID uint, eventID uint) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
event := TabCalendarEvent{}
|
||||||
|
if err := models.DB.Where("id = ?", eventID).First(&event).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !canModifyCalendarEvent(userID, event) {
|
||||||
|
return gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
if err := models.DB.Delete(&event).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldContent, _ := json.Marshal(event)
|
||||||
|
models.DB.Create(&TabCalendarLog{CalendarID: event.CalendarID, EventID: event.ID, UserID: userID, ActionType: "delete_event", OldContent: string(oldContent), Remark: "AI 助手删除日程"})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func calendarScheduleEventToAgent(event CalendarScheduleEvent) agents.ScheduleEvent {
|
||||||
|
return agents.ScheduleEvent{
|
||||||
|
ID: event.ID,
|
||||||
|
CalendarID: event.CalendarID,
|
||||||
|
UserID: event.UserID,
|
||||||
|
Title: event.Title,
|
||||||
|
StartDate: event.StartDate,
|
||||||
|
EndDate: event.EndDate,
|
||||||
|
ScheduleType: event.ScheduleType,
|
||||||
|
IsPublic: event.IsPublic,
|
||||||
|
Remark: event.Remark,
|
||||||
|
AccessNote: event.AccessNote,
|
||||||
|
CanEdit: event.CanEdit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calendarEventToAgent(event TabCalendarEvent, userID uint) *agents.ScheduleEvent {
|
||||||
|
accessNote := ""
|
||||||
|
canEdit := false
|
||||||
|
if userID > 0 {
|
||||||
|
var calendar TabCalendar
|
||||||
|
if models.DB.Where("id = ?", event.CalendarID).First(&calendar).Error == nil {
|
||||||
|
canEdit = canModifyCalendar(userID, event.UserID) || calendar.UserID == userID
|
||||||
|
}
|
||||||
|
if !event.IsPublic {
|
||||||
|
accessNote = "非公开日程,仅因当前用户具备相关权限可见"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &agents.ScheduleEvent{
|
||||||
|
ID: event.ID,
|
||||||
|
CalendarID: event.CalendarID,
|
||||||
|
UserID: event.UserID,
|
||||||
|
Title: event.Title,
|
||||||
|
StartDate: formatDatePtr(event.StartDate),
|
||||||
|
EndDate: formatDatePtr(event.EndDate),
|
||||||
|
ScheduleType: event.ScheduleType,
|
||||||
|
IsPublic: event.IsPublic,
|
||||||
|
Remark: event.Remark,
|
||||||
|
AccessNote: accessNote,
|
||||||
|
CanEdit: canEdit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func canModifyCalendarEvent(userID uint, event TabCalendarEvent) bool {
|
||||||
|
if userID == event.UserID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
var calendar TabCalendar
|
||||||
|
if models.DB.Where("id = ?", event.CalendarID).First(&calendar).Error == nil {
|
||||||
|
return canModifyCalendar(userID, calendar.UserID)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func QueryCalendarSchedulesForAI(query CalendarScheduleQuery) ([]CalendarScheduleEvent, error) {
|
func QueryCalendarSchedulesForAI(query CalendarScheduleQuery) ([]CalendarScheduleEvent, error) {
|
||||||
startDate := dateOnly(query.StartDate)
|
startDate := dateOnly(query.StartDate)
|
||||||
endDate := dateOnly(query.EndDate)
|
endDate := dateOnly(query.EndDate)
|
||||||
|
|||||||
Reference in New Issue
Block a user