Files
smartmate/backend/agent/taskquery/nodes_test.go
Losita 09dca9f772 Version: 0.6.5.dev.260316
 feat(agent): 通用分流接入随口问图编排,修复任务查询条数与重复输出问题

- ♻️ 将 Agent 路由升级为通用 `action` 分流机制,统一支持 `chat` / `quick_note_create` / `task_query`
- 🧩 新增 `taskquery` 子模块并落地图编排链路:`plan -> quadrant -> time_anchor -> tool_query -> reflect`
- 🔧 在图内接入 `query_tasks` 工具调用,支持自动放宽检索条件与反思重试,最多重试 2 次
- 🚪 保持 `/agent/chat` 作为多合一入口,不额外新增任务查询 HTTP 接口
- 🪄 修复“随口问”场景下的双重列表输出问题:LLM 仅保留简短前缀,任务列表统一由后端进行确定性渲染
- 🎯 修复显式数量约束失效问题:支持提取“来一个”“前 3 个”“top5”等数量表达,并将其锁定为 `limit`
- 🛡️ 防止在重试或放宽检索阶段改写用户显式指定的数量约束
-  补充并更新测试,覆盖路由解析、数量提取、`limit` 生效及重复输出等关键场景

📝 docs: 更新随口问链路文档与决策记录

- 📚 更新 README 5.4,新增/修订随口问链路 Mermaid 图
- 🧭 新增随口问功能决策记录 FDR
2026-03-16 22:30:45 +08:00

87 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package taskquery
import (
"strings"
"testing"
)
// TestExtractExplicitLimitFromUser_Number
// 目的:验证用户原话里的阿拉伯数字数量诉求可以被正确提取。
func TestExtractExplicitLimitFromUser_Number(t *testing.T) {
limit, ok := extractExplicitLimitFromUser("给我3个优先级低的任务")
if !ok {
t.Fatalf("期望识别到显式数量")
}
if limit != 3 {
t.Fatalf("数量识别错误,期望=3 实际=%d", limit)
}
}
// TestExtractExplicitLimitFromUser_ChineseNumber
// 目的:验证常见中文数字(如“前五个”)也能识别数量。
func TestExtractExplicitLimitFromUser_ChineseNumber(t *testing.T) {
limit, ok := extractExplicitLimitFromUser("前五个简单任务给我看看")
if !ok {
t.Fatalf("期望识别到中文数量")
}
if limit != 5 {
t.Fatalf("数量识别错误,期望=5 实际=%d", limit)
}
}
// TestExtractExplicitLimitFromUser_LaiYiGe
// 目的:验证“来一个...”这种口语数量表达也能识别为 1。
func TestExtractExplicitLimitFromUser_LaiYiGe(t *testing.T) {
limit, ok := extractExplicitLimitFromUser("来一个我的简单任务")
if !ok {
t.Fatalf("期望识别到“来一个”的显式数量")
}
if limit != 1 {
t.Fatalf("数量识别错误,期望=1 实际=%d", limit)
}
}
// TestBuildTaskQueryFinalReply_RespectsLimit
// 目的:验证最终回复会按 plan.limit 输出对应条数,而不是由 LLM 自由决定条数。
func TestBuildTaskQueryFinalReply_RespectsLimit(t *testing.T) {
items := []TaskQueryToolRecord{
{ID: 1, Title: "任务1", PriorityLabel: "简单不重要", DeadlineAt: "2026-03-16 10:00"},
{ID: 2, Title: "任务2", PriorityLabel: "简单不重要", DeadlineAt: "2026-03-17 10:00"},
{ID: 3, Title: "任务3", PriorityLabel: "简单不重要", DeadlineAt: "2026-03-18 10:00"},
}
reply := buildTaskQueryFinalReply(items, QueryPlan{Limit: 2}, "好的")
if !strings.Contains(reply, "整理了 2 条任务") {
t.Fatalf("回复未体现 limit=2reply=%s", reply)
}
if strings.Contains(reply, "3. ") {
t.Fatalf("回复不应出现第3条reply=%s", reply)
}
}
// TestBuildTaskQueryFinalReply_NoDuplicateList
// 目的:验证当 llmReply 已带列表内容时,不会和后端确定性列表重复拼接。
func TestBuildTaskQueryFinalReply_NoDuplicateList(t *testing.T) {
items := []TaskQueryToolRecord{
{ID: 1, Title: "任务1", PriorityLabel: "简单不重要", DeadlineAt: "2026-03-16 10:00"},
}
llmReply := "以下是你的任务:\n#1 任务1"
reply := buildTaskQueryFinalReply(items, QueryPlan{Limit: 1}, llmReply)
if strings.Contains(reply, "以下是你的任务") {
t.Fatalf("不应保留 llm 列表头reply=%s", reply)
}
if !strings.Contains(reply, "整理了 1 条任务") {
t.Fatalf("应保留后端确定性列表头reply=%s", reply)
}
}
// TestApplyRetryPatch_RespectExplicitLimit
// 目的:验证用户显式数量存在时,反思补丁不能改写 limit。
func TestApplyRetryPatch_RespectExplicitLimit(t *testing.T) {
plan := QueryPlan{Limit: 1, SortBy: "deadline", Order: "asc"}
limit := 10
next := applyRetryPatch(plan, taskQueryRetryPatch{Limit: &limit}, 1)
if next.Limit != 1 {
t.Fatalf("显式数量锁应生效,期望=1 实际=%d", next.Limit)
}
}