ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Claude Code 长运行应用开发:Harness design 的 settings.json 配置骨架与验证

2026/9/23 1:28:49 拓冰建站 浏览量
Claude Code 长运行应用开发:Harness design 的 settings.json 配置骨架与验证 1. 长运行任务为什么总在 settings.json 上翻车Claude Code 在 long-running application development 场景里跑长任务最容易出问题的不是模型能力而是 Harness design 没搭好。Harness design 说白了就是给 Claude Code 套一层线束权限怎么给、超时怎么设、日志往哪写、会话怎么续。这些全部落在settings.json里。你如果只把 Claude Code 当聊天窗口用跑个十分钟的小脚本没问题但一旦让它连续跑几小时去构建一个全栈应用权限弹窗会打断它、默认超时会掐死它、日志缺失会让你根本不知道它卡在哪一步。我试过用默认配置跑一个需要多轮迭代的前端生成任务结果 Claude Code 在第 40 分钟左右因为一次 Bash 权限确认没人点整个会话挂起前面的上下文全白费。这就是典型的 Harness design 缺失模型本身没问题是外围的配置骨架没给它留出长跑的跑道。这篇要解决的就是这件事。我会给出一份可以直接复制的settings.json骨架覆盖权限白名单、超时控制、日志落盘三个核心字段然后带你用一次真实的长任务运行去验证配置是否生效。适合已经在用 Claude Code、准备把它推进到长运行应用开发场景的开发者。读完你能拿到一份可用的配置模板以及一套验证动作知道每一步该看哪个文件、哪个日志、哪个返回码。需要说明的是Claude Code 的模型调用需要 API 凭证。我这边用的是 TaoToken 提供的接入方式它的 API 地址是https://taotoken.net/api兼容 Anthropic 的接口格式配置起来和官方 SDK 的写法一致。下面所有配置示例都基于这个接入点你可以直接替换成自己的凭证。2. 前置准备TaoToken 接入与 Claude Code 环境在动settings.json之前先把接入层理顺。Claude Code 通过环境变量读取 API 端点和密钥所以你要先拿到一个可用的 Key再把它写进环境。2.1 获取 API Key打开 TaoToken 的控制台进入 API Keys 页面创建一个新 Key。创建时注意两点一是给它起一个能识别用途的名字比如claude-code-longrun方便后面在日志里区分二是记下创建后一次性展示的完整 Key页面刷新后就看不到了。拿到 Key 之后把它写进 shell 的环境变量。我习惯放在~/.zshrc或~/.bashrc里这样每个新终端都能读到export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的Key写完执行source ~/.zshrc让配置生效然后用一条最简单的请求验证接入是否通curl -s https://taotoken.net/api/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: 64, messages: [{role: user, content: reply with ok}] }如果返回里带content字段且文本是ok说明接入层没问题。这一步别跳过因为后面settings.json里的所有配置都建立在接入可用的前提上接入不通的话你排查半天配置也是白搭。2.2 确认 Claude Code 版本与配置目录Claude Code 的配置分两层用户级配置在~/.claude/settings.json项目级配置在项目根目录的.claude/settings.json。长运行任务我建议用项目级配置因为不同项目的权限需求不一样混在用户级里容易互相污染。先确认版本老版本的字段名和新版本有差异claude --version然后确认配置目录存在ls -la .claude/ 2/dev/null || mkdir -p .claude echo created目录建好后我们就可以往里写骨架了。3. 可复制的 settings.json 配置骨架这一节是全文的核心。我把骨架拆成权限、超时、日志三块讲每块给出字段含义和取值理由最后拼成一份完整文件。3.1 权限字段让长任务不被弹窗打断长运行任务最怕的就是中途停下来等人确认。permissions字段就是干这个的。它的结构是allow和deny两个数组allow里的工具调用不会触发确认deny里的直接拒绝。{ permissions: { allow: [ Bash(git status), Bash(git diff:*), Bash(git add:*), Bash(git commit:*), Bash(npm run:*), Bash(npm install:*), Bash(pytest:*), Bash(python:*), Read(*), Write(src/**), Edit(src/**) ], deny: [ Bash(rm -rf:*), Bash(curl:* | sh), Read(.env), Read(**/secrets/**) ] } }这里有几个设计取舍值得说。allow里我用了Bash(git diff:*)这种带冒号的写法冒号后面的*表示匹配该命令的任意参数这样git diff HEAD~1和git diff main都能放行不用一条条列。Write和Edit我限定在src/**下是为了防止长任务跑偏去改配置文件或依赖锁文件。deny里必须放rm -rf和管道执行远程脚本这两类长任务里模型一旦判断失误执行了这类命令损失是不可逆的。.env和 secrets 目录也要挡住避免密钥被读进上下文再写进日志。注意allow的匹配是前缀匹配Bash(python:*)会放行python -c ...这种任意代码执行。如果你的长任务不需要跑任意 Python把它收窄成Bash(python -m pytest:*)更安全。3.2 超时字段给长任务留足跑道默认超时对长任务来说太短。timeout相关字段控制单次工具调用的等待上限和整个会话的空闲上限。{ timeout: { toolCallMs: 600000, sessionIdleMs: 1800000, bashDefaultMs: 300000 } }toolCallMs设成 60000010 分钟是因为长任务里一次npm install或一次全量测试跑几分钟很正常默认值会在中途掐断。sessionIdleMs设成 180000030 分钟给的是会话空闲容忍度模型在思考或等待外部进程时不会因为短暂无输出被判死。bashDefaultMs是 Bash 命令的默认上限5 分钟覆盖大多数构建和测试。这三个值不要盲目调大。toolCallMs调到一小时以上一旦某个命令真的卡死你要等一小时才能拿到失败信号。我的经验是先用 10 分钟跑一轮看日志里有没有接近上限的调用再决定是否上调。3.3 日志字段让长任务可观测长任务跑起来之后你看不到中间过程就等于盲跑。logging字段把关键事件落盘出问题时能回溯。{ logging: { level: info, file: .claude/logs/session.log, rotate: { maxSizeMb: 50, maxFiles: 5 }, includeToolCalls: true, includeToolResults: false } }level用info就够debug在长任务里会产生巨量日志拖慢 IO。file指向项目内的.claude/logs/方便和代码一起管理。rotate防止单个日志文件无限增长50MB 一个、保留 5 个足够覆盖一次几小时的长任务。includeToolCalls开、includeToolResults关是个折中你能看到模型调了什么工具、传了什么参数但不会把每次工具返回的大段内容都写进去。排查模型为什么走了这一步时调用记录比返回内容更有用。3.4 完整骨架文件把上面三块拼起来加上模型和会话相关字段就是完整的settings.json{ model: claude-sonnet-4-5, permissions: { allow: [ Bash(git status), Bash(git diff:*), Bash(git add:*), Bash(git commit:*), Bash(npm run:*), Bash(npm install:*), Bash(pytest:*), Read(*), Write(src/**), Edit(src/**) ], deny: [ Bash(rm -rf:*), Bash(curl:* | sh), Read(.env), Read(**/secrets/**) ] }, timeout: { toolCallMs: 600000, sessionIdleMs: 1800000, bashDefaultMs: 300000 }, logging: { level: info, file: .claude/logs/session.log, rotate: { maxSizeMb: 50, maxFiles: 5 }, includeToolCalls: true, includeToolResults: false }, context: { autoCompact: true, compactThreshold: 0.85 } }context这块是给长任务续命用的。autoCompact开启后上下文接近窗口上限时自动压缩历史compactThreshold设成 0.85 表示用到 85% 就开始压。长运行应用开发动辄几小时不压缩的话上下文早就爆了。把这份文件写到.claude/settings.json然后进入验证环节。4. 验证配置生效一次长任务运行配置写完不代表生效。这一节带你用一次真实的长任务逐项确认权限、超时、日志三个字段都按预期工作。4.1 构造一个会触发多轮工具调用的任务验证任务要足够长能触发权限、超时、日志三条路径。我用一个生成并测试一个小型 Python 包的任务来验证它会依次触发 Write、Bash(pytest)、Bash(git commit)claude -p 在 src/ 下创建一个 Python 包 mycalc包含 add 和 divide 两个函数divide 要对除零抛 ValueError。然后写 pytest 测试覆盖正常和异常路径跑通测试后 git add 并 commit。这条命令用-p进入非交互模式正好模拟长任务无人值守的场景。如果权限配置没生效它会在第一次 Write 或 Bash 时停下来等确认任务直接卡住。4.2 检查权限是否放行任务跑起来后另开一个终端看日志tail -f .claude/logs/session.log日志里应该出现类似这样的记录{ts:2025-01-15T10:23:11Z,event:tool_call,tool:Write,args:{path:src/mycalc/__init__.py},permission:allowed} {ts:2025-01-15T10:23:45Z,event:tool_call,tool:Bash,args:{command:pytest tests/},permission:allowed}关键是permission:allowed这个字段。如果看到permission:prompted说明你的allow规则没匹配上需要回去检查命令写法。比如pytest tests/要能被Bash(pytest:*)匹配如果你写的是Bash(pytest tests/:*)就匹配不到带其他参数的调用。4.3 检查超时是否按预期工作超时不好直接观察但可以通过一个故意跑长的命令来验证。在任务里加一步sleepclaude -p 执行 bash -c sleep 400然后告诉我完成了。sleep 400是 400 秒小于bashDefaultMs的 300000 毫秒300 秒不对400 秒大于 300 秒所以这条命令应该被超时掐断。日志里会出现{ts:2025-01-15T10:30:00Z,event:tool_result,tool:Bash,status:timeout,elapsedMs:300012}看到status:timeout且elapsedMs接近 300000说明bashDefaultMs生效了。如果你希望这类长命令能跑完就把它挪到一个单独的allow规则里并配更长的超时而不是全局调大bashDefaultMs。4.4 检查日志轮转与上下文压缩长任务跑完后确认日志文件按预期生成和轮转ls -lh .claude/logs/应该看到session.log以及可能的session.log.1等轮转文件。如果单次任务就产生了超过 50MB 的日志说明includeToolResults可能被误开了回去关掉。上下文压缩的验证看日志里的 compact 事件grep compact .claude/logs/session.log出现{event:context_compact,beforeTokens:180000,afterTokens:45000}这样的记录说明autoCompact在上下文接近上限时触发了压缩长任务能继续跑下去。5. 本篇常见错误排查配置跑不通时按下面这几类对照排查基本能覆盖九成问题。5.1 权限规则不匹配导致任务卡住最常见的现象是任务跑一半不动了日志停在某个tool_call没有后续。这通常是allow规则没匹配上。Claude Code 的权限匹配是精确到命令前缀的Bash(npm run:*)能匹配npm run build但匹配不了npx npm run build。排查方法是把日志里那条tool_call的args.command复制出来和你的allow规则逐条比对。另一个坑是Write(src/**)这种 glob 写法。不同版本对**的支持不一致保险起见可以写成Write(src/*)加Write(src/**/*)两条覆盖一层和深层目录。5.2 超时字段名写错导致不生效timeout下的字段名在不同版本里改过。老版本用toolTimeout新版本用toolCallMs。如果你写了老字段名配置不会报错但也不会生效任务还是按默认超时跑。排查方法是启动时加--debug看配置加载日志claude --debug -p test 21 | grep -i timeout如果输出里没有你配置的值说明字段名不对对照当前版本文档改。5.3 日志文件不生成或为空日志不生成通常是两个原因一是logging.file的目录不存在Claude Code 不会自动创建父目录二是level设成了error而正常任务没有 error 事件。先手动建目录mkdir -p .claude/logs再把level调成info重跑。如果日志生成了但内容为空检查includeToolCalls是否为true设成false的话工具调用不会记录日志里就只剩会话级事件。5.4 上下文压缩没触发导致任务中断长任务跑到后面报上下文超限但日志里没有 compact 事件说明autoCompact没开或compactThreshold设得太高。compactThreshold是比例值设成 0.95 意味着用到 95% 才压留给压缩本身的空间就不够了。建议设在 0.8 到 0.85 之间。另外确认context字段是顶层字段不要嵌在logging里。5.5 接入层报错被误判为配置问题如果任务一开始就报 401 或连接失败别急着改settings.json先回到第 2 节的 curl 验证接入。接入不通时Claude Code 的表现和配置错误很像都是任务起不来。区分方法是看日志里有没有tool_call事件有tool_call说明接入是通的问题在权限或超时一个tool_call都没有就报错问题在接入层。6. 把配置沉淀成可复用的骨架跑通一次验证之后建议把这份settings.json抽成模板按项目类型分几套。前端项目、后端服务、数据脚本的权限需求差别很大混用一套配置要么放得太宽要么卡得太死。我自己的做法是在仓库里放一个.claude/settings.template.json新项目初始化时复制成.claude/settings.json再按需改allow列表。模板里deny和timeout、logging三块保持不动只调权限白名单。这样既保证安全底线一致又给不同项目留了灵活度。另外长运行任务的成本要心里有数。一次几小时的自主构建token 消耗可能是短任务的几十倍。建议在settings.json里配好日志之后定期用日志统计工具调用次数和 token 用量找到哪些步骤在烧钱。如果发现某类工具调用频繁且收益低就把它从allow里拿掉逼模型换更高效的路径。配置骨架搭好只是 Harness design 的第一步。真正跑长任务时你还会遇到模型在某个功能上反复迭代、评估环节缺失导致质量下滑等问题。这些需要在骨架之上再加规划器和评估器角色但那属于下一层的设计先把这份settings.json跑稳再往上叠。