Files

99 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 轻量版拓展分析自然语言触发 — 第一轮测试报告
- 时间:2026-08-27
- 被测提交:a60f45754(轻量拓展分析自然语言触发,4 文件 +813/-8,未 push)
- 测试环境:/home/xzq/miniconda3/envs/plantai-bff/bin/python(Python 3.11.15 / pytest 9.1.1 / pytest-cov 7.1.0;本轮补充安装 pyyaml/openai/pandas/requests/openpyxl 后方可跑通 main.py 相关测试)
## 测试文件清单
| 文件 | 新增/追加 | 用例数 | 结果 |
|---|---|---|---|
| tests/unit/test_extension_stage_lightweight_nl_intent.py | 新增 | 58 | 全绿 |
| tests/unit/test_extension_stage_lightweight_nl_backend.py | 新增 | 18 | 全绿 |
| tests/unit/test_extension_stage_lightweight_frontend_contract.py | 追加 5 个测试(未删任何现有断言,原 15 → 现 20) | 20 | 全绿 |
## 通过/失败数
| 组 | 结果 |
|---|---|
| A 组(nl_intent 单元) | 58 passed |
| B 组(main.py 后端集成) | 18 passed |
| C 组(前端契约) | 20 passed |
| 新增三件套合计 | 96 passed |
| 回归:Phase A 三件套(agent/input_binding/golden_runner_semantic) | 6 passed |
| 回归:test_agent_chat_actions + deepseek_semantics | 46 passed |
| 回归:全套 test_extension_stage_lightweight*.py | 312 passed + **6 既有失败** + 1 skip |
**6 个既有失败**(已在父提交 90f51ed60 干净 worktree 上逐一复现,与本改动无关,不拦路):
1. test_extension_stage_lightweight_fact_contract.py::test_build_execution_facts_keeps_unmatched_outputs_without_guessing
2. test_extension_stage_lightweight_runtime_binding.py::test_shell_patch_targets_record_hash_literal_kind_and_exact_count
3. test_extension_stage_lightweight_runtime_binding.py::test_same_uploaded_path_in_two_steps_forms_one_safe_path_binding
4. test_extension_stage_lightweight_runtime_binding.py::test_repeated_parameter_literal_across_facts_is_not_patchable
5. test_extension_stage_lightweight_runtime_package.py::test_unrelated_implicit_output_remains_blocked
6. test_extension_stage_lightweight_target_launchers.py::test_target_launchers_share_lightweight_extension_runtime_roots
## 覆盖率
`python3 -m pytest --cov=gfa_core.extension_stage_lightweight.nl_intent --cov-report=term-missing tests/unit/test_extension_stage_lightweight_nl_intent.py`
- gfa_core/extension_stage_lightweight/nl_intent.py:**187 stmts / 0 miss / 100%**
- 无未覆盖行(补测过程:93% → 98% → 99% → 100%,最终 0 遗漏)
## 失败用例+关键报错
本轮无功能缺陷导致的测试失败(测试自身两处断言问题已修复:集合类型断言、reason 前缀断言)。
## 重点验证结论(对应 developer 标记风险 R1-R4 + 测试要求 1-10)
### R1 目录截断(P0 缺陷确认)
真实 store 实测:61 个 published 记录,`_build_catalog` 只放前 11 个即达 11276 字符,truncated=True。
「拓展分析」强触发词下 `_rough_candidates` 返回全部 61 个,但目录按目录名排序截断后 **12_/13_/14_/15_/16_/17_/18_ 共 50 个扩展(含 manifestqc、tfbs、kaks_cis、motif、pav_cnv 等 18 案例中的后 7 个)LLM 完全不可见**,任何消息都无法命中这些扩展。
测试固化:test_real_store_catalog_truncation_smoke、test_build_catalog_truncates_when_over_max_chars。
### R2 中文关键词粗筛(缺陷确认)
真实 store 实测:「帮我做样本表校验」→ 0 候选、不调 LLM(function_summary 全英文,纯中文功能词无法命中 `_summary_keywords` 英文 token 与中文子串)。只有强触发词(拓展分析/扩展分析/extension)或工具词(hmmsearch/hmmer/blast/blastp)才能进 LLM。**注意:工具词路径会把全部 61 个记录都当候选(无法按工具区分),再叠加 R1 截断,实际能解析的只有前 11 个。**
测试固化:test_real_store_pure_chinese_function_words_miss_rough_candidates。
### R3 prefill 字段位置(实现一致)
已验证:`LightweightIntentResolution.prefill` 在 dataclass 顶层;`_match_lightweight_extension_intent` 返回 dict 中 `params.prefill`(顶层副本)与 `params.llm_slots`;前端 `openLightweightNlRunDialog` 读 `intentParams.llm_slots` 的 input_overrides/parameter_overrides/explicit_prefix,不依赖顶层。C 组断言按实际实现写,无错位。
### R4 extension_name 英文(产品缺陷候选,记录待验收)
已确认:`extension_name` 取 function_summary 英文首句(≤60 字符),后端 message 与前端向导标题会显示英文(如 "This tool searches sequences with hmmsearch.")。测试固化:test_resolution_uses_first_sentence_as_extension_name。建议后续加中文 display_name 映射。
### 测试要求 1-10 覆盖情况
1. 无触发词 → extension_id None 且 client_factory 计数为 0(参数化 3 条消息)✅
2. 触发词 + 无 records → 不调 LLM(reason=no_published_records)✅
3. _build_catalog:只含 replaceable inputs 的 {path,role,description}、剔 locked input、parameter 无 value、含 function_summary、超长截断置 truncated ✅
4. _sanitize_resolution:编造 path/fact_id/extension_id/非 str value/confidence 非法 → None+原因(参数化 7 种非法输入 + 4 种非对象);合法 → 正确映射 ✅
5. LLM 抛异常/坏 JSON/缺 choices → None resolution 不抛(reason=llm_unavailable_or_invalid 或 invalid_resolution: 前缀)✅
6. happy path:「使用 HMM 拓展分析对刚上传的文件进行分析,参数 e-value=1e-5」→ input_overrides/parameter_overrides/confidence/prefill 全对 ✅
7. load_published_lightweight_records:published/confirmation_pending/baseline_failed/坏 JSON/非对象 → 只返回 published;坏 store 根返回 [] ✅
8. job_file_hints 进入 LLM user 消息 JSON(并测 MAX_JOB_FILE_HINTS 截断 200)✅
9. 参数值约束:sanitize 只做结构校验(fact_id 合法即收),不校验"参数是否在用户消息中出现"(prompt 层约束),测试固化该行为 ✅
10. 覆盖率 100%(见上)✅
### B 组关键用例
- _match_lightweight_extension_intent:命中返回 lightweight:True/llm_slots/extension_store/prefill/confidence/parse_reason;空 store、resolution None、extension_id None → 均返回 {} ✅
- 回退三处:Phase A 命中不标 lightweight;Phase A 空列表 → 回退轻量(三个空返回点中 candidates 为空与 trigger 为空两处直接测到;rnaseq 短路点静态确认回退存在,未单独构造 rnaseq 消息,因为该消息本身也不命中轻量粗筛)✅
- _lightweight_job_file_hints:input+workspace 相对路径、空 job_id → []、200 上限 ✅
- import-paths 端点:合法 /home/xzq/ 下文件复制进 _nl_imports、同名冲突加 _1 后缀、前缀外路径 400、不存在路径 400、空 job_id 400 ✅(注:source 必须在 /home/xzq/ 下构造,tmp_path 在 /tmp 会被前缀校验拒绝)
- _run_agent_chat 短路:预闸命中 + 轻量 intent → action="propose_extension_stage"、model_used="rule",且 monkeypatch 证明未触碰 LLMManager/RouterAgent(会抛异常);预闸不命中 → 走正常 MainAgent 流程 ✅
- /api/agent/plan 守卫端到端:轻量 intent 不调 _extension_stage_plan_response;Phase A 命中正常调用 ✅
## 遗留问题清单
| 编号 | 位置 | 现象 | 建议修复 |
|---|---|---|---|
| 1(P0) | gfa_core/extension_stage_lightweight/nl_intent.py `_build_catalog`(约 128-173 行) | 真实 store 61 个 published 记录只进前 11 个即截断(11276/12000 字符),目录按目录名排序,12_ 及以后的 50 个扩展(含 18 案例中 12-18 的 7 类)LLM 不可见,「拓展分析」强触发词下也永远无法命中 | 截断策略需改为"全部进目录、按相关性排序"或"先粗筛再目录化"(按关键词把候选缩到相关子集),至少保证 18 案例的功能词能进目录;12_ 以后的扩展在截断前应有兜底(如只放 extension_id+summary 的轻量条目) |
| 2(P1) | 同文件 `_rough_candidates`(约 108-122 行) | 纯中文功能词(如"样本表校验")0 候选不调 LLM,只能靠强触发词或英文工具词进入;工具词 hmmsearch/hmmer/blast 路径会把全部 61 个记录都当候选(不按工具区分) | 中文 summary 子串匹配对 18 案例 prompt 影响需产品确认;工具词路径建议按工具名映射到对应扩展(如 hmmsearch → 03_ 系列),或至少接受"全候选+目录排序优化" |
| 3(P1) | gfa_core/extension_stage_lightweight/nl_intent.py `_rough_candidates`(约 113-114 行) | `str(record.get("extension_id"))` 对缺失 id 的记录产出 "None" 字符串候选 | 改为 `record.get("extension_id") and str(...)` 过滤,或直接 `[r["extension_id"] for r in records if r.get("extension_id")]` |
| 4(P2) | 同文件 `resolve_lightweight_extension_intent`(约 265-346 行) | `extension_name` 取 function_summary 英文首句(≤60),后端 message 与前端向导标题显示英文 | 产品验收时确认是否需中文 display_name 映射;若需,在 record 或 catalog 层加中文名字段 |
| 5(P2) | apps/web/vue3/main.py `nl_import_paths`(约 4449-4512 行) | 目录导入用 `shutil.copytree(dirs_exist_ok=True)` 合并进目标目录,且同名目录无冲突后缀(与文件路径的 _1 后缀策略不一致);多路径循环中前一个成功、后一个 400 时已复制的文件不回滚 | 目录同名冲突建议同样加后缀;整体事务性按需评估(低风险,向导场景可接受部分成功) |
| 6(P3/信息) | 全套 lightweight 回归 | 6 个既有失败(见上清单),父提交 90f51ed60 复现,与本改动无关 | 不拦路;建议另开任务修复 |
| 7(P3/信息) | 测试环境 | plantai-bff 环境缺 pyyaml/openai/pandas/requests/openpyxl,main.py 相关测试此前跑不通(test_agent_chat_actions 修复前 27 failed) | 已补充安装;建议把依赖清单写进仓库 requirements 或测试文档 |
## 测试环境备注
- 本轮在 plantai-bff conda 环境补充安装了:pyyaml、openai、pandas、requests、openpyxl(均为项目既有依赖,纯 pip 安装,未动仓库文件)。
- 验证既有失败时在父提交 90f51ed60 建了临时 worktree,验证后已删除(git worktree remove --force)。
- 工作区改动仅限测试文件:tests/unit/test_extension_stage_lightweight_nl_intent.py(新增)、tests/unit/test_extension_stage_lightweight_nl_backend.py(新增)、tests/unit/test_extension_stage_lightweight_frontend_contract.py(追加 5 个测试,现有断言未删)。开发代码未触碰,未 push。