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.
This commit is contained in:
xiongxiao
2026-01-27 04:02:34 +08:00
parent da7b06e519
commit 50332fe2f4
23 changed files with 665 additions and 201 deletions

View File

@@ -0,0 +1,142 @@
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);

View File

@@ -0,0 +1 @@
import './ai.ts'