generated from template/apps-template
优化noco-agent功能和修复相关问题
- 修复Core类中的类型定义和拼写错误 - 添加tableId参数支持到NocoLifeService - 优化认证逻辑,支持环境变量配置 - 增强配置功能,返回当前配置信息 - 改进任务完成功能,支持批量操作 - 添加记录创建和更新功能 - 更新依赖包版本 - 修复导出类型定义 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ app.route({
|
||||
ctx.throw(400, '缺少参数 question');
|
||||
}
|
||||
const token = ctx.query.token || '';
|
||||
const tableId = ctx.query.tableId || '';
|
||||
const slicedQuestion = question.slice(0, 10);
|
||||
if (slicedQuestion.startsWith('配置多维表格')) {
|
||||
const res = await ctx.call({
|
||||
@@ -27,11 +28,12 @@ app.route({
|
||||
ctx.body = res.body;
|
||||
return;
|
||||
}
|
||||
const nocoLifeService = new NocoLifeService({ token });
|
||||
const nocoLifeService = new NocoLifeService({ token, tableId });
|
||||
await nocoLifeService.initConfig()
|
||||
const routes = ctx.app.getList().filter(r => r.path.startsWith('noco-life') && r.key !== 'chat');
|
||||
const v = `${routes.map((r, index) => `${index + 1}工具名称: ${r.id}\n描述: ${r.description}\n`).join('\n')}\n\n当用户询问时,如果拥有工具,请返回 JSON 数据,不存在工具,则返回分析判断,数据JSON数据类型是{id,payload},外面的id是工具的id。如果工具有参数,在 payload 当中,默认不需要参数,如果工具内部需要id,在payload当中。`
|
||||
const ai: BaseChat = useContextKey('ai');
|
||||
const slicedQuestion2 = question.slice(0, 1000);
|
||||
const answer = await ai.chat([
|
||||
{ role: 'system', content: `你是一个多维表格助理,你的任务是帮助用户操作和查询多维表格的数据。你可以使用以下工具来完成任务:\n\n${v}` },
|
||||
{ role: 'user', content: question }
|
||||
@@ -64,7 +66,8 @@ app.route({
|
||||
middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const token = ctx.query.token || '';
|
||||
const nocoLifeService = new NocoLifeService({ token });
|
||||
const tableId = ctx.query.tableId || '';
|
||||
const nocoLifeService = new NocoLifeService({ token, tableId });
|
||||
await nocoLifeService.initConfig()
|
||||
const life = nocoLifeService.life;
|
||||
|
||||
@@ -100,49 +103,66 @@ app.route({
|
||||
app.route({
|
||||
path: 'noco-life',
|
||||
key: 'done',
|
||||
description: `完成某件事情,然后判断下一次运行时间。参数是id,数据类型是number。`,
|
||||
description: `完成某件事情,然后判断下一次运行时间。参数是id,数据类型是number。如果多个存在,则是ids的number数组`,
|
||||
middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
const ids = ctx.query.ids || [];
|
||||
if (!id || ids.length === 0) {
|
||||
ctx.throw(400, '缺少参数 id');
|
||||
}
|
||||
console.log('id', id);
|
||||
if (ids.length === 0 && id) {
|
||||
ids.push(Number(id));
|
||||
}
|
||||
console.log('id', id, ids);
|
||||
const token = ctx.query.token || '';
|
||||
const nocoLifeService = new NocoLifeService({ token });
|
||||
const tableId = ctx.query.tableId || '';
|
||||
const nocoLifeService = new NocoLifeService({ token, tableId });
|
||||
await nocoLifeService.initConfig()
|
||||
const life = nocoLifeService.life;
|
||||
// 获取记录详情
|
||||
const recordRes = await life.getItem(id);
|
||||
if (recordRes.code !== 200) {
|
||||
ctx.throw(500, '获取记录详情失败');
|
||||
}
|
||||
const record = recordRes.data;
|
||||
const messages = [];
|
||||
const changeItem = async (id: number) => {
|
||||
const life = nocoLifeService.life;
|
||||
// 获取记录详情
|
||||
const recordRes = await life.getItem(id);
|
||||
if (recordRes.code !== 200) {
|
||||
// ctx.throw(500, '获取记录详情失败');
|
||||
messages.push({
|
||||
id,
|
||||
content: `获取记录 ${id} 详情失败`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const record = recordRes.data;
|
||||
|
||||
// 检查启动时间是否大于今天
|
||||
const startTime = record['启动时间'];
|
||||
const today = dayjs().startOf('day');
|
||||
const startDate = dayjs(startTime).startOf('day');
|
||||
// 检查启动时间是否大于今天
|
||||
const startTime = record['启动时间'];
|
||||
const today = dayjs().startOf('day');
|
||||
const startDate = dayjs(startTime).startOf('day');
|
||||
|
||||
if (startDate.isAfter(today)) {
|
||||
ctx.throw(400, '还没到今天呢,到时候再做吧');
|
||||
}
|
||||
// 计算下一次运行时间
|
||||
// 1. 知道当前时间
|
||||
// 2. 知道任务类型,如果是每日,则加一天;如果是每周,则加七天;如果是每月,则加一个月,如果是每年农历,需要转为新的,如果是其他,需要智能判断
|
||||
// 3. 更新记录
|
||||
const strTime = (time: string) => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
const currentTime = strTime(new Date().toISOString());
|
||||
const isLuar = record['类型']?.includes?.('农历');
|
||||
let summay = record['总结'] || '无';
|
||||
if (summay.length > 200) {
|
||||
summay = summay.substring(0, 200) + '...';
|
||||
}
|
||||
const prompt = record['提示词'] || '';
|
||||
const type = record['类型'] || '';
|
||||
const content = `上一次执行的时间是${strTime(startTime)},当前时间是${currentTime},请帮我计算下一次的运行时间,如果时间不存在,默认在8点启动。
|
||||
if (startDate.isAfter(today)) {
|
||||
// ctx.throw(400, '还没到今天呢,到时候再做吧');
|
||||
messages.push({
|
||||
id,
|
||||
content: `记录 ${id} 的启动时间是 ${dayjs(startTime).format('YYYY-MM-DD HH:mm:ss')},还没到今天呢,到时候再做吧`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 计算下一次运行时间
|
||||
// 1. 知道当前时间
|
||||
// 2. 知道任务类型,如果是每日,则加一天;如果是每周,则加七天;如果是每月,则加一个月,如果是每年农历,需要转为新的,如果是其他,需要智能判断
|
||||
// 3. 更新记录
|
||||
const strTime = (time: string) => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
const currentTime = strTime(new Date().toISOString());
|
||||
const isLuar = record['类型']?.includes?.('农历');
|
||||
let summay = record['总结'] || '无';
|
||||
if (summay.length > 200) {
|
||||
summay = summay.substring(0, 200) + '...';
|
||||
}
|
||||
const prompt = record['提示词'] || '';
|
||||
const type = record['类型'] || '';
|
||||
const content = `上一次执行的时间是${strTime(startTime)},当前时间是${currentTime},请帮我计算下一次的运行时间,如果时间不存在,默认在8点启动。
|
||||
${prompt ? `这是我给你的提示词,帮你更好地理解我的需求:${prompt}` : ''}
|
||||
|
||||
相关资料是
|
||||
@@ -150,43 +170,115 @@ ${prompt ? `这是我给你的提示词,帮你更好地理解我的需求:${
|
||||
总结:${summay}
|
||||
类型: ${type}
|
||||
`
|
||||
const ai = useContextKey('ai');
|
||||
await ai.chat([
|
||||
{ role: 'system', content: `你是一个时间计算专家,擅长根据任务类型和时间计算下一次运行时间。只返回我对应的日期的结果,格式是:YYYY-MM-DD HH:mm:ss。` },
|
||||
{ role: 'user', content }
|
||||
])
|
||||
let nextTime = ai.responseText?.trim();
|
||||
try {
|
||||
// 判断返回的时间是否可以格式化
|
||||
if (nextTime && dayjs(nextTime).isValid()) {
|
||||
const time = dayjs(nextTime);
|
||||
if (isLuar) {
|
||||
const festival = createLunarDate({ year: time.year(), month: time.month() + 1, day: time.date() });
|
||||
const { date } = toGregorian(festival);
|
||||
nextTime = dayjs(date).toISOString();
|
||||
const ai = useContextKey('ai');
|
||||
await ai.chat([
|
||||
{ role: 'system', content: `你是一个时间计算专家,擅长根据任务类型和时间计算下一次运行时间。只返回我对应的日期的结果,格式是:YYYY-MM-DD HH:mm:ss。` },
|
||||
{ role: 'user', content }
|
||||
])
|
||||
let nextTime = ai.responseText?.trim();
|
||||
try {
|
||||
// 判断返回的时间是否可以格式化
|
||||
if (nextTime && dayjs(nextTime).isValid()) {
|
||||
const time = dayjs(nextTime);
|
||||
if (isLuar) {
|
||||
const festival = createLunarDate({ year: time.year(), month: time.month() + 1, day: time.date() });
|
||||
const { date } = toGregorian(festival);
|
||||
nextTime = dayjs(date).toISOString();
|
||||
} else {
|
||||
nextTime = time.toISOString();
|
||||
}
|
||||
} else {
|
||||
nextTime = time.toISOString();
|
||||
messages.push({
|
||||
id,
|
||||
content: `记录 ${id} 的任务 "${record['标题']}",AI 返回的时间格式无效,无法格式化,返回内容是:${ai.responseText}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ctx.throw(500, 'AI 返回的时间格式无效,无法格式化');
|
||||
} catch (e) {
|
||||
messages.push({
|
||||
id,
|
||||
content: `记录 ${id} 的任务 "${record['标题']}",AI 返回结果解析失败,返回内容是:${ai.responseText}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.throw(500, 'AI 返回结果解析失败');
|
||||
const update = await life.updateItem({ Id: id, '启动时间': nextTime });
|
||||
if (update.code !== 200) {
|
||||
messages.push({
|
||||
id,
|
||||
content: `记录 ${id} 的任务 "${record['标题']}",更新记录失败`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const msg = {
|
||||
id,
|
||||
nextTime,
|
||||
showCNTime: dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
content: `任务 "${record['标题']}" 已标记为完成。下一次运行时间是 ${dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')}`
|
||||
};
|
||||
messages.push(msg);
|
||||
}
|
||||
const update = await life.updateItem({ Id: id, '启动时间': nextTime });
|
||||
if (update.code !== 200) {
|
||||
ctx.throw(500, '更新记录失败');
|
||||
|
||||
for (const _id of ids) {
|
||||
await changeItem(Number(_id));
|
||||
}
|
||||
ctx.body = {
|
||||
id,
|
||||
nextTime,
|
||||
showCNTime: dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
content: `任务 "${record['标题']}" 已标记为完成。下一次运行时间是 ${dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')}`
|
||||
content: messages.map(m => m.content).join('\n'),
|
||||
list: messages
|
||||
};
|
||||
|
||||
}).addTo(app);
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'noco-life',
|
||||
key: 'record',
|
||||
description: `创建或者更新一条新的记录,参数是 question 和 id, 如果id存在,则更新记录,否则创建新的记录`,
|
||||
middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const { id, question } = ctx.query;
|
||||
let summary = '空'
|
||||
const token = ctx.query.token || '';
|
||||
const tableId = ctx.query.tableId || '';
|
||||
const nocoLifeService = new NocoLifeService({ token, tableId });
|
||||
await nocoLifeService.initConfig()
|
||||
const life = nocoLifeService.life;
|
||||
const ai = useContextKey('ai');
|
||||
let record = null;
|
||||
if (id) {
|
||||
record = await life.getItem(id);
|
||||
if (record.code !== 200) {
|
||||
// 获取记录失败
|
||||
} else {
|
||||
summary = record.data['总结'] || ''
|
||||
}
|
||||
}
|
||||
const prompt = `对当前的内容进行总结,要求简洁扼要,200字以内。如果内容已经很简洁,则不需要修改。当前内容是:${question}\n历史总结内容是:${summary}`;
|
||||
await ai.chat([
|
||||
{ role: 'system', content: `你是一个总结专家,擅长将冗长的信息进行提炼和总结。` },
|
||||
{ role: 'user', content: prompt }
|
||||
])
|
||||
const newSummary = ai.responseText?.trim() || '';
|
||||
if (record) {
|
||||
// 更新记录
|
||||
const updateRes = await life.updateItem({ Id: id, '总结': newSummary });
|
||||
if (updateRes.code !== 200) {
|
||||
ctx.throw(500, '更新记录失败');
|
||||
}
|
||||
ctx.body = {
|
||||
id: id,
|
||||
content: `已更新记录 ${id} 的总结内容为:${newSummary}`
|
||||
}
|
||||
} else {
|
||||
// 创建记录
|
||||
const createRes = await life.createItem({ '标题': question.slice(0, 50), '总结': newSummary, '任务': '运行中', '启动时间': new Date().toISOString() });
|
||||
if (createRes.code !== 200) {
|
||||
ctx.throw(500, '创建记录失败');
|
||||
}
|
||||
ctx.body = {
|
||||
id: createRes.data.Id,
|
||||
content: `已创建新的记录,ID 是 ${createRes.data.Id}\n内容是:${newSummary}`
|
||||
}
|
||||
}
|
||||
}).addTo(app);
|
||||
app.route({
|
||||
path: 'noco-life',
|
||||
key: 'how-to-use',
|
||||
|
||||
Reference in New Issue
Block a user