UNPKG

flux-agent

Version:

FluxAgent - 一个可灵活插拔的AI Agent系统框架,基于TypeScript开发,支持流式执行、事件系统、插件系统、知识库管理等功能 (Protected Release) (Protected Release) (Protected Release) (Protected Release) (Protected Release) (Protected Release) (Protected Release) (Protected Release) (Protected Release) (

286 lines (285 loc) 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Recognizer = void 0; const LLM_1 = require("./LLM"); /** * 缓存包装器,用于缓存单个文本片段的 embededText 结果 */ class EmbededTextCache { constructor() { this.cache = new Map(); this.maxCacheSize = 1000; // 最大缓存条目数 } /** * 获取缓存的结果 */ get(text) { return this.cache.get(text); } /** * 设置缓存结果 */ set(text, embededText) { // 如果缓存已满,删除最旧的条目 if (this.cache.size >= this.maxCacheSize) { const firstKey = this.cache.keys().next().value; if (firstKey) { this.cache.delete(firstKey); } } this.cache.set(text, embededText); } /** * 批量设置缓存结果 */ setBatch(embededTexts) { for (const embededText of embededTexts) { this.set(embededText.origin, embededText); } } /** * 清空缓存 */ clear() { this.cache.clear(); } /** * 获取缓存统计信息 */ getStats() { return { size: this.cache.size, maxSize: this.maxCacheSize }; } } /** * 创建带缓存的 generateEmbededTexts 函数 */ function createCachedGenerateEmbededTexts(originalFunction) { const cache = new EmbededTextCache(); return async (result) => { const embededTexts = []; const uncachedTexts = []; // 1. 检查缓存,分离已缓存和未缓存的文本 for (const item of result.data) { const text = typeof item === 'string' ? item : item.text; const cachedResult = cache.get(text); if (cachedResult) { embededTexts.push(cachedResult); console.log(`使用缓存的 embededText: ${text}`); } else { uncachedTexts.push(item); } } // 2. 如果有未缓存的文本,调用原始函数处理 if (uncachedTexts.length > 0) { console.log(`需要处理 ${uncachedTexts.length} 个未缓存的文本:`, uncachedTexts); // 创建只包含未缓存文本的 RecognizeResult const uncachedResult = { success: true, data: uncachedTexts }; // 调用原始函数处理未缓存的文本 const newEmbededTexts = await originalFunction(uncachedResult); // 缓存新结果 cache.setBatch(newEmbededTexts); // 合并结果 embededTexts.push(...newEmbededTexts); } console.log(`最终返回 ${embededTexts.length} 个 embededTexts`); return embededTexts; }; } class Recognizer { constructor(embedConfig, onTokenUsage) { this.config = embedConfig; // 如果配置了 recognizeLLM,则初始化对应的 LLM 实例 if (this.config.recognizeLLM) { // 确保配置了 JSON 响应格式 const llmConfig = { ...this.config.recognizeLLM, response_format: { type: 'json_object' } }; this.recognizeLLM = new LLM_1.OpenAILLM(llmConfig, 'recognize', onTokenUsage); } // 创建带缓存的 generateEmbededTexts 函数 this.cachedGenerateEmbededTexts = createCachedGenerateEmbededTexts(this.config.generateEmbededTexts); } /** * 检查是否启用了 embed 功能 */ enabled() { return this.config.enable; } /** * 让内容变成流式输出 */ async streamContent(content, callbacks) { const streamId = `recognizer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; if (callbacks) { const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const chunkSize = 16; // 每次推送的字符数 const delayMs = 320; // 推送间隔,模拟时间感 let partial = ''; for (let i = 0; i < content.length; i += chunkSize) { const token = content.slice(i, i + chunkSize); partial += token; if (callbacks.onToken) { callbacks.onToken(token, streamId); } if (callbacks.onPartialResponse) { callbacks.onPartialResponse(partial, streamId); } // 模拟流式的时间维度 await sleep(delayMs); } if (callbacks.onComplete) { callbacks.onComplete({ content: content, streamId }, streamId); } } return { content: content || null, streamId, }; } /** * 处理文本内容,执行完整的 embed 流程 * @param content 待处理的原始文本内容 * @returns 处理后的文本内容 */ async process(content, callbacks) { if (!this.enabled()) { return { content: content || null, streamId: `recognizer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, }; } try { // 1. Content Fragment 提取 const recognizeResult = await this.extractContentFragments(content); if (!recognizeResult.success || recognizeResult.data.length === 0) { return await this.streamContent(content, callbacks); } // 2. 引用搜索(使用缓存) const embededTexts = await this.cachedGenerateEmbededTexts(recognizeResult); // 3. Embed:基于引用搜索结果进行正则替换 const result = this.performEmbed(content, embededTexts); // 4. 模拟流式输出 return await this.streamContent(result, callbacks); ; } catch (error) { console.error('Recognizer process error:', error); return { content: content || null, streamId: `recognizer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, }; } } /** * 提取内容片段 * @param content 原始文本内容 * @returns 识别结果 */ async extractContentFragments(content) { if (!this.recognizeLLM) { throw new Error('RecognizeLLM not configured'); } const messages = [ { role: 'system', content: ` ## 身份定义 你是一个专业的文本提取专家,用来识别目标文本并提取出来 ## 文本识别逻辑 ${this.config.recognizePrompt} ## 输出格式 请根据文本识别逻辑,提取出目标文本,并返回一个JSON对象,格式如下: 如果成功识别出目标文本,则返回如下格式: { "success": true, "data": [{"type": "xxx", "text": "识别出的文本片段1"}, {"type": "xxx", "text": "识别出的文本片段2"}] } 如果未识别出目标文本,则返回如下格式: { "success": false, "data": [] }` }, { role: 'user', content: `请从以下文本中提取出目标文本片段 文本内容: ${content}` } ]; try { // 由于 ILLM 接口限制,我们需要通过配置来设置 response_format const response = await this.recognizeLLM.chat(messages); if (!response.content) { return { success: false, data: [] }; } const result = JSON.parse(response.content); // 对识别结果进行去重处理 if (result.success && result.data.length > 0) { const uniqueData = this.deduplicateRecognizeData(result.data); console.log(`去重前: ${result.data.length} 个片段,去重后: ${uniqueData.length} 个片段`); return { success: result.success, data: uniqueData }; } return result; } catch (error) { console.error('Content fragment extraction error:', error); return { success: false, data: [] }; } } /** * 对识别数据进行去重处理 * @param data 原始识别数据 * @returns 去重后的数据 */ deduplicateRecognizeData(data) { const seen = new Set(); const uniqueData = []; for (const item of data) { const text = typeof item === 'string' ? item : item.text; if (!seen.has(text)) { seen.add(text); uniqueData.push(item); } else { console.log(`去重跳过重复文本: ${text}`); } } return uniqueData; } /** * 执行 embed 替换 * @param content 原始内容 * @param embededTexts 需要替换的文本映射 * @returns 替换后的内容 */ performEmbed(content, embededTexts) { let result = content; for (const embededText of embededTexts.filter(item => item.target)) { // 使用正则表达式进行精确替换,避免误替换 const regex = new RegExp(`${this.escapeRegExp(embededText.origin)}`, 'g'); result = result.replace(regex, embededText.target); } return result; } /** * 转义正则表达式特殊字符 * @param string 需要转义的字符串 * @returns 转义后的字符串 */ escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } } exports.Recognizer = Recognizer;