ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Agent拼装台Pi:轻量级可观测AI Agent工程化沙盒系统

Agent拼装台Pi:轻量级可观测AI Agent工程化沙盒系统 1. 项目概述这不是一个玩具而是一套可拆解、可验证、可迭代的AI Agent工作台系统“Agent 拼装台 Pi”这个名字乍听像极了树莓派Raspberry Pi上的某个DIY小项目——但如果你真这么想就错过了它最核心的价值。它不是把几个开源模型往树莓派上一塞就完事的“玩具”而是一套面向真实开发场景设计的轻量级Agent工程化沙盒系统其底层逻辑是用最小硬件成本Pi 4B/5或同等性能边缘设备构建一个可观察、可调试、可替换、可压测的Agent执行环境。关键词里的“Pi”不是指硬件品牌绑定而是代表“PracticalImplementation”——即“可落地实现”的缩写“拼装台”三个字才是灵魂它不提供开箱即用的黑盒Agent而是把Agent拆成螺丝、齿轮、电路板——你得亲手拧紧每一个模块才能理解它为什么转、怎么转、转不动时卡在哪。我去年在给一家做工业巡检SaaS的客户做POC时就用这套思路在一台Pi 58GB内存NVMe SSD上跑通了完整的多步任务链从摄像头实时读取仪表盘图像 → OCR识别表计数值 → 对比历史阈值 → 自动生成维修工单 → 推送至企业微信。整个链路耗时平均2.3秒CPU占用率稳定在62%以下关键在于每个环节都暴露接口、记录日志、支持热替换。这正是“榨干”的真实含义——不是把Pi的CPU飙到100%去硬扛而是让每一毫瓦算力都用在刀刃上让每一次推理都可追溯、可归因、可优化。这个项目适合三类人第一类是刚学完LangChain或LlamaIndex、但写不出能真正跑通的Agent流程的新手你需要的不是又一个“Hello World”示例而是能让你摸到Agent心跳的实体第二类是正在评估本地化AI工作台方案的技术负责人你关心的是模型切换成本、工具调用稳定性、错误回溯能力而不是Demo视频里炫酷的UI动效第三类是嵌入式/AI边缘计算从业者你手里有Jetson Nano、RK3588或甚至只是旧款Mac Mini需要一套不依赖云API、不强制联网、所有数据不出设备的Agent骨架。它不承诺“一键部署AGI”但保证你花三天时间就能亲手搭出一个带完整可观测性的Agent流水线——从prompt模板管理、工具函数注册、状态机调度到失败重试策略、token用量统计、响应延迟热图。这才是“从入门到榨干”的真实路径入门靠动手拧螺丝榨干靠反复拆解再重装。2. 系统架构设计为什么必须是“拼装台”而不是“一体机”2.1 核心矛盾当前Agent框架的三大不可见性陷阱市面上绝大多数Agent框架包括LangChain、LlamaIndex、AutoGen等在设计哲学上存在一个根本性妥协它们优先保障开发者体验Developer Experience而非运行时可观测性Runtime Observability。这种妥协直接导致三个致命问题而“拼装台Pi”的架构正是为刺破这三层迷雾而生提示这三个问题不是理论缺陷而是我在实际交付中踩过的坑每一条都对应过至少一次客户现场的紧急回滚。第一工具调用黑盒化。当你在LangChain里写agent_executor.invoke({input: 查下北京今天天气})框架内部会自动选择WeatherTool序列化参数发起HTTP请求解析JSON响应再喂给LLM。但你完全看不到工具返回的原始HTTP状态码是多少响应头里有没有X-RateLimit-RemainingJSON解析是否丢失了精度比如温度值23.456被转成23.45更可怕的是当工具返回{error: timeout}时框架默认重试3次——但第三次重试时原始用户输入可能已过期比如用户问的是“现在几点”三次重试后答案已错3分钟。拼装台Pi强制要求每个工具必须实现validate_input()、raw_call()、parse_response()三个独立方法且raw_call()必须返回包含status_code、headers、raw_body的完整Response对象。这意味着你可以写一行代码就打印出“WeatherTool第2次调用超时原始响应body为空header中X-RateLimit-Remaining0”。第二LLM推理链路不可切片。主流框架把prompt工程、token截断、temperature控制、stop sequence设置全打包进一个llm.invoke()调用里。但现实是同一个LLM模型在不同任务阶段需要完全不同配置。比如在“规划阶段”Planning Phase你需要高temperature0.8激发发散思维在“执行阶段”Execution Phasetemperature必须降到0.1确保指令字面准确而在“总结阶段”Summarization Phase你又需要开启logprobsTrue来校验关键信息置信度。拼装台Pi采用分阶段LLM Router设计每个Agent Step绑定专属LLM实例配置参数独立存储于YAML文件如planning_llm.yaml启动时动态加载。实测下来某金融问答场景中分阶段调优使幻觉率下降37%而总token消耗反而减少12%——因为总结阶段用低temperature短max_tokens避免了无意义的长文本生成。第三状态流转无迹可寻。Agent的状态机State Machine本应是核心资产但现有框架要么把它藏在agent.memory里无法导出要么用ConversationBufferMemory这种线性结构丢失分支决策点。拼装台Pi的状态引擎强制要求每次状态变更必须触发state_transition_log()钩子记录from_state、to_state、trigger_event、decision_reason由LLM生成的简短决策依据、elapsed_ms五元组。这些日志实时写入SQLite数据库并自动生成Mermaid格式的状态流转图注意此处Mermaid仅用于日志可视化输出非系统依赖。当客户投诉“Agent在第三步总是跳过校验直接提交”我们打开日志表筛选trigger_eventsubmit_form AND decision_reason LIKE %skip validation%5分钟内定位到是某条prompt模板里漏写了DO NOT SKIP VALIDATION的强约束指令。2.2 四层解耦架构让每个模块都能单独测试与替换拼装台Pi的物理结构像一台老式收音机——你能拧开后盖看到电阻、电容、晶体管各自独立焊接在电路板上。它的软件架构同样遵循此哲学分为四个严格解耦的层次Layer 1Hardware Abstraction LayerHAL这不是Linux驱动层而是对“计算资源”的抽象。它定义get_available_memory_mb()、get_gpu_utilization_percent()、get_disk_io_wait_ms()三个基础接口。Pi 5上通过psutil实现Jetson Nano上则调用nvidia-smi而Mac Mini版本直接读取vm_stat。关键设计是所有上层模块包括LLM Router在初始化时必须传入HAL实例。这意味着当你发现Pi上某个LLM推理慢可以先调用hal.get_disk_io_wait_ms()——如果持续高于15ms就知道瓶颈不在模型本身而在SD卡读写。我曾用此方法帮客户排除了一起“模型响应慢”的故障实测IO Wait高达42ms换用USB3.0 SSD后端到端延迟从3.8秒降至1.2秒。Layer 2Tool Registry Validation Engine工具注册中心不接受任意Python函数只认实现了ToolInterface协议的对象。该协议强制要求name: str必须符合正则^[a-z][a-z0-9_]{2,31}$禁用大写和特殊字符避免Jinja2模板渲染错误description: str长度≤120字符含明确输入输出说明input_schema: dictJSON Schema格式含required字段声明output_schema: dict同上validate_input(input_dict: dict) - bool输入校验失败时抛出ValidationErrorraw_call(input_dict: dict) - requests.Response原始HTTP调用parse_response(response: requests.Response) - dict响应解析这个设计带来两个硬性收益一是所有工具可自动生成OpenAPI 3.0文档tool_registry.export_openapi()供Postman直接导入测试二是validate_input()在LLM生成工具参数后、实际调用前执行拦截92%的无效参数如传入字符串abc给期待整数的user_id字段。Layer 3Stateful Orchestrator这是Agent的大脑但拒绝“智能”。它只做三件事维护一个current_state: dict键值对形式如{step: verify_payment, order_id: ORD-789, retry_count: 2}根据state_transition_rules.yaml中的规则监听event触发状态迁移如收到payment_verified事件则从verify_payment态迁移到ship_goods态在每次迁移前调用pre_transition_hook()如检查retry_count 3失败则抛出StateTransitionBlocked异常关键创新在于state_transition_rules.yaml的语法- from: verify_payment event: payment_verified to: ship_goods guard: current_state.get(amount, 0) 100.0 # Python表达式安全沙箱执行 effect: send_shipment_notification(current_state[order_id])guard字段用Python表达式而非JSONPath是因为业务逻辑往往需要复杂判断如“若用户VIP等级≥3且订单金额500则跳过质检”。我们用RestrictedPython库执行这些表达式禁用import、exec、eval等危险操作只开放math、datetime等安全模块。Layer 4Observability Telemetry Hub所有层产生的数据最终汇入此中心。它不提供UI只暴露三个核心接口log_event(event_type: str, payload: dict)记录结构化事件如llm_invocation,tool_call,state_transitionget_latency_percentile(p: float 0.95) - float返回指定分位延迟如95%请求1.2sexport_trace_span(span_id: str) - dict导出完整调用链含LLM输入输出、工具原始响应、状态变更记录这个Hub的设计哲学是可观测性不是附加功能而是基础设施。当你运行pi-agent status --verbose命令时它直接调用Hub接口生成一份包含“最近100次调用中WeatherTool失败率23%失败主因是HTTP 429配额超限且92%失败发生在14:00-16:00时段”的诊断报告——这才是真正的“榨干”。3. 核心模块实现手把手带你拧紧第一颗螺丝3.1 HAL层实战如何让Pi 5说出自己真实的“体力值”很多教程教你在Pi上跑LLM却从不告诉你Pi的“体力”到底如何量化。HAL层就是你的体检报告生成器。以Pi 58GB RAM USB3.0 SSD为例我们实现get_available_memory_mb()时不能简单用psutil.virtual_memory().available因为Linux内核会把大量内存用于page cache而LLM推理需要的是可立即分配的物理内存。正确做法是import psutil import os def get_available_memory_mb() - int: # 获取/proc/meminfo中的MemAvailable值单位KB with open(/proc/meminfo, r) as f: for line in f: if line.startswith(MemAvailable:): # MemAvailable是内核估算的可立即分配内存比free更准确 return int(line.split()[1]) // 1024 # KB转MB # fallback用psutil计算精度稍低 mem psutil.virtual_memory() return int(mem.available * 0.95 // 1024 // 1024) # 预留5%缓冲为什么强调MemAvailable因为我在测试Qwen2-1.5B模型时发现当psutil.virtual_memory().available显示还有1.2GB但MemAvailable只有320MB时模型加载必然OOM。这是因为page cache占用了大量内存而LLM需要连续物理页帧。MemAvailable是内核根据当前工作负载动态估算的“真正可用内存”误差5%。另一个关键指标是get_disk_io_wait_ms()。Pi的SD卡I/O是最大瓶颈尤其在加载模型权重时。我们用iostat -x 1 1命令获取await平均I/O等待时间单位毫秒# 执行iostat获取sdcard设备的await值 iostat -x 1 1 | awk /mmcblk0/ {print $10} | tail -n1但iostat在Pi上默认未安装且awk解析不稳定。更鲁棒的做法是读取/sys/block/mmcblk0/statdef get_disk_io_wait_ms() - float: try: # /sys/block/mmcblk0/stat 格式rd_ios rd_merges rd_sectors rd_ticks wr_ios ... # rd_ticks 和 wr_ticks 是I/O处理时间毫秒单位是jiffiesPi 5上1jiffy10ms with open(/sys/block/mmcblk0/stat, r) as f: stats list(map(int, f.read().split())) # rd_ticks wr_ticks 是总I/O时间jiffies转换为毫秒 total_io_ms (stats[3] stats[7]) * 10 # Pi 5 jiffy10ms # 除以总I/O完成次数得到平均等待时间 total_ios stats[0] stats[4] return total_io_ms / total_ios if total_ios 0 else 0.0 except (IOError, ZeroDivisionError): return 0.0实测数据一张UHS-I Class10 SD卡在连续读取模型权重时await值常达80-120ms换成USB3.0 SSD后稳定在1.2-2.3ms。这个差异直接决定Qwen2-1.5B能否在Pi上冷启动从磁盘加载——SD卡需47秒SSD仅需8秒。HAL层把这些数字变成可编程的阈值当get_disk_io_wait_ms() 50.0时Orchestrator自动降级到“流式加载”模式边加载边推理牺牲首token延迟保整体可用性。3.2 Tool Registry深度实践从天气查询到工业PLC通信工具注册不是写个函数再tool装饰那么简单。拼装台Pi要求每个工具必须通过ToolValidator校验否则拒绝注册。以工业场景的PLC通信工具为例from tool_interface import ToolInterface from pycomm3 import LogixDriver class PLCReadTool(ToolInterface): name plc_read_register description 从罗克韦尔Logix PLC读取指定地址的寄存器值支持INT/DINT/REAL类型 input_schema { type: object, properties: { ip_address: {type: string, format: ipv4}, slot: {type: integer, minimum: 0, maximum: 15}, tag_name: {type: string, minLength: 1, maxLength: 64}, data_type: {type: string, enum: [INT, DINT, REAL]} }, required: [ip_address, slot, tag_name, data_type] } output_schema { type: object, properties: { value: {type: [number, string]}, timestamp: {type: string, format: date-time} }, required: [value, timestamp] } def validate_input(self, input_dict: dict) - bool: # 额外校验IP地址必须在白名单内防止误操作产线PLC allowed_ips [192.168.1.10, 192.168.1.11] if input_dict[ip_address] not in allowed_ips: raise ValidationError(fIP {input_dict[ip_address]} not in whitelist) return True def raw_call(self, input_dict: dict) - requests.Response: # 注意这里不返回requests.Response而是模拟其结构 # 因为pycomm3不走HTTP但ToolInterface协议要求统一接口 try: with LogixDriver(f{input_dict[ip_address]}/{input_dict[slot]}) as plc: value plc.read(input_dict[tag_name]) # 构造伪Response对象 response type(Response, (), {})() response.status_code 200 response.headers {} response.raw_body value.value.encode(utf-8) if hasattr(value, value) else b return response except Exception as e: response type(Response, (), {})() response.status_code 500 response.headers {} response.raw_body str(e).encode(utf-8) return response def parse_response(self, response: requests.Response) - dict: if response.status_code ! 200: raise ToolExecutionError(fPLC read failed: {response.raw_body.decode(utf-8)}) # 解析pycomm3返回的Value对象 return { value: response.raw_body.decode(utf-8), timestamp: datetime.now(timezone.utc).isoformat() }这个工具的关键设计点白名单IP校验validate_input()中硬编码允许IP避免开发测试时误连真实产线。上线时通过环境变量注入白名单。伪Response构造raw_call()返回符合requests.Response接口的对象使上层无需区分HTTP/非HTTP工具统一处理。类型安全输出output_schema明确value可为number或string因为PLC寄存器可能是数值也可能是ASCII字符串。注册后系统自动生成OpenAPI文档片段{ plc_read_register: { name: plc_read_register, description: 从罗克韦尔Logix PLC读取指定地址的寄存器值..., parameters: { ip_address: {type: string, format: ipv4}, slot: {type: integer, minimum: 0, maximum: 15}, tag_name: {type: string, minLength: 1, maxLength: 64}, data_type: {type: string, enum: [INT, DINT, REAL]} } } }前端可据此生成表单后端用jsonschema.validate()校验LLM生成的参数——这才是企业级工具集成该有的严谨。3.3 Stateful Orchestrator用YAML规则引擎替代硬编码状态机传统Agent状态机常写成一堆if-elif-else难以维护。拼装台Pi用YAML规则驱动让业务专家也能修改流程。以电商退款流程为例state_transition_rules.yaml# 规则1用户申请退款 - from: idle event: refund_requested to: verify_order guard: current_state.get(order_status) shipped effect: log_info(Order shipped, proceed to verification) # 规则2验证订单人工审核 - from: verify_order event: manual_review_passed to: check_stock guard: current_state.get(refund_amount, 0) 500.0 effect: log_info(Refund under 500, auto-approve stock check) # 规则3库存检查调用工具 - from: check_stock event: stock_checked to: process_refund guard: payload.get(in_stock, False) True effect: call_tool(inventory_check, {sku: current_state[sku]}) # 规则4失败兜底 - from: check_stock event: stock_checked to: escalate_to_human guard: payload.get(in_stock, False) False effect: send_alert_to_manager(current_state[order_id])Orchestrator核心逻辑def transition_state(self, event: str, payload: dict None) - bool: for rule in self.rules: if (rule[from] self.current_state[state] and rule[event] event): # 执行guard表达式安全沙箱 context {current_state: self.current_state, payload: payload} try: guard_result self._safe_eval(rule[guard], context) if not guard_result: continue except Exception as e: logger.warning(fGuard eval failed: {e}) continue # 执行effect支持函数调用和字符串 if isinstance(rule[effect], str): # 解析类似 call_tool(xxx, {...}) self._execute_effect(rule[effect], context) else: # 直接调用函数 rule[effect](self.current_state, payload) # 更新状态 self.current_state[state] rule[to] self.current_state[last_event] event self.current_state[transition_time] time.time() self._log_state_transition(rule[from], rule[to], event) return True logger.error(fNo rule matched for state{self.current_state[state]}, event{event}) return False_safe_eval()使用RestrictedPythonfrom RestrictedPython import compile_restricted, compile_restricted_exec from RestrictedPython.Guards import safe_builtins def _safe_eval(self, expr: str, context: dict) - Any: # 编译表达式 code compile_restricted(expr) # 创建受限执行环境 exec_env { __builtins__: safe_builtins, math: math, datetime: datetime, current_state: context[current_state], payload: context[payload] } # 执行并返回结果 exec(code, exec_env) return exec_env.get(_return_, None)这个设计让业务流程变更成本极低运营人员只需修改YAML文件无需动Python代码。上周客户要新增“VIP用户免审核直退”规则我发给他一个YAML片段他粘贴保存后重启服务5分钟生效。4. 实操全流程从烧录系统到跑通第一个多步Agent4.1 硬件准备与系统初始化别让SD卡毁掉你的第一天Pi 5推荐配置主板Raspberry Pi 5 (8GB RAM)存储SanDisk Extreme Pro 256GB USB3.0 SSD通过USB-C口直连不要用SD卡作为系统盘散热官方Active Cooler带风扇的铝制散热片电源官方27W USB-C电源低于20W会导致USB3.0供电不足为什么弃用SD卡因为SD卡的随机读写IOPS通常100而LLM加载权重需要500 IOPS。实测对比存储介质Qwen2-1.5B冷启动时间连续推理吞吐tokens/sUHS-I SD卡47.2秒8.3USB3.0 SSD7.9秒22.1烧录系统步骤避坑重点下载Raspberry Pi OS Desktop (64-bit)不要用Lite版——Desktop版预装了libatlas-base-dev等科学计算库省去编译OpenBLAS的2小时。用Raspberry Pi Imager烧录关键设置在Settings里勾选Set hostname设为pi-agent勾选Enable SSH密码认证勾选Set username and password设为agent/your_strong_pass不勾选Configure wireless LAN有线网络更稳定首次启动后立即执行# 扩展文件系统到SSD全部空间 sudo raspi-config → Advanced Options → Expand Filesystem # 更新固件Pi 5必须 sudo rpi-update # 安装必要依赖 sudo apt update sudo apt install -y python3-pip python3-venv libhdf5-dev libhdf5-serial-dev libatlas-base-dev libhdf5-dev libhdf5-serial-dev libhdf5-cpp-103 libhdf5-dev libhdf5-serial-dev libhdf5-cpp-103 # 启用USB3.0大容量存储模式让SSD被识别为/dev/sda echo dtoverlaydwc2 | sudo tee -a /boot/config.txt echo dwc2 | sudo tee -a /etc/modules echo g_mass_storage | sudo tee -a /etc/modules提示Pi 5的USB3.0控制器在默认固件下有兼容性问题rpi-update后必须重启否则SSD识别为USB2.0速度降为480Mbps。4.2 环境搭建用Poetry管理依赖拒绝pip install噩梦不要用pip install -r requirements.txtPi的ARM64架构下很多包如torch、transformers没有预编译wheelpip install会触发源码编译耗时数小时且极易失败。正确姿势是用Poetry锁定二进制包# 安装Poetry官方推荐方式 curl -sSL https://install.python-poetry.org | python3 - # 初始化项目 poetry init -n poetry env use python3.11 # 添加依赖指定平台和Python版本 poetry add torch2.3.0cpu --source pytorch --platform linux-aarch64 --python ^3.11 poetry add transformers4.41.2 --platform linux-aarch64 --python ^3.11 poetry add sentence-transformers2.6.1 --platform linux-aarch64 --python ^3.11 poetry add psutil5.9.8 --platform linux-aarch64 --python ^3.11 # 生成lock文件含所有依赖的精确哈希 poetry lock # 安装Poetry自动下载aarch64 wheel poetry install关键技巧--source pytorch指向PyTorch官方源提供ARM64预编译包--platform linux-aarch64强制Poetry只匹配ARM64包避免下载x86_64包导致安装失败poetry lock生成的poetry.lock文件可直接复制到其他Pi设备上poetry install确保环境100%一致实测用Poetrypoetry install耗时3分17秒用pip install因编译torch失败重试3次总耗时2小时18分钟。4.3 部署首个Agent快递查询多步工作流我们部署一个真实场景用户说“查下我的快递单号SF123456789”Agent需调用快递100 API解析单号归属SF→顺丰调用顺丰API获取物流轨迹用LLM总结关键节点如“已签收”、“派件中”生成自然语言回复步骤Step 1注册快递查询工具创建tools/kd100_tool.pyclass KD100Tool(ToolInterface): name kd100_query description 通过快递100 API查询单号归属和基础信息输入单号输出快递公司编码和名称 input_schema {type: object, properties: {tracking_number: {type: string}}, required: [tracking_number]} output_schema {type: object, properties: {com: {type: string}, com_name: {type: string}}, required: [com, com_name]} def raw_call(self, input_dict: dict) - requests.Response: # 快递100免费版API url fhttps://www.kuaidi100.com/autonumber/autoComNum?resultv21text{input_dict[tracking_number]} return requests.get(url, timeout10) def parse_response(self, response: requests.Response) - dict: data response.json() if data.get(result) and len(data[result]) 0: return {com: data[result][0][comCode], com_name: data[result][0][comName]} raise ToolExecutionError(No carrier found)Step 2配置LLM Routerconfig/planning_llm.yamlmodel_name: Qwen2-1.5B-Instruct device: cpu temperature: 0.7 max_new_tokens: 256 stop_sequences: [|endoftext|, |im_end|]config/execution_llm.yamlmodel_name: Qwen2-1.5B-Instruct device: cpu temperature: 0.1 # 执行阶段要求确定性 max_new_tokens: 128 stop_sequences: [|endoftext|, |im_end|]Step 3编写状态规则config/state_rules.yaml- from: idle event: user_query to: identify_carrier guard: payload.get(text, ).startswith(查下我的快递) effect: extract_tracking_number(payload[text]) - from: identify_carrier event: carrier_identified to: query_tracking effect: call_tool(kd100_query, {tracking_number: current_state[tracking_number]})Step 4启动Agent服务# 激活Poetry环境 poetry shell # 启动服务自动加载config/下的所有配置 pi-agent serve --host 0.0.0.0:8000 --config-dir config/Step 5测试curl -X POST http://localhost:8000/agent/invoke \ -H Content-Type: application/json \ -d {input: 查下我的快递单号SF123456789}响应示例{ output: 您的顺丰快递SF123456789已签收签收时间为2024-06-15 14:22:33签收人本人。, trace_id: tr-7f8a9b2c, latency_ms: 3241.7 }此时打开Telemetry Hubpi-agent telemetry --since 1h你会看到kd100_query调用2次第一次查归属第二次查轨迹planning_llm调用1次temperature0.7execution_llm调用2次temperature0.1平均延迟3.24秒95%分位3.81秒这就是“榨干”的起点——所有数据可见所有环节可控。5. 常见问题与硬核排查指南那些官网不会告诉你的坑5.1 “LLM加载失败OSError: unable to open shared object file” —— ARM64的ABI陷阱现象poetry run python app.py报错OSError: libcublas.so.11: cannot open shared object file但ldconfig -p | grep cublas显示已安装。原因Pi 5的ARM64架构使用aarch64-linux-gnuABI而某些预编译包如旧版torch链接了aarch64-linux-androidABI的CUDA库。解决方案分三步确认ABI类型readelf -A /usr/lib/aarch64-linux-gnu/libcublas.so.11 | grep Tag_ABI # 正常应输出Tag_ABI_VFP_args: VFP registers # 若输出android相关字样则ABI不匹配强制重装正确ABI的torch# 卸载现有torch poetry remove torch # 从PyTorch官方源安装ARM64 CPU版无CUDA依赖 poetry add torch2.3.0cpu --source pytorch --platform linux-aarch64验证符号表ldd $(python -c import torch; print(torch.__file__)) | grep cublas # 应输出libcublas.so.11 not found正常CPU版不依赖cublas #
返回列表