Version: 0.9.27.dev.260418
后端: 1. SSE 心跳保活——解决 Vite dev proxy 在 LLM thinking 静默期判 idle 断连 - api/agent.go:ChatAgent 新增 5 秒 heartbeat ticker,select 增加 heartbeat.C 分支,每 5 秒写入 SSE 注释行 : ping\n\n 并 Flush - service/agentsvc/agent_newagent.go:graph 执行失败时增加 context.Canceled / requestCtx.Err() 判断,客户端断连只记 warn 不推 errChan 也不跑 fallback,消除 "错误通道已满" 日志噪音 2. 随口记工具(quick_note_create)接入新 Agent 链路 - agent/node/quicknote.go:parseOptionalDeadlineWithNow / quickNoteLocation 首字母大写导出,供新链路复用旧链路成熟的时间解析和时区能力 - agent/node/quicknote_tool.go:parseOptionalDeadline / quickNoteLocation 同步导出,补充调用目的注释 - newAgent/tools/quicknote.go:新增 QuickNoteToolHandler,实现新链路 quick_note_create 工具的参数校验、时间解析、写库调用 - newAgent/tools/registry.go:DefaultRegistryDeps 新增 QuickNote 字段;新增 RequiresScheduleState 方法和 scheduleFreeTools 集合;注册 quick_note_create 工具(不加入 writeTools,不走 confirm 确认) - cmd/start.go:NewDefaultRegistryWithDeps 注入 QuickNote.CreateTask 闭包,捕获 taskRepo 实例写库 3. Execute 节点随口记 speak 清空 + 非 ScheduleState 工具支持 - newAgent/node/execute.go:新增非写工具 confirm→continue 自动降级逻辑;新增 quick_note_create speak 强制清空,收口统一交给 deliver,避免 execute + deliver 重复废话 - newAgent/node/execute.go:executeToolCall / executePendingTool 中 scheduleState nil 检查改为仅拦截 RequiresScheduleState 的工具;为不依赖 ScheduleState 的工具自动注入 _user_id 参数 - newAgent/prompt/execute.go:有 plan / ReAct 两套系统 prompt 中,"写操作"规则细化为"日程写操作";新增 quick_note_create 专属执行规则:speak 必须留空,收口由 deliver 完成,调用成功后可 continue 处理多任务 - newAgent/prompt/chat.go:execute 路由描述补充"记录任务/提醒"场景 前端: 1. Vite dev proxy SSE 透传配置 - vite.config.ts:/api 代理新增 configure 回调,设置 x-accel-buffering: no 和 cache-control: no-cache,禁用代理缓冲 2.SSE 流式处理修复 - AssistantPanel.vue:reasoning_content 守卫放宽,移除 !assistantMessage.content.trim() 外层条件,正文回流后仍允许追加 reasoning(工具调用摘要、阶段状态等),不再吞掉 execute/deliver 的 reasoning_content - AssistantPanel.vue:流式完成后跳过 loadConversationMessages,避免 persistVisibleMessage 尚未落库时 merge 产生重复或丢失 仓库:无
This commit is contained in:
@@ -295,6 +295,25 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
||||
// speak 后处理:补列表序号换行 + 末尾加 \n 防止连续 speak 在前端粘连。
|
||||
decision.Speak = normalizeSpeak(decision.Speak) // 末尾已含 \n
|
||||
|
||||
// 非写工具的 confirm 动作自动降级为 continue。
|
||||
// 调用目的:quick_note_create 等非写工具不应走确认卡片流程;
|
||||
// 即使 LLM 误输出 action=confirm,也在此处强制修正,
|
||||
// 确保 speak 正常推流和持久化,不会因 confirm 卡片跳过 persistVisibleAssistantMessage。
|
||||
if decision.Action == newagentmodel.ExecuteActionConfirm &&
|
||||
decision.ToolCall != nil &&
|
||||
input.ToolRegistry != nil &&
|
||||
!input.ToolRegistry.IsWriteTool(decision.ToolCall.Name) {
|
||||
decision.Action = newagentmodel.ExecuteActionContinue
|
||||
}
|
||||
|
||||
// 随口记工具 speak 清空:
|
||||
// 1. quick_note_create 是轻量记录操作,不需要 execute 阶段向用户输出任何文案;
|
||||
// 2. 收口统一由 deliver 阶段完成,避免 execute + deliver 重复输出导致废话;
|
||||
// 3. 后端强制清空兜底,即使 LLM 误填了 speak 也不会推流到前端。
|
||||
if decision.ToolCall != nil && strings.EqualFold(decision.ToolCall.Name, "quick_note_create") {
|
||||
decision.Speak = ""
|
||||
}
|
||||
|
||||
// 自省校验:next_plan / done 必须附带 goal_check,否则不推进,追加修正让 LLM 重试。
|
||||
if decision.Action == newagentmodel.ExecuteActionNextPlan ||
|
||||
decision.Action == newagentmodel.ExecuteActionDone {
|
||||
@@ -1364,8 +1383,8 @@ func executeToolCall(
|
||||
if registry == nil {
|
||||
return fmt.Errorf("工具注册表未注入")
|
||||
}
|
||||
if scheduleState == nil {
|
||||
return fmt.Errorf("日程状态未加载,无法执行工具")
|
||||
if scheduleState == nil && registry.RequiresScheduleState(toolName) {
|
||||
return fmt.Errorf("日程状态未加载,无法执行工具 %q", toolName)
|
||||
}
|
||||
if !registry.HasTool(toolName) {
|
||||
// LLM 拼错或编造了工具名,走 correction 机制给重试机会,而非直接 fatal。
|
||||
@@ -1410,6 +1429,13 @@ func executeToolCall(
|
||||
}
|
||||
|
||||
beforeDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
// 调用目的:为不依赖 ScheduleState 的工具注入用户身份,工具层通过 args["_user_id"] 提取。
|
||||
if !registry.RequiresScheduleState(toolName) {
|
||||
if toolCall.Arguments == nil {
|
||||
toolCall.Arguments = make(map[string]any)
|
||||
}
|
||||
toolCall.Arguments["_user_id"] = flowState.UserID
|
||||
}
|
||||
result := registry.Execute(scheduleState, toolName, toolCall.Arguments)
|
||||
afterDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
log.Printf(
|
||||
@@ -1514,6 +1540,13 @@ func executePendingTool(
|
||||
|
||||
// 4. 执行工具。
|
||||
beforeDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
// 调用目的:为不依赖 ScheduleState 的工具注入用户身份,工具层通过 args["_user_id"] 提取。
|
||||
if !registry.RequiresScheduleState(pending.ToolName) {
|
||||
if args == nil {
|
||||
args = make(map[string]any)
|
||||
}
|
||||
args["_user_id"] = flowState.UserID
|
||||
}
|
||||
result := registry.Execute(scheduleState, pending.ToolName, args)
|
||||
afterDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
log.Printf(
|
||||
|
||||
Reference in New Issue
Block a user