| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat: 答案标注所引用的来源片段 Co-authored-by: gcw_FGGzX14K<1324326841@qq.com> | 1 个月前 | |
fix: 修复超长段落不切分的问题 段落长度超过 max_chars 时,原来会整段作为一个块输出,可能超出嵌入模型的上下文长度。 现在优先按换行把超长段落切开,切不动才硬切,再走原来的累积逻辑。 - chunker.py:新增 _split_long,累积前先切超长段落 - tests/test_chunker.py:补两个用例(切分上限、内容不丢失) - CHANGELOG.md:记录本次修复 改前:chunk_text('x'*2500, max_chars=1000) 返回 [2500] 改后:每块都 <= 1000 本机 15 个测试全部通过。 | 1 个月前 | |
feat: 新增 --embed-backend 与 search,索引检索不再强依赖 Ollama 以往索引和检索都必须先装好 Ollama 并 pull 嵌入模型,否则第一条 add 就报 「can't reach Ollama」。想试试本地笔记检索的人得先下几个 GB 的模型,多数人在 这一步就走了——而这一步其实并非必要。 把「嵌入」抽成可换的后端: - ollama(默认):行为完全不变,现有索引和用法不受影响 - onnx:用 chromadb 自带的 all-MiniLM-L6-v2 在本机算,384 维,不需要模型服务 再新增 search 命令,把「检索」从「生成」里拆出来:只打印最相近的片段和距离, 不调用语言模型。于是配 onnx 后端时,索引和检索完全不需要 Ollama: lorebox add ./notes --embed-backend onnx lorebox search "笔记里写过什么" 两点需要留意: - 两个后端的向量宽度不同(384 / 768),不能放进同一个 collection。store 在建索引 时把后端写进 collection 的 metadata,之后用另一个后端访问会直接报错并说明原因, 而不是让 chromadb 在深处抛一个和后端无关的错误。早期建的索引没有这个字段,按 ollama 处理(那时只有这一个后端)。 - onnx 模型在首次使用时下载一次,之后才真正离线。这一点写进了 README 的限制段, 不把「离线」说得比实际更满。 顺带把 Ollama 的报错变成引导:add / search 连不上 Ollama 时,会提示可以改用 --embed-backend onnx,而不是只丢一句连接失败。 改动: - 新增 lorebox/embed.py:后端分发与维度表 - store.py:__init__ 接受 backend,记录并校验,混用时给出明确错误 - retrieve.py / ask.py:透传 backend - cli.py:add / ask / search 新增 --embed-backend;新增 search 命令;新增 _ollama_hint - pyproject.toml:新增可选依赖 local-embed = ["onnxruntime>=1.17"] - README / docs/architecture.md / CHANGELOG.md:同步新能力与限制 验证:全量 92 个测试通过(74 → 92)。新增 18 个:test_embed 8 个(后端分发、 忽略模型参数、空输入、未知后端、维度表)、test_cli_search 5 个(不调用模型、 空结果、前缀短路、后端透传、报错引导)、test_store 3 个(后端混用保护、同后端 放行、旧索引按 ollama 兼容)、test_retrieve 2 个(backend 透传与默认值)。 另实测 python -m lorebox --help 列出 9 个命令,search / add / ask 的 --embed-backend 选项输出正常。 | 16 天前 | |
fix: resolve name shadowing between ask command and ask module | 1 个月前 | |
feat: ask 新增 --json,输出结构化结果 想把问答接进脚本或别的工具,只能去解析人类可读的纯文本输出,稍微改一下格式就 崩了。 现在加 --json,输出一个对象:question、answer、sources(每条带编号、文件名和 80 字摘要)。中文不转义(ensure_ascii=False),否则中文笔记的场景下满屏都是 \u 转义码,没法看。 用法上的一个取舍:--json 时忽略 --no-sources,来源照样带上。理由是解析 JSON 的 调用方基本都需要来源,而少一个字段会让下游反而更麻烦。这一点写进了代码注释。 顺带把来源摘要的截取逻辑抽成 _preview/_source_entry,文本输出与 JSON 输出共用, 避免两处各写一份、以后改一处忘一处。 改动: - cli.py:ask 新增 --json;抽出 _preview 与 _source_entry - tests/test_cli_ask_json.py:新增 4 个用例(结构化字段、中文不转义、 --no-sources 不影响 JSON、不加 --json 时输出仍是纯文本) 验证:全量 57 个测试通过(53 → 57),原有 53 个未受影响 | 17 天前 | |
feat: ask 新增 --stream,回答边生成边打印 改前 ollama.generate 写死 stream=False,长回答必须等整个生成完才一次性显示, 中间几十秒终端没有任何反馈,看起来像卡死。现在 ask 加了 --stream:ollama.py 新增 generate_stream,按行解析 Ollama 的 NDJSON 流逐段吐出;cli.py 的 ask 在 --stream 下先取 chunks、拼好提示词,再用 generate_stream 逐段打印,最后补一个换行,来源列表 照旧在末尾打印。不加 --stream 时输出与以前完全一致。 为让「检索」和「生成」能分开调用,ask.py 抽出了 retrieve_chunks() 与 build_prompt(), answer() 的签名和返回值没动,既有调用方与测试不受影响。 --stream 与 --json 不能同时用:--json 要求完整结果才能序列化, 两者同时给出时直接报 UsageError 退出,而不是静默忽略其中一个。 验证:.venv\Scripts\python.exe run_tests.py,69 个用例全绿(原有 57 个未受影响, 新增 12 个:test_ollama_stream.py 6 个覆盖分片顺序、空行/空分片跳过、done 提前收尾、 非 200 与连不上转成 OllamaError、请求确实带 stream=True;test_cli_ask_stream.py 6 个 覆盖逐段打印、提示词含检索上下文、默认不流式、--no-sources 组合、--json 互斥)。 | 17 天前 | |
feat: 新增 ~/.lorebox.toml,常用选项不必每次重复 改前: --store、--model、--embed-model、--top-k、--min-score、 --max-chars、--overlap、--embed-backend 只能在命令行上重复敲, ~/.lorebox.toml 即使存在也没人读。 改后:新增 lorebox/config.py,从 ~/.lorebox.toml 读这 8 个键当默认值, 优先级是命令行 → 配置文件 → 内置默认。文件缺失是正常状态,空文件与没有文件 完全等价,所以现有用法一个字都不用改;键名写错、类型不符、多余的键只在 stderr 警告并跳过该项;TOML 语法错误退回内置默认并提示,不让命令起不来(连 --help 都还能用)。embed_backend 写了不支持的值时提示可选值并退回 ollama,而不是 抛一个看不出值来自哪里的 UsageError。 文件按 utf-8-sig 解码:实测 PowerShell / 记事本写出带 BOM 的文件,用 utf-8 解码会被 TOML 当成语法错误、整份配置静默失效——这条是端到端验证时才撞出来的, 不是设想出来的。 实现上走 click 的 default_map,各子命令函数一行没改;键与命令选项的映射集中 在 cli.CONFIG_KEYS / cli.CONFIG_PARAMS。pyproject.toml 加 tomli(仅 python_version < "3.11"),因为 3.11 起标准库才有 tomllib。 验证: - run_tests.py 全量 154 个用例通过(原有 129 个一个没坏),新增 tests/test_config.py(12 个)与 tests/test_cli_config.py(13 个),覆盖 验收里的「配置文件生效」「命令行覆盖配置」「文件损坏不崩」,另加两条防漂移 测试:配置键必须是某个命令的真实选项、也不能是没人用的死键。 - 端到端实测(不打 mock):LOREBOX_CONFIG 指向临时配置(store=临时目录)后 python -m lorebox list 在该目录建索引并输出 index is empty;再给 --store 时改用命令行给的目录,两个目录分别验证确实各自被创建。 - 没有配置文件时行为不变:lorebox list 仍用 .lorebox/(有断言)。 - --help 显示的仍是内置默认值,这点已写进 README 与架构文档,避免误以为 配置没生效。 | 5 天前 | |
feat: 新增 doctor 命令,一次检查本地环境 装完之后出问题只能靠猜:OllamaError 只覆盖「连不上」这一种情况,模型没 pull 时 只会看到 Ollama 返回的原始报错;store 目录不可写,也往往要等 add 跑到一半才暴露。 doctor 把这些一次查完并逐项打印: - Ollama 是否连得上(连不上时打印具体地址和启动提示) - 本地模型数量,以及要用的对话模型与嵌入模型是否已经 pull (比较时去掉 :tag,避免 qwen2.5 与 qwen2.5:latest 被当成两个不同的东西) - store 目录是否可用,以及当前索引的文件数与片段数 有任一项失败就以非零状态退出,方便放进脚本当前置检查。Ollama 连不上时不会提前 中断,仍会把 store 那一项报出来——这样用户一次就能看到全部问题,而不是修一个 再撞下一个。 改动: - ollama.py:新增 _get() 与 list_models()(读 /api/tags) - cli.py:新增 doctor 命令 - tests/test_ollama.py:新增 3 个用例(解析模型名、空列表、连不上时抛 OllamaError) - tests/test_cli_doctor.py:新增 3 个用例(全部通过、模型缺失、Ollama 不可达) 验证:全量 39 个测试通过,原有 33 个未受影响;另在本地手工跑了一次,逐项输出与 退出码符合预期 | 23 天前 | |
feat: add export subcommand to dump the index as JSON | 1 个月前 | |
feat: 支持索引 .docx 与 .epub 文档(可选 formats 依赖) 改前现象:add 只认 Markdown/纯文本/RST/PDF,真实笔记里大量 Word 文档和 网页剪存(epub)静默进不了索引,想索引只能手动转文本。 改后现象:cli._read() 新增 .docx(python-docx 抽段落)与 .epub (ebooklib 读章节 + beautifulsoup4 剥 HTML 取正文)两个分支; EXTENSIONS 默认集合加入这两种后缀;pyproject.toml 新增可选依赖组 formats = ["python-docx", "ebooklib", "beautifulsoup4"]。没装可选库时 _read_docx/_read_epub 抛带 pip install "lorebox[formats]" 提示的明确错误, 而不是 ImportError 栈——延续「不静默跳过文件」的既有约定。 验证方式:新增 tests/test_cli_formats.py,7 个用例覆盖验收标准——用 python-docx/ebooklib 造最小 docx/epub 断言 _read 能抽出文本、mock 掉 embed 后 add 能真正建索引并被 store 检索到、把 docx/ebooklib 模块置空时 给出 formats 提示;全量 run_tests.py 129 个测试全部通过。 | 6 天前 | |
feat: 新增 import 命令,export 支持带向量导出 export 一直是只能出不能进:导出的 dump 里只有 source 和 text,既没有对应的导入 命令,也做不了「换机器迁移索引」——对方拿着 dump 想把索引建起来,只能把所有文件 重新嵌入一遍,那还不如直接 add。 现在 export 加了 --with-embeddings:导出时带上 id、source、sha 和向量。新增 import 命令读回这种 dump,直接写进 store,全程不需要 Ollama。 两点说明: - 不带 --with-embeddings 时,导出的仍是原来的 [{source, text}] 格式,已有用法和 脚本不受影响。 - dump 里只要有一条缺向量就直接报错退出,并提示用 --with-embeddings 重新导出。 不做「静默重新嵌入」:那需要 Ollama 在场,会把这个只读的导入操作变成一次意外的 网络调用,用户也看不出发生了什么。宁可不导入,也不要导入半个索引。 实现细节:store.dump() 用 chromadb 的 get(include=[documents, metadatas, embeddings]) 取完整记录,返回的向量是 numpy 数组,转成普通 list 后才能写 JSON。import 的函数名 是 import_(import 是关键字),命令名用 name="import" 注册,命令行里就是 lorebox import。 改动: - store.py:新增 dump() - cli.py:export 新增 --with-embeddings;新增 import 命令 - tests/test_cli_import.py:新增 5 个用例(导出后再导入内容的闭环、dump 含 id/文本/ 向量、不带开关时格式不变、无向量 dump 被拒绝、空 dump 不算错误) 验证:全量 74 个测试通过(69 → 74),原有 69 个未受影响 | 16 天前 | |
feat: list 按文件聚合输出,加 --chunks 查看明细 list 此前每个片段打一行。一个文件切成几十块就是几十行输出,文件一多根本看不出 索引里到底有哪些文件——而这正是这个命令要回答的问题。 现在默认按文件汇总,每个文件一行,显示块数与字符数: notes/a.md 2 chunk(s) 6 char(s) notes/b.md 1 chunk(s) 5 char(s) 想看逐块内容时加 --chunks,输出的还是原来那种「来源 + 前 60 字摘要」的逐行格式。 聚合用 dict 保证顺序稳定。 验证: - 新增 tests/test_cli_list.py 三个用例:空索引、按文件汇总(2 个文件断言输出恰好 2 行)、 --chunks 逐块输出(3 个片段断言 3 行) - 全量 24 个测试通过,原有 21 个未受影响 | 1 个月前 | |
fix: 修复 GBK 编码文件读取失败的问题 _read 此前只用 UTF-8 打开文本文件。Windows 上用记事本另存的中文 txt 通常是 GBK 编码,一读就抛 UnicodeDecodeError,把整个 add 中断——一个文件编码不对, 后面的文件全都不索引了。 现在依次尝试 UTF-8、GB18030,两者都不匹配时退回带替换字符的读取并打印一行 警告。选 GB18030 而不是 GBK,是因为它是 GBK 的超集,除了常见的 GBK 文件还能 覆盖四字节形式;最后一步保留替换字符是为了不让单个异常文件毁掉整次索引, 但会明确提示,避免静默把乱码写进索引。 验证: - 用 GBK 写一个内容为「中文笔记」的 txt,改前抛 UnicodeDecodeError,改后正确读出 - 新增 4 个用例:UTF-8、GBK、GB18030 四字节、以及无法识别编码时不抛异常 - 全量 19 个测试通过,原有 15 个未受影响 | 1 个月前 | |
feat: 新增 --embed-backend 与 search,索引检索不再强依赖 Ollama 以往索引和检索都必须先装好 Ollama 并 pull 嵌入模型,否则第一条 add 就报 「can't reach Ollama」。想试试本地笔记检索的人得先下几个 GB 的模型,多数人在 这一步就走了——而这一步其实并非必要。 把「嵌入」抽成可换的后端: - ollama(默认):行为完全不变,现有索引和用法不受影响 - onnx:用 chromadb 自带的 all-MiniLM-L6-v2 在本机算,384 维,不需要模型服务 再新增 search 命令,把「检索」从「生成」里拆出来:只打印最相近的片段和距离, 不调用语言模型。于是配 onnx 后端时,索引和检索完全不需要 Ollama: lorebox add ./notes --embed-backend onnx lorebox search "笔记里写过什么" 两点需要留意: - 两个后端的向量宽度不同(384 / 768),不能放进同一个 collection。store 在建索引 时把后端写进 collection 的 metadata,之后用另一个后端访问会直接报错并说明原因, 而不是让 chromadb 在深处抛一个和后端无关的错误。早期建的索引没有这个字段,按 ollama 处理(那时只有这一个后端)。 - onnx 模型在首次使用时下载一次,之后才真正离线。这一点写进了 README 的限制段, 不把「离线」说得比实际更满。 顺带把 Ollama 的报错变成引导:add / search 连不上 Ollama 时,会提示可以改用 --embed-backend onnx,而不是只丢一句连接失败。 改动: - 新增 lorebox/embed.py:后端分发与维度表 - store.py:__init__ 接受 backend,记录并校验,混用时给出明确错误 - retrieve.py / ask.py:透传 backend - cli.py:add / ask / search 新增 --embed-backend;新增 search 命令;新增 _ollama_hint - pyproject.toml:新增可选依赖 local-embed = ["onnxruntime>=1.17"] - README / docs/architecture.md / CHANGELOG.md:同步新能力与限制 验证:全量 92 个测试通过(74 → 92)。新增 18 个:test_embed 8 个(后端分发、 忽略模型参数、空输入、未知后端、维度表)、test_cli_search 5 个(不调用模型、 空结果、前缀短路、后端透传、报错引导)、test_store 3 个(后端混用保护、同后端 放行、旧索引按 ollama 兼容)、test_retrieve 2 个(backend 透传与默认值)。 另实测 python -m lorebox --help 列出 9 个命令,search / add / ask 的 --embed-backend 选项输出正常。 | 16 天前 | |
feat: add stats subcommand to summarize the index | 1 个月前 | |
feat: stats 支持按文件列出明细 stats 原本只给文件数、片段数、字符数三个总数,看不出哪个文件占得最多、哪个文件的 片段数异常。资料一多,想找「最占地方的那几份」只能自己去翻 .lorebox 目录。 现在加了 --files:按文件列出片段数与字符数,按字符数从大到小排;--top N 只看前几 个。原有的三行汇总保持不变,不加参数时输出与以前完全一样。 边界情况:空索引加 --files 不会崩(没有文件可列时直接返回),这一点单独写了用例。 改动: - cli.py:stats 新增 --files 与 --top - tests/test_cli_stats_files.py:新增 4 个用例(汇总不变、按字符数排序、--top 生效、 空索引不崩) 验证:全量 43 个测试通过(39 → 43),原有 39 个未受影响 | 20 天前 | |
feat: add 支持更多文件后缀,并可用 --ext 追加 _walk 里把后缀写死成 .md/.txt/.pdf,.markdown 和 .rst 会被静默跳过。用户看到 文件没进索引,却没有任何提示,也不知道问题出在后缀上。 现在默认集合加入 .markdown 与 .rst,并抽出 EXTENSIONS 常量;另外 add 新增 --ext,可以追加自己的后缀(可重复传,例如 --ext .org --ext .adoc)。 验证: - 新增 tests/test_cli_walk.py 三个用例:默认后缀集合、追加后缀生效、 不存在的路径被跳过 - 全量 27 个测试通过,原有 24 个未受影响 | 1 个月前 | |
feat: 新增 ~/.lorebox.toml,常用选项不必每次重复 改前: --store、--model、--embed-model、--top-k、--min-score、 --max-chars、--overlap、--embed-backend 只能在命令行上重复敲, ~/.lorebox.toml 即使存在也没人读。 改后:新增 lorebox/config.py,从 ~/.lorebox.toml 读这 8 个键当默认值, 优先级是命令行 → 配置文件 → 内置默认。文件缺失是正常状态,空文件与没有文件 完全等价,所以现有用法一个字都不用改;键名写错、类型不符、多余的键只在 stderr 警告并跳过该项;TOML 语法错误退回内置默认并提示,不让命令起不来(连 --help 都还能用)。embed_backend 写了不支持的值时提示可选值并退回 ollama,而不是 抛一个看不出值来自哪里的 UsageError。 文件按 utf-8-sig 解码:实测 PowerShell / 记事本写出带 BOM 的文件,用 utf-8 解码会被 TOML 当成语法错误、整份配置静默失效——这条是端到端验证时才撞出来的, 不是设想出来的。 实现上走 click 的 default_map,各子命令函数一行没改;键与命令选项的映射集中 在 cli.CONFIG_KEYS / cli.CONFIG_PARAMS。pyproject.toml 加 tomli(仅 python_version < "3.11"),因为 3.11 起标准库才有 tomllib。 验证: - run_tests.py 全量 154 个用例通过(原有 129 个一个没坏),新增 tests/test_config.py(12 个)与 tests/test_cli_config.py(13 个),覆盖 验收里的「配置文件生效」「命令行覆盖配置」「文件损坏不崩」,另加两条防漂移 测试:配置键必须是某个命令的真实选项、也不能是没人用的死键。 - 端到端实测(不打 mock):LOREBOX_CONFIG 指向临时配置(store=临时目录)后 python -m lorebox list 在该目录建索引并输出 index is empty;再给 --store 时改用命令行给的目录,两个目录分别验证确实各自被创建。 - 没有配置文件时行为不变:lorebox list 仍用 .lorebox/(有断言)。 - --help 显示的仍是内置默认值,这点已写进 README 与架构文档,避免误以为 配置没生效。 | 5 天前 | |
feat: 新增 --embed-backend 与 search,索引检索不再强依赖 Ollama 以往索引和检索都必须先装好 Ollama 并 pull 嵌入模型,否则第一条 add 就报 「can't reach Ollama」。想试试本地笔记检索的人得先下几个 GB 的模型,多数人在 这一步就走了——而这一步其实并非必要。 把「嵌入」抽成可换的后端: - ollama(默认):行为完全不变,现有索引和用法不受影响 - onnx:用 chromadb 自带的 all-MiniLM-L6-v2 在本机算,384 维,不需要模型服务 再新增 search 命令,把「检索」从「生成」里拆出来:只打印最相近的片段和距离, 不调用语言模型。于是配 onnx 后端时,索引和检索完全不需要 Ollama: lorebox add ./notes --embed-backend onnx lorebox search "笔记里写过什么" 两点需要留意: - 两个后端的向量宽度不同(384 / 768),不能放进同一个 collection。store 在建索引 时把后端写进 collection 的 metadata,之后用另一个后端访问会直接报错并说明原因, 而不是让 chromadb 在深处抛一个和后端无关的错误。早期建的索引没有这个字段,按 ollama 处理(那时只有这一个后端)。 - onnx 模型在首次使用时下载一次,之后才真正离线。这一点写进了 README 的限制段, 不把「离线」说得比实际更满。 顺带把 Ollama 的报错变成引导:add / search 连不上 Ollama 时,会提示可以改用 --embed-backend onnx,而不是只丢一句连接失败。 改动: - 新增 lorebox/embed.py:后端分发与维度表 - store.py:__init__ 接受 backend,记录并校验,混用时给出明确错误 - retrieve.py / ask.py:透传 backend - cli.py:add / ask / search 新增 --embed-backend;新增 search 命令;新增 _ollama_hint - pyproject.toml:新增可选依赖 local-embed = ["onnxruntime>=1.17"] - README / docs/architecture.md / CHANGELOG.md:同步新能力与限制 验证:全量 92 个测试通过(74 → 92)。新增 18 个:test_embed 8 个(后端分发、 忽略模型参数、空输入、未知后端、维度表)、test_cli_search 5 个(不调用模型、 空结果、前缀短路、后端透传、报错引导)、test_store 3 个(后端混用保护、同后端 放行、旧索引按 ollama 兼容)、test_retrieve 2 个(backend 透传与默认值)。 另实测 python -m lorebox --help 列出 9 个命令,search / add / ask 的 --embed-backend 选项输出正常。 | 16 天前 | |
feat: 记录索引的嵌入模型,换模型访问时报错而不是静默返回噪声 改前:索引只记后端名(ollama / onnx)。把 --embed-model 从 nomic-embed-text 换成 bge-m3 时 后端名没变,于是不报任何错;写进去的向量与查询向量不在同一个空间,检索结果全是噪声,而且 失败是静默的,用户只会觉得「这工具查不准」。 改后:collection metadata 同时记 backend / embed_model / dimension。embed_model 记的是后端 实际运行的模型(onnx 忽略 --embed-model,一律记 all-MiniLM-L6-v2,否则每次都被误判为换过 模型),dimension 取自首次写入时的真实向量宽度,而不是后端标称宽度。用另一个模型打开已有 索引会抛 RuntimeError,CLI 把它转成可读错误,说明两个模型名和两种解法(换 --store,或删掉 索引重新 add)。早期索引没有 embed_model,模型无从推断,就不写也不校验,按当前传入值放行。 验证:新增 tests/test_embed_model.py,7 个用例覆盖「模型名与真实宽度被记录」「同名可重开」 「换模型报错且带两个模型名」「老索引放行」「onnx 记自身模型名」「CLI search 换模型非零退出 且不触发检索」;全量 115 个测试用 run_tests.py 跑通,原有用例无一回归。 | 10 天前 | |
feat: 新增 lorebox gc,把删除留下的空间还给磁盘 改前:删除片段、删除 collection 之后, .lorebox/ 只涨不跌。删行只把 sqlite 的页标成空闲, 文件本身不变小;collection 被删掉后它的向量段目录还留在磁盘上,没有任何一条记录指向它。 stats 只看条数,用户看不出索引到底占了多少磁盘,也判断不了该不该重建。 改后:store.gc() 把这两处垃圾收走 —— 对索引自己的 sqlite 文件跑 VACUUM,把空闲页还给文件 系统;段目录则拿 sqlite 里 segments 表的 id 逐一比对,不在表里且目录名是 UUID 形状的才删 (--store . 会把索引放进笔记目录里,宁可不删也不能删错)。新增 lorebox gc 命令输出 before / after / freed / removed / kept;stats 增加 disk: 与 reclaimable:,有可回收量 时才多打一行并提示跑 gc。 Windows 上被本进程打开着的段目录删不掉(WinError 32),所以 gc() 不看有没有抛异常,而是删 完回文件系统确认目录是否真的没了:删掉的计 removed_dirs,没删掉的计 leftover_dirs,命令 输出提示「再跑一次 lorebox gc」,不把没释放的空间算进 freed。 验证: - 新增 tests/test_gc.py 共 13 个用例,覆盖「大量删除后体积下降」「gc 不碰索引里剩下的块」 「空索引安全 no-op」「索引不存在时给提示且不建目录」「段目录按 segments 表判定并回收」 「不是 UUID 形状的目录绝不动」「stats 报 disk / reclaimable」「未回收的目录不被静默丢掉」。 - run_tests.py 全量 184 个用例全绿(原 171 + 新增 13)。 - 端到端实测:装入 400 块后删到 10 块,lorebox stats 报 disk 1.4 MB / reclaimable 636 KB, lorebox gc 后变成 799 KB、freed 636 KB、kept 10 chunk(s),再跑 stats 不再提示可回收。 | 3 天前 | |
feat: 增量索引细化到块,改一段不再重嵌整份文件 改前:增量索引只按文件判断。文件里改一个标点,它的 sha 就变了,整份文件的几十个块 全部重新求嵌入。大文件上这一步慢得明显。 改后:每个块按正文算内容指纹 chunk_sha,写进 metadata。重新索引时把该文件已有的块 连同向量一起取回来(store.get_source_chunks),按指纹比对:对得上的直接沿用旧向量, 对不上的才求嵌入。块的 id 也从「路径+序号」改成「路径+内容指纹」,所以只是挪了位置 的块仍保持同一身份。 为什么按内容而不按位置:块边界由累积逻辑决定。改一段之后,如果它的长度变化让某个块 「装得下/装不下」后续段落,后面所有块的边界都会连锁偏移;这时按序号比对,结论永远 是「全变了」,等于没做优化。 实测(onnx 后端,三个 400 字段落,max-chars 450): - 改写中间一段 → 2 复用,1 嵌入 - 末尾追加一段 → 3 复用,1 嵌入 - 删掉一段 → 全部复用,一次嵌入都没调 - 文件没改动 → 整体跳过 如实写下的边界(已进 README / CHANGELOG / architecture): - 改动开头且长度变化较大 → 后续块内容重新组合、指纹全变 → 大部分重嵌。实测把首段从 400 字扩到 700 字,原本「首段+次段」挤在一个块里,扩容后两块都重新组合,复用率为 0。这是累积式变长分块的固有性质,不是实现缺陷;根治只能改成固定段落数分组,代价 是块大小不均,而块质量对检索的影响大于索引速度,所以不换。 - 带 --overlap 时末尾追加仍能复用:overlap 只取前一块的尾部,前面的块不动它就不变。 只有前一块本身变了,后面块的 overlap 前缀才跟着失效。 - 同一文件里内容完全相同的块只存一份——同样的文本得到同样的向量,存两份对检索没有 增益。 兼容性:升级前建的索引没有 chunk_sha,此时直接对块正文求指纹,两者含义相同,所以 照样能复用,不必付一次全量重嵌的代价。 验证:新增 tests/test_incremental.py 共 7 个用例,覆盖上述四种场景、同文件重复块去重、 老索引复用,以及「复用块确实沿用了旧向量」而不是只跳过了调用;全量 122 个测试用 run_tests.py 跑通,原有用例无一回归。 | 6 天前 | |
feat: ask 支持多轮追问,追问时能接上上文 改前:ask 是一次性的,问完即忘。追问「那第二点呢」时模型看不到上一轮的问答, 「那」指什么全靠猜,只能把上文整段复制再问一遍。 改后:新增 --interactive(-i),进入后一行一个问题,每一问都把最近几轮的问答 拼在提示词最前面;保留轮数由 --history-turns 控制(默认 3,设 0 则每问独立)。 空行、exit、quit 或 stdin 读到末尾都会退出,管道输入与手敲走同一条路径。配合 --stream 时把流式片段拼回完整答案再记进历史;--json 与 --interactive 互斥(一个 JSON 对象只描述一次问答);既不给问题又不加 --interactive 时给出用法错误,而不是 静默空转。store 只打开一次、--path 只解析一次。非交互模式下的提示词与输出完全 不变:build_prompt 不带 history 时返回的字符串与改动前逐字节一致。 验证:run_tests.py 全量 146 个测试全绿(新增 18 个用例,其余一个没坏)。新用例 覆盖:历史渲染进提示词、超上限丢弃最旧一轮、--history-turns 0、空行/exit/EOF 三种 退出方式、命令行问题优先于 stdin、多轮时每轮都打印来源、--interactive 与 --json 互斥、缺问题时退出码非零;并用 mock 断言第二问实际发给 ollama.generate 的提示词里 带着第一问的问答。 | 4 天前 | |
feat: 新增 doctor 命令,一次检查本地环境 装完之后出问题只能靠猜:OllamaError 只覆盖「连不上」这一种情况,模型没 pull 时 只会看到 Ollama 返回的原始报错;store 目录不可写,也往往要等 add 跑到一半才暴露。 doctor 把这些一次查完并逐项打印: - Ollama 是否连得上(连不上时打印具体地址和启动提示) - 本地模型数量,以及要用的对话模型与嵌入模型是否已经 pull (比较时去掉 :tag,避免 qwen2.5 与 qwen2.5:latest 被当成两个不同的东西) - store 目录是否可用,以及当前索引的文件数与片段数 有任一项失败就以非零状态退出,方便放进脚本当前置检查。Ollama 连不上时不会提前 中断,仍会把 store 那一项报出来——这样用户一次就能看到全部问题,而不是修一个 再撞下一个。 改动: - ollama.py:新增 _get() 与 list_models()(读 /api/tags) - cli.py:新增 doctor 命令 - tests/test_ollama.py:新增 3 个用例(解析模型名、空列表、连不上时抛 OllamaError) - tests/test_cli_doctor.py:新增 3 个用例(全部通过、模型缺失、Ollama 不可达) 验证:全量 39 个测试通过,原有 33 个未受影响;另在本地手工跑了一次,逐项输出与 退出码符合预期 | 23 天前 | |
perf: 嵌入改用批量接口,减少请求次数 embed() 原本对每个片段单独发一次 POST /api/embeddings。一份切成 200 块的笔记就是 200 次往返,本地模型虽然不走网络,但每次请求的序列化与调度开销累加起来,索引大 文档要等很久。 改成优先 POST /api/embed,一次把整批文本放进 input 提交,拿回 {"embeddings": [...]}。 旧版 Ollama 没有这个端点,请求失败时自动退回原来的逐条 /api/embeddings,行为不变。 两个保护: - 返回条数与输入条数不一致时也走回退。宁可多花几次请求,也不能让向量错位挂到别的 片段上——那种错误不会报异常,只会让检索结果悄悄变差。 - 空输入直接返回,不发任何请求。 改动: - ollama.py:embed() 优先批量、失败或条数不符时回退 - tests/test_ollama_embed.py:新增 5 个用例(一次请求完成批量、请求体格式、 端点缺失时回退、返回条数不符时回退、空输入不发请求) 验证:全量 53 个测试通过(48 → 53),原有 48 个未受影响 | 18 天前 | |
feat: 支持用 OLLAMA_HOST 指定 Ollama 地址 DEFAULT_HOST 一直是模块级常量 "http://localhost:11434",各函数把它当默认参数值。 Ollama 装在别的机器、或者换了端口时,用户没有任何办法改,只能去改代码。而 OLLAMA_HOST 本来就是 Ollama 自己的约定,跟着走更自然。 改成 default_host():每次调用时读环境变量,没设就回退到 localhost:11434;值里不带 scheme 时自动补 http://(官方文档的示例常写 127.0.0.1:11434 这种)。之所以在调用时 读、而不是在 import 时读,是因为模块级读取会让测试和长驻进程拿不到后来改过的环境。 所有 host 参数的默认值从 DEFAULT_HOST 改成 None,由 _post/_get 在入口处解析,这样 「显式传 None」和「完全不传」的行为一致。 改动: - ollama.py:DEFAULT_HOST 常量改为 default_host();_post / _get / list_models / embed / generate 的 host 默认值改为 None - tests/test_ollama_host.py:新增 5 个用例(回退 localhost、裸 host:port 补 scheme、 保留显式 scheme、空白值回退、调用时才解析环境) 验证:全量 48 个测试通过(43 → 48),原有 43 个未受影响 | 19 天前 | |
feat: ask 新增 --stream,回答边生成边打印 改前 ollama.generate 写死 stream=False,长回答必须等整个生成完才一次性显示, 中间几十秒终端没有任何反馈,看起来像卡死。现在 ask 加了 --stream:ollama.py 新增 generate_stream,按行解析 Ollama 的 NDJSON 流逐段吐出;cli.py 的 ask 在 --stream 下先取 chunks、拼好提示词,再用 generate_stream 逐段打印,最后补一个换行,来源列表 照旧在末尾打印。不加 --stream 时输出与以前完全一致。 为让「检索」和「生成」能分开调用,ask.py 抽出了 retrieve_chunks() 与 build_prompt(), answer() 的签名和返回值没动,既有调用方与测试不受影响。 --stream 与 --json 不能同时用:--json 要求完整结果才能序列化, 两者同时给出时直接报 UsageError 退出,而不是静默忽略其中一个。 验证:.venv\Scripts\python.exe run_tests.py,69 个用例全绿(原有 57 个未受影响, 新增 12 个:test_ollama_stream.py 6 个覆盖分片顺序、空行/空分片跳过、done 提前收尾、 非 200 与连不上转成 OllamaError、请求确实带 stream=True;test_cli_ask_stream.py 6 个 覆盖逐段打印、提示词含检索上下文、默认不流式、--no-sources 组合、--json 互斥)。 | 17 天前 | |
feat: 检索加相似度阈值与 MMR 重排序 改前:query() 不管相关性都按距离返回 top_k 条,不相关的片段照样占位;同一段落的多份重复副本能把所有位置占满,把真正有用的其他片段挤掉。 改后:ask / search 新增 --min-score(默认 0,不过滤,行为与此前一致);检索先多取 top_k×3 个候选,再按 MMR(λ 固定 0.5)挑满 top_k,重复内容不再独占位置;search 输出额外打印 0..1 的 score,方便对着阈值调。 验证:run_tests.py 全绿,共 108 个用例(原有 92 个 + 新增 16 个 tests/test_rerank.py),原有用例无一失败。 | 11 天前 | |
feat: 新增 --embed-backend 与 search,索引检索不再强依赖 Ollama 以往索引和检索都必须先装好 Ollama 并 pull 嵌入模型,否则第一条 add 就报 「can't reach Ollama」。想试试本地笔记检索的人得先下几个 GB 的模型,多数人在 这一步就走了——而这一步其实并非必要。 把「嵌入」抽成可换的后端: - ollama(默认):行为完全不变,现有索引和用法不受影响 - onnx:用 chromadb 自带的 all-MiniLM-L6-v2 在本机算,384 维,不需要模型服务 再新增 search 命令,把「检索」从「生成」里拆出来:只打印最相近的片段和距离, 不调用语言模型。于是配 onnx 后端时,索引和检索完全不需要 Ollama: lorebox add ./notes --embed-backend onnx lorebox search "笔记里写过什么" 两点需要留意: - 两个后端的向量宽度不同(384 / 768),不能放进同一个 collection。store 在建索引 时把后端写进 collection 的 metadata,之后用另一个后端访问会直接报错并说明原因, 而不是让 chromadb 在深处抛一个和后端无关的错误。早期建的索引没有这个字段,按 ollama 处理(那时只有这一个后端)。 - onnx 模型在首次使用时下载一次,之后才真正离线。这一点写进了 README 的限制段, 不把「离线」说得比实际更满。 顺带把 Ollama 的报错变成引导:add / search 连不上 Ollama 时,会提示可以改用 --embed-backend onnx,而不是只丢一句连接失败。 改动: - 新增 lorebox/embed.py:后端分发与维度表 - store.py:__init__ 接受 backend,记录并校验,混用时给出明确错误 - retrieve.py / ask.py:透传 backend - cli.py:add / ask / search 新增 --embed-backend;新增 search 命令;新增 _ollama_hint - pyproject.toml:新增可选依赖 local-embed = ["onnxruntime>=1.17"] - README / docs/architecture.md / CHANGELOG.md:同步新能力与限制 验证:全量 92 个测试通过(74 → 92)。新增 18 个:test_embed 8 个(后端分发、 忽略模型参数、空输入、未知后端、维度表)、test_cli_search 5 个(不调用模型、 空结果、前缀短路、后端透传、报错引导)、test_store 3 个(后端混用保护、同后端 放行、旧索引按 ollama 兼容)、test_retrieve 2 个(backend 透传与默认值)。 另实测 python -m lorebox --help 列出 9 个命令,search / add / ask 的 --embed-backend 选项输出正常。 | 16 天前 | |
feat: 新增 --embed-backend 与 search,索引检索不再强依赖 Ollama 以往索引和检索都必须先装好 Ollama 并 pull 嵌入模型,否则第一条 add 就报 「can't reach Ollama」。想试试本地笔记检索的人得先下几个 GB 的模型,多数人在 这一步就走了——而这一步其实并非必要。 把「嵌入」抽成可换的后端: - ollama(默认):行为完全不变,现有索引和用法不受影响 - onnx:用 chromadb 自带的 all-MiniLM-L6-v2 在本机算,384 维,不需要模型服务 再新增 search 命令,把「检索」从「生成」里拆出来:只打印最相近的片段和距离, 不调用语言模型。于是配 onnx 后端时,索引和检索完全不需要 Ollama: lorebox add ./notes --embed-backend onnx lorebox search "笔记里写过什么" 两点需要留意: - 两个后端的向量宽度不同(384 / 768),不能放进同一个 collection。store 在建索引 时把后端写进 collection 的 metadata,之后用另一个后端访问会直接报错并说明原因, 而不是让 chromadb 在深处抛一个和后端无关的错误。早期建的索引没有这个字段,按 ollama 处理(那时只有这一个后端)。 - onnx 模型在首次使用时下载一次,之后才真正离线。这一点写进了 README 的限制段, 不把「离线」说得比实际更满。 顺带把 Ollama 的报错变成引导:add / search 连不上 Ollama 时,会提示可以改用 --embed-backend onnx,而不是只丢一句连接失败。 改动: - 新增 lorebox/embed.py:后端分发与维度表 - store.py:__init__ 接受 backend,记录并校验,混用时给出明确错误 - retrieve.py / ask.py:透传 backend - cli.py:add / ask / search 新增 --embed-backend;新增 search 命令;新增 _ollama_hint - pyproject.toml:新增可选依赖 local-embed = ["onnxruntime>=1.17"] - README / docs/architecture.md / CHANGELOG.md:同步新能力与限制 验证:全量 92 个测试通过(74 → 92)。新增 18 个:test_embed 8 个(后端分发、 忽略模型参数、空输入、未知后端、维度表)、test_cli_search 5 个(不调用模型、 空结果、前缀短路、后端透传、报错引导)、test_store 3 个(后端混用保护、同后端 放行、旧索引按 ollama 兼容)、test_retrieve 2 个(backend 透传与默认值)。 另实测 python -m lorebox --help 列出 9 个命令,search / add / ask 的 --embed-backend 选项输出正常。 | 16 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 1 个月前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 17 天前 | ||
| 17 天前 | ||
| 5 天前 | ||
| 23 天前 | ||
| 1 个月前 | ||
| 6 天前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 20 天前 | ||
| 1 个月前 | ||
| 5 天前 | ||
| 16 天前 | ||
| 10 天前 | ||
| 3 天前 | ||
| 6 天前 | ||
| 4 天前 | ||
| 23 天前 | ||
| 18 天前 | ||
| 19 天前 | ||
| 17 天前 | ||
| 11 天前 | ||
| 16 天前 | ||
| 16 天前 |