ARTICLE DETAIL

资讯详情

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

Agent Skills:智能体的可靠执行协议与工程化实践

Agent Skills:智能体的可靠执行协议与工程化实践 1. 项目概述Agent-Skills 不是插件而是智能体的“肌肉记忆”“agent-skills”这个标题乍看像一个技术名词但实际它代表的是一套正在快速成型的工程范式——不是某个具体工具也不是某家公司的私有协议而是智能体Agent在真实业务场景中“动手做事”的能力基建层。我从2023年中期开始系统性地构建和落地Agent应用做过客服对话路由、跨系统数据同步、自动化报告生成等十几个生产级项目发现所有能真正跑起来的Agent背后都有一套高度结构化、可复用、可测试的skills体系。它不是LLM的“提示词增强”而是把“调用API”“执行CLI命令”“解析文件”“操作数据库”这些传统后端动作封装成带类型签名、输入校验、错误重试、上下文感知的标准化函数单元。比如你让Agent“查一下今天销售Top 3的SKU”它不会靠大模型硬猜而是精准触发sales_report_top_n这个skill传入{n: 3, date: 2024-06-15}参数拿到结构化JSON结果后再组织语言回复。这就像教一个新员工不光告诉他“去查销售数据”而是给他一张清晰的操作手册、一个已配置好的查询终端、一套标准的返回格式规范。核心关键词“CLI”“slash commands”“API”已经暴露了它的本质——skills是智能体与现实世界交互的协议接口层。CLI不是指终端命令行而是Command-Line Interface的抽象延伸任何可被明确命名、接受结构化输入、返回确定性输出的原子操作都是CLI/slash commands是前端或协作平台如Slack、飞书中用户触发skill的快捷入口本质是UI层对skill ID的映射而API则是skills最底层的执行载体90%以上的skills最终都会转化为HTTP请求、数据库查询或本地进程调用。最近三个月我在三个不同客户现场部署时发现凡是跳过skills设计、直接让LLM“自由发挥”的项目上线两周内必出三类问题一是API调用参数错乱导致库存扣减失败二是CLI命令缺少超时控制卡死整个任务流三是不同skill之间状态不隔离A技能改了全局变量B技能读取到脏数据。这些问题根源不在模型而在skills层缺乏工程约束。所以“agent-skills”真正的价值不是让Agent更“聪明”而是让它更“可靠”——就像给自动驾驶汽车装上ABS和ESP不是提升最高速度而是确保每次刹车都稳、每次转向都准。适合正在搭建内部Agent平台的技术负责人、想把LLM接入业务系统的后端工程师、以及需要快速验证Agent落地可行性的产品经理。如果你还在用prompt硬编码调用逻辑或者把API密钥直接塞进system prompt里那这篇就是为你写的实操指南。2. 整体架构设计为什么必须放弃“Prompt驱动一切”的幻觉2.1 从Prompt到Skills一次认知升级的必要性三年前我们做第一个Agent PoC时所有动作都靠prompt硬编“你是一个电商客服请调用订单查询APIURL是https://api.xxx.com/order?user_id{{user_id}}header带X-API-Key: {{key}}”。当时觉得挺酷直到上线第三天凌晨两点运维电话打来“订单查询接口被打爆了QPS冲到3000监控显示全是重复请求”。排查发现LLM在生成URL时把user_id拼错了导致404错误率97%而重试机制又没加模型就不断生成新请求……这事让我彻底反思把业务逻辑、安全凭证、错误处理全压在prompt里等于让一个刚毕业的实习生同时负责架构设计、代码编写、测试上线和运维监控——风险不可控。skills架构的核心思想就是把“做什么”intent和“怎么做”execution彻底解耦。LLM只负责理解用户意图、拆解任务步骤、选择合适的skill并填充参数而skill本身是一个独立模块自带输入校验比如user_id必须是8位数字、参数转换把自然语言“昨天”转成2024-06-14、重试策略HTTP 503时指数退避重试3次、熔断保护连续5次失败自动暂停调用。这种分离带来的收益是量级的我们最新一个金融风控Agentskills层拦截了83%的非法参数请求避免了下游核心系统被无效流量冲击平均单次任务执行耗时下降42%因为skill复用率高达76%不用每次重新生成完整API调用字符串。2.2 四层技能栈从原子能力到复合行为的演进路径skills不是平铺直叙的函数列表而是一个分层演化的体系。我按能力粒度和抽象程度把它划分为四层每层解决不同维度的问题L0 原子技能Atomic Skills最基础的不可再分操作如http_get、db_query、shell_exec。它们不包含业务逻辑只做纯粹的I/O。例如http_get只接收url和headers返回原始响应体和状态码。关键设计点在于强类型输入输出url必须是合法HTTP URL用正则校验headers必须是Dict[str, str]返回值固定为{status_code: int, body: str, headers: dict}。这样LLM调用时无法传入{url: rm -rf /}这种危险参数——类型系统在第一道门就拦住了。L1 领域技能Domain Skills基于L0组合的业务功能单元如get_user_order_list、calculate_inventory_level。它们封装了领域知识get_user_order_list会自动拼接认证token、设置超时为5秒、对空响应做默认处理。这里的关键是上下文注入——每个skill执行时自动注入当前会话ID、用户角色、时间戳等元信息避免开发者手动传递。我们曾有个电商项目get_user_order_list需要根据用户VIP等级返回不同字段如果靠LLM每次判断错误率高达22%改成skill内嵌规则后准确率100%。L2 流程技能Workflow Skills协调多个L1 skill完成端到端任务如process_refund_request。它定义了执行顺序先查订单状态再校验退款资格最后调支付接口、异常分支订单不存在走A路径库存不足走B路径、状态持久化每步结果存入Redis。重点在于状态机驱动skill不依赖LLM记忆上下文而是通过state key显式管理进度即使Agent重启也能从中断处继续。L3 协作技能Collaborative Skills跨Agent协同能力如escalate_to_human_agent。它不直接执行业务而是触发工作流引擎把当前会话转给人工客服系统并同步历史消息、用户情绪标签、预判解决方案。这是skills向组织级能力演进的关键一步——让Agent不再是孤岛而是企业服务网络中的一个节点。这四层不是理论模型而是我们团队真实的代码目录结构/skills/atomic/、/skills/domain/ecommerce/、/skills/workflow/refund/、/skills/collab/。新成员入职第一天就能通过目录结构快速理解系统能力边界比读几百页文档高效得多。2.3 CLI与Slash Commands不是UI而是技能的“身份证”很多人把CLI和Slash Commands当成前端交互方式其实它们是skills的注册中心和路由表。我们内部叫它“Skill Registry”所有skills必须在这里注册才能被调用。注册时需声明name: 技能唯一标识如ecommerce.get_order_statusdescription: 一句话说明用途供LLM做意图识别parameters: JSON Schema定义的输入参数含类型、必填项、示例值cli_alias: CLI命令别名如/order-statuspermissions: 所需权限列表如[user:read, order:read]当用户输入/order-status 12345678时系统不是简单执行命令而是解析/order-status匹配到ecommerce.get_order_statusskill校验当前用户是否有order:read权限RBAC检查将12345678按Schema转换为{order_id: 12345678}注入会话上下文用户ID、设备类型等调用skill执行器这种设计带来两个关键优势一是安全可控所有调用都经过权限校验和参数净化二是可审计每条CLI记录都包含调用者、时间、参数摘要、执行结果比纯prompt日志清晰十倍。我们曾用这套机制快速定位一个资损问题财务部门发现某天退款金额异常高通过CLI日志筛选出所有/process_refund调用发现是某个测试账号用错误参数触发了批量退款——而如果是prompt驱动这种问题根本无法追溯。3. 核心细节解析如何设计一个生产级Skill3.1 输入校验别让LLM的“脑补”毁掉你的系统LLM最大的特性是“创造性”但对生产系统来说这是最危险的属性。我们吃过太多亏让LLM生成数据库查询语句它把SELECT * FROM users WHERE id ?写成SELECT ALL FROM users WHERE id IS ?让LLM构造API参数它把{page: 1}写成{page: first}。skills的第一道防线就是拒绝一切非结构化输入。我们的校验分三层Schema级校验使用Pydantic V2定义skill输入模型。以get_product_info为例from pydantic import BaseModel, Field, validator from typing import Optional class GetProductInfoInput(BaseModel): sku: str Field(..., min_length6, max_length20, patternr^[A-Z]{2}\d{4}$) include_stock: bool Field(defaultTrue) lang: str Field(defaultzh-CN, patternr^[a-z]{2}-[A-Z]{2}$) validator(sku) def validate_sku_format(cls, v): if not v.startswith(AB) and not v.startswith(CD): raise ValueError(SKU must start with AB or CD) return v.upper()这段代码强制sku必须是6-20位、以AB或CD开头的大写字母数字组合lang必须是标准语言代码。LLM传入{sku: ab1234}会被自动转成{sku: AB1234}传入{sku: xyz}则直接报错根本不会进入执行环节。业务级校验Schema只管格式业务规则要单独写。比如process_refund要求订单状态必须是shipped或delivered我们在skill执行前加一层检查def _validate_order_status(order_id: str) - bool: order db.query(SELECT status FROM orders WHERE id ?, order_id) if order.status not in [shipped, delivered]: raise BusinessRuleError(fOrder {order_id} status {order.status} not eligible for refund) return True这种校验放在skill内部而不是让LLM记住规则——毕竟模型会遗忘代码不会。防注入校验对所有可能拼接SQL或Shell命令的参数做白名单过滤。比如shell_exec技能command参数只允许ls,cat,grep等安全命令且参数必须是预定义的枚举值ALLOWED_COMMANDS {ls: [-l, -a], cat: [config.json, version.txt]} if command not in ALLOWED_COMMANDS: raise SecurityError(fCommand {command} not allowed) if args and args[0] not in ALLOWED_COMMANDS[command]: raise SecurityError(fArgument {args[0]} not allowed for {command})这比任何WAF都有效因为攻击面被压缩到极致。提示校验失败时不要返回模糊错误如“参数错误”而要给出具体修复建议。比如sku格式不对返回SKU格式错误应为AB4位数字如AB1234。LLM看到这种提示下次生成就会修正形成正向反馈。3.2 执行器设计让Skill真正“稳”下来一个skill的执行器Executor决定了它在生产环境的生死。我们总结出五个必须实现的要素超时控制所有外部调用必须设硬超时。HTTP请求用requests.timeout数据库查询用connection.timeoutShell命令用subprocess.run(timeout...)。特别注意超时值不是拍脑袋定的。我们用P99延迟20%作为基准——比如订单查询API P99是800ms我们就设1000ms超时。低于P99太多会误杀正常请求高于太多拖慢整体流程。重试策略不是所有错误都该重试。我们按HTTP状态码分类400类Bad Request立即失败重试无意义429Rate Limited指数退避重试最多3次500/503Server Error线性退避重试最多2次网络超时立即重试最多3次 重试逻辑封装在统一装饰器里避免每个skill重复写retry( retryretry_if_exception_type((requests.Timeout, requests.ConnectionError)), waitwait_exponential(multiplier1, min1, max10), stopstop_after_attempt(3) ) def execute_http_call(url, data): return requests.post(url, jsondata, timeout10)熔断器Circuit Breaker当skill连续失败达到阈值如5分钟内失败10次自动切换到“半开”状态放行1个请求试探成功则恢复失败则继续保持熔断。我们用circuitbreaker库实现关键是熔断状态持久化——不能存在内存里否则进程重启就失效。我们存到Redis键名为circuit_breaker:ecommerce.get_order_status值为JSON{state: open, last_failure: 2024-06-15T10:23:45Z}。结果缓存对读操作skill加LRU缓存。但缓存策略要精细get_product_info按sku缓存TTL设300秒get_sales_summary按date缓存TTL设3600秒。关键是缓存穿透防护当查询skuINVALID时缓存空结果null5秒避免恶意刷空缓存。可观测性埋点每个skill执行前后打日志包含skill_name、input_hash、duration_ms、statussuccess/error、error_type。我们用OpenTelemetry上报到Jaeger能直观看到哪个skill最慢、哪个错误最多。上周发现calculate_inventory_level平均耗时突增到2.3秒追踪发现是缓存失效后DB查询没走索引——没有这些埋点问题根本发现不了。3.3 权限与安全Skills不是特权通道而是最小权限执行器把skills当普通函数调用是最大的安全误区。我们见过最危险的设计一个db_execskill接受任意SQLLLM生成DROP TABLE users;就直接执行了。正确的做法是基于能力的权限模型Capability-Based Access Control每个skill声明自己需要的最小权限集。get_user_profile只需user:readupdate_user_address需要user:write。用户登录时系统根据角色分配权限令牌JWT包含permissions: [user:read, order:read]。skill执行前检查令牌中是否包含所需权限。缺失则拒绝不给任何提示防止权限探测。关键操作如退款、删数据要求二次确认skill返回{requires_confirmation: true, message: 确认退款128.5元}前端弹窗用户点击确认后才真正执行。更进一步我们对敏感skill做沙箱隔离数据库操作skill只能访问只读副本写操作必须走专门的write_gatewayskillShell命令skill运行在Docker容器里挂载只读文件系统网络仅允许访问内网API网关API调用skill的token由Vault动态生成每次调用后立即失效这套机制让我们通过了金融客户的等保三级审计。他们最看重的不是“能不能做”而是“做了什么、谁做的、有没有授权、有没有留痕”。4. 实操过程从零搭建一个可运行的Skills框架4.1 环境准备与依赖安装我们选择Python 3.10作为主语言生态成熟、类型支持好框架基于FastAPI轻量、异步、OpenAPI自动生成。第一步是初始化项目结构mkdir agent-skills cd agent-skills python -m venv venv source venv/bin/activate # Windows用 venv\Scripts\activate pip install fastapi uvicorn pydantic python-jose[cryptography] redis sqlalchemy requests # 安装技能开发专用库 pip install pydantic-settings circuitbreaker python-dotenv关键依赖说明pydantic-settings管理环境变量区分dev/staging/prod配置circuitbreaker熔断器实现比手写状态机可靠python-dotenv从.env文件加载配置避免密钥硬编码环境变量.env示例# 核心配置 ENVIRONMENTdevelopment REDIS_URLredis://localhost:6379/0 DATABASE_URLsqlite:///./app.db # API密钥生产环境应从Vault获取 ECOMMERCE_API_KEYsk_live_abc123 PAYMENT_API_SECRETsec_live_def456 # 熔断器配置 CIRCUIT_BREAKER_FAILURE_THRESHOLD5 CIRCUIT_BREAKER_TIMEOUT_SECONDS300注意.env文件绝不能提交到Git我们在CI流程中用Secret Manager注入本地开发用dotenv加载。曾经有同事误提交API密钥导致公司支付接口被刷单——现在所有密钥都走Vault本地用占位符。4.2 定义第一个原子SkillHTTP GET封装创建skills/atomic/http_get.pyfrom pydantic import BaseModel, Field, HttpUrl from typing import Optional, Dict, Any import requests from circuitbreaker import circuit from fastapi import HTTPException class HttpGetInput(BaseModel): url: HttpUrl Field(..., description目标URL必须是HTTP或HTTPS协议) headers: Optional[Dict[str, str]] Field(default_factorydict, description请求头) timeout: float Field(default10.0, ge1.0, le30.0, description超时时间秒) class HttpGetOutput(BaseModel): status_code: int body: str headers: Dict[str, str] circuit(failure_threshold3, recovery_timeout60) def http_get(input_data: HttpGetInput) - HttpGetOutput: 安全的HTTP GET请求执行器 - 自动校验URL格式 - 强制超时控制 - 熔断保护 - 错误分类处理 try: response requests.get( str(input_data.url), headersinput_data.headers, timeoutinput_data.timeout ) # 对常见错误状态码做友好包装 if response.status_code 404: raise HTTPException(status_code404, detailfResource not found: {input_data.url}) elif response.status_code 500: raise HTTPException(status_code503, detailService unavailable) return HttpGetOutput( status_coderesponse.status_code, bodyresponse.text, headersdict(response.headers) ) except requests.Timeout: raise HTTPException(status_code504, detailRequest timeout) except requests.ConnectionError: raise HTTPException(status_code502, detailConnection failed) except Exception as e: raise HTTPException(status_code500, detailfUnexpected error: {str(e)})这个skill看似简单但包含了所有生产级要素Pydantic校验、熔断器装饰、超时控制、错误分类。测试它# test_http_get.py def test_http_get_success(): input_data HttpGetInput(urlhttps://httpbin.org/get) result http_get(input_data) assert result.status_code 200 assert args in result.body def test_http_get_timeout(): input_data HttpGetInput(urlhttps://httpbin.org/delay/15, timeout5) with pytest.raises(HTTPException) as exc: http_get(input_data) assert exc.value.status_code 5044.3 构建领域Skill电商订单查询基于原子skill构建skills/domain/ecommerce/get_order_status.pyfrom pydantic import BaseModel, Field, constr from typing import Optional import json from skills.atomic.http_get import http_get, HttpGetInput, HttpGetOutput class GetOrderStatusInput(BaseModel): order_id: constr(min_length12, max_length20, regexr^\d$) Field(..., description订单ID纯数字) include_details: bool Field(defaultFalse, description是否包含详细商品信息) class GetOrderStatusOutput(BaseModel): order_id: str status: str # pending, shipped, delivered, cancelled created_at: str items: Optional[list] None # 仅当include_detailsTrue时返回 def get_order_status(input_data: GetOrderStatusInput) - GetOrderStatusOutput: 查询订单状态 - 领域技能示例 - 自动注入认证头 - 参数转换order_id转为API所需格式 - 错误映射API错误转为业务错误 # 构造API请求 api_url fhttps://api.ecommerce.com/v1/orders/{input_data.order_id} # 注入认证头从环境变量或Vault获取 headers { Authorization: fBearer {get_api_key()}, Content-Type: application/json } # 调用原子skill atomic_input HttpGetInput( urlapi_url, headersheaders, timeout8.0 ) atomic_result http_get(atomic_input) # 解析响应 try: data json.loads(atomic_result.body) except json.JSONDecodeError: raise ValueError(Invalid JSON response from API) # 映射到领域模型 output GetOrderStatusOutput( order_idinput_data.order_id, statusdata.get(status, unknown), created_atdata.get(created_at, ), ) if input_data.include_details and items in data: output.items data[items] return output def get_api_key() - str: 安全获取API密钥 - 生产环境应从Vault调用 import os return os.getenv(ECOMMERCE_API_KEY, dummy-key-for-dev)关键点领域逻辑封装自动拼接URL、注入认证头、解析JSON、映射字段LLM只需传order_id安全密钥管理get_api_key()函数是占位符生产环境替换为Vault调用错误处理JSON解析失败、字段缺失都转为明确异常不静默失败4.4 CLI与Slash Command集成让Skill可被用户触发创建api/cli_router.py用FastAPI实现CLI网关from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel from typing import Dict, Any from skills.domain.ecommerce.get_order_status import get_order_status, GetOrderStatusInput from skills.domain.ecommerce.get_user_profile import get_user_profile, GetUserProfileInput import re router APIRouter() # CLI命令注册表 CLI_REGISTRY { /order-status: { skill: get_order_status, input_model: GetOrderStatusInput, description: 查询订单状态用法/order-status order_id }, /user-profile: { skill: get_user_profile, input_model: GetUserProfileInput, description: 查询用户资料用法/user-profile user_id } } class CliRequest(BaseModel): command: str args: str router.post(/cli) async def execute_cli(request: CliRequest): CLI执行入口 支持格式/order-status 12345678 或 /order-status --include-details 12345678 # 解析命令 match re.match(r^/(\w)(?:\s(.*))?$, request.command.strip()) if not match: raise HTTPException(status_code400, detailInvalid command format) cmd_name, args_str match.groups() # 查找注册的skill if cmd_name not in CLI_REGISTRY: raise HTTPException(status_code404, detailfCommand /{cmd_name} not found) registry CLI_REGISTRY[cmd_name] # 解析参数简化版生产用argparse if args_str: # 处理--flag参数和位置参数 args_dict {} parts args_str.split() i 0 while i len(parts): if parts[i].startswith(--): key parts[i][2:] if i 1 len(parts) and not parts[i 1].startswith(--): args_dict[key] parts[i 1] i 2 else: args_dict[key] True i 1 else: # 位置参数假设第一个是order_id if order_id not in args_dict: args_dict[order_id] parts[i] i 1 else: args_dict {} # 实例化输入模型 try: input_instance registry[input_model](**args_dict) except Exception as e: raise HTTPException(status_code400, detailfInvalid arguments: {str(e)}) # 执行skill try: result registry[skill](input_instance) return {success: True, result: result.dict()} except HTTPException: raise except Exception as e: raise HTTPException(status_code500, detailfSkill execution failed: {str(e)})测试CLI# 启动服务 uvicorn main:app --reload # 发送CLI请求 curl -X POST http://localhost:8000/cli \ -H Content-Type: application/json \ -d {command:/order-status,args:12345678}返回{ success: true, result: { order_id: 12345678, status: shipped, created_at: 2024-06-10T14:23:15Z, items: null } }这就是一个可运行的CLI网关。前端如Slack App只需把用户输入的/order-status 12345678转发到这里就能得到结构化结果。4.5 技能市场与动态加载让Skills可插拔、可热更新Skills不能写死在代码里必须支持动态注册。我们设计了一个SkillManager从文件系统或数据库加载skills# core/skill_manager.py import importlib import os from pathlib import Path from typing import Dict, Callable, Any class SkillManager: def __init__(self): self.skills: Dict[str, Callable] {} self.input_models: Dict[str, Any] {} def load_skills_from_dir(self, dir_path: str): 从目录动态加载skills for file_path in Path(dir_path).rglob(*.py): if file_path.name.startswith(_) or file_path.name __init__.py: continue # 计算模块路径 rel_path file_path.relative_to(Path(dir_path)) module_name str(rel_path).replace(/, .).replace(.py, ) try: module importlib.import_module(fskills.{module_name}) # 查找skill函数约定函数名以skill_开头 for attr_name in dir(module): if attr_name.startswith(skill_): func getattr(module, attr_name) skill_name attr_name[6:] # 去掉skill_前缀 self.skills[skill_name] func # 尝试获取input model model_name attr_name.replace(skill_, Input) if hasattr(module, model_name): self.input_models[skill_name] getattr(module, model_name) print(fLoaded skills from {file_path}) except Exception as e: print(fFailed to load {file_path}: {e}) def get_skill(self, name: str) - Callable: return self.skills.get(name) # 初始化 skill_manager SkillManager() skill_manager.load_skills_from_dir(./skills)这样新增一个skill只需在skills/domain/下新建文件无需重启服务。我们还做了热重载监听文件变化自动reload模块——当然生产环境慎用测试环境极大提升开发效率。5. 常见问题与排查技巧实录那些踩过的坑希望你绕开5.1 LLM调用Skill失败的三大高频原因及诊断法在20个项目中LLM无法正确调用skill的情况占比超60%。根本原因不是模型不行而是提示工程和skill设计不匹配。以下是真实案例和解法问题1参数名不一致导致LLM“瞎猜”场景skill定义GetOrderStatusInput.order_id但LLM总生成{orderId: 123}。根因Pydantic模型字段名是order_id但LLM训练数据里API参数多用驼峰命名。解法在skill描述中显式声明参数映射class GetOrderStatusInput(BaseModel): order_id: str Field(..., description订单IDAPI参数名order_id不是orderId)更彻底的方案在LLM的system prompt里加约束“所有参数名必须严格匹配skill定义的snake_case字段名禁止使用camelCase或kebab-case”。问题2LLM过度“优化”参数传入非法值场景get_user_profile要求user_id是8位数字LLM却传{user_id: U12345678}带前缀。根因LLM从用户消息“查用户U12345678的资料”中提取ID没做清洗。解法在skill输入模型中加预处理钩子validator(user_id) def clean_user_id(cls, v): return re.sub(r[^0-9], , v) # 移除非数字字符这样即使LLM传U12345678也会被转成12345678。问题3LLM忽略required字段传空参数场景process_refund要求refund_amountLLM却传{}导致skill校验失败。根因LLM认为“金额可以计算”但skill没提供计算逻辑。解法双保险策略在skill描述中强调“refund_amount为必填数值单位分不可为空或0”在LLM调用前加参数补全中间件检测缺失required字段用默认值或调用辅助skill如estimate_refund_amount填充。诊断工具我们开发了一个skill_call_analyzer记录每次LLM生成的skill调用JSON对比skill Schema自动生成不匹配报告。上线后参数错误率从31%降到4%。5.2 性能瓶颈排查为什么你的Skill总是超时Skills性能问题往往藏在“看不见”的地方。分享三个真实瓶颈点瓶颈1DNS解析阻塞现象http_get偶尔超时但API本身P99200ms。排查用tcpdump抓包发现DNS查询耗时2秒。解法在Docker容器中配置--dns8.8.8.8或在代码中用requests.Session预热DNSsession requests.Session() session.get(https://api.example.com, timeout1) # 预热瓶颈2JSON序列化反序列化开销现象处理大响应体1MB时skill耗时80%花在json.loads()。排查用cProfile分析json.loads占CPU时间75%。解法对大响应体改用ijson流式解析只提取需要的字段import ijson parser ijson.parse(urllib.request.urlopen(url)) # 只取items数组的第一个元素 for prefix, event, value in parser: if prefix items.item and event start_map: break瓶颈3数据库连接池耗尽现象
返回列表