Files
cnb/agent/routes/knowledge/ai.ts
xiongxiao 50332fe2f4 feat: update package dependencies and add new routes for CNB environment management
- Updated package.json and pnpm-lock.yaml with new dependencies and versions.
- Removed outdated readme files from requirements.
- Enhanced CNB environment configuration in cnb-env.ts with new VS Code remote SSH settings.
- Modified KnowledgeBase class to return structured results.
- Updated Workspace class to return structured results.
- Implemented new routes for managing CNB cookies and VS Code proxy URIs.
- Added AI chat functionality for querying knowledge base.
- Created skills for cleaning up closed workspaces.
2026-01-27 04:02:34 +08:00

142 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import { CNBChat } from '@kevisual/ai/browser'
/**
调用cnb-ai-chat技能, repo为kevisual/starred-auto.
问题是用户提供的问题是OpenListTeam/OpenList是什么有多少star
*/
app.route({
path: 'cnb',
key: 'cnb-ai-chat',
description: '调用cnb的知识库ai对话功能进行聊天',
middleware: ['auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'cnb-ai-chat',
title: '调用cnb的知识库ai对话功能进行聊天',
summary: '调用cnb的知识库ai对话功能进行聊天基于cnb提供的ai能力',
args: {
question: tool.schema.string().describe('用户输入的消息内容'),
repo: tool.schema.string().optional().describe('知识库仓库ID默认为空表示使用默认知识库'),
}
})
}
}).define(async (ctx) => {
const question = ctx.query?.question;
if (!question) {
ctx.body = { content: '请提供有效的消息内容' };
return;
}
let repo = ctx.query?.repo;
if (!repo) {
// 如果未指定知识库仓库ID则使用默认知识库
const res = await cnb.repo.getRepoList({ flags: 'KnowledgeBase' });
if (res.code === 200 && res.data.length > 0) {
repo = res.data[0].path;
}
}
console.log("Using knowledge base repo:", repo);
const ragRes = await cnb.knowledgeBase.queryKnowledgeBase(repo || '', {
query: question,
score_threshold: 0.62,
top_k: 10,
});
if (ragRes.code !== 200) {
ctx.body = { content: `查询知识库失败,错误信息:${ragRes.message}` };
return;
}
const list = ragRes.data || [];
// 构建RAG上下文包含文件来源和相似度信息
const ragContext = list.map((item, index) => {
const source = item.metadata?.path || item.metadata?.name || '未知来源';
const type = item.metadata?.type === 'code' ? '〔代码〕' : '〔文档〕';
const scorePercent = Math.round((item.score || 0) * 100);
const url = item.metadata?.url || '无';
return `〔来源${index + 1}${type} ${source} (相似度: ${scorePercent}%)\n${item.chunk}\n访问地址: ${url}`;
}).join('\n\n---\n\n');
// hunyuan-a13b
// enable_thinking
const chat = new CNBChat({
repo,
token: cnb.token,
model: 'hunyuan-a13b'
});
const messages = [
{
role: 'system',
content: `[角色定义]='''\n你是一个专业的技术助手擅长基于提供的知识库内容进行准确、有条理的分析和回答。你的特点是\n1. 严格基于RAG检索到的上下文内容进行回答不添加未经验证的信息\n2. 回答时清晰标注信息来源,便于用户追溯查证\n3. 面对不确定的信息,明确标注「根据提供的内容无法确定」\n4. 代码相关问题注重可执行性和最佳实践\n'''[要求]='''\n1. 严格遵循用户的提问要求,优先解决用户的核心问题\n2. 避免侵犯版权的内容不复制原文超过100字技术术语和函数名除外\n3. 使用中文进行响应,语言专业且易于理解\n4. 如果上下文存在多个来源,优先使用相似度更高的内容\n5. 对于代码片段,确保完整且可直接使用\n6. 当上下文中没有相关信息时,直接说明「知识库中未找到相关内容」\n7. 在思考过程中分析:用户的真实意图是什么?提供的上下文是否足够回答?\n'''[回答格式]='''\n- 先简要说明回答的核心结论\n- 如有必要,分点阐述详细分析过程\n- 标注关键信息来源标注【来源X】即可\n- 提供可操作的建议或代码示例\n'''`
},
{
role: 'user',
content: `[上下文]='''\n${ragContext}\n'''\n\n[用户问题]='''\n${question}\n'''\n\n请基于以上上下文知识库内容回答用户问题。`
}
] as Array<{ role: 'system' | 'user' | 'assistant', content: string }>;
const response = await chat.chat({
messages
});
const txt = chat.responseText;
ctx.body = { content: txt, response };
}).addTo(app);
// RAG知识库查询技能: 查询openlist有多少star
app.route({
path: 'cnb',
key: 'cnb-rag-query',
description: '调用cnb的知识库RAG查询功能进行问答',
middleware: ['auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'cnb-rag-query',
title: '调用cnb的知识库RAG查询功能进行问答',
summary: '调用cnb的知识库RAG查询功能进行问答基于cnb提供的知识库能力',
args: {
question: tool.schema.string().describe('用户输入的消息内容'),
repo: tool.schema.string().optional().describe('知识库仓库ID默认为空表示使用默认知识库'),
}
})
}
}).define(async (ctx) => {
const question = ctx.query?.question;
if (!question) {
ctx.body = { content: '请提供有效的消息内容' };
return;
}
let repo = ctx.query?.repo;
if (!repo) {
// 如果未指定知识库仓库ID则使用默认知识库
const res = await cnb.repo.getRepoList({ flags: 'KnowledgeBase' });
if (res.code === 200 && res.data.length > 0) {
repo = res.data[0].path;
}
}
console.log("Using knowledge base repo:", repo);
const ragRes = await cnb.knowledgeBase.queryKnowledgeBase(repo || '', {
query: question,
score_threshold: 0.62,
top_k: 10,
});
if (ragRes.code !== 200) {
ctx.body = { content: `查询知识库失败,错误信息:${ragRes.message}` };
return;
}
const list = ragRes.data || [];
let answer = `基于知识库「${repo}」的查询结果:\n\n`;
if (list.length === 0) {
answer += '知识库中未找到相关内容。';
} else {
list.forEach((item, index) => {
const source = item.metadata?.path || item.metadata?.name || '未知来源';
const type = item.metadata?.type === 'code' ? '〔代码〕' : '〔文档〕';
const scorePercent = Math.round((item.score || 0) * 100);
const url = item.metadata?.url || '无';
answer += `【来源${index + 1}${type} ${source} (相似度: ${scorePercent}%)\n${item.chunk}\n访问地址: ${url}\n\n`;
});
}
ctx.body = { content: answer };
}).addTo(app);