Airi 与 Home Assistant 集成:从 ADR 到 Electron 主进程服务的落地实现
人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载导读本文基于 docs/ai/adr/2026-10-06-home-assistant-integration.md 这份已接受accepted的架构决策记录完整剖析 Airi 如何将 Home Assistant 变成 Stage Tamagotchi Electron 的一等公民服务让用户说出关掉客厅灯模型就能在同一轮对话内读取设备状态并执行控制。文章将带你理解四个候选方案channel 插件、extension、MCP 工具链、主进程服务为何被否决或保留并逐层展开三个类型化工具、IPC 契约、请求边界防护与配置存储的源码级实现读完可直接在 Stage Tamagotchi 中配置并使用该集成。背景与上下文Issue #1491 的诉求Airi 的核心形态是赛博生命灵魂容器模型需要的不只是聊天还有对真实世界的读写能力。Home Assistant 集成的起点是 Issue #1491用户希望说出turn off the living room light然后由 Airi 真正执行——注意这里的难点不在识别意图而在执行后把真实结果读回来。在方案确定之前plugins/airi-plugin-homeassistant只是一个脚手架提交4401b96c92025-12-29为它添加了package.json、tsconfig.json与 tsdown 配置以及一个仅含console.warn(WIP)的 src/index.ts同日出现的airi-plugin-bilibili-laplace也是同一形态。该脚手架依赖proj-airi/server-sdk暗示最初的构想是一个 channel 插件但没有任何代码真正 import 它本次工作将其移除因为集成最终落在了应用内部而不是一个独立进程。决策Home Assistant 是 Electron 主进程的一等公民服务ADR 给出的最终决策是Home Assistant 作为 Stage Tamagotchi Electron 主进程的一个一等公民服务存在通过**类型化工具typed tools**交给渲染进程renderer再挂载给模型。各关注点concern的落点如下关注点位置地址、令牌及所有出站请求apps/stage-tamagotchi/src/main/services/airi/home-assistant/IPC 契约apps/stage-tamagotchi/src/shared/eventa/home-assistant.tsHome Assistant API 客户端及其校验packages/stage-ui/src/libs/home-assistant/client.ts工具定义packages/stage-ui/src/tools/home-assistant.ts工具挂载与设置页apps/stage-tamagotchi/src/renderer/从源码看这一机制沿用两条既有先例computer-use与mcp-servers都是主进程服务 → 把工具交给模型的成熟路径。在 apps/stage-tamagotchi/src/main/index.ts 中可以看到setupComputerUse(context)与setupHomeAssistant(context)并列注册setupMcpManager则通过 build 阶段初始化。为什么不是 channel 插件这是整份 ADR 最关键的推理链。channel 插件在插件开始任何工作之前就回答了工具调用builtIn_emitSparkCommand通过一个类型化信封把命令发给模块但 action 是未类型化的文本见 packages/core-agent/src/agents/spark-command/tools.ts。只要 socket 接受事件工具就立刻返回spark:command sent (id) to ...——模型读不到 Home Assistant 调用的真实结果工具返回的成功是没有人验证过的成功。协议虽然携带 ack 形状spark:emit带命令的eventId但 packages/stage-ui/src/stores/character/orchestrator/store.ts 中的handleSparkEmit是一个空函数注释明确写着Currently no-op。结果只能通过context:update或spark:notify在**另一个轮次turn**才可能到达。channel 插件还要求用户手动启动进程且 channel 服务器不存任何配置插件必须自己持久化设置设置页只能推送一次实时更新。结论对执行并读取结果这个需求channel 插件的协议在架构上就不匹配。为什么不是 extensionproj-airi/plugin-sdk能提供带真实返回值的类型化工具表面上看满足需求但三个事实否定了它extension 无法接收配置。ExtensionSetupContext只携带extension、subscriptions、kits、modules见 packages/plugin-sdk/src/extension/shared.tsmanifest 解析器会拒绝未知字段因此 base URL 和 token没有入口路径。extension 代码直接跑在 Electron 主进程无沙箱、无进程边界而主进程没有uncaughtException处理器——setup之后一次 throw 就可能让整个应用退出。packages/plugin-sdk/src/index.ts 第一行就声明APIs may change without warningWIP 状态且仓库中没有任何示例 extension 注册过工具路径未被证明。为什么不是 MCP 工具链且保留这条路径模型已经拥有类型化工具builtIn_mcpListTools与builtIn_mcpCallTool见 packages/stage-ui/src/tools/mcp.ts主进程从mcp.json运行 stdio MCP 服务器。Home Assistant 官方自带 MCP 服务器用户今天就可以零代码接入。但这条路存在两个结构性问题因此本集成不替代它、而是并存Home Assistant 通过HTTP提供 MCP需要远程传输remote transportMCP 路径把实体解析entity resolution交给模型——客厅灯到底对应哪个entity_id得靠模型自己猜。本集成的存在意义恰恰是给模型一个真正属于 Home Assistant 的工具并让客厅灯的解析发生在插件里而不是发生在 prompt 里。模型调用什么三个类型化工具集成就绪后模型的工具列表中出现三个工具且每个结果都在同一轮内可读工具参数结果home_assistant_list_entitiesdomain可选实体及其状态带数量上限home_assistant_get_stateentity_id状态与属性home_assistant_call_servicedomain、service、entity_id可选、data可选Home Assistant 实际改变的状态三个工具定义在 packages/stage-ui/src/tools/home-assistant.ts 中通过createHomeAssistantTools(client)异步构造tool()需要把 Zod schema 异步转换为 JSON Schema。需要注意data是一个JSON 对象字符串而不是对象。这并非随意设计z.record()会生成propertyNames字段OpenAI 会拒绝它——同样的约束也出现在builtIn_mcpCallTool旁边。工具内通过parseServiceData做严格校验必须是扁平 JSON 对象否则抛出data must be a JSON object, for example {brightness: 200}。实体列表默认上限DEFAULT_ENTITY_LIMIT 60可配置范围 1200。这是有意的上下文保护大型 Home Assistant 实例可能上报数千实体全量塞进上下文只会让模型无法使用。列表返回紧凑摘要行entityId、state、friendly_name并在截断时附加提示引导模型用domain过滤。工具只在地址与令牌都已存储时才挂载。未配置时集成对模型完全不可见因为任何调用都必然失败——给模型一个注定失败的可见工具是设计上要避免的状态。挂载逻辑渲染进程的 store挂载由 apps/stage-tamagotchi/src/renderer/stores/tools/home-assistant.ts 中的useTamagotchiHomeAssistantStore完成工具 ID 带home-assistant:前缀refresh()先按前缀卸载旧工具再按需重挂通过useElectronEventaInvoke(homeAssistantRequest)构造 client 的 transport——渲染进程从不持有 base URL 或 token也不向 Home Assistant 直接开 socket只发送一个 path由主进程决定怎么执行watch(() settings.configured)监听开关 凭证的合成状态任一变化都触发刷新。store 在每个窗口创建refresh是同步动作由看到变化的窗口请求 leader 窗口挂载或卸载特别注意这些工具不带defaultActive: false或requiresExplicitSelection因为共享的activeTools过滤器会丢弃这两者需要让模型可见的工具不能设置它们。配置与凭证主进程独享秘密主进程持有地址与令牌存储在 Electronuser data 目录下的home-assistant-v1.json由createConfig(home-assistant, v1.json, ...)创建见 apps/stage-tamagotchi/src/main/services/airi/home-assistant/index.ts。配置 schema 用 valibot 校验仅含baseUrl与token两个字段。IPC 契约定义在 apps/stage-tamagotchi/src/shared/eventa/home-assistant.ts三个事件homeAssistantGetConfig渲染进程读回地址与是否已有令牌以及tokenPreview掩码永远不会读到令牌本身homeAssistantSetConfig写入配置。token 字段缺省表示保留原令牌update.token ?? current.token所以用户改地址时无需重新粘贴密钥传空字符串才清除homeAssistantRequest渲染进程提交一个 path method仅GET/POST 可选 body主进程执行。令牌掩码toTokenPreview保留最后 4 个字符、其余替换为 8 个圆点如••••••••abcd让用户能分辨存的是哪把钥匙但页面永远拿不到可用密钥。设置页 apps/stage-tamagotchi/src/renderer/pages/settings/modules/home-assistant.vue 提供开关、地址输入、令牌输入typepassword以及保存/测试两个按钮Test按钮先保存再调用testConnection()通过读取实体列表数量来证明连接可用并把实体计数显示在状态行。关于安全边界ADR 明确令牌以明文存储在配置文件中。仓库内其他所有设置文件都以同样方式存密钥且当前代码未使用 ElectronsafeStorage设置页会明确告知用户令牌保存在本机。令牌是否迁移到safeStorage被列为开放问题见后文。模块开关与凭证的分离packages/stage-ui/src/stores/modules/home-assistant.ts 刻意将开关与凭证分开存放enabled属于用户、留在渲染进程本地存储地址与令牌属于主进程所以 store 只镜像一个hasCredentials布尔标志。configured enabled hasCredentials这个合成值驱动工具的挂载。镜像标志让模块卡片无需 IPC 调用即可诚实显示状态同时给 Stage Web 一个可读值——但 Stage Web 从不写它因此集成在那里保持关闭。请求边界双重校验的路径防护渲染进程只发一个 path主进程负责拼接地址与令牌。这让请求处理器成为唯一决定请求去向的地方因此它检查路径两次实现在 apps/stage-tamagotchi/src/main/services/airi/home-assistant/request.ts文本检查assertApiPath必须以/api/开头不含..、不含//、不含空白、不含%。URL 构造后复查resolveRequestUrl拼出完整 URL 后解析后的pathname必须仍然位于 base path 之下。%规则是安全核心URL 解析器会把%2e%2e解码成父级段所以/api/%2e%2e/secrets.yaml能通过纯文本检查实际却解析到/api/之上。而客户端packages/stage-ui/src/libs/home-assistant/client.ts构造的每个路径都只含[a-z0-9_.]字符——出现%就说明调用方不是本应用客户端。resolveRequestUrl还支持base URL 带子路径的反向代理场景https://example.com/ha/api/states会正确解析为/ha/api/states复查逻辑因此把pathname与basePath /api/前缀比较而不是写死根路径。其他防护细节normalizeBaseUrl会 trim 并去掉尾部斜杠拒绝非http:/https:协议file:///etc/passwd、ws://都会被拒拒绝无法解析的 URL单请求超时requestTimeoutMs 15_00015 秒渲染进程工具调用会等待这个响应实例停止应答时必须让调用失败而不是无限挂住整个轮次readBody依次尝试 JSON 解析、纯文本、空响应三种情况toRequestError将失败响应转为模型可操作的错误消息401 明确提示Home Assistant 拒绝了访问令牌其他状态码附带statusText与响应详情。请求流程主进程 handlerindex.ts的完整流程读取配置若baseUrl为空抛错Home Assistant is not configured yet...若token为空抛错Home Assistant has no access token yet...校验 method 只能是GET/POSTresolveRequestUrl构造目标 URL内含两次路径校验fetch携带Authorization: Bearer tokenPOST 时补充Content-Type: application/json与 JSON 序列化 body并挂AbortSignal.timeout(requestTimeoutMs)网络层失败归一化为带 base URL 的错误非 2xx 响应交给toRequestError成功则readBody返回。测试如何锁住这些规则vitest 为每个安全规则提供了可验证依据拒绝file://与ws://协议、拒绝/secrets.yaml与/api非/api/前缀、拒绝..与//遍历、专门用/api/%2e%2e/secrets.yaml验证百分号逃逸被拦截注释点明客户端从不转义任何东西所以 % 意味着入侵者、拒绝可能拆分请求行的空白与\n、验证反向代理子路径保留、验证 token 掩码格式与 401 错误文案。平台范围Stage Tamagotchi 先行当前集成仅限 Stage TamagotchiElectron 桌面端Stage Web 与 Stage Pocket 暂缓浏览器页面无法直接访问http://192.168.x.x上的 Home Assistant 实例除非在 Home Assistant 侧配置cors_allowed_origins且 https 页面访问 http 地址会被**混合内容mixed content**拦截Stage Pocket 需要等待一台可用的 iOS 测试设备。不过架构上预留了扩展性客户端createHomeAssistantClient本身接收一个 transportHomeAssistantTransport类型见 client.ts浏览器直接 fetch、Electron 走主进程 IPC、移动端用原生 HTTP 插件。因此另一个平台只需新增一个 transport而无需再写一个客户端。决策留下的开放选项ADR 明确记录了几条仍未关闭的路径面向 Stage Web 的 channel 插件复用同一客户端但同样无法向模型返回结果extension待 plugin SDK 支持配置、且有可运行的示例之后MCP 路线完全不需要本仓库代码随时可用。非目标Non-goals与后续演进第一阶段的明确边界无实体白名单、无逐实体权限模型无 Stage Web 工作无 channel 插件plugins/下的脚手架已移除若未来 Stage Web 需要再新增一个包无事件订阅第一阶段是按请求读写设备自发状态变化属于后续特性。开放问题call_service是否需要像computer_use那样的requiresExplicitSelection门控共享 composer 不发送tools字段因此该门控只在 Tamagotchi 渲染进程内生效设置页是否需要实体别名映射entity alias map让客厅灯无需模型读取实体列表即可解析令牌是否迁移到 ElectronsafeStorageHome Assistant 工具集是否会扩展到 areas区域与 scenes场景小结一次主进程服务式集成的完整范式回看整份 ADR它示范了一个值得复用的决策模式当模型需要调用真实系统并读取结果时先核对结果能否在同一轮返回channel 插件因此出局、配置能否注入extension 因此出局、是否已有零代码路径MCP 因此保留。最终形态——主进程独享凭证、渲染进程只发 path、类型化工具同轮读写、路径双重校验——既是 Airi 对computer-use/mcp-servers先例的延续也为其他本地设备集成提供了现成的架构模板。如果你在 Stage Tamagotchi 中启用该模块并配置好地址与令牌就能让模型像操作自己的工具一样说出关掉客厅灯并真的看到灯灭。赞分享人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载相关推荐OpenJarvis 外部 MCP 服务器集成实战从 Home Assistant 到本地 Stdio 工具OpenJarvis 外部 MCP 服务器集成实战从 Home Assistant 到本地 Stdio 工具 本指南围绕 OpenJarvis 的 tools10个实用技巧教你用awesome-home-assistant实现完美的第三方服务集成10个实用技巧教你用awesome home assistant实现完美的第三方服务集成 Home Assistant是一款开源家庭自动化系统它将本地控制和隐知识库文档教程上一篇Shenzen Smartpet P19 宠物训练项圈 Sub-GHz 信号库解析Flipper Zero 命名规范、通道与强度对照全指南下一篇gpui-kit Command 组件实战指南构建可搜索、可虚拟化的 ⌘K 命令面板创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Compose 自定义 Indication 完全指南:从 InteractionSource 到可复用点击反馈

Compose 自定义 Indication 完全指南:从 InteractionSource 到可复用点击反馈

做了几年 Compose 的界面开发,你会发现一个迟早会碰上的问题:默认的ripple(水波纹)虽然够用,但它永远长成一个样。Material 官方把波纹设计成了统一的颜色、统一的按压缩放逻辑,一旦视觉设计师给了你一张“…

2026/10/17 21:04:11 阅读更多 →
Java 8 Stream API 入门与实战:从流水线到并行流

Java 8 Stream API 入门与实战:从流水线到并行流

“Java8之Steam API”——先别急着笑,我第一次接触这个API的时候,也在关键词里把 Stream 打成了 Steam,当时还纳闷为什么怎么搜都是游戏平台。这个标题字是错的,但很多人确实是从“Steam”这个错误拼写开始认识 Java 8 的 Stream …

2026/10/17 20:11:21 阅读更多 →
两串电池被动均衡实战:MP2642GR-Z与PIC32MX695F512L协同设计

两串电池被动均衡实战:MP2642GR-Z与PIC32MX695F512L协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 21:00:45 阅读更多 →

最新新闻

ETL设计实战:数据抽取清洗转换的七步防御与避坑指南

ETL设计实战:数据抽取清洗转换的七步防御与避坑指南

简介:本资源是一份面向数据工程师、BI开发人员及ETL初学者的系统性实践指南,聚焦ETL核心环节——数据抽取、清洗与转换的设计原理与落地要点。文档深入剖析ODS层数据抽取策略(含同构/异构数据库、文件类数据源的适配方案)、三类脏…

2026/10/17 21:04:12 阅读更多 →
三端统一酒店管理系统:开箱即用的实时房态与订单协同方案

三端统一酒店管理系统:开箱即用的实时房态与订单协同方案

简介:这是一套开箱即用的酒店管理系统完整源码,面向中小型酒店经营者、全栈开发初学者及微信小程序实践者,解决传统酒店在订单、房间、餐饮及多端协同管理上的数字化落地难题。资源共1477个文件,主体为微信小程序(193个…

2026/10/17 21:04:12 阅读更多 →
Python新手避坑指南:虚拟环境、pip配置与项目结构实战

Python新手避坑指南:虚拟环境、pip配置与项目结构实战

简介:这是一份专为零基础或少量编程经验者打造的Python入门实战指南,系统覆盖Web开发、数据分析、人工智能与自动化等主流方向所需的核心基础。文档以清晰逻辑组织六大模块:从Python特性与多领域应用场景切入,到环境安装与IDE配置…

2026/10/17 21:04:12 阅读更多 →
llmperf 大模型性能测试工具怎么用:输出参数解读与 TaoToken 统一 Key 接入实测

llmperf 大模型性能测试工具怎么用:输出参数解读与 TaoToken 统一 Key 接入实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 21:04:12 阅读更多 →
Hive类递归实现:迭代法展开层级树的完整方案

Hive类递归实现:迭代法展开层级树的完整方案

做数仓的,迟早会遇到一类需求:把一张带上下级关系的表,展开成一颗完整的树。比如组织架构,比如商品类目,比如BOM物料清单,再比如接口调用链。在Oracle或者SQL Server里,写个递归CTE就解决了&…

2026/10/17 21:04:12 阅读更多 →
C++竞赛常用算法模板合集:图论、动态规划、计算几何实战

C++竞赛常用算法模板合集:图论、动态规划、计算几何实战

简介:面向ACM/ICPC等程序设计竞赛的C实用模板库,按七大主题分类组织,涵盖头文件模板、数学、字符串、数据结构、图论、计算几何与动态规划,既适合赛前速刷,也可用于日常训练和教学参考。压缩包共83个文件,以…

2026/10/17 21:03:09 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →