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
JavaScript
;
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;