feat(group-agent): 优化对话形式
All checks were successful
Egg Server MIflow / build-image (push) Successful in 35s
All checks were successful
Egg Server MIflow / build-image (push) Successful in 35s
This commit is contained in:
parent
b992ee0b21
commit
629d25287c
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -9,14 +9,18 @@
|
||||
"devcontainer",
|
||||
"devcontainers",
|
||||
"eamodio",
|
||||
"EOPK",
|
||||
"esbenp",
|
||||
"Euqi",
|
||||
"Gruntfuggly",
|
||||
"langchain",
|
||||
"langfuse",
|
||||
"metas",
|
||||
"mina",
|
||||
"mindnote",
|
||||
"openai",
|
||||
"openchat",
|
||||
"PWTHWP",
|
||||
"tseslint",
|
||||
"userid",
|
||||
"wlpbbgiky",
|
||||
|
32
index.ts
32
index.ts
@ -1,5 +1,4 @@
|
||||
import logger from "@egg/logger"
|
||||
import { makeCheckPathTool } from "@egg/path-tool"
|
||||
|
||||
import { manageBotReq } from "./routes/bot"
|
||||
import { manageMessageReq } from "./routes/message"
|
||||
@ -12,39 +11,36 @@ initSchedule()
|
||||
|
||||
const server = Bun.serve({
|
||||
async fetch(req) {
|
||||
// 路由处理
|
||||
const { exactCheck, startsWithCheck, fullCheck } = makeCheckPathTool(
|
||||
req.url
|
||||
)
|
||||
// 生成上下文
|
||||
const ctx = await genContext(req)
|
||||
const { path, genResp, logger } = ctx
|
||||
// 非健康检查由打印必要信息
|
||||
if (
|
||||
exactCheck("/bot") ||
|
||||
exactCheck("/message") ||
|
||||
exactCheck("/sheet") ||
|
||||
startsWithCheck("/micro_app")
|
||||
path.exact("/bot") ||
|
||||
path.exact("/message") ||
|
||||
path.exact("/sheet") ||
|
||||
path.startsWith("/micro_app")
|
||||
) {
|
||||
ctx.logger.info(`${req.method} ${req.url}`)
|
||||
ctx.logger.debug(`req body: ${ctx.text}`)
|
||||
logger.info(`${req.method} ${req.url}`)
|
||||
logger.debug(`req body: ${ctx.text}`)
|
||||
}
|
||||
// 逻辑处理
|
||||
try {
|
||||
// 机器人
|
||||
if (exactCheck("/bot")) return await manageBotReq(ctx)
|
||||
if (path.exact("/bot")) return await manageBotReq(ctx)
|
||||
// 消息代理发送
|
||||
if (exactCheck("/message")) return await manageMessageReq(ctx)
|
||||
if (path.exact("/message")) return await manageMessageReq(ctx)
|
||||
// 表格代理操作
|
||||
if (exactCheck("/sheet")) return await manageSheetReq(ctx)
|
||||
if (path.exact("/sheet")) return await manageSheetReq(ctx)
|
||||
// 小程序
|
||||
if (startsWithCheck("/micro_app")) return await manageMicroAppReq(ctx)
|
||||
if (path.startsWith("/micro_app")) return await manageMicroAppReq(ctx)
|
||||
// 健康检查
|
||||
if (fullCheck("/health")) return ctx.genResp.healthCheck()
|
||||
if (path.full("/health")) return genResp.healthCheck()
|
||||
// 其他
|
||||
return ctx.genResp.healthCheck("hello, there is egg, glade to serve you!")
|
||||
return genResp.healthCheck("hello, there is egg, glade to serve you!")
|
||||
} catch (error: any) {
|
||||
// 错误处理
|
||||
return ctx.genResp.serverError(error.message || "server error")
|
||||
return genResp.serverError(error.message || "server error")
|
||||
}
|
||||
},
|
||||
port: 3000,
|
||||
|
@ -35,12 +35,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@egg/hooks": "^1.2.0",
|
||||
"@egg/lark-msg-tool": "^1.2.1",
|
||||
"@egg/lark-msg-tool": "^1.4.0",
|
||||
"@egg/logger": "^1.4.3",
|
||||
"@egg/net-tool": "^1.6.5",
|
||||
"@egg/path-tool": "^1.3.0",
|
||||
"@langchain/openai": "^0.3.0",
|
||||
"@egg/path-tool": "^1.4.1",
|
||||
"@langchain/openai": "^0.3.2",
|
||||
"joi": "^17.13.3",
|
||||
"langfuse-langchain": "^3.26.0",
|
||||
"node-schedule": "^2.1.1",
|
||||
"p-limit": "^6.1.0",
|
||||
"pocketbase": "^0.21.5",
|
||||
|
@ -1,33 +1,12 @@
|
||||
import { getActionType, getIsActionMsg } from "@egg/lark-msg-tool"
|
||||
import { sleep } from "bun"
|
||||
|
||||
import { Context } from "../../types"
|
||||
import groupAgent from "./groupAgent"
|
||||
|
||||
/**
|
||||
* 返回ChatId卡片
|
||||
* @param {LarkAction.Data} body
|
||||
* @returns {Promise<string>} 返回包含ChatId卡片的JSON字符串
|
||||
*/
|
||||
const makeChatIdCard = async ({ body }: Context.Data): Promise<string> => {
|
||||
await sleep(500)
|
||||
return JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: body.open_chat_id,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const ACTION_MAP = {
|
||||
chat_id: makeChatIdCard,
|
||||
group_selector: groupAgent.setChatGroupContext,
|
||||
sendFunctionSelector: groupAgent.sendFunctionSelector,
|
||||
sendTimeScopeSelector: groupAgent.sendTimeScopeSelector,
|
||||
manageGroupMsg: groupAgent.manageGroupMsg,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,18 +15,15 @@ const ACTION_MAP = {
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
const manageBtnClick = async (ctx: Context.Data): Promise<void> => {
|
||||
const { body, larkService, logger } = ctx
|
||||
const { body, logger } = ctx
|
||||
const { action } = body?.action?.value as {
|
||||
action: keyof typeof ACTION_MAP
|
||||
}
|
||||
logger.info(`got button click action: ${action}`)
|
||||
logger.info(`Got lark action: ${action}`)
|
||||
if (!action) return
|
||||
const func = ACTION_MAP[action]
|
||||
if (!func) return
|
||||
const card = await func(ctx)
|
||||
if (!card) return
|
||||
// 更新飞书的卡片
|
||||
await larkService.message.update(body.open_message_id, card)
|
||||
func(ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,6 +132,7 @@ const manageCMDMsg = (ctx: Context.Data): boolean => {
|
||||
if (text.includes("share") && text.includes("简报")) {
|
||||
logger.info(`bot command is share report, chatId: ${chatId}`)
|
||||
// 这个用时比较久,先发一条提醒用户收到了请求
|
||||
// TODO: 迁移到简报服务中
|
||||
larkService.message.send(
|
||||
"chat_id",
|
||||
chatId,
|
||||
@ -148,17 +149,11 @@ const manageCMDMsg = (ctx: Context.Data): boolean => {
|
||||
return true
|
||||
}
|
||||
// 选择群组信息
|
||||
if (text.trim() === "/sg") {
|
||||
logger.info(`bot command is /sg, chatId: ${chatId}`)
|
||||
if (text.trim() === "@群聊助手") {
|
||||
logger.info(`bot command is @群聊助手, chatId: ${chatId}`)
|
||||
groupAgent.sendGroupSelector(ctx)
|
||||
return true
|
||||
}
|
||||
// 获取当前群组信息
|
||||
if (text.trim() === "/cg") {
|
||||
logger.info(`bot command is /cg, chatId: ${chatId}`)
|
||||
groupAgent.getCurrentGroup(ctx)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -1,74 +0,0 @@
|
||||
import { getChatId, LarkAction, LarkEvent } from "@egg/lark-msg-tool"
|
||||
|
||||
import db from "../../../db"
|
||||
import { Context } from "../../../types"
|
||||
import { genGroupAgentSuccessMsg } from "../../../utils/genMsg"
|
||||
|
||||
/**
|
||||
* 发送群组选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const sendGroupSelector = async ({ larkService, body }: Context.Data) => {
|
||||
const chatId = getChatId(body)!
|
||||
const { data: innerList } = await larkService.chat.getInnerList()
|
||||
// 组织群组数据
|
||||
const groups = innerList.map((v) => ({
|
||||
text: v.name,
|
||||
value: `${v.chat_id}|${v.name}`,
|
||||
}))
|
||||
larkService.message.sendTemp("chat_id", chatId, "ctp_AA00oqPWPTdc", {
|
||||
groups,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前群组
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const getCurrentGroup = async (ctx: Context.Data, needSendMsg = true) => {
|
||||
const body = ctx.body as LarkEvent.Data
|
||||
const chatId = getChatId(body)!
|
||||
const group = await db.groupAgentConfig.get(
|
||||
body.event.sender.sender_id.user_id
|
||||
)
|
||||
if (!needSendMsg) return group
|
||||
if (!group) {
|
||||
await sendGroupSelector(ctx)
|
||||
return
|
||||
}
|
||||
const msg = genGroupAgentSuccessMsg(`当前群组:${group.chat_name}`)
|
||||
ctx.larkService.message.send("chat_id", chatId, "interactive", msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组上下文
|
||||
* @param ctx - 上下文数据,包含body, larkService和logger
|
||||
*/
|
||||
const setChatGroupContext = async (ctx: Context.Data) => {
|
||||
const { larkService, logger } = ctx
|
||||
const body = ctx.body as LarkAction.Data
|
||||
const targetId = body?.action?.option?.split?.("|")[0]
|
||||
const targetName = body?.action?.option?.split?.("|")[1]
|
||||
if (!targetId || !targetName) {
|
||||
logger.error(
|
||||
`invalid targetId or targetName: ${JSON.stringify(body?.action)}`
|
||||
)
|
||||
}
|
||||
// 更新群组数据
|
||||
await db.groupAgentConfig.upsert({
|
||||
user_id: body.user_id,
|
||||
chat_id: targetId,
|
||||
chat_name: targetName,
|
||||
})
|
||||
// 更新成功消息
|
||||
const successMsg = genGroupAgentSuccessMsg(`已将群组切换至 ${targetName}`)
|
||||
larkService.message.update(body.open_message_id, successMsg)
|
||||
}
|
||||
|
||||
const groupManager = {
|
||||
sendGroupSelector,
|
||||
setChatGroupContext,
|
||||
getCurrentGroup,
|
||||
}
|
||||
|
||||
export default groupManager
|
@ -1,117 +1,216 @@
|
||||
import { getChatId, getMsgText, LarkEvent } from "@egg/lark-msg-tool"
|
||||
|
||||
import db from "../../../db"
|
||||
import { Context } from "../../../types"
|
||||
import {
|
||||
genGroupAgentErrorMsg,
|
||||
genGroupAgentSuccessMsg,
|
||||
} from "../../../utils/genMsg"
|
||||
getActionOption,
|
||||
getActionValue,
|
||||
getChatId,
|
||||
getMessageId,
|
||||
LarkAction,
|
||||
} from "@egg/lark-msg-tool"
|
||||
|
||||
import { Context } from "../../../types"
|
||||
import llm from "../../../utils/llm"
|
||||
import groupManager from "./groupManager"
|
||||
|
||||
enum CardTemplate {
|
||||
groupSelector = "ctp_AA0LgXOkJpIn",
|
||||
functionSelector = "ctp_AA0xqIITeUex",
|
||||
timeScopeSelector = "ctp_AA00oqPWT1Hq",
|
||||
resultReport = "ctp_AA00oqPWTHWP",
|
||||
successMsg = "ctp_AA0LgXOkEOPK",
|
||||
errorMsg = "ctp_AA0LgXOkEuqi",
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据聊天历史回答用户的问题
|
||||
* @param ctx - 上下文数据
|
||||
* @param userInput - 用户输入
|
||||
* @param targetChatId - 目标群组ID
|
||||
* @param loadingMsgId - loading消息ID
|
||||
* 发送群组选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const chat2llm = async (
|
||||
ctx: Context.Data,
|
||||
userInput: string,
|
||||
targetChatId: string,
|
||||
loadingMsgId?: string
|
||||
) => {
|
||||
const { logger, body } = ctx
|
||||
// 发送消息给Deepseek模型解析时间,返回格式为 YYYY-MM-DD HH:mm:ss
|
||||
const { startTime, endTime } = await llm.parseTime(userInput)
|
||||
logger.info(`Parsed result: startTime = ${startTime}, endTime = ${endTime}`)
|
||||
const sendGroupSelector = async ({
|
||||
larkService,
|
||||
body,
|
||||
logger,
|
||||
requestId,
|
||||
}: Context.Data) => {
|
||||
const { data: innerList } = await larkService.chat.getInnerList()
|
||||
logger.info(`Inner list: ${JSON.stringify(innerList)}`)
|
||||
// 组织群组数据
|
||||
const groups = innerList.map((v) => ({
|
||||
text: v.name,
|
||||
value: `${v.chat_id}|${v.name}`,
|
||||
}))
|
||||
larkService.message.sendTemp(
|
||||
"chat_id",
|
||||
getChatId(body),
|
||||
CardTemplate.groupSelector,
|
||||
{
|
||||
groups,
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送功能选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const sendFunctionSelector = async (ctx: Context.Data) => {
|
||||
const { larkService, logger, requestId } = ctx
|
||||
const body = ctx.body as LarkAction.Data
|
||||
const option = getActionOption(body)
|
||||
logger.debug(`Action option: ${JSON.stringify(option)}`)
|
||||
const [chatId, chatName] = option.split("|")
|
||||
if (!chatId || !chatName) {
|
||||
logger.error(
|
||||
`Invalid targetChatId or targetChatName: ${JSON.stringify(option)}`
|
||||
)
|
||||
return
|
||||
}
|
||||
// 组织功能数据
|
||||
const functions = [
|
||||
{
|
||||
text: "总结消息",
|
||||
value: "summary|总结消息",
|
||||
},
|
||||
]
|
||||
larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.functionSelector,
|
||||
{
|
||||
functions,
|
||||
chatId,
|
||||
chatName,
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送时间范围选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const sendTimeScopeSelector = async (ctx: Context.Data) => {
|
||||
const { larkService, logger, requestId } = ctx
|
||||
const body = ctx.body as LarkAction.Data
|
||||
const option = getActionOption(body)
|
||||
logger.debug(`Action option: ${JSON.stringify(option)}`)
|
||||
const [functionId, functionName] = option.split("|")
|
||||
if (!functionId || !functionName) {
|
||||
logger.error(`Invalid functionId or functionName: ${option}`)
|
||||
return
|
||||
}
|
||||
const value = getActionValue(body)
|
||||
logger.debug(`Action value: ${JSON.stringify(value)}`)
|
||||
const { chatId, chatName } = value
|
||||
if (!chatName || !chatId) {
|
||||
logger.error(`Invalid chatName or chatId: ${JSON.stringify(value)}`)
|
||||
return
|
||||
}
|
||||
larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.timeScopeSelector,
|
||||
{
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送结果报告
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const manageGroupMsg = async (ctx: Context.Data) => {
|
||||
const { larkService, logger, requestId } = ctx
|
||||
const body = ctx.body as LarkAction.Data
|
||||
const value = getActionValue(body)
|
||||
logger.debug(`Action value: ${JSON.stringify(value)}`)
|
||||
const { chatId, chatName, functionId, functionName, timeScope } = value
|
||||
if (!chatId || !chatName || !functionId || !functionName || !timeScope) {
|
||||
logger.error(`Invalid value: ${JSON.stringify(value)}`)
|
||||
return
|
||||
}
|
||||
// 发送一个loading的消息
|
||||
await larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.successMsg,
|
||||
{
|
||||
content: "Group Agent 正在爬楼中,请稍等...",
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
|
||||
// 记录发送loading消息后的时间戳
|
||||
const startTime = Date.now()
|
||||
|
||||
// 获取历史消息,timeScope为1、3、7,分别代表1天、3天、7天
|
||||
// 获取服务器的时区偏移量(以分钟为单位)
|
||||
const serverTimezoneOffset = new Date().getTimezoneOffset()
|
||||
// 上海时区的偏移量(UTC+8,以分钟为单位)
|
||||
const shanghaiTimezoneOffset = -8 * 60
|
||||
// 计算时间戳,调整为上海时区
|
||||
const startTimeTimestamp =
|
||||
Math.round(new Date(startTime).getTime() / 1000) +
|
||||
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
|
||||
const endTimeTimestamp =
|
||||
Math.round(new Date(endTime).getTime() / 1000) +
|
||||
Math.round(new Date().getTime() / 1000) +
|
||||
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
|
||||
|
||||
const startTimeTimestamp = endTimeTimestamp - Number(timeScope) * 24 * 60 * 60
|
||||
// 获取群聊中的历史记录
|
||||
const { data: chatHistory } = await ctx.larkService.message.getHistory(
|
||||
targetChatId,
|
||||
const { data: chatHistory } = await larkService.message.getHistory(
|
||||
chatId,
|
||||
String(startTimeTimestamp),
|
||||
String(endTimeTimestamp)
|
||||
)
|
||||
// 如果没有历史记录则返回错误消息
|
||||
if (chatHistory.length === 0) {
|
||||
logger.error("Chat history is empty")
|
||||
const content = genGroupAgentErrorMsg("未找到聊天记录")
|
||||
if (loadingMsgId) {
|
||||
await ctx.larkService.message.update(loadingMsgId, content)
|
||||
} else {
|
||||
await ctx.larkService.message.sendInteractive2Chat(
|
||||
getChatId(body),
|
||||
content
|
||||
)
|
||||
}
|
||||
await larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.errorMsg,
|
||||
{
|
||||
content: "未找到聊天记录",
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
logger.debug(`Chat history: ${JSON.stringify(chatHistory)}`)
|
||||
const llmRes = await llm.queryWithChatHistory(
|
||||
userInput,
|
||||
JSON.stringify(chatHistory)
|
||||
)
|
||||
logger.info(`LLM result: ${llmRes.content}`)
|
||||
const successMsg = genGroupAgentSuccessMsg(llmRes.content as string)
|
||||
// 发送LLM结果
|
||||
if (loadingMsgId) {
|
||||
await ctx.larkService.message.update(loadingMsgId, successMsg)
|
||||
} else {
|
||||
await ctx.larkService.message.sendInteractive2Chat(
|
||||
getChatId(body),
|
||||
successMsg
|
||||
try {
|
||||
const llmRes = await llm.invokeLLM4ChatHistory(
|
||||
functionId,
|
||||
JSON.stringify(chatHistory)
|
||||
)
|
||||
// 记录大模型返回结果后的时间戳
|
||||
const endTime = Date.now()
|
||||
// 计算时间差并存储在processingTime变量中,以秒为单位
|
||||
const processingTime = ((endTime - startTime) / 1000).toFixed(2)
|
||||
logger.info(`LLM takes time: ${processingTime}s, result: ${llmRes}`)
|
||||
await larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.resultReport,
|
||||
{
|
||||
chatName,
|
||||
functionName,
|
||||
llmRes,
|
||||
timeScope,
|
||||
requestId,
|
||||
processingTime,
|
||||
}
|
||||
)
|
||||
} catch (error: any) {
|
||||
logger.error(`LLM error: ${error.message}`)
|
||||
await larkService.message.updateTemp(
|
||||
getMessageId(body),
|
||||
CardTemplate.errorMsg,
|
||||
{
|
||||
content: "LLM调用失败: " + error.message,
|
||||
requestId,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组代理处理消息
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manageGroupMsg = async (ctx: Context.Data) => {
|
||||
const { logger } = ctx
|
||||
logger.info("Start to manage group message")
|
||||
const body = ctx.body as LarkEvent.Data
|
||||
// 获取用户输入
|
||||
const userInput = getMsgText(body)
|
||||
// 先获取当前对话的目标群组ID
|
||||
const group = await groupManager.getCurrentGroup(ctx, false)
|
||||
// 没有目标群组ID则发送群组选择器,并保存当前问题,在选择后立即处理
|
||||
if (!group || !group.chat_id) {
|
||||
logger.info("No group found, send group selector")
|
||||
groupManager.sendGroupSelector(ctx)
|
||||
db.groupAgentConfig.upsert({
|
||||
user_id: body.event.sender.sender_id.user_id,
|
||||
pre_query: userInput,
|
||||
})
|
||||
return
|
||||
}
|
||||
// 发送一个loading的消息
|
||||
const loadingRes = await ctx.larkService.message.sendInteractive2Chat(
|
||||
getChatId(body),
|
||||
genGroupAgentSuccessMsg("小煎蛋正在爬楼中,请稍等...")
|
||||
)
|
||||
if (loadingRes.code !== 0) {
|
||||
logger.error("Failed to send loading message")
|
||||
}
|
||||
const { message_id } = loadingRes.data
|
||||
await chat2llm(ctx, userInput, group.chat_id, message_id)
|
||||
}
|
||||
|
||||
const groupAgent = {
|
||||
...groupManager,
|
||||
sendGroupSelector,
|
||||
sendFunctionSelector,
|
||||
sendTimeScopeSelector,
|
||||
manageGroupMsg,
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { makeCheckPathTool } from "@egg/path-tool"
|
||||
|
||||
import { Context } from "../../types"
|
||||
|
||||
/**
|
||||
@ -67,13 +65,13 @@ const manageBatchUser = async (ctx: Context.Data) => {
|
||||
* @returns
|
||||
*/
|
||||
export const manageMicroAppReq = async (ctx: Context.Data) => {
|
||||
const { exactCheck } = makeCheckPathTool(ctx.req.url, "/micro_app")
|
||||
const path = ctx.path.child("/micro_app")
|
||||
// 处理登录请求
|
||||
if (exactCheck("/login")) {
|
||||
if (path.exact("/login")) {
|
||||
return manageLogin(ctx)
|
||||
}
|
||||
// 处理批量获取用户信息请求
|
||||
if (exactCheck("/batch_user")) {
|
||||
if (path.exact("/batch_user")) {
|
||||
return manageBatchUser(ctx)
|
||||
}
|
||||
return ctx.genResp.ok()
|
||||
|
@ -76,6 +76,16 @@ class LarkMessageService extends LarkBaseService {
|
||||
return this.patch<LarkServer.BaseRes>(path, { content })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新模板消息
|
||||
* @param messageId 消息id
|
||||
* @param templateId 模板ID
|
||||
* @param variable 模板变量
|
||||
*/
|
||||
async updateTemp(messageId: string, templateId: string, variable: any) {
|
||||
return this.update(messageId, genTempMsg(templateId, variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息历史记录
|
||||
* @param chatId 会话ID
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { NetTool } from "@egg/net-tool"
|
||||
import { PathCheckTool } from "@egg/path-tool"
|
||||
import { Logger } from "winston"
|
||||
|
||||
import { AttachService, LarkService } from "../services"
|
||||
@ -13,5 +14,7 @@ export namespace Context {
|
||||
text: string
|
||||
larkService: LarkService
|
||||
attachService: AttachService
|
||||
path: PathCheckTool
|
||||
searchParams: URLSearchParams
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,25 @@
|
||||
import { getActionValue } from "@egg/lark-msg-tool"
|
||||
import loggerIns from "@egg/logger"
|
||||
import { NetTool } from "@egg/net-tool"
|
||||
import { PathCheckTool } from "@egg/path-tool"
|
||||
import { v4 as uuid } from "uuid"
|
||||
|
||||
import { AttachService, LarkService } from "../services"
|
||||
import { Context } from "../types"
|
||||
|
||||
/**
|
||||
* 获取之前的requestId。
|
||||
*
|
||||
* @param {any} body - 请求体。
|
||||
* @returns {string} 返回之前的requestId。
|
||||
*/
|
||||
const getPreRequestId = (body: any) => {
|
||||
// 在Action请求中会带上之前的requestId
|
||||
const value = getActionValue(body)
|
||||
if (!value || !value.requestId) return ""
|
||||
return value.requestId
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求上下文。
|
||||
*
|
||||
@ -12,12 +27,6 @@ import { Context } from "../types"
|
||||
* @returns {Promise<Context.Data>} 返回包含请求上下文的对象。
|
||||
*/
|
||||
const genContext = async (req: Request) => {
|
||||
const requestId = uuid()
|
||||
const logger = loggerIns.child({ requestId })
|
||||
const genResp = new NetTool({ requestId })
|
||||
const larkService = new LarkService("egg", requestId)
|
||||
const attachService = new AttachService({ requestId })
|
||||
|
||||
let body: any = null
|
||||
let text: string = ""
|
||||
try {
|
||||
@ -26,8 +35,16 @@ const genContext = async (req: Request) => {
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
const requestId = getPreRequestId(body) || uuid()
|
||||
const logger = loggerIns.child({ requestId })
|
||||
const genResp = new NetTool({ requestId })
|
||||
const larkService = new LarkService("egg", requestId)
|
||||
const attachService = new AttachService({ requestId })
|
||||
const path = new PathCheckTool(req.url)
|
||||
|
||||
return {
|
||||
req,
|
||||
path,
|
||||
requestId,
|
||||
logger,
|
||||
genResp,
|
||||
@ -35,6 +52,7 @@ const genContext = async (req: Request) => {
|
||||
text,
|
||||
larkService,
|
||||
attachService,
|
||||
searchParams: new URL(req.url).searchParams,
|
||||
} as Context.Data
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ export const genSheetDbSuccessMsg = (content: string) =>
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genGroupAgentErrorMsg = (content: string) =>
|
||||
genErrorMsg("🧑💻 小煎蛋 Group Agent 错误提醒", content)
|
||||
genErrorMsg("🧑💻 Group Agent 错误提醒", content)
|
||||
|
||||
/**
|
||||
* 生成 Group Agent 成功消息的 JSON 字符串
|
||||
@ -93,4 +93,4 @@ export const genGroupAgentErrorMsg = (content: string) =>
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genGroupAgentSuccessMsg = (content: string) =>
|
||||
genSuccessMsg("🧑💻 感谢使用小煎蛋 Group Agent", content)
|
||||
genSuccessMsg("🧑💻 感谢使用 Group Agent", content)
|
||||
|
53
utils/llm.ts
53
utils/llm.ts
@ -1,8 +1,19 @@
|
||||
import { ChatOpenAI } from "@langchain/openai"
|
||||
import { CallbackHandler, Langfuse } from "langfuse-langchain"
|
||||
import { z } from "zod"
|
||||
|
||||
import db from "../db"
|
||||
|
||||
const langfuseParams = {
|
||||
publicKey: "pk-lf-7328c2f1-595a-4023-994b-78762d3dde9e",
|
||||
secretKey: "sk-lf-bdf20cd5-ff2c-45c3-9021-8292dbb1bef3",
|
||||
baseUrl: "https://langfuse.yingbo.im:333",
|
||||
}
|
||||
|
||||
const langfuseHandler = new CallbackHandler(langfuseParams)
|
||||
|
||||
const langfuse = new Langfuse(langfuseParams)
|
||||
|
||||
/**
|
||||
* 获取Deepseek模型
|
||||
* @param temperature 温度
|
||||
@ -40,36 +51,38 @@ const parseTime = async (userInput: string) => {
|
||||
)
|
||||
}
|
||||
|
||||
export enum LlmPromptType {
|
||||
summary = "summary",
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据聊天历史回答用户的问题
|
||||
* @param userInput 用户输入
|
||||
* 调用LLM模型
|
||||
* @param promptType 提示类型
|
||||
* @param chatHistory 聊天历史
|
||||
* @returns
|
||||
*/
|
||||
const queryWithChatHistory = async (userInput: string, chatHistory: string) => {
|
||||
const invokeLLM4ChatHistory = async (
|
||||
promptType: LlmPromptType,
|
||||
chatHistory: string
|
||||
) => {
|
||||
const prompt = await langfuse.getPrompt(promptType)
|
||||
const compiled_prompt = prompt.compile({
|
||||
chatHistory,
|
||||
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
|
||||
})
|
||||
const model = await getDeepseekModel(0.5)
|
||||
return await model.invoke(
|
||||
`
|
||||
当前时间为 ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
|
||||
你是一个专业的聊天记录分析工程师,给定以下用户输入和聊天历史,帮我回答用户的问题
|
||||
如果无法回答或者用户的问题不清晰,请引导用户问出更具体的问题
|
||||
|
||||
用户输入:
|
||||
\`\`\`
|
||||
${userInput.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
聊天历史:
|
||||
\`\`\`
|
||||
${chatHistory.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
`
|
||||
)
|
||||
const { content } = (await model.invoke(compiled_prompt, {
|
||||
callbacks: [langfuseHandler],
|
||||
})) as {
|
||||
content: string
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
const llm = {
|
||||
getDeepseekModel,
|
||||
parseTime,
|
||||
queryWithChatHistory,
|
||||
invokeLLM4ChatHistory,
|
||||
}
|
||||
|
||||
export default llm
|
||||
|
Loading…
x
Reference in New Issue
Block a user