feat(group-agent): 支持大模型语义化理解用户输入
All checks were successful
Egg Server CI/CD / build-image (push) Successful in 50s
Egg Server CI/CD / refresh-image (push) Successful in 12s
Egg Server CI/CD / fast-deploy (push) Successful in 3s

This commit is contained in:
zhaoyingbo 2024-10-16 12:20:25 +00:00
parent d75ca80cc3
commit d64d6211c0
13 changed files with 525 additions and 185 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,14 +1,44 @@
import { cardComponent } from "@egg/lark-msg-tool"
const functionSelector = {
const groupSelector = {
config: {
update_multi: true,
},
elements: [
{
tag: "markdown",
content: "已经选中的群聊:**${chatName}**\n",
content: "请选择要对话的群聊",
},
{
tag: "action",
actions: [
{
tag: "select_static",
placeholder: {
tag: "plain_text",
content: "请选择群名称",
},
value: {
cardGroup: "groupAgent",
cardName: "groupSelector",
},
options: "${groupOptions}",
},
],
},
{
tag: "hr",
},
cardComponent.commonNote,
],
header: cardComponent.pendingHeader,
}
const functionSelector = {
config: {
update_multi: true,
},
elements: [
{
tag: "action",
actions: [
@ -19,11 +49,10 @@ const functionSelector = {
content: "请选择功能",
},
value: {
chatId: "${chatId}",
chatName: "${chatName}",
action: "sendTimeScopeSelector",
cardGroup: "groupAgent",
cardName: "functionSelector",
},
options: "${functions}",
options: "${functionOptions}",
},
],
},
@ -42,8 +71,7 @@ const timeScopeSelector = {
elements: [
{
tag: "markdown",
content:
"已经选中群聊:**${chatName}**\n\n已经选择功能**${functionName}**\n\n请选择时间范围",
content: "请选择时间范围",
},
{
tag: "action",
@ -56,13 +84,9 @@ const timeScopeSelector = {
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
cardGroup: "groupAgent",
cardName: "timeScopeSelector",
timeScope: "1",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
@ -73,13 +97,9 @@ const timeScopeSelector = {
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
cardGroup: "groupAgent",
cardName: "timeScopeSelector",
timeScope: "3",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
@ -90,13 +110,9 @@ const timeScopeSelector = {
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
cardGroup: "groupAgent",
cardName: "timeScopeSelector",
timeScope: "7",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
],
@ -116,8 +132,7 @@ const resultReport = {
elements: [
{
tag: "markdown",
content:
"群聊:**${chatName}**,功能:**${functionName}**,总结天数:**${timeScope}**\n\n以下内容由AI模型生成耗时**${processingTime}s**",
content: "${content}",
},
{
tag: "hr",
@ -134,10 +149,18 @@ const resultReport = {
header: cardComponent.successHeader,
}
export const functionOptionList = [
{
id: "summary-qwen-72b-instruct-int4",
name: "总结消息",
},
]
const cardMap = {
functionSelector,
timeScopeSelector,
resultReport,
groupSelector,
}
export default cardMap

View File

@ -34,9 +34,9 @@
"typescript": "^5.5.4"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.19.2",
"@dotenvx/dotenvx": "^1.19.3",
"@egg/hooks": "^1.2.0",
"@egg/lark-msg-tool": "^1.9.0",
"@egg/lark-msg-tool": "^1.13.0",
"@egg/logger": "^1.4.4",
"@egg/net-tool": "^1.9.2",
"@egg/path-tool": "^1.4.1",

View File

@ -1,10 +1,8 @@
import { Context } from "../../types"
import groupAgent from "./groupAgent"
const ACTION_MAP = {
sendFunctionSelector: groupAgent.sendFunctionSelector,
sendTimeScopeSelector: groupAgent.sendTimeScopeSelector,
manageGroupMsg: groupAgent.manageGroupMsg,
const GROUP_MAP = {
groupAgent,
}
/**
@ -12,13 +10,16 @@ const ACTION_MAP = {
* @param {Context.Data} ctx - body, larkService和logger
*/
const manageAction = async (ctx: Context.Data) => {
const { body, logger } = ctx
const { action } = body?.action?.value as {
action: keyof typeof ACTION_MAP
const {
larkBody: { actionValue },
logger,
} = ctx
const { cardGroup } = actionValue as {
cardGroup: keyof typeof GROUP_MAP
}
logger.info(`Got lark action: ${action}`)
if (!action) return
const func = ACTION_MAP[action]
logger.info(`Got lark action cardGroup: ${cardGroup}`)
if (!cardGroup) return
const func = GROUP_MAP[cardGroup]
if (!func) return
return func(ctx)
}
@ -31,9 +32,11 @@ export const manageActionMsg = async (ctx: Context.Data) => {
const {
larkBody: { actionType },
} = ctx
// 只处理按钮和静态选择器
if (!["button", "select_static"].includes(actionType!))
return ctx.genResp.ok()
const card = await manageAction(ctx)
// 如果有返回卡片则返回卡片
if (card) return ctx.genResp.json(card)
return ctx.genResp.ok()
}

View File

@ -85,7 +85,7 @@ const manageIdMsg = ({
}: Context.Data): void => {
larkService.message.sendCard2Chat(
chatId,
larkCard.genTempCard("chatId", { chat_id: chatId }) as string
larkCard.genTempCard("chatId", { chat_id: chatId })
)
}
@ -156,9 +156,9 @@ const manageCMDMsg = (ctx: Context.Data) => {
return
}
// 选择群组信息
if (msgText.trim() === "/groupchat") {
if (msgText.trim().startsWith("/groupchat")) {
logger.info(`bot command is /groupchat, chatId: ${chatId}`)
groupAgent.sendGroupSelector(ctx)
groupAgent(ctx)
return
}
return

View File

@ -1,4 +1,5 @@
import { parseJsonString } from "@egg/hooks"
import { LarkEvent } from "@egg/lark-msg-tool"
import logger from "@egg/logger"
import { Context, LarkServer } from "../../../types"
@ -41,22 +42,48 @@ const extractTextFromJson = (data: any): string => {
* @param context -
* @returns
*/
const getChatHistory = async ({
larkService,
larkBody: {
actionValue: { chatId, timeScope },
},
}: Context.Data): Promise<Message[]> => {
// 获取历史消息timeScope为1、3、7分别代表1天、3天、7天
const getChatHistory = async (
{ larkService }: Context.Data,
{
chatId,
timeScope,
startTime,
endTime,
mentions: targetUsers,
}: {
chatId: string
timeScope?: string
startTime?: string
endTime?: string
mentions?: LarkEvent.Mention[]
}
): Promise<Message[]> => {
// 获取服务器的时区偏移量(以分钟为单位)
const serverTimezoneOffset = new Date().getTimezoneOffset()
// 上海时区的偏移量UTC+8以分钟为单位
const shanghaiTimezoneOffset = -8 * 60
// 计算时间戳,调整为上海时区
const endTimeTimestamp =
Math.round(new Date().getTime() / 1000) +
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
const startTimeTimestamp = endTimeTimestamp - Number(timeScope) * 24 * 60 * 60
let startTimeTimestamp: number
let endTimeTimestamp: number
if (startTime && endTime) {
// 将startTime和endTime转换为时间戳并调整为上海时区
startTimeTimestamp = Math.round(
new Date(startTime).getTime() / 1000 +
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
)
endTimeTimestamp = Math.round(
new Date(endTime).getTime() / 1000 +
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
)
} else {
// 计算时间戳,调整为上海时区
endTimeTimestamp =
Math.round(new Date().getTime() / 1000) +
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
startTimeTimestamp = endTimeTimestamp - Number(timeScope) * 24 * 60 * 60
}
// 获取群聊中的历史记录
const { data: chatHistory } = await larkService.message.getHistory(
chatId,
@ -65,6 +92,12 @@ const getChatHistory = async ({
)
if (chatHistory.length === 0) return []
const targetUsersSet = new Set(
targetUsers
?.filter?.((user) => user.id.user_id)
?.map?.((user) => user.id.open_id) ?? []
)
// 清洗数据
// 取出所有的被AT的人以及发送者
const mentions: Map<string, string> = new Map()
@ -80,12 +113,28 @@ const getChatHistory = async ({
mentions.set(mention.id, mention.name)
}
}
if (chat.sender && chat.sender.sender_type === "user") {
senders.add(chat.sender.id)
// 过滤掉不是文本和post消息的消息
if (!allowedMsgTypes.includes(chat.msg_type)) {
continue
}
if (allowedMsgTypes.includes(chat.msg_type)) {
filteredMsg.push(chat)
// 过滤掉不是用户发送的消息
if (!chat.sender || chat.sender.sender_type !== "user") {
continue
}
// 如果目标用户集合不为空,并且消息的发送者不在目标用户集合中,并且消息中提到的所有用户都不在目标用户集合中,则跳过该消息
if (
targetUsersSet.size !== 0 &&
!targetUsersSet.has(chat.sender.id) &&
(!chat.mentions ||
chat.mentions.every((mention) => !targetUsersSet.has(mention.id)))
) {
continue
}
senders.add(chat.sender.id)
filteredMsg.push(chat)
}
// 取出没有被AT的发送者

View File

@ -1,114 +1,117 @@
import { genCardOptions } from "@egg/lark-msg-tool"
import { genCardOptions, LarkEvent } from "@egg/lark-msg-tool"
import { Context } from "../../../types"
import { functionOptionList } from "../../../constant/card"
import { Context, LarkServer } from "../../../types"
import llm from "../../../utils/llm"
import getChatHistory from "./chatHistory"
/**
*
* @param ctx - body和larkService
*/
const sendGroupSelector = async ({
larkService,
logger,
larkCard,
larkBody: { chatId },
}: Context.Data) => {
const cardGender = larkCard.child("groupAgent")
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.sendCard2Chat(
chatId,
cardGender.genTempCard("groupSelector", { groups }) as string
)
}
/**
*
*
* @param ctx -
* @param innerList -
* @param commonVal -
*/
const sendFunctionSelector = async ({
logger,
larkCard,
larkBody: { actionOption },
}: Context.Data) => {
const cardGender = larkCard.child("groupAgent", false)
logger.debug(`Action option: ${JSON.stringify(actionOption)}`)
const [chatId, chatName] = (actionOption ?? "").split("|")
if (!chatId || !chatName) {
logger.error(
`Invalid targetChatId or targetChatName: ${JSON.stringify(actionOption)}`
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.genErrorCard("Invalid targetChatId or targetChatName")
}
return cardGender.genCard("functionSelector", {
functions: genCardOptions({
: "summary-qwen-72b-instruct-int4|总结消息",
}),
chatId,
chatName,
)
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 sendTimeScopeSelector = async ({
logger,
larkCard,
larkBody: { actionOption, actionValue },
}: Context.Data) => {
const cardGender = larkCard.child("groupAgent", false)
logger.debug(`Action option: ${JSON.stringify(actionOption)}`)
const [functionId, functionName] = (actionOption ?? "").split("|")
if (!functionId || !functionName) {
logger.error(
`Invalid functionId or functionName: ${JSON.stringify(actionOption)}`
)
return cardGender.genErrorCard("Invalid functionId or functionName")
}
logger.debug(`Action value: ${JSON.stringify(actionValue)}`)
const { chatId, chatName } = actionValue
if (!chatName || !chatId) {
logger.error(`Invalid chatName or chatId: ${JSON.stringify(actionValue)}`)
return cardGender.genErrorCard("Invalid chatName or chatId")
}
return cardGender.genCard("timeScopeSelector", {
const genTimeScopeSelector = async (
{ 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,
})
}
const sendGroupReport = async (ctx: Context.Data) => {
const {
larkService,
logger,
requestId,
larkCard,
larkBody: { actionValue, messageId },
} = ctx
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 } = ctx
const cardGender = larkCard.child("groupAgent")
const { chatName, functionId, functionName, timeScope } = actionValue
const updateCard = (content: any) =>
larkService.message.update(messageId, content)
// action需要返回loading的消息event需要主动update卡片所以loading就放外边了
// 记录发送loading消息后的时间戳
const startTime = Date.now()
const processStart = Date.now()
// 获取聊天记录
const chatHistory = await getChatHistory(ctx)
const chatHistory = await getChatHistory(ctx, {
chatId,
timeScope,
startTime,
endTime,
mentions,
})
// 如果没有历史记录则返回错误消息
if (chatHistory.length === 0) {
logger.error("Chat history is empty")
await larkService.message.update(
messageId,
cardGender.genErrorCard("未找到聊天记录")
)
await updateCard(cardGender.genErrorCard("未找到聊天记录"))
return
}
logger.debug(`Chat history: ${JSON.stringify(chatHistory)}`)
@ -119,20 +122,30 @@ const sendGroupReport = async (ctx: Context.Data) => {
time: new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
})
// 记录大模型返回结果后的时间戳
const endTime = Date.now()
const processEnd = Date.now()
// 计算时间差并存储在processingTime变量中以秒为单位
const processingTime = ((endTime - startTime) / 1000).toFixed(2)
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", {
chatName,
functionName,
content,
llmRes,
timeScope,
requestId,
processingTime,
})
)
} catch (error: any) {
@ -145,37 +158,228 @@ const sendGroupReport = async (ctx: Context.Data) => {
}
/**
*
* @param ctx - body和larkService
*
* @param ctx -
* @param innerList -
*/
const manageGroupMsg = async (ctx: Context.Data) => {
const parseGroupAgentQuery = async (
ctx: Context.Data,
innerList: LarkServer.ChatGroupData[],
mentions?: LarkEvent.Mention[]
) => {
const {
logger,
larkBody: { msgText, chatId: rawChatId },
larkService,
larkCard,
larkBody: { actionValue },
} = ctx
const cardGender = larkCard.child("groupAgent")
const cardGender = larkCard.child("groupAgent", false)
// 发送一个loading的消息
const {
data: { message_id },
} = await larkService.message.sendCard2Chat(
rawChatId,
cardGender.genPendingCard("分析中,请稍等...")
)
logger.debug(`Action value: ${JSON.stringify(actionValue)}`)
const updateCard = (content: any) =>
larkService.message.update(message_id, content)
const { chatId, chatName, functionId, functionName, timeScope } = actionValue
if (!chatId || !chatName || !functionId || !functionName || !timeScope) {
logger.error(`Invalid value: ${JSON.stringify(actionValue)}`)
return cardGender.genErrorCard("Invalid value")
// 组织群组数据
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)
// 判断顺序是 群组 -> 功能 -> 时间范围
// 返回群组选择器,其他的值往里边丢就行
if (!chatId || !chatName) {
updateCard(
genGroupSelector(ctx, innerList, {
functionName,
functionId,
startTime,
endTime,
mentions,
})
)
return
}
// 返回功能选择器,其他的值往里边丢就行
if (!functionId || !functionName) {
updateCard(
genFunctionSelector(ctx, {
chatId,
chatName,
startTime,
endTime,
mentions,
})
)
return
}
// 返回时间范围选择器,其他的值往里边丢就行
if (!startTime || !endTime) {
updateCard(
genTimeScopeSelector(ctx, {
chatId,
chatName,
functionId,
functionName,
mentions,
})
)
return
}
// 处理群组消息
sendGroupReport(ctx)
// 发送一个loading的消息
// 设置齐全,返回结果报告
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")
sendCard(genGroupSelector(ctx, innerList, { mentions }))
return
}
// 如果是群聊,获取群聊名称并发送功能
const {
data: { name: chatName },
} = await larkService.chat.getChatInfo(rawChatId)
logger.info(`Send function selector to group chat: ${chatName}`)
sendCard(genFunctionSelector(ctx, { chatName, mentions }))
return
}
// 用户有输入,使用大模型进行解析发送对应卡片
await parseGroupAgentQuery(ctx, innerList, mentions)
return
}
/**
* Action消息
* @param ctx -
*/
const manageActionMsg = async (ctx: Context.Data) => {
const {
larkBody: { actionOption, actionValue, messageId },
logger,
} = ctx
const cardGender = ctx.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) {
return genFunctionSelector(ctx, {
chatId,
chatName,
startTime,
endTime,
timeScope,
mentions,
})
}
// 判断是否需要返回时间范围选择器
if (!timeScope || !startTime || !endTime) {
return genTimeScopeSelector(ctx, {
chatId,
chatName,
functionId,
functionName,
mentions,
})
}
// 设置齐全,返回结果报告
sendGroupReport(ctx, messageId, {
chatId,
chatName,
functionId,
functionName,
timeScope,
startTime,
endTime,
mentions,
})
return cardGender.genPendingCard("正在爬楼中,请稍等...")
}
const groupAgent = {
sendGroupSelector,
sendFunctionSelector,
sendTimeScopeSelector,
manageGroupMsg,
/**
* Agent的主入口
* @param ctx -
*/
const groupAgent = async (ctx: Context.Data) => {
const {
larkBody: { isEventMsg, isActionMsg },
} = ctx
// 如果是Event则解析自然语言并发送对应的卡片
if (isEventMsg) return manageEventMsg(ctx)
// 如果是Action则取出用户选的值并判断是否需要继续发送表单卡片或者开始大模型推理
if (isActionMsg) return manageActionMsg(ctx)
}
export default groupAgent

View File

@ -28,6 +28,15 @@ class LarkChatService extends LarkBaseService {
message: "ok",
}
}
/**
*
* @param chatId ID
*/
async getChatInfo(chatId: string) {
const path = `/im/v1/chats/${chatId}`
return this.get<LarkServer.BaseRes<LarkServer.ChatGroupData>>(path)
}
}
export default LarkChatService

View File

@ -13,9 +13,12 @@ class LarkMessageService extends LarkBaseService {
receiveIdType: LarkServer.ReceiveIDType,
receiveId: string,
msgType: LarkServer.MsgType,
content: string
content: string | Record<string, any>
) {
const path = `/im/v1/messages?receive_id_type=${receiveIdType}`
if (typeof content === "object") {
content = JSON.stringify(content)
}
if (msgType === "text" && !content.includes('"text"')) {
content = JSON.stringify({ text: content })
}
@ -31,7 +34,10 @@ class LarkMessageService extends LarkBaseService {
* @param receiveId IDID类型应与查询参数receiveIdType
* @param content
*/
async sendCard2Chat(receiveId: string, content: string) {
async sendCard2Chat(
receiveId: string,
content: string | Record<string, any>
) {
return this.send("chat_id", receiveId, "interactive", content)
}
@ -49,8 +55,11 @@ class LarkMessageService extends LarkBaseService {
* @param messageId id
* @param content JSON结构序列化后的字符串msgType对应不同内容
*/
async update(messageId: string, content: string) {
async update(messageId: string, content: string | Record<string, any>) {
const path = `/im/v1/messages/${messageId}`
if (typeof content === "object") {
content = JSON.stringify(content)
}
return this.patch<LarkServer.BaseRes>(path, { content })
}

5
test/agent.http Normal file
View File

@ -0,0 +1,5 @@
POST http://localhost:3000/bot?app=egg
Content-Type: application/json
{"schema":"2.0","header":{"event_id":"c0aed3b0911e18e8b941746256e8d4ce","token":"tV9djUKSjzVnekV7xTg2Od06NFTcsBnj","create_time":"1728979404867","event_type":"im.message.receive_v1","tenant_key":"2ee61fe50f4f1657","app_id":"cli_a1eff35b43b89063"},"event":{"message":{"chat_id":"oc_8c789ce8f4ecc6695bb63ca6ec4c61ea","chat_type":"group","content":"{\"text\":\"@_user_1 /groupchat @_user_2 你好\"}","create_time":"1728979404353","mentions":[{"id":{"open_id":"ou_032f507d08f9a7f28b042fcd086daef5","union_id":"on_7111660fddd8302ce47bf1999147c011","user_id":""},"key":"@_user_1","name":"小煎蛋","tenant_key":"2ee61fe50f4f1657"},{"id":{"open_id":"ou_470ac13b8b50fc472d9d8ee71e03de26","union_id":"on_9dacc59a539023df8b168492f5e5433c","user_id":"zhaoyingbo"},"key":"@_user_2","name":"赵英博","tenant_key":"2ee61fe50f4f1657"}],"message_id":"om_17492a15fdc15b3a0fda31352dae49dc","message_type":"text"},"sender":{"sender_id":{"open_id":"ou_470ac13b8b50fc472d9d8ee71e03de26","union_id":"on_9dacc59a539023df8b168492f5e5433c","user_id":"zhaoyingbo"},"sender_type":"user","tenant_key":"2ee61fe50f4f1657"}}}

View File

@ -0,0 +1,23 @@
import llm from "../utils/llm"
const groupInfo = JSON.stringify([
{
id: "oc_ef98c2a9229657f99d4ef573a30fe91c",
name: "MIAI-FE 人工智能部-前端组",
},
{
id: "oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1",
name: "方糖の家",
},
])
const functionInfo = JSON.stringify([
{
id: "summary-qwen-72b-instruct-int4",
name: "总结消息",
},
])
const userInput = "方糖说了什么"
llm.parseGroupAgentQuery(userInput, groupInfo, functionInfo).then(console.log)

View File

@ -49,7 +49,7 @@ const genContext = async (req: Request) => {
const path = new PathCheckTool(req.url)
const larkCard = new LarkCard(
"egg",
true,
false,
requestId,
cardMap,
tempMap,

View File

@ -60,24 +60,39 @@ const getModel = async (modelName: keyof typeof modelMap, temperature = 0) => {
)
}
const timeConfig = z.object({
const groupAgentConfig = z.object({
chatId: z.string().describe("群聊ID"),
chatName: z.string().describe("群聊名称"),
functionId: z.string().describe("功能ID"),
functionName: z.string().describe("功能名称"),
startTime: z.string().describe("开始时间,格式为 YYYY-MM-DD HH:mm:ss"),
endTime: z.string().describe("结束时间,格式为 YYYY-MM-DD HH:mm:ss"),
})
/**
*
* GroupAgent用户输入
* @param userInput
* @param groupInfo
* @param functionInfo
* @returns
*/
const parseTime = async (userInput: string) => {
const model = await getModel("deepseek-chat")
const structuredLlm = model.withStructuredOutput(timeConfig, { name: "time" })
const parseGroupAgentQuery = async (
userInput: string,
groupInfo: string,
functionInfo: string
) => {
const model = await getModel("gpt-4o")
const structuredLlm = model.withStructuredOutput(groupAgentConfig, {
name: "groupAgent",
})
return await structuredLlm.invoke(
`
${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
${groupInfo}
${functionInfo}
IDID
\`\`\`
${userInput.replaceAll("`", " ")}
@ -120,7 +135,7 @@ const invoke = async (
}
const llm = {
parseTime,
parseGroupAgentQuery,
invoke,
}