feat: 优化时间解析器功能
This commit is contained in:
parent
59a2effabc
commit
99d9360bd5
@ -1,5 +1,5 @@
|
||||
import llm from "../../utils/llm"
|
||||
|
||||
const userInput = "下个月份的活动"
|
||||
const userInput = "你对刚才的问题怎么看"
|
||||
|
||||
llm.timeParser(userInput, "localTest").then(console.log)
|
@ -1,83 +1,8 @@
|
||||
import { PromptTemplate } from "@langchain/core/prompts"
|
||||
import { z } from "zod"
|
||||
|
||||
import { adjustTimeRange, getTimeRange } from "../time"
|
||||
import { adjustTimeRange, getSpecificTime, getTimeRange } from "../time"
|
||||
import { getLangfuse, getModel } from "./base"
|
||||
|
||||
/**
|
||||
* 解析timeParser用户输入
|
||||
* 如果解析失败,则返回过去三天的时间范围
|
||||
* @param userInput 用户输入
|
||||
* @param requestId 请求ID
|
||||
* @returns
|
||||
*/
|
||||
const timeParser = async (userInput: string, requestId: string) => {
|
||||
const { langfuseHandler } = await getLangfuse(
|
||||
"parseGroupAgentQuery",
|
||||
requestId
|
||||
)
|
||||
const model = await getModel()
|
||||
const structuredLlm = model.withStructuredOutput(
|
||||
z.object({
|
||||
s: z.string().describe("开始时间,格式为 YYYY-MM-DD HH:mm:ss"),
|
||||
e: z.string().describe("结束时间,格式为 YYYY-MM-DD HH:mm:ss"),
|
||||
}),
|
||||
{
|
||||
name: "timeParser",
|
||||
}
|
||||
)
|
||||
|
||||
const invokeParser = async () => {
|
||||
try {
|
||||
return await structuredLlm.invoke(
|
||||
`
|
||||
当前时间为:${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
|
||||
请解析以下用户输入,提取出开始时间和结束时间,默认为过去三天
|
||||
除非用户指定到了分钟,开始时间为当天的 00:00:00,结束时间为当天的 23:59:59
|
||||
请将解析结果严格按照以下格式返回:
|
||||
\`\`\`ts
|
||||
interface GroupAgent {
|
||||
s: string // 开始时间,格式为 YYYY-MM-DD HH:mm:ss
|
||||
e: string // 结束时间,格式为 YYYY-MM-DD HH:mm:ss
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
用户输入:
|
||||
\`\`\`
|
||||
${userInput.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
`,
|
||||
{
|
||||
callbacks: [langfuseHandler],
|
||||
}
|
||||
)
|
||||
} catch {
|
||||
// 如果解析失败,则返回空字符串
|
||||
return { s: "", e: "" }
|
||||
}
|
||||
}
|
||||
|
||||
const validateResult = (result: any) => {
|
||||
const regex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/
|
||||
return regex.test(result.s) && regex.test(result.e)
|
||||
}
|
||||
|
||||
let result
|
||||
let attempts = 0
|
||||
do {
|
||||
result = await invokeParser()
|
||||
attempts++
|
||||
} while (!validateResult(result) && attempts < 3)
|
||||
|
||||
// 如果解析失败,则返回过去三天的时间范围
|
||||
if (!validateResult(result)) {
|
||||
return getTimeRange("threeDays")
|
||||
}
|
||||
|
||||
// 解析成功,调整时间范围
|
||||
return adjustTimeRange(result.s, result.e)
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用LLM模型
|
||||
* @param promptName 提示Key
|
||||
@ -110,6 +35,61 @@ const invoke = async (
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间解析器
|
||||
* @param userInput
|
||||
* @param requestId
|
||||
* @returns
|
||||
*/
|
||||
const timeParser = async (userInput: string, requestId: string) => {
|
||||
const time = new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })
|
||||
const weekDay = `星期${"日一二三四五六"[new Date().getDay()]}`
|
||||
const invokeParser = async () => {
|
||||
try {
|
||||
const res = (await invoke(
|
||||
"timeParser",
|
||||
{
|
||||
time,
|
||||
weekDay,
|
||||
userInput,
|
||||
lastWeekStart: getSpecificTime("lastMonday", 0),
|
||||
lastWeekEnd: getSpecificTime("lastSunday", 24),
|
||||
yesterdayAfternoonStart: getSpecificTime("yesterday", 12),
|
||||
yesterdayAfternoonEnd: getSpecificTime("yesterday", 18),
|
||||
threeDayStart: getSpecificTime("twoDaysAgo", 0),
|
||||
threeDayEnd: getSpecificTime("today", 24),
|
||||
},
|
||||
requestId
|
||||
)) as string
|
||||
return JSON.parse(res.replaceAll("`", ""))
|
||||
} catch (e) {
|
||||
console.error("🚀 ~ timeParser ~ invokeParser ~ e", e)
|
||||
// 如果解析失败,则返回空字符串
|
||||
return { s: "", e: "" }
|
||||
}
|
||||
}
|
||||
|
||||
const validateResult = (result: any) => {
|
||||
const regex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/
|
||||
return regex.test(result.s) && regex.test(result.e)
|
||||
}
|
||||
|
||||
let result
|
||||
let attempts = 0
|
||||
do {
|
||||
result = await invokeParser()
|
||||
attempts++
|
||||
} while (!validateResult(result) && attempts < 3)
|
||||
|
||||
// 如果解析失败,则返回过去三天的时间范围
|
||||
if (!validateResult(result)) {
|
||||
return getTimeRange("threeDays")
|
||||
}
|
||||
|
||||
// 解析成功,调整时间范围
|
||||
return adjustTimeRange(result.s, result.e)
|
||||
}
|
||||
|
||||
const llm = {
|
||||
timeParser,
|
||||
invoke,
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @param {Date} date - 日期对象
|
||||
* @returns {string} - 格式化后的日期字符串,格式为 YYYY-MM-DD HH:mm:ss
|
||||
*/
|
||||
const formatDate = (date: Date) => {
|
||||
export const formatDate = (date: Date) => {
|
||||
const pad = (num: number) => (num < 10 ? "0" + num : num)
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
}
|
||||
@ -20,29 +20,65 @@ const parseDate = (dateStr: string) => {
|
||||
return new Date(year, month - 1, day, hours, minutes, seconds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间点的时间
|
||||
* @param {("today" | "yesterday" | "twoDaysAgo" | "oneWeekAgo" | "lastMonday" | "lastSunday")} timePoint - 时间点,可以是 "today"、"yesterday"、"twoDaysAgo"、"oneWeekAgo"、"lastMonday" 或 "lastSunday"
|
||||
* @param {number} hourRange - 整点范围,输入 24 为 23:59:59
|
||||
* @returns {string} - 格式化后的时间字符串,格式为 YYYY-MM-DD HH:mm:ss
|
||||
*/
|
||||
export const getSpecificTime = (
|
||||
timePoint:
|
||||
| "today"
|
||||
| "yesterday"
|
||||
| "twoDaysAgo"
|
||||
| "oneWeekAgo"
|
||||
| "lastMonday"
|
||||
| "lastSunday",
|
||||
hourRange: number,
|
||||
needFormat: boolean = true
|
||||
) => {
|
||||
const now = new Date()
|
||||
const targetTime = new Date(now)
|
||||
|
||||
if (timePoint === "yesterday") {
|
||||
targetTime.setDate(now.getDate() - 1)
|
||||
} else if (timePoint === "twoDaysAgo") {
|
||||
targetTime.setDate(now.getDate() - 2)
|
||||
} else if (timePoint === "oneWeekAgo") {
|
||||
targetTime.setDate(now.getDate() - 7)
|
||||
} else if (timePoint === "lastMonday") {
|
||||
const day = now.getDay()
|
||||
const diff = day === 0 ? 6 : day - 1 // adjust when day is sunday
|
||||
targetTime.setDate(now.getDate() - diff - 7)
|
||||
} else if (timePoint === "lastSunday") {
|
||||
const day = now.getDay()
|
||||
const diff = day === 0 ? 7 : day // adjust when day is not sunday
|
||||
targetTime.setDate(now.getDate() - diff)
|
||||
}
|
||||
|
||||
if (hourRange === 24) {
|
||||
targetTime.setHours(23, 59, 59, 999)
|
||||
} else {
|
||||
targetTime.setHours(hourRange, 0, 0, 0)
|
||||
}
|
||||
|
||||
return needFormat ? formatDate(targetTime) : targetTime
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间范围的开始时间和结束时间
|
||||
* @param {("daily" | "weekly" | "threeDays")} timeScope - 时间范围,可以是 "daily"、"weekly" 或 "threeDays"
|
||||
* @returns {{ startTime: string, endTime: string }} - 格式化后的开始时间和结束时间
|
||||
*/
|
||||
export const getTimeRange = (timeScope: "daily" | "weekly" | "threeDays") => {
|
||||
const now = new Date()
|
||||
const startTime = new Date(now)
|
||||
startTime.setHours(0, 0, 0, 0)
|
||||
const endTime = new Date(now)
|
||||
endTime.setHours(23, 59, 59, 999)
|
||||
|
||||
if (timeScope === "weekly") {
|
||||
const day = now.getDay()
|
||||
const diff = day === 0 ? 6 : day - 1 // adjust when day is sunday
|
||||
startTime.setDate(now.getDate() - diff)
|
||||
} else if (timeScope === "threeDays") {
|
||||
startTime.setDate(now.getDate() - 2)
|
||||
const startTimeMap = {
|
||||
daily: getSpecificTime("today", 0),
|
||||
weekly: getSpecificTime("oneWeekAgo", 0),
|
||||
threeDays: getSpecificTime("twoDaysAgo", 0),
|
||||
}
|
||||
|
||||
return {
|
||||
startTime: formatDate(startTime),
|
||||
endTime: formatDate(endTime),
|
||||
startTime: startTimeMap[timeScope] as string,
|
||||
endTime: getSpecificTime("today", 24) as string,
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +107,11 @@ export const adjustTimeRange = (startTime: string, endTime: string) => {
|
||||
end.setHours(23, 59, 59, 999)
|
||||
}
|
||||
|
||||
const maxSpan = 60 * 24 * 60 * 60 * 1000 // 60天的毫秒数
|
||||
if (end.getTime() - start.getTime() > maxSpan) {
|
||||
start = new Date(end.getTime() - maxSpan)
|
||||
}
|
||||
|
||||
return {
|
||||
startTime: formatDate(start),
|
||||
endTime: formatDate(end),
|
||||
|
Loading…
x
Reference in New Issue
Block a user