feat: 优化时间解析器功能

This commit is contained in:
zhaoyingbo 2024-12-02 03:11:15 +00:00
parent 59a2effabc
commit 99d9360bd5
3 changed files with 114 additions and 93 deletions

View File

@ -1,5 +1,5 @@
import llm from "../../utils/llm"
const userInput = "下个月份的活动"
const userInput = "你对刚才的问题怎么看"
llm.timeParser(userInput, "localTest").then(console.log)

View File

@ -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,

View File

@ -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),