人工智能

你的WiFi在看着你

你有没有过这种感觉——走进一个房间,抬头就看到一个黑色的小圆球对着你。

家里的智能摄像头、公司的安防系统、小区门口的人脸识别,它们无处不在。不管你怎么说服自己"又没做什么亏心事",那个镜头始终让你不太舒服。

我也一样。所以当我上周在 GitHub 上看到一个叫 RuView 的项目时,愣了好一阵。

这个项目做的事情很简单也很颠覆:用普通 WiFi 信号来感知你的存在、呼吸频率、心率,甚至能做姿态估计和跌倒检测。不需要摄像头,不需要穿戴任何设备,一颗 9 美元的 ESP32 芯片就够了。

WiFi 怎么"看"人

先别急着觉得这是科幻。

WiFi 信号本质上是一种射频电磁波。它在房间里传播时,会被人体吸收、反射和散射。你站在 WiFi 路由器和接收器之间,信号就会产生可测量的变化。通过分析这些变化——技术上叫 CSI(信道状态信息)——可以推断出空间里正在发生什么。

这个方向学术界已经研究了十几年。2015 年 MIT 的 RF-Capture 就展示过用 WiFi 信号追踪人体姿态;后来的 DensePose、MultiFormer 不断刷新精度。WiFi 感知在学术圈不算新鲜事。

代码越写越快,但谁来审?

最近几个月,一个趋势在我身边越来越明显:团队里用 Cursor、Copilot、Claude Code 的人越来越多,但大家聊的不再是"AI 生成的代码有多好",而是"review AI 代码有多累"。

一个同事上周跟我吐槽:以前 review 同事的代码,至少能理解他的思路,知道为什么这么写。现在 review AI 生成的代码,逻辑上没毛病,但总觉得哪里不对——命名风格不统一,错误处理过度防御,有些地方用了团队从没用过的设计模式,还有些地方莫名其妙地绕了一圈。

他说了一句让我印象很深的话:"我感觉自己不是在 review 代码,是在面试一个永远不累的实习生。"

这句话点破了一个很多人还没意识到的事情:AI 编码工具正在把工程师的核心工作从"写代码"变成"审代码",而大部分团队还没有为这个转变做好准备。

代码审查量在暴增,但审查能力没跟上

先说一个最直观的数据。Cursor 官方说他们的用户已经有超过 50% 的代码是 AI 生成的。我自己团队的感受也差不多——一个中等复杂度的需求,以前可能写两天,现在用 AI 辅助,大半天就能出初稿。

但"出初稿"和"能上线"之间,隔着一道巨大的鸿沟。

登录一次就信任一天?

一个真实场景

某个周一早上,安全团队收到告警:一个客服账号在周末两天内导出了5000条客户记录。排查下来,这个客服在9:00正常登录系统,角色权限允许查看客户信息。到10:00,他已经批量导出了大量数据;10:15,这些文件被转发到了他的个人邮箱。

安全团队的结论是:"用户具有相应权限。"

这句话才是问题所在。客服确实有查看客户记录的权限,但他的正常工作模式是一天处理20个工单,每次查看一两条记录。两天导出5000条数据,完全不在正常行为范围内。这个偏差,本应在操作发生的那一刻就被系统捕捉到,而不是等到事后审计。

登录时的授权为什么不够用

大部分系统的授权模型是这样的:用户登录时验证身份,根据角色分配权限,之后在会话期间的所有操作,都默认信任那次登录的授权结果。

这个模型在过去是够用的。系统部署在内网,用户在公司电脑上操作,数据访问模式相对固定。但在云环境下,这套逻辑开始松动。

原因很直接:登录时的授权只解决了"能不能做"的问题,没有回答"该不该做"。

谷歌A2UI:AI终于不用写代码了

博客分类: 

最近有个趋势越来越明显:前端工程师看着 Copilot、Cursor 生成的 UI 代码,开始焦虑自己是不是要被替代了。

我觉得这个焦虑方向搞错了。真正值得关注的事情,不是 AI 写的代码比你好,而是 AI 可能根本不需要写代码了。

谷歌刚发布的 A2UI v0.9 就是这个方向上的一步。A2UI 全称 Agent-to-UI,是一套与框架无关的标准,让 AI 智能体声明 UI 意图,由 Web、移动端、桌面端原生渲染。关键词是"声明意图"——AI 不需要生成 HTML、CSS 或组件代码,它只需要说"我需要一个什么界面",平台自己搞定渲染。

这件事值得认真聊聊。

AI 生成代码的真正问题,不是代码质量

现在 AI 写前端代码的方式,本质上是"生成一堆代码,人来兜底"。Copilot 也好,Cursor 也好,甚至那些号称能生成完整页面的工具,都在解决同一个问题:怎么让生成的代码能跑起来。

但跑起来只是最低标准。在生产环境里,一段前端代码要真正"能用",得跟已有系统严丝合缝地集成:用团队的设计系统、符合品牌规范、支持无障碍访问、不超标性能预算、跟现有状态管理兼容、能扛住国际化。

AI Agent 一天烧掉 1.4 万美元,你的风控系统还在睡大觉

博客分类: 

上周看到一个案例,震得我好一阵没缓过来。

一家三个人的小公司,AWS 月费常年维持在十几美元的水平。某天早上,创始人打开邮箱,发现一封来自 AWS 的账单提醒——单日费用 1.4 万美元。不是月度,是单日。

排查的结果更让人窒息:一个 EC2 实例上的静态密钥不知道什么时候泄露了,攻击者拿着这个密钥大量调用 Bedrock 的 Claude 模型,跑了一整夜的推理任务。从安全角度看,这甚至不算什么高级攻击,就是一个最基础的凭证扫描 + 资源滥用。但从财务角度看,一家月支出十几美元的公司,在一夜之间欠下了一笔可能需要半年才能消化的债务。

这个案例之所以让我印象深刻,是因为它揭示了一个在 AI Agent 时代正在加速放大的系统性风险:算力消耗的速度,已经远远超过了传统风控系统的反应速度。

传统账单风控的三道防线,全部失灵

大多数公司的云成本风控,本质上就三道防线:

第一道:预算告警。 设置一个月度预算阈值,超过 80% 发邮件提醒。问题是,AI 算力的消耗速度是指数级的。一个大模型推理任务可能在几分钟内就烧掉几百美元,而告警系统的检测周期通常是小时级甚至天级的。等告警邮件送达,预算早就被打穿了。

你以为流很省内存,其实在背N份账

最近帮一个 Node.js 中间层排查 OOM。一个聚合接口,把三四个下游接口的数据拼一下吐给前端,单次请求的数据量也就两三 MB,看着毫无压力。但 Grafana 上常驻内存随 QPS 线性爬升,GC 触发得越来越频繁,终于在晚高峰把进程撑爆了。

翻代码,逻辑大致是这样:

```js
const responses = await Promise.all(
endpoints.map(url => fetch(url).then(r => r.json()))
)
return res.json(merge(responses))
```

看起来人畜无害。`fetch` 拿到的是流,`.json()` 一次性消费它,`merge` 拼好再 `res.json()` 序列化出去。整个链路里数据至少出现了三份:原始响应体、解析后的对象、合并后的对象。再加上序列化时的字符串缓冲,单请求峰值轻松到原始数据的四五倍。这还没算 V8 把这些短命对象塞进新生代、触发 Scavenge 的开销。

Vibe Coding在生产环境活不过三天

"Vibe Coding"这个词火了有一阵子了。Andrej Karpathy说他写代码时更像是给AI指个方向,然后让AI去实现,自己更像是在"vibing"——感受代码的走向,而不是一行行敲出来。这说法一出,无数开发者深有共鸣。毕竟谁不想当那个只管提需求、不用写实现的人呢?

这体验确实爽。你告诉AI"帮我写一个用户认证模块",唰唰唰生成了一个看起来像模像样的实现。你甚至不用读完每一行代码,直接点运行,测试通过,感觉自己效率起飞。

但我观察到一个现象:Vibe Coding在Demo里无所不能,在生产环境里处处掣肘。那些靠AI快速拼出来的代码,一旦进入有历史包袱的真实业务系统,就开始不断露馅。而且问题往往不是代码本身有Bug,是这些代码跟现有系统格格不入。

这不是AI不够聪明。这是Vibe Coding这个工作方式本身的前提就不成立。

 

你以为在写代码,其实在丢上下文

 

Vibe Coding的核心体验是什么?你只需要表达意图,代码自动出来。这个体验让人上瘾,因为它跳过了编程中最痛苦的部分——把模糊的想法翻译成精确的逻辑。

但有一个被忽略的前提:你跳过的那些部分,恰恰是你建立代码上下文的过程。

错误处理的谎言:你以为在兜底,其实在埋雷

打开任何一个前端项目的代码,搜索 `catch`,你大概率会看到这样的画面:

```javascript
try {
await fetchData();
} catch (e) {
console.error(e);
message.error('操作失败,请稍后重试');
}
```

后端也好不到哪去:

```javascript
try {
await processOrder(orderId);
} catch (err) {
logger.error('订单处理失败', { orderId, error: err.message });
throw new BusinessException('系统异常,请重试');
}
```

这两段代码有一个共同的特质——它们让你觉得错误被"处理"了。日志打了,提示也弹了,异常也抛了,一切看起来都很体面。

但你仔细想想:用户看到"操作失败,请稍后重试"之后能做什么?重试?然后呢——再失败一次?运维看到一条"订单处理失败"的日志,能定位到什么?是库存扣减超时,还是支付回调丢失,还是风控拦截?

前端状态管理的终极错觉:我们都在重新发明数据库

前端的现状有点荒诞:我们嘴上说自己在写 UI,实际上大部分时间在跟数据较劲。Redux、Zustand、React Query、SWR、Dva、Pinia——每出一个新方案,大家就觉得"这次终于对了"。但如果你把所有这些方案放在一起,去掉它们的外壳看本质,会发现一个尴尬的事实:它们解决的问题,数据库几十年前就解决了,而且解决得更好。

我们不是在做状态管理,我们是在重新发明数据库。只是发明得很差。

 

一个 Redux Store 就是一个简化版数据库

 

Redux 可能是最明显的例子。你写一个 Redux store 的时候,本质上在做什么?

定义一个全局的数据结构——这叫 Schema。写 reducer 处理 action——这叫事务(Transaction),action 本身就是 WAL 日志。写 selector 查询数据——这叫查询引擎。写 middleware 拦截 action——这叫触发器(Trigger)。用 normalize 归一化数据——这不就是数据库范式吗?

接口契约的幻象:为什么类型对齐救不了前后端协作

前后端分离之后,我们发明了一个安慰自己的概念——"接口契约"。Swagger 文档一摆,TypeScript 类型一生成,团队觉得天上地下都對齐了:字段名对齐了,类型对齐了,必填选填对齐了。然后上线第一天就出了 bug——status 为 3 的情况前端压根没处理,amount 的单位后端返回的是分前端当成了元,createTime 在后端是 UTC 前端按本地时间解析了。

没错,类型是对上了。但类型对上和意思对上之间,隔的不是一行代码,是一个认知深渊。

 

接口文档给了我们一种虚假的安全感

 

我做过很多次前后端协作的接口评审,也主持过不少次。每次的流程都差不多:后端同事打开 Swagger 或 YApi,一个字段一个字段地过,前端同事点头说"收到"。大家都很认真,评审记录也写了,看起来协作很顺畅。

但评审过程有个微妙的问题——我们在确认的是"形状",不是"语义"。

什么叫形状?`{ orderId: string, status: number, amount: number }` 这就是形状。字段名有了,类型有了,看起来很完整。但这句话里藏了多少没说清楚的东西?

页面