refactor: 更新群聊助手逻辑,移除订阅功能并整合报告功能
This commit is contained in:
parent
3f971a0e77
commit
bc67792f30
@ -1,412 +1,9 @@
|
||||
import { genCardOptions, LarkEvent } from "@egg/lark-msg-tool"
|
||||
import manual from "./manual"
|
||||
import report from "./report"
|
||||
|
||||
import { functionOptionList } from "../../constant/card"
|
||||
import { Context, LarkServer } from "../../types"
|
||||
import llm from "../../utils/llm"
|
||||
import getChatHistory from "./chatHistory"
|
||||
|
||||
/**
|
||||
* 生成群组选择器
|
||||
* @param ctx - 上下文数据
|
||||
* @param innerList - 内部群组列表
|
||||
* @param commonVal - 通用值
|
||||
*/
|
||||
const genGroupSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
innerList: LarkServer.ChatGroupData[],
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 组织群组数据
|
||||
const groupOptions = genCardOptions(
|
||||
innerList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = `${item.chat_id}|${item.name}`
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
)
|
||||
return cardGender.genCard("groupSelector", {
|
||||
groupOptions,
|
||||
...commonVal,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成功能选择器
|
||||
* @param ctx - 上下文数据
|
||||
* @param commonVal - 通用值
|
||||
*/
|
||||
const genFunctionSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 组织功能数据
|
||||
const functionOptions = genCardOptions(
|
||||
functionOptionList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = `${item.id}|${item.name}`
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
)
|
||||
return cardGender.genCard("functionSelector", {
|
||||
functionOptions,
|
||||
...commonVal,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成时间范围选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const genTimeScopeSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
return larkCard.child("groupAgent").genCard("timeScopeSelector", commonVal)
|
||||
}
|
||||
|
||||
const sendGroupReport = async (
|
||||
ctx: Context.Data,
|
||||
messageId: string,
|
||||
{
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
}: {
|
||||
chatId: string
|
||||
chatName: string
|
||||
functionId: string
|
||||
functionName: string
|
||||
timeScope?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
mentions?: LarkEvent.Mention[]
|
||||
}
|
||||
) => {
|
||||
const { larkService, logger, larkCard, requestId } = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
const updateCard = (content: any) =>
|
||||
larkService.message.update(messageId, content)
|
||||
// action需要返回loading的消息,event需要主动update卡片,所以loading就放外边了
|
||||
// 记录发送loading消息后的时间戳
|
||||
const processStart = Date.now()
|
||||
// 获取聊天记录
|
||||
const chatHistory = await getChatHistory(ctx, {
|
||||
chatId,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
// 如果没有历史记录则返回错误消息
|
||||
if (chatHistory.length === 0) {
|
||||
logger.error("Chat history is empty")
|
||||
await updateCard(cardGender.genErrorCard("未找到聊天记录"))
|
||||
return
|
||||
}
|
||||
logger.debug(`Chat history: ${JSON.stringify(chatHistory)}`)
|
||||
|
||||
try {
|
||||
const llmRes = await llm.invoke(
|
||||
functionId,
|
||||
{
|
||||
chatHistory: JSON.stringify(chatHistory),
|
||||
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
|
||||
},
|
||||
requestId
|
||||
)
|
||||
// 记录大模型返回结果后的时间戳
|
||||
const processEnd = Date.now()
|
||||
// 计算时间差并存储在processingTime变量中,以秒为单位
|
||||
const processingTime = ((processEnd - processStart) / 1000).toFixed(2)
|
||||
logger.info(`LLM takes time: ${processingTime}s, result: ${llmRes}`)
|
||||
|
||||
// 动态生成content内容
|
||||
const timeRange = timeScope
|
||||
? `总结天数:**${timeScope}**`
|
||||
: `时间范围:**${startTime}** 至 **${endTime}**`
|
||||
const mentionsList = mentions
|
||||
? `圈选的用户:\n${mentions
|
||||
.filter((v) => v.id.user_id)
|
||||
.map((mention: any) => `- ${mention.name}`)
|
||||
.join("\n")}`
|
||||
: ""
|
||||
|
||||
const content = `群聊:**${chatName}**,功能:**${functionName}**,${timeRange}\n\n${mentionsList}\n\n以下内容由AI模型生成,耗时:**${processingTime}s**`
|
||||
|
||||
// 更新消息卡片
|
||||
await larkService.message.update(
|
||||
messageId,
|
||||
cardGender.genCard("resultReport", {
|
||||
content,
|
||||
llmRes,
|
||||
})
|
||||
)
|
||||
} catch (error: any) {
|
||||
logger.error(`LLM error: ${error.message}`)
|
||||
await larkService.message.update(
|
||||
messageId,
|
||||
cardGender.genErrorCard("LLM调用失败: " + error.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析用户输入中的值并发送对应的表单卡片
|
||||
* @param ctx - 上下文数据
|
||||
* @param innerList - 内部群组列表
|
||||
*/
|
||||
const parseGroupAgentQuery = async (
|
||||
ctx: Context.Data,
|
||||
innerList: LarkServer.ChatGroupData[],
|
||||
mentions?: LarkEvent.Mention[]
|
||||
) => {
|
||||
// TODO:处理在群聊里的情况,不用获取群组名称
|
||||
const {
|
||||
larkBody: { msgText, chatId: rawChatId },
|
||||
larkService,
|
||||
larkCard,
|
||||
logger,
|
||||
requestId,
|
||||
} = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
|
||||
// 发送一个loading的消息
|
||||
const {
|
||||
data: { message_id },
|
||||
} = await larkService.message.sendCard2Chat(
|
||||
rawChatId,
|
||||
cardGender.genPendingCard("分析中,请稍等...")
|
||||
)
|
||||
|
||||
const updateCard = (content: any) =>
|
||||
larkService.message.update(message_id, content)
|
||||
|
||||
// 组织群组数据
|
||||
const groupInfo = JSON.stringify(
|
||||
innerList.map((v) => ({
|
||||
name: v.name,
|
||||
id: v.chat_id,
|
||||
}))
|
||||
)
|
||||
// 获取功能信息
|
||||
const functionInfo = JSON.stringify(functionOptionList)
|
||||
|
||||
// 使用大模型解析用户输入
|
||||
const { chatId, chatName, functionName, functionId, startTime, endTime } =
|
||||
await llm.parseGroupAgentQuery(msgText, groupInfo, functionInfo, requestId)
|
||||
logger.info(
|
||||
`Parsed group agent query: chatId: ${chatId}, chatName: ${chatName}, functionName: ${functionName}, functionId: ${functionId}, startTime: ${startTime}, endTime: ${endTime}`
|
||||
)
|
||||
|
||||
// 判断顺序是 群组 -> 功能 -> 时间范围
|
||||
|
||||
// 返回群组选择器,其他的值往里边丢就行
|
||||
if (!chatId || !chatName) {
|
||||
logger.info("Send group selector")
|
||||
updateCard(
|
||||
genGroupSelector(ctx, innerList, {
|
||||
functionName,
|
||||
functionId,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
// 返回功能选择器,其他的值往里边丢就行
|
||||
if (!functionId || !functionName) {
|
||||
logger.info("Send function selector")
|
||||
updateCard(
|
||||
genFunctionSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
// 返回时间范围选择器,其他的值往里边丢就行
|
||||
if (!startTime || !endTime) {
|
||||
logger.info("Send time scope selector")
|
||||
updateCard(
|
||||
genTimeScopeSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info("Send group report")
|
||||
// 设置齐全,返回结果报告
|
||||
updateCard(cardGender.genPendingCard("正在爬楼中,请稍等..."))
|
||||
sendGroupReport(ctx, message_id, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理事件消息
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manageEventMsg = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { msgText, chatType, chatId: rawChatId, mentions },
|
||||
larkService,
|
||||
logger,
|
||||
} = ctx
|
||||
|
||||
// 获取群组信息
|
||||
const { data: innerList } = await larkService.chat.getInnerList()
|
||||
logger.info(`Inner list: ${JSON.stringify(innerList)}`)
|
||||
const sendCard = (content: string) =>
|
||||
larkService.message.sendCard2Chat(rawChatId, content)
|
||||
|
||||
// 去过去掉所有非必要的信息为空的话
|
||||
if (msgText.replace("/groupchat", "").replaceAll(" ", "") === "") {
|
||||
// 私聊发送正常的群组选择器
|
||||
if (chatType === "p2p") {
|
||||
logger.info("Send group selector to p2p chat")
|
||||
await sendCard(genGroupSelector(ctx, innerList, { mentions }))
|
||||
return
|
||||
}
|
||||
// 如果是群聊,获取群聊名称并发送功能
|
||||
const {
|
||||
data: { name: chatName },
|
||||
} = await larkService.chat.getChatInfo(rawChatId)
|
||||
logger.info(`Send function selector to group chat: ${chatName}`)
|
||||
await sendCard(genFunctionSelector(ctx, { chatName, mentions }))
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(`User input: ${msgText}, chatType: ${chatType}, use llm to parse`)
|
||||
// 用户有输入,使用大模型进行解析发送对应卡片
|
||||
await parseGroupAgentQuery(ctx, innerList, mentions)
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理Action消息
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manageActionMsg = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { actionOption, actionValue, messageId },
|
||||
larkCard,
|
||||
logger,
|
||||
} = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
logger.debug(`Action option: ${JSON.stringify(actionOption)}`)
|
||||
logger.debug(`Action value: ${JSON.stringify(actionValue)}`)
|
||||
let { chatId, chatName, functionId, functionName } = actionValue
|
||||
const { timeScope, startTime, endTime, cardName, mentions } = actionValue
|
||||
// 如果是群组选择器返回值
|
||||
if (cardName === "groupSelector") {
|
||||
const [newChatId, newChatName] = (actionOption ?? "").split("|")
|
||||
if (!newChatId || !newChatName) {
|
||||
logger.error(
|
||||
`Invalid targetChatId or targetChatName: ${JSON.stringify(actionOption)}`
|
||||
)
|
||||
return cardGender.genErrorCard("Invalid targetChatId or targetChatName")
|
||||
}
|
||||
chatId = newChatId
|
||||
chatName = newChatName
|
||||
}
|
||||
// 如果是功能选择器返回值
|
||||
if (cardName === "functionSelector") {
|
||||
const [newFunctionId, newFunctionName] = (actionOption ?? "").split("|")
|
||||
if (!newFunctionId || !newFunctionName) {
|
||||
logger.error(
|
||||
`Invalid functionId or functionName: ${JSON.stringify(actionOption)}`
|
||||
)
|
||||
return cardGender.genErrorCard("Invalid functionId or functionName")
|
||||
}
|
||||
functionId = newFunctionId
|
||||
functionName = newFunctionName
|
||||
}
|
||||
// 时间返回的返回值就会带在timeScope里,不需要再处理
|
||||
// 理论上来说,这里的chatId, chatName肯定是有值的,不需要再判断
|
||||
// 判断是否需要返回功能选择器
|
||||
if (!functionId || !functionName) {
|
||||
logger.info("Send function selector")
|
||||
return genFunctionSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
startTime,
|
||||
endTime,
|
||||
timeScope,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
// 判断是否需要返回时间范围选择器
|
||||
if (!(timeScope || (startTime && endTime))) {
|
||||
logger.info("Send time scope selector")
|
||||
return genTimeScopeSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
logger.info("Send group report")
|
||||
// 设置齐全,返回结果报告
|
||||
sendGroupReport(ctx, messageId, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
return cardGender.genPendingCard("正在爬楼中,请稍等...")
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组Agent的主入口
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const groupAgent = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { isEvent, isAction },
|
||||
logger,
|
||||
} = ctx
|
||||
try {
|
||||
// 如果是Event,则解析自然语言并发送对应的卡片
|
||||
if (isEvent) return await manageEventMsg(ctx)
|
||||
// 如果是Action,则取出用户选的值并判断是否需要继续发送表单卡片或者开始大模型推理
|
||||
if (isAction) return await manageActionMsg(ctx)
|
||||
} catch (e: any) {
|
||||
logger.error(`Group agent error: ${e.message}`)
|
||||
return ctx.larkCard.child("groupAgent").genErrorCard("Group agent error")
|
||||
}
|
||||
const groupAgent = {
|
||||
manual,
|
||||
report,
|
||||
}
|
||||
|
||||
export default groupAgent
|
||||
|
412
controller/groupAgent/manual.ts
Normal file
412
controller/groupAgent/manual.ts
Normal file
@ -0,0 +1,412 @@
|
||||
import { genCardOptions, LarkEvent } from "@egg/lark-msg-tool"
|
||||
|
||||
import { functionOptionList } from "../../constant/card"
|
||||
import { Context, LarkServer } from "../../types"
|
||||
import llm from "../../utils/llm"
|
||||
import getChatHistory from "./chatHistory"
|
||||
|
||||
/**
|
||||
* 生成群组选择器
|
||||
* @param ctx - 上下文数据
|
||||
* @param innerList - 内部群组列表
|
||||
* @param commonVal - 通用值
|
||||
*/
|
||||
const genGroupSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
innerList: LarkServer.ChatGroupData[],
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 组织群组数据
|
||||
const groupOptions = genCardOptions(
|
||||
innerList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = `${item.chat_id}|${item.name}`
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
)
|
||||
return cardGender.genCard("groupSelector", {
|
||||
groupOptions,
|
||||
...commonVal,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成功能选择器
|
||||
* @param ctx - 上下文数据
|
||||
* @param commonVal - 通用值
|
||||
*/
|
||||
const genFunctionSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 组织功能数据
|
||||
const functionOptions = genCardOptions(
|
||||
functionOptionList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = `${item.id}|${item.name}`
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
)
|
||||
return cardGender.genCard("functionSelector", {
|
||||
functionOptions,
|
||||
...commonVal,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成时间范围选择器
|
||||
* @param ctx - 上下文数据,包含body和larkService
|
||||
*/
|
||||
const genTimeScopeSelector = (
|
||||
{ larkCard }: Context.Data,
|
||||
commonVal: Record<string, any> = {}
|
||||
) => {
|
||||
return larkCard.child("groupAgent").genCard("timeScopeSelector", commonVal)
|
||||
}
|
||||
|
||||
const sendGroupReport = async (
|
||||
ctx: Context.Data,
|
||||
messageId: string,
|
||||
{
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
}: {
|
||||
chatId: string
|
||||
chatName: string
|
||||
functionId: string
|
||||
functionName: string
|
||||
timeScope?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
mentions?: LarkEvent.Mention[]
|
||||
}
|
||||
) => {
|
||||
const { larkService, logger, larkCard, requestId } = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
const updateCard = (content: any) =>
|
||||
larkService.message.update(messageId, content)
|
||||
// action需要返回loading的消息,event需要主动update卡片,所以loading就放外边了
|
||||
// 记录发送loading消息后的时间戳
|
||||
const processStart = Date.now()
|
||||
// 获取聊天记录
|
||||
const chatHistory = await getChatHistory(ctx, {
|
||||
chatId,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
// 如果没有历史记录则返回错误消息
|
||||
if (chatHistory.length === 0) {
|
||||
logger.error("Chat history is empty")
|
||||
await updateCard(cardGender.genErrorCard("未找到聊天记录"))
|
||||
return
|
||||
}
|
||||
logger.debug(`Chat history: ${JSON.stringify(chatHistory)}`)
|
||||
|
||||
try {
|
||||
const llmRes = await llm.invoke(
|
||||
functionId,
|
||||
{
|
||||
chatHistory: JSON.stringify(chatHistory),
|
||||
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
|
||||
},
|
||||
requestId
|
||||
)
|
||||
// 记录大模型返回结果后的时间戳
|
||||
const processEnd = Date.now()
|
||||
// 计算时间差并存储在processingTime变量中,以秒为单位
|
||||
const processingTime = ((processEnd - processStart) / 1000).toFixed(2)
|
||||
logger.info(`LLM takes time: ${processingTime}s, result: ${llmRes}`)
|
||||
|
||||
// 动态生成content内容
|
||||
const timeRange = timeScope
|
||||
? `总结天数:**${timeScope}**`
|
||||
: `时间范围:**${startTime}** 至 **${endTime}**`
|
||||
const mentionsList = mentions
|
||||
? `圈选的用户:\n${mentions
|
||||
.filter((v) => v.id.user_id)
|
||||
.map((mention: any) => `- ${mention.name}`)
|
||||
.join("\n")}`
|
||||
: ""
|
||||
|
||||
const content = `群聊:**${chatName}**,功能:**${functionName}**,${timeRange}\n\n${mentionsList}\n\n以下内容由AI模型生成,耗时:**${processingTime}s**`
|
||||
|
||||
// 更新消息卡片
|
||||
await larkService.message.update(
|
||||
messageId,
|
||||
cardGender.genCard("resultReport", {
|
||||
content,
|
||||
llmRes,
|
||||
})
|
||||
)
|
||||
} catch (error: any) {
|
||||
logger.error(`LLM error: ${error.message}`)
|
||||
await larkService.message.update(
|
||||
messageId,
|
||||
cardGender.genErrorCard("LLM调用失败: " + error.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析用户输入中的值并发送对应的表单卡片
|
||||
* @param ctx - 上下文数据
|
||||
* @param innerList - 内部群组列表
|
||||
*/
|
||||
const parseGroupAgentQuery = async (
|
||||
ctx: Context.Data,
|
||||
innerList: LarkServer.ChatGroupData[],
|
||||
mentions?: LarkEvent.Mention[]
|
||||
) => {
|
||||
// TODO:处理在群聊里的情况,不用获取群组名称
|
||||
const {
|
||||
larkBody: { msgText, chatId: rawChatId },
|
||||
larkService,
|
||||
larkCard,
|
||||
logger,
|
||||
requestId,
|
||||
} = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
|
||||
// 发送一个loading的消息
|
||||
const {
|
||||
data: { message_id },
|
||||
} = await larkService.message.sendCard2Chat(
|
||||
rawChatId,
|
||||
cardGender.genPendingCard("分析中,请稍等...")
|
||||
)
|
||||
|
||||
const updateCard = (content: any) =>
|
||||
larkService.message.update(message_id, content)
|
||||
|
||||
// 组织群组数据
|
||||
const groupInfo = JSON.stringify(
|
||||
innerList.map((v) => ({
|
||||
name: v.name,
|
||||
id: v.chat_id,
|
||||
}))
|
||||
)
|
||||
// 获取功能信息
|
||||
const functionInfo = JSON.stringify(functionOptionList)
|
||||
|
||||
// 使用大模型解析用户输入
|
||||
const { chatId, chatName, functionName, functionId, startTime, endTime } =
|
||||
await llm.parseGroupAgentQuery(msgText, groupInfo, functionInfo, requestId)
|
||||
logger.info(
|
||||
`Parsed group agent query: chatId: ${chatId}, chatName: ${chatName}, functionName: ${functionName}, functionId: ${functionId}, startTime: ${startTime}, endTime: ${endTime}`
|
||||
)
|
||||
|
||||
// 判断顺序是 群组 -> 功能 -> 时间范围
|
||||
|
||||
// 返回群组选择器,其他的值往里边丢就行
|
||||
if (!chatId || !chatName) {
|
||||
logger.info("Send group selector")
|
||||
updateCard(
|
||||
genGroupSelector(ctx, innerList, {
|
||||
functionName,
|
||||
functionId,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
// 返回功能选择器,其他的值往里边丢就行
|
||||
if (!functionId || !functionName) {
|
||||
logger.info("Send function selector")
|
||||
updateCard(
|
||||
genFunctionSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
// 返回时间范围选择器,其他的值往里边丢就行
|
||||
if (!startTime || !endTime) {
|
||||
logger.info("Send time scope selector")
|
||||
updateCard(
|
||||
genTimeScopeSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
mentions,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info("Send group report")
|
||||
// 设置齐全,返回结果报告
|
||||
updateCard(cardGender.genPendingCard("正在爬楼中,请稍等..."))
|
||||
sendGroupReport(ctx, message_id, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理事件消息
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manageEventMsg = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { msgText, chatType, chatId: rawChatId, mentions },
|
||||
larkService,
|
||||
logger,
|
||||
} = ctx
|
||||
|
||||
// 获取群组信息
|
||||
const { data: innerList } = await larkService.chat.getInnerList()
|
||||
logger.info(`Inner list: ${JSON.stringify(innerList)}`)
|
||||
const sendCard = (content: string) =>
|
||||
larkService.message.sendCard2Chat(rawChatId, content)
|
||||
|
||||
// 去过去掉所有非必要的信息为空的话
|
||||
if (msgText.replace("/groupchat", "").replaceAll(" ", "") === "") {
|
||||
// 私聊发送正常的群组选择器
|
||||
if (chatType === "p2p") {
|
||||
logger.info("Send group selector to p2p chat")
|
||||
await sendCard(genGroupSelector(ctx, innerList, { mentions }))
|
||||
return
|
||||
}
|
||||
// 如果是群聊,获取群聊名称并发送功能
|
||||
const {
|
||||
data: { name: chatName },
|
||||
} = await larkService.chat.getChatInfo(rawChatId)
|
||||
logger.info(`Send function selector to group chat: ${chatName}`)
|
||||
await sendCard(genFunctionSelector(ctx, { chatName, mentions }))
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(`User input: ${msgText}, chatType: ${chatType}, use llm to parse`)
|
||||
// 用户有输入,使用大模型进行解析发送对应卡片
|
||||
await parseGroupAgentQuery(ctx, innerList, mentions)
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理Action消息
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manageActionMsg = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { actionOption, actionValue, messageId },
|
||||
larkCard,
|
||||
logger,
|
||||
} = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
logger.debug(`Action option: ${JSON.stringify(actionOption)}`)
|
||||
logger.debug(`Action value: ${JSON.stringify(actionValue)}`)
|
||||
let { chatId, chatName, functionId, functionName } = actionValue
|
||||
const { timeScope, startTime, endTime, cardName, mentions } = actionValue
|
||||
// 如果是群组选择器返回值
|
||||
if (cardName === "groupSelector") {
|
||||
const [newChatId, newChatName] = (actionOption ?? "").split("|")
|
||||
if (!newChatId || !newChatName) {
|
||||
logger.error(
|
||||
`Invalid targetChatId or targetChatName: ${JSON.stringify(actionOption)}`
|
||||
)
|
||||
return cardGender.genErrorCard("Invalid targetChatId or targetChatName")
|
||||
}
|
||||
chatId = newChatId
|
||||
chatName = newChatName
|
||||
}
|
||||
// 如果是功能选择器返回值
|
||||
if (cardName === "functionSelector") {
|
||||
const [newFunctionId, newFunctionName] = (actionOption ?? "").split("|")
|
||||
if (!newFunctionId || !newFunctionName) {
|
||||
logger.error(
|
||||
`Invalid functionId or functionName: ${JSON.stringify(actionOption)}`
|
||||
)
|
||||
return cardGender.genErrorCard("Invalid functionId or functionName")
|
||||
}
|
||||
functionId = newFunctionId
|
||||
functionName = newFunctionName
|
||||
}
|
||||
// 时间返回的返回值就会带在timeScope里,不需要再处理
|
||||
// 理论上来说,这里的chatId, chatName肯定是有值的,不需要再判断
|
||||
// 判断是否需要返回功能选择器
|
||||
if (!functionId || !functionName) {
|
||||
logger.info("Send function selector")
|
||||
return genFunctionSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
startTime,
|
||||
endTime,
|
||||
timeScope,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
// 判断是否需要返回时间范围选择器
|
||||
if (!(timeScope || (startTime && endTime))) {
|
||||
logger.info("Send time scope selector")
|
||||
return genTimeScopeSelector(ctx, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
mentions,
|
||||
})
|
||||
}
|
||||
|
||||
logger.info("Send group report")
|
||||
// 设置齐全,返回结果报告
|
||||
sendGroupReport(ctx, messageId, {
|
||||
chatId,
|
||||
chatName,
|
||||
functionId,
|
||||
functionName,
|
||||
timeScope,
|
||||
startTime,
|
||||
endTime,
|
||||
mentions,
|
||||
})
|
||||
return cardGender.genPendingCard("正在爬楼中,请稍等...")
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组Agent的主入口
|
||||
* @param ctx - 上下文数据
|
||||
*/
|
||||
const manual = async (ctx: Context.Data) => {
|
||||
const {
|
||||
larkBody: { isEvent, isAction },
|
||||
logger,
|
||||
} = ctx
|
||||
try {
|
||||
// 如果是Event,则解析自然语言并发送对应的卡片
|
||||
if (isEvent) return await manageEventMsg(ctx)
|
||||
// 如果是Action,则取出用户选的值并判断是否需要继续发送表单卡片或者开始大模型推理
|
||||
if (isAction) return await manageActionMsg(ctx)
|
||||
} catch (e: any) {
|
||||
logger.error(`Group agent error: ${e.message}`)
|
||||
return ctx.larkCard.child("groupAgent").genErrorCard("Group agent error")
|
||||
}
|
||||
}
|
||||
|
||||
export default manual
|
@ -1,6 +1,7 @@
|
||||
import { LarkService } from "@egg/net-tool"
|
||||
|
||||
import { appMap } from "../../constant/appMap"
|
||||
import { RespMessage } from "../../constant/message"
|
||||
import prisma from "../../prisma"
|
||||
import { Context } from "../../types"
|
||||
import genContext from "../../utils/genContext"
|
||||
@ -8,27 +9,22 @@ import llm from "../../utils/llm"
|
||||
import { getTimeRange } from "../../utils/time"
|
||||
import getChatHistory from "./chatHistory"
|
||||
|
||||
interface Subscription {
|
||||
id: bigint
|
||||
chat_id: string
|
||||
robot_id: string
|
||||
initiator: string
|
||||
terminator: string
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
}
|
||||
|
||||
/**
|
||||
* 总结指定聊天记录
|
||||
* @param {Context.Data} ctx - 请求上下文
|
||||
* @param {string} timeScope - 时间范围
|
||||
* @param {Subscription} subscription - 订阅信息
|
||||
* @param {any} subscription - 订阅信息
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const genReport = async (
|
||||
ctx: Context.Data,
|
||||
timeScope: "daily" | "weekly",
|
||||
subscription: Subscription
|
||||
subscription: {
|
||||
id: bigint
|
||||
chat_id: string
|
||||
robot_id: string
|
||||
initiator: string
|
||||
}
|
||||
) => {
|
||||
const { logger, requestId, larkCard } = ctx
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
@ -181,10 +177,122 @@ const gen4Test = async (ctx: Context.Data, timeScope: "daily" | "weekly") => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册消息总结的订阅
|
||||
* @returns
|
||||
*/
|
||||
const subscribe = async ({
|
||||
app,
|
||||
larkService,
|
||||
logger,
|
||||
larkBody,
|
||||
larkCard,
|
||||
}: Context.Data) => {
|
||||
try {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 判断是否有 chatId 和 userId
|
||||
if (!larkBody.chatId || !larkBody.userId) {
|
||||
logger.error(`chatId or userId is empty`)
|
||||
return
|
||||
}
|
||||
// 先查询是否已经存在订阅
|
||||
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
||||
{
|
||||
where: {
|
||||
chat_id: larkBody.chatId,
|
||||
terminator: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
// 如果已经存在订阅,则返回已经注册过了
|
||||
if (subscription) {
|
||||
logger.info(`chatId: ${larkBody.chatId} has been registered`)
|
||||
// 发送已经注册过的消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.hasRegistered)
|
||||
)
|
||||
return
|
||||
}
|
||||
// 注册订阅
|
||||
await prisma.chat_agent_summary_subscription.create({
|
||||
data: {
|
||||
chat_id: larkBody.chatId,
|
||||
robot_id: app,
|
||||
initiator: larkBody.userId,
|
||||
},
|
||||
})
|
||||
// 发送成功消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.registerSuccess)
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error(`Subscribe error: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消消息总结的订阅
|
||||
* @returns
|
||||
*/
|
||||
const unsubscribe = async ({
|
||||
logger,
|
||||
larkBody,
|
||||
larkService,
|
||||
larkCard,
|
||||
}: Context.Data) => {
|
||||
try {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 判断是否有 chatId 和 userId
|
||||
if (!larkBody.chatId || !larkBody.userId) {
|
||||
logger.error(`chatId or userId is empty`)
|
||||
return
|
||||
}
|
||||
// 查找现有的订阅
|
||||
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
||||
{
|
||||
where: {
|
||||
chat_id: larkBody.chatId,
|
||||
terminator: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
// 如果没有找到订阅,则返回错误
|
||||
if (!subscription) {
|
||||
logger.info(`chatId: ${larkBody.chatId} has not been registered`)
|
||||
// 发送已经取消订阅的消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
||||
)
|
||||
return
|
||||
}
|
||||
// 更新订阅,设置终止者和终止时间
|
||||
await prisma.chat_agent_summary_subscription.update({
|
||||
where: {
|
||||
id: subscription.id,
|
||||
},
|
||||
data: {
|
||||
terminator: larkBody.userId,
|
||||
},
|
||||
})
|
||||
// 发送成功消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error(`Unsubscribe error: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const report = {
|
||||
genReport,
|
||||
genAllReport,
|
||||
gen4Test,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
}
|
||||
|
||||
export default report
|
||||
|
@ -1,120 +0,0 @@
|
||||
import { RespMessage } from "../../constant/message"
|
||||
import prisma from "../../prisma"
|
||||
import { Context } from "../../types"
|
||||
|
||||
/**
|
||||
* 注册消息总结的订阅
|
||||
* @returns
|
||||
*/
|
||||
const subscribe = async ({
|
||||
app,
|
||||
larkService,
|
||||
logger,
|
||||
larkBody,
|
||||
larkCard,
|
||||
}: Context.Data) => {
|
||||
try {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 判断是否有 chatId 和 userId
|
||||
if (!larkBody.chatId || !larkBody.userId) {
|
||||
logger.error(`chatId or userId is empty`)
|
||||
return
|
||||
}
|
||||
// 先查询是否已经存在订阅
|
||||
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
||||
{
|
||||
where: {
|
||||
chat_id: larkBody.chatId,
|
||||
terminator: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
// 如果已经存在订阅,则返回已经注册过了
|
||||
if (subscription) {
|
||||
logger.info(`chatId: ${larkBody.chatId} has been registered`)
|
||||
// 发送已经注册过的消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.hasRegistered)
|
||||
)
|
||||
return
|
||||
}
|
||||
// 注册订阅
|
||||
await prisma.chat_agent_summary_subscription.create({
|
||||
data: {
|
||||
chat_id: larkBody.chatId,
|
||||
robot_id: app,
|
||||
initiator: larkBody.userId,
|
||||
},
|
||||
})
|
||||
// 发送成功消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.registerSuccess)
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error(`Subscribe error: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消消息总结的订阅
|
||||
* @returns
|
||||
*/
|
||||
const unsubscribe = async ({
|
||||
logger,
|
||||
larkBody,
|
||||
larkService,
|
||||
larkCard,
|
||||
}: Context.Data) => {
|
||||
try {
|
||||
const cardGender = larkCard.child("groupAgent")
|
||||
// 判断是否有 chatId 和 userId
|
||||
if (!larkBody.chatId || !larkBody.userId) {
|
||||
logger.error(`chatId or userId is empty`)
|
||||
return
|
||||
}
|
||||
// 查找现有的订阅
|
||||
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
||||
{
|
||||
where: {
|
||||
chat_id: larkBody.chatId,
|
||||
terminator: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
// 如果没有找到订阅,则返回错误
|
||||
if (!subscription) {
|
||||
logger.info(`chatId: ${larkBody.chatId} has not been registered`)
|
||||
// 发送已经取消订阅的消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
||||
)
|
||||
return
|
||||
}
|
||||
// 更新订阅,设置终止者和终止时间
|
||||
await prisma.chat_agent_summary_subscription.update({
|
||||
where: {
|
||||
id: subscription.id,
|
||||
},
|
||||
data: {
|
||||
terminator: larkBody.userId,
|
||||
},
|
||||
})
|
||||
// 发送成功消息
|
||||
await larkService.message.sendCard2Chat(
|
||||
larkBody.chatId,
|
||||
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error(`Unsubscribe error: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const subscription = {
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
}
|
||||
|
||||
export default subscription
|
@ -2,7 +2,7 @@ import groupAgent from "../../controller/groupAgent"
|
||||
import { Context } from "../../types"
|
||||
|
||||
const GROUP_MAP = {
|
||||
groupAgent,
|
||||
groupAgent: groupAgent.manual,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,5 @@
|
||||
import tempMap from "../../constant/template"
|
||||
import groupAgent from "../../controller/groupAgent"
|
||||
import report from "../../controller/groupAgent/report"
|
||||
import subscription from "../../controller/groupAgent/subscription"
|
||||
import { Context } from "../../types"
|
||||
import createKVTemp from "../sheet/createKVTemp"
|
||||
|
||||
@ -115,25 +113,25 @@ const manageCMDMsg = (ctx: Context.Data) => {
|
||||
// 注册群组
|
||||
if (msgText === "开启日报、周报") {
|
||||
logger.info(`bot command is register, chatId: ${chatId}`)
|
||||
subscription.subscribe(ctx)
|
||||
groupAgent.report.subscribe(ctx)
|
||||
return
|
||||
}
|
||||
// 注销群组
|
||||
if (msgText === "关闭日报、周报") {
|
||||
logger.info(`bot command is unregister, chatId: ${chatId}`)
|
||||
subscription.unsubscribe(ctx)
|
||||
groupAgent.report.unsubscribe(ctx)
|
||||
return
|
||||
}
|
||||
// 立即发送日简报
|
||||
if (msgText === "总结日报") {
|
||||
logger.info(`bot command is summary, chatId: ${chatId}`)
|
||||
report.gen4Test(ctx, "daily")
|
||||
groupAgent.report.gen4Test(ctx, "daily")
|
||||
return
|
||||
}
|
||||
// 立即发送周简报
|
||||
if (msgText === "总结周报") {
|
||||
logger.info(`bot command is summary, chatId: ${chatId}`)
|
||||
report.gen4Test(ctx, "weekly")
|
||||
groupAgent.report.gen4Test(ctx, "weekly")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -178,7 +176,7 @@ const manageCMDMsg = (ctx: Context.Data) => {
|
||||
// 选择群组信息
|
||||
if (msgText.startsWith("/g")) {
|
||||
logger.info(`bot command is /groupchat, chatId: ${chatId}`)
|
||||
groupAgent(ctx)
|
||||
groupAgent.manual(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user