news 2026/9/1 6:10:51

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模拟 Anthropic Claude 多模态 API 调用的 Mock 测试类详解

在开发依赖 Anthropic Claude 系列模型(如claude-mythos-5claude-fable-5claude-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_tokens=1024,system=None,**kwargs):""" 模拟 `client.messages.create` 调用,支持文本、图片和文档输入。 参数与官方 SDK 一致: - model: 模型名称 - messages: 消息列表,content 可以是字符串或块列表(文本、图片、文档等) - max_tokens: 最大生成 token 数 - system: 系统提示词(可选) - **kwargs: 其他参数(忽略) 返回模拟的 Message 对象,结构与官方 SDK 返回一致。 """ifmodelnotinself.SUPPORTED_MODELS:raiseValueError(f"Unsupported model:{model}. Supported models:{self.SUPPORTED_MODELS}")# 提取最后一条用户消息的内容,支持字符串和块列表user_text=""media_info=[]# 存储图片和文档信息formsginreversed(messages):ifmsg.get("role")=="user":content=msg.get("content","")ifisinstance(content,str):user_text=contentelifisinstance(content,list):text_parts=[]forblockincontent:block_type=block.get("type")ifblock_type=="text":text_parts.append(block.get("text",""))elifblock_typein("image","document"):source=block.get("source",{})source_type=source.get("type","unknown")media_type=source.get("media_type","unknown")ifblock_type=="image":ifsource_type=="base64":data=source.get("data","")media_info.append(f"[Image:{media_type}, base64 data length:{len(data)}]")elifsource_type=="url":url=source.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_type=="document":filename=block.get("filename","unnamed")ifsource_type=="base64":data=source.get("data","")media_info.append(f"[Document:{filename},{media_type}, base64 data length:{len(data)}]")elifsource_type=="url":url=source.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_prefix=f"[System:{system}] "ifsystemelse""media_prefix=" ".join(media_info)+" "ifmedia_infoelse""response_text=f"{system_prefix}{media_prefix}Mock response from{model}. You said:{user_text}"# 根据 max_tokens 粗略截断(每 token 按 4 字符估算)ifmax_tokensisnotNoneandmax_tokens>0:response_text=response_text[:max_tokens*4]# 构建模拟的 Message 对象(与官方 SDK 返回结构兼容)mock_content_block=SimpleNamespace(type="text",text=response_text)mock_usage=SimpleNamespace(input_tokens=len(user_text.split())+(len(system.split())ifsystemelse0),output_tokens=len(response_text.split()))mock_message=SimpleNamespace(id="msg_mock_12345",type="message",role="assistant",content=[mock_content_block],model=model,stop_reason="end_turn",stop_sequence=None,usage=mock_usage)returnmock_messageclassMockAnthropic:""" 模拟 Anthropic 官方客户端,用法与 `anthropic.Anthropic` 完全一致,支持多模态与文档附件。 """def__init__(self,api_key=None,**kwargs):# 可接受 api_key 等参数,但全部忽略self.messages=MockMessages()

关键设计说明

  1. 接口一致性MockAnthropic类暴露messages属性,其类型为MockMessages,提供create方法,与官方client.messages.create签名一致。
  2. 消息解析create方法遍历消息列表,提取最后一条用户消息的内容。内容可以是字符串或列表,列表中的块类型支持textimagedocument
  3. 多模态识别:对于图片和文档块,提取其来源信息(base64 数据长度或 URL)并生成描述性文本,使模拟回复能够反映输入内容。
  4. 响应构造:使用SimpleNamespace构造包含contentusage等属性的对象,content是一个列表,包含一个type="text"的块,与真实响应结构一致。
  5. 错误处理:如果传入不支持的模型名称,会抛出ValueError,模拟真实 SDK 的行为。
  6. 可扩展性:若需要支持流式响应或异步调用,可在此基础上添加相应方法。

所有类型的调用语句示例

以下使用MockAnthropic客户端演示各种可能的调用场景,并展示模拟输出结果。

1. 非多模态:纯文本字符串

client=MockAnthropic()message=client.messages.create(model="claude-opus-5",max_tokens=1024,messages=[{"role":"user","content":"Hello, Claude!"}])print(message.content[0].text)

输出

Mock response from claude-opus-5. You said: Hello, Claude!

2. 非多模态:文本块列表

message=client.messages.create(model="claude-fable-5",max_tokens=512,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. 非多模态:带系统提示词

message=client.messages.create(model="claude-mythos-5",max_tokens=1024,system="You 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_image="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="message=client.messages.create(model="claude-opus-5",max_tokens=1024,messages=[{"role":"user","content":[{"type":"text","text":"What's 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: What's in this image?

5. 多模态:文本 + 图片 URL

message=client.messages.create(model="claude-fable-5",max_tokens=512,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 文档(PDF)

base64_pdf="JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nGNgYGJgYGAQZBBhYBSLAGQzB8hgsDAwgATRDAxMgUwGKMDAwMDAwsDAxMDAwMDCwMDAwMDwP0gAISYGBgYGBgYG"message=client.messages.create(model="claude-opus-5",max_tokens=2048,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. 多模态:文本 + 文档 URL

message=client.messages.create(model="claude-fable-5",max_tokens=1024,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. 多模态:混合文本、图片和文档

message=client.messages.create(model="claude-opus-5",max_tokens=4096,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):message=client.messages.create(model="claude-opus-5",max_tokens=1024,messages=[{"role":"user","content":prompt}])returnmessage.content[0].text# 测试代码deftest_ask_claude():mock_client=MockAnthropic()result=ask_claude("Hello",mock_client)assert"Mock response"inresult

使用 unittest.mock.patch 替换

如果不希望修改生产代码的客户端创建逻辑,可以使用patchanthropic.Anthropic替换为MockAnthropic

fromunittest.mockimportpatchimportanthropicdefask_claude(prompt):client=anthropic.Anthropic()message=client.messages.create(model="claude-opus-5",max_tokens=1024,messages=[{"role":"user","content":prompt}])returnmessage.content[0].textdeftest_ask_claude_with_patch():withpatch('anthropic.Anthropic',MockAnthropic):result=ask_claude("Hello")assert"Mock response"inresult

通过这种方式,测试代码与生产代码保持完全一致,只需在测试环境中替换客户端类即可。

优点与局限性

优点

  1. 零外部依赖:无需网络、API 密钥或真实服务,测试可离线运行。
  2. 接口完全兼容:调用语法与官方 SDK 相同,降低学习成本,且测试代码可直接迁移到生产。
  3. 支持多模态:能够处理图片和文档附件,覆盖更多真实场景。
  4. 灵活可定制:可以根据需要调整模拟回复的内容或行为(例如模拟错误、特定输出等)。

局限性

  1. 模拟响应不够真实:返回的文本是模板化的,无法验证业务逻辑对实际模型输出多样性的处理。
  2. 缺少高级功能:尚未实现流式响应、异步调用、工具调用(Tool Use)等高级特性(但可扩展)。
  3. token 估算粗略:模拟的 token 计数基于简单的字符/单词估算,可能与真实 SDK 不一致。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 6:09:27

雪花算法(Snowflake Algorithm)

雪花算法(Snowflake Algorithm) 一篇带你搞懂雪花算法的文章,从原理到实现,从问题到优化。 目录 什么是雪花算法算法原理详解核心特点与优势Java 完整实现常见问题与解决方案与其他 ID 方案对比生产环境最佳实践总结 1. 什么是雪…

作者头像 李华
网站建设 2026/9/1 6:09:02

CRT特丽珑显示器重温经典动画:技术原理与实战配置指南

在数字内容消费日益高清化、高刷化的今天,我们习惯于用4K OLED屏幕观看最新的影视作品。然而,有一种独特的体验正在被一小部分爱好者重新发掘:使用上世纪末至本世纪初的CRT显示器,特别是索尼特丽珑(Trinitron&#xff…

作者头像 李华
网站建设 2026/9/1 6:08:14

LAMMPS源码编译与GPU加速全流程指南

简介:一份面向 Windows 平台上 Lammps 用户的开源源码资源,定位是解决从安装、环境配置到 GPU 加速调用的实操问题。包体共 3 个文件,以 HTML 说明页为主体,配以 .inscode 工程入口和 .gitignore 工程配置,整体约 5KB&…

作者头像 李华
网站建设 2026/9/1 6:07:56

收费合理的小程序制作公司怎么选:新手创业和夫妻店先看这几项

新手创业、夫妻店和社区小门店做小程序,最容易被“低价”带偏。有人只看几百元的开通费用,忽略了支付、商品管理和后续维护;也有人一开始就按定制项目报价,功能还没想清楚,预算已经先超出预期。对这类商家来说&#xf…

作者头像 李华
网站建设 2026/9/1 6:07:13

DeepSeek Harness插件开发指南:提示词管理与API调用增强

这次我们来看一个为 DeepSeek Harness 开发的插件项目。DeepSeek Harness 本身是一个功能强大的 AI 编程助手,但官方功能总有覆盖不到的地方。这个项目就是针对官方缺失的两个实用功能,开发了对应的插件来补全。对于经常使用 DeepSeek Harness 进行代码生…

作者头像 李华
网站建设 2026/9/1 6:05:35

产线串码写入与校验工具包:从SN到CRC的防呆设计

简介:面向创维电视及智能终端生产线的串码写入与校验工具包,专注解决SN码、MAC地址等关键参数的批量写入、读取与校验,同时集成条码打印与工位检测能力,适用于工厂测试人员、产线信息化工程师以及自动化设备集成商,尤其…

作者头像 李华