ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Yuxi 测试审计与发布门禁 Oracle 收敛:从“检查源码“到“验证运行时事实“的测试治理实践

Yuxi 测试审计与发布门禁 Oracle 收敛:从“检查源码“到“验证运行时事实“的测试治理实践 Yuxi 测试审计与发布门禁 Oracle 收敛从检查源码到验证运行时事实的测试治理实践【免费下载链接】Yuxi可私有部署的多租户知识智能体平台统一 RAG、知识图谱、多智能体、MCP/Skills、沙盒与权限管理。Self-hosted knowledge agent platform for RAG, knowledge graphs and multi-agent workflows.项目地址: https://gitcode.com/GitHub_Trending/yu/Yuxi导读本文基于 Yuxi 仓库中的架构决策记录 2026-09-07-test-suite-audit-follow-up.md完整还原一次针对测试套件的第二轮独立审计如何识别出 Prompt 测试未验证真实组装结果、默认分块上限测试不足以排除错误默认值、benchmark reorder 测试依赖调度时序等证据边界缺陷并逐一收敛为可独立证明运行时事实的 oracle。读完本文你将掌握一套可复用的测试治理方法论——包括负向断言必须作用在运行时组装结果上、用事件同步替代固定睡眠、用精确计数替代模糊上限三条核心原则以及 Backend 与 Web 两侧测试套件的取舍标准与验证基线可直接用于你自己的测试套件审计。背景测试套件精简后的独立审查发现了什么Yuxi 的测试套件此前已经过一次精简但在精简后的独立审查中审计者仍发现若干证据边界不完整的问题集中在四类场景问题具体表现后果Prompt 测试不完整只检查源字符串没有验证实际组装结果无法证明运行时 prompt 中不存在重复注入默认分块上限测试不足只断言硬上限不校验默认值1.5 倍硬上限允许错误的默认分块值通过benchmark reorder 测试依赖调度时序依赖固定延迟和调用计数事件循环调度变化会制造假失败或假绿决策记录引用失效文件引用了已删除的测试文件文档与仓库状态脱节误导后续读者此外审计还立下两条硬性纪律不以测试名称相似或文件较长作为删除理由源码检查尚无等价行为覆盖时保留并明确它只证明源码结构不能冒充浏览器行为证据。该决策记录的类型被标记为simplificationOwner 指向backend/test/unit/agents/test_chatbot_prompt.py说明这是一次以简驭繁的收敛——不是删减覆盖而是把每条断言都钉在真正的观察边界上。核心决策概览针对上述问题审计作出三项关键收敛决策在运行时组装后的 chatbot prompt 上保留html:preview不被重复注入的负向断言benchmark worker 用事件同步异常与后续调用确保 reorder buffer 的测试不依赖固定睡眠默认 512 token 配置使用精确 token 计数作为 oracle并修正决策记录中已删除测试文件的合并路径。每一条决策都对应一个被明确记录并否决的替代方案详见下文各节这正是 ADR 文档决策可回溯、理由可审计的体现。决策一Prompt 负向断言必须作用在运行时组装结果上问题只检查源字符串无法证明运行时不重复注入旧的 Prompt 测试只检查prompt.py中定义的源字符串常量。审计判定该做法不可接受源码中不存在html:preview与运行时组装后的 prompt 中不存在html:preview是两回事——组装逻辑如拼接文件系统约束、日期、用户 system prompt可能引入重复注入。决策对build_prompt_with_context的真实输出做负向断言收敛后的测试位于 test_chatbot_prompt.py其核心用例为from types import SimpleNamespace from yuxi.agents.buildin.chatbot.prompt import build_prompt_with_context def test_chatbot_prompt_declares_workspace_visibility_and_default_write_boundary(): prompt build_prompt_with_context( SimpleNamespace( workdir_path/home/gem/user-data/projects/11111111-1111-4111-8111-111111111111, system_prompt, ) ) assert 可以读取其他 Project 目录作为参考 in prompt assert 未经用户明确要求不得在当前 Project Workdir 之外 in prompt assert /home/gem/user-data/agents/skills/ in prompt assert html:preview not in prompt它调用真实的 build_prompt_with_context 函数传入带workdir_path和system_prompt的上下文对象对组装结果同时做正向断言工作区可见性、默认写边界、个人 Skill 目录和负向断言html:preview不重复注入。从 prompt.py 的实现可以看到组装过程build_prompt_with_context会拼接当前日期、基础 PROMPT、动态生成的| 文件系统约束 |段落以及用户的system_prompt且当 context 缺少workdir_path时会抛出ValueError。这解释了为什么测试必须构造带workdir_path的上下文——它是该函数正常运行的前置条件。替代方案只检查源字符串被拒绝文档明确记录只检查源字符串拒绝因为它不能证明实际运行时 prompt 没有重复注入。这一原则可推广为一条通用的 oracle 选择准则凡是断言X 不发生都应尽量放在最终产物上检查而不是检查产生产物的中间常量。决策二benchmark worker 用事件同步替代固定睡眠问题reorder buffer 测试依赖调度时序Yuxi 的 benchmark generation 采用多 worker 并发 reorder buffer 按attempt_no连续 yield 的流式架构见 benchmark_generation 相关实现。旧的测试用固定延迟 调用计数来模拟某 worker 先于另一 worker 产出的时序而事件循环的调度顺序一旦变化就会出现假失败该同步的没同步上或假绿该暴露的竞态没暴露。决策用asyncio.Event显式同步收敛后的测试位于 test_benchmark_generation.py全部改用asyncio.Event做确定性同步。以异常收敛测试为例第 403 行起pytest.mark.asyncio async def test_iter_generated_benchmark_items_drains_reorder_buffer_on_exception(monkeypatch): 多 worker 下某 worker 抛异常时reorder 缓冲中已产出但未按 attempt_no 连续 yield 的 item 经 drain 路径保全。 monkeypatch.setattr(benchmark_generation, select_model, lambda model_spec: TrackingLlm()) monkeypatch.setattr(benchmark_generation, kb_manager, NoQueryKnowledgeBase()) call_count 0 first_call_started asyncio.Event() release_first_call asyncio.Event() async def fake_generate(**kwargs): nonlocal call_count call_count 1 if call_count 1: first_call_started.set() await release_first_call.wait() # 阻塞第一个调用模拟延迟后抛异常 raise RuntimeError(worker error) ...测试通过first_call_started.set()标记第一个 worker 已进入调用、release_first_call控制何时放行从而精确构造第一个 attempt 永不产出、其他 worker 的 item 卡在 reorder 缓冲的时序并断言异常 break 后 drain 路径把这些乱序 item 一并 yieldassert items [ {query: q2, gold_answer: a, gold_chunk_ids: [anchor_chunk]}, {query: q3, gold_answer: a, gold_chunk_ids: [anchor_chunk]}, {query: q4, gold_answer: a, gold_chunk_ids: [anchor_chunk]}, ]配套用例还验证了真流式第 2 次 LLM 调用未完成前第 1 条已产出test_iter_generated_benchmark_items_yields_before_all_workers_finish并配合asyncio.wait_for(collect_items(), timeout5)防止死锁挂死异常时取消其他 workertest_iter_generated_benchmark_items_cancels_other_workers_on_exception断言第二个 worker 收到CancelledErrorsecond_worker_cancelled.is_set()。替代方案保留固定延迟和调用计数被拒绝文档明确记录保留固定延迟和调用计数拒绝因为事件循环调度变化会制造假失败或假绿。事件同步的优点在于无论调度器如何切换测试只依赖事件已设置这一语义事实而不是过了多少毫秒。决策三默认 512 token 上限用精确计数 oracle问题只断言硬上限会让错误默认值漏网general parser 的分块流程存在两层上限默认目标上限512 token与可选硬上限hard_limit_token_num。旧的测试只断言任意 chunk 不超过硬上限但若实现把默认值错误放宽例如从 512 改成 10241.5 倍硬上限768依然成立错误的默认值就会通过测试。决策锁定默认值的精确 token 计数收敛后的 test_chunking_token_limit.py 新增了专门锁定默认值的用例def test_default_config_uses_512(self): doc 测试\n * 400 # 约 800 token默认值错误放宽到 1024 时不会切分 chunks general.chunk_markdown(doc) token_counts [nlp.count_tokens(chunk) for chunk in chunks] assert token_counts [514, 286]该用例的巧妙之处在于测试\n * 400约 800 token若默认上限被错误放宽到 1024 则根本不会切分测试即失败同时精确断言切分结果是[514, 286]把默认值语义本身变成 oracle。该文件还通过_isolated_modulesfixture 用sys.modules占位隔离加载nlp.py与parsers/general.py绕开 yuxi 包的重依赖链langchain / pydantic / .env并在运行后清理sys.modules避免污染其他测试——这同时印证了文档中unit 测试仍可在不修改sys.path的情况下收集的验证结论。同文件内的TestHardSplitByTokenLimit还覆盖了hard_limit_token_num768时短尾合并的精确行为[512, 758]与max_chars0时下限取 1 的边界[a, b, c]。替代方案只断言硬上限被拒绝文档明确记录只断言硬上限拒绝因为 1.5 倍硬上限允许错误的默认分块值通过。这条决策同时适用于语义分块semantic parser合并后的 test_semantic_chunking.py 中test_empty_heading_preserves_current_title_context显式传入parser_config{chunk_token_num: 512}验证空标题行不丢失标题上下文test_truncated_heading_token_stream_is_ignored用 monkeypatch 制造截断的 heading token 流验证空结果。决策四Web 测试按实际观察边界取舍Web 侧的审计遵循同一哲学——每条测试必须落在它真正能观察到的边界上保留请求、状态、协议、解析和渲染结果的测试。例如共享详情框架的运行时测试检查具名面板内容实际渲染、删除 Tab 后对应内容消失在内存中移除动态内容插槽的负控会因面板内容缺失而失败即通过运行时模板行为验证插槽、动态 Tab 与forceRender透传HTML 预览尺寸仍在渲染输出上检查工具参数解析保留实际输入结果测试。删除仅冻结 CSS 数值、图标、旧文案和搬迁路径的检查——这些属于一次性迁移的产物删除它们不代表视觉回归已有自动覆盖文档对此有清醒声明。混合测试只删装饰性断言保留可访问性、路由、数据隔离与迟到响应检查。源码检查策略尚无等价行为覆盖时保留但明确其边界——只证明源码结构不能冒充浏览器行为证据暂留的源码结构检查不得作为 UI 行为的证据。文档还记录了 Web 守卫测试的收敛细节守卫测试同时覆盖正常放行与拒绝配置合并读取检查具体返回值和 Store 状态模型优先级源码检查先确认表达式存在轮询生命周期检查请求完成后再次调度及停止后的清理。且测试入口仍使用web/package.json的全目录 selector未跳过或排除测试——避免用配置文件排除这种隐式手段掩盖覆盖缺口。决策五Backend 测试保留独立观察面与 QA parser 合并保留原则观察面不因文件名而删Backend 侧确立的保留原则是业务、权限、事务、队列、文件隔离、恢复和部署边界的独立观察面全部保留。文档特别强调live_api_cleanup见 test_live_api_cleanup.py 与 test_live_api_cleanup_run_rows.py、性能工具、Compose 契约test_docker_compose_service_boundaries.py和惰性导入检查均有明确 consumer不因文件名含 cleanup/tmp/source 或单文件用例少而删除——这是对以名取人式审计的明确否定。合并两个 QA parser 测试收敛为一个文件两个 QA parser 测试合并为 test_qa_parser.py共享一次隔离加载_load_qa_parser通过importlib.util.spec_from_file_location按文件路径隔离加载ragflow_like/parsers/qa.py并完整保留全部 oracle能力面对应测试类/用例结构化提取Q:/A: 前缀、标题路径TestSplitLongQaChunks、TestAtxHeadingBoundary围栏边界~~~ / 内不拆出虚构问答TestPrefixFenceBoundary含fence_with_info_string、不匹配围栏、未闭合围栏吸收等孤儿答案归属TestOrphanAnswer超长切分限长TestChunkMarkdownLengthCap走实现默认常量锁定默认上限不超 embedding 承诺默认配置语义max_chars显式传参 默认路径双覆盖其中_EMBEDDING_CHAR_LIMIT 4000是独立于实现常量_QA_CHUNK_MAX_CHARS的保守字符兜底对应 bge_m3 的 4096 token 上下文上限避免自我引用 oracle——测试的期望值不应直接拷贝实现常量。TestSplitLongQaChunks还覆盖了含 tab 的问题保持完整、非英语答案标记不作分隔符、无答案标记的 tab 文本硬切等边界。文档强调没有合并观察边界不同的 unit、integration 和 E2E 测试——合并只发生在同一观察边界的文件之间。第二轮收敛死辅助对象清理与附件服务更名决策记录还记录了第二轮收敛的三项动作删除 3 个同文件内无消费者的死辅助对象静态引用检查确认FakeKnowledgeBase、_ChildContext、_make_thread_files在仓库内无消费者后才删除真实附件服务测试更名从test_tmp_attachment_service.py更名为 test_attachment_service.py消除临时脚本的错误信号更名后仍收集相同的 16 个用例不删除仍被外部清理入口调用的兼容函数队列策略正向测试改为断言规范化返回值覆盖enqueue/reject/steer三个有效值的具体返回结果而不是只验证不抛异常两个 unit 测试移除无效的 cwdsys.path注入示例问题测试的 3 个重复FakeKnowledgeBase定义收敛为 1 个按详情注入的 fake异常和成功分支的返回断言保持不变。此外后端测试目录的静态审计确认6 个数据 fixture 均被解析或真实知识库路由使用其中测试图片.png作为测试文档.docx的嵌入资源由转换结果间接校验见 backend/test/data 目录未删除仍被真实路由或解析测试使用的 fixture。验证结果与发布门禁基线收敛完成后验证基线如下验证项结果Prompt、分块和 benchmark generation 相关单元测试36 passedruff check与改动文件ruff format --check通过已删除的test_semantic_chunking_empty_heading.py引用改为合并后的 test_semantic_chunking.pyWeb 守卫测试同时覆盖正常放行与拒绝配置合并读取检查具体返回值和 Store 状态模型优先级源码检查先确认表达式存在轮询生命周期请求完成后再次调度及停止后的清理unit 测试收集无需修改sys.path即可收集后果、旧能力禁令与重新引入条件后果测试继续覆盖真实组装结果、worker 异常收敛和默认配置语义精确分块 oracle 依赖当前 tokenizer 的确定性结果tokenizer 或分块策略变更时必须同步审阅语义 Owner 和决策记录Web 的样式数值与一次性迁移不再由源码正则冻结但这些删除不代表视觉回归已有自动覆盖队列、流式、API、文件隔离和设置交互测试继续独立维护暂留的源码结构检查不能冒充浏览器行为证据。旧能力禁令文档用一句话立下红线测试不得退回只检查源常量、固定睡眠调度或模糊长度上限的证据。这三个退回场景恰好对应本次收敛的三大决策可作为任何测试评审的快速自检清单。重新引入条件文档规定只有满足以下全部条件才可替换当前 oracle新的测试仍能独立证明同一运行时事实对调度有显式同步能区分配置回归。方法论提炼可复用的测试审计清单综合全文可将本次收敛提炼为六条可迁移到其他项目的审计准则负向断言对准最终产物断言X 不发生时检查运行时组装/渲染后的结果而非源常量用事件同步替代睡眠所有并发时序测试使用asyncio.Event或等价同步原语并配合asyncio.wait_for超时防挂死默认值本身要成为 oracle用精确计数断言默认配置的产物形态防止错误默认值在硬上限掩护下漏网期望常量独立于实现常量测试中的上限兜底值应来自独立推导如模型上下文预算避免自我引用按观察边界取舍测试保留请求/状态/协议/渲染结果等真实观察边界删除只冻结迁移产物的断言源码结构检查可保留但必须声明其证据边界不以文件名或文件大小作删除依据有明确 consumer 的测试cleanup、性能、Compose 契约、惰性导入必须保留合并测试只发生在同一观察边界内且不得改变 import 隔离与 oracle 语义。【免费下载链接】Yuxi可私有部署的多租户知识智能体平台统一 RAG、知识图谱、多智能体、MCP/Skills、沙盒与权限管理。Self-hosted knowledge agent platform for RAG, knowledge graphs and multi-agent workflows.项目地址: https://gitcode.com/GitHub_Trending/yu/Yuxi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表