
1. OpenCode Agent 里 additionalProperties 到底卡住了什么如果你正在用 OpenCode 搭 Agent并且让模型按 JSON Schema 输出结构化数据那你大概率遇到过这两种情况要么模型多吐了一个字段整个对象被校验器直接拒掉要么你明明想限制字段结果模型塞了一堆onClick、customClass这种前端根本不认识的键程序在解析阶段就崩了。这两个问题的根子都在 JSON Schema 的additionalProperties这个关键字上。先把概念说清楚。additionalProperties是 JSON Schema 标准里用来控制「对象中未在properties里显式声明的属性是否合法」的开关。它有三个取值形态false表示不允许任何额外字段多一个就报错true表示随便加校验器不管传一个子 Schema 则表示额外字段可以存在但必须符合这个子 Schema 的约束。OpenCode 在它的工具定义里几乎每个工具都写了additionalProperties: false目的很直接——防止 AI 幻觉乱填参数保证前端只收到它能理解的字段。这篇面向的是已经在写 OpenCode Agent 提示词、需要让模型输出稳定 JSON 的开发者。我会用question工具和bash工具的真实结构做例子给你可复制的 schema 片段、可运行的 bash 校验命令并且通过 TaoToken 的统一 Key 通道把模型调用和校验串起来让你能自己回放整个流程。适合谁手上有 Agent 项目、被 JSON 校验报错折磨过、想让输出结构可控的人。读完你能自己写出带additionalProperties约束的 schema并用命令行验证模型返回到底合不合规。我试过把additionalProperties设成true图省事结果模型开始自由发挥返回里混进了界面不支持的字段前端渲染直接白屏。后来老老实实收紧才把输出稳定下来。下面按步骤拆。2. TaoToken 前置统一 Key 通道与 OpenCode 接入准备在动 schema 之前先把调用通道理顺。OpenCode Agent 要调模型你需要一个稳定的 API 入口和一把 Key。TaoToken 在这里的角色是统一 Key 通道——你不用为每个模型单独配一套鉴权和地址用同一把 Key 就能切换不同模型这对调试 JSON Schema 特别有用因为你可以快速换模型对比谁的输出更守规矩。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制保存好后面配置里要用。注意这个 Key 只在创建时完整显示一次丢了就重新建。然后确认你的接入地址。TaoToken 的 API 基址是 https://taotoken.net/api 注意这里不带任何查询参数配置 Base URL 时就用这个。模型对话的入口在 https://taotoken.net/api 如果你想先在网页上手动试一条 prompt 看模型怎么回可以用模型对话页面https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat 。长期跑编码和 Agent 任务的话Coding Plan 更划算入口是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan 。OpenCode 的配置通常走它自己的配置文件但底层就是 OpenAI 兼容的接口。你需要准备三件套Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiAPI Key 填你刚建的那把Model ID 按你要用的模型填比如claude-sonnet-4-5这类标识。这三件套在 OpenCode 的 provider 配置里对应baseURL、apiKey、model三个字段。如果你用的是 Claude Code 那套体系接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc 里面有 Base URL 和鉴权头的写法。控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentconsole 可以看调用量和余额。这里有个坑要提前说很多人把 Base URL 写成带/v1或者带一堆参数的地址结果请求 404。TaoToken 的基址就是https://taotoken.net/api路径拼接交给 SDK 或 OpenCode 自己处理。Key 的鉴权头是标准的Authorization: Bearer 你的Key别自己发明格式。配置好之后先用一条最简单的 curl 确认通道是通的再进 schema 环节。通道不通后面所有校验都是白搭。3. 可复制配置question 与 bash 工具的 schema 片段现在进入正题。OpenCode 的question工具它的parameters是一个对象properties下面有questions字段类型是 array每个 item 是一个问题对象。这个 item 对象里required规定了三个必填字段question、header、options。而additionalProperties: false就加在这个 item 对象层级上意思是每个问题对象除了这三个字段不许再出现别的键。先给你一份可以直接抄的 JSON Schema 片段对应question工具里单个问题的结构{ type: object, properties: { question: { type: string, description: 展示给用户的完整问句例如您希望使用哪种数据库 }, header: { type: string, maxLength: 30, description: 简短标签用于侧边栏或步骤条例如数据库选择 }, options: { type: array, items: { type: object, properties: { label: { type: string, description: 选项上显示的简短文字 }, description: { type: string, description: 对该选项的详细说明 } }, required: [label, description], additionalProperties: false } }, multiple: { type: boolean, default: false, description: 是否允许多选默认单选 } }, required: [question, header, options], additionalProperties: false }注意两个层级的additionalProperties: false一个是问题对象本身一个是options数组里的每个选项对象。选项对象只允许label和description模型要是给你塞个onClick或者customClass校验直接失败。这就是 OpenCode 防幻觉的核心手段。再看bash工具。它的描述文件在opencode/packages/opencode/src/tool/bash.txt工具定义同样带additionalProperties: false。bash 工具和终端里直接敲 bash 的区别在于它先检查后执行工作目录默认在${directory}运行不用手动cd而且被明确禁止用于文件读写那是其他专用工具的活只负责跑git、npm这类命令。路径处理上提示词强制要求给带空格的路径加双引号。它跑在一个持久 shell session 里上次设置的环境变量下次还能用。如果你在 OpenCode 里用 TOML 或 settings 形式配置 provider可以这样写路径按你本地 OpenCode 配置目录来通常是项目根或用户配置目录下的配置文件[provider.taotoken] baseURL https://taotoken.net/api apiKey sk-你的TaoToken密钥 model claude-sonnet-4-5三件套齐了Base URL、Key、Model ID。这里 Model ID 按你实际要调的模型填别照抄。配置里不要加/v1不要加多余查询串。把 schema 和 provider 配置放一起你的 Agent 在生成工具调用参数时就会受additionalProperties: false约束。模型如果试图多输出字段校验层会拦下来你就能在日志里看到具体是哪个字段越界了。4. 验证请求用 bash 回放校验结果配置写完不算完得验证。最直接的办法是用 bash 发一条请求把模型返回的 JSON 抓下来再用校验器跑一遍。下面给你一套可复制的命令。先设环境变量避免 Key 写死在命令里export TAOTOKEN_API_KEYsk-你的TaoToken密钥 export TAOTOKEN_BASE_URLhttps://taotoken.net/api然后发一条让模型按 schema 输出问题对象的请求。这里用 curl 模拟 OpenAI 兼容的 chat completions 调用curl -s ${TAOTOKEN_BASE_URL}/v1/chat/completions \ -H Authorization: Bearer ${TAOTOKEN_API_KEY} \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-5, messages: [ { role: user, content: 请生成一个向用户询问数据库选择的问题对象只输出 JSON字段为 question、header、optionsoptions 每项含 label 和 description。 } ], response_format: { type: json_object } } response.json拿到response.json后把里面的 content 抽出来存成payload.json。如果你本地有jq可以这样抽jq -r .choices[0].message.content response.json payload.json接着用 Python 的jsonschema库做校验。先装库pip install jsonschema把第 3 节的 schema 存成question.schema.json然后跑校验脚本python3 - PY import json from jsonschema import validate, ValidationError with open(question.schema.json) as f: schema json.load(f) with open(payload.json) as f: data json.load(f) try: validate(instancedata, schemaschema) print(PASS: 输出符合 schema无额外字段) except ValidationError as e: print(FAIL:, e.message) print(出错路径:, list(e.absolute_path)) PY如果模型老老实实只输出question、header、options你会看到PASS。如果它多塞了一个字段比如id或者type你会看到类似Additional properties are not allowed (id was unexpected)的报错并且路径会告诉你越界字段在哪一层。这就是回放校验的价值——你能精确定位是模型幻觉还是你的 schema 写漏了。想对比不同模型谁更守规矩把model字段换掉再跑一遍就行Key 和地址都不用动这就是统一 Key 通道的便利。验证模型输出是否稳定也可以直接在模型对话页面手动试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat 。实测下来additionalProperties: false加上response_format: json_object能把大部分乱填字段的情况挡在解析之前。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth配 schema 和调通道的过程中报错基本集中在几个地方。逐个对照。401 Unauthorized。这个最常见九成是 Key 的问题。检查三件事Key 有没有复制完整前后有没有空格、鉴权头是不是Authorization: Bearer sk-xxx格式、Key 有没有被删或者过期。如果你在 OpenCode 配置里写的是apiKey字段确认它被正确读进了请求头。还有一种情况是 Base URL 写错导致请求打到了别的服务返回 401这时候核对https://taotoken.net/api有没有拼错。local proxy failed。这个报错通常出现在你本地配了某种转发或者代理层但目标地址不可达。先确认你的 Base URL 是直连https://taotoken.net/api不要经过额外的本地转发。如果你在 OpenCode 或 Claude Code 里配了自定义 endpoint检查它有没有被覆盖成localhost之类的地址。把配置里的 baseURL 显式写成 TaoToken 的地址重启客户端再试。reading choices 报错比如Cannot read properties of undefined (reading choices)。这说明你拿到的响应体里没有choices字段通常是请求根本没成功返回的是错误对象但代码直接去读choices[0]了。排查方法先把原始响应打印出来别急着解析。用 curl 加-i看 HTTP 状态码和响应体确认是 200 还是 4xx。如果是 4xx看错误信息里说了什么。常见原因是model字段填了一个不存在的 Model ID或者response_format用法不被该模型支持。OAuth 相关报错。如果你用的是 Claude Code 那套可能会碰到 OAuth 鉴权流程的问题。Claude Code 的接入方式和纯 API Key 不同需要按接入文档配置。文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc 。如果你同时用 CC Switch 或 Cline MCP 这类工具记住三件套要写全Base URL、Key、Model ID缺一个都会鉴权失败。Codex 的auth.json里也是同样三件套字段名按各自规范来别混用。还有一个 schema 层面的坑additionalProperties拼写。注意标准写法是additionalProperties不是addtionalProperties少个 i。网上很多文章标题写错你复制的时候要核对。写错了校验器不认等于没约束模型照样乱填。排查顺序建议先 curl 确认通道通再看响应体结构最后才怀疑 schema。通道问题占八成schema 问题占两成。6. 把 Key 通道和 schema 校验固定成流程走到这里你手上应该有一套能跑通的东西TaoToken 的 Key 和 Base URL 配好了question和bash工具的 schema 片段能复制了bash 校验命令能回放结果了常见报错也知道怎么对照了。剩下的就是把它固定成日常流程。我的做法是每次改完 schema先跑一遍 curl 抓样本再用 jsonschema 校验通过了才接进 OpenCode 的正式流程。模型换版本的时候也跑一遍对比看新模型是不是还守additionalProperties: false的规矩。Key 就用同一把地址不变省得每次重新配。如果你要长期跑 Agent 任务Coding Plan 的入口在这里https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan 。需要新建或管理 Key 就去 https://taotoken.net/api-keys 控制台看用量在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentconsole 。接入细节查文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc 。最后提醒一句additionalProperties收紧之后模型偶尔会因为「想多给点信息」而触发校验失败这时候别急着放宽约束先看它多出来的是什么字段。如果是你 schema 漏定义的合理字段补进properties如果是纯幻觉就让它失败失败日志本身就是最好的调试信号。