第十一篇:AnthropicClient 深度解析 —— Claude Code 如何封装 Anthropic API

发布时间:2026/7/5 3:58:39
第十一篇:AnthropicClient 深度解析 —— Claude Code 如何封装 Anthropic API 第十一篇AnthropicClient 深度解析 —— Claude Code 如何封装 Anthropic API源码位置src/services/anthropic/AnthropicClient.ts一、引言为什么需要 AnthropicClient上一篇我们深入分析了callModel.ts了解了 Claude Code 如何调用大模型 API。但有一个问题callModel.ts只负责调用而真正的连接是如何建立的答案就在AnthropicClient.ts里。这个文件是 Claude Code 与 Anthropic API 之间的桥梁负责创建和管理 HTTP 连接处理认证和授权实现流式响应解析错误处理和重试机制支持多种模型和参数配置二、AnthropicClient 架构概览┌─────────────────────────────────────────────────────────────┐ │ AnthropicClient │ ├─────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ HTTP Client │ │ Auth Manager │ │ Stream Parser │ │ │ │ (axios/fetch)│ │ (API Key) │ │ (SSE/EventStream)│ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Retry Logic │ │ Error Handler│ │ Model Router │ │ │ │ (指数退避) │ │ (分类处理) │ │ (模型选择) │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘三、核心类设计3.1 AnthropicClient 类定义// src/services/anthropic/AnthropicClient.tsexportclassAnthropicClient{privateapiKey:string;privatebaseURL:string;privatehttpClient:AxiosInstance;privateretryConfig:RetryConfig;constructor(config:AnthropicClientConfig){this.apiKeyconfig.apiKey;this.baseURLconfig.baseURL||https://api.anthropic.com;this.httpClientthis.createHttpClient();this.retryConfig{maxRetries:config.maxRetries||3,backoffMultiplier:2,initialDelayMs:1000};}}设计要点配置驱动通过AnthropicClientConfig接口灵活配置依赖注入httpClient可替换便于测试默认安全合理的重试和超时默认值3.2 配置接口interfaceAnthropicClientConfig{apiKey:string;// Anthropic API 密钥baseURL?:string;// API 基础 URL可选maxRetries?:number;// 最大重试次数timeoutMs?:number;// 请求超时时间model?:string;// 默认模型maxTokens?:number;// 默认最大 token 数}interfaceRetryConfig{maxRetries:number;backoffMultiplier:number;initialDelayMs:number;}四、HTTP 客户端创建4.1 创建 axios 实例privatecreateHttpClient():AxiosInstance{constclientaxios.create({baseURL:this.baseURL,timeout:60000,// 60秒超时headers:{Content-Type:application/json,x-api-key:this.apiKey,anthropic-version:2023-06-01}});// 添加请求拦截器client.interceptors.request.use((config){this.logRequest(config);returnconfig;},(error)Promise.reject(error));// 添加响应拦截器client.interceptors.response.use((response)response,(error)this.handleHttpError(error));returnclient;}关键设计anthropic-version头部指定 API 版本确保兼容性拦截器统一处理请求日志和错误60秒超时适应大模型推理的延迟4.2 错误处理privatehandleHttpError(error:AxiosError):Promisenever{if(error.response){conststatuserror.response.status;constdataerror.response.dataasAnthropicError;switch(status){case401:thrownewAnthropicAuthError(API key 无效或已过期,data);case429:thrownewAnthropicRateLimitError(请求过于频繁,data);case500:case502:case503:thrownewAnthropicServerError(Anthropic 服务暂时不可用,data);default:thrownewAnthropicAPIError(API 错误:${data.error?.message},data);}}thrownewAnthropicNetworkError(网络连接失败,error);}错误分类AnthropicAuthError认证问题401AnthropicRateLimitError限流问题429AnthropicServerError服务端问题5xxAnthropicNetworkError网络问题五、核心 API 方法5.1 发送消息非流式asyncsendMessage(messages:Message[],options:MessageOptions{}):PromiseMessageResponse{constrequestBody:MessageRequest{model:options.model||this.defaultModel,max_tokens:options.maxTokens||this.defaultMaxTokens,messages:messages,temperature:options.temperature??0.7,system:options.system};constresponseawaitthis.httpClient.postMessageResponse(/v1/messages,requestBody);returnresponse.data;}5.2 流式发送消息async*streamMessage(messages:Message[],options:MessageOptions{}):AsyncGeneratorStreamChunk,void,unknown{constrequestBody:MessageRequest{...this.buildRequestBody(messages,options),stream:true// 启用流式响应};constresponseawaitthis.httpClient.post(/v1/messages,requestBody,{responseType:stream,adapter:http// 使用 Node.js 原生 http 模块});yield*this.parseStream(response.data);}流式响应的优势实时显示用户无需等待完整响应降低延迟首 token 时间TTFT显著缩短更好的 UX类似 ChatGPT 的打字机效果六、流式响应解析6.1 SSEServer-Sent Events解析器privateasync*parseStream(stream:ReadableStream):AsyncGeneratorStreamChunk,void,unknown{constreaderstream.getReader();constdecodernewTextDecoder();letbuffer;try{while(true){const{done,value}awaitreader.read();if(done)break;bufferdecoder.decode(value,{stream:true});// 处理 SSE 格式data: {...}\n\nconstlinesbuffer.split(\n\n);bufferlines.pop()||;// 保留不完整的最后一块for(constlineoflines){if(line.startsWith(data: )){constjsonStrline.slice(6);// 去掉 data: 前缀if(jsonStr[DONE]){return;// 流结束}constchunkJSON.parse(jsonStr);yieldthis.transformChunk(chunk);}}}}finally{reader.releaseLock();}}6.2 流式数据块转换privatetransformChunk(rawChunk:any):StreamChunk{switch(rawChunk.type){casecontent_block_delta:return{type:text,content:rawChunk.delta?.text||};casemessage_stop:return{type:stop,stopReason:rawChunk.message?.stop_reason};caseerror:thrownewAnthropicStreamError(rawChunk.error?.message);default:return{type:unknown,raw:rawChunk};}}SSE 格式示例data: {type: content_block_delta, delta: {text: Hello}} data: {type: content_block_delta, delta: {text: world}} data: [DONE]七、重试机制7.1 指数退避重试asyncwithRetryT(operation:()PromiseT,context:RetryContext):PromiseT{letlastError:Error;for(letattempt0;attemptthis.retryConfig.maxRetries;attempt){try{returnawaitoperation();}catch(error){lastErrorerrorasError;// 不可重试的错误直接抛出if(!this.isRetryableError(error)){throwerror;}// 最后一次尝试失败抛出错误if(attemptthis.retryConfig.maxRetries){break;}// 计算退避延迟constdelayMsthis.calculateBackoff(attempt);this.logger.warn(请求失败${delayMs}ms 后重试 (${attempt1}/${this.retryConfig.maxRetries}),{error:lastError.message});awaitthis.sleep(delayMs);}}thrownewAnthropicRetryExhaustedError(重试次数已耗尽:${lastError!.message},context);}7.2 重试策略privateisRetryableError(error:unknown):boolean{if(errorinstanceofAnthropicRateLimitError){returntrue;// 429 限流可重试}if(errorinstanceofAnthropicServerError){returntrue;// 5xx 服务端错误可重试}if(errorinstanceofAnthropicNetworkError){returntrue;// 网络错误可重试}returnfalse;// 其他错误不重试}privatecalculateBackoff(attempt:number):number{constbaseDelaythis.retryConfig.initialDelayMs;constmultiplierMath.pow(this.retryConfig.backoffMultiplier,attempt);constjitterMath.random()*1000;// 添加随机抖动returnMath.min(baseDelay*multiplierjitter,30000);// 最多30秒}八、模型路由8.1 支持的多模型constSUPPORTED_MODELS{// Claude 3.5 系列claude-3-5-sonnet-20241022:{maxTokens:8192,contextWindow:200000,supportsVision:true},claude-3-5-haiku-20241022:{maxTokens:4096,contextWindow:200000,supportsVision:false},// Claude 3 系列claude-3-opus-20240229:{maxTokens:4096,contextWindow:200000,supportsVision:true},claude-3-sonnet-20240229:{maxTokens:4096,contextWindow:200000,supportsVision:true}}asconst;8.2 模型选择逻辑selectModel(requirements:ModelRequirements):string{const{needsVision,maxTokens,preferredSpeed}requirements;// 根据需求筛选合适的模型constcandidatesObject.entries(SUPPORTED_MODELS).filter(([_,spec]){if(needsVision!spec.supportsVision)returnfalse;if(maxTokensspec.maxTokensmaxTokens)returnfalse;returntrue;});// 按优先级排序if(preferredSpeedfast){// 优先选择 Haiku最快returncandidates.find(([name])name.includes(haiku))?.[0]||candidates[0][0];}// 默认选择 Sonnet平衡returncandidates.find(([name])name.includes(sonnet))?.[0]||candidates[0][0];}九、使用示例9.1 基础用法constclientnewAnthropicClient({apiKey:process.env.ANTHROPIC_API_KEY,model:claude-3-5-sonnet-20241022,maxTokens:4096});// 非流式调用constresponseawaitclient.sendMessage([{role:user,content:你好Claude}]);console.log(response.content[0].text);9.2 流式调用// 流式调用conststreamclient.streamMessage([{role:user,content:写一首关于AI的诗}]);forawait(constchunkofstream){if(chunk.typetext){process.stdout.write(chunk.content);// 实时输出}}9.3 带系统提示constresponseawaitclient.sendMessage([{role:user,content:分析这段代码}],{system:你是一位资深软件工程师擅长代码审查。,temperature:0.3// 更确定的输出});十、源码亮点总结特性实现方式价值配置驱动构造函数接收配置对象灵活、可测试拦截器模式axios request/response interceptors统一处理日志和错误流式解析AsyncGenerator SSE 解析低延迟、好体验指数退避随机抖动 最大延迟限制避免雪崩、快速恢复错误分类自定义错误类体系精准定位问题模型路由需求驱动的模型选择成本与性能平衡十一、下一篇预告第12篇我们将深入src/services/anthropic/MessageBuilder.ts看看 Claude Code 如何构建复杂的对话消息处理多模态输入文本 图片管理对话上下文和 token 预算敬请期待Claude Code 源码解析系列源码泄露事件深度解析开发环境搭建指南CLI入口与命令系统AI对话引擎全解析工具系统深度解析CLI命令行解析核心Handler处理器链QueryEngine查询引擎query.ts API对话层callModel.ts API调用层AnthropicClient API客户端← 本文MessageBuilder 消息构建器待续