local-deep-research 1.7.0 发布解读:Chat 多轮研究模式、日志凭据防泄漏加固与推理模型输出治理
2026/9/16 20:55:27 网站建设 项目流程

local-deep-research 1.7.0 发布解读:Chat 多轮研究模式、日志凭据防泄漏加固与推理模型输出治理

【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

本篇技术指南基于local-deep-research仓库的 1.7.0 版本发布说明,结合仓库源码逐项解读该版本的核心变更:全新引入的 Chat 多轮对话式研究模式(含可配置的追问上下文策略、流式引用渲染与大量正确性/无障碍修复)、围绕 Logurudiagnoseredact_secrets展开的凭据防泄漏安全加固,以及针对推理模型<think>标签的全局输出治理。读完本文,你将能理解这些功能的配置入口、底层实现原理与适用边界,并可直接在自托管部署中应用对应的安全配置建议。

一、版本概览:1.7.0 的三条主线

从发布说明的整体结构看,1.7.0 的改动可以归纳为三条主线:

  1. 安全加固(Security):将redact_secrets()凭据脱敏覆盖到所有携带凭据的异常日志调用点,并把 Loguru 的diagnose=True局部变量转储从LDR_APP_DEBUG中剥离出来,改为独立的显式开关LDR_LOGURU_DIAGNOSE
  2. 新功能(New Features):正式引入Chat Mode交互式多轮研究会话,支持上下文累积、流式进度与引用、会话归档/恢复/删除/Markdown 导出,以及四种可配置的追问上下文模式。
  3. 缺陷修复(Bug Fixes):覆盖 Chat Mode 的流式引用、数据完整性、可访问性、资源边界,以及非 Chat 研究路径上的队列调度、报告组装、引用格式等一批问题。

下文将按照「先安全、再功能、后修复」的顺序展开,并在每个小节补充对应源码证据。

二、安全加固:让凭据不再泄漏进日志

2.1 背景:Loguru 的diagnose=True默认行为

Loguru 在记录异常时默认开启diagnose=True,这意味着当logger.exception(...)触发时,每个 traceback 帧中所有局部变量的repr()都会被渲染进日志。在一个承载 LLM 密钥、Authorization头、SQLCipher 主密码的深度研究应用中,这等于把最敏感的内容原样写进每一个日志 sink。发布说明明确指出:此前开启LDR_APP_DEBUG做常规调试的同时也会把diagnose一起打开,任何一次异常都可能让 API Key、SQLCipher 主密码、Authorization头落入日志。

2.2 双环境变量分离:LDR_APP_DEBUG只管日志级别

1.7.0 将两者解耦:

  • LDR_APP_DEBUG:现在提升日志级别到 DEBUG,不再附带局部变量转储;
  • LDR_LOGURU_DIAGNOSE=true:需要显式设置才能开启 Loguru 的diagnose局部变量渲染,且开启时会打印显式警告。

对应源码在 secure_logging.py:

def is_diagnose_mode() -> bool: """Return True when the operator explicitly opted into traceback output.""" return env_truthy("LDR_APP_DEBUG") and env_truthy("LDR_LOGURU_DIAGNOSE")

注意:is_diagnose_mode()要求两个变量同时为真才开启诊断模式。同时,SecureLogger代理把exception()重写为logger.opt(exception=is_diagnose_mode(), depth=1).error(...)(见 secure_logging.py),在正常运维下异常事件仍以 ERROR 级别落日志,但记录中不携带异常对象——任何 sink 都无法渲染 traceback,而消息文本本身仍会进入所有 sink。因此调用点仍需自行脱敏消息文本,这正是scrub_error的职责。

配置文档 CONFIGURATION.md 中的app.debug/LDR_APP_DEBUG条目已同步更新说明:仅提升日志级别、单独启用 Loguru 局部变量转储。

2.3redact_secrets()覆盖面扩展

发布说明称redact_secrets()的覆盖范围从 Google provider 扩展到了所有携带凭据的异常日志调用点,包括:

  • OpenAI-compatible provider 基类的list_models
  • 自定义 OpenAI endpoint provider、OpenAI embedding provider;
  • Web 搜索引擎子系统:BaseSearchEngine.run()以及 Tavily、Exa、Serper、Google PSE、Mojeek、PubMed、Semantic Scholar、Guardian、ScaleSerp、SerpAPI、GitHub 等引擎的主 HTTP 调用错误路径;
  • 本版本还补上了遗漏的 NASA ADS 引擎:其_get_previews原本使用logger.exception,在 loguru diagnose 模式下会渲染持有Authorization: Bearer <key>self.headers,现已改用带脱敏的logger.warning

脱敏机制本身采用「双重擦除(dual-scrub)」模式,核心实现在 log_sanitizer.py:

  • sanitize_error_message():按凭据形态做正则脱敏,覆盖Bearer令牌、带/不带 scheme 的Authorization头、x-api-key头、URL 内嵌凭据(user:pass@host,包括postgresql://redis://等 DSN)、URL 查询参数(?api_key=?access_token=等)、以及sk-/pk-、GoogleAIza/ya29.、GitHubghp_/github_pat_、AWSAKIA/ASIA、Slackxox*/xapp、JWT(eyJ…)等已知令牌前缀;
  • redact_secrets():按调用方提供的已知字面量str.replace,多个密钥按长度降序处理(避免短密钥吞掉长密钥的子串),默认最小脱敏长度 8 字符、替换标记为***REDACTED***
  • scrub_error():把两者组合成一个 catch 块内可直接调用的防御性函数——它在except中运行、保证永不抛异常,str(error)与每个 secret 的str()转换都做了保护。

BaseSearchEngine通过_secret_attrs声明每个引擎自己的敏感属性(默认即("api_key",),见 search_engine_base.py),_scrub_error委托给scrub_error,把引擎的密钥解析进secrets参数。Guardian 与 ScaleSerp 则在此基础上叠加了各自已有的正则清洗器,形成纵深防御。

2.4 MCP 子进程与 Benchmark CLI 的diagnose=False锁定

diagnose默认开的问题不只存在于 Web/CLI 日志器:

  • MCP 子进程(ldr-mcp:stderr sink 固定diagnose=False。MCP 请求处理器里有大量logger.exception(...)路径,此前失败时会把api_keyAuthorization头、搜索引擎密钥写进 MCP 客户端的 stderr 日志。MCP 子进程没有 debug 模式,因此该开关被无条件关闭。
  • Benchmark CLI(ldr-benchmark/ benchmark_commands.py):两处logger.add(sys.stderr, ...)均固定diagnose=False。benchmark 包中的 LLM 评分调用、搜索引擎 runner、数据集加载器同样存在凭据泄漏面,且该 CLI 绕过config_logger,环境变量门控覆盖不到它。
  • 示例脚本(examples/example_browsecomp.py与三个api_usage/programmatic/片段同样传入diagnose=False。这些文件是用户复制进自己脚本的模板,此前默认行为会把凭据泄漏传播到任何下游代码。

配套地,测试夹具与开发脚本(tests/conftest.py的 loguru-caplog fixture、tests/api_tests/__main__runner、tests/test_openai_api_key_e2e.py等)也已统一传入diagnose=False,保证 pytest 崩溃时帧内凭据不会进入 CI 日志。

2.5 会话 API 的限流补齐

Chat 的PATCH(重命名/归档)与DELETE会话路由现在与其它变更状态的 Chat 端点一样带有每用户限流,此前它们只受全局限流器约束,导致会话 API 上存在不均衡的滥用面。

2.6 运维建议

  • 生产环境保持LDR_LOGURU_DIAGNOSE未设置(或设为非真值),LDR_APP_DEBUG仅在需要更详细日志级别时开启;
  • 只有排查疑难 traceback 时才临时同时设置LDR_APP_DEBUG=trueLDR_LOGURU_DIAGNOSE=true,排查完立即移除;
  • 若自己编写调用搜索引擎/LLM provider 的脚本,复制examples/模板时应保留diagnose=False
  • 不要绕过secure_logging.logger直接使用 loguru 的logger.exception(),除非确认消息已通过scrub_error脱敏。

三、新功能:Chat Mode 多轮研究会话

3.1 功能总览

Chat Mode 是 1.7.0 的重头戏(对应 PR #2953),在 features.md 的 Chat Mode 章节 中被标记为Experimental。它提供:

  • 交互式多轮研究对话,会话跨轮次累积上下文(实体、主题、来源数量);
  • 答案构建过程中实时流式输出研究步骤与引用;
  • 会话持久化到每用户数据库(默认加密),跨登出保留;
  • 会话生命周期:归档、重新激活、永久删除;
  • 可选 LLM 生成的会话标题(chat.llm_title_generation开关);
  • 会话可导出为 Markdown;
  • 始终使用 quick 研究模式(v1),每个会话同一时间一个进行中的研究。

入口为侧边栏Chat链接或/chat/路由,页面模板在 chat.html,路由实现在 web/routers/chat.py。

3.2 追问上下文模式:chat.followup_context_mode

追问问题不再从零开始。默认情况下,之前的对话会被浓缩成一个聚焦于你新问题的短摘要(使用你配置的 LLM),作为该次追问研究的"previous findings"传入,使多轮研究保持在主题内并受控于上下文长度。四种模式定义在 default_settings.json 的chat.followup_context_mode项:

模式行为额外 LLM 成本
summary(默认)针对新问题对整段对话做 LLM 摘要每次追问多一次 LLM 调用
raw最近的研究发现,截断后使用
full整个对话原文
none无先前上下文(仅新问题 + 原始主题)

选择逻辑实现在 chat/context.py:ChatContextManager读取快照中的chat.followup_context_mode(缺省用DEFAULT_FOLLOWUP_CONTEXT_MODE = "summary"),none直接返回空、raw取最近发现截断、full取完整记录,而summary模式(在无问题时)会回退到 raw 最近发现。该模块还会记录"本次追问运行了哪种模式、构建了多少字符上下文",解决此前上下文构建(尤其 LLM 摘要)零日志输出、看起来像莫名停顿的问题。

3.3 流式体验:引用即时渲染、思考气泡与停止计数

  • 流式内联引用:每个发送给客户端的 chunk 都会经过与最终答案相同的引用格式化器,并带一个小 carry buffer 暂存跨 chunk 边界的不完整[N标记,直到闭合]到达。因此用户能看到[[arxiv.org-1]](url)(或report.citation_format设置的其它格式)实时出现,保存时不会出现格式翻转。服务端 partial-content buffer 仍存原始 chunk,中途终止时恢复保存不会被重复格式化。
  • 思考指示器:追问时显示 "Summarizing previous conversation…",研究开始时显示 agent 在工具调用间隙的中间推理文本(如 "I should compare the published benchmark results next…"),随每一步更新。
  • 停止计数:点击 Stop 后,若工作线程阻塞在 LLM HTTP 调用中(Qwen 3、DeepSeek-R1 等思考型模型在<think>块内不产出 chunk,可能 30 秒以上才出现停止检查点),进度任务行会显示 "Stopping research… (Ns)" 秒数。该计数在 3 秒后才出现,研究停止/完成/出错时自动清除。这是 UX 层面的缓解,彻底降低终止延迟需要 per-token 流式或基于信号的 HTTP 中断等更深的改造。
  • 友好化里程碑文案Tool: search_pubmed — "covid"变为🔍 Searching PubMed: "covid"Tool: fetch_url — "https://…"变为📖 Reading the page: "https://…",工具结果显示为📄 From {engine}: …。没有显式显示名的引擎回退为清洗后的Title Case形式,新增搜索引擎无需改代码即可适配。子主题研究里程碑会拾取工具的subtopics: list[str]参数并字符串化,显示🔬 Investigating subtopic: "topic1, topic2"而不是空引号。

3.4 引用格式在 Chat 中的落地

Chat 模式答案走apply_inline_hyperlinks回退路径(langgraph-agent 综合文本不产出## Sources块),此前硬编码NUMBER_HYPERLINKS导致所有 Chat 答案都输出[[1]](url)。1.7.0 改为按CitationFormatter.mode分发(见 citation_formatter.py 的CitationMode枚举),report.citation_format设置的格式现在对 Chat 引用真正生效:

  • number_hyperlinks— 数字超链接[1]
  • domain_hyperlinks— 域名超链接[arxiv.org]
  • domain_id_hyperlinks— 智能编号[arxiv.org][arxiv.org-1]
  • domain_id_always_hyperlinks— 一致编号[arxiv.org-1]
  • source_tagged_hyperlinks— 源标签 + 全局引用编号保留(如[arxiv-1][openai.com-2][arxiv-3]

apply_inline_hyperlinks现在还接受"url""link"作为来源字典的目标键——SearXNG 来源把目标放在"link"下(search_engine_searxng.py),此前回退超链接路径会静默丢弃它,导致答案下方 Sources 区已完整填充而正文引用只是不可点击的[N]

四、Bug Fixes:一次全面扫尾

4.1 推理模型输出治理:<think>块不再泄漏

  • 流式引用路径base_citation_handler._invoke_with_streaming现在把拼接的 chunk 先经get_llm_response_text再返回——.stream()绕过了ProcessingLLMWrapper.invoke(唯一剥离标签的位置),修复后流式结果与非流式invoke()契约一致,<think>…</think>不再进入持久化的 Chat 答案。
  • 主综合路径(synthesize_findings与标准 knowledge generator)也剥离<think>,不再只由引用处理器兜底;同时修复精确/强制答案提取器在模型返回空(或仅 think)响应时多出一个". <content>"的问题。
  • 中央 LLM 包装器将字符串返回型 provider 归一化为 message 对象,并对异步ainvoke也应用<think>剥离,消除'str' object has no attribute 'content'崩溃。Message 对象的tool_calls/reasoning_content保留,仅重写.content
  • 已知局限(已在代码内注明):LangGraphcreate_agent/bind_toolslanggraph-agentmcp策略)通过ProcessingLLMWrapper.__getattr__解析到基础 LLM,绕过<think>剥离,这是外观性泄漏(推理模型标签可能出现在 agent 输出中),不影响直接invoke()调用。

4.2 Chat 会话的数据完整性与资源边界

  • 消息排序尊重同毫秒行的sequence_number;"Load older" 游标改用复合(created_at, id)键,边界行不再被静默丢弃;Markdown 导出翻页遍历完整对话(此前截断为 50 条);部分保存的幂等标记仅在数据库写入成功后才设置,瞬时失败不再永久丢失 assistant 响应。
  • send_message现在解析数字型搜索设置(search.iterationssearch.questions_per_iteration)再执行写入用户消息 + IN_PROGRESS 研究行的原子写——畸形值返回干净的 400,而不是行已提交后的未处理 500(此前会留下孤儿行并通过 per-session 409 守卫使会话不可用)。
  • 内联引用 carry buffer 上限 64 字节;客户端streamedContent累加器上限 256 KB(镜像服务端_MAX_PARTIAL_BUFFER_BYTES),并附带一次性 "(Response truncated — exceeded display limit.)" 提示,防止无max_tokens的模型撑爆浏览器标签页。
  • 标题校验剥离 Unicode 格式/行分隔符字符(Cf/Zl/Zp,包括零宽空格U+200B-U+200D与 BOMU+FEFF);regenerate_title_with_llm幂等(标题已不再是回退值则跳过 LLM 调用),并剥离 LLM 返回标题中的\n/\r,避免伪造日志聚合器中的第二条记录。
  • ChatService.delete_session仅在删除提交之后设置内存终止标志,失败的提交不能再杀掉仍存在会话的在途研究。
  • 直接模式容量拒绝路径processor_v2._start_research_directly现在为重新排队的研究递增QueueStatus.queued_tasks(此前计数保持 0,队列处理器会误判队列为空)。

4.3 会话 API 与队列调度

  • Chat 触发的研究会插入UserActiveResearch行,计入每用户app.max_concurrent_researches上限(此前 Chat 研究对该上限不可见,可被多个标签页绕过);该行与研究行原子提交,spawn 失败时清理,正常完成由现有完成清扫中间件移除。
  • 队列调度器processor_v2._start_queued_researches新增专门except SystemAtCapacityError:瞬时容量拒绝保持 QUEUED 等待下一轮,而不是计入SPAWN_RETRY_LIMIT并在负载下被误标 FAILED。
  • update_task_status支持processingqueued计数还原转换,容量拒绝路径使用它,修复全局容量拒绝重排队时active_tasks槽位泄漏、queued_tasks漂移到 0 导致队列停摆的问题。
  • cleanup_research_resources报告调用方传入的真实终态(用户终止为 SUSPENDED、出错为 FAILED),不再硬编码COMPLETED——停止研究不再发出虚假的 "completed" socket 信号(此前产生多余的 "[Stopped]" 气泡与误导性的 100%/Completed 翻转)。

4.4 引用格式化与报告组装

  • apply_inline_hyperlinksCitationFormatter.mode分发(前述)。
  • 报告结构解析修复:无句点的编号章节行(如1 Introduction)不再IndexError崩溃;含句点的章节名(如1. U.S. Policy)不再被截断成U——章节号只按第一个句点拆分,并对畸形行有保护。
  • format_links_to_markdown(search_utilities.py)在sorted()前把引用索引强制转为str——不同策略产出的intrecursive_decomposition_strategy.py)与str_build_sources_markdown回退)混合不再触发TypeError: '<' not supported between instances of 'str' and 'int',排序仍保持数字序。
  • assemble_full_report不再用 blanketexcept包住_build_sources_markdown——错误传播到调用方既有 500 路径,而不是渲染一份看似完整实则静默缺失全部 Sources 的报告。
  • GET /api/report/<id>sources字段改从结构化research_resources表读取(原先读的是 chat-mode-v2 重构后从未写入的死元数据键all_links_of_system)。
  • GitHub 搜索相关性排序拒绝负数、越界、非整数结果索引并去重,畸形 LLM 响应不能再选中错误结果、重复列出同一仓库或丢弃全部结果。

4.5 非 Chat 路径修复

  • 新闻订阅Run now或逾期订阅检查运行新闻订阅时不再在订阅无显式模型时强制 LLM 为ollama/llama3。此前news/flask_api.py硬编码这些值(真值覆盖了用户配置的llm.provider/llm.model),未配置模型的订阅会被静默切到 Ollama 运行且通常失败;现在未设置模型以None传递,start_research回退到用户设置。
  • 资源泄漏:Ollama embeddings 工厂加weakref.finalize安全网,直接构造OllamaEmbeddings的程序化调用方在实例被 GC 时释放底层 httpx 客户端(见 ollama.py);Library RAG 服务在每个HTTP 请求结束时关闭(包括流式端点),与 embedding-manager 关闭路径一起阻止文件描述符累积。
  • WebSocket 订阅所有权_user_owns_research现在同时接受 Benchmark 运行(数字BenchmarkRun.id),benchmark 页面的实时进度(当前任务详情与 SearXNG 限流警告)不再被静默丢弃,且没有放宽授权边界。
  • LangGraphAgentStrategy.analyze_topic不再在工具调用显示循环里用截断的工具参数覆盖query参数——作为默认策略,此前首次web_search后原始研究问题会被 ≤80 字符的搜索参数替换,进而污染引用再综合、回退综合与记录的question字段。
  • send_message采用共享@require_json_body装饰器,非 JSON 请求体不再绕过统一的 400 契约;GET /api/report/<id>已改读research_resources表。
  • 报告持久化形状变更research_history.report_content持久化时只存综合答案(LLM 散文 + 内联引用),不再内嵌## Sources/## Research Metrics。用户可见视图不变——assemble_full_report()(report_assembly_service.py)按需重构旧显示形状,含内嵌章节的旧行会被检测且不重复追加。直接调用storage.get_report()/storage.get_report_with_metadata()的代码需改用assemble_full_report()以获得旧形状。News API 的findings字段同样为 answer-only 报告内容(有意为之),结构化 top-N 来源链接在独立的links数组中。

五、研发流程与测试质量配套

发布说明还包含若干内部质量改进,对二次开发者有参考价值:

  • CI 并行稳定性test_settings_routes_coverage.py的 94 个测试(每个都含 register + login + SQLCipher KDF + 10 次 Alembic 迁移)从-n auto并行池拆出为专用串行步骤,用--cov-append合并覆盖率,避免 xdist worker 静默死亡;pytest-timeoutsignal改为thread方法,全局单测超时从 60s 提升到 180s(SIGALRM 在 Python 3.14 的weakref.py清理中会破坏 xdist worker 并丢弃覆盖率数据)。
  • 测试质量:多个脆弱的测试模式被重构——7 个复制粘贴的captured_username块合并为单一 fixture,16 行SocketIOService._instance = None重置改为 autouse fixture(setup 与 teardown 都运行),三个同义反复的 dict 断言测试换成驱动真实SettingsManager的行为测试,三个针对chat/routes.py的源码 grep 测试换成驱动共享 reclaim 辅助函数与真实 SQLite 会话的 8 个行为测试。
  • 对 Chat Mode 流式路径剥离<think>、carry buffer 溢出契约、LLM 标题换行剥离等均新增了回归测试。

六、升级与运维清单

  1. 日志安全:确认部署环境中LDR_LOGURU_DIAGNOSE未开启;如需调试,临时同时设置LDR_APP_DEBUG=true+LDR_LOGURU_DIAGNOSE=true,用毕即除。详细环境变量说明见 env_configuration.md。
  2. Chat 入口:通过侧边栏Chat/chat/使用多轮研究会话;会话持久化在每用户加密数据库中。
  3. 追问上下文:按需调整chat.followup_context_modesummary/raw/full/none),注意仅summary每次追问产生额外 LLM 调用。
  4. 引用格式report.citation_format现在对 Chat 答案同样生效,可按需切换为域名/源标签式引用。
  5. 并发上限:Chat 触发的研究现在计入app.max_concurrent_researches,多标签页并行 Chat 会受到同一每用户上限约束。
  6. 报告形状变更:若你直接调用了storage.get_report()等 API,请改用assemble_full_report()获取包含 Sources 的旧形状。
  7. 新闻订阅:订阅未显式配置模型时不再被强制切到 Ollama,将正确回退到全局llm.provider/llm.model设置。

以上所有要点均可在 1.7.0 发布说明 及其引用的源码文件中核对;更完整的 Chat Mode 功能说明见 features.md 的 Chat Mode 章节,搜索引擎与引用格式的配置见 CONFIGURATION.md。

【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询