generated from tailored/router-template
update
This commit is contained in:
3
src/agent/agent.ts
Normal file
3
src/agent/agent.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { QueryRouterServer } from '@kevisual/router';
|
||||
|
||||
export const agent = new QueryRouterServer();
|
||||
16
src/agent/ai.ts
Normal file
16
src/agent/ai.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { SiliconFlowProvider } from '@kevisual/ai';
|
||||
import { config } from '../modules/config.ts';
|
||||
|
||||
export const ai = new SiliconFlowProvider({
|
||||
model: 'Qwen/Qwen3-32B',
|
||||
// model: 'Pro/deepseek-ai/DeepSeek-R1',// 只有充值能用
|
||||
apiKey: config.SILICONFLOW_API_KEY,
|
||||
});
|
||||
|
||||
ai.getUsageInfo()
|
||||
.then((usage) => {
|
||||
console.log('AI usage info:', usage);
|
||||
})
|
||||
.catch((res) => {
|
||||
console.error('Error fetching AI usage info:', res.status);
|
||||
});
|
||||
95
src/agent/analyze/content.ts
Normal file
95
src/agent/analyze/content.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { logger } from '@/agent/logger.ts';
|
||||
const getJsonFromString = (str: string) => {
|
||||
// 尝试从字符串中提取JSON对象
|
||||
try {
|
||||
const jsonMatch = str.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
return JSON.parse(jsonMatch[1]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing JSON from string:', error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
agent
|
||||
.route({
|
||||
path: 'analyze',
|
||||
key: 'content',
|
||||
description: '分析文本内容,意图分析,判断是否需要获取上下文包函小红书的图片,视频,文本。',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const text = ctx.query?.text || '';
|
||||
|
||||
let result = {
|
||||
image: false,
|
||||
video: false,
|
||||
text: false,
|
||||
comment: false,
|
||||
};
|
||||
|
||||
const prompt = `
|
||||
请分析<context>包函的内容,判断是否需要包含小红书的图片、视频或文本信息,返回一个JSON对象,包含三个布尔值:image(是否包含图片)、video(是否包含视频)、text(是否包含小红书文本),comment(是否是评论信息)。
|
||||
如果文本中提到图片或视频,请返回true,否则返回false。
|
||||
|
||||
文本示例:
|
||||
1. "解答一下这个笔记。" , text为true
|
||||
2. "分析一下这个图片。", image为true
|
||||
3. "这个视频介绍的是什么。", video为true
|
||||
4. "评价一下这个评论。", comment为true
|
||||
|
||||
返回内容示例:
|
||||
\`\`\`json
|
||||
{
|
||||
"image": true,
|
||||
"video": false,
|
||||
"text": true,
|
||||
"comment": false
|
||||
}
|
||||
\`\`\`
|
||||
分析的文本的内容是:
|
||||
<context>
|
||||
${text}
|
||||
</context>
|
||||
`;
|
||||
const now = Date.now();
|
||||
console.log('start');
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
|
||||
console.log('end', Date.now() - now, 'ms');
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
logger.error('Empty response from AI:', res);
|
||||
}
|
||||
const json = getJsonFromString(ans);
|
||||
if (!json) {
|
||||
logger.error('Invalid JSON format in response:', ans);
|
||||
ctx.throw(400, 'Invalid JSON format in response');
|
||||
}
|
||||
result = {
|
||||
image: json.image || false,
|
||||
video: json.video || false,
|
||||
text: json.text || false,
|
||||
comment: json.comment || false,
|
||||
};
|
||||
ctx.body = result;
|
||||
})
|
||||
.addTo(agent);
|
||||
64
src/agent/fix/prompt.ts
Normal file
64
src/agent/fix/prompt.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { logger } from '@/agent/logger.ts';
|
||||
|
||||
const getTagContent = (text: string) => {
|
||||
const match = text.match(/<content>([\s\S]*?)<\/content>/);
|
||||
return match ? match[1].trim() : text;
|
||||
};
|
||||
agent
|
||||
.route({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
description: '对小红书的提示词进行修正和优化',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const text = ctx.query?.text || '';
|
||||
const now = Date.now();
|
||||
console.log('start');
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: `
|
||||
你是一个提示词优化的专家,请根据用户提供的提示词进行修正和优化,其中用户的提示词返回的要求如果没有或者不明确,请你都修正为要求返回的文本在500字以内,且内容是纯文本格式,不能是markdown模式,也不包含任何HTML标签或其他格式化内容。
|
||||
|
||||
只对提示词进行优化,并且不需要对内容进行分析或总结。
|
||||
|
||||
示例1. 用户提示词
|
||||
<content>总结笔记</content>
|
||||
优化后的提示词
|
||||
<content>请总结一下这个笔记,要求返回的内容是纯文本格式,字数不超过500字。</content>
|
||||
示例2. 用户提示词
|
||||
<content>分析一下这个图片</content>
|
||||
优化后的提示词
|
||||
<content>请分析一下这个图片,要求返回的内容是纯文本格式,字数不超过500字。</content>
|
||||
|
||||
|
||||
用户的提示词是
|
||||
<content>
|
||||
${text}
|
||||
</content>
|
||||
`,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
|
||||
console.log('end', Date.now() - now, 'ms');
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
logger.error('Empty response from AI:', res);
|
||||
}
|
||||
ctx.body = getTagContent(ans)
|
||||
})
|
||||
.addTo(agent);
|
||||
7
src/agent/index.ts
Normal file
7
src/agent/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { agent } from './agent.ts';
|
||||
import './analyze/content.ts';
|
||||
import './fix/prompt.ts';
|
||||
|
||||
import './xhs.ts';
|
||||
|
||||
export { agent };
|
||||
3
src/agent/logger.ts
Normal file
3
src/agent/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Logger } from '@kevisual/logger';
|
||||
|
||||
export const logger = new Logger();
|
||||
19
src/agent/test/analyze.ts
Normal file
19
src/agent/test/analyze.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text1 = '解答一下这个笔记。';
|
||||
const text2 = '分析一下这个图片';
|
||||
const text3 = '这个视频介绍的是什么';
|
||||
const text4 = '评价一下这个评论。';
|
||||
const text5 = '关于这个评论。';
|
||||
const text6 = '1+1=';
|
||||
const res = await agent.call({
|
||||
path: 'analyze',
|
||||
key: 'content',
|
||||
payload: {
|
||||
text: text6,
|
||||
},
|
||||
});
|
||||
console.log('analyze content res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
0
src/agent/test/common.ts
Normal file
0
src/agent/test/common.ts
Normal file
18
src/agent/test/prompt-fix.ts
Normal file
18
src/agent/test/prompt-fix.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// 其中回复的要求是以纯文本,具体的内容在<content></content>当中
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text = '请总结一下这个笔记。';
|
||||
const text2 = '告诉我1+1的值';
|
||||
const text3 = 'html和css的大纲是什么?';
|
||||
const text4 = '1+1=';
|
||||
const res = await agent.call({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
payload: {
|
||||
text: text4,
|
||||
},
|
||||
});
|
||||
console.log('fix xhs res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
53
src/agent/xhs.ts
Normal file
53
src/agent/xhs.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import { agent } from './agent.ts';
|
||||
import { ai } from './ai.ts';
|
||||
/**
|
||||
* 清除文本中的@信息
|
||||
* @param text
|
||||
*/
|
||||
const clearAtInfo = (text: string = '') => {
|
||||
const newText = text.replace(/@[\u4e00-\u9fa5\w]+/g, '').replace(/#.*?#/g, '');
|
||||
return newText.trim();
|
||||
};
|
||||
agent
|
||||
.route({
|
||||
path: 'xhs',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { text = '' } = ctx.query || {};
|
||||
const id = nanoid();
|
||||
const no_at_text = clearAtInfo(text);
|
||||
const resFix = await agent.call({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
payload: {
|
||||
text: no_at_text,
|
||||
},
|
||||
});
|
||||
if (resFix.code !== 200) {
|
||||
ctx.throw(500, 'AI 小红书prompt优化错误: ' + resFix.message);
|
||||
return;
|
||||
} else {
|
||||
console.log('小红书优化的文本', resFix.body);
|
||||
}
|
||||
const prompt_text = resFix.body || '';
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt_text,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((error) => {
|
||||
ctx.throw(500, 'AI 服务错误: ' + error.status);
|
||||
return error;
|
||||
});
|
||||
ctx.body = res.choices?.[0]?.message?.content || '';
|
||||
})
|
||||
.addTo(agent);
|
||||
3
src/modules/notify.ts
Normal file
3
src/modules/notify.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const notify = () => {
|
||||
//
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { agent } from '@/agent/index.ts';
|
||||
import { taskApp, queue, xhsApp } from '../task.ts';
|
||||
import { random, omit } from 'lodash-es';
|
||||
import util from 'node:util';
|
||||
@@ -10,6 +11,7 @@ taskApp
|
||||
.route({
|
||||
path: 'task',
|
||||
key: 'getUnread',
|
||||
description: '获取未读提及消息',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const res = await xhsApp.call({
|
||||
@@ -50,6 +52,7 @@ taskApp
|
||||
.route({
|
||||
path: 'task',
|
||||
key: 'getMention',
|
||||
description: '获取提及消息',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { unread_count } = ctx.query;
|
||||
@@ -61,11 +64,9 @@ taskApp
|
||||
num: unread_count,
|
||||
},
|
||||
});
|
||||
console.log('mentionRes', mentionRes.body);
|
||||
if (mentionRes.code === 200) {
|
||||
let data = mentionRes.body || [];
|
||||
// data = data.map((item) => omit(item, 'mention'));
|
||||
console.log('queryMention', util.inspect(data, { depth: 10 }));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
queue.add(
|
||||
@@ -86,7 +87,6 @@ taskApp
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('add mention task', item);
|
||||
await sleep(200);
|
||||
}
|
||||
}
|
||||
@@ -94,9 +94,7 @@ taskApp
|
||||
path: 'mention',
|
||||
key: 'postRead',
|
||||
});
|
||||
console.log('postRead', postRead.body);
|
||||
}
|
||||
await sleep(1000);
|
||||
ctx.body = {
|
||||
job: unread_count,
|
||||
};
|
||||
@@ -109,7 +107,7 @@ taskApp
|
||||
key: 'ai',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const data = ctx.query.data;
|
||||
const data = ctx.query.data; // 为提及的相关信息
|
||||
const note_id = data.note_id;
|
||||
const xsec_token = data.xsec_token;
|
||||
const comment_id = data.comment.comment_id;
|
||||
@@ -119,6 +117,21 @@ taskApp
|
||||
content,
|
||||
comment_id,
|
||||
};
|
||||
const resAgent = await agent.call({
|
||||
path: 'xhs',
|
||||
payload: {
|
||||
text: content,
|
||||
},
|
||||
});
|
||||
let responseText = '';
|
||||
let errorText = '';
|
||||
if (resAgent.code !== 200) {
|
||||
errorText = '【调用错误】';
|
||||
responseText = `${resAgent.message}`;
|
||||
} else {
|
||||
responseText = resAgent.body;
|
||||
}
|
||||
postData.content = responseText;
|
||||
const res = await xhsApp.call({
|
||||
path: 'mention',
|
||||
key: 'addComment',
|
||||
|
||||
Reference in New Issue
Block a user