搜 索

ai-assist:从入门到放弃

  • 3阅读
  • 2026年06月24日
  • 0评论
首页 / AI/大数据 / 正文

基于 underestimatedme/ai-assist(v0.1.0)。

这是一份劝退指南。把内部平台包成 Claude Code 技能这件事,从"我周末就能搞定"到"算了不做了",中间有八个固定的坑,每个坑都有一批人留在里面。下面按你会遇到的顺序讲:这一关长什么样、多少人死在这、以及这个仓库为它准备了什么。

前情提要:仓库里所有 host 是虚构的 *.internal.example.com,所有 API 形状是编的。它不是能跑的工具,是一副骨架加一套判断。

flowchart LR A["① 跑起来
5 分钟"] --> B["② 拿到凭证
💀 死人最多"] B --> C["③ 第一个真实请求
比想象容易"] C --> D["④ 写成技能"] D --> E["⑤ 活过第二天
💀 用户在这放弃你"] E --> F["⑥ 学会说不知道
💀 信任在这死掉"] F --> G["⑦ 给第二个人用
💀 真正的劝退点"] G --> H["⑧ 升级不把人登出"]

劝退点 0:它其实不给你任何权限

先把期待压下去。这套东西不授予任何访问权。它复用你浏览器里已经存在的会话,以你的身份、在你已有的权限内、在你自己的机器上跑。

所以如果你的目标是"让 AI 帮我查我本来查不到的东西"——现在放弃,省下一周。

它真正解决的是另一个问题:内部平台都有 Web UI、没有 API token、登录走 SSO,跨四个平台回答一个问题要开八个标签页。把每个平台包一次,那一下午变成一句话。这才是回报所在。


第一关:跑起来(这关不会死人)

git clone git@github.com:underestimatedme/ai-assist.git
cd ai-assist && make dev      # = claude --plugin-dir $(pwd)

没有 build,没有 install。改文件、重启 Claude、再试——这就是全部开发循环。另外两个目标:make validate 校验结构,make clean 删掉所有运行时数据。

对着仓库自带的虚构 host,/datastore-query setup 会在 DNS 阶段失败。这是预期行为,不是坏了。在这里以为自己装错了而放弃的人,是第一批。


第二关:拿到凭证 —— 死人最多的一关

这关之所以劝退,是因为大部分人把它想复杂了:以为要在 agent 和浏览器之间搭一座桥。

没有桥。 Claude Code 是跑在你机器上的一个进程,它的 shell 以你的用户执行命令,和你自己敲没区别。交接物是磁盘上的一个文件。

sequenceDiagram autonumber actor U as 你 participant C as Claude participant S as capture-cookie.py participant B as 浏览器窗口 participant F as .local/cookies/prod.txt U->>C: /datastore-query setup C->>S: shell: python3 capture-cookie.py csrftoken,sessionid S->>B: 在你桌面上开真实窗口 headless=False Note over B: 复用你的 Chrome profile
SSO 通常直接放行,零输入 U-->>B: 需要时登录 S->>S: 每秒轮询,直到必需的 cookie 名都出现 S->>F: 写文件,关浏览器,exit 0 Note over C,F: 之后:curl -b "$(cat )" ...
Claude 全程没看见凭证的值

想通这一点,剩下的都是普通工作。想不通,就会在这里放弃。

这一关的四个具体死法

症状真实原因出路
窗口根本开不出来在容器 / 纯 SSH / 云 runner 里,没有显示器这是硬依赖。退回手工粘贴 cookie
Chrome profile unavailableChrome 正在运行,profile 目录被它锁着关掉 Chrome,或接受全新浏览器 + 手动登录
ERR_CERT_AUTHORITY_INVALID内网 ingress 自签证书,登录页压根没渲染出来--insecure只对这类 host
轮询到超时,cookie 一直不出现cookie 名写错了,或者你那个 SSO 租户的名字不一样DevTools > Application > Cookies 自己看

你的平台是哪种鉴权

scripts/capture-cookie.py 覆盖三种,按 DevTools 里看到的选:

你看到--mode备注
Cookie: sessionid=...cookie(默认)传逗号分隔的 cookie 名
Authorization: Basic ...basic-auth成功响应上截 header
Authorization: Bearer ...token--match-url /api/login
自定义头 / 签名 / mTLS自己加第四种约 15 行:一个 attach + 一个 check

几个不写出来你会踩一遍的细节:

  • 复用 Chrome profile,所以登录通常是零交互的——这是这套方案能被日常使用的原因。
  • 清掉目标 cookie,但保留上游 IdP 的 cookie。这个组合让活着的 SSO 会话静默换出一个新的平台会话。全清会把你踢回登录页。
  • basic-auth 跳过 401/403 响应:那是挑战不是成功,从挑战上截的 header 可能是错的。
  • 诊断只打 cookie 的名字,永不打值。这些输出会进终端、日志、可能进 bug 报告。
  • 永远不要让用户把密码敲进对话——那会落进 transcript。让他在自己终端里跑命令。

退出码是契约:0 成功,1 Playwright 起不来,3 超时或窗口被关。flows/setup.md 按这三个码分支到"验证"或"手工兜底"。


第三关:第一个真实请求(比你以为的容易)

这关的正确姿势是先离开技能,回到你自己的 shell

  1. 在浏览器里做那件事,开着 Network 面板。
  2. 找到带答案的那个请求,右键 → Copy as cURL。
  3. 粘进终端,确认能跑。
  4. 把除 CookieContent-Type 之外的 header 全删掉,看还能不能跑。通常能。
  5. 剩下的东西改写成 scripts/query.sh,URL 从 config.yml 读,cookie 从 .local/ 读。

你不是在逆向一个 API,你是在重放一个浏览器已经展示给你的请求。这个区别决定了这一关是一天还是一周。

完成判据很硬:bash skills/datastore-query/scripts/query.sh -e prod -d <db> "SELECT 1" 打印出真东西。


第四关:写成技能(这里开始有设计)

两条分工规则,违反了不会立刻报错,但会在第三个 flow 的时候把你埋掉:

SKILL.md 拥有一切共享的东西——参数解析、环境解析、鉴权预检、分发。全部只做一次,在路由之前做完。
flow 文件只拥有自己那几步。它假定输入已归一化、预检已通过,绝不复述共享逻辑。

还有一条隐蔽的:description 决定你的技能会不会被调用。写"什么时候用它",不是"它是什么"。

Query internal databases. Use when the user asks to look up rows, check whether data exists, or inspect table structure.
A datastore querying tool.

以及:删掉你没写的 flow。一个指向不存在文件的引用,比缺一个功能更糟。


第五关:让它活过第二天 —— 用户在这里放弃你

凭证会过期,通常每天一次。没有自动恢复,每天早上第一次查询都会失败,然后大家就不再打开这个工具了。你不会收到任何反馈,工具只是安静地死了。

契约只有一句:恢复一次,或者明说失败了什么。

stateDiagram-v2 [*] --> 调用 调用 --> 成功: ok 调用 --> 失败: error 失败 --> 恢复一次: 先通报再恢复 恢复一次 --> 重试 重试 --> 成功: ok 重试 --> 明确上报: 仍然失败 明确上报 --> [*]: 哪个源失败、尝试了什么、什么因此未知 成功 --> [*] note right of 明确上报 绝不静默降级: 不换数据源 不悄悄丢一列 不把陈旧缓存当成新鲜结果 end note

这条在每一个调用点都成立,不只在 dispatch 的预检。dispatch 把控制权交给 flow 之后,规则不失效。

一个容易做错的细节:探针返回 302要读 Location 的目标,不是只看状态码。把所有重定向当失败,会把用户送进无意义的重登循环。而"连不上"根本不是鉴权问题——用户没连 VPN,重新登录一百次也没用。


第六关:学会说"不知道" —— 信任在这里死掉

毁掉这类工具最快的方式,是给出一个自信的错误数字。一次就够。

所以 service-profileUNKNOWN 写成了一个合法判定:

UNKNOWN 是一个真实的判定,当它为真时必须使用。因为"没有明显失败"就把它降级成 HEALTHY,正是一个监控工具变得比没有更糟的方式。

判断下线的那个 flow 说得更直接:

一次失败的查询和一个真实的零,在报告里长得一模一样,含义却相反。

同一类的判断还有:

  • 零不自动等于业务事实。报"一条都没有"之前先排除:环境错了、时区导致时间窗错位、软删除标志过滤光了、只读副本没追上。并且说明你排除了哪几个。
  • 空的 Prometheus series 不等于 0。指标名拼错和"真的没有"返回一模一样。先 count(<metric>) 确认指标存不存在。
  • 不要对分位数求平均。avg(p99) 不是任何东西的 p99。
  • 每个数字都带上时间窗和环境。把 staging 的数当 prod 报,比不报更糟——因为它会被相信。

只读闸门要放在脚本里

case "$FIRST_WORD" in
  SELECT|SHOW|EXPLAIN|DESC|DESCRIBE) ;;
  *) echo "Error: '$FIRST_WORD' is not permitted..." >&2; exit 2 ;;
esac

脚本里那句注释说完了理由:一条只是"请求模型遵守"的规则,会在上下文变长的那天失效。

同理,第一个技能务必是只读、高频、低风险的。早期一次糟糕的写操作会杀死整个项目,而信任没有第二次机会。


第七关:给第二个人用 —— 真正的劝退点

不是给团队。给一个同事,坐你旁边,用他自己的机器。

他前十分钟踩的每个坑,都是你的 backlog,而且不会是你预期的那些。常见的:没装 Python;Chrome 开着所以 profile 被锁;VPN 配置不同;他那个 SSO 租户的 cookie 名不一样。

很多项目死在这里的原因是:作者觉得"我这边明明是好的"。你这边当然是好的,那是你调了一周的那台机器。


第八关:升级把所有人登出

插件管理器装 v0.2.0 时会铺一个新目录,v0.1.0 那个(装着大家的 cookie 和 schema)留在旁边不动。没有迁移的话,每次升级静默登出所有人——用户的反应是不再升级

flowchart LR A["…/ai-assist-v0.1.0/
skills/x/.local/
cookies · schemas"] -.->|"ls -dt 找最新的兄弟安装"| C{".migrated
在吗"} C -->|在| D[立即返回,零成本] C -->|不在| E["按契约 cp -r"] E --> B["…/ai-assist-v0.2.0/
skills/x/.local/"]
bash "${CLAUDE_PLUGIN_ROOT}/scripts/migrate-local.sh" datastore-query cookies schemas

尾部参数是这个技能的迁移契约。它放在调用点(SKILL.md 里)而不是脚本内部,这样每个技能拥有自己的清单,且清单出现在自己的 diff 里。语义三条:只在源有、本地无时才拷;原样拷贝无格式翻译;sentinel 门控所以放在每次 dispatch 最前面是免费的。

顺带一条运营现实:用户只有在版本号变化时才会收到更新。一个忘了 bump plugin.json 的修复,等于没发布。


如果你没放弃:值得抄走的结构

三个技能,其实是三种原型

flowchart TB SP["service-profile
编排型 · 自己不碰平台"] DQ["datastore-query
能力型 · Cookie 鉴权"] MQ["metrics-query
能力型 · Basic 鉴权"] SP -->|"公开接口 /datastore-query ..."| DQ SP -->|"公开接口 /metrics-query ..."| MQ DQ --> P1[(数据查询平台)] MQ --> P2[(指标平台)]

两个能力型技能除了中间那段(请求怎么发、响应怎么解)完全同构。写完第一个,第二个就是把中间换掉。

编排型才是价值密度最高的:能力型回答"这个数是多少",编排型回答"这个服务还好吗"——后者才是人真正会问的。它有一条硬规则:只通过公开接口调别的技能,绝不读别人的 .local/、绝不直接调它的脚本、绝不复述它的规则。以及它必须容忍局部失败——每个源都跑,失败的在输出里点名,绝不用一个看起来合理的值填空。

五层,只有一层和 Claude Code 绑定

位置拥有
分发plugin.json、SKILL.md frontmatterharness 如何发现和路由
配置config.yml每个 URL、凭证文件名、环境差异
凭证capture-cookie.py.local/获取和存放会话凭证
能力<skill>/scripts/*确定性执行:HTTP、解析、格式化
知识SKILL.mdflows/shared/做什么、什么顺序、结果怎么读

只有分发层是 Claude Code 专属的,换 harness 时另外四层原样搬走。撑住这个性质的是两条规则:

  1. 脚本从 $0 推自己的路径,不读 ${CLAUDE_PLUGIN_ROOT} 这类 harness 变量(markdown 里可以用,脚本里不行)。这样它在技能里、cron 里、测试里、你自己 shell 里跑得一样。
  2. 环境标识进去,其余内部解析query.sh -e prod -d orders "SELECT ..." ✅;把 cookie 路径当参数传进来 ❌。目的是把基础设施管道从模型手里拿走,不是把意图拿走——服务名、查询、时间范围仍然是调用方的责任。

真正的资产是 markdown,不是脚本

脚本是简单的部分,几百行 curl 和 JSON。值得留下来的东西是那些判断:一个零可能是滞后的副本;一个空 series 通常是标签拼错;UNKNOWN 是真实结论。这些不在任何厂商文档里,是你团队搞错一次学到的。

docs/ADAPTING.md 给了可以直接套的模板——症状 / 实测边界(你真测过什么,不是你假设什么)/ 绕过方式 / 误导信号——以及一份该写的清单:哪些权限失败是静默的、哪些端点有门禁且界线实际在哪、读请求是否打副本落后多久、每个平台用哪个时区报时(混用两个会产出自信而完全错误的分析)、状态码和枚举值用人话是什么意思。

判断标准:每当你发现自己在给同事解释一个坑,那段解释就该进 flow 文件。


适配清单:只有五处要改

按依赖顺序:config.yml(URL、cookie 名、环境)→ 凭证捕获模式 → 能力脚本 → flow 文件(你真正的工作在这) → 重命名插件。

能力脚本要换的正好三处,别多换:请求(URL、方法、payload)、响应解析错误映射("凭证过期" / "你没这个授权" / "平台挂了"是三个不同问题,混成一个会把用户送去重新登录,而真正的问题是缺一个授权)。

明确不要改的四样:自定位脚本、环境标识契约、脚本里的只读闸门、失败处理策略。


什么时候真的该放弃

诚实地说,这几种情况下别做:

  • 你的 Claude Code 不在有桌面的本机上跑。凭证捕获这一环就不成立,剩下的都是硬撑。
  • 你想包的第一个平台是会写数据的。换一个只读高频的先做。
  • 你不打算写 flow 文件,只想要脚本。那你要的是几个 shell 别名,不是这套东西——而且别名更省事。
  • 没有第二个用户。只有你自己用的话,投入产出比不如直接把那几条 curl 存成 alias。

选题公式:频率 × 上下文切换成本 × 结果的确定性。第一个一定要只读、高频、低风险

反过来,如果你撑过了第七关——有第二个人在自己机器上用起来了——那基本就稳了。之后第二个技能是复制粘贴换中间,第三个开始共享部分不再变动。


附录

坑清单速查

现象真实原因处理
浏览器窗口开不出来容器 / SSH / 云 runner,没有显示器硬依赖,退回手工粘贴
Chrome profile unavailableChrome 在跑,profile 被锁关掉 Chrome
ERR_CERT_AUTHORITY_INVALID内网自签证书--insecure,仅限此类 host
探针 302,用户反复被要求重登把所有重定向当失败了Location 目标
报"会话过期",重登没用根本没连 VPN区分开,指向 references.vpn-setup
指标查询返回 0空 series ≠ 0,多半是名字拼错count(<metric>)
查出 0 行,报"没有数据"环境 / 时区 / 软删除 / 副本滞后逐个排除并说明
升级后所有人被登出没调 migrate-local.sh加到 dispatch 入口
mapfile: command not foundmacOS /bin/bash 还是 3.2while IFS= read -r
发了新版但没人收到忘了 bump plugin.json版本号是唯一的更新触发器

命令

make dev          # 加载插件启动 Claude Code
make validate     # 校验插件结构
make clean        # 清空所有 .local/ 运行时数据
/datastore-query  setup [env] | setup-check [env] | sync [env] <db>... | explain <SQL> | <自然语言问题>
/metrics-query    setup [env] | health <service> | traffic <service> | promql <expr>
/service-profile  <service> [health|usage] [env]

五条最容易忘、代价最大的规则

  1. 脚本从 $0 推路径,不读 harness 变量。
  2. 环境标识进去,URL 和凭证路径在脚本内部解析。
  3. 只读闸门在脚本里,不在 prompt 里。
  4. 恢复一次,或者明说失败了什么。绝不静默降级。
  5. 已经在别处存在的事实,按位置引用,不复述。
评论区
暂无评论
avatar