feat(group-agent): 新增支持群组问答
Some checks failed
Egg Server MIflow / build-image (push) Failing after 5m7s
Some checks failed
Egg Server MIflow / build-image (push) Failing after 5m7s
This commit is contained in:
parent
88ccafed92
commit
b992ee0b21
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -5,14 +5,17 @@
|
||||
"Chakroun",
|
||||
"commitlint",
|
||||
"dbaeumer",
|
||||
"deepseek",
|
||||
"devcontainer",
|
||||
"devcontainers",
|
||||
"eamodio",
|
||||
"esbenp",
|
||||
"Gruntfuggly",
|
||||
"langchain",
|
||||
"metas",
|
||||
"mina",
|
||||
"mindnote",
|
||||
"openai",
|
||||
"openchat",
|
||||
"tseslint",
|
||||
"userid",
|
||||
|
@ -2,13 +2,14 @@ FROM micr.cloud.mioffice.cn/zhaoyingbo/bun:alpine-cn
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
# COPY package*.json ./
|
||||
|
||||
COPY bun.lockb ./
|
||||
# COPY bun.lockb ./
|
||||
|
||||
COPY .npmrc ./
|
||||
# COPY .npmrc ./
|
||||
|
||||
RUN bun install
|
||||
|
||||
# RUN bun install
|
||||
|
||||
COPY . .
|
||||
|
||||
|
40
db/appConfig/index.ts
Normal file
40
db/appConfig/index.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { RecordModel } from "pocketbase"
|
||||
|
||||
import { managePb404 } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
interface AppConfigRecordModel extends RecordModel {
|
||||
value: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
const get = async (key: string) => {
|
||||
const config = await managePb404<AppConfigRecordModel>(() =>
|
||||
pbClient.collection("config").getFirstListItem(`key='${key}'`)
|
||||
)
|
||||
if (!config) return ""
|
||||
return config.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Deepseek的apiKey
|
||||
* @returns {string} ak
|
||||
*/
|
||||
const getDeepseekApiKey = async () => get("deepseek_api_key")
|
||||
|
||||
/**
|
||||
* 获取OpenAI的key
|
||||
* @returns {string} ak
|
||||
*/
|
||||
const getOpenAIApiKey = async () => get("openai_api_key")
|
||||
|
||||
const appConfig = {
|
||||
getOpenAIApiKey,
|
||||
getDeepseekApiKey,
|
||||
}
|
||||
|
||||
export default appConfig
|
27
db/groupAgentConfig/index.ts
Normal file
27
db/groupAgentConfig/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { DB } from "../../types"
|
||||
import { managePb404 } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
const get = async (userId: string) =>
|
||||
managePb404<DB.GroupAgentConfig>(() =>
|
||||
pbClient
|
||||
.collection("group_agent_config")
|
||||
.getFirstListItem(`user_id='${userId}'`)
|
||||
)
|
||||
|
||||
const upsert = async (data: Partial<DB.GroupAgentConfig>) => {
|
||||
const { user_id } = data
|
||||
const old = await get(user_id!)
|
||||
if (old) {
|
||||
await pbClient.collection("group_agent_config").update(old.id, data)
|
||||
return old.id
|
||||
}
|
||||
return pbClient.collection("group_agent_config").create(data)
|
||||
}
|
||||
|
||||
const groupAgentConfig = {
|
||||
get,
|
||||
upsert,
|
||||
}
|
||||
|
||||
export default groupAgentConfig
|
@ -1,5 +1,7 @@
|
||||
import apiKey from "./apiKey"
|
||||
import appConfig from "./appConfig"
|
||||
import appInfo from "./appInfo"
|
||||
import groupAgentConfig from "./groupAgentConfig"
|
||||
import log from "./log"
|
||||
import messageGroup from "./messageGroup"
|
||||
import tenantAccessToken from "./tenantAccessToken"
|
||||
@ -10,6 +12,8 @@ const db = {
|
||||
messageGroup,
|
||||
log,
|
||||
tenantAccessToken,
|
||||
groupAgentConfig,
|
||||
appConfig,
|
||||
}
|
||||
|
||||
export default db
|
||||
|
@ -36,9 +36,10 @@
|
||||
"dependencies": {
|
||||
"@egg/hooks": "^1.2.0",
|
||||
"@egg/lark-msg-tool": "^1.2.1",
|
||||
"@egg/logger": "^1.4.2",
|
||||
"@egg/net-tool": "^1.6.3",
|
||||
"@egg/logger": "^1.4.3",
|
||||
"@egg/net-tool": "^1.6.5",
|
||||
"@egg/path-tool": "^1.3.0",
|
||||
"@langchain/openai": "^0.3.0",
|
||||
"joi": "^17.13.3",
|
||||
"node-schedule": "^2.1.1",
|
||||
"p-limit": "^6.1.0",
|
||||
|
@ -1,15 +1,15 @@
|
||||
import type { LarkAction } from "@egg/lark-msg-tool"
|
||||
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: LarkAction.Data): Promise<string> => {
|
||||
const makeChatIdCard = async ({ body }: Context.Data): Promise<string> => {
|
||||
await sleep(500)
|
||||
return JSON.stringify({
|
||||
type: "template",
|
||||
@ -27,6 +27,7 @@ const makeChatIdCard = async (body: LarkAction.Data): Promise<string> => {
|
||||
|
||||
const ACTION_MAP = {
|
||||
chat_id: makeChatIdCard,
|
||||
group_selector: groupAgent.setChatGroupContext,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,11 +35,8 @@ const ACTION_MAP = {
|
||||
* @param {Context.Data} ctx - 上下文数据,包含body, larkService和logger
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
const manageBtnClick = async ({
|
||||
body,
|
||||
larkService,
|
||||
logger,
|
||||
}: Context.Data): Promise<void> => {
|
||||
const manageBtnClick = async (ctx: Context.Data): Promise<void> => {
|
||||
const { body, larkService, logger } = ctx
|
||||
const { action } = body?.action?.value as {
|
||||
action: keyof typeof ACTION_MAP
|
||||
}
|
||||
@ -46,7 +44,7 @@ const manageBtnClick = async ({
|
||||
if (!action) return
|
||||
const func = ACTION_MAP[action]
|
||||
if (!func) return
|
||||
const card = await func(body)
|
||||
const card = await func(ctx)
|
||||
if (!card) return
|
||||
// 更新飞书的卡片
|
||||
await larkService.message.update(body.open_message_id, card)
|
||||
@ -64,5 +62,6 @@ export const manageActionMsg = (ctx: Context.Data): boolean => {
|
||||
}
|
||||
const actionType = getActionType(ctx.body)
|
||||
if (actionType === "button") manageBtnClick(ctx)
|
||||
if (actionType === "select_static") manageBtnClick(ctx)
|
||||
return true
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import { LarkService } from "../../services"
|
||||
import { Context } from "../../types"
|
||||
import createKVTemp from "../sheet/createKVTemp"
|
||||
import groupAgent from "./groupAgent"
|
||||
|
||||
/**
|
||||
* 是否为P2P或者群聊并且艾特了小煎蛋
|
||||
@ -37,12 +38,17 @@ const filterIllegalMsg = ({
|
||||
}: Context.Data): boolean => {
|
||||
// 没有chatId的消息不处理
|
||||
const chatId = getChatId(body)
|
||||
logger.debug(`bot req chatId: ${chatId}`)
|
||||
logger.info(`bot req chatId: ${chatId}`)
|
||||
if (!chatId) return true
|
||||
|
||||
// 非私聊和群聊中艾特小煎蛋的消息不处理
|
||||
if (!getIsP2pOrGroupAtBot(body)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取msgType
|
||||
const msgType = getMsgType(body)
|
||||
logger.debug(`bot req msgType: ${msgType}`)
|
||||
logger.info(`bot req msgType: ${msgType}`)
|
||||
// 放行纯文本消息
|
||||
if (msgType === "text") {
|
||||
// 过滤艾特全体成员的消息
|
||||
@ -79,19 +85,19 @@ const filterIllegalMsg = ({
|
||||
* @param {LarkService} service - Lark服务实例
|
||||
*/
|
||||
const manageIdMsg = (chatId: string, service: LarkService): void => {
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
service.message.sendTemp("chat_id", chatId, "ctp_AAi3NnHb6zgK", {
|
||||
chat_id: chatId,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复引导消息
|
||||
* @param {Context.Data} ctx - 上下文数据,包含body, larkService和logger
|
||||
*/
|
||||
const manageHelpMsg = (chatId: string, service: LarkService): void => {
|
||||
service.message.sendTemp("chat_id", chatId, "ctp_AAyVx5R39xU9", {
|
||||
chat_id: chatId,
|
||||
})
|
||||
service.message.send("chat_id", chatId, "interactive", content)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,15 +108,20 @@ const manageIdMsg = (chatId: string, service: LarkService): void => {
|
||||
const manageCMDMsg = (ctx: Context.Data): boolean => {
|
||||
const { body, logger, larkService, attachService } = ctx
|
||||
const text = getMsgText(body)
|
||||
logger.debug(`bot req text: ${text}`)
|
||||
logger.info(`bot req text: ${text}`)
|
||||
const chatId = getChatId(body)
|
||||
if (!chatId) return false
|
||||
// 处理命令消息
|
||||
if (text.trim() === "/id") {
|
||||
logger.info(`bot command is /id, chatId: ${chatId}`)
|
||||
manageIdMsg(chatId, larkService)
|
||||
return true
|
||||
}
|
||||
// 帮助
|
||||
if (text.trim() === "/help") {
|
||||
logger.info(`bot command is /help, chatId: ${chatId}`)
|
||||
manageHelpMsg(chatId, larkService)
|
||||
return true
|
||||
}
|
||||
// CI监控
|
||||
if (text.trim() === "/ci") {
|
||||
logger.info(`bot command is /ci, chatId: ${chatId}`)
|
||||
@ -136,33 +147,21 @@ const manageCMDMsg = (ctx: Context.Data): boolean => {
|
||||
createKVTemp.createFromEvent(ctx)
|
||||
return true
|
||||
}
|
||||
// 选择群组信息
|
||||
if (text.trim() === "/sg") {
|
||||
logger.info(`bot command is /sg, 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复引导消息
|
||||
* @param {Context.Data} ctx - 上下文数据,包含body, larkService和logger
|
||||
*/
|
||||
const replyGuideMsg = ({ body, larkService, logger }: Context.Data): void => {
|
||||
const chatId = getChatId(body)
|
||||
logger.info(`reply guide message, chatId: ${chatId}`)
|
||||
if (!chatId) return
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
enable_forward: false,
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAyVx5R39xU9",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
})
|
||||
larkService.message.send("chat_id", chatId, "interactive", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Event消息
|
||||
* @param {Context.Data} ctx - 上下文数据
|
||||
@ -181,7 +180,7 @@ export const manageEventMsg = (ctx: Context.Data): boolean => {
|
||||
if (manageCMDMsg(ctx)) {
|
||||
return true
|
||||
}
|
||||
// 返回引导消息
|
||||
replyGuideMsg(ctx)
|
||||
// 群组消息处理
|
||||
groupAgent.manageGroupMsg(ctx)
|
||||
return true
|
||||
}
|
||||
|
74
routes/bot/groupAgent/groupManager.ts
Normal file
74
routes/bot/groupAgent/groupManager.ts
Normal file
@ -0,0 +1,74 @@
|
||||
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
|
118
routes/bot/groupAgent/index.ts
Normal file
118
routes/bot/groupAgent/index.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import { getChatId, getMsgText, LarkEvent } from "@egg/lark-msg-tool"
|
||||
|
||||
import db from "../../../db"
|
||||
import { Context } from "../../../types"
|
||||
import {
|
||||
genGroupAgentErrorMsg,
|
||||
genGroupAgentSuccessMsg,
|
||||
} from "../../../utils/genMsg"
|
||||
import llm from "../../../utils/llm"
|
||||
import groupManager from "./groupManager"
|
||||
|
||||
/**
|
||||
* 根据聊天历史回答用户的问题
|
||||
* @param ctx - 上下文数据
|
||||
* @param userInput - 用户输入
|
||||
* @param targetChatId - 目标群组ID
|
||||
* @param loadingMsgId - loading消息ID
|
||||
*/
|
||||
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 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) +
|
||||
(shanghaiTimezoneOffset - serverTimezoneOffset) * 60
|
||||
|
||||
// 获取群聊中的历史记录
|
||||
const { data: chatHistory } = await ctx.larkService.message.getHistory(
|
||||
targetChatId,
|
||||
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
|
||||
)
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组代理处理消息
|
||||
* @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,
|
||||
manageGroupMsg,
|
||||
}
|
||||
|
||||
export default groupAgent
|
@ -1,7 +1,7 @@
|
||||
import { getChatId, getChatType, getUserId } from "@egg/lark-msg-tool"
|
||||
|
||||
import { Context, LarkServer } from "../../types"
|
||||
import { genSheetDbErrorMsg, genTempMsg } from "../../utils/genMsg"
|
||||
import { genSheetDbErrorMsg } from "../../utils/genMsg"
|
||||
|
||||
/**
|
||||
* 创建键值多维表格
|
||||
@ -93,8 +93,12 @@ const createFromEvent = async (ctx: Context.Data) => {
|
||||
if (addRes.code !== 0) throw new Error(addRes.message)
|
||||
}
|
||||
// 全部成功,发送成功消息
|
||||
const successMsg = genTempMsg("ctp_AA00oqPWPXtG", createRes.data)
|
||||
ctx.larkService.message.send("chat_id", chatId, "interactive", successMsg)
|
||||
ctx.larkService.message.sendTemp(
|
||||
"chat_id",
|
||||
chatId,
|
||||
"ctp_AA00oqPWPXtG",
|
||||
createRes.data
|
||||
)
|
||||
} catch (e: any) {
|
||||
ctx.logger.error(`create KV bitable failed: ${e.message}`)
|
||||
const errorMsg = genSheetDbErrorMsg(e.message)
|
||||
|
@ -1,12 +1,12 @@
|
||||
import LarkBaseService from "./base"
|
||||
|
||||
class LarkAuthService extends LarkBaseService {
|
||||
getAk(app_id: string, app_secret: string) {
|
||||
getAk(appId: string, appSecret: string) {
|
||||
return this.post<{ tenant_access_token: string; code: number }>(
|
||||
"/auth/v3/tenant_access_token/internal",
|
||||
{
|
||||
app_id,
|
||||
app_secret,
|
||||
app_id: appId,
|
||||
app_secret: appSecret,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class LarkBaseService extends NetToolBase {
|
||||
data: error.data,
|
||||
message: error.message,
|
||||
} as T
|
||||
this.logger.error("larkNetTool catch error: ", JSON.stringify(res))
|
||||
this.logger.error(`larkNetTool catch error: ${JSON.stringify(res)}`)
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
33
services/lark/chat.ts
Normal file
33
services/lark/chat.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { LarkServer } from "../../types"
|
||||
import LarkBaseService from "./base"
|
||||
|
||||
class LarkChatService extends LarkBaseService {
|
||||
/**
|
||||
* 获取机器人所在群列表
|
||||
*/
|
||||
async getInnerList() {
|
||||
const path = "/im/v1/chats"
|
||||
const chatList = []
|
||||
let hasMore = true
|
||||
let pageToken = ""
|
||||
while (hasMore) {
|
||||
const { data, code } = await this.get<
|
||||
LarkServer.BaseListRes<LarkServer.ChatGroupData>
|
||||
>(path, {
|
||||
page_size: 100,
|
||||
page_token: pageToken,
|
||||
})
|
||||
if (code !== 0) break
|
||||
chatList.push(...data.items)
|
||||
hasMore = data.has_more
|
||||
pageToken = data.page_token
|
||||
}
|
||||
return {
|
||||
code: 0,
|
||||
data: chatList,
|
||||
message: "ok",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default LarkChatService
|
@ -6,14 +6,14 @@ class LarkDriveService extends LarkBaseService {
|
||||
* 批量获取文档元数据。
|
||||
*
|
||||
* @param docTokens - 文档令牌数组。
|
||||
* @param doc_type - 文档类型,默认为 "doc"。
|
||||
* @param user_id_type - 用户ID类型,默认为 "user_id"。
|
||||
* @param docType - 文档类型,默认为 "doc"。
|
||||
* @param userIdType - 用户ID类型,默认为 "user_id"。
|
||||
* @returns 包含元数据和失败列表的响应对象。
|
||||
*/
|
||||
async batchGetMeta(
|
||||
docTokens: string[],
|
||||
doc_type = "doc",
|
||||
user_id_type = "user_id"
|
||||
docType = "doc",
|
||||
userIdType = "user_id"
|
||||
) {
|
||||
const path = "/drive/v1/metas/batch_query"
|
||||
// 如果docTokens长度超出150,需要分批请求
|
||||
@ -27,11 +27,11 @@ class LarkDriveService extends LarkBaseService {
|
||||
const data = {
|
||||
request_docs: docTokensSlice.map((id) => ({
|
||||
doc_token: id,
|
||||
doc_type,
|
||||
doc_type: docType,
|
||||
})),
|
||||
}
|
||||
return this.post<LarkServer.BatchDocMetaRes>(path, data, {
|
||||
user_id_type,
|
||||
user_id_type: userIdType,
|
||||
})
|
||||
}
|
||||
)
|
||||
@ -40,7 +40,7 @@ class LarkDriveService extends LarkBaseService {
|
||||
return res.data?.metas || []
|
||||
})
|
||||
|
||||
const failed_list = responses.flatMap((res) => {
|
||||
const failedList = responses.flatMap((res) => {
|
||||
return res.data?.failed_list || []
|
||||
})
|
||||
|
||||
@ -48,7 +48,7 @@ class LarkDriveService extends LarkBaseService {
|
||||
code: 0,
|
||||
data: {
|
||||
metas,
|
||||
failed_list,
|
||||
failedList,
|
||||
},
|
||||
message: "success",
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import LarkAuthService from "./auth"
|
||||
import LarkChatService from "./chat"
|
||||
import LarkDriveService from "./drive"
|
||||
import LarkMessageService from "./message"
|
||||
import LarkSheetService from "./sheet"
|
||||
@ -10,6 +11,7 @@ class LarkService {
|
||||
user: LarkUserService
|
||||
sheet: LarkSheetService
|
||||
auth: LarkAuthService
|
||||
chat: LarkChatService
|
||||
requestId: string
|
||||
|
||||
constructor(appName: string, requestId: string) {
|
||||
@ -18,6 +20,7 @@ class LarkService {
|
||||
this.user = new LarkUserService(appName, requestId)
|
||||
this.sheet = new LarkSheetService(appName, requestId)
|
||||
this.auth = new LarkAuthService(appName, requestId)
|
||||
this.chat = new LarkChatService(appName, requestId)
|
||||
this.requestId = requestId
|
||||
}
|
||||
|
||||
|
@ -1,40 +1,114 @@
|
||||
import { LarkServer } from "../../types/larkServer"
|
||||
import { genTempMsg } from "../../utils/genMsg"
|
||||
import LarkBaseService from "./base"
|
||||
|
||||
class LarkMessageService extends LarkBaseService {
|
||||
/**
|
||||
* 发送卡片
|
||||
* @param {LarkServer.ReceiveIDType} receive_id_type 消息接收者id类型 open_id/user_id/union_id/email/chat_id
|
||||
* @param {string} receive_id 消息接收者的ID,ID类型应与查询参数receive_id_type 对应
|
||||
* @param {MsgType} msg_type 消息类型 包括:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
|
||||
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
||||
* @param receiveIdType 消息接收者id类型 open_id/user_id/union_id/email/chat_id
|
||||
* @param receiveId 消息接收者的ID,ID类型应与查询参数receiveIdType 对应
|
||||
* @param msgType 消息类型 包括:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
|
||||
* @param content 消息内容,JSON结构序列化后的字符串。不同msgType对应不同内容
|
||||
*/
|
||||
async send(
|
||||
receive_id_type: LarkServer.ReceiveIDType,
|
||||
receive_id: string,
|
||||
msg_type: LarkServer.MsgType,
|
||||
receiveIdType: LarkServer.ReceiveIDType,
|
||||
receiveId: string,
|
||||
msgType: LarkServer.MsgType,
|
||||
content: string
|
||||
) {
|
||||
const path = `/im/v1/messages?receive_id_type=${receive_id_type}`
|
||||
if (msg_type === "text" && !content.includes('"text"')) {
|
||||
const path = `/im/v1/messages?receive_id_type=${receiveIdType}`
|
||||
if (msgType === "text" && !content.includes('"text"')) {
|
||||
content = JSON.stringify({ text: content })
|
||||
}
|
||||
return this.post<LarkServer.BaseRes>(path, {
|
||||
receive_id,
|
||||
msg_type,
|
||||
return this.post<LarkServer.BaseRes<{ message_id: string }>>(path, {
|
||||
receive_id: receiveId,
|
||||
msg_type: msgType,
|
||||
content,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新卡片
|
||||
* @param {string} message_id 消息id
|
||||
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
||||
* 发送模板消息
|
||||
* @param receiveIdType 消息接收者id类型 open_id/user_id/union_id/email/chat_id
|
||||
* @param receiveId 消息接收者的ID,ID类型应与查询参数receiveIdType 对应
|
||||
* @param templateId 模板ID
|
||||
* @param variable 模板变量
|
||||
*/
|
||||
async update(message_id: string, content: string) {
|
||||
const path = `/im/v1/messages/${message_id}`
|
||||
async sendTemp(
|
||||
receiveIdType: LarkServer.ReceiveIDType,
|
||||
receiveId: string,
|
||||
templateId: string,
|
||||
variable: any
|
||||
) {
|
||||
return this.send(
|
||||
receiveIdType,
|
||||
receiveId,
|
||||
"interactive",
|
||||
genTempMsg(templateId, variable)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送富文本信息
|
||||
* @param receiveId 消息接收者的ID,ID类型应与查询参数receiveIdType 对应
|
||||
* @param content 消息内容
|
||||
*/
|
||||
async sendInteractive2Chat(receiveId: string, content: string) {
|
||||
return this.send("chat_id", receiveId, "interactive", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文本信息
|
||||
* @param receiveId 消息接收者的ID,ID类型应与查询参数receiveIdType 对应
|
||||
* @param content 消息内容
|
||||
*/
|
||||
async sendText2Chat(receiveId: string, content: string) {
|
||||
return this.send("chat_id", receiveId, "text", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新卡片
|
||||
* @param messageId 消息id
|
||||
* @param content 消息内容,JSON结构序列化后的字符串。不同msgType对应不同内容
|
||||
*/
|
||||
async update(messageId: string, content: string) {
|
||||
const path = `/im/v1/messages/${messageId}`
|
||||
return this.patch<LarkServer.BaseRes>(path, { content })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息历史记录
|
||||
* @param chatId 会话ID
|
||||
* @param startTime 开始时间 秒级时间戳
|
||||
* @param endTime 结束时间 秒级时间戳
|
||||
*/
|
||||
async getHistory(chatId: string, startTime: string, endTime: string) {
|
||||
const path = `/im/v1/messages`
|
||||
const messageList = [] as LarkServer.MessageData[]
|
||||
let hasMore = true
|
||||
let pageToken = ""
|
||||
while (hasMore) {
|
||||
const { code, data } = await this.get<
|
||||
LarkServer.BaseListRes<LarkServer.MessageData>
|
||||
>(path, {
|
||||
container_id_type: "chat",
|
||||
container_id: chatId,
|
||||
start_time: startTime,
|
||||
end_time: endTime,
|
||||
page_size: 50,
|
||||
page_token: pageToken,
|
||||
})
|
||||
if (code !== 0) break
|
||||
messageList.push(...data.items)
|
||||
hasMore = data.has_more
|
||||
pageToken = data.page_token
|
||||
}
|
||||
return {
|
||||
code: 0,
|
||||
data: messageList,
|
||||
message: "ok",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default LarkMessageService
|
||||
|
@ -4,10 +4,10 @@ import LarkBaseService from "./base"
|
||||
class LarkSheetService extends LarkBaseService {
|
||||
/**
|
||||
* 向电子表格中插入行。
|
||||
* @param {string} sheetToken - 表格令牌。
|
||||
* @param {string} range - 插入数据的范围。
|
||||
* @param {string[][]} values - 要插入的值。
|
||||
* @returns {Promise<LarkServer.BaseRes>} 返回一个包含响应数据的Promise。
|
||||
* @param sheetToken 表格令牌。
|
||||
* @param range 插入数据的范围。
|
||||
* @param values 要插入的值。
|
||||
* @returns 返回一个包含响应数据的Promise。
|
||||
*/
|
||||
async insertRows(sheetToken: string, range: string, values: string[][]) {
|
||||
const path = `/sheets/v2/spreadsheets/${sheetToken}/values_append?insertDataOption=INSERT_ROWS`
|
||||
@ -21,9 +21,9 @@ class LarkSheetService extends LarkBaseService {
|
||||
|
||||
/**
|
||||
* 获取指定范围内的电子表格数据。
|
||||
* @param {string} sheetToken - 表格令牌。
|
||||
* @param {string} range - 要获取数据的范围。
|
||||
* @returns {Promise<LarkServer.SpreadsheetRes>} 返回一个包含响应数据的Promise。
|
||||
* @param sheetToken 表格令牌。
|
||||
* @param range 要获取数据的范围。
|
||||
* @returns 返回一个包含响应数据的Promise。
|
||||
*/
|
||||
async getRange(sheetToken: string, range: string) {
|
||||
const path = `/sheets/v2/spreadsheets/${sheetToken}/values/${range}?valueRenderOption=ToString`
|
||||
@ -32,35 +32,38 @@ class LarkSheetService extends LarkBaseService {
|
||||
|
||||
/**
|
||||
* 获取指定表格的所有数据表(多维表格专用)
|
||||
* @param {string} appToken - 表格令牌。
|
||||
* @returns {Promise<LarkServer.BaseRes} 返回一个包含响应数据的Promise。
|
||||
* @param appToken 表格令牌。
|
||||
* @returns 返回一个包含响应数据的Promise。
|
||||
*/
|
||||
async getTables(appToken: string) {
|
||||
const path = `/bitable/v1/apps/${appToken}/tables`
|
||||
let has_more = true
|
||||
const res = [] as LarkServer.TableData[]
|
||||
while (has_more) {
|
||||
const tableList = [] as LarkServer.TableData[]
|
||||
let hasMore = true
|
||||
let pageToken = ""
|
||||
while (hasMore) {
|
||||
const { data, code } = await this.get<
|
||||
LarkServer.BaseListRes<LarkServer.TableData>
|
||||
>(path, {
|
||||
page_size: 100,
|
||||
page_token: pageToken,
|
||||
})
|
||||
if (code !== 0) break
|
||||
res.push(...data.items)
|
||||
has_more = data.has_more
|
||||
tableList.push(...data.items)
|
||||
hasMore = data.has_more
|
||||
pageToken = data.page_token
|
||||
}
|
||||
return {
|
||||
code: 0,
|
||||
data: res,
|
||||
data: tableList,
|
||||
message: "ok",
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定数据表的所有视图(多维表格专用)
|
||||
* @param {string} appToken - 表格令牌。
|
||||
* @param {string} tableId - 表格ID。
|
||||
* @returns {Promise<LarkServer.BaseRes} 返回一个包含响应数据的Promise。
|
||||
* @param appToken 表格令牌。
|
||||
* @param tableId 表格ID。
|
||||
* @returns 返回一个包含响应数据的Promise。
|
||||
*/
|
||||
async getViews(appToken: string, tableId: string) {
|
||||
const path = `/bitable/v1/apps/${appToken}/tables/${tableId}/views`
|
||||
@ -85,9 +88,9 @@ class LarkSheetService extends LarkBaseService {
|
||||
|
||||
/**
|
||||
* 获取指定数据表的记录。(多维表格专用)
|
||||
* @param {string} appToken - 表格令牌。
|
||||
* @param {string} tableId - 表格ID。
|
||||
* @returns {Promise<LarkServer.BaseRes>} 返回一个包含响应数据的Promise。
|
||||
* @param appToken 表格令牌。
|
||||
* @param tableId 表格ID。
|
||||
* @returns 返回一个包含响应数据的Promise。
|
||||
*/
|
||||
async getRecords(appToken: string, tableId: string) {
|
||||
const path = `/bitable/v1/apps/${appToken}/tables/${tableId}/records`
|
||||
|
@ -4,7 +4,7 @@ import LarkBaseService from "./base"
|
||||
class LarkUserService extends LarkBaseService {
|
||||
/**
|
||||
* 登录凭证校验
|
||||
* @param {string} code 登录凭证
|
||||
* @param code 登录凭证
|
||||
* @returns
|
||||
*/
|
||||
async code2Login(code: string) {
|
||||
@ -14,38 +14,38 @@ class LarkUserService extends LarkBaseService {
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param {string} user_id 用户ID
|
||||
* @param {"open_id" | "user_id"} user_id_type 用户ID类型
|
||||
* @param userId 用户ID
|
||||
* @param userIdType 用户ID类型
|
||||
* @returns
|
||||
*/
|
||||
async getOne(user_id: string, user_id_type: "open_id" | "user_id") {
|
||||
const path = `/contact/v3/users/${user_id}`
|
||||
async getOne(userId: string, userIdType: "open_id" | "user_id") {
|
||||
const path = `/contact/v3/users/${userId}`
|
||||
return this.get<LarkServer.UserInfoRes>(path, {
|
||||
user_id_type,
|
||||
user_id_type: userIdType,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取用户信息
|
||||
* @param {string[]} user_ids 用户ID数组
|
||||
* @param {"open_id" | "user_id"} user_id_type 用户ID类型
|
||||
* @param userIds 用户ID数组
|
||||
* @param userIdType 用户ID类型
|
||||
* @returns
|
||||
*/
|
||||
async batchGet(user_ids: string[], user_id_type: "open_id" | "user_id") {
|
||||
async batchGet(userIds: string[], userIdType: "open_id" | "user_id") {
|
||||
const path = `/contact/v3/users/batch`
|
||||
|
||||
// 如果user_id长度超出50,需要分批请求,
|
||||
const userCount = user_ids.length
|
||||
const userCount = userIds.length
|
||||
const maxLen = 50
|
||||
|
||||
const requestMap = Array.from(
|
||||
{ length: Math.ceil(userCount / maxLen) },
|
||||
(_, index) => {
|
||||
const start = index * maxLen
|
||||
const user_idsSlice = user_ids.slice(start, start + maxLen)
|
||||
const getParams = `${user_idsSlice
|
||||
const getParams = `${userIds
|
||||
.slice(start, start + maxLen)
|
||||
.map((id) => `user_ids=${id}`)
|
||||
.join("&")}&user_id_type=${user_id_type}`
|
||||
.join("&")}&user_id_type=${userIdType}`
|
||||
return this.get<LarkServer.BatchUserInfoRes>(path, getParams)
|
||||
}
|
||||
)
|
||||
|
14
test/getChatHistory.ts
Normal file
14
test/getChatHistory.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { LarkService } from "../services"
|
||||
|
||||
const service = new LarkService("egg", "")
|
||||
|
||||
const currentTime = Math.floor(new Date().getTime() / 1000)
|
||||
const yesterdayTime = currentTime - 24 * 60 * 60
|
||||
|
||||
const res = await service.message.getHistory(
|
||||
"oc_c83f627bde3da39b01bbbfb026a00111",
|
||||
yesterdayTime.toString(),
|
||||
currentTime.toString()
|
||||
)
|
||||
|
||||
console.log(JSON.stringify(res, null, 2))
|
7
test/getInnerList.ts
Normal file
7
test/getInnerList.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { LarkService } from "../services"
|
||||
|
||||
const service = new LarkService("egg", "")
|
||||
|
||||
const res = await service.chat.getInnerList()
|
||||
|
||||
console.log(JSON.stringify(res, null, 2))
|
@ -1,6 +1,13 @@
|
||||
import { RecordModel } from "pocketbase"
|
||||
|
||||
export namespace DB {
|
||||
export interface GroupAgentConfig extends RecordModel {
|
||||
user_id: string
|
||||
chat_id: string
|
||||
chat_name: string
|
||||
pre_query?: string
|
||||
}
|
||||
|
||||
export interface AppInfo extends RecordModel {
|
||||
name: string
|
||||
app_id: string
|
||||
|
@ -95,6 +95,39 @@ export namespace LarkServer {
|
||||
view_private_owner_id?: string
|
||||
}
|
||||
|
||||
export interface MessageData {
|
||||
message_id: string
|
||||
root_id: string
|
||||
parent_id: string
|
||||
msg_type: MsgType
|
||||
create_time: string
|
||||
update_time: string
|
||||
deleted: boolean
|
||||
updated: boolean
|
||||
chat_id: string
|
||||
sender: {
|
||||
id: string
|
||||
id_type: "open_id" | "app_id"
|
||||
sender_type: "user" | "app"
|
||||
}
|
||||
body: {
|
||||
content: string
|
||||
}
|
||||
mentions: any[]
|
||||
upper_message_id: string
|
||||
}
|
||||
|
||||
export interface ChatGroupData {
|
||||
avatar: string
|
||||
chat_id: string
|
||||
description: string
|
||||
external: boolean
|
||||
name: string
|
||||
owner_id: string
|
||||
owner_id_type: "open_id" | "user_id"
|
||||
tenant_key: string
|
||||
}
|
||||
|
||||
export interface BaseRes<T = any> {
|
||||
code: number
|
||||
data: T
|
||||
|
@ -56,6 +56,7 @@ export const genTempMsg = (id: string, variable: any) =>
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
enable_forward: false,
|
||||
},
|
||||
template_id: id,
|
||||
template_variable: variable,
|
||||
@ -68,7 +69,7 @@ export const genTempMsg = (id: string, variable: any) =>
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genSheetDbErrorMsg = (content: string) =>
|
||||
genErrorMsg("🍳 小煎蛋 Sheet DB 错误提醒", content)
|
||||
genErrorMsg("🍪 小煎蛋 Sheet DB 错误提醒", content)
|
||||
|
||||
/**
|
||||
* 生成 Sheet DB 成功消息的 JSON 字符串
|
||||
@ -76,4 +77,20 @@ export const genSheetDbErrorMsg = (content: string) =>
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genSheetDbSuccessMsg = (content: string) =>
|
||||
genSuccessMsg("🍳 感谢使用小煎蛋 Sheet DB", content)
|
||||
genSuccessMsg("🍪 感谢使用小煎蛋 Sheet DB", content)
|
||||
|
||||
/**
|
||||
* 生成 Group Agent 错误消息的 JSON 字符串
|
||||
* @param {string} content - 消息内容
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genGroupAgentErrorMsg = (content: string) =>
|
||||
genErrorMsg("🧑💻 小煎蛋 Group Agent 错误提醒", content)
|
||||
|
||||
/**
|
||||
* 生成 Group Agent 成功消息的 JSON 字符串
|
||||
* @param {string} content - 消息内容
|
||||
* @returns {string} JSON 字符串
|
||||
*/
|
||||
export const genGroupAgentSuccessMsg = (content: string) =>
|
||||
genSuccessMsg("🧑💻 感谢使用小煎蛋 Group Agent", content)
|
||||
|
75
utils/llm.ts
Normal file
75
utils/llm.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { ChatOpenAI } from "@langchain/openai"
|
||||
import { z } from "zod"
|
||||
|
||||
import db from "../db"
|
||||
|
||||
/**
|
||||
* 获取Deepseek模型
|
||||
* @param temperature 温度
|
||||
*/
|
||||
const getDeepseekModel = async (temperature = 0) => {
|
||||
const model = "deepseek-chat"
|
||||
const apiKey = await db.appConfig.getDeepseekApiKey()
|
||||
const baseURL = "https://api.deepseek.com"
|
||||
return new ChatOpenAI({ apiKey, temperature, model }, { baseURL })
|
||||
}
|
||||
|
||||
const timeConfig = z.object({
|
||||
startTime: z.string().describe("开始时间,格式为 YYYY-MM-DD HH:mm:ss"),
|
||||
endTime: z.string().describe("结束时间,格式为 YYYY-MM-DD HH:mm:ss"),
|
||||
})
|
||||
|
||||
/**
|
||||
* 解析时间
|
||||
* @param userInput 用户输入
|
||||
* @returns
|
||||
*/
|
||||
const parseTime = async (userInput: string) => {
|
||||
const model = await getDeepseekModel()
|
||||
const structuredLlm = model.withStructuredOutput(timeConfig, { name: "time" })
|
||||
return await structuredLlm.invoke(
|
||||
`
|
||||
当前时间为 ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
|
||||
你是一个专业的语义解析工程师,给定以下用户输入,帮我解析出开始时间和结束时间
|
||||
如果不包含时间信息,请返回当天的起始时间到当前时间
|
||||
用户输入:
|
||||
\`\`\`
|
||||
${userInput.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据聊天历史回答用户的问题
|
||||
* @param userInput 用户输入
|
||||
* @param chatHistory 聊天历史
|
||||
* @returns
|
||||
*/
|
||||
const queryWithChatHistory = async (userInput: string, chatHistory: string) => {
|
||||
const model = await getDeepseekModel(0.5)
|
||||
return await model.invoke(
|
||||
`
|
||||
当前时间为 ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}
|
||||
你是一个专业的聊天记录分析工程师,给定以下用户输入和聊天历史,帮我回答用户的问题
|
||||
如果无法回答或者用户的问题不清晰,请引导用户问出更具体的问题
|
||||
|
||||
用户输入:
|
||||
\`\`\`
|
||||
${userInput.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
聊天历史:
|
||||
\`\`\`
|
||||
${chatHistory.replaceAll("`", " ")}
|
||||
\`\`\`
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
const llm = {
|
||||
getDeepseekModel,
|
||||
parseTime,
|
||||
queryWithChatHistory,
|
||||
}
|
||||
|
||||
export default llm
|
Loading…
x
Reference in New Issue
Block a user