DSH上生产前的最后一关:凭据进了对话上下文、apiKeyEnv填错、会话日志落盘,踩了4个坑
DSH实战系列第4篇(最后一篇)。前三篇:第1篇选路(Skill+Python CLI),第2篇SKILL.md怎么写给LLM看,第3篇硬门禁怎么防LLM写翻车。
这篇讲运维层——生产DSH和玩demo的差别,全在这些不起眼的细节里。说难听点:前3篇的架构再漂亮,API Key跟着对话上下文发上云,一切白搭。
四个坑:凭据泄露未遂、配置字段填错、模型声明不全、日志落盘——全是我在自己机器上真踩过的,有截图级细节。
坑1:凭据跟着Agent的回复进了对话上下文
翻车现场
货盘Skill要连飞书和OMS,凭据(app_id/app_secret)放在skill目录的config.json里。某天我让Agent"看下配置,帮我确认OMS地址对不对"——它很忠实地读了配置文件,然后把整个app_secret复述进了对话,出现在它回复的草稿里。
后背发凉的地方想深一层:
-
这段对话的上下文是要发给云端模型的——凭据进了上下文=凭据出了你的机器
-
会话日志落盘——152个会话全存在
.dsh/sessions/目录里,secret跟着永久躺在磁盘上 -
模型厂商的日志留存策略你一无所知
Python人秒懂:这就是把密码写在settings.py里,然后随手print(vars(config))——任何一次调试输出都是泄露点。区别是LLM的"调试输出"你控制不了它什么时候发生。
根因
Agent对你的工作区有文件读权限,它分不清"配置"和"机密"——你让它看,它就看全量。而且这不能怪DSH:这是所有"Agent+本地文件"架构的共性问题。
修复:三层防御
① 存储层:env优先,config只是兜底
# 脚本里读凭据:环境变量优先
app_id = os.environ.get("FEISHU_APP_ID") or cfg["app_id"]
系统环境变量放key,config.json里的机密字段单独拆一个文件,gitignore掉。
② 行为层:红线写死在SKILL.md
## 红线(必须遵守) - 严禁在回复、日志、其他文件中出现密码。
我的禅道Skill第一版就把这条焊在红线第4条。配合一个技巧:需要验证凭据时,让LLM调脚本里的Test命令——脚本内部校验,只返回"通过/失败",不返回凭据本身。LLM有验证需求但拿不到明文。
③ 出口层:定期巡检
会话日志目录过一眼,涉敏项目的会话用完清掉。
| 防层 | 手段 | 挡住什么 |
|---|---|---|
| 存储 | env优先+机密文件独立+gitignore | 仓库泄露、配置误读 |
| 行为 | SKILL.md红线+Test命令模式 | 对话复述 |
| 出口 | 会话清理巡检 | 日志落盘残留 |
三层任何一层单独存在都不保险——红线上限90%(概率约束),env只管存储不管复述,清理只是兜底。叠起来才接近100%。
坑2:apiKeyEnv字段填了Key值本身
翻车现场
配置自定义模型provider时,DSH的配置长这样:
llm-pi-ai: providers: zai-coding-cn: apiKeyEnv: ZAI_CODING_CN_API_KEY # ← 注意这个字段 models: [...]
第一次配的时候,我盯着apiKeyEnv想当然:填key。填完Provider死活连不上,报MISSING_CREDENTIAL。排了半天——排的方向全是网络和权限,最后才反应过来问题在眼皮底下。
根因
字段名已经说清楚了:apiKeyEnv——要的是环境变量的名字,不是key的值。DSH读到ZAI_CODING_CN_API_KEY这个字符串,去系统环境变量里找同名变量取值。
Java同学应该会心一笑:${DB_PASSWORD}里填了明文密码——占位符语法里放值,是最经典的配置错误。这个字段的设计意图恰恰是配合坑1的防御:配置文件里永远只出现变量名,key本体永远不落盘。
修复
# Windows设环境变量(key本体只在这里) setx ZAI_CODING_CN_API_KEY "sk-xxxxxxxx" # DSH配置里只引用名字 # apiKeyEnv: ZAI_CODING_CN_API_KEY
顺带说我的完整模型接入方案(生产可用形态):
| 通道 | 配置 | 说明 |
|---|---|---|
| 主力DeepSeek | baseURL指向公司内网网关 | key在网关侧统一管理,本机配置零明文;公司环境直连不稳,中转还顺带做了缓存和审计 |
| 备用GLM系列 | apiKeyEnv引用环境变量 | 便宜模型跑批量小活:分类、格式转换、简单查询 |
| 按任务调推理力度 | reasoningEffort参数 | 复杂规划用high,批量小活用low——成本敏感时的第一个旋钮 |
多模型不是炫技是刚需:主力模型抽风/限流时切备用通道,业务不断。
坑3:模型声明不全——contextWindow没写
翻车现场
配备用provider的models列表,前几个模型我老老实实抄全了字段:
models:
- { id: glm-5.1, name: GLM-5.1, contextWindow: 200000, maxTokens: 131072 }
- { id: glm-5.2, name: GLM-5.2, contextWindow: 1000000, maxTokens: 131072 }
后来新加一个快速模型,图省事只写了两项:
- { id: glm-5.3-flash, name: glm-5.3-flash } # ← 少了字段
然后这个模型干长文档任务时表现诡异:有时正常,有时静默截断——上下文超了它不报错,直接把前面内容裁掉,任务输出莫名其妙丢信息。排查到这一行才想起来:没声明contextWindow,DSH不知道这个模型的真实容量,上下文管理算错了。
根因
DSH按你声明的contextWindow做上下文预算(该塞多少工具定义、该截多少历史)。声明缺失→用默认值猜→预算和真实容量对不上→要么浪费(预算太保守),要么截断(预算超容量)。静默截断最阴险:不报错,只有输出质量悄悄劣化。
修复
# 自定义模型的字段写全,一项都别省
- { id: xxx, name: xxx,
contextWindow: 200000, # 上下文容量——DSH做预算的依据
maxTokens: 131072 } # 单次输出上限
原则:配置文件里的每个字段都是宿主做决策的输入,你的"省事"就是它的"盲区"。改完之后长文档任务稳定——一个字段的事,排了一晚上。
坑4:会话日志落盘——152个会话都是敏感档案
翻车现场
某天清理磁盘才发现:.dsh/sessions/目录下152个会话文件静静躺着——每一次对话全文、每一条工具调用记录、每一份被读进上下文的文件内容。包括:早期不懂事时让Agent读过的配置文件(含明文secret)、公司内部表格数据、生产接口的返回内容。
根因
DSH的追记式事件日志(append-only,第1篇类比过数据库WAL)是它的核心设计——可回放、可审计,这本身是优点。但优点换个场景就是负债:本地单机用,敏感内容全量落盘且默认永不过期。
修复
三条纪律:
# ① 定期清理:涉敏项目的会话用完删(回放价值低) # ② 升级前备份.dsh整个目录(会话+配置+skill都在里面) robocopy %USERPROFILE%\.dsh %USERPROFILE%\.dsh_backup_v0.1.0-rc.8 /E # ③ profile隔离:desktop日常交互 / headless跑自动化——不同profile不同会话空间
第②条不是凑数——我升级DSH时配置被迁移逻辑动过(.dsh目录里至今躺着settings.yaml.bak和settings.yaml.imported两具"尸体")。rc版不保证配置向前兼容,升级前备份、升级后diff,是省不掉的仪式。
DSH生产检查清单(全系列收官)
四篇文章的内容浓缩成一张可勾选的清单,接生产前过一遍:
配置层
- API Key零明文:env变量或内网网关,本机配置文件里只有变量名
- 自定义模型字段写全:contextWindow/maxTokens一项不省
- 升级前备份整个
.dsh;生产链路锁DSH版本号
Skill层
- SKILL.md有凭据红线("严禁在回复中出现密码")
- 依赖已声明装好,禁止LLM现场pip install
- 命令模板可复制+状态机写死(第2篇)
安全层
- 写操作唯一入口+
--confirm-batch+PENDING_CONFIRM状态机(第3篇) - 门禁有专门测试,断言审计目录零写入(第3篇)
- MCP只挂只读工具,写入全走CLI(第3篇)
- 涉敏会话定期清理(本篇)
4坑速查表
| 坑 | 翻车 | 根因 | 修复 | 类比 |
|---|---|---|---|---|
| 凭据进上下文 | secret被复述进对话发上云 | LLM分不清配置和机密 | 三层防御:env优先+红线+Test命令+会话清理 | settings.py里print(vars) |
| apiKeyEnv填Key值 | Provider报MISSING_CREDENTIAL | 字段要的是变量名不是值 | key放系统env,配置只引用名字 | ${DB_PASSWORD}里填明文 |
| 模型声明不全 | 长任务静默截断 | contextWindow没声明,预算算错 | models字段写全,一项不省 | 数据库连接串少写参数 |
| 会话日志落盘 | 152个会话含敏感内容永不过期 | append-only设计的双面性 | 定期清理+升级备份+profile隔离 | WAL日志没配保留策略 |
最后说句实话(系列终)
四篇写完,回头看这套东西的本质:
DSH给了你一个"LLM能干活"的运行时,但"LLM干活不出事"是纯工程问题——说明书文体(第2篇)、状态机门禁(第3篇)、凭据防御(本篇),一层都不能省。
很多人觉得Agent应用的技术含量在prompt、在模型选型。做了152个会话我的体会反过来了:模型能力是租来的,工程防线才是你自己的资产。这四层防线加起来不到500行代码,但它们是"敢接生产"和"只能跑demo"的分界线。
系列完结。你的Python技能栈一行不用重写:包一层CLI、写一份给LLM看的说明书、给写操作上把exit(2)的锁、把key藏进环境变量——你的Python工具,从此就是DSH的生产插件。
更多推荐


所有评论(0)