Version: 0.9.61.dev.260501

后端:
1. 主动调度 graph + session bridge 收口——把 dry-run / select / preview / confirm / rerun 串成受限 graph,新增 active_schedule_sessions 缓存与聊天拦截,ready_preview 后释放回自由聊天
2. 会话与通知链路对齐——notification 统一绑定 conversation_id,action_url 指向 /assistant/{conversation_id},会话不存在改回 404 语义,避免 wrong param type 误导排障
3. estimated_sections 写入与主动调度消费链路补齐——任务创建、quick task 与随口记入口都透传估计节数,主动调度只消费落库值

前端:
4. AssistantPanel 最小适配主动调度预览与失败态——复用主动调度卡片/微调弹窗,补历史加载失败可见提示与跨账号会话拦截

文档:
5. 更新主动调度缺口分阶段实施计划和实现方案,标记阶段 0-2 收口并同步接力状态
This commit is contained in:
Losita
2026-05-01 20:48:32 +08:00
parent 0a014f7472
commit a3eaa9b2c2
42 changed files with 4377 additions and 357 deletions

View File

@@ -108,7 +108,7 @@ type AgentGraphDeps struct {
// 2. 这里只保留“创建任务 / 查询任务”两类轻量能力,避免再回退到已下线的孤立工具链。
type QuickTaskDeps struct {
// CreateTask 创建一条四象限任务,返回 task_id。
CreateTask func(userID int, title string, priorityGroup int, deadlineAt *time.Time, urgencyThresholdAt *time.Time) (taskID int, err error)
CreateTask func(userID int, title string, priorityGroup int, estimatedSections int, deadlineAt *time.Time, urgencyThresholdAt *time.Time) (taskID int, err error)
// QueryTasks 按条件查询用户任务列表。
QueryTasks func(ctx context.Context, userID int, params TaskQueryParams) ([]TaskQueryResult, error)
}

View File

@@ -26,10 +26,11 @@ type TaskQueryParams struct {
// 2. 结果既可用于 quick_task 节点文本回复,也可供 service 装配其他轻量输出;
// 3. 不负责序列化策略和文案渲染。
type TaskQueryResult struct {
ID int `json:"id"`
Title string `json:"title"`
PriorityGroup int `json:"priority_group"`
PriorityLabel string `json:"priority_label"`
IsCompleted bool `json:"is_completed"`
DeadlineAt string `json:"deadline_at,omitempty"`
ID int `json:"id"`
Title string `json:"title"`
PriorityGroup int `json:"priority_group"`
EstimatedSections int `json:"estimated_sections"`
PriorityLabel string `json:"priority_label"`
IsCompleted bool `json:"is_completed"`
DeadlineAt string `json:"deadline_at,omitempty"`
}

View File

@@ -20,6 +20,7 @@ type TaskQueryTaskRecord struct {
ID int
Title string
PriorityGroup int
EstimatedSections int
IsCompleted bool
DeadlineAt *time.Time
UrgencyThresholdAt *time.Time

View File

@@ -9,6 +9,7 @@ import (
"time"
infrallm "github.com/LoveLosita/smartflow/backend/infra/llm"
taskmodel "github.com/LoveLosita/smartflow/backend/model"
newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
newagentprompt "github.com/LoveLosita/smartflow/backend/newAgent/prompt"
newagentrouter "github.com/LoveLosita/smartflow/backend/newAgent/router"
@@ -41,6 +42,7 @@ type quickTaskDecision struct {
Title string `json:"title,omitempty"`
DeadlineAt string `json:"deadline_at,omitempty"`
PriorityGroup *int `json:"priority_group,omitempty"`
EstimatedSections *int `json:"estimated_sections,omitempty"`
UrgencyThresholdAt string `json:"urgency_threshold_at,omitempty"`
TaskID *int `json:"task_id,omitempty"`
@@ -137,8 +139,8 @@ func RunQuickTaskNode(ctx context.Context, input QuickTaskNodeInput) error {
}
break
}
log.Printf("[DEBUG] quick_task: 解析结果 chat=%s action=%s title=%s deadline_at=%s priority_group=%v urgency_threshold_at=%q",
flowState.ConversationID, decision.Action, decision.Title, decision.DeadlineAt, decision.PriorityGroup, decision.UrgencyThresholdAt)
log.Printf("[DEBUG] quick_task: 解析结果 chat=%s action=%s title=%s deadline_at=%s priority_group=%v estimated_sections=%v urgency_threshold_at=%q",
flowState.ConversationID, decision.Action, decision.Title, decision.DeadlineAt, decision.PriorityGroup, decision.EstimatedSections, decision.UrgencyThresholdAt)
// 阶段二:流式推送标签后正文。
if visible != "" {
@@ -266,6 +268,7 @@ func handleQuickTaskCreate(
if priorityGroup == 0 {
priorityGroup = quickNoteFallbackPriority(deadline)
}
estimatedSections := taskmodel.NormalizeEstimatedSections(decision.EstimatedSections)
var urgencyThreshold *time.Time
if raw := strings.TrimSpace(decision.UrgencyThresholdAt); raw != "" {
@@ -280,9 +283,9 @@ func handleQuickTaskCreate(
urgencyThreshold = &fallback
}
log.Printf("[DEBUG] quick_task: CreateTask 参数 chat=%s title=%s priorityGroup=%d deadline=%v urgencyThreshold=%v urgency_raw=%q",
flowState.ConversationID, title, priorityGroup, deadline, urgencyThreshold, decision.UrgencyThresholdAt)
taskID, err := input.QuickTaskDeps.CreateTask(flowState.UserID, title, priorityGroup, deadline, urgencyThreshold)
log.Printf("[DEBUG] quick_task: CreateTask 参数 chat=%s title=%s priorityGroup=%d estimatedSections=%d deadline=%v urgencyThreshold=%v urgency_raw=%q estimated_raw=%v",
flowState.ConversationID, title, priorityGroup, estimatedSections, deadline, urgencyThreshold, decision.UrgencyThresholdAt, decision.EstimatedSections)
taskID, err := input.QuickTaskDeps.CreateTask(flowState.UserID, title, priorityGroup, estimatedSections, deadline, urgencyThreshold)
if err != nil {
return quickTaskActionResult{AssistantText: fmt.Sprintf("记录失败了(%s稍后再试试", err)}
}
@@ -290,7 +293,7 @@ func handleQuickTaskCreate(
flowState.UsedQuickNote = true
return quickTaskActionResult{
AssistantText: "已帮你记下这条任务。",
BusinessCard: buildTaskRecordBusinessCard(taskID, title, priorityGroup, deadline, urgencyThreshold),
BusinessCard: buildTaskRecordBusinessCard(taskID, title, priorityGroup, estimatedSections, deadline, urgencyThreshold),
}
}
@@ -351,13 +354,14 @@ func handleQuickTaskQuery(
}
}
func buildTaskRecordBusinessCard(taskID int, title string, priorityGroup int, deadline *time.Time, urgencyThreshold *time.Time) *newagentstream.StreamBusinessCardExtra {
func buildTaskRecordBusinessCard(taskID int, title string, priorityGroup int, estimatedSections int, deadline *time.Time, urgencyThreshold *time.Time) *newagentstream.StreamBusinessCardExtra {
data := map[string]any{
"id": taskID,
"title": strings.TrimSpace(title),
"priority_group": priorityGroup,
"priority_label": newagentshared.PriorityLabelCN(priorityGroup),
"status": "todo",
"id": taskID,
"title": strings.TrimSpace(title),
"priority_group": priorityGroup,
"estimated_sections": estimatedSections,
"priority_label": newagentshared.PriorityLabelCN(priorityGroup),
"status": "todo",
}
if formatted := formatQuickTaskTime(deadline); formatted != "" {
data["deadline_at"] = formatted
@@ -383,11 +387,12 @@ func buildTaskQueryBusinessCard(params newagentmodel.TaskQueryParams, results []
taskItems := make([]map[string]any, 0, len(results))
for _, task := range results {
item := map[string]any{
"id": task.ID,
"title": strings.TrimSpace(task.Title),
"priority_group": task.PriorityGroup,
"priority_label": newagentshared.PriorityLabelCN(task.PriorityGroup),
"is_completed": task.IsCompleted,
"id": task.ID,
"title": strings.TrimSpace(task.Title),
"priority_group": task.PriorityGroup,
"estimated_sections": task.EstimatedSections,
"priority_label": newagentshared.PriorityLabelCN(task.PriorityGroup),
"is_completed": task.IsCompleted,
}
if deadline := strings.TrimSpace(task.DeadlineAt); deadline != "" {
item["deadline_at"] = deadline

View File

@@ -22,7 +22,7 @@ const quickTaskSystemPrompt = `
JSON 字段说明:
- action只能是 create / query / ask
- create 时title 必填deadline_at 必填priority_group 必填,范围 1-4urgency_threshold_at 满足条件时填写,条件在下面
- create 时title 必填deadline_at 必填priority_group 必填,范围 1-4estimated_sections 必填,范围 1-4不确定默认 1urgency_threshold_at 满足条件时填写,条件在下面
- query 时quadrant 可选 1-4keyword 可选limit 可选deadline_after/deadline_before 可选(用于截止时间窗口筛选)
- ask 时question 必填
@@ -37,9 +37,9 @@ JSON 字段说明:
示例:
<SMARTFLOW_DECISION>{"action":"create","title":"明天开会","deadline_at":"明天下午3点"}</SMARTFLOW_DECISION>
<SMARTFLOW_DECISION>{"action":"create","title":"明天开会","deadline_at":"明天下午3点","estimated_sections":1}</SMARTFLOW_DECISION>
好的,我来帮你记一下。
<SMARTFLOW_DECISION>{"action":"create","title":"下周交报告","deadline_at":"下周五 18:00","priority_group":2,"urgency_threshold_at":"下周四 09:00"}</SMARTFLOW_DECISION>
<SMARTFLOW_DECISION>{"action":"create","title":"下周交报告","deadline_at":"下周五 18:00","priority_group":2,"estimated_sections":2,"urgency_threshold_at":"下周四 09:00"}</SMARTFLOW_DECISION>
好的,我也帮你记一下。
<SMARTFLOW_DECISION>{"action":"query","limit":5}</SMARTFLOW_DECISION>