From 52bf23e500cbaf07cfc915ebdac87d1230c2b64e Mon Sep 17 00:00:00 2001 From: Color2333 <1552429809@qq.com> Date: Mon, 20 Jul 2026 00:07:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(tools):=20=5Frequire=5Fpaper=20detached=20s?= =?UTF-8?q?ession=20=E5=AF=BC=E8=87=B4=20skim/deep=5Fread/embed=20?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:_require_paper 在 `with session_scope() as session:` 里 `return paper`, session 关闭后 paper 变 detached。read.py 的 _skim_paper/_deep_read_paper/ _embed_paper 在 session 外访问 paper.title/paper.abstract/paper.embedding, 触发 lazy load → 报 "Instance is not bound to a Session"。 实测复现:confirm resume 执行 skim_paper 时 action_result success=false, summary 为 detached session 错误。LLM 看到失败后只能靠嘴总结,agent 可用性严重受损(看似 LLM 跑偏,实为工具层 bug)。 修复:在 session 关闭前: 1. 主动触发所有常用列属性加载(id/title/arxiv_id/abstract/pdf_path/ publication_date/embedding/read_status/metadata_json/favorited/rejected/ source/source_id/doi),防止 expunge 后访问未加载列报错 2. session.expunge(paper) 让 paper 带着已加载的属性离开 session Paper 无 relationship/lazy 字段,全是普通列,expunge 后访问安全。 影响范围:_skim_paper、_deep_read_paper、_embed_paper(均经 _require_paper)。 _figures_analysis、_reasoning_analysis 自己管理 session 且在 session 内 取属性,不受影响。 实测验证:fix 后 confirm resume skim_paper → action_result success=true, 返回完整粗读报告(one_liner/innovations/keywords/title_zh/abstract_zh), LLM 据此给出正确总结。 --- packages/ai/tools/base.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/ai/tools/base.py b/packages/ai/tools/base.py index 2144d37..0e0a731 100644 --- a/packages/ai/tools/base.py +++ b/packages/ai/tools/base.py @@ -37,7 +37,14 @@ def _resolve_paper_id(val: str) -> tuple[UUID | None, str | None]: def _require_paper(paper_id: str): - """校验 paper_id(支持短 ID 前缀)+ 查库""" + """校验 paper_id(支持短 ID 前缀)+ 查库。 + + 返回的 paper 对象已从 session expunge(detached),但其所有列属性在 + session 内已加载,离开 session 后访问 title/abstract/embedding 等不会 + 触发 lazy load(Paper 无 relationship 字段,全是普通列)。 + 修 detached session bug:此前 return paper 后 session 关闭,handler + 访问 paper.title 报 "Instance not bound to a Session"。 + """ pid, err = _resolve_paper_id(paper_id) if err: return None, ToolResult(success=False, summary=err) @@ -45,6 +52,25 @@ def _require_paper(paper_id: str): with session_scope() as session: try: paper = PaperRepository(session).get_by_id(pid) + # 在 session 关闭前 expunge,让 paper 带着已加载的列属性离开。 + # 触发所有列属性加载(防 expunge 后访问未加载列报错): + _ = ( + paper.id, + paper.title, + paper.arxiv_id, + paper.abstract, + paper.pdf_path, + paper.publication_date, + paper.embedding, + paper.read_status, + paper.metadata_json, + paper.favorited, + paper.rejected, + paper.source, + paper.source_id, + paper.doi, + ) + session.expunge(paper) return paper, None except ValueError: return None, ToolResult(