
这周有个朋友问我我已经在 Claude 的网页对话里把提示词调得很好了为什么一写进代码就到处报错他遇到的情况很有代表性。先是请求打到一半返回529 overloaded后面跟着一句 this is a server-side issue, usually temporary接着他把一段很长的文档塞进上下文得到一个 400提示maximum context length is 1048576 tokens再后来他想装 Claude Code 做辅助命令行却提示claude 不是内部或外部命令也不是可运行的程序或批处理文件。这三件事放在一起恰好暴露了一个容易被忽略的事实Prompt 评估和程序运行是两种完全不同的能力。在一套面向 Claude 应用架构师的前置课程里Part 5 的主题就是 Prompt Eval to Running从提示词评估走向真正运行。很多人以为这一步的重点是把提示词打磨得更好实际上这一步真正练的是怎么让一个依赖大模型的程序在真实环境里稳定、可重复、成本可控地跑起来。我的核心判断是从 Prompt 评估到真正运行中间缺的不是“更好的提示词”而是一层工程外壳。它是输入校验、异常处理、上下文预算、重试策略、日志追踪、回归评测和上线监控的集合。提示词决定模型能不能完成任务工程外壳决定系统能不能反复完成这个任务。你迟早要补上它区别只在于主动补还是被线上事故逼着补。1. 先分清Prompt 评估和“程序运行”是两种完全不同的能力1.1 为什么网页里效果好一写进代码就到处报错在网页对话或者在线评估界面里你能做到很多“隐形操作”输入不合适你会手动改写上下文太长界面会帮你截断一次结果不好你直接重新生成模型没按格式输出你心里自动修正。这本质上是一种人机协作模型负责生成人负责兜底。但程序运行没有这个“人”。一旦把提示词写进代码它变成一串固定模板输入变成程序里的变量重新生成变成一次函数调用上下文管理变成截断或摘要策略格式修正变成校验和重试逻辑。过去靠人临场发挥的部分现在必须提前设计出来。所以在网页里“效果好”只能证明模型在这个输入上有能力完成任务。它不能证明你的程序能稳定处理这一类任务。单次成功是“可能事件”系统稳定运行是“概率事件”这两件事之间隔着一整套运行时设计。1.2 评估阶段要回答的不是“推荐哪个提示词”而是“边界在哪里”做 Prompt 评估时不要只问“这个提示词效果好不好”要问四个更具体的问题。第一是正确性核心任务能不能完成输出内容是否准确。第二是稳定性同样输入多跑几次结果是否稳定尤其要关注 temperature 偏高时会不会偏离。第三是格式一致性要求输出 JSON 就真的输出 JSON而不是在代码块里包一层。第四是边界输入变长、变复杂、变成多轮之后任务会在哪里崩。记录评估结果时我建议用一张最简单的表用例 ID输入描述期望结果实际结果是否通过备注E-01正常提问返回结构化 JSON通过是耗时 1.2sE-02超长文档给出摘要报 context 超限否需要截断策略E-03恶意输入拒绝回答拒绝是安全边界正常这张表的价值不在于记录“好”而在于记录“边界在哪里”。你后续的所有工程改造都应该围绕这些失败用例展开。1.3 为什么它会被放在架构师路径的前置位置架构师设计的是系统不是提示词。如果一个设计者没有见过 529、没有处理过上下文超限、没有体验过限流和格式校验失败设计出来的架构就很可能是“理想情况下的架构”。放在前置课程里目的不是让你记住 API 的每一个参数而是让你先建立起对运行时失败的体感。知道模型调用会以哪些方式失败知道失败之后有哪些处理手段知道成本、延迟、稳定性的三角关系才谈得上做架构决策。这一节看着像基础实际上决定了你后面设计 Agent、设计多工具调用、设计评估体系时会不会踩空。2. 把“对话”变成“代码”从最小可运行链路开始2.1 环境准备API Key、SDK、还有那个总让人卡住的 CLI在开始写业务逻辑之前先确认三件事API Key 可用、SDK 或 HTTP 客户端可用、CLI 工具能用。API Key 通过环境变量注入最常见的变量名是ANTHROPIC_API_KEY。不要在代码里硬编码也不要把 Key 提交到 Git 仓库。这是底线不是建议。Claude Code 这类 CLI 工具常见的安装方式是 npm 全局安装。如果你遇到claude 不是内部或外部命令通常不是工具坏了而是安装路径没进 PATH。优先检查 Node.js 版本、npm 全局 bin 目录是否在 PATH 中然后重新打开终端再试。这类问题在 Windows、macOS、Linux 上出现的概率都不低但排查思路是一样的先看环境再看安装日志。还有一个很容易踩的坑模型名。不同版本的 API 支持不同的模型名当你用第三方网关或者换了 API 版本时经常会看到类似the supported api model names are ...的报错。这时候要做的不是怀疑提示词而是先核对端点、模型名和协议版本。模型名不匹配提示词写得再好也没有用。2.2 一条最小调用确认消息链路是通的很多人一上来就写很复杂的多轮对话逻辑结果第一行请求就报错。更稳妥的做法是先用一条消息把链路跑通。一个常见的 HTTP 调用结构是这样的curl https://api.anthropic.com/v1/messages \ -H x-api-key: $ANTHROPIC_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-5, max_tokens: 1024, messages: [ {role: user, content: 用一句话说明 RESTful API 的核心设计原则} ] }如果你用官方 SDK代码会更短from anthropic import Anthropic client Anthropic() # 默认读取 ANTHROPIC_API_KEY resp client.messages.create( modelclaude-sonnet-4-5, max_tokens1024, messages[ {role: user, content: 用一句话说明 RESTful API 的核心设计原则} ], ) print(resp.content[0].text)注意两点。第一max_tokens是必填参数它不只是一个“建议值”而是请求结构的一部分漏掉会直接报错。第二响应里除了content还有stop_reason和usage。usage里包含input_tokens和output_tokens这是你后面做成本核算和上下文管理的基础数据。先用一条消息确认链路是通的再谈 Agent、多工具、批量任务。链路没通之前所有优化都是空中楼阁。2.3 建议从一开始就做流式输出单次调用和流式调用不是一个“用户体验优化”的问题而是一个运行时决策。不开启流式服务端要等完整响应生成完才返回。输出越长等待越久超时的概率越高。开启流式之后响应以事件流的方式逐步返回你可以拿到content_block_delta之类的增量事件逐块拼接最终内容。流式带来的工程代价是你需要处理增量拼接、判断流何时结束、在服务端把缓冲区不断累加。但换来的是更可控的超时、更低的首字延迟以及更好的用户体验。如果一开始就按“非流式”把代码写死后面想改成流式往往要重构调用层。不如在最小链路阶段就把流式接进去之后所有业务逻辑都基于最终拼接结果编写两边都不耽误。3. 让系统稳定的不是模型而是异常处理与上下文管理3.1 最常见的几类报错先把类型分清楚从网上大量讨论来看大家遇到的报错高度集中。把这些报错按类型分清楚比逐条搜索解决方案更高效。状态码/现象典型信息发生阶段处理思路400maximum context length is 1048576 tokens请求校验压缩上下文、截断、分块400model name not recognized / not supported请求校验核对模型名与端点版本401 / 403authentication / permission鉴权检查 Key、权限范围429rate limit限流退避重试、降低并发529overloaded, server-side issue, usually temporary服务端过载指数退避重试socket 断开connection closed unexpectedly网络层检查网络、超时、增加重试529 是服务端过载通常确实是临时的。这时候最忌讳的是“立刻全力重试”因为服务端已经被压垮你再加重并发只会让恢复更慢。正确的做法是按退避策略重试。400 context length 是上下文预算问题不是提示词问题。它会直接告诉你窗口上限比如 1048576 tokens。这个问题在长文档、批量任务里非常常见必须在请求发出之前就管理好输入长度。socket 断开这类问题通常发生在网络不稳定的环境里。先检查你的连接超时、读取超时设置再看日志里请求到底是在哪个阶段断的。不要一上来就怀疑模型或者提示词。遇到问题时我建议按这个顺序排查先复现并记录现象再看输入包括格式、编码、长度、文件路径然后看环境包括依赖版本、网络、权限接着看参数包括模型名、max_tokens、超时、并发最后才看工具边界包括版本限制和已知缺陷。大多数“奇怪报错”在第二步和第三步就能定位。3.2 上下文窗口1M 是上限不是建议值maximum context length is 1048576 tokens这个错误提示最容易让人误解的地方在于看到 1M 就觉得可以随便塞。实际上上下文窗口是所有 token 的总预算包括 system prompt、历史消息、工具定义、用户输入以及你要求模型生成的输出。也就是说输入和输出共享同一个窗口。如果你已经塞了接近 1M 的输入模型几乎没有空间生成输出请求必然失败。所以在设计请求时要给输出预留空间。一个保守的做法是把窗口的 80% 作为输入预算上限剩下的留给输出。在计算输入预算时不要按字数估算。中英文的 token 密度不一样不同模型的分词方式也可能不同。最稳妥的方式是用官方提供的 token 计数工具或者在发送前让 SDK 帮你估算。如果一次请求的上下文会动态增长一定要在代码里检查“当前消息总长度”超出预算就走截断或摘要。常见的截断策略有三种长文本先摘要把原始内容压缩成关键信息多轮对话只保留最近几轮更早的内容压缩成概要system prompt 永远保留因为它定义了任务规则。从常见实践看模型对上下文中间部分的信息关注度相对偏弱所以重要信息尽量放在开头或结尾不要埋在超长文本的中段。3.3 重试、退避、超时一套保守但有效的默认策略不是所有错误都值得重试。一个干净的判断标准是400 和 401 这类请求本身有问题的错误不要盲目重试先修请求429、529、socket 断开这类临时性错误才适合重试。重试要带退避不要立刻重来。一个常见的写法是第一次等待 1 到 2 秒然后 4 秒、8 秒指数增长再加上随机抖动避免多个请求同时重试造成“重试风暴”。重试次数要有上限默认 3 到 5 次就够超过之后应该进入人工告警而不是无限循环。超时设置也要分成两层连接超时和读取超时。连接超时解决“连不上”的问题读取超时解决“连上了但一直不返回”的问题。流式场景下超时通常按“多久没有新数据”来判断而不是按“整个请求总耗时”因为长输出本来就需要较长时间。一个示意性的重试结构如下import time import random def call_with_retry(fn, max_retries4): for attempt in range(max_retries): try: return fn() except Exception as e: # 只有临时性错误才继续重试 if getattr(e, status_code, None) not in (429, 529): raise wait 2 ** attempt random.random() time.sleep(wait) raise RuntimeError(请求持续失败超过重试上限)注意不要一上来就把并发和重试次数拉满。先用一条样例确认输入、输出和日志都正常再逐步增加压力。4. Prompt 优化要变成工程动作建立回归评估集4.1 先建一个小而真实的评估集没有评估集的 Prompt 优化本质上是在凭感觉做事。你今天改了一个词觉得效果好了很可能只是恰好碰上这个输入运气好换个输入可能反而变差。要避免这种“灵光一现型优化”就需要一个固定的评估集。评估集不需要大10 到 30 个用例就够起步。关键是覆盖三类场景正常任务、边界任务、对抗任务。正常任务代表你的核心业务边界任务包括超长输入、空输入、多轮对话、特殊格式对抗任务包括恶意输入、越狱尝试、与任务无关的请求。建立评估集时最好从真实日志里捞输入而不是自己凭空编。那些线上失败的 bad case一定要回流到评估集里。一个不能复现线上问题的评估集价值会大打折扣。评估集小一点没关系但必须反映真实任务分布。宁可 20 个真实用例也不要 200 个自编用例。4.2 用人工与自动两条路径交叉验证评估可以分两层。第一层是自动检查。如果输出要求是 JSON就写一个校验器检查能不能解析、字段是否齐全、枚举值是否合法、长度是否在范围内。自动检查的价值在于快每次改动 Prompt 之后都能立刻跑一遍。第二层是人工打分。自动检查只能判断“结构对不对”判断不了“内容好不好”。人工打分可以围绕准确性、完整性、安全性和语气几个维度给分。10 到 30 个用例人工跑一遍其实花不了太多时间但它能给你一个稳定可比的分数。现在也有人用“另一个模型当裁判”来做自动评估这个做法可以用但要注意裁判模型本身也有偏好和误判关键场景仍然需要人工抽检。把两种情况结合自动检查保证格式和硬性规则人工打分保证内容质量两条路径交叉验证。4.3 改动提示词之后先跑回归再决定是否上线当你改进一个提示词时最常遇到的情况是A 用例变好了B 用例变差了。这不代表你的改动是失败的它只说明改动有取舍。关键问题是这个取舍你知不知道跑回归的意义就在这里。每次改动 Prompt 或参数都跑同一套评估集记录通过率和关键用例的变化。只要你能说出来“这次改动让 E-02 从失败变通过但 E-07 从通过变失败”决策就有了依据。如果连这个都说不出来那就等于没有验证。Prompt 本身也要做版本管理。把每一版 Prompt 写成文件和评估结果一起放进 Git 仓库。改 Prompt 和改代码一样要能回退、能对比、能追溯。否则你很难回答“上周那个效果很好的版本到底长什么样”这种基本问题。5. 上线之前还需要补齐这几块工程拼图5.1 密钥与配置不要把 Key 写进代码API Key 泄露的后果不需要多解释。把它放在代码里尤其是放在会被提交到仓库的代码里等于把钥匙贴在门框上。生产环境至少要做到开发、测试、生产使用不同的 Key不同服务使用不同的 Key方便单独吊销按最小权限分配权限范围。如果某个 Key 泄露要能快速吊销而不影响其他服务。另外日志里不要打印完整请求头因为请求头里可能包含 Key。如果必须打印请求信息要先把敏感字段打码。5.2 日志、追踪与成本要能回答“这个请求发生了什么”线上出问题时你需要的不是“重新跑一遍试试”而是能从日志里还原当时发生了什么。一条完整的调用日志至少应该包含时间戳、模型名、请求 ID、输入 token 数、输出 token 数、延迟、stop_reason、错误类型。为什么要把 token 数记下来因为成本等于 token 数乘以单价。没有 usage 数据你就不知道一个批量任务到底花了多少钱也不知道哪个业务线在烧钱。日志不只是为了排错还是成本治理的基础。建议给每个业务设置月成本预算和告警一旦某个接口的 token 消耗异常立刻能定位到具体请求和调用方。大模型应用上线后成本失控是比功能 bug 更常见的隐形问题。5.3 并发、限流与排队别等报 429 才想起来API 平台通常有速率限制按每分钟请求数RPM和每分钟 token 数TPM计算。你在网页里测的时候感觉不到一上线多用户同时请求429 就会冒出来。客户端要做自己的并发控制。常见的方式是队列加工作线程请求进队列工作线程限制并发数逐个执行。批量任务不要一次性全部提交而是分批提交观察错误率和延迟再逐渐加量。监控端不要只看平均值要看 p95 和 p99 延迟。平均值漂亮不等于体验稳定长尾请求往往才是用户真正感知到的卡顿。5.4 输出校验与兜底模型输出不是契约模型输出和数据库查询不一样它不是合同即使你说了“必须输出 JSON”它也有概率在输出里夹杂解释、Markdown 代码块或者直接把 JSON 截断。所以输出校验必须有。拿到内容之后先尝试解析再校验关键字段。如果校验失败一个常见的补救方式是“把错误信息反馈给它让它重新输出一次”。这种 one-shot 修正对格式问题通常有效但要设置次数上限不能无限循环。如果多次修正仍然失败就要有兜底策略。按风险从低到高排列返回固定文案、降级到更小的模型、让用户重新发起、进入人工处理队列。没有兜底的系统一旦模型输出异常整个业务流程就会卡死。6. 从评估到运行一份五步检查单6.1 五步框架与每一步的过关标准把前面所有内容收拢成一份可执行的检查单适合从零开始接入 Claude API 的场景。步骤主要动作过关标准1. 固定评估集收集 10 到 30 个代表用例覆盖正常、边界、对抗三类场景2. 最小链路跑通用一条消息完成 API 调用正常返回日志有 usage 和 stop_reason3. 覆盖异常路径为 529、400、429、超时写处理逻辑故障发生后系统能恢复不卡死4. 回归验证改动 Prompt 或参数后跑评估集关键用例不劣化通过率不下降5. 小流量上线5% 到 10% 流量灰度加监控错误率、延迟、成本都在预算内这五步不一定要按顺序严格走完但每一步都应该有一个明确产出。快速上线的前提是前四步已经有了可验证的结果。6.2 什么时候可以大胆上线什么时候必须保守如果应用是单轮请求、固定输入、输出可以被机器严格校验、并且有人员复核环节这类低风险场景可以相对快速上线。即使模型偶尔出错影响也在可控范围内。但如果应用涉及多轮对话、Agent 自主决策、外部工具调用、面向终端用户的开放式内容生成就必须保守。这类场景里模型的一次错误会被后续步骤放大最终结果可能完全不可控。灰度、监控、回退机制不是可选项而是上线条件。还有一个前置条件清单密钥管理是否到位、网络是否稳定、成本预算是否设置、日志是否完备。任何一项缺失都不建议直接进入生产。6.3 建议你现在就去做的最小实验如果看完这篇你只想做一件事我建议做这个选一个真实的小任务固定输入、固定输出格式用一条消息把 Claude API 调用链路打通记录 usage然后故意制造一次 400 或 529看看你的程序能不能恢复。这个实验会把本节讨论的所有问题都暴露出来你会发现上下文怎么算、错误怎么处理、日志要记什么、重试要怎么写。然后再建一个 10 个用例的评估集跑一次基线。做完这些你再去设计更复杂的架构会踏实很多。从评估到运行本质上是把“不确定的模型