// 内置工具包 A:知识库。宿主放文件到 knowledge/ 即得可检索能力,不写任何注册代码。 // // 三个工具名、入参、行为与改造前逐字节一致(listKnowledgeFiles / readKnowledgeFile / // searchKnowledge),日志与既有测试因此保持连续;历史消息本就被 toPlainTextHistory // 剥成纯文本,不含工具结构,所以这次改造对存量会话零迁移。 // // 与改造前唯一的行为差异:知识库根目录不存在时不再让整条对话失败,而是本包不贡献 // 工具与能力段。助手已不只有知识库一种能力,一个可选能力缺席不该拖垮其余能力。 import { AiApiError } from '../../ai-shared/AiError'; import { describeKnowledgeCapability } from '../KnowledgeCatalog'; import { KnowledgeRepository } from '../KnowledgeRepository'; import { createKnowledgeTools } from '../KnowledgeTools'; import type { AssistantToolBundle, AssistantToolDisplay, AssistantToolkit, AssistantToolkitContext, } from './AssistantToolkit'; export const KNOWLEDGE_TOOLKIT_NAME = 'knowledge'; /** 工具细节含知识库路径,对用户只说"查阅资料",不暴露文件名。 */ const KNOWLEDGE_DISPLAY: AssistantToolDisplay = { pending: '正在查阅资料…', done: '已查阅资料', error: '查阅资料失败', }; export const knowledgeToolkit: AssistantToolkit = { name: KNOWLEDGE_TOOLKIT_NAME, create(context: AssistantToolkitContext): AssistantToolBundle { const repository = openRepository(context); if (!repository) { return { tools: {} }; } const tools = createKnowledgeTools(repository); return { tools, display: Object.fromEntries( Object.keys(tools).map((name) => [name, KNOWLEDGE_DISPLAY]), ), systemPrompt: describeKnowledgeCapability(repository, context.signal), }; }, }; function openRepository(context: AssistantToolkitContext): KnowledgeRepository | undefined { try { return new KnowledgeRepository(context.config); } catch (error) { if (error instanceof AiApiError) { console.warn(`[soma-ai][助手] 知识库不可用,本次对话不提供资料检索:${error.message}`); return undefined; } throw error; } }