Files
prompt-optimizer/experience.md
T
linshen eb2172eb50 feat: 重构LLM服务,引入LangChain并优化错误处理
本次提交对LLM服务进行了全面重构,主要变更包括:

- 引入LangChain库,支持多模型提供商(OpenAI、Gemini)
- 重构错误处理机制,增加更精细的错误类型
- 优化模型实例缓存和配置验证
- 更新服务层接口,提高代码可读性和可维护性
- 调整模型配置类型,增加provider字段
- 更新单元测试和集成测试,适配新的服务架构
- 添加LangChain相关依赖

重构目标:
- 提高代码的可扩展性
- 统一模型调用接口
- 改进错误处理和类型安全
- 支持更多模型提供商
2025-01-30 23:14:24 +08:00

12 KiB
Raw Blame History

项目经验总结(优化版)

📋 快速索引


架构设计

API集成规范

  1. 配置分离

    • 业务逻辑与API配置解耦
    • 统一接口格式(OpenAI兼容)
    • 独立管理提示词模板
    // 配置示例
    export default {
      baseURL: "https://api.openai.com/v1",
      models: ["gpt-4", "gpt-3.5"]
    }
    
  2. 模块化结构

    ├─ api/       # API封装
    ├─ services/  # 业务逻辑
    ├─ config/    # 全局配置
    ├─ components/# UI组件
    └─ prompts/   # 提示模板库
    

LLM服务架构

设计要点 实现方案
接口标准化 统一使用OpenAI格式
多服务商兼容 Provider标识区分(如Gemini)
动态配置更新 支持热加载配置
敏感信息管理 环境变量+本地加密存储

错误处理

典型问题与修复

问题场景 解决方案 发生日期
模板ID与模型Key混淆 明确功能ID与API Key分离 2024-03-22
状态同步异常 增加状态同步处理函数 2024-03-22
全局currentProvider污染 显式传递模型参数 2024-03-22

处理原则

  1. 开发环境:保留完整错误堆栈
  2. 生产环境:友好提示+日志记录
  3. 通用规则:
    try {
      await apiCall();
    } catch (err) {
      console.error("[API Error]", err.context);
      throw new Error("服务调用失败,请检查配置");
    }
    

测试规范

核心要点

  1. 环境变量

    // Vite项目必须使用import.meta.env
    const apiKey = import.meta.env.VITE_GEMINI_KEY; 
    
  2. 测试架构

    • 集中管理多服务商配置
    • 独立测试数据库
    • 模拟异常场景(网络错误/无效Token)
  3. 用例设计

    describe("API测试", () => {
      it("应正确处理多轮对话", async () => {
        const response = await sendRequest(messages);
        expect(response.content).toMatch(/优化建议/);
      });
    });
    

Vue开发

组件规范

  1. Composable使用

    // 正确用法
    const { data } = useFetch();
    // 错误用法
    onMounted(() => {
      const { data } = useFetch(); // 禁止在生命周期内调用
    });
    
  2. 状态同步方案

    <!-- 父组件 -->
    <ModelManager @update="handleModelUpdate" />
    
    <!-- 子组件 -->
    <script setup>
    const emit = defineEmits(['update']);
    const updateModel = () => {
      emit('update', newModels);
    }
    </script>
    

UI设计准则

分类 规范
布局系统 基于Tailwind的响应式网格
主题管理 CSS变量+透明度层级控制
交互反馈 加载状态/禁用状态/过渡动画

工具与配置

NPM管理

# 常用命令
npm outdated          # 检查可更新包
ncu -u "eslint*"      # 安全更新指定包

版本策略

符号 含义 示例
^ 允许小版本更新 ^1.2.3 → 1.3.0
~ 仅允许补丁更新 ~1.2.3 → 1.2.4

YAML处理

  1. 常见错误

    # 错误示例
    -invalid: 缺少空格
    tags: 
      -test  # 列表格式错误
    
  2. 修复方案

    # 正确格式
    valid_key: "值"
    tags:
      - test
      - template
    

关键重构经验

服务层优化(2024-03-19)

  1. 类型安全

    interface ModelConfig {
      name: string;
      baseURL: string;  // 非可选字段
      models: string[];
    }
    
  2. 错误处理改进

    • 统一继承自 LLMError 基类
    • 批量验证错误收集
    class ValidationError extends LLMError {
      constructor(errors: string[]) {
        super(`配置验证失败: ${errors.join(', ')}`);
      }
    }
    

配置管理(2024-03-21)

  1. 模型启用验证

    function validateConfig(config) {
      if (!config.apiKey) {
        throw new Error("API Key为必填项");
      }
    }
    
  2. 环境变量处理

    /// <reference types="vite/client" />
    interface ImportMetaEnv {
      VITE_GEMINI_KEY: string;
    }
    

LLM 服务重构经验

LangChain 集成

  • 日期:[当前日期]
  • 场景:将原生 API 调用重构为 LangChain 实现
  • 主要改进:
    1. 更好的可扩展性
    2. 统一的接口抽象
    3. 内置的错误处理
    4. 支持高级特性

最佳实践

  1. 使用模型实例缓存提高性能
  2. 统一消息格式转换
  3. 保持公共接口稳定
  4. 分层错误处理

注意事项

  1. 需要安装相关依赖:
    npm install langchain @langchain/core @langchain/openai @langchain/anthropic
    
  2. 不同模型提供商的配置参数可能不同
  3. 建议使用 TypeScript 获得更好的类型支持

未来扩展

  1. 支持更多模型提供商
  2. 添加高级功能:
    • 链式调用
    • 代理功能
    • 向量存储
    • 记忆管理
  3. 性能优化:
    • 并发请求
    • 流式响应
    • 缓存策略

API密钥管理经验

设计决策

  1. 采用用户自管理API密钥的方式
  2. 启用OpenAI库的浏览器支持
  3. 提供完整的密钥管理指导

原因

  1. 应用定位为个人工具
  2. 用户可以完全控制自己的API使用
  3. 避免后端服务的开销和维护
  4. 保持应用的简单性和可移植性

安全考虑

  1. 密钥由用户自行创建和管理
  2. 建议用户为应用单独创建密钥
  3. 推荐在OpenAI平台设置使用限制
  4. 提供密钥安全使用的最佳实践指导

用户体验优化

  1. 提供详细的API密钥获取教程
  2. 添加密钥验证功能
  3. 显示当前密钥的使用状态
  4. 提供密钥使用的安全提示

技术实现

const model = new OpenAI({
  apiKey: userProvidedKey,
  baseURL: modelConfig.baseURL,
  dangerouslyAllowBrowser: true // 必要的配置
});

最佳实践建议

  1. 在本地存储中加密保存密钥
  2. 提供清除密钥的功能
  3. 定期提醒用户检查密钥使用情况
  4. 提供密钥轮换的指导

测试修复经验总结

1. 常见测试失败原因

  • 方法名不匹配:实现变更后未同步更新测试用例
  • 错误消息不一致:测试期望与实际实现的错误消息格式不匹配
  • 模板验证逻辑不一致:测试用例未反映最新的业务逻辑

2. 修复策略

  • 统一错误消息格式:确保测试用例与实际代码的错误消息完全匹配
  • 移除多余的模拟:删除不必要的模拟调用
  • 添加缺失的模拟:确保所有必要的依赖都被正确模拟

3. 最佳实践

  • 错误消息管理:统一定义错误消息,避免分散管理
  • 测试用例维护:及时更新测试用例以反映最新的代码行为
  • Mock 策略:只模拟必要的方法,避免过度模拟
  • 测试覆盖:关注边界条件和异常场景的测试

4. 改进建议

  • 使用常量定义错误消息
  • 完善错误处理的类型定义
  • 添加更多边界条件测试
  • 创建测试辅助函数简化重复设置

5. 测试代码示例

// 错误消息常量
const ERROR_MESSAGES = {
  TEMPLATE_NOT_FOUND: '模板不存在或无效',
  MANAGER_NOT_INITIALIZED: '模板管理器未初始化',
  MODEL_NOT_FOUND: '模型不存在'
} as const;

// 测试辅助函数
function setupMocks(options: {
  templateContent?: string,
  modelExists?: boolean,
  messageResult?: string
}) {
  const { templateContent, modelExists = true, messageResult } = options;
  
  if (modelExists) {
    vi.spyOn(modelManager, 'getModel').mockReturnValue(mockModelConfig);
  } else {
    vi.spyOn(modelManager, 'getModel').mockReturnValue(undefined);
  }
  
  if (templateContent !== undefined) {
    vi.spyOn(templateManager, 'getTemplate').mockResolvedValue({
      ...mockTemplate,
      template: templateContent
    });
  }
  
  if (messageResult) {
    vi.spyOn(llmService, 'sendMessage').mockResolvedValue(messageResult);
  }
}

6. 测试改进经验(2024-03-22)

边界条件测试要点

  1. 输入验证

    • 空字符串处理
    • 超长输入处理
    • 特殊字符处理
    • 无效参数处理
  2. 服务异常处理

    • 超时处理
    • 空响应处理
    • 网络错误处理
    • 服务降级处理
  3. 状态记录验证

    • 历史记录完整性
    • 状态变更正确性
    • 关联关系准确性
    • 时序记录准确性

测试代码组织

describe('功能测试组', () => {
  // 基础测试
  describe('基本功能', () => {
    it('正常场景', async () => {});
    it('异常场景', async () => {});
  });

  // 边界条件
  describe('边界条件', () => {
    it('空输入', async () => {});
    it('超长输入', async () => {});
    it('特殊字符', async () => {});
  });

  // 状态管理
  describe('状态管理', () => {
    it('状态记录', async () => {});
    it('状态查询', async () => {});
    it('状态关联', async () => {});
  });
});

测试优化技巧

  1. Mock 优化

    // 统一的 Mock 设置
    function setupServiceMocks(options: MockOptions) {
      const {
        templateContent,
        modelResponse,
        shouldFail
      } = options;
    
      if (shouldFail) {
        vi.spyOn(service, 'call').mockRejectedValue(new Error('预期的错误'));
      } else {
        vi.spyOn(service, 'call').mockResolvedValue(modelResponse);
      }
    }
    
  2. 错误处理验证

    // 统一的错误处理验证
    expect(() => {
      // 触发错误的操作
    }).rejects.toThrow(expect.objectContaining({
      name: 'ValidationError',
      message: expect.stringContaining('预期的错误消息')
    }));
    
  3. 测试数据管理

    // 集中管理测试数据
    const TEST_DATA = {
      validPrompt: '有效的提示词',
      invalidPrompt: '',
      longPrompt: 'a'.repeat(10000),
      specialChars: '!@#$%^&*()',
    } as const;
    

改进效果

  1. 测试覆盖率提升
  2. 边界场景更完善
  3. 代码可维护性提高
  4. 测试执行效率提升

LangChain消息类型修复经验

问题描述

在使用LangChain进行消息处理时,遇到类型不匹配的问题:

Argument of type 'AIMessage' is not assignable to parameter of type 'AIMessageChunk'

原因分析

  1. LangChain的消息类型有多种,包括AIMessage和AIMessageChunk
  2. AIMessageChunk是专门用于流式响应的消息类型
  3. 在调用invoke方法时,返回类型为AIMessageChunk

解决方案

  1. 将导入语句从AIMessage改为AIMessageChunk:
import { AIMessageChunk } from '@langchain/core/messages'
  1. 更新所有使用AIMessage的地方为AIMessageChunk

最佳实践

  1. 使用正确的消息类型:
    • AIMessage: 用于普通消息
    • AIMessageChunk: 用于流式响应
    • HumanMessage: 用于用户输入
    • SystemMessage: 用于系统消息
  2. 在mock测试时确保使用正确的消息类型
  3. 参考LangChain文档中的类型定义

相关文档