Snowflake 托管编码代理的三个新旋钮:思考强度、系统提示、Stage 挂载
Snowflake 2026 年 10 月 28 日那条发布说明只有四段,很容易一扫而过。它给 Cortex Agents 的 Coding Agent 加了三个公开控制项——这个托管服务把你的应用接上一个沙箱化编码循环(bash、文件读写、grep、glob、联网搜索、SQL 执行、skills),而你不用自己搭 harness。三个控制项是 thinking_effort、instructions.system、stage_mounts。它们听起来都像「偏好设置」。实际上其中两个是成本与复现性决策,另一个做的事比名字暗示的要少。
数据中心里的机架,代表托管代理沙箱
一、每一项到底控制什么
thinking_effort 设置编排模型的推理强度,接受 minimal、low、medium、high、xhigh、max。每个模型只支持其中一个子集,而且——这句话要读两遍——超出子集的请求会被降到该模型接受的最高档。省略该字段则保持模型默认。instructions.system 在托管的编码代理提示词后追加系统指令,文档明确说它不替换沙箱、工具或 skill 的指令。stage_mounts 设在 tool_resources 的 code_toolset_all 条目上,把 Snowflake stage 或带版本的 stage 挂进沙箱,带版本的 stage 包括 notebook、Streamlit 应用、workspace 和 agent。workspace_mounts 没有变,仍然以 live 版本挂载一个持久可写工作区。三项都作用于包含 code_toolset_all 工具的请求。
POST /api/v2/cortex/agent:run
Content-Type: application/json
Authorization: Bearer $SNOWFLAKE_TOKEN
{
"messages": [
{ "role": "user", "content": [{ "type": "text",
"text": "Model weekly churn from RAW.EVENTS and write the result to churn_weekly.csv" }] }
],
"models": { "orchestration": "<your-orchestration-model>" },
"instructions": {
"system": "Dialect is Snowflake SQL. Never SELECT *. Prefer CREATE OR REPLACE TEMP TABLE. State assumptions before writing code."
},
"tools": [
{ "tool_spec": { "type": "code_toolset_all", "name": "code_toolset_all" } }
],
"tool_resources": {
"code_toolset_all": {
"stage_mounts": [
{ "stage": "@DB.SCHEMA.SKILL_STAGE", "version": "v3", "mount": "/mnt/skills" }
]
}
},
"thinking_effort": "medium"
}
// thinking_effort accepts minimal, low, medium, high, xhigh, max.
// Each model supports a subset; a value above that subset is silently
// lowered to the highest level the model accepts. Omit it for the default.二、thinking_effort 是上限,不是调节滑杆
因为过高的值会被静默降级,你无法用 thinking_effort 证明实际跑的是哪一档。你在一个上限为 high 的模型上要 xhigh,拿到的是 high,而且没有报错。这影响两个地方:成本模型里,你以为买到了更多推理,其实没有;评测里,一个看起来没用的档位,可能压根没被触发过。修法简单到令人尴尬,却几乎没人做:在自己的任务上扫一遍档位,记下实际耗时与输出长度,把一条平线当作「超出子集」的证据,而不是当作「强度没用」的证据。
import json, os, time, urllib.request
URL = "https://<account>.snowflakecomputing.com/api/v2/cortex/agent:run"
BODY = {
"messages": [{"role": "user", "content": [{"type": "text", "text": TASK}]}],
"models": {"orchestration": MODEL},
"tools": [{"tool_spec": {"type": "code_toolset_all", "name": "code_toolset_all"}}],
}
def run(effort=None):
body = dict(BODY)
if effort:
body["thinking_effort"] = effort
req = urllib.request.Request(
URL, data=json.dumps(body).encode(),
headers={"Content-Type": "application/json",
"Authorization": "Bearer " + os.environ["SNOWFLAKE_TOKEN"]},
method="POST")
t0 = time.time()
raw = urllib.request.urlopen(req, timeout=900).read().decode()
return {"effort": effort, "seconds": round(time.time() - t0, 1),
"chars": len(raw), "body": json.loads(raw)}
# Sweep the levels you care about on the same task, three times each.
# A level that never changes latency or output length is not doing anything.
for level in [None, "low", "medium", "high"]:
runs = [run(level) for _ in range(3)]
print(level, [r["seconds"] for r in runs], [r["chars"] for r in runs])三、instructions.system 是追加,不是覆盖
这是这次发布里最让人安心、也最容易被误解的一点。你的系统文本是追加的,所以它剥不掉沙箱或工具契约——这是个好的安全属性。反面是它也无法覆盖工具行为,而且它和代理拉进上下文的不可信内容共处同一个提示词:挂载 stage 里的文件、抓来的网页、查询结果。把它当成品味、方言和验证习惯的放置处(比如「写代码前先声明假设」),永远不要用它划安全边界,边界应该放在工具白名单和挂载权限里。如果你的用户文本可能包含「忽略前面的指令」,在它进入对话前就清掉。
# Versioned stage vs live workspace: pick per artefact, not per habit.
# stage_mounts -> read-only view of a versioned stage (notebook, Streamlit app,
# workspace, agent). Same bytes every run. Use for skills,
# reference notebooks, and anything an audit may replay.
# workspace_mounts -> persistent, writable, at version "live". Use for scratch
# output the agent is expected to keep between runs.
tool_resources = {
"code_toolset_all": {
"stage_mounts": [
{"stage": "@ANALYTICS.GOV.APPROVED_SKILLS", "version": "v7",
"mount": "/mnt/skills"}, # read-only, pinned
{"stage": "@ANALYTICS.GOV.REF_NOTEBOOKS", "version": "v12",
"mount": "/mnt/ref"}, # read-only, pinned
],
"workspace_mounts": [
{"stage": "@ANALYTICS.SCRATCH.WS", "mount": "/mnt/work"} # live, writable
],
}
}
# Rule of thumb: if losing the mount would change an answer next month,
# it belongs in stage_mounts with an explicit version.四、复现性住在 stage_mounts 里
两种挂载并存,回答的是不同问题。workspace_mounts 给你一个 live 版本的持久可写工作区:适合代理正在迭代的中间 CSV,但作为证据没用,因为下个月 live 已经是别的东西了。带明确版本的 stage_mounts 每次给你同样的字节。所以拆分很简单:任何「消失就会改变答案」的东西——skills、参考 notebook、已批准的查询模式——进 stage_mounts 并钉住版本;代理可以随便乱写的进 workspace_mounts。如果你说不清某个产物属于哪一边,通常说明它该被版本化。
from pydantic import BaseModel, field_validator
class RunRequest(BaseModel):
user_text: str
system_instructions: str
@field_validator("user_text")
@classmethod
def strip_control_claims(cls, v: str) -> str:
banned = [
"ignore previous instructions",
"you are now",
"new system prompt",
"disregard the sandbox",
]
low = v.lower()
hits = [b for b in banned if b in low]
if hits:
raise ValueError("untrusted text tries to edit the managed prompt: %r" % hits[0])
return v
# instructions.system is appended to the managed coding-agent prompt; it does not
# replace the sandbox, tool or skill instructions. That is good news: you cannot
# accidentally strip the guardrails. It is bad news only if you were counting on
# a system instruction to override a tool's behaviour -- it will not.五、上线之前先把旋钮量一遍
面对新控制项,本能是使劲拉满。请反过来做:同一任务每档跑三次,记录实际耗时、输出长度和你自己的通过率,把中位数与 p90 发布到代理其它 SLI 旁边。两种模式值得采取行动。如果提高强度没有任何可测变化,那档在模型支持的子集之上,你在为一个标签付钱。如果它让 p90 翻倍而通过率不动,你是在用延迟换没有证据的质量。这时再定默认值——并且把你用的判据写下来,因为下一个模型发布会移动子集,而旧假设不会自己通知你。
import statistics
def compare(rows, key="seconds"):
out = []
for level in sorted({r["effort"] or "default" for r in rows}):
vals = [r[key] for r in rows if (r["effort"] or "default") == level]
out.append({"effort": level, "n": len(vals),
"median": round(statistics.median(vals), 1),
"p90": round(sorted(vals)[int(len(vals) * 0.9) - 1], 1) if len(vals) > 1 else vals[0]})
return out
# Publish this table next to the agent's other SLIs, not in a wiki page.
# Two failure patterns to look for:
# 1. raising effort changes nothing -> the level is above the model's subset
# 2. raising effort doubles p90 and does not improve the pass rate -> stop paying
CRITERIA = {
"accept_new_default": lambda base, cand: (
cand["p90"] <= base["p90"] * 1.5 and cand["pass_rate"] >= base["pass_rate"]
),
}六、能扛过下一次模型发布的铺开方式
第一周:把喂给代理的每个挂载钉死,把无版本引用改成带版本的 stage,并记下哪些产物无法版本化。第二周:搭好扫描脚本,在最重要的任务上拿到两到三档的真实数字。第三周:把数字搬进看板,按你写下的判据设默认值,把请求体放进版本控制,让评审能同时看到强度与挂载版本。第四周:编排模型一换就重跑扫描。这些都不光鲜,但它决定了你拥有的是一个可审计的代理,还是一个行为已经悄悄变了却没人发现的代理。
延迟与成本指标看板
抽象的网络节点连接图
📌 常见问题 FAQ
Cortex Agents Coding Agent 新增的三个控制项是什么?
thinking_effort、instructions.system,以及 stage_mounts。它们作用于包含 code_toolset_all 工具的 agent:run 请求。
thinking_effort 接受哪些取值?
minimal、low、medium、high、xhigh、max。每个模型支持其中一部分,超出子集的请求会被降到该模型接受的最高档;省略该字段则保持模型默认。
instructions.system 会替换沙箱或工具指令吗?
不会。Snowflake 的发布说明写明它只是向托管编码代理提示词追加系统指令,不替换沙箱、工具或 skill 指令。
stage_mounts 与 workspace_mounts 有什么区别?
stage_mounts 把 stage 或带版本的 stage(notebook、Streamlit 应用、workspace、agent)挂进沙箱,内容被钉住、可复现。workspace_mounts 没有变化,以 live 版本挂载一个持久可写工作区。
为什么提高 thinking_effort 可能看不出效果?
因为请求的档位可能高于编排模型支持的子集,于是被静默降到最高可用档。只有逐档测量耗时与输出长度,才知道实际跑的是什么。