DeerFlow自动化测试:基于Python的CI/CD集成方案

如果你正在用DeerFlow做深度研究,每次手动运行测试、检查代码质量,是不是觉得有点麻烦?特别是当项目更新频繁,或者团队协作开发时,手动测试不仅效率低,还容易出错。

今天我就来分享一个实用的方案:把DeerFlow的自动化测试集成到Python项目的CI/CD流程里。简单说,就是让代码每次提交都能自动跑测试、生成报告,你只需要关注结果就行。这套方案基于GitHub Actions,配置简单,效果明显,能帮你节省大量时间。

1. 为什么需要CI/CD集成?

先说说我自己的经历。刚开始用DeerFlow时,每次改完代码都要手动跑一遍测试,看看有没有问题。时间一长就发现几个痛点:

手动测试的麻烦

  • 容易忘记跑测试,导致问题没及时发现
  • 测试结果分散,没有统一记录
  • 团队协作时,每个人的环境配置不同,测试结果可能不一致
  • 代码质量检查靠自觉,标准不统一

CI/CD能解决什么

  • 代码一提交就自动跑测试,不用手动操作
  • 测试结果集中管理,随时查看历史记录
  • 确保所有人在同一标准下开发
  • 自动生成测试报告,问题一目了然

DeerFlow本身就有完善的测试框架,支持单元测试、集成测试和覆盖率检查。我们要做的就是把这套测试流程自动化,让它成为开发流程的一部分。

2. 环境准备与基础配置

在开始配置CI/CD之前,先确保你的DeerFlow项目已经设置好本地测试环境。如果你还没做过,跟着下面几步快速过一遍。

2.1 检查现有测试框架

DeerFlow项目已经内置了测试框架,用make命令就能管理。打开项目根目录,看看有没有这些文件:

# 查看测试相关文件
ls -la tests/
ls -la Makefile

你应该能看到类似这样的结构:

tests/
├── integration/
│   └── test_workflow.py
├── unit/
│   └── test_*.py
└── conftest.py

Makefile  # 包含test、coverage等命令

2.2 本地测试验证

先确保本地测试能正常运行,这样CI/CD配置才有意义:

# 安装测试依赖(如果还没装)
uv pip install -e ".[test]"

# 运行所有测试
make test

# 运行特定集成测试
pytest tests/integration/test_workflow.py -v

# 检查测试覆盖率
make coverage

如果这些命令都能正常执行,说明测试环境没问题。记下测试用时和覆盖率结果,后面配置CI时会用到。

2.3 准备必要的配置文件

DeerFlow测试需要一些环境配置,主要是API密钥和模型设置。为了在CI环境中安全使用,我们需要处理敏感信息:

# 复制示例配置文件
cp .env.example .env.ci
cp conf.yaml.example conf.yaml.ci

# 编辑.ci文件,使用测试专用的占位符
# 在.env.ci中:
SEARCH_API=duckduckgo  # 使用无需API的搜索引擎
# 其他API密钥留空或使用测试专用密钥

# 在conf.yaml.ci中:
# 使用本地测试模型或模拟接口

重要提示:实际项目中,API密钥等敏感信息应该使用GitHub Secrets管理,不要直接写在配置文件里。我们后面会详细讲怎么安全配置。

3. GitHub Actions工作流配置

现在进入核心部分:配置GitHub Actions。我会分步骤讲解,每个配置都有详细说明。

3.1 基础工作流文件

在项目根目录创建.github/workflows/test.yml文件:

name: DeerFlow CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # 每周一早上6点运行一次完整测试
    - cron: '0 6 * * 1'

env:
  PYTHON_VERSION: '3.12'
  UV_VERSION: '0.4.0'

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
        
    - name: Install uv
      run: |
        curl -LsSf https://astral.sh/uv/install.sh | sh
        echo "$HOME/.cargo/bin" >> $GITHUB_PATH
        
    - name: Install dependencies
      run: |
        uv sync --all-extras
        uv pip install -e ".[test]"
        
    - name: Run tests
      run: make test
      
    - name: Generate coverage report
      run: make coverage
      
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

这个基础配置做了几件事:

  1. 在代码推送或PR时自动触发
  2. 设置Python 3.12环境(DeerFlow要求)
  3. 安装uv包管理器
  4. 安装项目依赖和测试依赖
  5. 运行测试并生成覆盖率报告
  6. 上传报告到Codecov(可选)

3.2 处理环境变量和敏感信息

测试DeerFlow需要API密钥,但在CI环境中不能明文存储。用GitHub Secrets安全管理:

# 在test job的steps中添加
- name: Setup environment variables
  env:
    TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    # 其他需要的API密钥
  run: |
    # 创建测试用的.env文件
    cat > .env << EOF
    SEARCH_API=tavily
    TAVILY_API_KEY=${TAVILY_API_KEY:-}
    LANGSMITH_TRACING=false
    EOF
    
    # 创建测试用的conf.yaml
    cat > conf.yaml << EOF
    BASIC_MODEL:
      base_url: "https://api.openai.com/v1"
      model: "gpt-3.5-turbo"
      api_key: ${OPENAI_API_KEY:-}
    EOF

然后在GitHub仓库设置中添加Secrets:

  1. 进入仓库 → Settings → Secrets and variables → Actions
  2. 点击"New repository secret"
  3. 添加TAVILY_API_KEY、OPENAI_API_KEY等
  4. 值填实际的API密钥(测试环境可以用限制权限的密钥)

3.3 矩阵测试配置

为了确保兼容性,可以测试多个Python版本:

jobs:
  test:
    strategy:
      matrix:
        python-version: ['3.12', '3.13']
        os: [ubuntu-latest, macos-latest]
    
    runs-on: ${{ matrix.os }}
    
    steps:
    # ... 前面的步骤相同
    
    - name: Run tests with matrix
      run: |
        # 根据OS调整命令
        if [ "$RUNNER_OS" == "Windows" ]; then
          # Windows特定命令
          python -m pytest tests/ -v
        else
          make test
        fi

3.4 集成测试专项配置

DeerFlow的集成测试可能需要更多资源,可以单独配置:

integration-test:
  needs: test
  runs-on: ubuntu-latest
  if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main'
  
  steps:
  - name: Checkout code
    uses: actions/checkout@v4
    
  - name: Set up environment
    run: |
      # 安装Docker(如果需要)
      sudo apt-get update
      sudo apt-get install -y docker.io
      
  - name: Run integration tests
    timeout-minutes: 30
    run: |
      # 启动测试需要的服务
      docker-compose -f docker-compose.test.yml up -d
      
      # 等待服务就绪
      sleep 30
      
      # 运行集成测试
      pytest tests/integration/ -v --tb=short
      
      # 清理
      docker-compose -f docker-compose.test.yml down

4. 测试用例编写与优化

CI/CD的核心是测试用例的质量。DeerFlow的测试主要分三类,每类都有不同的编写策略。

4.1 单元测试:验证核心组件

单元测试关注单个函数或类的行为。DeerFlow的agent节点、工具函数都适合做单元测试:

# tests/unit/test_coordinator.py
import pytest
from deerflow.src.graph.nodes import coordinator_node
from deerflow.src.graph.state import State

def test_coordinator_with_valid_input():
    """测试coordinator处理正常输入"""
    state = State(
        messages=[{"role": "user", "content": "什么是机器学习?"}],
        locale="zh"
    )
    
    result = coordinator_node(state)
    
    assert result is not None
    assert "goto" in result
    # coordinator应该将控制权交给planner
    assert result["goto"] == "planner"

def test_coordinator_with_sensitive_content():
    """测试coordinator处理敏感内容"""
    state = State(
        messages=[{"role": "user", "content": "敏感测试内容"}],
        locale="zh"
    )
    
    result = coordinator_node(state)
    
    # 应该礼貌拒绝并结束
    assert result["goto"] == "__end__"
    assert "抱歉" in str(result.get("update", {}))

def test_coordinator_edge_cases():
    """测试边界情况"""
    # 空消息
    state = State(messages=[], locale="zh")
    result = coordinator_node(state)
    assert result["goto"] == "__end__"
    
    # 超长消息
    long_content = "test" * 1000
    state = State(messages=[{"role": "user", "content": long_content}], locale="zh")
    result = coordinator_node(state)
    # 应该能正常处理或截断
    assert result is not None

4.2 集成测试:验证工作流

集成测试关注多个组件如何协作。DeerFlow的多agent工作流特别适合集成测试:

# tests/integration/test_workflow.py
import pytest
from deerflow.src.graph.graph import create_workflow_graph
from langgraph.checkpoint.memory import MemorySaver

class TestDeerFlowWorkflow:
    @pytest.fixture
    def workflow(self):
        """创建测试用的工作流图"""
        memory = MemorySaver()
        graph = create_workflow_graph(checkpointer=memory)
        return graph
    
    def test_basic_research_flow(self, workflow):
        """测试基础研究流程"""
        config = {"configurable": {"thread_id": "test_thread_1"}}
        
        # 初始化状态
        inputs = {
            "messages": [{"role": "user", "content": "Python是什么?"}],
            "locale": "zh"
        }
        
        # 执行工作流
        for event in workflow.stream(inputs, config):
            # 检查每个步骤的状态
            if "coordinator" in event:
                assert event["coordinator"] is not None
            elif "planner" in event:
                # planner应该生成研究计划
                plan = event["planner"].get("current_plan")
                assert plan is not None
                assert hasattr(plan, "steps")
        
        # 最终应该生成报告
        final_state = workflow.get_state(config)
        assert "final_report" in final_state.values
        
    def test_workflow_with_human_feedback(self, workflow):
        """测试带人工反馈的流程"""
        config = {"configurable": {"thread_id": "test_thread_2"}}
        
        inputs = {
            "messages": [{"role": "user", "content": "解释深度学习"}],
            "locale": "zh",
            "auto_accepted_plan": False  # 启用人工反馈
        }
        
        # 模拟人工反馈
        events = []
        for event in workflow.stream(inputs, config):
            events.append(event)
            
            # 当进入human_feedback节点时,模拟用户输入
            if "human_feedback" in event:
                feedback_event = workflow.invoke(
                    {"feedback": "[ACCEPTED]"},
                    config
                )
                events.append(feedback_event)
        
        # 验证流程完成
        assert len(events) > 0
        assert any("reporter" in str(e) for e in events)

4.3 模拟外部依赖

测试时不应该调用真实API,既慢又不稳定。用pytest-mock模拟外部服务:

# tests/unit/test_researcher.py
import pytest
from unittest.mock import Mock, AsyncMock
from deerflow.src.graph.nodes import researcher_node
from deerflow.src.tools.search import TavilySearchTool

@pytest.mark.asyncio
async def test_researcher_with_mocked_search(mocker):
    """测试researcher使用模拟的搜索工具"""
    # 模拟Tavily搜索返回
    mock_search = mocker.patch.object(TavilySearchTool, '_arun')
    mock_search.return_value = [
        {
            "title": "测试结果",
            "content": "这是模拟的搜索结果",
            "url": "https://example.com"
        }
    ]
    
    # 创建测试状态
    state = {
        "current_plan": Mock(
            steps=[Mock(
                step_type="RESEARCH",
                description="搜索Python相关信息",
                execution_res=None
            )]
        ),
        "observations": []
    }
    
    result = await researcher_node(state)
    
    # 验证搜索被调用
    mock_search.assert_called_once()
    
    # 验证结果处理
    assert "observations" in result.get("update", {})
    observations = result["update"]["observations"]
    assert len(observations) > 0
    assert "测试结果" in str(observations)

def test_researcher_error_handling(mocker):
    """测试researcher的错误处理"""
    # 模拟搜索失败
    mocker.patch.object(TavilySearchTool, '_arun', 
                       side_effect=Exception("API错误"))
    
    state = {
        "current_plan": Mock(
            steps=[Mock(step_type="RESEARCH", description="测试搜索")]
        ),
        "observations": []
    }
    
    # 应该能处理异常而不崩溃
    try:
        result = researcher_node(state)
        # 如果有结果,应该包含错误信息
        if result:
            assert "error" in str(result).lower()
    except Exception:
        pytest.fail("researcher应该处理API错误,而不是抛出异常")

5. 测试报告与质量门禁

测试不仅要运行,还要有清晰的报告和质量标准。这样才能真正提升代码质量。

5.1 生成详细的测试报告

在GitHub Actions中配置测试报告生成:

- name: Run tests with detailed reporting
  run: |
    # 生成JUnit格式报告(供GitHub显示)
    pytest tests/ \
      --junitxml=test-results.xml \
      --cov=deerflow \
      --cov-report=xml:coverage.xml \
      --cov-report=html:coverage_html \
      -v
    
    # 生成测试时长报告
    pytest tests/ --durations=10 > test-durations.txt

- name: Upload test results
  uses: actions/upload-artifact@v4
  with:
    name: test-reports
    path: |
      test-results.xml
      coverage.xml
      coverage_html/
      test-durations.txt
    retention-days: 30

- name: Publish Test Report
  uses: dorny/test-reporter@v1
  if: always()  # 即使测试失败也生成报告
  with:
    name: Pytest Results
    path: test-results.xml
    reporter: java-junit

5.2 设置质量门禁

定义测试通过的标准,不达标就阻止合并:

quality-gate:
  needs: test
  runs-on: ubuntu-latest
  if: always()
  
  steps:
  - name: Download test results
    uses: actions/download-artifact@v4
    with:
      name: test-reports
  
  - name: Check test results
    run: |
      # 检查测试是否全部通过
      if grep -q 'failures="[1-9]' test-results.xml; then
        echo " 有测试失败,请检查"
        exit 1
      fi
      
      # 检查覆盖率是否达标
      MIN_COVERAGE=80
      COVERAGE=$(grep -o 'line-rate="[0-9.]*"' coverage.xml | grep -o '[0-9.]*' | head -1)
      COVERAGE_PERCENT=$(echo "$COVERAGE * 100" | bc)
      
      if (( $(echo "$COVERAGE_PERCENT < $MIN_COVERAGE" | bc -l) )); then
        echo " 代码覆盖率 ${COVERAGE_PERCENT}% 低于要求 ${MIN_COVERAGE}%"
        exit 1
      else
        echo " 代码覆盖率 ${COVERAGE_PERCENT}% 达标"
      fi
      
      # 检查测试时长(防止测试过慢)
      MAX_DURATION=300  # 5分钟
      TOTAL_TIME=$(grep -o 'time="[0-9.]*"' test-results.xml | grep -o '[0-9.]*' | awk '{sum+=$1} END {print sum}')
      
      if (( $(echo "$TOTAL_TIME > $MAX_DURATION" | bc -l) )); then
        echo "  测试总时长 ${TOTAL_TIME}s 超过 ${MAX_DURATION}s,考虑优化"
        # 这里不失败,只是警告
      fi

5.3 代码质量检查

除了测试,还要检查代码风格和类型:

lint-and-type-check:
  runs-on: ubuntu-latest
  
  steps:
  - name: Checkout code
    uses: actions/checkout@v4
  
  - name: Set up Python
    uses: actions/setup-python@v5
    with:
      python-version: '3.12'
  
  - name: Install dependencies
    run: |
      uv sync
      uv pip install black ruff mypy types-requests
  
  - name: Check code formatting with Black
    run: |
      black --check --diff deerflow/ tests/
    
  - name: Lint with Ruff
    run: |
      ruff check deerflow/ tests/ --fix
    
  - name: Type checking with MyPy
    run: |
      mypy deerflow/ --ignore-missing-imports
    
  - name: Security check with Bandit
    run: |
      uv pip install bandit
      bandit -r deerflow/ -ll

6. 高级配置与优化技巧

基础配置完成后,可以进一步优化CI/CD流程,提升效率和稳定性。

6.1 缓存依赖加速构建

GitHub Actions可以缓存Python依赖,大幅减少构建时间:

- name: Cache uv dependencies
  uses: actions/cache@v4
  with:
    path: |
      ~/.cache/uv
      .venv
    key: ${{ runner.os }}-uv-${{ hashFiles('pyproject.toml', 'uv.lock') }}
    restore-keys: |
      ${{ runner.os }}-uv-

- name: Cache pip dependencies
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements*.txt') }}

6.2 并行测试执行

对于大型测试套件,可以并行运行测试加快速度:

- name: Run tests in parallel
  run: |
    # 使用pytest-xdist并行运行
    pytest tests/ -n auto --dist=loadscope
    
    # 或者按目录拆分
    pytest tests/unit/ -n 2 &
    pytest tests/integration/ -n 2 &
    wait

6.3 数据库和外部服务测试

DeerFlow可能用到数据库(如PostgreSQL checkpoint),需要测试环境:

services:
  postgres:
    image: postgres:15
    env:
      POSTGRES_PASSWORD: testpassword
      POSTGRES_DB: testdb
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5
    ports:
      - 5432:5432

- name: Test with PostgreSQL
  env:
    DATABASE_URL: postgresql://postgres:testpassword@localhost:5432/testdb
  run: |
    # 设置环境变量
    export LANGGRAPH_CHECKPOINT_SAVER=true
    export LANGGRAPH_CHECKPOINT_DB_URL=$DATABASE_URL
    
    # 运行数据库相关测试
    pytest tests/integration/test_checkpoint.py -v

6.4 性能测试集成

对于DeerFlow这样的AI应用,性能也很重要:

performance-test:
  runs-on: ubuntu-latest
  needs: test
  
  steps:
  - name: Run performance tests
    timeout-minutes: 10
    run: |
      # 安装性能测试工具
      uv pip install pytest-benchmark
      
      # 运行基准测试
      pytest tests/performance/ -v --benchmark-only
      
      # 检查响应时间
      python -c "
      import time
      from deerflow.src.graph.graph import create_workflow_graph
      
      graph = create_workflow_graph()
      start = time.time()
      
      # 测试典型查询
      inputs = {'messages': [{'role': 'user', 'content': '简单测试'}]}
      for _ in graph.stream(inputs, {'configurable': {'thread_id': 'perf_test'}}):
          pass
      
      duration = time.time() - start
      print(f'工作流执行时间: {duration:.2f}秒')
      assert duration < 30, '响应时间过长'
      "

7. 实际应用与问题排查

配置完成后,在实际使用中可能会遇到一些问题。这里分享一些常见问题的解决方法。

7.1 常见问题与解决

问题1:测试在CI中通过,但本地失败

# 解决方案:确保环境一致
- name: Use consistent Python version
  uses: actions/setup-python@v5
  with:
    python-version: '3.12.8'  # 指定精确版本

问题2:API密钥在CI中失效

# 解决方案:使用测试专用密钥和模拟
- name: Use mock for external APIs in CI
  env:
    USE_MOCK_APIS: 'true'
  run: |
    if [ "$USE_MOCK_APIS" = "true" ]; then
      # 使用模拟服务
      export SEARCH_API=duckduckgo  # 无需密钥
      # 或者启动本地模拟服务器
      python -m tests.mock_services &
    fi

问题3:测试超时

# 解决方案:设置超时和重试
- name: Run tests with timeout and retry
  timeout-minutes: 15
  continue-on-error: true  # 超时不立即失败
  
- name: Retry flaky tests
  if: failure()
  run: |
    # 只重试失败的测试
    pytest tests/ --lf -v

7.2 监控与告警

配置测试失败时的通知:

- name: Notify on failure
  if: failure()
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: `CI/CD Test Failed on ${context.ref}`,
        body: `测试运行失败,请检查:\n\n${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
        labels: ['ci-failure']
      })

7.3 定期维护任务

设置定期任务维护测试健康度:

name: Weekly Test Maintenance

on:
  schedule:
    - cron: '0 2 * * 0'  # 每周日早上2点

jobs:
  update-dependencies:
    runs-on: ubuntu-latest
    
    steps:
    - name: Update dependencies
      run: |
        uv lock --upgrade
        # 运行测试确保兼容性
        make test
        
    - name: Create PR if updates available
      uses: peter-evans/create-pull-request@v5
      with:
        commit-message: 'chore: update dependencies'
        title: 'Weekly dependency updates'
        body: '自动更新的依赖包'
        branch: 'deps-update'

8. 总结

把DeerFlow集成到CI/CD流程里,看起来要配置不少东西,但实际用起来真的很省心。我自己的项目配置完后,最大的感受就是放心——代码质量有保障,问题能早发现,团队协作也更顺畅。

这套方案的核心思路其实很简单:自动化能手动做的事,标准化能自动化的流程。从基础测试到质量门禁,每一步都是为了减少人为错误,提升开发效率。

如果你刚开始接触CI/CD,建议先从基础测试开始,跑通整个流程后再慢慢添加高级功能。遇到问题也不用担心,GitHub Actions的日志很详细,大部分问题都能找到解决方法。

实际用下来,这套配置在中等规模的项目里效果不错,测试运行稳定,反馈及时。当然,每个项目情况不同,你可能需要根据实际需求调整一些参数,比如测试覆盖率要求、超时时间等。关键是要找到适合自己团队的平衡点,既保证质量,又不拖慢开发节奏。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

更多推荐