ANNOTATION_PERSISTENCE_FIX.md 5.5 KB

标注结果持久化修复

问题描述

  1. 标注结果没有加载:用户保存标注后,再次打开标注界面时,之前的标注结果没有被加载
  2. 任务状态没有更新:保存标注后,任务列表中的状态没有正确显示为"已完成"

问题根源

1. 前端没有加载已有标注

annotation-view.tsx 中,编辑器初始化时:

  • 只创建了新的空白标注 (as.createAnnotation())
  • 没有检查是否已有标注结果
  • 没有将已有标注加载到编辑器中

2. 保存逻辑不正确

保存标注时:

  • 每次都创建新的标注记录,而不是更新已有的
  • 任务状态更新逻辑不正确(只是增加进度,而不是直接标记为完成)

修复方案

1. 加载已有标注 (annotation-view.tsx)

修改数据加载逻辑

// 在 loadData 函数中添加
const existingAnnotations = await getTaskAnnotations(id);
console.log('Existing annotations:', existingAnnotations);

// Store existing annotations for later use
if (existingAnnotations.length > 0) {
  // Use the most recent annotation
  const latestAnnotation = existingAnnotations[0];
  annotationResultRef.current = latestAnnotation.result;
  console.log('Loaded existing annotation:', latestAnnotation);
}

修改编辑器初始化逻辑

onStorageInitialized: (LS: any) => {
  const initAnnotation = () => {
    const as = LS.annotationStore;
    
    // Check if we have existing annotation data
    const existingResult = annotationResultRef.current;
    
    if (existingResult && existingResult.result && Array.isArray(existingResult.result)) {
      console.log('Loading existing annotation:', existingResult);
      
      // Create annotation with existing data
      const annotation = as.createAnnotation({
        userGenerate: false,
        result: existingResult.result,
      });
      as.selectAnnotation(annotation.id);
      
      // Set up snapshot listener
      // ...
    } else {
      console.log('Creating new annotation');
      
      // Create new empty annotation
      const annotation = as.createAnnotation();
      as.selectAnnotation(annotation.id);
      // ...
    }
  };
  setTimeout(initAnnotation, 100);
}

2. 修复保存逻辑

检查并更新已有标注

// Check if annotation already exists for this task
const existingAnnotations = await getTaskAnnotations(id);

if (existingAnnotations.length > 0) {
  // Update existing annotation
  const existingAnnotation = existingAnnotations[0];
  await updateAnnotation(existingAnnotation.id, {
    result: resultData,
  });
  console.log('Updated existing annotation:', existingAnnotation.id);
} else {
  // Create new annotation
  await createAnnotation({
    task_id: id,
    user_id: 'current_user',
    result: resultData,
  });
  console.log('Created new annotation');
}

直接标记任务为已完成

// Update task status to completed
await updateTask(id, {
  status: 'completed',
});

console.log('Task marked as completed');

3. 添加必要的 API 导入

import { 
  getTask, 
  getProject, 
  createAnnotation, 
  updateTask, 
  getTaskAnnotations,  // 新增
  updateAnnotation      // 新增
} from '../../services/api';

修复效果

修复前

  • ❌ 再次打开标注界面时,之前的标注结果丢失
  • ❌ 每次保存都创建新的标注记录(数据库中有重复)
  • ❌ 任务状态不会变为"已完成"

修复后

  • ✅ 再次打开标注界面时,自动加载之前的标注结果
  • ✅ 保存时更新已有标注,而不是创建新记录
  • ✅ 保存后任务状态正确显示为"已完成"
  • ✅ 任务列表中的进度和状态正确更新

测试步骤

  1. 创建新任务并标注

    • 打开一个待处理的任务
    • 完成标注
    • 点击"保存"按钮
    • 验证:返回任务列表后,任务状态显示为"已完成"
  2. 重新打开已标注的任务

    • 在任务列表中点击已完成的任务
    • 验证:编辑器中显示之前保存的标注结果
    • 修改标注
    • 再次保存
    • 验证:修改被正确保存(没有创建重复记录)
  3. 检查数据库

    • 查看 annotations
    • 验证:每个任务只有一条标注记录
    • 验证:updated_at 字段在修改后正确更新

相关文件

  • web/apps/lq_label/src/views/annotation-view/annotation-view.tsx - 标注视图组件
  • web/apps/lq_label/src/services/api.ts - API 服务层
  • backend/routers/annotation.py - 标注 API 路由
  • backend/routers/task.py - 任务 API 路由

注意事项

  1. LabelStudio 数据格式

    • createAnnotation()userGenerate: false 参数表示使用预加载的数据
    • result 字段必须是数组格式
  2. 并发问题

    • 当前实现假设一个任务只有一个标注者
    • 如果需要支持多人标注,需要修改逻辑(根据 user_id 过滤)
  3. 性能优化

    • 当前每次保存都会查询已有标注
    • 可以考虑在组件状态中缓存标注 ID,避免重复查询

后续优化建议

  1. 添加用户认证

    • 当前使用硬编码的 'current_user'
    • 应该从认证上下文中获取真实的用户 ID
  2. 支持多人标注

    • 根据用户 ID 加载和保存标注
    • 显示其他用户的标注(只读模式)
  3. 添加自动保存

    • 定期自动保存标注结果
    • 避免用户忘记保存导致数据丢失
  4. 添加版本历史

    • 保存标注的历史版本
    • 支持回退到之前的版本