
Opik Python SDKllm_unit完全指南基于 Pytest 的 LLM 单元测试追踪与实验上报【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm本篇指南聚焦 Opik Python SDK 中的llm_unit装饰器讲解如何将普通 Pytest 测试升级为可被 Opik 平台自动追踪的 LLM 单元测试运行测试时 Opik 会自动为每次测试运行创建实验Experiment将测试名、输入、期望输出、元数据与测试结果Passed/Failed写入 Opik并生成终端汇总报告。读完本文你将掌握llm_unit的三个核心参数、与track/pytest.mark.parametrize的组合用法、底层数据流从测试钩子到实验落库的完整调用链以及通过配置开关控制该功能的实操方案。一、llm_unit是什么把 Pytest 用例变成可追踪的 LLM 实验在 LLM 应用开发中传统单元测试只能回答断言是否通过却无法沉淀这次测试跑了什么输入、模型输出了什么、期望是什么。Opik 的 Pytest 集成通过llm_unit装饰器解决这一问题当你在测试函数上标记llm_unit并运行pytest时Opik 会自动创建一个实验experiment并把每条测试的以下信息记录进去测试名称test name测试输入input期望输出expected output附加元数据metadata测试结果以名为Passed的反馈分数写入值为 1.0 或 0.0。该 API 由 SDK 顶层导出位于 sdks/python/src/opik/init.py并注册为 Pytest 插件见 sdks/python/setup.py 中的pytest11入口点因此无需额外手动注册钩子。llm_unit装饰器的完整签名与参数说明如下实现在 sdks/python/src/opik/plugins/pytest/decorator.pydef llm_unit( expected_output_key: str expected_output, input_key: str input, metadata_key: str metadata, ) - Callable[[Any], Any]:参数默认值作用expected_output_keyexpected_output指定测试函数参数中哪一个参数名会被当作 LLM 任务的期望输出expected_output记录下来不指定时 Opik 会尝试查找名为expected_output的参数input_keyinput指定测试函数参数中哪一个参数名会被当作 LLM 任务的输入input记录下来不指定时查找名为input的参数metadata_keymetadata指定测试函数参数中哪一个参数名会被当作元数据metadata记录下来不指定时查找名为metadata的参数值得注意的是装饰器内部通过_get_test_run_content进行参数归一化非字典类型的input/expected_output/metadata会被自动包装成{input: ...}、{expected_output: ...}、{metadata: ...}形式的字典并自动注入test_name取 pytest 的 node id。这一行为保证了写入数据集的数据结构始终统一方便后续按测试维度检索。二、快速上手从朴素断言到可观测实验2.1 安装与配置确保已安装 Opik Python SDK 且完成平台连接配置自托管部署使用opik configure --use_local云端使用opik configure然后编写如下测试import pytest from opik import track, llm_unit track def llm_application(user_question: str) - str: # LLM application code here return Paris llm_unit() def test_simple_passing_test(): user_question What is the capital of France? response llm_application(user_question) assert response Paris运行pytest之后每次测试运行会生成一个名为Test-Suite-本地时间戳的实验时间戳格式由 sdks/python/src/opik/plugins/pytest/experiment_runner.py 中的datetime_helpers.local_timestamp()生成实验挂载在名为tests的数据集dataset下每条测试的输入、期望输出、元数据构成一个数据集条目每个测试用例对应的 Trace 被关联到实验条目同时写入Passed反馈分数通过 sdks/python/src/opik/plugins/pytest/hooks.py 的pytest_sessionfinish钩子调用client.log_traces_feedback_scores完成。运行结束后终端会打印一个 Rich 格式的汇总面板见 sdks/python/src/opik/plugins/pytest/summary.py┌─────────────────────────────────────────┐ │ Opik: LLM Test Results │ │ Passed: 1 │ │ Failed: 0 │ │ Total: 1 │ │ See the results: Opik UI 地址 │ └─────────────────────────────────────────┘2.2 传参与命名约定若你的测试参数名与默认值不同可通过三个*_key参数显式指定。例如llm_unit(input_keyquery, expected_output_keyanswer, metadata_keytags) def test_example(query, answer, tags): ...从源码看参数映射在装饰器工厂内部被构建为argnames_mapping字典{expected_output: ..., input: ..., metadata: ...}随后通过inspect_helpers.extract_inputs从函数实参中按映射提取对应值。若测试函数没有名为input/expected_output/metadata或你指定的*_key的参数对应字段将被记录为None并不影响测试本身的执行。三、进阶用法与track和参数化测试组合3.1 与track组合获得完整调用链llm_unit与track组合是推荐姿势track负责对 LLM 应用调用链打点llm_unit负责把整条测试包装成一次可追踪的 Trace。装饰器内部实现为opik.track(capture_inputFalse)外层包装见 decorator.py随后通过opik_context.update_current_trace/update_current_span将归一化后的输入与元数据写入当前 Trace 和 Span——这就是为什么测试的输入会同时出现在 Trace 输入与实验输入中且test_name会被从 Trace 输入中剔除源码中trace_input.pop(test_name)的注释即为我们不需要它在 traces 中。3.2 与pytest.mark.parametrize组合批量跑同一断言llm_unit与 Pytest 的参数化装饰器天然兼容可用同一组断言覆盖多组输入-期望对该示例也出现在 sdks/python/examples/demo_data.py 的演示数据中import pytest from opik import track, llm_unit track def llm_application(user_question: str) - str: # LLM application code here return Paris llm_unit(expected_output_keyexpected_output) pytest.mark.parametrize(user_question, expected_output, [ (What is the capital of France?, Paris), (What is the capital of Germany?, Berlin), ]) def test_simple_passing_test(user_question, expected_output): response llm_application(user_question) assert response expected_output每个参数化实例都会生成独立的测试记录node id 包含参数值例如test_things.py::test_example[13 32] (call)见 decorator.py 中_get_test_nodeid的注释示例因此在 Opik 实验中可以逐条查看每个输入-输出对的通过情况。3.3 与evaluate的分工官方推荐见 demo 数据中的指引开发期对 LLM 应用做细粒度评估时优先使用evaluate函数因为它提供更详细的评估报告而llm_unit适合在既有 Pytest 测试体系中无缝接入用于跟踪整体通过率与逐条通过率实现 CI 回归观测。四、底层原理装饰器背后的完整数据流4.1 插件自动激活机制llm_unit的追踪并非始终开启。插件通过 hooks.py 中的pytest_collection_modifyitems检查测试对象是否带有_opik_llm_unit True标记由装饰器在包装函数上通过setattr(wrapper, _opik_llm_unit, True)设置。一旦发现此类测试即自动激活插件config._opik_pytest_active True。插件激活的三种途径_is_plugin_enabled逻辑命令行参数--opikpytest_addoption中注册默认False配置文件pytest.ini中设置opik_pytest_enabled true通过parser.addini注册的布尔型 ini 项测试集合中存在_opik_llm_unit标记的测试自动激活。4.2 测试运行期收集输入并打点 Trace测试执行时包装函数依次完成通过PYTEST_CURRENT_TEST环境变量解析出当前测试的 node id_get_test_nodeid格式如sdks/python/tests/test_x.py::TestGroup::test_example (call)取空格前的部分将 node id 存入test_runs_storage.LLM_UNIT_TEST_RUNS集合构造TestRunContent输入/期望输出/元数据定义见 test_run_content.py并分别缓存 Trace 数据与内容到TEST_RUNS_TO_TRACE_DATA、TEST_RUNS_CONTENTS存储定义见 test_runs_storage.py更新当前 Trace/Span 的 input 与 metadata执行原始测试函数并返回结果。若追踪过程中出现异常装饰器只记录错误日志而不影响测试结果——追踪失败不会导致测试失败这一点对 CI 稳定性很重要。4.3 会话结束期写分数、建实验、落库pytest_sessionfinish钩子hooks.py在测试会话结束时执行筛选出本次会话中所有被llm_unit标记的测试条目对每条有测试报告report且有 Trace 数据的测试构造BatchFeedbackScoreDictnamePassedvaluefloat(report.passed)即通过为 1.0失败为 0.0批量调用client.log_traces_feedback_scores写入反馈分数调用experiment_runner.run执行实验落库最后client.flush()确保数据发送完成。4.4 实验落库的幂等设计experiment_runner.py 的实现展示了数据集去重策略dataset_item_content_to_ids { json.dumps(dataset_item.get_content(), sort_keysTrue): dataset_item.id for dataset_item in existing_dataset_items }即将已有数据集条目按内容json.dumps(..., sort_keysTrue)建立内容→ID 索引新测试若与已有条目内容完全一致TestRunContent序列化后相同则复用已有数据集条目 ID否则生成新 ID 并插入。随后创建实验并批量写入ExperimentItemReferences关联dataset_item_id、trace_id、project_name。这意味着重复运行相同输入、相同期望输出的测试不会在tests数据集中产生重复条目实验可安全地反复创建。4.5 配置开关pytest_experiment_enabled是否执行上述实验上报受 SDK 配置项控制。在 sdks/python/src/opik/config.py 中pytest_experiment_enabled: bool True If enabled, tests decorated with llm_unit will log data to Opik experiments装饰器工厂内部会先读取该配置config.get_from_user_inputs()若为False则直接返回原始函数、不做任何包装见 decorator.py即功能可在不改动测试代码的情况下整体关闭。这在本地调试、或只想跑纯断言不希望产生实验数据时非常实用。五、在 CI 中的落地建议结合上述机制推荐的生产实践统一参数命名测试函数参数尽量命名为input/expected_output/metadata可直接使用默认的llm_unit()代码最简洁结合断言与反馈分数Passed反馈分数与断言结果强相关float(report.passed)可在 Opik 平台按实验聚合查看通过率趋势实现回归监控利用参数化覆盖边界用pytest.mark.parametrize批量覆盖正常、边界与失败样本每条样本在实验中独立可见按需关闭在不需要上报的场景如频繁的本地迭代通过配置pytest_experiment_enabled false关闭避免产生噪音数据与evaluate分工开发期细粒度评估用evaluate回归期整体通过率观测用llm_unit实验。六、总结llm_unit是 Opik 将 Pytest 单元测试与实验追踪体系打通的关键接口它以极低的心智成本一个装饰器把断言通过与否升级为结构化、可查询、可对比的实验数据并天然兼容track调用链追踪与pytest.mark.parametrize批量用例。其实现插件激活、Trace 打点、反馈分数、数据集去重、实验落库全部集中在 sdks/python/src/opik/plugins/pytest/ 目录下配套单元测试见 sdks/python/tests/unit/plugins/pytest/test_hooks.py感兴趣可进一步阅读源码验证本文所述机制。【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考