基于llama.cpp构建跨平台本地AI助手:从模型部署到智能体开发实战
1. 项目概述构建一个真正属于你的本地AI助手在AI应用井喷的今天我们似乎已经习惯了将对话、文档甚至个人思考都托付给云端服务。但随之而来的隐私焦虑、网络延迟和持续的订阅费用总让人感觉缺了点什么。有没有一种可能让一个足够聪明的AI助手完全运行在你自己的手机或电脑上数据不出门响应零延迟还能通过一个安全的通道让你在任何地方访问它这正是Asbestos项目试图回答的问题。Asbestos不是一个简单的模型封装器它是一个雄心勃勃的、面向生产级别的跨平台研究项目。它的核心目标很明确将强大的llama.cpp C推理引擎深度集成到Android、iOS和桌面原生环境中并在此基础上构建一个功能完备、能够自主执行任务的本地智能体Agent最后通过安全的端口转发技术将这个本地能力安全地暴露到公网。简单来说它让你在消费级硬件甚至只是CPU上拥有一个私有的、高性能的、可远程访问的ChatGPT式体验。这不仅仅是技术演示更是一种“数据主权”理念的工程实践——你完全拥有并控制你的AI和数据流。2. 核心架构与技术选型解析2.1 为什么选择llama.cpp作为统一后端在启动一个跨平台本地AI项目时后端引擎的选择是决定性的。Asbestos选择了llama.cpp这背后是一系列经过深思熟虑的权衡。首先性能与效率是首要考量。llama.cpp以其极致的C优化而闻名它通过量化技术、内存映射mmap和针对不同CPU指令集如AVX2、AVX512的优化使得大型语言模型LLM能够在没有独立GPU的消费级设备上流畅运行。对于移动端Android/iOS和资源受限的桌面环境这种对计算和内存效率的榨取是至关重要的。一个未经优化的引擎可能会让手机发烫、响应迟缓导致用户体验灾难。其次跨平台一致性。llama.cpp使用纯C编写并提供了清晰的C接口。这意味着我们可以为Android通过JNI、iOS通过Objective-C桥接和桌面CLI编写统一的核心调用逻辑。虽然各平台UI和构建系统不同但底层的模型加载、推理循环和上下文管理可以共享同一套经过充分测试的C代码库极大地减少了维护成本和潜在的错误。最后活跃的社区与模型生态。llama.cpp支持的GGUF模型格式已成为本地部署的事实标准。Hugging Face等模型社区上有海量的、针对不同任务优化的GGUF格式模型从轻量级的0.5B参数模型到庞大的70B参数模型为Asbestos项目的功能扩展如代码生成、多语言支持提供了坚实的基础。选择llama.cpp就是选择了一个繁荣的上下游生态。2.2 移动端原生体验的实现策略移动端开发的核心矛盾在于如何在利用高性能C后端的同时提供流畅、符合平台规范的原生用户体验Asbestos给出了“桥接层原生UI”的答案。对于Android平台项目采用了标准的JNI (Java Native Interface)方案。Kotlin编写的UI层通过JNI调用封装好的C函数。这里的挑战在于构建系统的配置。项目没有使用简单的预编译库而是将llama.cpp作为子模块通过CMake在编译时与Android NDK工具链一起构建。这样做的好处是能针对目标设备如ARM64-v8a进行最优编译但带来了构建配置的复杂性这也是后面会提到的挑战之一。UI方面它实现了带进度反馈的模型下载器这看似简单实则涉及后台任务管理、磁盘空间预检和网络状态恢复是保证用户体验稳定性的关键。对于iOS平台策略更为巧妙。它没有直接嵌入源码而是构建了一个XCFramework。XCFramework是苹果推荐的二进制分发格式它可以将为真机arm64和模拟器x86_64编译的库打包在一起。Asbestos-ios目录下的Swift Package封装了这个XCFramework并提供了一套Swift友好的API。这样主应用asbestos-ios-app可以像导入任何其他Swift包一样使用AI引擎完全隔离了底层的C细节。性能上它充分利用了苹果的Metal框架进行GPU加速推理对于不支持Metal的旧设备或复杂操作则回退到使用Accelerate框架进行CPU加速确保了兼容性与性能的平衡。2.3 从静态模型到主动智能体Agent的演进仅仅能对话的模型只是一个“知识库”而Asbestos的asbestos-agent模块将其升级为了一个“执行者”。这是项目从“玩具”迈向“工具”的关键一步。智能体的核心思想是赋予LLM使用工具的能力。在Asbestos中这个智能体运行在一个本地的Python FastAPI服务器内。它内部启动了一个llama-server进程来自llama.cpp项目该进程提供了基础的聊天补全功能。FastAPI服务器则扮演了“大脑”和“调度中心”的角色封装OpenAI API格式它将llama-server的接口包装成标准的/v1/chat/completions端点这使得任何兼容OpenAI API的客户端包括其自带的Web UI都能直接连接。工具调用与执行当用户请求涉及实际操作如“列出当前目录文件”、“创建一个笔记”时智能体会规划步骤调用对应的Python函数工具来执行Shell命令、读写文件等。安全拦截机制这是智能体设计的重中之重。任何具有潜在破坏性的操作如rm -rf, 向系统目录写文件都会触发“人在回路”拦截。智能体会暂停生成一个唯一的确认ID并在Web UI中等待用户的明确批准。这防止了智能体被恶意提示词诱导或自身“幻觉”导致误操作。更值得一提的是其“项目洞察”功能它直接针对开发者“知识萎缩”的痛点。当你将项目代码库提供给智能体时它不仅能回答关于代码的问题还能生成交互式的Mermaid流程图来展示函数调用关系提供并排的代码预览甚至出题考你例如“foo()函数在第几行调用了bar()”并交互式地评判你的答案。这个功能强迫开发者与代码进行深度互动而非简单地索取答案有助于巩固和加深对代码库的理解。2.4 安全远程访问端口转发的艺术让本地服务能被安全地远程访问是实现“随时随地控制家中电脑”愿景的最后一块拼图。Asbestos没有自建复杂的信令服务器而是巧妙地利用了成熟的安全隧道技术。其原理是在本地运行的asbestos-agent服务器监听一个端口如localhost:8080。然后使用Cloudflare Tunnel、VS Code Dev Tunnels或ngrok等工具在本地与这些服务商的边缘节点之间建立一条加密的TCP隧道。公网用户访问隧道提供的HTTPS网址时流量会被加密转发到你的本地端口。这种方式的优势非常明显无需公网IP与端口映射解决了大多数家庭网络没有固定公网IP或运营商屏蔽入站端口的问题。HTTPS加密流量从客户端到隧道服务商再到你本地全程加密避免了中间人攻击。零配置防火墙不需要在路由器上设置任何转发规则。访问控制许多隧道服务支持设置身份验证如密码、OAuth增加了另一层安全屏障。项目文档建议使用这些隧道正是看中了它们开箱即用、安全可靠的特点让开发者能聚焦于AI智能体本身的功能而非网络基础设施的搭建。3. 多模态视觉能力的集成与实践3.1 LLaVA与mmproj让模型“看见”图像Asbestos的CLI工具展示了一项前沿能力本地多模态视觉理解。这依赖于LLaVA类型的技术架构。LLaVA的核心思想不是训练一个全新的视觉-语言模型而是“嫁接”将一个预训练的视觉编码器如CLIP的输出通过一个称为多模态投影器mmproj的可训练线性层映射到语言模型LLM的嵌入空间。在Asbestos中当你运行vision.sh脚本分析图片时背后发生了以下步骤图像编码图片被输入一个视觉编码器已集成在llama.cpp的视觉分支中转换为一系列视觉特征向量。特征投影这些视觉特征通过对应的mmproj模型文件如qwen3.5-0.8b-mmproj.gguf进行投影变换成与Qwen语言模型文本嵌入维度对齐的特征序列。上下文构建投影后的视觉特征被当作特殊的“图像令牌”与你的文本提示如“描述这张图片”的文本令牌拼接在一起形成一个多模态上下文。模型推理这个组合的上下文被送入Qwen语言模型。模型像处理文本一样处理这些视觉令牌最终生成对图像的描述或回答。这个过程完全在本地进行无需将图片上传到任何云端服务完美契合了项目的隐私保护宗旨。3.2 视觉CLI的实操与扩展asbestos-cli/vision.sh脚本是一个高效的封装。我们来看看它的典型用法和内部原理# 基本用法描述图片 ./asbestos-cli/vision.sh ~/Pictures/cat.jpg “详细描述这张图片。” # 进阶用法视觉问答 ./asbestos-cli/vision.sh diagram.png “解释这张流程图的工作原理。”这个脚本内部主要调用了llama.cpp项目编译出的llama-cli工具并传递了正确的参数例如指定主模型文件、视觉投影器文件、图片路径和提示词。对于开发者而言可以很容易地修改此脚本或借鉴其逻辑将视觉能力集成到自己的自动化流程中比如自动图片内容审核扫描下载文件夹识别可能的不当内容。文档图像信息提取拍摄书本或白板的照片让模型总结关键点。无障碍应用为视障用户实时描述周围环境需结合手机摄像头实时取流。注意运行视觉功能需要下载两个文件主语言模型GGUF文件和对应的mmproj投影器文件。务必确保这两个模型是配对版本否则会导致投影维度不匹配推理结果毫无意义或直接报错。4. 深入构建过程挑战与解决方案实录将复杂的C引擎塞进移动平台绝非易事。Asbestos的构建过程踩遍了可能的坑这些经验对于任何从事类似跨平台原生AI集成的开发者都极具价值。4.1 Android NDK与CMake的版本冲突问题现象在Android Studio中同步或构建项目时Gradle在配置CMake构建原生库时失败报错信息可能指向某些CMake命令不存在或llama.cpp的CMakeLists.txt解析错误。根本原因llama.cpp项目为了追求性能使用了较新版本的CMake语法和特性。而Android Studio默认捆绑的CMake版本可能较旧例如3.18。此外Android的NDK构建链有其特殊的编译器和标志可能与llama.cpp中某些为桌面平台优化的编译选项如GGML_CPU_KLEIDIAI冲突。解决方案锁定CMake版本在app/build.gradle.kts中显式指定一个与llama.cpp兼容的新版CMake。android { ... externalNativeBuild { cmake { version 3.22.1 path file(src/main/cpp/CMakeLists.txt) } } }修补CMakeLists可能需要修改llama.cpp子模块中的CMakeLists.txt或在其外层包裹一个适配层。例如通过add_compile_definitions()或修改target_compile_options()来禁用或修改那些在Android NDK中不支持的特定CPU优化标志。处理预编译库有时最稳妥的方式是预先为Android的每种ABI应用二进制接口如arm64-v8a, armeabi-v7a交叉编译好llama.cpp库然后以预编译静态库或动态库的形式引入。Asbestos选择了源码集成灵活性更高但构建配置更复杂。4.2 移动端的存储与内存墙问题现象在手机上下载数百MB甚至上GB的模型文件时应用突然崩溃或下载失败日志显示IOException或ENOSPC设备无剩余空间。深层挑战移动设备存储空间碎片化且有限。用户在下载前可能看到的总空间是足够的但在下载过程中其他应用可能正在写入缓存或系统进行后台操作导致空间动态不足。此外直接使用FileOutputStream写入大文件如果不在子线程进行会阻塞UI如果处理不当还可能导致内存溢出OOM因为Java/Kotlin在读写前可能会缓冲数据。解决方案预检与友好提示在开始下载前使用StatFsAndroid或FileManageriOS精确计算可用空间。规则应设为所需空间 模型文件大小 * 安全系数如1.2为系统临时文件和写入开销留出缓冲。如果空间不足立即提示用户清理而不是开始下载后中途失败。流式下载与磁盘直写使用OkHttpAndroid或URLSessioniOS的流式下载API将网络流直接管道到文件输出流避免将整个文件内容缓存在内存中。同时下载任务必须在后台线程如Kotlin的CoroutineScope(Dispatchers.IO)或Swift的Task.detached中执行。断点续传实现断点续传逻辑可以大幅改善用户体验。这需要服务器支持Range请求头并在本地保存已下载的字节数。虽然Asbestos的初始版本可能未实现但这是生产级应用必须考虑的功能。4.3 iOS中的并发与UI更新陷阱问题现象在iOS应用中模型推理时能收到文本回调但更新UI时Xcode控制台抛出“Publishing changes from background threads is not allowed”警告或者UI更新卡顿、丢失部分令牌单词。技术根源llama.cpp的回调函数通常在C后台线程中被触发。在Swift中所有UI更新必须在主线程Main Thread上执行。如果直接从后台线程调用Published属性更新或直接修改SwiftUI的State就会违反这一规则导致未定义行为。解决方案彻底拥抱Swift的现代并发模型Swift Concurrency。使用MainActor隔离将管理模型状态和UI状态的类或方法标记为MainActor。这确保了该类中的所有代码都将在主线程上运行。MainActor class LlamaState: ObservableObject { Published var generatedText ... }安全地从后台回调中派发在接收到C后台线程的令牌回调时使用Task { MainActor in }将UI更新操作切换到主线程。// 在C回调的封装函数中 func onTokenGenerated(_ token: String) { Task { MainActor in self.llamaState.generatedText.append(token) } }结构化并发管理将整个推理过程封装在一个Task中并使用await来安全地等待异步的模型加载和推理步骤确保整个流程的线程安全。4.4 打造健壮的iOS XCFramework问题现象尝试构建一个包含真机和模拟器架构的XCFramework时失败错误信息提示找不到llama.h等头文件或者链接器报错符号找不到。构建流程剖析构建XCFramework通常需要两步1) 分别编译真机arm64和模拟器x86_64的静态库。2) 使用xcodebuild -create-xcframework命令将两个库打包并包含正确的头文件和模块映射modulemap。解决方案Asbestos提供的build-ios-xcframework.sh脚本自动化了这个复杂过程。关键步骤包括独立构建为真机和模拟器分别设置不同的SDKiphoneosvsiphonesimulator和ARCHarm64vsx86_64环境变量调用cmake和make进行编译。头文件处理llama.cpp的头文件可能分散在多个子目录中。脚本需要在构建后将所有这些必要的头文件如llama.h,common.h等复制到一个统一的include目录中。生成Modulemap对于Swift Package ManagerSPM能正确识别C库需要创建一个module.modulemap文件明确声明库的模块名和导出的头文件。module AsbestosCore { header “llama.h” header “common.h” export * }打包最后使用xcodebuild命令指定两个.a静态库文件和共用的include目录生成最终的.xcframework包。这个包可以被直接拖入Xcode项目或通过SPM分发。5. 模型选择与性能调优指南5.1 为什么是Qwen 0.8BAsbestos默认推荐Qwen 3.5 0.8B模型这是一个非常务实的选择。在本地部署尤其是移动端需要在模型能力、响应速度和资源消耗之间找到黄金平衡点。参数规模0.8B8亿参数对于移动端CPU推理是一个甜点。更大的模型如7B在内存和速度上对手机挑战极大更小的模型如0.5B则可能在逻辑推理和指令跟随能力上显著下降。0.8B在提供可用智能的同时保持了较低的内存占用量化后约500MB-800MB和可接受的生成速度。Qwen系列优势Qwen通义千问系列模型在代码生成、数学推理和中文理解上表现突出且其3.5版本在指令遵循和对话能力上有了长足进步。其开放的协议和丰富的量化版本也便于集成。量化策略GGUF格式支持多种量化精度如Q4_K_M, Q5_K_S。Q4_K_M通常在精度和速度上取得很好的平衡是移动端的首选。你可以在Hugging Face上找到不同量化等级的Qwen模型根据设备性能进行选择。5.2 关键推理参数解析在运行模型时通过CLI或API传递的参数直接影响输出质量和速度。以下是一些核心参数参数典型值作用与影响调优建议-c,--ctx-size2048, 4096上下文窗口大小。决定了模型能“记住”多长的对话历史。越大对话能力越强但内存占用越高。对于聊天2048通常足够对于长文档分析需增大。-n,--n-predict-1, 512最大生成令牌数。-1表示无限不推荐。设为合理值如512可防止模型陷入无意义的长篇大论。-t,--threads4, 8用于推理的CPU线程数。通常设为设备物理核心数。过多线程可能导致线程切换开销反而降低性能。--temp0.7, 0.8温度参数控制输出的随机性。越高接近1.0越有创意但可能不连贯越低接近0越确定但可能重复。0.7-0.8是通用对话的推荐值。--top-p0.9, 0.95核采样nucleus sampling参数。与温度配合使用通常保持0.9-0.95有助于提高输出质量。--repeat-penalty1.1重复惩罚。降低模型重复相同词句的概率。如果发现模型经常重复可轻微上调至1.1-1.2。在Asbestos的移动端或Agent配置中这些参数通常被硬编码在合理的默认值中。如果你需要调整可能需要修改Android的JNI调用参数或iOS Swift包中的初始化配置。5.3 内存映射mmap与加载优化llama.cpp默认使用内存映射来加载GGUF模型文件。这是一个关键的性能优化原理操作系统不会一次性将整个模型文件如800MB读入物理内存而是将文件映射到进程的虚拟地址空间。当推理需要某一部分模型权重时操作系统才会按需将对应的文件页加载到物理内存中。优势极大减少了应用启动时的内存压力和等待时间。对于大型模型这避免了启动时长达数十秒的加载过程实现了“秒开”。注意事项mmap依赖于文件系统的性能。将模型放在高速存储如手机内部存储而非低速SD卡上至关重要。此外首次访问模型不同部分时会有轻微的按需加载延迟但后续推理会很快。6. 面向开发者的扩展与定制6.1 集成自有模型Asbestos项目默认使用Qwen但将其替换为你喜欢的任何GGUF格式模型非常简单。获取模型从Hugging Face等平台下载GGUF格式的模型文件例如llama-2-7b-chat.Q4_K_M.gguf。替换文件移动端在Android项目的app/src/main/assets/目录或你指定的下载目录下替换默认模型文件。在iOS中将模型文件作为资源包进应用或修改代码中的默认下载URL。Agent/CLI修改asbestos-agent或asbestos-cli中指向模型文件的路径配置。调整参数不同模型的最优推理参数如上下文大小-c可能不同。你可能需要根据新模型的推荐配置调整对应平台上的默认参数。6.2 为智能体添加新工具asbestos-agent的强大之处在于其可扩展的工具系统。为其添加一个新工具例如“获取当前天气”只需几步定义工具函数在Agent的Python代码中例如tools.py创建一个新的异步函数并用装饰器描述它。from pydantic import BaseModel, Field import httpx class WeatherInput(BaseModel): city: str Field(descriptionThe city name to get weather for) tool(descriptionGet the current weather for a given city.) async def get_weather(city: str) - str: Fetches weather data from a public API. # 注意这是一个示例实际需要使用可靠的天气API和API Key async with httpx.AsyncClient() as client: # 示例URL请替换为真实的API resp await client.get(fhttps://api.weatherapi.com/v1/current.json?keyYOUR_KEYq{city}) data resp.json() return fThe weather in {city} is {data[current][condition][text]}, temperature {data[current][temp_c]}°C.注册工具确保这个函数被导入并添加到Agent初始化时的工具列表中。更新系统提示Agent的系统提示System Prompt中包含了工具的描述。虽然现代Agent能自动发现工具但为了最佳效果可以在系统提示中简要说明新工具的功能。安全考虑如果工具涉及网络请求或外部数据务必考虑错误处理如网络超时、速率限制并避免在工具中硬编码敏感信息如API密钥应使用环境变量。6.3 构建与部署自动化对于团队或想要持续集成的开发者可以将Asbestos的构建流程自动化。Android可以配置GitHub Actions或Jenkins在每次提交时自动用NDK构建APK。关键步骤包括安装特定版本的CMake、配置NDK路径以及运行./gradlew :app:assembleRelease。iOS XCFrameworkbuild-ios-xcframework.sh脚本本身就可以在CI runner如macOS虚拟机上运行。你可以将其集成到CI流程中自动为每个版本生成并打包XCFramework供其他项目通过SPM依赖。Agent Docker化将asbestos-agent及其Python依赖打包成Docker镜像可以极大简化在服务器或NAS上的部署。Dockerfile需要安装Python、pip依赖并设置好启动命令。结合端口映射可以一键启动一个带安全隧道的私有AI助手服务。7. 常见问题排查与实战心得在实际部署和运行Asbestos的过程中你可能会遇到以下典型问题。这里记录了我的排查思路和解决方法。7.1 模型下载失败或加载缓慢现象App中下载进度条卡住或报错CLI启动时卡在“加载模型”阶段。排查步骤网络检查确认设备网络通畅。对于移动端尝试切换Wi-Fi和蜂窝网络。对于CLI检查是否有代理设置干扰。存储权限移动端确保应用已获得存储文件写入权限。在Android 11或iOS上需要动态请求权限。磁盘空间使用系统工具或命令df -h确认目标磁盘分区有足够空间至少是模型文件的1.5倍。源地址问题默认的Hugging Face链接可能因网络问题无法访问。可以尝试将模型文件预先下载到本地然后修改代码指向本地文件路径。心得在移动端永远不要在主线程进行网络I/O。必须使用后台任务并做好完善的错误处理和重试机制给用户清晰的反馈如“下载失败点击重试”。7.2 推理速度异常缓慢现象生成每个词都需要好几秒手机发热严重。排查与优化确认模型量化等级首先检查你使用的GGUF文件是否是适合移动端的量化版本如Q4_K_M。使用Q8或FP16版本在手机上会极其缓慢。检查线程数确保推理线程数-t参数设置正确。通常设置为设备CPU的物理核心数。在Android上可以通过Runtime.getRuntime().availableProcessors()获取在iOS上使用ProcessInfo.processInfo.activeProcessorCount。设置过多线程反而会增加开销。关闭无关应用在移动设备上后台运行的其他应用会争抢CPU和内存资源。在进行性能测试时尽量关闭所有后台应用。散热状态手机过热会触发温控降频导致CPU性能大幅下降。确保在凉爽环境下测试或考虑为应用添加一个“省电/低速”模式在温度高时主动降低线程数。心得移动端AI推理的性能表现极不稳定受设备型号、系统负载、电池状态、温度影响巨大。在开发时必须进行真机性能摸底测试并设定合理的性能预期。UI上提供“取消生成”的按钮是必须的。7.3 智能体Agent不执行任务或执行错误现象向Web UI发送指令如“列出文件”Agent回复“我已执行”但实际上什么都没发生或者执行了错误操作。排查步骤检查工具调用日志启动Agent时确保日志级别设置为DEBUG或INFO。查看后台日志确认Agent是否正确解析了指令并调用了对应的工具函数。验证“人在回路”拦截对于写文件、删除等操作检查Web UI是否弹出了确认对话框。如果没有可能是安全拦截机制未生效需要检查工具函数的tool装饰器配置或Agent的安全策略逻辑。审查工具函数逻辑工具函数内部的代码可能有bug。例如文件路径拼接错误、Shell命令语法错误、或依赖的外部命令不存在。在工具函数内部添加详细的日志打印是调试的关键。系统提示System PromptAgent的行为很大程度上受系统提示引导。如果Agent经常拒绝执行合理任务或执行方式怪异可能需要微调系统提示更清晰地定义它的角色、能力和安全边界。心得开发Agent时“白盒化”工具执行过程至关重要。每个工具函数都应该返回结构化的执行结果成功/失败、输出信息、错误码并将这些信息反馈给用户和日志系统。模糊的反馈会让调试变得异常困难。7.4 远程隧道连接不稳定现象通过Cloudflare Tunnel或ngrok的网址可以访问Web UI但时常断开或Agent响应超时。排查与解决隧道服务商限制免费的隧道服务通常有带宽、连接数或每月流量限制。检查服务商的控制面板看是否达到限制。本地网络问题家庭网络的不稳定如Wi-Fi信号弱、路由器性能差会导致隧道底层TCP连接中断。尝试将运行Agent的设备用网线直连路由器。防火墙/杀毒软件干扰某些本地防火墙或安全软件可能会干扰隧道客户端如cloudflared的出站连接。尝试将其加入白名单。隧道客户端版本确保使用的隧道客户端是最新稳定版。旧版本可能存在已知的bug。备用方案如果某个隧道服务不稳定可以尝试切换到另一个。Asbestos的设计支持多种隧道切换通常只需修改一行启动命令。心得对于需要7x24小时稳定服务的场景免费的隧道服务可能不够可靠。可以考虑使用付费隧道计划或者如果你有公网IP使用DDNS 反向代理如Caddy, Nginx的方式是更自主、更稳定的选择但这需要一定的网络配置知识。经过这一系列从架构设计、技术攻坚到实战调优的深度探索一个轮廓清晰、功能强大的本地AI助手生态便跃然眼前。它不再是一个遥不可及的概念而是一套可拆解、可复现、可扩展的工程实践。无论是将其作为学习移动AI开发的蓝本还是作为打造个人生产力利器的起点Asbestos项目都提供了一个坚实而富有启发性的基石。