ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Caveman TypeScript SDK(@caveman-ai/sdk) 深度解析:Cave 客户端、延迟工具检索、字节安全压缩与本地运行时策略

Caveman TypeScript SDK(@caveman-ai/sdk) 深度解析:Cave 客户端、延迟工具检索、字节安全压缩与本地运行时策略 Caveman TypeScript SDK(caveman-ai/sdk) 深度解析:Cave 客户端、延迟工具检索、字节安全压缩与本地运行时策略【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman本文为 Caveman 项目 TypeScript SDK 开发指引 的完整技术展开:以单文件、零运行时依赖的caveman-ai/sdk为对象,讲清Cave主客户端、CaveTrace请求级追踪、BM25 服务端工具检索、字节安全的compress()委托压缩、可逆 checkpoint/上下文打包、依赖免费的 OTLP/JSON 导出器,以及带 Ed25519 签名验证的RuntimePolicyClient的完整契约与实现细节。读完本文,你可以把该 SDK 接入自己的 Agent 网关调用链,并理解它在 wire 协议、fail-closed 语义与跨语言 parity 上做出的每一个设计取舍。一、SDK 定位:单文件、零运行时依赖、ES Modulepackage.json 定义了该包的发布形态与硬约束:包名caveman-ai/sdk,当前版本1.0.0;CHANGELOG.md 记录了 1.0.0(2026-07-26)“记录了稳定的 TypeScript SDK API 与/sdk/v1/*wire 基线,并与 Python SDK 锁定 coordinated-major 与 parity 规则”;type: module,入口dist/index.js、类型dist/index.d.ts,sideEffects: false,发布物只有dist/、README 和 LICENSE;engines 要求 Node.js ≥ 22.13(README.md 同样强调此要求);零运行时依赖:dependencies为空,devDependencies只有typescript5.9.3;脚本:buildtsc;test:types用tsconfig.test.json做纯类型编译;test:node跑node --test --test-force-exit tests/*.runtime.mjs;完整test会先 build 再依次跑类型测试与运行时测试。整个 SDK 的实现只有一个文件 src/index.ts(约 2400 行),导出Cave(主客户端)、CaveTrace(请求级追踪)、CaveOptions、CaveTool、ToolSearchResult、CompressOptions、CompressResult以及ContextPack*系列类型。零依赖的直接后果是:SDK 内部所有加密(WebCrypto Ed25519/SHA-256)、base64 解码、URL 解析都自己实现,不引入任何第三方包。二、目录结构与测试分层AGENTS.md 给出的 Layout 与仓库实际一一对应,测试按“类型断言 / 运行时行为”严格分层:路径职责src/index.ts整个 SDK 的实现与全部公开导出tests/tool-search.test.ts类型级断言,只被tsc --noEmit编译,不执行tests/tool-search.runtime.mjs运行时测试,node:test 全局 fetch mock,导入dist/产物tests/runtime-policy.runtime.mjs 与 tests/runtime-policy.test.ts驱动 packages/sdk/parity/ 下与 Python SDK 共享的runtime-policyfixture:fetch wire、签名用例、全部assignment_vectors(精确浮点相等)、全部guard_cases(算子真值表放在 fixture 里而非测试文件中)、全部decision_cases;要求“迭代数组,永远不要硬编码数量”tests/parity.runtime.mjs跨语言一致性套件,驱动共享fixtures.json—— 同一组 fixture 两个语言,任一 SDK 缺字段即 CI 失败tests/trace-continuity.runtime.mjstrace/span id 铸造规则 哪些请求携带x-cave-trace-id/x-cave-parent-span-id,镜像 Python 侧test_trace_continuity.pytsconfig.json / tsconfig.test.json两份独立配置,测试配置覆盖tests/;两者都继承仓库根目录的 tsconfig.base.json其余运行时测试(如 tests/exporter.runtime.mjs、tests/context-pack.runtime.mjs、tests/assembly.runtime.mjs)分别覆盖后文各 API 的行为契约。开发约定是:先构建再跑运行时测试——pnpm build pnpm test:node。三、Cave 主客户端:连接参数与校验语义new Cave(options)是入口,apiKey、baseURL、agent三者必填,缺失即抛错(index.ts#L350-L376)。CaveOptions的完整字段(index.ts#L4):字段说明apiKey/baseURL/agent必填。连接 Caveman 网关的项目密钥、网关地址与 agent 标识defaultWorkflow默认工作流标签;每个请求的x-cave-workflow头都不得省略retentionmetadata|zdr|configured,数据保留策略声明verifyOnInit构造时是否执行连接校验controlURL可选的 control-api(/api/v1/*)地址,与baseURL分离user不透明的终端用户标识,以x-cave-user-hash转发;若是 PII 需自行先哈希timeoutMs所有 SDK HTTP 请求的总截止期,默认 30 秒,必须是正整数signal调用方取消信号,作用于所有 SDK HTTP 请求两个值得注意的校验/归一化行为:URL 严格校验:normalizedServiceURL()要求baseURL/controlURL是绝对 http(s) URL、无内嵌凭据、无 query/fragment、首尾无空白,并剥掉末尾斜杠(index.ts#L335-L348)。CAVE_WORKFLOW环境变量兜底:未显式设置defaultWorkflow时,构造函数会读取CAVE_WORKFLOW并归一化为网关标签规则(小写[a-z0-9_-],最长 96);不合法的环境值被静默忽略而不是让每个请求 400(index.ts#L358-L375)。源码注释说明其用途:cave wrap --workflow x这类包装器可以为整条应用链路打标签而无需改代码。最小可用示例来自 README.md:import { Cave } from caveman-ai/sdk; const cave new Cave({ apiKey: process.env.CAVE_API_KEY!, baseURL: http://127.0.0.1:8787, agent: support-agent, }); const result await cave.compress(large payload); console.log(result.output, result.basis); // basis is inferred四、CaveTrace:trace 连续性与 ID 铸造规则cave.trace(opts, fn)把回调包进一个CaveTrace(index.ts#L391-L393),它承担三件事:铸造连续 ID:traceId为 32 位小写十六进制、根spanId为 16 位小写十六进制,由 OTel 导出器同款 RNG 生成。若opts.traceId/opts.spanId用于续接入站 trace,则形状不符的值会被替换而不是发上 wire(防止把任意字符串放进请求头)。给经过 trace 的每个 provider 调用加头:凡是CaveTrace发起的 provider 请求都携带x-cave-trace-idx-cave-parent-span-id;而直接走Cave构造的通用/sdk/v1/*调用与 provider 客户端两者都不带。唯一的 SDK 端点例外是CaveTrace.tool,它的/sdk/v1/events调用会携带 trace id 与根 parent span id。工具调用埋点:trace.tool(name, options, fn)在回调前后向/sdk/v1/events发送span_type: tool.call事件(含outcome: ok|error、sequence、duration_ms、tags),且best-effort—— 遥测失败永远不覆盖工具结果或异常(index.ts#L793-L817)。CaveTrace.exporter({serviceName?})返回的OTelExporter的defaultTraceId就是该 trace 的 id —— 这是让“SDK 自己记录的 span”与“网关落库的请求行”汇入同一条 trace 的机制(index.ts#L784-L791);同一 service name 重复调用返回同一个 buffer(含 runtime-policy 决策 span),便于调用方统一 flush。该 API 镜像 Python 的Trace.exporter。trace 内还暴露了两类便捷面:trace.model.openai.responses.create(body, {cave:{latencyClass, toolSessionId}}):传latencyClass会设置x-cave-async头(值非interactive时为true);传toolSessionId会设置x-cave-tool-session头(index.ts#L824-L829)。trace.artifacts.page(value, options)/trace.artifacts.get(id):page 发送版本化信封(头x-cave-artifact-envelope: value-v1)到/sdk/v1/artifacts,gateway 只存储 JSONvalue,成功时返回一个含artifact_id的可检索占位块;strategy:verbatim则绕过存储直接原样返回;stored ! true或缺artifact_id时抛错而不是猜(index.ts#L831-L845)。五、延迟工具检索:tools() 与 toolSearch()工具目录句柄cave.tools({ catalog, strategy, initialToolCount?, maxLoadedTools? })返回{ initial, strategy, search(query, opts?) }(index.ts#L468-L520):strategyall(默认):initial即整个目录;search()依然请求服务端。strategydeferred:initial 所有alwaysLoad工具 至多initialToolCount(默认8)个非 alwaysLoad 工具;maxLoadedTools若设置,必须不小于 alwaysLoad 工具数,且 lazy 配额取min(initialToolCount, maxLoadedTools - alwaysLoadCount)。任何情况下都不返回全量目录——必须显式调用search()。search()的关键契约:1.0 破坏性变更:search()是async,返回PromiseToolSearchResult(此前是同步返回CaveTool[]),调用方必须await;opts.ranker(bm25|embeddings)被原样透传给网关;SDK 自身从不计算相似度 —— 这是“byte-safe 零依赖”原则的一部分;opts.toolSessionId以请求体session_id下发,使 provider 回调能把“已调用的延迟工具”重新注入;响应sessionId对应 wire 的session_id,provider 侧头为x-cave-tool-session;返回值的 token 口径:sentSchemaTokens/fullSchemaTokens是估算,tokenBasis披露所用计数器,basis恒为inferred;savedTokens是本地派生量(full - sent),reductionPct四舍五入到一位小数(index.ts#L1614-L1615)。实现层还有防御性校验:目录名必须非空且唯一;网关返回的每个工具名必须能映射回本地 catalog 且不得重复,否则抛错(index.ts#L1617-L1634);sentSchemaTokens fullSchemaTokens不成立时两个计数按 0 处理。cave.toolSearch(catalog, query, opts?)是脱离tools()句柄的直接变体,契约完全一致(index.ts#L526-L532)。六、compress():委托式压缩与字节安全 fail-closedcave.compress(payload, opts?)POST/sdk/v1/compress并映射 Engine 报告(index.ts#L1650-L1706)。核心语义是“SDK 委托,不重实现压缩器”;任何一步出问题都走fail-closed 直通:触发条件:非 2xx、网络异常、响应体不是带字符串output的报告、tokens_before/tokens_after非严格非负整数、tokensAfter tokensBefore、或output payload但计数不一致;直通结果:output为原始输入、ratio: 0、无recoveryHandle、tokenCountBasis: unavailable;ratio永远由客户端用校验后的计数重新推导((before - after) / before),不复用服务端字段,避免“乐观”报告;basis恒为inferred—— SDK 永不输出verified(那需要 Cloudactive路径背书)。CompressResult还包含contentType、tokensBefore/After、recoveryHandle(恢复字节级原稿的句柄,未存储时缺省)、method(如toon、elision)、losslessToModel(模型可见输出是否完整保留值)。CompressOptions.contentType可作为检测器提示,取值json | toon | log | code | diff | search-result | text | toolschema等(index.ts#L43)。七、上下文打包与可逆 Checkpointcave.context.pack(query, items, options)是仅连接态(connected-only)的有损选择器:POST/sdk/v1/context/pack,把调用方拥有的片段字节发给网关,由它决定“什么进入模型窗口”(缓存最优装配assemble()决定“放在哪里”,两者互补而非替代)。它从不写 CCR、不跑在本地 wrap 里,并要求调用方保留deferredIds点名的每一条片段以便重供(index.ts#L1708-L1796)。输入契约(ContextPackItem/ContextPackOptions,index.ts#L78-L107):item:id(稳定、调用方拥有,用于精确报告被省略项)、text、可选tokens(缺省或 0 时由网关计数)、timestamp(RFC 3339,供 recency 信号)、priority(调用方相关性加权)、pin(必须入选;若 pin 项放不下,调用返回诚实的零);options:maxTokens(必须 0)、reserveTokens(为下一轮响应/工具调用预留)、now(RFC 3339 确定性评分时钟)、recencyHalfLifeMs、recencyWeight、errorBoost。返回值校验极其严格,任何一条不满足都回退为“全部原始 item 零推断节省”的直通:选中 id 必须全部存在于请求且无重复;deferredIds必须与“请求集减选中集、按请求序”逐位相等;tokensUsed tokensSaved tokensBefore;deferredCount deferredIds.length。这保证ContextPackResult中的数字自洽,basis恒为inferred(该选择器从不进入 verified-savings 账本)。可逆 checkpoint 分两半:CaveTrace.context.checkpoint(messages, options)→ POST/sdk/v1/checkpoints,网关持久化(Valkey)并返回可逆的source_ref;CaveTrace.context.expand(sourceRef)→ GET/sdk/v1/checkpoints/{ref}/expand,取回存储的{ source_ref, version, messages, checkpoint }。响应缺source_ref或messages数组时抛错——源码注释明确:“不能展开的 checkpoint 是 bug”,可逆性是强制要求(index.ts#L847-L864)。八、Provider 客户端与 Bedrock 描述符cave.openai()/anthropic()/gemini()/vertex()是薄 provider 客户端,全部经网关前缀(/openai/v1、/anthropic、/gemini、/vertex)代理,每个都暴露.rawfetch 逃生舱(镜像 PythonProvider.raw);upstreamKey用于转发上游凭据(如 Vertex 的 Google OAuth2 access token 会以Authorization: Bearer …转发)。cave.bedrock({region, endpoint?})是不发任何网络请求的第一方路由描述符(index.ts#L413-L425):endpoint缺省为runtime,网关前缀/bedrock;显式mantle返回/bedrock/anthropic;其他值直接抛错;返回{ region, endpoint, gatewayPrefix, instrumented: true, sdkOnly: false },供 AWS SDK 或 agent 包装器自行接线,本身不携带任何 AWS 密钥。另外cave.prompts.internalBrevity({style, preserveErrorsVerbatim?, preserveCodeVerbatim?})生成输出风格片段(style:none返回空串),镜像 Pythoncave.prompts.internal_brevity(index.ts#L597-L600)。九、零依赖 OTel 导出器cave.exporter({serviceName?})返回OTelExporter(index.ts#L443-L456):recordSpan(...)把当前 GenAI 字段映射到gen_ai.*属性;SpanOptions涵盖inputTokens/outputTokens/cachedTokens(cached 是 input 的子集,绝不叠加)、costUsd、provider/model/operation/toolName、status(unset|ok|error)、纳秒级起止时间与自由attributes;export()以OTLP/JSONPOST 到标准/v1/traces,携带 Caveman 标准头(x-cave-api-key/x-cave-agent/x-cave-workflow,由otlpHeaders()组装);legacy 路径/otlp/v1/traces仅保留为服务端兼容;serviceName缺省为Cave的 agent slug(网关用它兜底 agent 标签);span 落库到caveman.spans,全程无需外部 OpenTelemetry 接线。十、RuntimePolicyClient:本地决策 Ed25519 签名策略cave.runtimePolicy({publicKey?, autoRefreshSeconds?, killEnv?})返回RuntimePolicyClient(index.ts#L1068-L1388),是路由面而非节省面——“只做路由,不含任何 savings 词汇”。refresh():唯一的网络调用(GET /sdk/v1/runtime-policy,标准头去掉 content-type):限流读:响应上限 1 MiB,超限(content-length预判或流式累计)抛oversized_response并取消读取(index.ts#L873-L912);30 秒超时:与 Pythontimeout30镜像,挂死的端点不会挂住调用方;先验签、后解析:对 bundle字符串的精确 UTF-8 字节做 Ed25519 验证(WebCrypto SPKI 导入,index.ts#L1534-L1547);TOFU 钉死时机:publicKey在签名验证成功的瞬间即被钉死——早于 schema/sequence 检查。这样“一个签过名但本客户端因形状拒绝的 bundle”不会打开降级窗口:随后到达的未签名 bundle 会被拒;拒绝回退 sequence、拒绝未知schema_version(当前接受caveman.runtime-policy.v1)、拒绝回退版本;任何失败都保留 last-known-good:refresh()永不抛错,只返回{ok, signed, error?};autoRefreshSeconds的 tick 若落在 refresh 飞行期间则跳过而不是堆叠(refreshing标志,index.ts#L1085-L1095)。decide(taskFamily, {unitKey, context, trace}):同步、纯本地、永不抛错。求值链(index.ts#L1268-L1329):本地 kill(kill()或killEnv环境变量,缺省CAVEMAN_POLICY_KILL,每次 decide 重读,0/false/no/off/空视为未置位)→ 无 bundle(policy_unavailable)→ bundle kill 旗标 → 无匹配task_family(no_policy)→ 无效策略(invalid_policy)→ 全 disabled(disabled)→guard AND 求值(eq/ne/gt/gte/lt/lte/in,类型不符判 false 而非强转,未知算子 fail-closed)→ 匹配多于 1 条即ambiguous_policy(与服务器“胜层 1 ⇒ 全弃”语义一致,绝不猜)→ 实验分臂:holdout 切片先切并强制压制到 fallback 路径(“holdout 是抑制,不是更危险的变体”);缺unitKey或实验配置非法一律 fallback,不猜臂。分臂的确定性分数由policyUnitFraction(...keys)计算:index.ts#L1509-L1524 逐字节复刻 Go 侧shared/platform/sampling.Fraction——每个 key 前缀 8 字节大端长度后整体 SHA-256,取前 8 摘要字节为大端 uint64,右移 11 位除以 2^53 得到 [0,1) 分数;该函数被导出以便调用方复现分臂、让跨语言 fixture 逐位钉死这一移植。decide()可选地把决策写成一个 span(caveman.policy.decision,属性含cave.policy.decision/reason/signed/id/version与实验 id/臂/propensity),sink 是鸭子类型(接受任何带recordSpan的对象或CaveTrace),且所有与 sink 的交互都在 try 内——“遥测永不破坏路由”。state()返回快照(hasBundle、signed、bundle kill 旗标、killedLocally、policyVersion/sequence),close()释放后台定时器(镜像 Pythonclose())。十一、RetryLoopBreaker 与预留的 JobsClientcave.retryLoopBreaker(threshold3)返回RetryLoopBreaker(index.ts#L664-L709):record(name, args)以“工具名 键排序 JSON 序列化参数”作为签名,同一签名连续重复超过阈值即在第threshold1次抛出RetryLoopError(携带签名、重复次数与阈值);任何不同的调用重置连击;guard(name, args, fn)先记录(可能抛错)再执行fn;reset()在新任务开始时清零。cave.jobs是预留的异步作业面:submit/status/cancel/wait/submitAndWait全部在任何网络 I/O 之前本地失败,抛AsyncJobsUnavailableError(codecave_async_jobs_unavailable)。源码注释解释了原因:在持久加密请求存储、凭据托管与排空 worker 就位之前,该表面拒绝制造“假 queued/completed”的假象。十二、Wire 约定、Gotchas 与验证路径AGENTS.md 的 Conventions 与 Gotchas 是该 SDK 的行为红线,逐条对应源码事实:byte-safe:SDK 把请求体原样发往网关,绝不重写;compress()是唯一产出更小字节的路径,且它委托Engine,任何异常直通原始输入(index.ts#L1651-L1660);命名映射:到网关的请求体键是snake_case(如input_schema、always_load、session_id、max_tools),响应在ToolSearchResult等类型上映射为camelCase;x-cave-workflow永不省略:默认值链为defaultWorkflow ?? unlabeled-workflow(见 index.ts#L392、index.ts#L1599);延迟工具会话交接三要素:请求session_id、结果sessionId、provider 头x-cave-tool-session;改动需同步 sdk-python 与 parity fixture;context packing 仅连接态且刻意有损:它选择“什么进窗口”,缓存最优装配决定“放哪里”;mirror sdk-python:每个字段/方法在两个 SDK 中都存在,由共享 parity 套件强制——分歧即 CI 失败;“改一个 SDK 就要改两个,还要改 fixture”;发布名caveman-ai/sdk,workspace 名在 npm 重定向计划落地前保持不变;reductionPct保留一位小数;savedTokens为派生值而非网关字段。验证路径固定为三步:# 1. 构建(dist/ 是运行时测试的导入源) pnpm build # 2. 类型级断言(只编译,不执行) pnpm test:types # 3. 运行时测试(against dist) pnpm test:node十三、小结Caveman 的 TypeScript SDK 是一个“把网关能力翻译成类型安全本地 API”的薄层:所有重活(压缩、排序、策略下发、checkpoint 存储)都在网关/Engine 侧,SDK 只负责三件事——忠实透传字节、fail-closed 地降级、以及让本地决策(策略、断点装配、重试打断)永不阻塞、永不猜测。跨语言 parity fixture 与“basis 恒为 inferred”的诚实性纪律,使它成为在多 SDK 表面共享同一套 wire 契约时,一个可直接对照 packages/sdk/ 下 Python 实现逐条核对的工程样本。【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表