1. 项目概述:为什么我们需要一个集成Ollama的API自动化测试框架?

最近在折腾大模型应用的后端开发,发现一个挺头疼的问题:每次迭代模型或者调整提示词(Prompt),都得手动去调用接口、检查返回结果,不仅效率低,还容易遗漏一些边界情况。特别是当我们把像Ollama这样的本地大模型服务集成到业务流里,测试就变得更复杂了。它不像传统的REST API返回固定的JSON结构,大模型的输出是开放式的文本,充满了不确定性。一个标点符号的改变、一个词语的替换,都可能影响下游业务逻辑的正确性。

这就是“Ollama集成测试框架”这个项目要解决的核心痛点。它不是一个从零造轮子的宏大工程,而是基于成熟的Python测试生态(主要是 pytest ),构建一套专门用于验证与大模型API交互的自动化测试方案。简单说,就是写一段脚本,能自动调用Ollama的API,然后智能地判断返回的文本是否符合我们的预期,比如是否包含了关键信息、是否遵循了指定的格式、情感倾向是否正确等等。

对于开发者、测试工程师或者任何需要确保AI应用输出稳定可靠的人来说,这套框架能带来几个实实在在的好处: 提升回归测试效率 ,每次代码或模型更新,一键运行所有测试用例; 保障输出质量 ,通过预设的断言规则,确保模型输出不会“跑偏”; 实现持续集成 ,可以轻松接入Jenkins、GitHub Actions等CI/CD流程,让测试成为开发环节中自然的一环。

2. 框架整体设计与核心思路拆解

2.1 核心需求与挑战分析

在动手搭建之前,我们先得想清楚要测什么,以及难点在哪。集成Ollama的API测试,主要关注以下几个层面:

  1. 基础连通性测试 :Ollama服务是否正常启动?API端点(通常是 http://localhost:11434/api/generate )能否访问?这是所有测试的基石。
  2. 功能正确性测试 :给定一个输入(Prompt),模型返回的文本内容是否满足业务要求?例如,让模型总结一段话,它是否真的给出了摘要,而不是复述原文?
  3. 性能与稳定性测试 :API的响应时间是否在可接受范围内?长时间、高并发调用下,服务是否会崩溃或产生严重延迟?
  4. 输出格式与结构测试 :对于需要结构化输出的场景(比如让模型生成JSON),返回的内容是否能被正确解析?字段是否齐全?
  5. 非确定性输出的验证 :这是最大的挑战。大模型的输出每次可能略有不同。我们不能简单地断言返回字符串完全等于某个值,而是需要更灵活的验证策略,如关键词匹配、正则表达式、语义相似度判断等。

基于这些需求,我们的框架设计思路就很明确了: pytest 为骨架,以 requests httpx 为HTTP客户端,封装针对Ollama API的调用,并设计一套丰富的“断言器”来应对非确定性输出。

2.2 技术栈选型与理由

为什么选这些工具?这里有个人的一些考量:

  • 测试框架:pytest 。这是Python社区的事实标准。它比 unittest 更简洁灵活,夹具(fixture)机制非常适合管理测试资源(如Ollama客户端连接),丰富的插件生态(如 pytest-html 生成报告, pytest-xdist 并行测试)能极大提升测试体验。
  • HTTP客户端:httpx 。虽然 requests 足够经典,但我更倾向于 httpx 。它兼容 requests 的API,上手无门槛,同时原生支持异步HTTP请求。在跑大量测试用例或性能测试时,异步能力能显著缩短总耗时。对于同步需求,它的用法和 requests 几乎一样。
  • 断言与验证库:综合使用
    • pytest 内置的 assert 语句,用于基础判断。
    • 正则表达式 ( re ) :用于验证输出是否匹配某种模式,例如日期格式、电话号码等。
    • 第三方库: deepdiff :用于复杂JSON结构的对比,可以忽略一些无关紧要的差异(如空格、字段顺序)。
    • 语义相似度: sentence-transformers openai 的Embedding API :这是应对非确定性输出的“大招”。通过计算生成文本与预期文本在向量空间中的余弦相似度,来判断它们的意思是否相近。虽然会引入额外依赖和计算开销,但对于关键场景的验证非常有效。
  • 配置管理:pydantic + dotenv 。用 pydantic BaseSettings 来定义和管理测试配置(如Ollama服务地址、默认模型、超时时间),通过 .env 文件区分不同环境(开发、测试、生产),保证测试的灵活性和安全性。
  • 测试数据管理:JSON/YAML文件 。将测试用例(输入Prompt、预期输出关键词、断言规则等)与测试脚本分离,存储在外部文件中。这样非技术人员也能参与维护测试用例,且便于进行数据驱动的测试。

注意 :选择 sentence-transformers 进行本地语义相似度计算时,需要下载模型文件(如 all-MiniLM-L6-v2 ),首次使用可能较慢。如果网络环境允许,使用OpenAI等在线API可能更方便,但会带来成本和网络依赖。

3. 核心细节解析与实操要点

3.1 Ollama API调用封装

直接在每个测试用例里写 requests.post(...) 会很冗余,且不利于维护。我们需要一个统一的客户端封装。

# core/ollama_client.py
import httpx
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
import logging

logger = logging.getLogger(__name__)

class OllamaGenerateRequest(BaseModel):
    """匹配Ollama /api/generate 接口的请求体"""
    model: str
    prompt: str
    stream: bool = False
    options: Optional[Dict[str, Any]] = None

class OllamaClient:
    def __init__(self, base_url: str = "http://localhost:11434", timeout: float = 30.0):
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.client = httpx.AsyncClient(timeout=timeout) # 使用异步客户端

    async def generate(self, request: OllamaGenerateRequest) -> str:
        """调用生成接口,返回完整的响应文本"""
        url = f"{self.base_url}/api/generate"
        try:
            # 注意:Ollama API 期望的是JSON body
            response = await self.client.post(url, json=request.dict())
            response.raise_for_status() # 状态码非2xx会抛出异常
            result = response.json()
            # 响应结构:{"model":"xx","response":"...","done":true}
            return result.get("response", "").strip()
        except httpx.HTTPStatusError as e:
            logger.error(f"API请求失败,状态码:{e.response.status_code}, 响应:{e.response.text}")
            raise
        except httpx.RequestError as e:
            logger.error(f"网络请求错误:{e}")
            raise
        except KeyError:
            logger.error("API响应格式异常,未找到'response'字段")
            raise

    async def close(self):
        """关闭客户端连接"""
        await self.client.aclose()

# 同步版本的简化封装(如果不需要异步)
class SyncOllamaClient:
    def __init__(self, base_url: str = "http://localhost:11434"):
        self.base_url = base_url

    def generate_sync(self, model: str, prompt: str) -> str:
        import requests
        url = f"{self.base_url}/api/generate"
        resp = requests.post(url, json={"model": model, "prompt": prompt, "stream": False})
        resp.raise_for_status()
        return resp.json().get("response", "").strip()

封装要点解析

  1. 使用Pydantic模型定义请求体 :这提供了类型提示和自动验证,确保我们发送的数据结构是正确的。 OllamaGenerateRequest 类清晰地定义了必填和可选字段。
  2. 统一错误处理 :通过 raise_for_status() try-except 块,将HTTP错误、网络错误和响应格式错误统一捕获并记录日志,便于测试失败时排查。
  3. 支持异步 :主客户端使用了 httpx.AsyncClient ,为后续性能测试或批量测试留出空间。同时提供了一个同步版本备用。
  4. 提取核心响应 :方法最终返回清洗过的 response 文本,方便测试断言直接使用。

3.2 灵活断言器的设计与实现

这是框架的“大脑”。我们需要设计多种断言方式,来应对不同的验证场景。

# core/assertions.py
import re
import json
from typing import List, Union, Pattern
from deepdiff import DeepDiff

class OllamaResponseAssertor:
    """Ollama API响应断言器"""

    @staticmethod
    def assert_contains(response_text: str, keywords: Union[str, List[str]]):
        """断言响应中包含特定关键词或短语"""
        if isinstance(keywords, str):
            keywords = [keywords]
        for kw in keywords:
            assert kw in response_text, f"响应中未找到关键词:'{kw}'。实际响应:{response_text[:200]}..."

    @staticmethod
    def assert_not_contains(response_text: str, forbidden_words: Union[str, List[str]]):
        """断言响应中不包含禁忌词"""
        if isinstance(forbidden_words, str):
            forbidden_words = [forbidden_words]
        for word in forbidden_words:
            assert word not in response_text, f"响应中包含禁忌词:'{word}'。"

    @staticmethod
    def assert_matches_pattern(response_text: str, pattern: Union[str, Pattern]):
        """使用正则表达式断言响应格式"""
        if isinstance(pattern, str):
            pattern = re.compile(pattern)
        assert pattern.search(response_text) is not None, f"响应不匹配模式:{pattern.pattern}"

    @staticmethod
    def assert_valid_json(response_text: str):
        """断言响应是可解析的JSON字符串"""
        try:
            json.loads(response_text)
        except json.JSONDecodeError as e:
            raise AssertionError(f"响应不是有效的JSON:{e}")

    @staticmethod
    def assert_json_schema(response_text: str, expected_schema: dict, ignore_order: bool = True):
        """断言JSON响应与预期结构匹配(使用deepdiff进行深度比较)"""
        try:
            actual_json = json.loads(response_text)
        except json.JSONDecodeError:
            raise AssertionError("响应不是有效的JSON,无法进行结构比较")
        diff = DeepDiff(actual_json, expected_schema, ignore_order=ignore_order)
        assert not diff, f"JSON结构不匹配,差异:{diff}"

    @staticmethod
    def assert_semantic_similarity(response_text: str, expected_text: str, threshold: float = 0.8):
        """断言响应与预期文本的语义相似度高于阈值(需要额外依赖)"""
        # 这里以sentence-transformers为例
        try:
            from sentence_transformers import SentenceTransformer, util
            # 建议将模型加载移到类外部或使用缓存,避免每次断言都加载
            model = SentenceTransformer('all-MiniLM-L6-v2')
            emb1 = model.encode(response_text, convert_to_tensor=True)
            emb2 = model.encode(expected_text, convert_to_tensor=True)
            cosine_score = util.cos_sim(emb1, emb2).item()
            assert cosine_score >= threshold, f"语义相似度{cosine_score:.3f}低于阈值{threshold}"
        except ImportError:
            raise RuntimeError("语义相似度断言需要安装 `sentence-transformers` 库。")

断言器设计心得

  1. 静态方法 :设计成静态方法,无需实例化即可使用,非常方便。例如 OllamaResponseAssertor.assert_contains(response, “答案”)
  2. 渐进式复杂度 :从简单的字符串包含( assert_contains )到复杂的语义相似度( assert_semantic_similarity ),提供不同粒度的验证工具。测试用例可以根据需要组合使用。
  3. 语义相似度断言慎用 :这是一个“重武器”。虽然强大,但计算开销大,且相似度阈值 threshold 需要根据具体任务反复调试确定(例如,摘要任务可能要求0.85,而创意写作0.7可能就够了)。建议只在关键用例中使用,并考虑缓存模型。
  4. 清晰的错误信息 :每个断言失败时,都输出包含上下文的具体错误信息,而不是简单的 AssertionError ,这能极大加速调试过程。

4. 实操过程:构建并运行你的第一个测试套件

4.1 项目结构与配置

让我们从一个完整的项目示例开始。假设项目目录结构如下:

ollama_api_test_framework/
├── .env                    # 环境变量配置
├── conftest.py            # pytest全局配置和fixture
├── requirements.txt       # 项目依赖
├── config/
│   └── settings.py       # 配置管理
├── core/
│   ├── __init__.py
│   ├── ollama_client.py  # 封装的客户端
│   └── assertions.py     # 断言器
├── test_data/
│   └── test_cases.yaml   # 数据驱动的测试用例
└── tests/
    ├── __init__.py
    ├── test_connectivity.py # 基础连通性测试
    └── test_functional.py   # 功能测试

首先,配置环境变量和设置。

# config/settings.py
from pydantic_settings import BaseSettings

class TestSettings(BaseSettings):
    ollama_base_url: str = "http://localhost:11434"
    default_model: str = "llama3.2:latest" # 根据你本地部署的模型调整
    request_timeout: int = 60
    semantic_similarity_threshold: float = 0.75

    class Config:
        env_file = ".env"

settings = TestSettings()
# .env 文件(可根据环境覆盖默认值)
OLLAMA_BASE_URL=http://localhost:11434
DEFAULT_MODEL=qwen2.5:7b
REQUEST_TIMEOUT=120

4.2 编写pytest Fixture管理资源

conftest.py pytest 的魔力所在,这里定义的fixture可以被所有测试文件使用。

# conftest.py
import pytest
import asyncio
from core.ollama_client import OllamaClient
from config.settings import settings

@pytest.fixture(scope="session")
def event_loop():
    """为异步测试创建事件循环"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(scope="session")
async def ollama_client():
    """创建并返回一个Ollama客户端实例,测试结束后自动关闭"""
    client = OllamaClient(base_url=settings.ollama_base_url, timeout=settings.request_timeout)
    yield client
    await client.close()

@pytest.fixture(scope="session")
def default_model():
    """返回默认测试模型"""
    return settings.default_model

Fixture使用技巧

  • scope="session" 表示这个fixture在整个测试会话中只创建一次,并被所有测试用例共享。对于HTTP客户端这种重量级资源,这能避免重复创建连接的开销。
  • 异步fixture需要配合 pytest-asyncio 插件使用(需 pip install pytest-asyncio )。 event_loop fixture是 pytest-asyncio 的常见写法,确保每个测试用例有独立的事件循环。

4.3 编写基础连通性测试

这是我们的第一个测试,确保测试环境是就绪的。

# tests/test_connectivity.py
import pytest

class TestOllamaConnectivity:
    """Ollama服务基础连通性测试"""

    @pytest.mark.asyncio
    async def test_service_is_up(self, ollama_client):
        """测试Ollama服务是否可访问"""
        # 这里可以调用一个简单的API,如 /api/tags 来验证
        # 但为了简单,我们尝试生成一个极短的文本来测试
        from core.ollama_client import OllamaGenerateRequest
        request = OllamaGenerateRequest(model="dummy-model-check", prompt="Hello", stream=False)
        # 我们预期这个请求会因为模型不存在而返回一个特定的错误,但这证明了服务是活的
        # 更稳健的做法是调用 /api/tags
        try:
            # 使用httpx直接调用tags接口更合适
            async with ollama_client.client as client:
                resp = await client.get(f"{ollama_client.base_url}/api/tags")
                assert resp.status_code == 200
                # 确保响应中有模型列表
                data = resp.json()
                assert "models" in data
                print(f"可用模型: {[m['name'] for m in data['models']]}")
        except Exception as e:
            pytest.fail(f"无法连接到Ollama服务: {e}")

    @pytest.mark.asyncio
    async def test_default_model_available(self, ollama_client, default_model):
        """测试默认配置的模型是否在本地可用"""
        async with ollama_client.client as client:
            resp = await client.get(f"{ollama_client.base_url}/api/tags")
            models = [m["name"] for m in resp.json().get("models", [])]
            assert default_model in models, f"默认模型 '{default_model}' 未在本地找到。可用模型: {models}"

4.4 编写数据驱动的功能测试

这是框架的核心应用。我们将测试用例数据放在YAML文件中。

# test_data/test_cases.yaml
test_cases:
  - name: "测试模型能正确回答简单事实问题"
    model: "llama3.2:latest" # 可覆盖默认模型
    prompt: "中国的首都是哪里?"
    assertions:
      - type: "contains"
        value: ["北京"]
      - type: "not_contains"
        value: ["东京", "华盛顿"]

  - name: "测试模型能按要求生成JSON格式"
    prompt: |
      请生成一个包含书名、作者和出版年份的JSON对象,内容关于一本经典的科幻小说。
    assertions:
      - type: "valid_json"
      - type: "json_schema"
        value:
          type: "object"
          required: ["书名", "作者", "出版年份"]
          properties:
            书名:
              type: "string"
            作者:
              type: "string"
            出版年份:
              type: "integer"

  - name: "测试模型总结能力(语义相似度)"
    prompt: |
      请用一句话总结以下段落:
      “自动化测试是软件开发中的重要环节,它能通过自动执行测试用例,替代部分手动测试工作,提高测试效率,保障软件质量,并支持持续集成和持续交付。”
    expected_summary: "自动化测试通过自动执行用例提升软件测试效率和质量。"
    assertions:
      - type: "semantic_similarity"
        threshold: 0.8 # 可覆盖全局阈值

然后,编写一个测试文件来读取这些数据并执行测试。

# tests/test_functional.py
import pytest
import yaml
import asyncio
from core.ollama_client import OllamaGenerateRequest
from core.assertions import OllamaResponseAssertor
from config.settings import settings

def load_test_cases():
    with open("test_data/test_cases.yaml", 'r', encoding='utf-8') as f:
        data = yaml.safe_load(f)
        return data.get('test_cases', [])

@pytest.mark.parametrize("test_case", load_test_cases())
@pytest.mark.asyncio
async def test_ollama_with_data(test_case, ollama_client):
    """数据驱动的Ollama功能测试"""
    tc_name = test_case['name']
    print(f"\n执行测试用例: {tc_name}")

    # 准备请求
    model = test_case.get('model', settings.default_model)
    prompt = test_case['prompt']
    request = OllamaGenerateRequest(model=model, prompt=prompt, stream=False)

    # 执行请求
    response_text = await ollama_client.generate(request)
    print(f"Prompt: {prompt[:50]}...")
    print(f"Response: {response_text[:200]}...")

    # 执行所有断言
    for assertion in test_case.get('assertions', []):
        a_type = assertion['type']
        a_value = assertion.get('value')

        if a_type == 'contains':
            OllamaResponseAssertor.assert_contains(response_text, a_value)
        elif a_type == 'not_contains':
            OllamaResponseAssertor.assert_not_contains(response_text, a_value)
        elif a_type == 'matches_pattern':
            OllamaResponseAssertor.assert_matches_pattern(response_text, a_value)
        elif a_type == 'valid_json':
            OllamaResponseAssertor.assert_valid_json(response_text)
        elif a_type == 'json_schema':
            OllamaResponseAssertor.assert_json_schema(response_text, a_value)
        elif a_type == 'semantic_similarity':
            expected = test_case.get('expected_summary')
            if not expected:
                pytest.skip(f"测试用例 '{tc_name}' 缺少 `expected_summary`,跳过语义相似度断言。")
            threshold = assertion.get('threshold', settings.semantic_similarity_threshold)
            OllamaResponseAssertor.assert_semantic_similarity(response_text, expected, threshold)
        else:
            raise ValueError(f"不支持的断言类型: {a_type}")

数据驱动测试的优势

  1. 关注点分离 :测试逻辑(Python代码)和测试数据(YAML)分离,结构清晰。
  2. 易于维护 :新增测试用例只需在YAML文件中添加一条记录,无需修改代码。
  3. 非技术人员可参与 :产品经理或业务专家可以直接编写或审查YAML中的Prompt和预期结果。
  4. 参数化测试 @pytest.mark.parametrize 让一个测试函数可以运行多条用例,并清晰展示每条用例的结果。

4.5 运行测试并生成报告

安装依赖后,就可以运行测试了。

# 安装依赖
pip install pytest httpx pydantic pydantic-settings pyyaml deepdiff sentence-transformers pytest-asyncio pytest-html

# 运行所有测试
pytest -v

# 运行特定测试文件
pytest tests/test_functional.py -v

# 运行测试并生成HTML报告
pytest --html=report.html --self-contained-html

# 遇到网络慢,模型加载慢?可以设置更长的超时时间
OLLAMA_REQUEST_TIMEOUT=300 pytest -v

5. 常见问题与排查技巧实录

在实际搭建和运行过程中,你肯定会遇到各种问题。下面是我踩过的一些坑和解决方案。

5.1 连接与超时问题

问题现象 可能原因 排查步骤与解决方案
httpx.ConnectError 连接被拒绝 1. Ollama服务未启动。
2. 端口号错误(默认是11434)。
3. 防火墙或网络策略阻止。
1. 终端运行 ollama serve 确保服务已启动。
2. 检查 OLLAMA_BASE_URL 配置,确认端口正确。
3. 使用 curl http://localhost:11434/api/tags 手动测试连通性。
httpx.ReadTimeout 读取超时 1. Prompt复杂或模型较大,生成时间过长。
2. 服务器资源(CPU/内存)不足。
3. 网络延迟高。
1. 增加超时设置 :在 OllamaClient 初始化或请求时设置更大的 timeout 值(如300秒)。
2. 优化Prompt :尝试简化Prompt,或使用更小的模型进行测试。
3. 监控资源 :运行测试时,观察系统资源使用情况。
httpx.HTTPStatusError: 404 Not Found API路径错误。 确认Ollama API路径。生成接口是 /api/generate ,列表模型是 /api/tags ,确保URL拼接正确。

实操心得 超时时间一定要根据模型大小和Prompt长度合理设置。 对于7B参数模型,简单问答可能只需几秒,但复杂推理或长文本生成可能需要几分钟。我通常会在配置中设置一个较大的默认值(如120秒),并为特定的长文本测试用例在代码中临时覆盖这个值。

5.2 模型与响应相关问题

问题现象 可能原因 排查步骤与解决方案
测试失败,提示模型不存在 1. 配置的模型名称错误。
2. 模型未拉取到本地。
1. 运行 ollama list 查看本地已有模型。
2. 使用 /api/tags 接口在测试初始化时验证模型是否存在。
3. 拉取模型: ollama pull <model-name>
断言失败,但肉眼观察输出“似乎”正确 1. 断言过于严格(如完全字符串匹配)。
2. 模型输出存在随机性。
3. 关键词有同义词或不同表述。
1. 改用更灵活的断言 :用 assert_contains 替代 == ,用 assert_semantic_similarity
2. 调整Prompt :在Prompt中明确要求“用‘北京’一词回答”,减少随机性。
3. 增加断言容错 :提供多个可能的关键词列表。
响应内容为空或非常短 1. Prompt不清晰,模型无法理解。
2. 模型“害怕”生成内容(在安全策略下)。
3. 生成参数(如 temperature )设置过低。
1. 优化Prompt工程 :让指令更清晰具体。例如,将“写点东西”改为“写一段关于春天的50字短文”。
2. 检查请求参数 :在 OllamaGenerateRequest options 中尝试调整 temperature (提高创造性)或 seed (固定随机性)。
3. 查看完整日志 :Ollama服务端日志可能有更多线索。

避坑技巧 对于关键的业务流测试,建议在Prompt中设置 seed 参数。 这能确保模型在相同输入下产生完全相同的输出,极大提高测试的确定性和可重复性。例如: "options": {"seed": 42} 。但要注意,这牺牲了输出的多样性,只适用于需要稳定输出的场景。

5.3 性能与稳定性测试进阶

基础功能测试通过后,你可能需要关注性能。

# tests/test_performance.py
import pytest
import asyncio
import time
from core.ollama_client import OllamaGenerateRequest

@pytest.mark.asyncio
async def test_single_request_latency(ollama_client, default_model):
    """测试单次请求的延迟是否在预期范围内"""
    prompt = "请用一句话介绍你自己。"
    request = OllamaGenerateRequest(model=default_model, prompt=prompt)

    start_time = time.perf_counter()
    response = await ollama_client.generate(request)
    end_time = time.perf_counter()

    latency = end_time - start_time
    print(f"单次请求延迟: {latency:.2f} 秒, 生成长度: {len(response)} 字符")

    # 断言延迟小于5秒(这个阈值需要你根据模型和硬件调整)
    assert latency < 5.0, f"请求延迟{latency:.2f}秒超过阈值"

@pytest.mark.asyncio
async def test_concurrent_requests(ollama_client, default_model):
    """测试并发处理能力(模拟轻度并发)"""
    async def make_one_request():
        req = OllamaGenerateRequest(model=default_model, prompt="说你好。")
        return await ollama_client.generate(req)

    num_requests = 5
    start_time = time.perf_counter()
    tasks = [make_one_request() for _ in range(num_requests)]
    responses = await asyncio.gather(*tasks)
    end_time = time.perf_counter()

    total_time = end_time - start_time
    print(f"并发 {num_requests} 个请求,总耗时: {total_time:.2f} 秒")
    # 简单检查是否所有请求都成功完成
    assert len(responses) == num_requests
    assert all(len(r) > 0 for r in responses)

性能测试注意 :对本地部署的Ollama进行压测要格外小心,很容易把电脑跑崩。建议从低并发数开始,逐步增加,并密切监控CPU和内存使用情况。性能基准(如延迟阈值)需要在你的特定硬件和模型上多次运行后确定。

5.4 框架扩展与集成建议

这个基础框架可以随着项目需求不断扩展:

  1. 测试报告增强 :集成 pytest-html 生成美观的HTML报告,或使用 pytest-allure 生成更专业的Allure报告,展示每个Prompt和对应的模型输出,便于回溯。
  2. CI/CD集成 :在 GitHub Actions GitLab CI 的配置文件中,添加一个测试步骤。确保在代码合并或发布前,自动运行Ollama API测试。
    # .github/workflows/test.yml 示例片段
    - name: Run Ollama API Tests
      run: |
        # 启动Ollama服务(假设已安装)
        ollama serve &
        sleep 10 # 等待服务启动
        # 运行测试
        pytest -v --html=report.html
    
  3. 多模型/多配置测试 :使用 @pytest.mark.parametrize 同时对多个模型(如 llama3.2 , qwen2.5 )或不同生成参数( temperature=0.1 vs 0.8 )进行测试,对比输出质量和性能。
  4. Mock测试 :对于单元测试,你可能不希望每次都调用真实的Ollama服务。可以使用 pytest-mock unittest.mock 来模拟 OllamaClient.generate 方法,返回预定义的响应,从而测试你的业务逻辑代码。

搭建这样一个框架的初期会花点时间,但一旦成型,它就会成为你AI应用开发流程中一个强大的安全网和效率工具。它能让你在模型迭代、Prompt调整时充满信心,因为你知道任何回归问题都会被自动化测试第一时间捕捉到。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐