尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

模拟 Anthropic Claude 多模态 API 调用的 Mock 测试类详解

模拟 Anthropic Claude 多模态 API 调用的 Mock 测试类详解 在开发依赖 Anthropic Claude 系列模型如claude-mythos-5、claude-fable-5、claude-opus-5的应用程序时单元测试的编写往往面临两大挑战一是需要真实的 API 密钥和网络连接二是真实调用会产生费用和延迟。为了解决这些问题我们可以设计一个 Mock 测试类它完全模拟官方 Python SDK 的调用接口使得测试代码与生产代码保持一致同时无需任何外部依赖。本文详细介绍了MockAnthropic类的设计与应用它提供了一个与官方 Anthropic Python SDK 完全兼容的模拟客户端支持多模态输入图片、文档和所有常见调用模式。通过使用该 Mock 类开发者可以在单元测试中高效地验证代码逻辑而无需关心网络、密钥或成本问题。未来可根据需要进一步扩展流式响应、异步调用等功能以满足更复杂的测试需求。MockAnthropic 类的设计与实现核心思想是模拟anthropic.Anthropic客户端的行为特别是client.messages.create方法。我们使用SimpleNamespace构建与官方 SDK 返回结构兼容的对象使得测试代码可以直接通过message.content[0].text获取文本而无需任何改动。完整代码fromtypesimportSimpleNamespaceclassMockMessages:模拟 client.messages 对象支持文本、图片和文档附件。SUPPORTED_MODELS{claude-mythos-5,claude-fable-5,claude-opus-5}defcreate(self,model,messages,max_tokens1024,systemNone,**kwargs): 模拟 client.messages.create 调用支持文本、图片和文档输入。 参数与官方 SDK 一致 - model: 模型名称 - messages: 消息列表content 可以是字符串或块列表文本、图片、文档等 - max_tokens: 最大生成 token 数 - system: 系统提示词可选 - **kwargs: 其他参数忽略 返回模拟的 Message 对象结构与官方 SDK 返回一致。 ifmodelnotinself.SUPPORTED_MODELS:raiseValueError(fUnsupported model:{model}. Supported models:{self.SUPPORTED_MODELS})# 提取最后一条用户消息的内容支持字符串和块列表user_textmedia_info[]# 存储图片和文档信息formsginreversed(messages):ifmsg.get(role)user:contentmsg.get(content,)ifisinstance(content,str):user_textcontentelifisinstance(content,list):text_parts[]forblockincontent:block_typeblock.get(type)ifblock_typetext:text_parts.append(block.get(text,))elifblock_typein(image,document):sourceblock.get(source,{})source_typesource.get(type,unknown)media_typesource.get(media_type,unknown)ifblock_typeimage:ifsource_typebase64:datasource.get(data,)media_info.append(f[Image:{media_type}, base64 data length:{len(data)}])elifsource_typeurl:urlsource.get(url,)media_info.append(f[Image URL:{url}, type:{media_type}])else:media_info.append(f[Image:{media_type}, source type:{source_type}])elifblock_typedocument:filenameblock.get(filename,unnamed)ifsource_typebase64:datasource.get(data,)media_info.append(f[Document:{filename},{media_type}, base64 data length:{len(data)}])elifsource_typeurl:urlsource.get(url,)media_info.append(f[Document URL:{filename},{url}, type:{media_type}])else:media_info.append(f[Document:{filename},{media_type}, source type:{source_type}])user_text .join(text_parts)break# 构造模拟回复文本system_prefixf[System:{system}] ifsystemelsemedia_prefix .join(media_info) ifmedia_infoelseresponse_textf{system_prefix}{media_prefix}Mock response from{model}. You said:{user_text}# 根据 max_tokens 粗略截断每 token 按 4 字符估算ifmax_tokensisnotNoneandmax_tokens0:response_textresponse_text[:max_tokens*4]# 构建模拟的 Message 对象与官方 SDK 返回结构兼容mock_content_blockSimpleNamespace(typetext,textresponse_text)mock_usageSimpleNamespace(input_tokenslen(user_text.split())(len(system.split())ifsystemelse0),output_tokenslen(response_text.split()))mock_messageSimpleNamespace(idmsg_mock_12345,typemessage,roleassistant,content[mock_content_block],modelmodel,stop_reasonend_turn,stop_sequenceNone,usagemock_usage)returnmock_messageclassMockAnthropic: 模拟 Anthropic 官方客户端用法与 anthropic.Anthropic 完全一致支持多模态与文档附件。 def__init__(self,api_keyNone,**kwargs):# 可接受 api_key 等参数但全部忽略self.messagesMockMessages()关键设计说明接口一致性MockAnthropic类暴露messages属性其类型为MockMessages提供create方法与官方client.messages.create签名一致。消息解析create方法遍历消息列表提取最后一条用户消息的内容。内容可以是字符串或列表列表中的块类型支持text、image和document。多模态识别对于图片和文档块提取其来源信息base64 数据长度或 URL并生成描述性文本使模拟回复能够反映输入内容。响应构造使用SimpleNamespace构造包含content、usage等属性的对象content是一个列表包含一个typetext的块与真实响应结构一致。错误处理如果传入不支持的模型名称会抛出ValueError模拟真实 SDK 的行为。可扩展性若需要支持流式响应或异步调用可在此基础上添加相应方法。所有类型的调用语句示例以下使用MockAnthropic客户端演示各种可能的调用场景并展示模拟输出结果。1. 非多模态纯文本字符串clientMockAnthropic()messageclient.messages.create(modelclaude-opus-5,max_tokens1024,messages[{role:user,content:Hello, Claude!}])print(message.content[0].text)输出Mock response from claude-opus-5. You said: Hello, Claude!2. 非多模态文本块列表messageclient.messages.create(modelclaude-fable-5,max_tokens512,messages[{role:user,content:[{type:text,text:Tell me a joke.}]}])print(message.content[0].text)输出Mock response from claude-fable-5. You said: Tell me a joke.3. 非多模态带系统提示词messageclient.messages.create(modelclaude-mythos-5,max_tokens1024,systemYou are a helpful assistant.,messages[{role:user,content:What is AI?}])print(message.content[0].text)输出[System: You are a helpful assistant.] Mock response from claude-mythos-5. You said: What is AI?4. 多模态文本 Base64 图片base64_imageiVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkA8AAQUBAScY42YAAAAASUVORK5CYIImessageclient.messages.create(modelclaude-opus-5,max_tokens1024,messages[{role:user,content:[{type:text,text:Whats in this image?},{type:image,source:{type:base64,media_type:image/png,data:base64_image}}]}])print(message.content[0].text)输出[Image: image/png, base64 data length: 68] Mock response from claude-opus-5. You said: Whats in this image?5. 多模态文本 图片 URLmessageclient.messages.create(modelclaude-fable-5,max_tokens512,messages[{role:user,content:[{type:text,text:Describe this picture.},{type:image,source:{type:url,url:https://example.com/image.jpg}}]}])print(message.content[0].text)输出[Image URL: https://example.com/image.jpg, type: unknown] Mock response from claude-fable-5. You said: Describe this picture.6. 多模态文本 Base64 文档PDFbase64_pdfJVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4CnN0cmVhbQp4nGNgYGJgYGAQZBBhYBSLAGQzB8hgsDAwgATRDAxMgUwGKMDAwMDAwsDAxMDAwMDCwMDAwMDwP0gAISYGBgYGBgYGmessageclient.messages.create(modelclaude-opus-5,max_tokens2048,messages[{role:user,content:[{type:text,text:Summarize this document.},{type:document,source:{type:base64,media_type:application/pdf,data:base64_pdf},filename:report.pdf}]}])print(message.content[0].text)输出[Document: report.pdf, application/pdf, base64 data length: 274] Mock response from claude-opus-5. You said: Summarize this document.7. 多模态文本 文档 URLmessageclient.messages.create(modelclaude-fable-5,max_tokens1024,messages[{role:user,content:[{type:text,text:Extract key points from this file.},{type:document,source:{type:url,url:https://example.com/document.txt},filename:notes.txt}]}])print(message.content[0].text)输出[Document URL: notes.txt, https://example.com/document.txt, type: unknown] Mock response from claude-fable-5. You said: Extract key points from this file.8. 多模态混合文本、图片和文档messageclient.messages.create(modelclaude-opus-5,max_tokens4096,messages[{role:user,content:[{type:text,text:Analyze the image and the document together.},{type:image,source:{type:url,url:https://example.com/chart.png}},{type:document,source:{type:base64,media_type:text/plain,data:SGVsbG8gV29ybGQ},filename:data.txt}]}])print(message.content[0].text)输出[Image URL: https://example.com/chart.png, type: unknown] [Document: data.txt, text/plain, base64 data length: 16] Mock response from claude-opus-5. You said: Analyze the image and the document together.如何在测试中使用 MockAnthropic直接实例化最简单的使用方式是在测试代码中直接创建MockAnthropic实例并将其传递给被测函数通过依赖注入或参数传递。defask_claude(prompt,client):messageclient.messages.create(modelclaude-opus-5,max_tokens1024,messages[{role:user,content:prompt}])returnmessage.content[0].text# 测试代码deftest_ask_claude():mock_clientMockAnthropic()resultask_claude(Hello,mock_client)assertMock responseinresult使用 unittest.mock.patch 替换如果不希望修改生产代码的客户端创建逻辑可以使用patch将anthropic.Anthropic替换为MockAnthropic。fromunittest.mockimportpatchimportanthropicdefask_claude(prompt):clientanthropic.Anthropic()messageclient.messages.create(modelclaude-opus-5,max_tokens1024,messages[{role:user,content:prompt}])returnmessage.content[0].textdeftest_ask_claude_with_patch():withpatch(anthropic.Anthropic,MockAnthropic):resultask_claude(Hello)assertMock responseinresult通过这种方式测试代码与生产代码保持完全一致只需在测试环境中替换客户端类即可。优点与局限性优点零外部依赖无需网络、API 密钥或真实服务测试可离线运行。接口完全兼容调用语法与官方 SDK 相同降低学习成本且测试代码可直接迁移到生产。支持多模态能够处理图片和文档附件覆盖更多真实场景。灵活可定制可以根据需要调整模拟回复的内容或行为例如模拟错误、特定输出等。局限性模拟响应不够真实返回的文本是模板化的无法验证业务逻辑对实际模型输出多样性的处理。缺少高级功能尚未实现流式响应、异步调用、工具调用Tool Use等高级特性但可扩展。token 估算粗略模拟的 token 计数基于简单的字符/单词估算可能与真实 SDK 不一致。
返回列表