简介:这是一份面向Claude AI应用开发者的实践型入门手册,适合希望从零构建高质量AI应用的初学者与进阶开发者。内容围绕Claude平台的功能解析、AI基础概念、实例代码与应用案例展开,覆盖智能聊天机器人、语音识别、图像识别等典型场景,并延伸至性能优化、可靠性保障、可扩展性设计以及数据隐私、偏见防范等AI伦理合规议题,帮助读者在动手实践中建立完整的开发思路。资源包共336个文件,约160.99MB,以ipynb交互式笔记、py脚本、md说明文档、png图示、txt与json配置数据为主,另含csv数据集、pdf资料、yaml/yml配置及少量模型与数据库文件,结构清晰,便于按模块检索学习。目前已有292人学习下载。手册通过真实案例展示理论到项目的落地过程,并总结常见错误规避与最佳实践,可作为快速入门向导与技能拓展参考。1. 从一次“跑不通”的 Demo 说起:Claude 应用开发到底在做什么很多人第一次接触 Claude 应用开发,是拿着一个 API Key,照着示例发了条消息,看到模型回了一句话,然后……就卡住了。能对话,但不知道怎么变成一个能用的应用;能调通,但一上真实场景就翻车。这不是你一个人的问题,而是因为“调用模型”和“开发应用”之间隔着一整套工程化的东西:上下文怎么管、工具怎么接、流式怎么处理、成本怎么控、失败怎么兜底。这份入门手册要解决的,就是把这层窗户纸捅破。它面向的是有基本编程能力、想把 Claude 接进自己产品的开发者——不管你是做内部工具、做垂直场景助手,还是做多轮对话系统。读完你应该能独立跑通一个带工具调用和流式输出的最小可用应用,并且知道每一步的参数为什么这么设、哪里最容易踩坑。下面我按“先立住概念、再动手复现、最后讲边界”的顺序往下走。2. 动手前先把三件事定下来:模型选型、接口形态、上下文预算在写第一行代码之前,有三件事如果没想清楚,后面返工的概率极高。这一章不讲虚的,只讲选型理由和可执行的判断标准。2.1 模型选型:别一上来就用最强的那个Claude 系列通常按能力分成几档,常见做法是按“任务复杂度”而不是“预算”来选。我的经验是分三层:高频、低复杂度的任务(分类、抽取、改写、路由)用轻量档,延迟低、单位成本低,适合放在主链路上。需要推理、多步规划、长上下文理解的任务用中档或高档,适合放在关键节点。真正需要最强模型的场景其实很少,通常是复杂代码生成、长文档深度分析这类。判断标准很简单:拿 50 条真实样本,分别用轻量档和强档跑一遍,看结果差异是否值得那个成本差。多数情况下,轻量档能覆盖 70% 以上的请求,剩下的走强档,这个“分级路由”本身就是省钱的关键。提示:不要用“我觉得这个任务很难”来选模型,要用真实样本的对比结果来选。感觉经常是错的。2.2 接口形态:同步、流式、还是批处理接口形态决定了你的用户体验和架构复杂度。三种常见形态:形态适用场景关键取舍同步请求后台任务、短输出、内部工具实现简单,但长输出会超时流式输出面向用户的对话、长文本生成体验好,但要处理分片和中断批处理离线跑量、数据标注、批量分析吞吐高、成本低,但无实时性面向用户的场景,流式几乎是默认选择。原因很直接:用户等 10 秒看到完整答案,和 1 秒内开始看到字往外蹦,体感差距巨大。但流式会带来分片拼接、错误中断、UI 状态管理这些额外工作,后面第 4 章会专门讲。2.3 上下文预算:把 token 当成钱来花上下文窗口再大也是有限的,而且是要花钱的。我一般会先算一笔账:系统提示词占多少、历史对话保留多少轮、检索回来的资料塞多少、留给输出的空间多少。一个常见的分配是:系统提示词:控制在 500~1500 token,写清楚角色、边界、输出格式。历史对话:只保留最近 N 轮,或者做摘要压缩,别无限堆。外部资料:按相关性排序后截断,宁可少而准,不要多而杂。输出预留:至少留 1000~2000 token,否则长回答会被硬截断。这里有个血泪经验:很多人把整个知识库往上下文里塞,结果模型被无关信息干扰,回答质量反而下降。上下文不是越多越好,是越相关越好。3. 跑通第一个最小应用:从发消息到工具调用概念定完,进入动手环节。这一章的目标是让你从零跑通一个“能对话、能调工具、能流式返回”的最小应用。我按依赖安装、基础调用、工具调用、流式输出四步走,每步都给可抄的代码。3.1 环境准备与最小调用先装依赖,用官方 SDK。这里以 Python 为例,其他语言思路一致。# 安装官方 SDK,建议用虚拟环境隔离 pip install anthropicimport anthropic # 从环境变量读 key,不要硬编码在代码里 client = anthropic.Anthropic() response = client.messages.create( model="claude-3-5-sonnet-latest", # 模型名按你账号可用的来填 max_tokens=1024, # 输出上限,按场景调 system="你是一个简洁的技术助手,回答控制在三句话内。", messages=[ {"role": "user", "content": "解释一下什么是幂等性。"} ], ) # content 是一个列表,取第一段的文本 print(response.content[0].text)逻辑说明:messages是对话历史,system是系统提示词,两者分开传。max_tokens是硬上限,设太小会导致回答被截断,设太大又浪费预算。参数上,model一定要用你账号实际可用的名字,别照抄网上的,版本对不上会直接报错。3.2 工具调用:让模型能“动手”工具调用是 Claude 应用开发里最关键的一步,它让模型从“只会说”变成“能做事”。核心机制是:你把工具的描述和参数结构告诉模型,模型判断需要调用时返回一个工具调用请求,你的代码执行后把结果回传,模型再基于结果生成最终回答。import anthropic import json client = anthropic.Anthropic() # 定义工具:名称、描述、参数结构 tools = [ { "name": "get_weather", "description": "查询指定城市的当前天气", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"], }, } ] def get_weather(city: str) -> str: # 真实场景这里调天气 API,示例直接返回 return f"{city} 当前晴,25 摄氏度" messages = [{"role": "user", "content": "北京今天天气怎么样?"}] # 第一轮:模型决定是否调用工具 resp = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=tools, messages=messages, ) # 检查是否有工具调用请求 if resp.stop_reason == "tool_use": tool_block = next(b for b in resp.content if b.type == "tool_use") # 执行本地函数 result = get_weather(**tool_block.input) # 把模型输出和工具结果都追加进历史 messages.append({"role": "assistant", "content": resp.content}) messages.append({ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": tool_block.id, "content": result, }], }) # 第二轮:模型基于工具结果生成回答 final = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=tools, messages=messages, ) print(final.content[0].text)逻辑说明:工具调用的关键在stop_reason,它等于tool_use时说明模型想调工具。tool_use_id必须原样回传,否则模型对不上是哪次调用。参数上,input_schema用 JSON Schema 描述,required字段要写全,模型会按这个结构生成参数。这里最容易翻车的地方是:忘了把 assistant 的那条消息也追加进历史,导致模型不知道自己在调工具。3.3 流式输出:让用户不用干等流式输出把生成过程分片返回,用户能实时看到内容。实现上要处理事件类型,常见的有内容增量、消息开始、消息结束。import anthropic client = anthropic.Anthropic() # 用 stream 上下文管理器,逐事件处理 with client.messages.stream( model="claude-3-5-sonnet-latest", max_tokens=1024, messages=[{"role": "user", "content": "写一段关于秋天的短散文。"}], ) as stream: for text in stream.text_stream: # 逐片打印,实际应用里推给前端 print(text, end="", flush=True)逻辑说明:text_stream是 SDK 封装好的文本增量迭代器,省去了手动解析事件类型的麻烦。参数上,流式模式下max_tokens依然生效,超了会中断。注意flush=True在终端里是必须的,否则缓冲区不刷新,看起来像卡住了。生产环境里还要处理网络中断和用户主动取消,这部分第 5 章会讲。4. 把 Demo 变成能用的应用:提示词、状态与错误处理跑通最小应用只是起点。真正上线前,提示词工程、对话状态管理、错误处理这三块决定了应用是“能用”还是“难用”。4.1 系统提示词怎么写才不玄学系统提示词不是越长越好,而是要结构化。我一般按四段写:角色定义、能力边界、输出格式、示例。举个例子:system_prompt = """你是一个订单查询助手。 职责:根据用户提供的订单号查询状态。 边界:只处理订单查询,其他问题礼貌拒绝并引导。 输出格式:先给结论,再给细节,不超过 100 字。 示例: 用户:帮我查下订单 A123 回答:订单 A123 已发货,预计明天送达。 """逻辑说明:角色定义让模型知道“我是谁”,边界防止它乱答,输出格式保证前端好渲染,示例是最有效的对齐手段。参数上,提示词里的示例(few-shot)通常比大段描述更管用,两三个高质量示例顶得上一千字说明。玄学的地方在于,同样的提示词换个模型档位效果可能差很多,所以提示词要跟着模型一起测。4.2 对话状态管理:别让历史无限膨胀多轮对话里,历史会越来越长,成本和延迟都会涨。常见做法有三种:滑动窗口:只保留最近 N 轮,简单但会丢早期信息。摘要压缩:把早期对话用模型总结成一段,省 token 但多一次调用。关键信息抽取:把用户的关键事实(姓名、偏好、订单号)抽成结构化字段,单独维护。我一般用滑动窗口加关键信息抽取的组合:最近几轮保留原文,更早的只留抽取出的结构化信息。这样既省 token,又不丢关键上下文。参数上,窗口大小按任务定,客服类场景 5~8 轮通常够用,复杂咨询可能要 10 轮以上。4.3 错误处理:把失败当成常态线上环境里,超时、限流、返回格式不对都是常态。我的原则是:任何一次模型调用都要有兜底。import anthropic from anthropic import APIError, RateLimitError client = anthropic.Anthropic() def safe_call(messages, retries=3): for i in range(retries): try: return client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, messages=messages, ) except RateLimitError: # 限流:退避重试 time.sleep(2 ** i) except APIError as e: # 其他 API 错误:记录并降级 print(f"调用失败: {e}") break # 兜底:返回友好提示,别把异常抛给用户 return None逻辑说明:限流用指数退避重试,其他错误直接降级。参数上,重试次数别太多,3 次足够,再多会拖长用户等待。兜底返回要设计好,宁可给一句“稍后再试”,也别让用户看到堆栈信息。5. 避坑与排查:那些让我返工三次的问题这一章是我踩过的坑里最有代表性的几条,每条按现象、原因、解决写,希望能帮你省下返工时间。5.1 工具调用返回空结果现象:模型明明该调工具,却直接回了一段文字,或者工具调用后没有后续回答。原因:多半是工具描述写得太模糊,模型判断不需要调用;或者工具结果回传时tool_use_id对不上。解决:把工具描述写具体,明确“什么时候该用”;回传结果时严格用模型给的tool_use_id,别自己造。5.2 流式输出中文乱码或断字现象:前端收到的文本出现半个汉字或乱码。原因:按字节切分而不是按字符切分,多字节字符被截断。解决:在服务端按字符边界拼接后再推给前端,或者用 SDK 提供的文本流接口,它已经处理了边界问题。5.3 长对话后模型“失忆”现象:聊了十几轮后,模型忘了前面说过的关键信息。原因:滑动窗口把早期信息截掉了,或者摘要压缩时丢了细节。解决:关键事实单独抽取维护,每轮都注入到系统提示词或上下文里,别指望模型自己记住。5.4 成本突然飙升现象:某天账单比平时高好几倍。原因:通常是某个循环里反复调用,或者上下文没控制住越滚越大。解决:给每次调用打日志,记录 token 用量;设一个单请求的 token 上限告警;定期 review 调用链,找出异常放大的地方。5.5 输出格式不稳定现象:要求返回 JSON,有时却返回带解释的文字。原因:提示词里格式约束不够强,或者模型档位太低。解决:用工具调用强制结构化输出,或者用“只返回 JSON,不要任何其他文字”这类强约束,并在解析端做容错。6. 进阶技巧:用工具调用做结构化输出与验证最后讲一个我常用的进阶技巧:把工具调用当成“结构化输出”的手段。很多人不知道,工具调用不仅能做事,还能强制模型按你定义的结构返回数据,这比在提示词里求它“返回 JSON”可靠得多。思路是这样的:定义一个“提交结果”的工具,参数结构就是你要的数据结构,然后让模型必须调用这个工具来给出最终答案。import anthropic client = anthropic.Anthropic() # 用工具 schema 强制结构化输出 extract_tool = { "name": "submit_extraction", "description": "提交从文本中抽取的结构化信息", "input_schema": { "type": "object", "properties": { "name": {"type": "string", "description": "人名"}, "company": {"type": "string", "description": "公司名"}, "role": {"type": "string", "description": "职位"}, }, "required": ["name", "company", "role"], }, } resp = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=[extract_tool], tool_choice={"type": "tool", "name": "submit_extraction"}, # 强制调用 messages=[{ "role": "user", "content": "张三在某某科技担任后端工程师。", }], ) # 直接从工具调用里取结构化数据 for block in resp.content: if block.type == "tool_use": print(block.input) # 已经是 dict,无需解析 JSON逻辑说明:tool_choice设为指定工具时,模型必须调用它,不会返回自由文本。block.input已经是解析好的字典,省去了 JSON 解析和容错。参数上,required字段要写全,模型会保证都填上。这个技巧在数据抽取、分类、表单填充场景里特别稳,比纯提示词方案可靠一个量级。验证方法也很直接:拿一批真实样本跑,统计结构化输出的成功率。如果某个字段经常缺失或格式不对,就回去改 schema 的描述,把字段含义写得更明确。我一般会维护一个小的测试集,每次改提示词或 schema 都跑一遍,防止改一处崩一片。一个我养成的习惯:任何要上线的提示词和工具定义,都先在测试集上跑够 100 条再发布,别凭感觉。这个习惯帮我挡掉了好几次线上事故。希望帮到你。本文还有配套的精品资源,点击获取
简介:这是一份面向Claude AI应用开发者的实践型入门手册,适合希望从零构建高质量AI应用的初学者与进阶开发者。内容围绕Claude平台的功能解析、AI基础概念、实例代码与应用案例展开,覆盖智能聊天机器人、语音识别、图像识别等典型场景,并延伸至性能优化、可靠性保障、可扩展性设计以及数据隐私、偏见防范等AI伦理合规议题,帮助读者在动手实践中建立完整的开发思路。资源包共336个文件,约160.99MB,以ipynb交互式笔记、py脚本、md说明文档、png图示、txt与json配置数据为主,另含csv数据集、pdf资料、yaml/yml配置及少量模型与数据库文件,结构清晰,便于按模块检索学习。目前已有292人学习下载。手册通过真实案例展示理论到项目的落地过程,并总结常见错误规避与最佳实践,可作为快速入门向导与技能拓展参考。1. 从一次“跑不通”的 Demo 说起:Claude 应用开发到底在做什么很多人第一次接触 Claude 应用开发,是拿着一个 API Key,照着示例发了条消息,看到模型回了一句话,然后……就卡住了。能对话,但不知道怎么变成一个能用的应用;能调通,但一上真实场景就翻车。这不是你一个人的问题,而是因为“调用模型”和“开发应用”之间隔着一整套工程化的东西:上下文怎么管、工具怎么接、流式怎么处理、成本怎么控、失败怎么兜底。这份入门手册要解决的,就是把这层窗户纸捅破。它面向的是有基本编程能力、想把 Claude 接进自己产品的开发者——不管你是做内部工具、做垂直场景助手,还是做多轮对话系统。读完你应该能独立跑通一个带工具调用和流式输出的最小可用应用,并且知道每一步的参数为什么这么设、哪里最容易踩坑。下面我按“先立住概念、再动手复现、最后讲边界”的顺序往下走。2. 动手前先把三件事定下来:模型选型、接口形态、上下文预算在写第一行代码之前,有三件事如果没想清楚,后面返工的概率极高。这一章不讲虚的,只讲选型理由和可执行的判断标准。2.1 模型选型:别一上来就用最强的那个Claude 系列通常按能力分成几档,常见做法是按“任务复杂度”而不是“预算”来选。我的经验是分三层:高频、低复杂度的任务(分类、抽取、改写、路由)用轻量档,延迟低、单位成本低,适合放在主链路上。需要推理、多步规划、长上下文理解的任务用中档或高档,适合放在关键节点。真正需要最强模型的场景其实很少,通常是复杂代码生成、长文档深度分析这类。判断标准很简单:拿 50 条真实样本,分别用轻量档和强档跑一遍,看结果差异是否值得那个成本差。多数情况下,轻量档能覆盖 70% 以上的请求,剩下的走强档,这个“分级路由”本身就是省钱的关键。提示:不要用“我觉得这个任务很难”来选模型,要用真实样本的对比结果来选。感觉经常是错的。2.2 接口形态:同步、流式、还是批处理接口形态决定了你的用户体验和架构复杂度。三种常见形态:形态适用场景关键取舍同步请求后台任务、短输出、内部工具实现简单,但长输出会超时流式输出面向用户的对话、长文本生成体验好,但要处理分片和中断批处理离线跑量、数据标注、批量分析吞吐高、成本低,但无实时性面向用户的场景,流式几乎是默认选择。原因很直接:用户等 10 秒看到完整答案,和 1 秒内开始看到字往外蹦,体感差距巨大。但流式会带来分片拼接、错误中断、UI 状态管理这些额外工作,后面第 4 章会专门讲。2.3 上下文预算:把 token 当成钱来花上下文窗口再大也是有限的,而且是要花钱的。我一般会先算一笔账:系统提示词占多少、历史对话保留多少轮、检索回来的资料塞多少、留给输出的空间多少。一个常见的分配是:系统提示词:控制在 500~1500 token,写清楚角色、边界、输出格式。历史对话:只保留最近 N 轮,或者做摘要压缩,别无限堆。外部资料:按相关性排序后截断,宁可少而准,不要多而杂。输出预留:至少留 1000~2000 token,否则长回答会被硬截断。这里有个血泪经验:很多人把整个知识库往上下文里塞,结果模型被无关信息干扰,回答质量反而下降。上下文不是越多越好,是越相关越好。3. 跑通第一个最小应用:从发消息到工具调用概念定完,进入动手环节。这一章的目标是让你从零跑通一个“能对话、能调工具、能流式返回”的最小应用。我按依赖安装、基础调用、工具调用、流式输出四步走,每步都给可抄的代码。3.1 环境准备与最小调用先装依赖,用官方 SDK。这里以 Python 为例,其他语言思路一致。# 安装官方 SDK,建议用虚拟环境隔离 pip install anthropicimport anthropic # 从环境变量读 key,不要硬编码在代码里 client = anthropic.Anthropic() response = client.messages.create( model="claude-3-5-sonnet-latest", # 模型名按你账号可用的来填 max_tokens=1024, # 输出上限,按场景调 system="你是一个简洁的技术助手,回答控制在三句话内。", messages=[ {"role": "user", "content": "解释一下什么是幂等性。"} ], ) # content 是一个列表,取第一段的文本 print(response.content[0].text)逻辑说明:messages是对话历史,system是系统提示词,两者分开传。max_tokens是硬上限,设太小会导致回答被截断,设太大又浪费预算。参数上,model一定要用你账号实际可用的名字,别照抄网上的,版本对不上会直接报错。3.2 工具调用:让模型能“动手”工具调用是 Claude 应用开发里最关键的一步,它让模型从“只会说”变成“能做事”。核心机制是:你把工具的描述和参数结构告诉模型,模型判断需要调用时返回一个工具调用请求,你的代码执行后把结果回传,模型再基于结果生成最终回答。import anthropic import json client = anthropic.Anthropic() # 定义工具:名称、描述、参数结构 tools = [ { "name": "get_weather", "description": "查询指定城市的当前天气", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"], }, } ] def get_weather(city: str) -> str: # 真实场景这里调天气 API,示例直接返回 return f"{city} 当前晴,25 摄氏度" messages = [{"role": "user", "content": "北京今天天气怎么样?"}] # 第一轮:模型决定是否调用工具 resp = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=tools, messages=messages, ) # 检查是否有工具调用请求 if resp.stop_reason == "tool_use": tool_block = next(b for b in resp.content if b.type == "tool_use") # 执行本地函数 result = get_weather(**tool_block.input) # 把模型输出和工具结果都追加进历史 messages.append({"role": "assistant", "content": resp.content}) messages.append({ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": tool_block.id, "content": result, }], }) # 第二轮:模型基于工具结果生成回答 final = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=tools, messages=messages, ) print(final.content[0].text)逻辑说明:工具调用的关键在stop_reason,它等于tool_use时说明模型想调工具。tool_use_id必须原样回传,否则模型对不上是哪次调用。参数上,input_schema用 JSON Schema 描述,required字段要写全,模型会按这个结构生成参数。这里最容易翻车的地方是:忘了把 assistant 的那条消息也追加进历史,导致模型不知道自己在调工具。3.3 流式输出:让用户不用干等流式输出把生成过程分片返回,用户能实时看到内容。实现上要处理事件类型,常见的有内容增量、消息开始、消息结束。import anthropic client = anthropic.Anthropic() # 用 stream 上下文管理器,逐事件处理 with client.messages.stream( model="claude-3-5-sonnet-latest", max_tokens=1024, messages=[{"role": "user", "content": "写一段关于秋天的短散文。"}], ) as stream: for text in stream.text_stream: # 逐片打印,实际应用里推给前端 print(text, end="", flush=True)逻辑说明:text_stream是 SDK 封装好的文本增量迭代器,省去了手动解析事件类型的麻烦。参数上,流式模式下max_tokens依然生效,超了会中断。注意flush=True在终端里是必须的,否则缓冲区不刷新,看起来像卡住了。生产环境里还要处理网络中断和用户主动取消,这部分第 5 章会讲。4. 把 Demo 变成能用的应用:提示词、状态与错误处理跑通最小应用只是起点。真正上线前,提示词工程、对话状态管理、错误处理这三块决定了应用是“能用”还是“难用”。4.1 系统提示词怎么写才不玄学系统提示词不是越长越好,而是要结构化。我一般按四段写:角色定义、能力边界、输出格式、示例。举个例子:system_prompt = """你是一个订单查询助手。 职责:根据用户提供的订单号查询状态。 边界:只处理订单查询,其他问题礼貌拒绝并引导。 输出格式:先给结论,再给细节,不超过 100 字。 示例: 用户:帮我查下订单 A123 回答:订单 A123 已发货,预计明天送达。 """逻辑说明:角色定义让模型知道“我是谁”,边界防止它乱答,输出格式保证前端好渲染,示例是最有效的对齐手段。参数上,提示词里的示例(few-shot)通常比大段描述更管用,两三个高质量示例顶得上一千字说明。玄学的地方在于,同样的提示词换个模型档位效果可能差很多,所以提示词要跟着模型一起测。4.2 对话状态管理:别让历史无限膨胀多轮对话里,历史会越来越长,成本和延迟都会涨。常见做法有三种:滑动窗口:只保留最近 N 轮,简单但会丢早期信息。摘要压缩:把早期对话用模型总结成一段,省 token 但多一次调用。关键信息抽取:把用户的关键事实(姓名、偏好、订单号)抽成结构化字段,单独维护。我一般用滑动窗口加关键信息抽取的组合:最近几轮保留原文,更早的只留抽取出的结构化信息。这样既省 token,又不丢关键上下文。参数上,窗口大小按任务定,客服类场景 5~8 轮通常够用,复杂咨询可能要 10 轮以上。4.3 错误处理:把失败当成常态线上环境里,超时、限流、返回格式不对都是常态。我的原则是:任何一次模型调用都要有兜底。import anthropic from anthropic import APIError, RateLimitError client = anthropic.Anthropic() def safe_call(messages, retries=3): for i in range(retries): try: return client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, messages=messages, ) except RateLimitError: # 限流:退避重试 time.sleep(2 ** i) except APIError as e: # 其他 API 错误:记录并降级 print(f"调用失败: {e}") break # 兜底:返回友好提示,别把异常抛给用户 return None逻辑说明:限流用指数退避重试,其他错误直接降级。参数上,重试次数别太多,3 次足够,再多会拖长用户等待。兜底返回要设计好,宁可给一句“稍后再试”,也别让用户看到堆栈信息。5. 避坑与排查:那些让我返工三次的问题这一章是我踩过的坑里最有代表性的几条,每条按现象、原因、解决写,希望能帮你省下返工时间。5.1 工具调用返回空结果现象:模型明明该调工具,却直接回了一段文字,或者工具调用后没有后续回答。原因:多半是工具描述写得太模糊,模型判断不需要调用;或者工具结果回传时tool_use_id对不上。解决:把工具描述写具体,明确“什么时候该用”;回传结果时严格用模型给的tool_use_id,别自己造。5.2 流式输出中文乱码或断字现象:前端收到的文本出现半个汉字或乱码。原因:按字节切分而不是按字符切分,多字节字符被截断。解决:在服务端按字符边界拼接后再推给前端,或者用 SDK 提供的文本流接口,它已经处理了边界问题。5.3 长对话后模型“失忆”现象:聊了十几轮后,模型忘了前面说过的关键信息。原因:滑动窗口把早期信息截掉了,或者摘要压缩时丢了细节。解决:关键事实单独抽取维护,每轮都注入到系统提示词或上下文里,别指望模型自己记住。5.4 成本突然飙升现象:某天账单比平时高好几倍。原因:通常是某个循环里反复调用,或者上下文没控制住越滚越大。解决:给每次调用打日志,记录 token 用量;设一个单请求的 token 上限告警;定期 review 调用链,找出异常放大的地方。5.5 输出格式不稳定现象:要求返回 JSON,有时却返回带解释的文字。原因:提示词里格式约束不够强,或者模型档位太低。解决:用工具调用强制结构化输出,或者用“只返回 JSON,不要任何其他文字”这类强约束,并在解析端做容错。6. 进阶技巧:用工具调用做结构化输出与验证最后讲一个我常用的进阶技巧:把工具调用当成“结构化输出”的手段。很多人不知道,工具调用不仅能做事,还能强制模型按你定义的结构返回数据,这比在提示词里求它“返回 JSON”可靠得多。思路是这样的:定义一个“提交结果”的工具,参数结构就是你要的数据结构,然后让模型必须调用这个工具来给出最终答案。import anthropic client = anthropic.Anthropic() # 用工具 schema 强制结构化输出 extract_tool = { "name": "submit_extraction", "description": "提交从文本中抽取的结构化信息", "input_schema": { "type": "object", "properties": { "name": {"type": "string", "description": "人名"}, "company": {"type": "string", "description": "公司名"}, "role": {"type": "string", "description": "职位"}, }, "required": ["name", "company", "role"], }, } resp = client.messages.create( model="claude-3-5-sonnet-latest", max_tokens=1024, tools=[extract_tool], tool_choice={"type": "tool", "name": "submit_extraction"}, # 强制调用 messages=[{ "role": "user", "content": "张三在某某科技担任后端工程师。", }], ) # 直接从工具调用里取结构化数据 for block in resp.content: if block.type == "tool_use": print(block.input) # 已经是 dict,无需解析 JSON逻辑说明:tool_choice设为指定工具时,模型必须调用它,不会返回自由文本。block.input已经是解析好的字典,省去了 JSON 解析和容错。参数上,required字段要写全,模型会保证都填上。这个技巧在数据抽取、分类、表单填充场景里特别稳,比纯提示词方案可靠一个量级。验证方法也很直接:拿一批真实样本跑,统计结构化输出的成功率。如果某个字段经常缺失或格式不对,就回去改 schema 的描述,把字段含义写得更明确。我一般会维护一个小的测试集,每次改提示词或 schema 都跑一遍,防止改一处崩一片。一个我养成的习惯:任何要上线的提示词和工具定义,都先在测试集上跑够 100 条再发布,别凭感觉。这个习惯帮我挡掉了好几次线上事故。希望帮到你。本文还有配套的精品资源,点击获取