@cogitator-ai/ 擦伤
为Cogitator AI代理的检索-增强生成管道. 装入文档,将其块入,嵌入,取回,并重新排序——所有文件都使用单个构建器API.
安装
pnpm add @cogitator-ai/rag
# Optional dependencies for specific loaders
pnpm add cheerio # HTML and web page loading
pnpm add papaparse # CSV loading
pnpm add pdf-parse # PDF loading
@cogitator-ai/memory (可选对等点)提供嵌入式服务,向量存储(InMemoryEmbeddingAdapter, (中文(简体) ), PostgresAdapter, (中文(简体) ), QdrantAdapter和 HybridSearch 以下使用; 任意 EmbeddingService 编号 : EmbeddingAdapter 执行工作。 网站文件 : RAG 区域咨询小组, (中文(简体) ), 装入器, (中文(简体) ), 弹簧, (中文(简体) ), 检索, (中文(简体) ), 重新排队。 。 。 .
特征
- 7 个文件装入器 ——文本,Markdown,JSON,CSV,HTML,PDF,网页
- 3 弹簧策略 固定大小、递归和语义(嵌入式)
- 4 检索战略 相近性、 MMR、 混合( BM25 + 向量),多克
- 2个调档 ——基于LLM的分数,Cohere Rerank API
- 管道构建器 - 流利的API 连接所有的东西
- 代理工具 ——入场
rag_search财务报告和审定财务报表rag_ingest协作者代理工具 - Zod 验证 ——有运行时间检查的型式安全配置
快速启动
import { RAGPipelineBuilder, TextLoader } from '@cogitator-ai/rag';
import { InMemoryEmbeddingAdapter, OpenAIEmbeddingService } from '@cogitator-ai/memory';
const pipeline = new RAGPipelineBuilder()
.withLoader(new TextLoader())
.withEmbeddingService(
new OpenAIEmbeddingService({
apiKey: process.env.OPENAI_API_KEY!,
})
)
.withEmbeddingAdapter(new InMemoryEmbeddingAdapter())
.withConfig({
chunking: { strategy: 'recursive', chunkSize: 500, chunkOverlap: 50 },
retrieval: { strategy: 'similarity', topK: 5, threshold: 0.3 },
})
.build();
// ingest documents from a file or directory
const { documents, chunks, unchanged } = await pipeline.ingest('./docs');
// query the knowledge base (options override the configured retrieval for this call)
const results = await pipeline.query('How does authentication work?', { topK: 3 });
for (const r of results) {
console.log(`[${r.score.toFixed(3)}] ${r.source}: ${r.content.slice(0, 100)}...`);
}
每个存储块都携带文档的加载器元数据(如:Markdown前事,PDF页码,CSV元数据列,页面标题/URL)+ documentId, (中文(简体) ), source, (中文(简体) ), sourceType, (中文(简体) ), order, (中文(简体) ), startOffset 财务报告和审定财务报表 endOffset。检索结果显示文档路径/URL result.source 而其余的则在 result.metadata。 。 。 .
文档加载器
| 装入器 | 格式 | 可选代码 | 页:1 |
|---|---|---|---|
TextLoader | .txt | —— 说 | 文件与目录 |
MarkdownLoader | .md, (中文(简体) ), .mdx | —— 说 | 可选前题 - > 元数据 |
JSONLoader | .json | —— 说 | 可配置的内容字段 |
JSONLLoader | .jsonl, (中文(简体) ), .ndjson | —— 说 | 每行1个文档,空行跳过 |
CSVLoader | .csv | papaparse | 列选择, row 元数据中的数字 |
HTMLLoader | .html, (中文(简体) ), .htm | cheerio | CSS 选择器、 脚本/ 样式已删除、 块形文字 |
PDFLoader | .pdf | pdf-parse | 整份文件或每页一份文件 |
WebLoader | http://, (中文(简体) ), https:// | cheerio | HTML 、 纯文本和 JSON 页面; SSRF 保护 |
目录来源( E)TextLoader, (中文(简体) ), MarkdownLoader)以排序的文件名顺序加载.
WebLoader 安全性
WebLoader 块回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回 页:1 这些地址。 检查在连接时间运行,每一次重定向跳出,所以DNS重新绑定无法绕过. 要占用内联网网站,请明确选择:
const intranet = new WebLoader({
allowPrivateNetwork: true,
headers: { Authorization: `Bearer ${process.env.WIKI_TOKEN}` },
timeoutMs: 15_000,
maxResponseBytes: 10 * 1024 * 1024,
});
机器人 . txt
传出 a robots 校验器和每个URL都与它的站点的机器人核对. txt在取出前,包含重定向目标. 不允许的 URL 负载失败 RobotsDisallowedError 并且从来没有要求. RobotsPolicy 从 @cogitator-ai/core rFC 9309的每个站点读取并缓存机器人.txt(最长的对接获胜, * 财务报告和审定财务报表 $ 模式,一个 4xx 机器人.txt 允许一切,一个 5xx 或网络失败 不允许任何东西,直到缓存到期 :
import { RobotsPolicy } from '@cogitator-ai/core';
import { WebLoader } from '@cogitator-ai/rag';
const userAgent = 'NewsBot/1.0 (+https://example.com/bot)';
const loader = new WebLoader({
headers: { 'user-agent': userAgent },
robots: new RobotsPolicy({ userAgent }), // matches "User-agent: NewsBot" groups, else "*"
});
反应被解压(gzip, deflate, br),使用宣布的字符集解码,并限于 maxResponseBytes (默认50MB). 超过极限的一页失败负载, 或者使用 onTooLarge: 'truncate' 装入它的第一个 maxResponseBytes 并标出 truncated: true 在其元数据中。 这让一个超过极限的页面不被失败,但是它的第一部分只在页面放入时才持有文本,许多新闻页面将兆字节的脚本放在文章前,这样就可以将限制保留在您所加载的页面之上. 非文本内容类型被拒绝.
import {
MarkdownLoader,
WebLoader,
CSVLoader,
PDFLoader,
JSONLoader,
JSONLLoader,
} from '@cogitator-ai/rag';
const md = new MarkdownLoader({ stripFrontmatter: true });
const web = new WebLoader({ selector: 'article' });
const csv = new CSVLoader({ contentColumn: 'body', metadataColumns: ['title'] });
const pdf = new PDFLoader({ splitPages: true }); // one document per page
const json = new JSONLoader({ contentField: 'text' });
const jsonl = new JSONLLoader({ contentField: 'text' });
管道有一个装载器 withChunker() 编号 : withRetriever() 用于替换配置将创建的块或取回器.
弹簧策略
| 战略 | 类 | 最佳服务 |
|---|---|---|
fixed | FixedSizeChunker | 简单、可预测的块大小 |
recursive | RecursiveChunker | 尊重段落/判决界限 |
semantic | SemanticChunker | 类组类似句子 |
固定规模
将文本分割成 准确的块 chunkSize 有可选重叠的字符.
import { FixedSizeChunker } from '@cogitator-ai/rag';
const chunker = new FixedSizeChunker({ chunkSize: 500, chunkOverlap: 50 });
const chunks = chunker.chunk(text, documentId);
递归
可配置分离器上的分数( 分数)\n\n, (中文(简体) ), \n, (中文(简体) ), . , (中文(简体) ), 试图使段落和句子保持完整。 Chunk 总是指向正文,块数不会超过 chunkSize,句子标出句子时保留了句子,重叠从未产生一个完全包含在上一个句子中的块.
import { RecursiveChunker } from '@cogitator-ai/rag';
const chunker = new RecursiveChunker({
chunkSize: 500,
chunkOverlap: 50,
separators: ['\n\n', '\n', '. ', ' '],
});
语义
使用在句子(和空行相隔的块)之间嵌入相似性来寻找自然断点. Async — 需要一个 EmbeddingService。 。 。 .
import { SemanticChunker } from '@cogitator-ai/rag';
const chunker = new SemanticChunker({
embeddingService,
breakpointThreshold: 0.5,
minChunkSize: 100,
maxChunkSize: 2000,
});
const chunks = await chunker.chunk(text, documentId);
工厂
import { createChunker } from '@cogitator-ai/rag';
const chunker = createChunker(
{ strategy: 'recursive', chunkSize: 500, chunkOverlap: 50 },
embeddingService
);
检索策略
| 战略 | 类 | 说明 |
|---|---|---|
similarity | SimilarityRetriever | 纯余弦相似性搜索 |
mmr | MMRRetriever | 最大边际相关性——平衡相关性和多样性 |
hybrid | HybridRetriever | 将 BM25 关键字搜索与矢量搜索( RRF) 合并 |
multi-query | MultiQueryRetriever | 将查询扩展为变量,合并结果 |
RAGPipelineBuilder 构建检索器匹配 retrieval.strategy 没有自定义时 withRetriever() 给出:
const pipeline = new RAGPipelineBuilder()
.withLoader(loader)
.withEmbeddingService(embeddingService)
.withEmbeddingAdapter(embeddingAdapter)
.withHybridSearch(new HybridSearch({ embeddingAdapter, embeddingService })) // 'hybrid'
.withQueryExpander(expandWithLLM) // 'multi-query'
.withConfig({
chunking: { strategy: 'recursive', chunkSize: 500, chunkOverlap: 50 },
retrieval: { strategy: 'multi-query', multiQueryCount: 3, topK: 5 },
})
.build();
| 战略 | 建筑师要求 | 使用的配置 |
|---|---|---|
similarity | —— 说 | topK, (中文(简体) ), threshold |
mmr | —— 说 | mmrLambda, (中文(简体) ), topK, (中文(简体) ), threshold |
hybrid | withHybridSearch(hybridSearch, weights?) | topK, (中文(简体) ), threshold |
multi-query | withQueryExpander(fn) | multiQueryCount (最大变种), topK; 当 withHybridSearch() 设置, 否则相似 |
相似性
import { SimilarityRetriever } from '@cogitator-ai/rag';
const retriever = new SimilarityRetriever({
embeddingAdapter,
embeddingService,
defaultTopK: 10,
defaultThreshold: 0.3,
});
const results = await retriever.retrieve('What is TypeScript?');
产妇死亡率
通过惩罚与已经选定的结果过于相似的结果来减少冗余。 如果向量商店不返回所储存的向量(例如Qdrant),则将候选人重新嵌入来计算多样性.
import { MMRRetriever } from '@cogitator-ai/rag';
const retriever = new MMRRetriever({
embeddingAdapter,
embeddingService,
defaultLambda: 0.7, // 1.0 = pure relevance, 0.0 = pure diversity
defaultTopK: 10,
});
混合
要求数 HybridSearch 从 @cogitator-ai/memory吞噬了断层 RAGPipeline 自动添加到 HybridSearch BM25 索引中(执行中的任何检索器) ChunkIndexer.indexChunk() 在摄入量上通知),仅关键词的点击被映射回它们的块,文件和元数据.
import { HybridRetriever } from '@cogitator-ai/rag';
import { HybridSearch } from '@cogitator-ai/memory';
const retriever = new HybridRetriever({
hybridSearch,
defaultWeights: { bm25: 0.4, vector: 0.6 },
});
多查询
生成查询变量并合并结果(每块最佳分数). 您提供了扩展函数( 典型的 LLM 调用) 。 原始查询总是被搜索;变体被删除并封顶 defaultMaxQueries 编号 : multiQueryCount。如果扩展失败,则单独使用原始查询;如果每次检索失败,则抛出错误.
import { MultiQueryRetriever } from '@cogitator-ai/rag';
const retriever = new MultiQueryRetriever({
baseRetriever: similarityRetriever,
expandQuery: async (query) => {
const response = await llm.generate(
`Generate 3 alternative phrasings for: "${query}". Return one per line.`
);
return response.split('\n').filter(Boolean);
},
defaultMaxQueries: 3,
});
工厂
import { createRetriever } from '@cogitator-ai/rag';
const retriever = createRetriever({
strategy: 'mmr',
embeddingAdapter,
embeddingService,
lambda: 0.7,
topK: 10,
});
重新排队
重新排序的回分数检索结果更精确. 通过管道配置启用 .
LLM 调整键
使用任何LLM来分分0-10尺度的文档相关性.
import { LLMReranker } from '@cogitator-ai/rag';
const reranker = new LLMReranker({
generateFn: (prompt) => llm.generate(prompt),
});
const pipeline = new RAGPipelineBuilder()
.withLoader(loader)
.withEmbeddingService(embeddingService)
.withEmbeddingAdapter(embeddingAdapter)
.withReranker(reranker)
.withConfig({
chunking: { strategy: 'recursive', chunkSize: 500, chunkOverlap: 50 },
retrieval: { strategy: 'similarity', topK: 20 },
reranking: { enabled: true, topN: 5 },
})
.build();
当模型的答案没有排序时(例如,一个将符号预算花在推理上的推理模型返回了空内容)或 generateFn 扔出后, 重新排序器会保留检索顺序并记录一个警告 。 过 onError 以观察后退,或 strict: true 将制作 rerank() 丢出一个 LLMRerankError (原始答案输入) response 和原始错误 cause:
const observed = new LLMReranker({
generateFn: (prompt) => llm.generate(prompt),
onError: (error, { query, response }) => logger.warn({ query, response }, error.message),
});
const strict = new LLMReranker({ generateFn: (prompt) => llm.generate(prompt), strict: true });
柯尔雷克
使用 Cohere Rerank API(默认为rerank-v3.5).
import { CohereReranker } from '@cogitator-ai/rag';
const reranker = new CohereReranker({
apiKey: process.env.COHERE_API_KEY!,
model: 'rerank-v3.5',
});
代理整合
使用 ragTools() 给一个合作者代理进入你的知识库.
import { Agent, tool } from '@cogitator-ai/core';
import { RAGPipelineBuilder, TextLoader, ragTools } from '@cogitator-ai/rag';
import { InMemoryEmbeddingAdapter, OpenAIEmbeddingService } from '@cogitator-ai/memory';
const pipeline = new RAGPipelineBuilder()
.withLoader(new TextLoader())
.withEmbeddingService(new OpenAIEmbeddingService({ apiKey: process.env.OPENAI_API_KEY! }))
.withEmbeddingAdapter(new InMemoryEmbeddingAdapter())
.withConfig({
chunking: { strategy: 'recursive', chunkSize: 400, chunkOverlap: 50 },
retrieval: { strategy: 'similarity', topK: 3, threshold: 0.3 },
})
.build();
await pipeline.ingest('./knowledge-base');
const [ragSearch, ragIngest] = ragTools(pipeline, { allowedRoots: ['./knowledge-base'] });
const agent = new Agent({
name: 'docs-assistant',
model: 'openai/gpt-6.1-sol',
instructions: 'Use rag_search to find information before answering.',
tools: [tool(ragSearch), tool(ragIngest)],
});
RAGTool 对象携带 Zod 参数计划并可以传递到 tool() 直接; createSearchTool(pipeline) 财务报告和审定财务报表 createIngestTool(pipeline, options) 一次建一个. rag_search 回到管道的配置 topK编号 :threshold 当模型省略时 limit编号 :threshold。 。 。 .
警卫: rag_ingest 阅读模型通过的任何来源。 当工具暴露于 LLM 时,用 allowedRoots (路径被卡通化,所以) .. 反向和同链的出逃被拒绝)和 allowUrls: false 如果不需要网络摄取。 URL 摄入通过 SSRF 保护 WebLoader 当管道使用一个.
重新摄入、清除和范围界定
输入源再次取代了它所储存的管道: 一旦新块被嵌入,那的旧块 source 删除,因此不留下重复文本或陈旧文本。 文档ID来自源(外加行,项目或其中的一页),并从文档ID和位置中块出ID,因此它们在再摄入时保持不变. removeSource(source) 删除源,使用 source 结果载( 文件的解析路径) .
未更改的来源被跳过: 每个块保留文档的散列, 块的块 fingerprint 和嵌入模型,以及 ingest() 当每次散列匹配时, 均不嵌入任何 unchanged ”)。 内置的块头有指纹 块头有你自己的需要readonly fingerprint = 'my-chunker:v1'用于跳过其来源。 被更改的源被写成新一代先被写入后被删除的旧源,因此查询永远不会发现它缺失,其不变的文档会保留它们的向量.
await pipeline.ingest('./docs/handbook.md'); // again after an edit: replaced, not duplicated
await pipeline.removeSource('/srv/app/docs/handbook.md');
每个查询范围都限制在 sourceType: 'document',因此与代理存储器共享的商店(例如Postgres存储器)从不返回消息或事实,包括私人信息或事实,作为RAG的结果. namespace 在配置中,几个管道 分开在一个商店 retrieval.filter (或 filter 查询选项)进一步缩小搜索范围:
const pipeline = new RAGPipelineBuilder()
// ...
.withConfig({ chunking, retrieval: { strategy: 'similarity', topK: 5 }, namespace: 'handbook' })
.build();
await pipeline.query('vacation policy', { filter: { metadata: { lang: 'en' } } });
替换和去除需要一个嵌入存储器 deleteByFilter每一个内置商店都有 在没有定制商店的情况下,附加了被重新吞噬的源头(并附有警告)和 removeSource 掷出. 具有自身索引的检索器( E)ChunkIndexer)通过 removeChunks(),作为 HybridRetriever 没错.
管道站点
const stats = pipeline.getStats();
console.log(stats.documentsIngested);
console.log(stats.chunksStored);
console.log(stats.queriesProcessed);
实例
见 examples/rag/ 可运行示例 :
- 01-基本检索.ts ——调取文档并运行语义查询
- 02 -- -- 战略 - 比较固定、递归和语义块
- 03号特工与破烂 ——配备RAG搜索工具的全代理
佐德·斯克马斯
import {
ChunkingStrategySchema,
ChunkingConfigSchema,
RetrievalStrategySchema,
RetrievalConfigSchema,
RerankingConfigSchema,
RAGPipelineConfigSchema,
} from '@cogitator-ai/rag';
许可证
麻省理工学院