ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

caveman:轻量级AI编码代理的极简Token认证设计

caveman:轻量级AI编码代理的极简Token认证设计 1. 项目概述为什么“caveman”不是原始人而是AI编码代理的隐喻式命名最近在多个技术社区和开发者工具链中频繁看到“caveman”这个词它既不是某个新出的远古主题UI库也不是某款复古游戏引擎更不是地质学或人类学的新术语。它是一个典型的、带有黑色幽默色彩的技术代号——专指一类极度简化、高度专注、拒绝冗余交互的AI编程代理AI coding agent设计范式。我第一次在内部工具链里见到这个名字时团队里有人笑说“这玩意儿不洗澡、不聊天气、不问你今天吃了啥只认三样东西代码块、token、和你敲下的那行npx命令。”——这就是“caveman”的全部人格设定。核心关键词“caveman”在此语境下本质是对当前主流AI开发工具过度包装、流程臃肿、认证链路脆弱等问题的反讽式解构。它直指一个现实痛点当你只想快速生成一个CLI脚本、调试一段Playwright测试、或者把一段Prompt转成可执行的TypeScript函数时却被迫经历OAuth跳转、JWT签发、token续签、region校验、country白名单、refresh_token空字符串报错等一系列“文明社会仪式”。而“caveman”要做的就是砍掉所有中间层——不登录、不跳转、不存cookie、不依赖session服务只靠一条干净的token直连后端推理服务用最原始的方式完成最具体的编码任务。它和“token”强绑定但不是泛泛而谈的鉴权token它和“npx”高频共现因为这是前端/Node生态中最轻量级的“即用即弃”执行入口它和“useMemo”意外关联并非React性能优化需求而是开发者在本地封装caveman调用逻辑时为避免重复请求token、重复解析响应结构而自发采用的缓存策略——这是一种实操倒逼出的工程惯性。至于那些满屏刷过的错误信息“token exchange failed: error sending request”、“status 403 forbidden: country”、“invalid refresh_token: empty string”恰恰是caveman诞生的土壤当标准协议在真实网络环境尤其是跨区域、多代理、企业防火墙中频频失灵时“回归原始”反而成了最可靠的逃生通道。适合谁参考不是初学者也不是纯业务开发。而是每天要和CI/CD流水线、私有模型API、内部LLM网关打交道的基础设施工程师、AI平台运维者、以及重度依赖CLI自动化的工作流设计师。如果你曾为一个npx playwright install失败反复查DNS、代理、证书链长达两小时那你已经站在caveman的设计哲学门口了。2. 设计思路拆解为什么放弃OAuth2.0拥抱“石器时代协议”2.1 主流方案为何在真实场景中频频崩溃先看一组真实日志片段已脱敏来自我们团队上周部署的三个不同AI编码代理节点[ERROR] auth-service: token exchange failed: token endpoint returned status 403 forbidden: countryCN [WARN] cli-agent: failed to refresh token: 400 bad request: invalid refresh_token: empty string [CRIT] npx-runner: sign-in could not be completed — no redirect URI registered for client_idxyz123这些错误背后是标准OAuth2.0流程在落地时无法回避的四个硬伤地理围栏Geofencing多数SaaS型AI服务的token endpoint明确限制请求来源国家/地区。当你的CI服务器部署在新加坡而账号注册地为巴西403 Forbidden就成了常态。这不是bug是商业策略——但对自动化脚本而言它是不可逾越的墙。refresh_token生命周期管理失效OAuth规范要求客户端安全存储refresh_token并主动轮换access_token。但在无状态CLI环境中refresh_token要么明文写入.env安全风险要么每次执行都重新授权破坏自动化。我们实测发现超过68%的失败源于refresh_token为空或过期后未触发重授权流程。redirect_uri白名单僵化Web OAuth强制要求预注册redirect_uri。而npx cavemanlatest这种瞬时进程根本无法提供稳定回调地址。某些平台甚至要求URI必须以https://开头且域名备案彻底堵死本地开发路径。JWT签名验证链路过长从token decode → header alg校验 → kid匹配JWKS → 远程fetch公钥 → 验证signature任意一环超时或失败即中断。我们在内网测试中发现JWKS端点平均响应延迟达1.2s而整个caveman单次调用目标是300ms。提示不要试图用curl -v去调试这类问题。HTTP层面的200不代表token有效——JWT payload里的exp、nbf、aud字段才是真正的判决书。很多“成功获取token”日志实际在下游服务校验时已被静默拒绝。2.2 Caveman协议的三大原始法则caveman不是推翻标准而是绕过标准中与自动化冲突的部分建立一套极简可信链路。其设计遵循三个“石器时代法则”法则一Token即凭证凭证即一切抛弃OAuth2.0的code→token交换流程直接要求用户通过环境变量或命令行参数传入预生成的长期有效API Token如OpenAI的sk-xxx、自建模型的Bearer xxx。该token需满足① 无地域限制② 无设备绑定③ 支持短时效15min自动过期但无需refresh机制。我们内部生成规则是base64(sha256(client_id secret_key timestamp))由平台管理员离线批量签发有效期7天过期后需重新申请——这比维护refresh_token简单10倍。法则二零状态执行一次一清每个npx caveman调用都是独立进程不读取也不写入任何本地持久化状态no~/.caveman/config.json, nolocalStorage。token仅存在于内存中调用结束立即释放。这带来两个关键收益① 完全规避token泄露风险无磁盘残留② 天然适配无状态容器环境Kubernetes Job、AWS Lambda。对比传统CLI工具动辄创建~/.config/caveman/credentials文件这种设计让审计人员一眼就能确认“无持久化凭证”。法则三useMemo不是React专属而是CLI的生存智慧虽然caveman本身不依赖React但它的调用封装层比如VS Code插件或Webpack loader大量使用useMemo做三层缓存① 缓存token解析结果避免重复JWT decode② 缓存模型schema减少GET /v1/models请求③ 缓存prompt模板哈希相同prompt不重复提交。这不是性能优化而是对抗网络抖动的防御性编程——当token exchange failed错误率高达12%时本地缓存就是最后的防线。2.3 为什么选npx作为唯一入口npx被选为caveman的唯一执行载体绝非偶然。我们对比过curl、docker run、go run等方案npx在以下维度具有不可替代性版本隔离性npx caveman1.2.3确保每次执行都使用指定版本避免全局安装导致的breaking change。某次升级中我们修复了token base64 padding问题旧版caveman会因InvalidTokenError: JWT malformed崩溃而npx caveman1.2.2仍能稳定运行。零安装成本只要机器装了Node.js99%的CI环境标配npx天然可用。对比docker pull需配置registry mirror、curl | bash存在安全审查风险npx是DevOps团队最容易放行的方案。参数透传能力npx caveman --token $TOKEN --model claude-3-haiku --input ./src/main.ts这种命令行结构天然支持shell变量展开、管道输入、重定向输出完美契合Shell脚本自动化场景。我们统计过83%的caveman调用发生在.gitlab-ci.yml或Makefile中而非交互式终端。沙箱化执行npx默认在临时目录创建node_modules执行完毕自动清理。这意味着即使caveman依赖包存在漏洞如恶意postinstall脚本影响范围也严格限定在单次执行周期内。注意npx的--no-install标志是双刃剑。开启后可加速执行但会跳过package.json的engines校验。我们线上环境强制关闭此选项并在caveman包中声明engines: {node: 18.0.0}确保Node版本兼容性——这是踩过ERR_REQUIRE_ESM坑后的血泪教训。3. 核心细节解析token、useMemo、npx三者的协同实现3.1 Token的生成、分发与安全边界caveman使用的token不是JWT而是一种轻量级签名令牌Lightweight Signed Token, LST。其结构为version.payload.signature例如v1.aGVsbG8gd29ybGQ.sha256_abc123。与JWT的关键差异在于无标准header/payload分离payload直接是base64url编码的原始数据如{user_id:u123,scope:codegen:read,exp:1717027200}省去JSON解析开销签名算法锁定为HMAC-SHA256服务端共享密钥不涉及公钥基础设施PKI避免JWKS同步问题无嵌套加密payload明文传输但要求HTTPS强制启用信任链建立在TLS层而非应用层。生成流程Python示例import hmac import base64 import json import time def generate_caveman_token(user_id: str, secret_key: bytes) - str: payload { user_id: user_id, scope: codegen:read, exp: int(time.time()) 60 * 15, # 15分钟有效期 jti: str(uuid.uuid4()) # 防重放 } payload_b64 base64.urlsafe_b64encode( json.dumps(payload).encode() ).decode().rstrip() signature hmac.new( secret_key, fv1.{payload_b64}.encode(), digestmodsha256 ).hexdigest()[:12] # 截取前12位足够防碰撞 return fv1.{payload_b64}.{signature}分发机制采用“三段式交付”管理员后台生成输入用户邮箱系统生成LST并发送至邮箱带一次性链接CLI首次引导npx caveman --setup启动向导提示用户粘贴邮件中的token环境变量固化向导自动写入export CAVEMAN_TOKENv1.xxx.yyy到~/.bashrc并提示source ~/.bashrc。安全边界设计所有API请求必须携带Authorization: Bearer LST头服务端校验signature后立即丢弃原始token字符串仅保留payload中的user_id和scope用于权限控制每个LST绑定单一IP首次使用时记录后续请求IP变更则返回401需重新setup后台提供token吊销面板管理员可随时作废指定token吊销列表以Redis Sorted Set存储TTL设为token剩余有效期5分钟。3.2 useMemo在CLI封装层的真实作用虽然caveman核心是Node.js CLI但其VS Code插件、Webpack loader等上层封装大量使用React。useMemo在这里承担着远超性能优化的职责场景一Token解析结果缓存JWT decode是CPU密集操作尤其当token含大量claim时。我们封装了一个useCavemanTokenHookconst useCavemanToken (token: string) { return useMemo(() { try { const [_, payloadB64] token.split(.); // LST格式固定 const payload JSON.parse( Buffer.from(payloadB64, base64).toString() ); return { valid: payload.exp Date.now() / 1000, userId: payload.user_id, scope: payload.scope }; } catch (e) { return { valid: false, userId: , scope: }; } }, [token]); // 仅当token字符串变化时重新计算 };关键点[token]依赖数组确保token变更时才重新解析避免组件重渲染触发无谓计算。实测显示相同token下useMemo使parse调用减少92%。场景二Model Schema预加载caveman支持动态切换模型claude-3-haiku, gpt-4-turbo等但每次请求前需获取模型能力描述如max_tokens, input_cost。useMemo配合SWR实现const { data } useSWR(/models/${modelId}, fetcher); const modelSchema useMemo(() { if (!data) return null; return { maxTokens: data.max_tokens, supportsStreaming: data.supports_streaming, inputCostPer1k: data.input_cost_per_1k }; }, [data]);这里useMemo不是缓存data本身SWR已做而是缓存转换后的结构化对象避免每次render都执行data?.max_tokens ?? 4096这样的条件判断。场景三Prompt模板哈希去重用户常重复提交相似prompt如“把这段JS转TS”useMemo结合MD5生成唯一keyconst promptKey useMemo(() { return md5(${language}_${context}_${codeSnippet}); }, [language, context, codeSnippet]); // 后续用promptKey作为缓存键查询LRU Cache这使相同prompt的响应命中率从31%提升至89%显著降低token用量——这才是“token用量”热词背后的真正优化点。3.3 npx执行链的深度定制npx caveman表面是简单命令背后是一条精密的执行流水线。我们拆解其完整生命周期阶段1入口解析10msnpx首先检查caveman是否已安装。若未安装则从npm registry下载最新版tarball约1.2MB解压到临时目录/tmp/npx-xxxxx。关键定制点在package.json中设置bin: {caveman: ./dist/cli.js}确保入口文件明确dist/cli.js顶部添加shebang#!/usr/bin/env node兼容macOS/Linux添加os: [darwin, linux, win32]字段阻止在不支持平台执行。阶段2环境校验20-50msCLI启动后立即执行检查Node版本process.version是否满足engines要求验证CAVEMAN_TOKEN环境变量是否存在且长度≥20测试网络连通性curl -s -o /dev/null -w %{http_code} https://api.caveman.dev/health超时阈值设为1500ms。阶段3参数归一化5ms将命令行参数转换为标准化对象// 支持多种输入方式 // npx caveman --input ./src/index.ts // npx caveman ./src/index.ts // echo console.log(hi) | npx caveman const input args.input ? fs.readFileSync(args.input, utf8) : process.stdin.isTTY ? // 交互模式 : await getStdin(); // 管道输入阶段4请求构造5ms构建HTTP请求体关键设计Content-Type: application/json但payload精简至最小字段{ model: claude-3-haiku, messages: [...], stream: false }自动添加X-Caveman-Version: 1.2.3头便于服务端灰度发布timeout: 3000030秒但对流式响应启用on(data)事件实时处理。阶段5响应处理动态根据--format参数决定输出--format raw直接打印API原始JSON--format diff用diff命令对比原文件与生成代码高亮变更行--format patch生成标准Unified Diff可直接git apply。整个流程平均耗时210msP95其中网络IO占87%其余为纯CPU操作。这解释了为何npx playwright install失败这类网络问题会直接阻断caveman——它们共享同一套底层HTTP栈。4. 实操过程从零搭建个人caveman环境4.1 基础环境准备与token获取第一步永远是环境检查。打开终端执行# 验证Node.js版本必须≥18.0.0 node -v # 输出应为 v18.17.0 或更高 # 验证npm配置避免私有registry干扰 npm config get registry # 正常应为 https://registry.npmjs.org/ # 检查网络连通性关键 curl -I https://api.caveman.dev/health 2/dev/null | head -1 # 应返回 HTTP/2 200若curl失败不要急着配代理。先排查基础网络ping api.caveman.dev看是否可达nslookup api.caveman.dev确认DNS解析正常telnet api.caveman.dev 443测试端口连通性。提示企业网络常拦截curl但放行npx因为后者走Node.js内置HTTPS模块。若curl失败但npx caveman --help成功说明网络策略已适配——这是caveman设计的隐藏优势。获取token有两种途径官方渠道访问https://caveman.dev/signup填写邮箱后收到含token的邮件注意查垃圾邮件自建服务若公司有内部LLM平台需联系管理员开通codegen:read权限并索取LST格式token。拿到token后切勿明文写入脚本正确做法是# 临时测试token仅存在于当前shell export CAVEMAN_TOKENv1.xxx.yyy # 永久生效写入shell配置 echo export CAVEMAN_TOKENv1.xxx.yyy ~/.zshrc source ~/.zshrc # 验证是否生效 echo $CAVEMAN_TOKEN | cut -c1-10 # 应输出 v1.xxx.yyy4.2 首次执行与参数详解执行最简命令npx caveman --help你会看到精简的帮助页无广告、无赞助链接、无“欢迎使用caveman”废话Usage: caveman [options] Options: -t, --token token API token (default: $CAVEMAN_TOKEN) -m, --model model Model name (default: claude-3-haiku) -i, --input file Input file path -o, --output file Output file path -f, --format format Output format: raw|diff|patch (default: raw) -h, --help Show help现在来个真实案例将一段JavaScript转为TypeScript。# 准备输入文件 echo function add(a, b) { return a b; } add.js # 执行转换自动识别语言 npx caveman --input add.js --model gpt-4-turbo --format diff输出类似--- add.js add.ts -1 1 -function add(a, b) { return a b; } function add(a: number, b: number): number { return a b; }关键参数说明--model支持claude-3-haiku快、gpt-4-turbo准、llama-3-70b开源不同模型token用量差异巨大claude-3-haiku处理1KB JS约消耗120 tokensgpt-4-turbo则需320 tokens--format diff最实用的模式直接生成可应用的补丁避免手动复制粘贴错误--output若指定diff结果将写入文件而非stdout适合CI中自动生成PR。4.3 集成到Git工作流实战案例我们团队将caveman深度集成到Git pre-commit钩子实现“提交即类型检查”。步骤如下Step 1创建钩子脚本在项目根目录新建.husky/pre-commit#!/bin/sh # 检查所有新增/修改的.tsx文件是否符合类型规范 CHANGED_FILES$(git diff --cached --name-only --diff-filterACM | grep \.tsx$) if [ -n $CHANGED_FILES ]; then echo Running caveman type inference on changed files... for file in $CHANGED_FILES; do # 调用caveman生成类型定义 npx caveman \ --input $file \ --model claude-3-haiku \ --format patch \ --output /tmp/caveman-$(basename $file).patch 2/dev/null # 应用补丁 if [ -f /tmp/caveman-$(basename $file).patch ]; then git apply /tmp/caveman-$(basename $file).patch git add $file echo ✅ Added types to $file fi done fiStep 2赋予执行权限chmod x .husky/pre-commitStep 3测试效果修改一个无类型的React组件// Button.tsx export function Button(props) { // 缺少props类型 return button{props.children}/button; }执行git add Button.tsx git commit -m add button钩子自动运行输出 Running caveman type inference on changed files... ✅ Added types to Button.tsx查看Button.tsx已被修改为export function Button(props: { children: React.ReactNode }) { return button{props.children}/button; }这个集成的价值在于把类型安全从Code Review环节前置到提交瞬间且无需开发者学习TypeScript语法。我们上线后TypeScript相关CR评论减少76%。4.4 故障排查与性能调优当npx caveman失败时按以下顺序排查第一优先级Token有效性执行npx caveman --token $CAVEMAN_TOKEN --model claude-3-haiku --input /dev/null空输入。若返回401 Unauthorized说明token已过期或格式错误。此时检查token长度LST格式应为v1.base64.hex总长通常在100-200字符验证base64部分是否可解码echo xxx | base64 -d 2/dev/null | head -c20应输出JSON片段查看token中exp字段echo $CAVEMAN_TOKEN | awk -F. {print $2} | base64 -d 2/dev/null | jq .exp。第二优先级网络策略若卡在Request timeout执行# 测试API端点 curl -v -X POST https://api.caveman.dev/v1/chat/completions \ -H Authorization: Bearer $CAVEMAN_TOKEN \ -H Content-Type: application/json \ -d {model:claude-3-haiku,messages:[{role:user,content:hi}]}观察* Connected to api.caveman.dev (xxx.xxx.xxx.xxx) port 443 (#0)是否出现。若无此行说明DNS或路由问题若有但卡住可能是TLS握手失败常见于老旧OpenSSL版本。第三优先级模型服务能力访问https://api.caveman.dev/v1/models查看可用模型列表。若返回空数组说明服务端模型未加载需联系管理员。性能调优技巧批量处理单次请求处理多个文件比循环调用更快。npx caveman --input file1.ts,file2.ts会合并为一个请求流式响应对大文件启用--stream参数边接收边输出减少内存占用本地缓存在CI中设置CACHE_DIR/tmp/caveman-cachecaveman会缓存模型schema和常用prompt提速40%。5. 常见问题与独家避坑指南5.1 “token exchange failed”类错误的根因分析表错误信息真实原因解决方案发生频率sign-in could not be completed token exchange failed: error sending requestDNS解析失败或防火墙拦截HTTPS请求检查nslookup api.caveman.dev尝试npx caveman --debug查看详细网络日志38%token endpoint returned status 403 forbidden: country服务端地理围栏策略激活使用--region us-east-1参数指定区域或联系管理员开通白名单29%failed to refresh token: 400 bad request: invalid refresh_token: empty stringcaveman不支持refresh_token此错误源于旧版客户端残留配置删除~/.config/caveman/目录重置环境变量15%your access token could not be refreshed. please log out and sign in again.用户混淆了caveman与Web版登录流程直接使用CAVEMAN_TOKEN环境变量忽略所有Web登录按钮12%login server error: token exchange failed: token endpoint returned服务端token endpoint临时不可用查看https://status.caveman.dev等待恢复或切换备用endpoint6%注意所有“token exchange failed”错误都与caveman无关——它是故意不实现OAuth交换流程的。遇到此类错误第一反应不是调试caveman而是检查是否误用了Web版登录凭证。5.2 企业环境特有问题与对策在金融、政务等强管控网络中caveman常遇特殊挑战问题HTTPS证书校验失败现象npx caveman报错UNABLE_TO_VERIFY_LEAF_SIGNATURE。原因企业中间人代理MITM Proxy替换SSL证书Node.js默认不信任其CA。对策临时方案NODE_TLS_REJECT_UNAUTHORIZED0 npx caveman ...仅限测试正式方案将企业CA证书添加到Node.js信任库export NODE_EXTRA_CA_CERTS/path/to/corp-ca.crt npx caveman ...问题NPM registry被屏蔽现象npx caveman卡在“Downloading”阶段。对策配置npm镜像npm config set registry https://registry.npmmirror.com或使用离线包下载caveman-1.2.3.tgz到本地执行npx ./caveman-1.2.3.tgz --help。问题token被安全审计系统拦截现象CI日志显示token被脱敏为v1.****.****。原因审计系统正则匹配v1\.[a-zA-Z0-9_-]{20,}\.模式。对策与安全部门协商将CAVEMAN_TOKEN加入白名单环境变量或改用文件方式npx caveman --token-file ~/.secret/caveman-token文件权限设为600。5.3 开发者最常踩的5个坑坑1在Makefile中直接写token错误写法convert: npx caveman --token v1.xxx.yyy --input main.js后果token泄露到版本库且无法被.gitignore保护。正确写法convert: export CAVEMAN_TOKEN : $(shell cat ~/.secret/token 2/dev/null) convert: npx caveman --input main.js坑2忽略token的scope权限现象npx caveman --model gpt-4-turbo返回403。原因该token仅授予codegen:read权限而gpt-4-turbo需codegen:write。对策重新申请token时勾选对应scope或使用--model claude-3-haiku权限要求更低。坑3用--format raw处理大文件现象终端卡死内存飙升。原因raw格式输出完整JSON含choices[0].message.content的base64编码大文本。对策始终对10KB文件使用--format diff或--format patch。坑4在Docker中未传递环境变量错误写法RUN npx caveman --input /app/src.ts后果容器内无CAVEMAN_TOKEN返回401。正确写法ARG CAVEMAN_TOKEN ENV CAVEMAN_TOKEN$CAVEMAN_TOKEN RUN npx caveman --input /app/src.ts并在docker build时传入docker build --build-arg CAVEMAN_TOKEN$TOKEN .坑5认为caveman能替代完整IDE现象开发者用npx caveman写整个React应用结果类型错误频出。真相caveman是精准手术刀不是万能瑞士军刀。它擅长单文件转换、API文档生成、SQL转ORM、正则表达式调试。不擅长跨文件依赖分析、状态管理设计、性能优化建议。我的经验是caveman处理完的代码必须经ESLintPrettier二次校验再人工Review核心逻辑。6. 生产环境部署与监控实践6.1 Kubernetes集群中的caveman Sidecar模式在微服务架构中我们不将caveman作为独立服务而是以Sidecar形式注入每个需要AI能力的Pod。YAML配置关键段spec: containers: - name: app image: myapp:v1.2 # 主应用容器 - name: caveman-proxy image: ghcr.io/caveman/cli:1.2.3 env: - name: CAVEMAN_TOKEN valueFrom: secretKeyRef: name: caveman-secrets key: token ports: - containerPort: 3001 name: caveman-api readinessProbe: httpGet: path: /health port: 3001 initialDelaySeconds: 5主应用通过http://localhost:3001/v1/chat/completions调用caveman流量不经过Service Mesh避免额外延迟。Sidecar的优势隔离性token存储在Sidecar的Secret中主容器无法直接读取弹性Sidecar可独立扩缩容应对突发AI请求可观测性Sidecar统一打日志包含request_id、model、tokens_used字段便于追踪token用量。6.2 Token用量监控与告警我们用Prometheus监控三个核心指标caveman_token_requests_total{status2xx}成功请求数caveman_tokens_used_total累计消耗token数caveman_request_duration_seconds_bucketP95响应延迟。告警规则示例当token用量超阈值时- alert: CavemanTokenQuotaExceeded expr: sum(rate(caveman_tokens_used_total[24h])) 1000000 for: 1h labels: severity: warning annotations: summary: Caveman token usage exceeded 1M/24h description: Current usage {{ $value }} tokens. Check for runaway jobs.关键洞察token用量与代码行数非线性相关。处理100行JS平均消耗85 tokens但处理1000行时仅消耗620 tokens因模型能复用上下文。因此告警阈值需按项目规模动态调整而非固定值。6.3 安全审计要点清单每年安全审计时我们重点检查✅ 所有CI/CD配置中CAVEMAN_TOKEN是否通过Secret Manager注入而非硬编码✅ 本地开发机上~/.bash_history是否禁用token记录export HISTIGNORE*CAVEMAN_TOKEN*✅ 网络策略是否限制caveman Sidecar仅能访问api.caveman.dev:443禁止其他外连✅ 日志系统是否脱敏
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进