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复述进了对话,出现在它回复的草稿里。

后背发凉的地方想深一层:

  1. 这段对话的上下文是要发给云端模型的——凭据进了上下文=凭据出了你的机器

  2. 会话日志落盘——152个会话全存在.dsh/sessions/目录里,secret跟着永久躺在磁盘上

  3. 模型厂商的日志留存策略你一无所知

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

顺带说我的完整模型接入方案(生产可用形态):

通道配置说明
主力DeepSeekbaseURL指向公司内网网关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的生产插件。

Logo

更多推荐