斯坦福大学 38% 的学生是残疾人 🤡🤡🤡

一个社会的良性运转,必须要守住底线。否则,精致利己主义的「聪明人」越来越多,总会有那么一天 —— 老实人不够用了。

老实人不够用了


老实人不够用了

上个月刷到卢诗翰的一条微博,里面提到的一组神奇的数据,看完直接让人颠覆认知:2025 年,美国斯坦福大学有 38% 的学生是残疾人

微博 @卢诗翰
https://weibo.com/3276099007/QxsqnrYpV

数据出自美国《大西洋月刊》 2025 年 12 月 2 日深度报道《Accommodation Nation》
https://www.theatlantic.com/magazine/2026/01/elite-university-student-accommodation/684946/

640

没有看错,不是 3.8%,是整整 38%,将近四成的顶尖名校学生,都顶着「残疾人」的身份。

要知道,斯坦福是全球顶流高校,能考进去的无不是万里挑一的聪明人,这群智商超群的年轻人,怎么会有近一半人残疾?

答案一点都不温情,反而充满了人性的现实:这不是真的残疾率高,而是 「聪明人」把福利规则,玩成了专属自己的捷径

斯坦福对残疾学生有明确的福利倾斜,宿舍分配就是最典型的例子。早年学校宿舍分配有一套默认规则,女生优先选新宿舍、单人间,男生住剩下的老宿舍、多人间,这套规则运行多年相安无事。后来 LGBT 平权运动兴起,按照性别区分被严格禁止,于是改成按年级分配,高年级优先,新生只能先将就。

但这条规则之外,留了一条看似充满善意的特殊通道:残疾人优先

残疾学生行动不便,需要低楼层、带电梯、空间宽敞的新宿舍,需要单人间避免舍友干扰,这些需求合情合理,任谁都挑不出毛病,毕竟关爱弱势群体,是最基本的道德底线。

可问题就出在「残疾人」的定义上。按照美国《残疾人法案》,残疾不单单指肢体缺陷,心理缺陷同样被纳入范畴。多动症、注意力缺陷、抑郁症、焦虑症等等,只要通过心理医生的简单测试,拿到诊断证明,就能被认定为心理残疾,顺利搭上这条特殊通道。

斯坦福的学生有多「聪明」?他们一眼就看穿了这个规则漏洞。

既然反对不了这项充满「大爱」的政策,既然法律认可心理残疾,既然诊断证明不难拿到,那最简单的办法就是:打不过就加入

我也去看心理医生,我也确诊心理残疾,我也享受优先选宿舍、住单人间新宿舍的福利。于是,短短几年间,斯坦福的残疾学生比例一路飙升,从个位数涨到了 38%。

而同期美国全社会真正的残疾人比例,不过 3%-5%。

这组对比数据无比讽刺:政策初衷是照顾那 3%-5% 真正需要帮助的弱势群体,最后却被这么多「聪明人」钻了空子,鸠占鹊巢。

那谁在吃亏?吃亏的是剩下 60% 的老实人

他们守规矩,不屑于用这种方式钻漏洞,只能住着老旧的宿舍,挤着双人间,默默承担规则被扭曲后的代价。

照这个趋势下去,今年 38%,明年可能就是 48%、58%,直到某一天,全校 98% 的学生都是「残疾人」,这套看似公平的规则,会彻底沦为一纸空文,彻底失去原本的意义。

640 (1)

这组数据,让我瞬间想起多年前一件被全网骂翻的事。

早年房价飞涨,国家大力推行廉租房、保障房,本意是给低收入群体一个安身之所,让穷人能有地方住。当时有位专家提出一个看似「反人性」的建议:廉租房不能建太好,格局要差,采光要差,甚至不能配独立卫生间,每一层楼建一个公共厕所就行

备注:我网上查了一下,相关出处有茅于轼、薛兆丰两个版本,不太确定当初的论点渊源,因此不在此处指名道姓了,就用「某位专家」指代一下吧。

这话一出,全网炸锅。所有人都骂他冷血、脑残,质问「穷人就不配在家里上厕所吗?穷人就不能住好房子吗?」,专家被喷得体无完肤,这个建议也被当成笑柄。

可今天我回过头来看,发现这位专家,才是真正看透人性、懂现实的明白人。

如果廉租房建得宽敞明亮、配套齐全,有独立卫生间、好采光,看上去是对穷人好,可结果只会是:真正的穷人住不上,「聪明人」挤破头抢占

怎么定义「穷人」?按工资收入?很多有钱人收入根本不体现在工资单上。按名下财产?不少老赖名下无车无房,照样过得潇洒。无论设定什么样的审核标准,总有一群「聪明人」能找到漏洞,开着大奔领救济金、住着豪宅抢保障房,这类新闻我们看得还少吗?

只有把福利资源设计得「有缺陷」,让那些钻空子的有钱人不屑一顾,白给都不想要,这一点点微薄的福利,才能真正落到最需要的穷人手里。

这不是刻薄,这是最接地气的务实,是看透人性之后的无奈选择,也是避免好心办坏事的唯一办法。

其实这两个案例,说透了一个很现实的道理,@卢诗翰 称之为:老实人经济学

它的核心逻辑很残酷:所有规则的风险和成本,最终都由老实人来分担;而那些钻规则漏洞的「聪明人」,总能牺牲群体利益,换取个人私利

不管是高校的福利政策,还是社会的保障制度,设计初衷都是为了公平、为了帮扶弱者,可人性的自私,总会让这些好政策慢慢跑偏。

制定规则的人,总习惯用最大的善意去揣测人心,觉得人人都会守规矩,觉得弱势群体能被妥善照顾,却忽略了总有一群「聪明人」,擅长把善意的规则,变成利己的工具。

而那些坚守底线、不愿投机取巧的老实人,既不会钻空子,也不想违背良心,最后只能默默承受规则扭曲后的代价,成为整个体系里的「接盘侠」。

我们做金融行业的,对这个道理感受更深。市场里总有投机者钻制度漏洞、规避风险获利,而坚守价值投资、守规矩的投资者,往往要承担市场波动、规则不完善带来的额外成本,本质上,也是老实人在为「聪明人」的投机买单。

所以,不管是政策设计,还是产品方案,从来都不能只讲情怀、只谈善意,更要直面人性的弱点,不惮以最坏的恶意去揣度人心。

不要给「聪明人」留下钻空子的空间,不要把规则建立在「人人都会自觉守规矩」的幻想上,要提前预判到投机行为,用最严苛、最务实的条款,堵住每一个可能被利用的漏洞。

只有这样,那些真正需要帮助的人,才能拿到本该属于他们的福利;那些守规矩的老实人,才不用一直吃亏。

一个社会的良性运转,必须要守住底线。否则,精致利己主义的「聪明人」越来越多,总会有那么一天 —— 老实人不够用了


全文链接 老实人不够用了

原文链接:https://www.nocobase.com/cn/blog/weekly-updates-20260416

汇总一周产品更新日志,最新发布可以前往我们的博客查看

NocoBase 目前更新包括的版本更新包括三个分支:mainnextdevelop

version.png

main :截止目前最稳定的版本,推荐安装此版本。

next:包含即将发布的新功能,经过初步测试的版本,可能存在部分已知或未知问题。主要面向测试用户,用于收集反馈和进一步优化功能。适合愿意提前体验新功能并提供反馈的测试用户。

develop:开发中的版本,包含最新的功能代码,可能尚未完成或存在较多不稳定因素,主要用于内部开发和快速迭代。适合对产品功能前沿发展感兴趣的技术用户,但可能存在较多问题或不完整功能,不建议在生产环境中使用。

main

main.png

v2.0.37

发布日期:2026-04-14

🚀 优化

  • [undefined]

    • 为法语 README 添加目录 (#9037) by @saraTabbane
    • 在 README.zh-CN.md 中添加目录 (#9038) by @gaston98765
    • 新增阿拉伯语文档首页翻译 (#9043) by @gaston98765
    • 文档:新增 get-started 入门页面的阿拉伯语翻译 (#9044) by @gaston98765
  • [client] 添加表单必填校验开关 (#9060) by @jiannx

🐛 修复

  • [utils] 为服务端发送 HTTP 请求增加安全控制,以避免可能的 SSRF 隐患 (#9079) by @mytharcher
    参考文档:环境变量

v2.0.36

发布日期:2026-04-10

🐛 修复

  • [client] 子表格添加多对多报错 (#9070) by @jiannx
  • [flow-engine] 修复外部数据源多对一字段无法在表格区块中添加 (#9068) by @jiannx
  • [通知管理] 修复站内信通知在工作流场景下的发送性能问题,降低同步发送路径阻塞 (#9066) by @mytharcher

v2.0.35

发布日期:2026-04-09

🐛 修复

  • [client] 修复子表格的数据选择按钮打开的弹窗中无法正确解析上级项变量值的问题。 (#8996) by @gchust
  • [数据表字段:代码] 修正代码字段的 UI 类型 (#9061) by @2013xile

develop

develop.png

v2.1.0-alpha.16

发布日期:2026-04-14

🚀 优化

  • [undefined]

    • 在 README.zh-CN.md 中添加目录 (#9038) by @gaston98765
    • 为法语 README 添加目录 (#9037) by @saraTabbane
  • [server] 完善插件管理相关的 Swagger API 文档 (#9080) by @2013xile

🐛 修复

  • [AI 员工] 修复 AI 员工对话中异常消息不显示问题 (#9097) by @cgyrock
  • [权限控制] 更新权限管理的 swagger 文档,增强 MCP 服务 (#9096) by @Andrew1989Y

v2.1.0-alpha.15

发布日期:2026-04-10

🎉 新特性

  • [区块:树] 新增树筛选区块 (#9016) by @jiannx
  • [认证:API 密钥] 添加 pm listgenerate-api-key 命令 (#9076) by @chenos
  • [AI 员工]

    • 为 AI 员工新增数据聚合查询工具与业务分析报告工具 (#9040) by @2013xile
    • 为 AI 员工增加基于 LLM 的联网搜索工具 (#9032) by @cgyrock
  • [迁移管理] 支持 migration 命令 by @chenos
  • [邮件管理] gmail 支持邮件别名发送 by @jiannx

🐛 修复

  • [flow-engine] 修复外部数据源多对一字段无法在表格区块中添加 (#9068) by @jiannx
  • [client]

    • 子表格添加多对多报错 (#9070) by @jiannx
    • 修复子表格的数据选择按钮打开的弹窗中无法正确解析上级项变量值的问题。 (#8996) by @gchust
    • 修复批量编辑操作会弹出不正确的未保存确认消息的问题 (#9054) by @gchust
    • 修复通过弹窗子表格添加记录后提交表单时会出现错误的二次确认弹窗的问题。 (#9035) by @gchust
    • 修复 ctx.request 无法覆盖 Authorization 头信息。 (#8850) by @gchust
  • [data-source-manager] 修复从数据库同步字段后部分字段类型未正确更新的问题 (#9046) by @2013xile
  • [通知管理] 修复站内信通知在工作流场景下的发送性能问题,降低同步发送路径阻塞 (#9066) by @mytharcher
  • [数据表字段:代码] 修正代码字段的 UI 类型 (#9061) by @2013xile
  • [工作流:HTTP 请求节点] 清理工作流 HTTP 请求节点的结果结构,避免暴露请求配置;新节点默认仅返回响应数据,并为失败请求增加调试日志。 (#9024) by @mytharcher
  • [AI 员工]

    • 修复中断 AI 员工推理过程会导致服务崩溃的问题 (#9031) by @cgyrock
    • 修复配置 APP_PUBLIC_PATH 后,子路径访问下 AI 员工上传文档失败的问题 (#9034) by @cgyrock
    • 修复在 Dashscope 上使用用 Deepseek 或 MiniMax 模型报错问题 (#9033) by @cgyrock

一、背景

做跨境电商的同学都知道,价格监控是选品和定价的关键。但实际操作中会遇到各种坑:

二、踩坑实录

三、避坑方案

3.1 IP层面的解决方案

  • 使用高质量住宅IP代替数据中心IP(后者更容易被识别)
  • 配置自动轮换策略:每个IP请求N次后自动切换
  • 选择覆盖目标市场国家的IP池
    3.2 请求层面的优化
# 伪代码示例:带重试和IP轮换的采集逻辑
def fetch_with_retry(url, max_retries=3):
    for i in range(max_retries):
        proxy = get_next_proxy()  # 从IP池获取下一个
        try:
            response = requests.get(url, proxies=proxy, timeout=10)
            if is_valid_data(response):  # 校验数据真实性
                return response
        except:
            continue
        time.sleep(2 ** i)  # 指数退避
    return None

3.3 架构层面的建议

  • 采集层与解析层分离
  • 使用消息队列缓冲请求(如Redis)
  • 结果存储结构化,便于分析

    四、工具链推荐

环节推荐方案理由
代理IPFluxisp / 其他住宅IP服务高成功率、合规来源
采集框架Scrapy / Playwright成熟的生态
调度Celery + Redis分布式任务管理
存储MongoDB / PostgreSQL灵活的数据结构

五、总结

价格监控的核心是稳定和准确:

  • IP质量决定稳定性 → 选择合规住宅IP
  • 数据验证决定准确性 → 加入校验逻辑
  • 架构设计决定可扩展性 → 预留分布式能力

“为什么我们的产品明明质量更好,却卖不过竞品?”

“为什么花了大价钱改良配方,用户却不买账?”

这是很多电商人和产品经理每天都在追问的问题。

在如今这个存量竞争的时代,闭门造车无异于赌博,而读懂用户才是唯一的捷径。

今天,我们来聊一个真实发生的故事,以及一个能帮你彻底看懂消费者的工具——表答。

一碗冬粉的“麻辣”教训

让我们把时间拨回到2022年。

当时,一家食品企业看准了北美市场的“麻辣热潮”,信心满满地将自家的“麻辣拌冬粉”上架到亚马逊。

结果却令人大跌眼镜:销售惨淡,无人问津。

为什么?是麻辣口味不对吗?团队百思不得其解。

直到他们爬取了亚马逊上所有关于冬粉、速食粉类的竞品评论,用AI进行语义分析后,才发现真相。

原来,美国消费者口中的“辣”,和中国人理解的“辣”完全不在一个量级。

他们认为这款产品“太辣了,无法接受”,而且他们对“冬粉”这个词(vermicelli)根本没概念,他们更倾向于搜索“Glass Noodles” 。

这个案例揭示了一个残酷的现实:你以为的,根本不是用户以为的。

为什么传统的“我以为”会失效?

在产品的开发与迭代中,常见的决策方式有两种:

  • 凭经验判断。老板或产品经理觉得什么方向对,就做什么。
  • 参考公开榜单。看BSR排名,看竞品销量,别人卖什么就跟着做什么。

这两种方式并非全无道理。

但它们有一个共同的盲区:听不到“为什么”。

你知道竞品评分是4.8星。但你不知道那0.2星的流失,是因为“包装难拆”还是“色差太大”。

你知道你的潜在顾客买了这个商品。但你不知道他们同时还买了什么、在意什么、生活中真正需要解决什么问题。

这就是「表答电商顾客画像」功能希望填补的空白。

表答「电商顾客画像」能帮你看到什么

表答的这个功能,核心逻辑其实很简单:输入一个亚马逊商品页链接,系统自动采集该商品的评分、标题、评论内容等原始字段。

在此基础上,你还可以选择开启一个进阶功能——采集评论者的历史评论记录。

图片

开启后,你看到的不再是“某个人对某件商品的评价”,而是这个消费者在过去一段时间内的完整购物轨迹:他买过什么、给过哪些好评和差评、他的消费偏好和生活场景是什么。

这构成了一个更完整的“顾客画像”。

总的来说,「表答电商顾客画像」功能不仅仅是一个数据采集工具,它更像是你的24小时在线消费者洞察分析师。

适用场景|这些时候,你最需要它

结合亚马逊运营实际,这5个高频场景,用表答能快速破局,避开“凭感觉”的坑:

01新品开发前:验证产品方向
用表答分析竞品评论,看看顾客在好评里反复提到哪些功能,在差评里集中抱怨哪些问题,更容易判断什么值得做,什么需求真实存在。

02产品迭代中:优化现有产品
采集自己商品评论者的历史评论记录,可以进一步看到他们在其他商品上的差评原因,从中找到影响体验和转化的真实问题。

03竞品调研:看懂对手的优势与软肋
通过分析竞品评论者画像,可以了解这类顾客还买什么、关注什么、对哪些问题更敏感,帮助你更全面地理解竞品为什么卖得好。

04产品线拓展:找到跨品类机会
通过历史评论记录,可以看到目标顾客还在买哪些相关品类,也能进一步发现这些品类中尚未被满足的需求。

05广告与内容优化:精准触达用户
从评论中提取顾客的真实原话,用在 Listing、广告文案和 A+ 页面中,通常比单纯从卖点出发更容易打动用户。

保姆级实操教程|0基础也能快速上手,一步不落地搞定调研

很多卖家担心“功能好用,但操作复杂”,完全不用慌!下面这份保姆级教程,从入口找到到数据采集完成,每一步都标清楚,新手也能跟着做,全程不踩坑(重点步骤划重点,建议收藏)。

第一步:找到「电商顾客画像」功能入口(超简单)

  1. 打开表答官网,首页下滑找到「电商顾客画像」模块;
  2. 点击「电商顾客画像」,进入功能操作页面(无多余弹窗,直接进入核心操作区)。

image.png

第二步:输入链接+设置采集选项(关键一步,别漏!)

  1. 打开亚马逊网页端,找到你要调研的商品页(可以是自己的商品,也可以是竞品商品,用于竞品调研);
  2. 复制该商品的完整链接(重点:必须是商品详情页完整链接,不能是搜索页链接,否则无法采集数据);
  3. 回到表答「电商顾客画像」操作页,找到“输入商品链接”的输入框,粘贴刚才复制的亚马逊商品链接,点击“开始采集”(验证成功会直接跳转采集页面,验证失败会弹出“请输入有效的Amazon商品页链接”,请检查链接是否完整);

image.png

  1. 在输入商品链接后,可以设置采集选项:勾选“同时采集评论者历史评论记录”(按需选择即可);
  2. 若勾选:将采集该商品所有评论者的历史评论记录,适合深度挖掘顾客偏好、拓展产品线;
  3. 若不勾选:仅采集该商品本身的评论、评分、标题等基础原始字段,适合快速优化当前商品;

image.png

  1. 勾选完成后,点击“开始采集”,进入下一步。

第三步:启动采集+查看/导出数据(坐等完成,无需盯守)

  1. 确认设置后,点击页面底部“开始采集”,系统会自动启动采集程序(采集速度根据评论数量而定,无需手动操作);
  2. 采集过程中,页面会显示“采集任务”小框,可以进行查看数据、停止并保存等操作;

image.png

  1. 采集完成后,点击“查看数据”,即可看到全量原始数据,包括:评论详情链接、用户名、星级评分、标题、日期、评论内容、有用票数、评论者主页链接,以及你勾选的“评论者历史评论记录”;

image.png

  1. 若需要后续分析,可以在左边的对话框输入分析指令,比如“筛选高频差评关键词”“总结顾客核心偏好”“对比好评与差评的核心差异”等,系统会根据你的指令,快速生成分析结果,无需手动筛选整理;

image.png

image.png

image.png

  1. 若需要保存数据用于后续复盘,可点击页面“采集任务”小框的“下载”,支持Excel格式导出,直接下载到电脑,方便后续编辑、筛选。

常见问题避坑:
① 链接验证失败:检查是否是亚马逊商品详情页完整链接,排除搜索页、购物车页链接;
② 采集速度慢:属于正常情况,评论数量越多,采集时间越长,无需重复点击“开始采集”;
③ 看不到历史评论:确认是否勾选“采集评论者历史评论记录”,未勾选则仅显示当前商品评论。

写在最后

亚马逊运营,从来不是一场“产品比拼”,而是一场“需求比拼”。

你能读懂顾客,就能打造出他们喜欢的商品;你能抓住需求,就能在激烈的竞争中站稳脚跟。

表答「电商顾客画像」,不只是一个工具,更是你亚马逊运营路上的“得力助手”。

它帮你把杂乱的评论变成精准的需求,把模糊的粉丝变成清晰的画像,让每一次商品开发都不踩坑,每一次产品线拓展都不盲目。

当前功能仅支持亚马逊商品页,后续我们将逐步拓展更多平台,敬请期待。

摘要

本文针对细胞治疗与干细胞研究中体外培养对合规性、无动物源性培养基质的技术需求,基于实验数据解析laminin521 CTG(CT521)细胞治疗级人重组层粘连蛋白培养基质的基础特性、干细胞培养中的实验表现,及该基质在科研与临床研究中的实际应用场景,为相关领域的培养基质选型与应用提供实验参考和技术依据。

一、laminin521 CTG的基础特性与合规指标

Biolaminin 521 CTG(简称CT521)为全长人重组层粘连蛋白类培养基质,专为细胞、基因或组织工程产品的临床研究与非商业制造设计,其核心基础特性与合规指标均有明确行业标准依据,具体如下:

1.1 合规性指标

CT521研发参考USP第<1043>章标准,符合ISCT二级无动物源性标准;每批次产品均附带分析证书(CoA)和无动物源性声明(AOF),产品相关信息可追溯,满足临床研究阶段的基质使用规范。

1.2 成分特性

CT521为全长人重组Laminin521底物,与胚胎发育早期人多能干细胞内细胞团自主分泌的层粘连蛋白521一致,可与细胞表面特异性受体结合,激活细胞内信号通路,介导细胞的生长、增殖与分化过程。

1.3 细胞适配范围

经实验验证,CT521可支持人多能干细胞(hPSCs)、间充质干细胞(MSCs)的体外贴壁培养,同时适配多数贴壁依赖性祖细胞的体外生长,适配范围覆盖干细胞研究与细胞治疗的核心靶细胞类型。

如需了解更多技术详情,可点击此处查看BioLamina全长层粘连蛋白信息

二、laminin521 CTG在干细胞培养中的实验表现

以人多能干细胞(hPSCs)为核心研究对象,将CT521与玻连蛋白、纤连蛋白等常规培养基质对比,同时开展长期传代培养实验,其在细胞增殖、培养均一性、细胞特性维持等方面的实验结果如下:

2.1 细胞增殖与传代表现

单细胞接种条件下(不添加ROCK抑制剂),hPSCs在CT521上的增殖速率与LN521相近,高于玻连蛋白(Vitronectin)培养体系;纤连蛋白(Fibronectin)无法支持hPSCs的正常生长;

人胚胎干细胞(hESC)系HS181、HS980及诱导多能干细胞(hiPSC)系iPSC3,在CT521上经10次传代培养后,可实现稳定的群体倍增,能满足相关研究对细胞量的基本需求。

hPSCs和hESCs在CT521上快速且稳定的增殖
图1.hPSCs和hESCs在CT521上快速且稳定的增殖

2.2 细胞培养均一性与操作适配性

CT521可诱导hPSCs形成均一的单层细胞,且与多种常规培养基兼容性良好,培养过程中可减少细胞自发分化现象,hESC(HS181)与hiPSC(iPSC3)从培养初期至细胞接近长满,均能维持正常细胞形态;

该基质支持hPSCs的单细胞接种,无需添加ROCK抑制剂,可简化体外培养的实验操作流程。

hPSCs在CT521上单层培养稳定且均一
图2.hPSCs在CT521上单层培养稳定且均一

2.3 干细胞多能性与分化能力维持

1. hESC(HS181)与hiPSC(iPSC3)在CT521上经10代培养后,仍能稳定表达多能性标记蛋白,表明其可实现干细胞多能性的长期维持;

hESCs和hiPSCs中在CT521上培养多能性和分化标记物表达正常
图3.hESCs和hiPSCs中在CT521上培养多能性和分化标记物表达正常

2. 经CT521培养的干细胞形成胚状体后,可按正常规律完成分化,表现为多能性标记蛋白表达下调,外胚层、中胚层标记蛋白表达上调,分化能力与体内原代细胞一致。

在CT521上长期培养后,hESC和hiPSC保持了分化能力
图4.在CT521上长期培养后,hESC和hiPSC保持了分化能力

2.4 细胞核型稳定性

hESC(HS181)在CT521上经13次传代培养后,经检测仍保持正常核型,未出现核型异常改变,满足干细胞体外长期培养的质量安全要求。

iPSCs在CT521上培养13次传代细胞核型正常
图5.iPSCs在CT521上培养13次传代细胞核型正常

三、CT521的实际应用场景

基于CT521的合规性指标与干细胞培养的实验表现,该基质目前已应用于干细胞基础科研与细胞治疗临床研究阶段,具体应用方向如下:

3.1 基础科研领域应用

干细胞生物学研究:依托CT521的无动物源性特性与稳定培养效果,可用于hPSCs自我更新机制、分化调控规律的基础研究,为干细胞生物学理论体系完善提供实验支撑;

药物研发与筛选:利用CT521培养疾病特异性干细胞模型,可在体外开展候选药物的疗效评估与毒性检测,为药物研发的体外筛选环节提供实验模型。

3.2 临床研究领域应用

在细胞治疗的临床研究阶段,CT521可作为体外培养基质,为各类干细胞治疗研究提供培养材料支撑,目前已应用于干细胞移植治疗血液系统疾病、间充质干细胞治疗自身免疫性疾病与组织损伤、神经干细胞治疗神经系统疾病等方向的临床前研究。

总结

本文基于已发表的实验数据,解析了Biolaminin 521 CTG细胞治疗级培养基质的合规性指标、基础特性,及该基质在干细胞体外培养中的多项实验表现。该基质为无动物源性的全长人重组层粘连蛋白,符合USP第<1043>章、ISCT二级等行业规范,可支持hPSCs、MSCs等多种干细胞的体外培养。

实验结果显示,该基质在干细胞增殖速率、培养均一性方面表现出良好的实验特性,且能实现干细胞多能性、正常分化能力的长期维持,经13次传代后仍可保障细胞正常核型。目前该基质已应用于干细胞基础科研与细胞治疗临床前研究,为细胞治疗与干细胞研究领域的培养基质选型提供了一项实验参考方案。上海曼博生物官方供应Biolaminin 521 CTG细胞治疗级培养基质,可前往曼博生物官网查看CT521产品详情

FAQ:CT521应用技术问题解答

Q1:laminin521 CTG是否为无动物源性产品,符合哪些行业标准?

A1:该产品为无动物源性的全长人重组产品,符合ISCT二级无动物源性标准,研发过程参考USP第<1043>章标准,每批次均附带CoA与AOF声明。

Q2:使用laminin521 CTG培养hPSCs时,是否需要添加ROCK抑制剂?

A2:过渡到CT521实现稳定培养后,无需添加ROCK抑制剂,可直接对hPSCs进行单细胞接种培养,无需额外添加该试剂。

Q3:laminin521 CTG可支持哪些细胞类型的体外培养?

A3:经实验验证,可支持人多能干细胞(hPSCs)、间充质干细胞(MSCs)体外培养,同时适配多数贴壁依赖性祖细胞的生长。

Q4:laminin521 CTG能否支持干细胞的长期传代培养,相关实验数据如何?

A4:可支持干细胞长期传代,实验中hESC、hiPSC经10次传代可实现稳定群体倍增,hESC(HS181)经13次传代后仍保持正常核型。

Q5:与玻连蛋白、纤连蛋白相比,laminin521 CTG在hPSCs培养中有何实验差异?

A5:hPSCs在CT521上的增殖速率明显高于玻连蛋白培养体系,而纤连蛋白无法支持hPSCs的正常生长;同时CT521为无动物源性基质,与临床研究的规范要求更适配。

技术资料说明

本文解析的CT521由BioLamina研发,相关实验数据均参考公开资料《Human ES and iPS cell culture On Biolaminin 521 cell therapy grade》;如需获取该产品的详细技术资料与原文件,可点击此处获取曼博生物全长层粘连蛋白技术支持!

前言

今天是一期function call的实战

先上代码

function_call

illustration-01.png

  • 先获取 Pod 当前状态
  • 让 LLM 判断这个状态是否需要进一步排查
  • 如果需要排查,就要求 LLM 调用 get_pod_logs 工具拿日志
  • 再让 LLM 基于日志输出问题原因、根因分析和修复建议

OpenAI 客户端初始化

这部分代码的作用,是初始化模型客户端,并从环境变量里读取模型名和接口地址

from openai import OpenAI
import json
import os


client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("API_BASE_URL")
)

model = os.getenv("DEFAULT_MODEL")

工具定义

声明可供模型调用的函数工具

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_pod_logs",
            "description": "获取Pod日志用于排查问题",
            "parameters": {
                "type": "object",
                "properties": {
                    "pod_name": {"type": "string"},
                    "namespace": {"type": "string"}
                },
                "required": ["pod_name"]
            }
        }
    }
]
  • 有一个叫 get_pod_logs 的工具
  • 这个工具是用来获取 Pod 日志的
  • 它需要什么参数
  • 哪个参数是必填的

function call 的本质不是让模型直接跑 python,而是让模型知道,有什么工具可以调用外部世界的接口

获取 Pod 状态和日志

接下来这两个函数,是本地实现的模拟逻辑,后期可以接入正式环境的数据,这里先用模拟数据

def get_pod_state(pod_name, namespace):
    # 可以用 kubectl 或 k8s API, 返回:Running / CrashLoopBackOff / OOMKilled 等
    return "CrashLoopBackOff"

def get_pod_logs(pod_name, namespace="default"):
    # 去日志平台上获取对应的日志即可
    return "ERROR: database connection failed\nException: timeout"

让 LLM 先判断是否有异常

下面这段代码,是第一次调用模型。

messages = [
    {
        "role": "system",
        "content": """你是Kubernetes运维专家。

判断 Pod 状态:
- 如果是 Running / 正常 → 返回 OK
- 如果是 CrashLoopBackOff / OOMKilled / Error → 返回 NEED_DEBUG

只返回 OK 或 NEED_DEBUG"""
    },
    {
        "role": "user",
        "content": f"Pod状态是: {pod_state}"
    }
]

resp = client.chat.completions.create(
    model=model,
    messages=messages
)

decision = resp.choices[0].message.content.strip()

LLM 调工具拿日志

如果真的有问题,调用预定义的函数获取相关信息

messages = [
    {
        "role": "system",
        "content": """你是一个Kubernetes故障诊断专家。

当Pod异常时:
1. 必须调用 get_pod_logs 获取日志
2. 根据日志分析问题
3. 输出:
   - 问题原因
   - 根因分析
   - 修复建议
"""
    },
    {
        "role": "user",
        "content": f"Pod {pod_name} 状态是 {pod_state},请排查问题"
    }
]

response = client.chat.completions.create(
    model=model,
    messages=messages,
    tools=tools
)

这里和第一轮最大的区别,就是多传了一个 tools=tools,这意味着模型此时知道自己可以发起函数调用,随后代码会检查模型是否真的触发了工具调用:

msg = response.choices[0].message

if msg.tool_calls:
    tool_call = msg.tool_calls[0]
    args = json.loads(tool_call.function.arguments)

拿到工具调用后,再由本地 Python 去真正执行:

logs = get_pod_logs(**args)

然后把工具执行结果,再喂回模型,让它基于日志继续完成最终分析:

final_resp = client.chat.completions.create(
    model=model,
    messages=[
        *messages,
        msg,
        {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": logs[:3000]
        }
    ]
)

执行链路

illustration-02.png

获取 Pod 状态
   ↓
LLM 判断:OK / NEED_DEBUG
   ↓
如果 NEED_DEBUG
   ↓
LLM 发起 tool call:get_pod_logs(pod_name, namespace)
   ↓
本地函数执行,拿到日志
   ↓
日志作为 tool message 回填给 LLM
   ↓
LLM 输出:问题原因 + 根因分析 + 修复建议

这里为什么不用 if-else

很多老哥看到这里,肯定会问:

既然 Running 就是正常,CrashLoopBackOff 就是异常,那直接 if-else 不香吗?

如果场景足够简单,当然可以直接写 if-else。甚至在这个场景中,第一段判断逻辑严格来说也确实可以用规则替代,像下面这样:

if pod_state in ["Running", "Succeeded"]:
    decision = "OK"
else:
    decision = "NEED_DEBUG"

这么写没毛病,而且更便宜、更稳定、更可控,那为什么还要引入 LLM?因为真实线上环境,往往没这么简单

线上判断一个 Pod 是否异常,很多时候不是看一个字段就能拍板,而是要综合很多信息一起看

1)状态看起来正常,但其实已经不正常了, Pod 状态还是 Running,但实际上业务已经挂了,比如:

  • readiness probe 一直失败
  • 接口 RT 飙升
  • 错误率升高
  • 日志持续报错
  • 下游数据库连接频繁超时
  • CPU 不高,但线程池已经卡死

这时候如果你只写:

if pod_state == "Running":
    return "OK"

2)状态异常,但未必需要立刻排查

  • pod 状态 Completed
  • 灰度发布期间有短暂重建
  • HPA 扩缩容带来的短时波动
  • 节点维护导致的瞬时漂移

这时候你如果只根据状态值机械判断,就很容易误报

3)真正有价值的判断,往往依赖多维信号

  • Pod 当前 状态
  • restart 次数
  • 最近 5 分钟日志
  • CPU / Memory 使用率
  • OOM 事件
  • 节点状态
  • 发布记录
  • 同服务其他副本是否也异常
  • Prometheus 指标波动

这个时候用 if-else 纯流程化的判断模式,对于事件的判断就不太合适了。if-else 擅长处理边界清晰、模式稳定的场景

而LLM 更适合处理需要综合上下文、模糊判断以及不固定条件的综合判断,所以这里采用LLM 的表达能力和归纳能力会更方便

illustration-03.png

总结

通过 Function Call 获取外部数据,最后让 LLM 输出总结性的建议以及处理方案

至于为什么不用 if-else,简单场景当然可以用固定流程,但当判断开始依赖多维数据、日志语义和上下文综合分析时,LLM 会更灵活。在更加复杂的场景下,则是固定流程和 LLM 组合使用

后记

在本文中,明确告诉了llm,通过get_pod_logs去拿pod日志去拿对应的数据,那如果遇到更加复杂的事故,我们需要

  • 1)拿pod日志,通过kubectl logs
  • 2)拿业务日志,通过日志平台接口
  • 3)获取负载数据,通过监控接口
  • 4)获取发布数据,通过发布平台接口
  • 5)获取上下游的top结构,通过cmdb接口
  • 6)太多了,列举不完...

如何让llm通过当前事故情况,自动选取对应的接口函数获取数据呢?这是个非常现实、棘手的问题了,但这不是本期内容了,我们下一期再见

联系我

  • 联系我,做深入的交流

至此,本文结束

在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

在网络数据采集中,遇到网站返回403状态码是常见的技术问题。该状态秒表示服务器理解请求但拒绝响应,通常与请求标识或网络来源有关。

住宅代理作爲网络访问的重要辅助,合理配置可有效降低此类问题的发生概率。

403状态码的常见成因

403错误的发生通常源于请求头信息异常或IP来源属性两类原因。如缺少User-Agent字段、Accept-Language与目标地区不匹配,或携带了某些服务器会拒绝的非标准头部,都可能导致服务器返回403响应。

此外,若使用地址段特征较爲集中的数据中心IP,部分网站会对这类IP设置较高的访问门槛,甚至直接列入限制名单。

请求头的规范化设置

排查403问题时,应首先检查请求头的完整性与合理性。建议设置规范的User-Agent字段,使用主流浏览器的最新版本标识;添加Accept、Accept-Language、Accept-Encoding等标准头部,使其与实际请求内容保持一致。

同时,保持Referer字段的逻辑连贯性,避免出现跨域跳转的异常情形。移除可能触发风控的非标准头部字段,保持请求的简洁性。

代理配置的合理选择

当请求头配置规范后仍出现403,需要考虑调整网络接入方式。其中,源自正规互联网服务提供商分配的住宅代理,就是更可靠的选择。相较于数据中心IP,住宅代理具有更好的稳定性与兼容性。

在实际配置中,建议选择与目标网站服务地区相匹配的IP地理位置;使用纯净度较高的代理资源,减少因IP信誉问题导致的请求被拒风险,提升整体访问成功率。

总结

网站返回403是数据采集过程中可系统解决的问题。通过规范请求头配置、合理选择住宅代理资源,并按照明确的排查路径逐步定位原因,大多数错误都可以得到有效处理。

很多团队接受 Kubernetes,并不难。

难的是:

第一次把应用真正跑起来。

代码在仓库里,集群已经有了,开发、运维、测试都在推进,但一到真正上线,动作立刻散开:

  • Dockerfile 谁来补
  • 多模块项目哪个模块先跑
  • 环境变量和依赖关系怎么配
  • 日志、访问和回滚去哪看

所以问题往往不是“不会 K8s”,而是:

上线这件事还没有被收束成一条围绕应用本身的路径。

为什么这件事值得单独拎出来

因为很多团队今天已经不需要继续讨论“要不要上 Kubernetes”。

真正拉开差距的,是:

  1. 能不能让普通开发团队完成第一次上线
  2. 能不能让上线之后继续迭代

如果第一次上线本身就很重,后面的升级、回滚和环境复制只会更重。

Rainbond 解决的不是“替代 Kubernetes”

更准确地说,Rainbond 想做的是:

在 Kubernetes 之上,把第一次上线这件事重新变回“围绕应用”的工作。

这体现在几个动作上:

  1. 从代码仓库进入平台
  2. 平台先识别项目结构
  3. 构建和组件链路统一
  4. 依赖关系成为应用编排的一部分
  5. 访问、日志、拓扑被放到同一视角

哪类团队更适合先走这条路

团队状态原因
没有专职 Kubernetes 工程师先解决第一次上线
传统应用迁移先跑通真实应用链路
私有化 / 内网 / 多环境交付更需要把动作收束起来
平台工程早期先从应用交付开始更现实

一个更直接的判断

如果你们团队现在最大的痛点还是:

  • 第一次上线太慢
  • 平台门槛太高
  • 交付动作太散

那 Rainbond 这类路径,通常比继续要求团队先吃下更多底层复杂度,更接近问题本身。

作者:喧喧IM杨晓敏,企业通讯解决方案专家

数字化发展的当下,政企单位的沟通效率有了明显提升。数据泄露风险和关键技术受限的问题,也让不少单位面临安全压力。处理核心机密和敏感数据的单位,公有云产品无法满足物理隔离需求,通讯协议不透明,这些都直接影响组织的安全管控和办公效率。

喧喧以全私有部署为核心,帮政企单位搭建稳定的信息防护体系。下面解析信创背景下,政企转向私有化办公体系的主要原因,也为大家提供实用的系统选型方案。

一、政企不再选用公有云工具的核心原因

过去十年,公有云通讯工具凭借上手简单的优势快速普及。信创建设持续推进的过程中,这类工具的短板逐渐暴露出来。

数据主权无法自主掌控是最突出的问题。公有云环境中,单位的沟通数据和文档资料都会存储在第三方服务器。政府部门和金融机构把核心数据交给第三方管理,一旦出现数据泄露或服务中断,造成的后果无法挽回。

物理隔离内网的适配性存在硬伤。军工财政统计等保密单位,日常都在完全断网的内网环境中工作。公有云工具需要联网完成验证,在内网环境中无法正常运行,会直接导致团队沟通中断。

国产化适配能力不达标。信创建设要求软件兼容龙芯飞腾海光等国产 CPU,同时适配麒麟统信等国产操作系统。部分传统工具的架构过于老旧,在信创环境中运行的稳定性很差。

二、喧喧 适配安全与集成的私有部署企业 IM

政企单位选型企业 IM,需要同时兼顾安全集成和易用性。喧喧是禅道软件旗下的核心协同工具,从研发之初就坚持产研结合的思路。目前已经服务陆军学院、某直辖市银行、中石油装备、宁波某商业银行等多家重点单位。

自主可控层面,喧喧支持服务器本地部署,数据的传输和存储全由单位自主管理。单位可以自主开展代码级审计,从源头杜绝安全后门,这是保障数据主权的基础条件。

运维成本层面,喧喧采用 Go 语言开发,整体架构轻量化。单台普通配置的物理服务器,就能支撑万人同时在线通讯。IT 资源紧张的分支机构,使用喧喧可以大幅减少硬件投入,也能降低后期的维护压力。

业务集成层面,喧喧不只是基础的通讯工具,更是政企业务系统的统一入口。通过内置应用中心,可深度集成 OA、ERP、禅道项目管理系统。员工在单一界面就能接收任务提醒查看流程审批,解决多系统切换带来的信息碎片化问题。

除此之外,喧喧已经完成全栈信创适配,在国产硬件和操作系统上运行稳定。军队和政府单位使用时,还支持消息审计和多端互通。软件体积小操作简单,员工的培训成本极低。

喧喧的稳定性在多个严苛场景中得到验证。智慧军工领域,喧喧为陆军学院和演习指挥系统提供即时通讯保障。断网环境下,可实现各部门信息资源接入和统一调度。金融领域,直辖市银行借助喧喧集成内部系统,保障万人级通讯安全可控。政务领域,财政信息管理中心和统计局调查总队,通过喧喧实现信息高效共享。

三、政企选择私有部署企业 IM 的六个关键维度

我们结合专业平台的选型经验, 整理出政企挑选私有化通信系统的实用参考维度, 方便快速匹配自身需求。

第一 部署架构要灵活独立

系统要支持单机集群分布式等多种部署方式 ,保密单位要确认系统可在无公网环境下独立激活与运行 ,无需外部验证即可正常使用。

第二 数据私密与审计要完善

除基础加密能力外 ,系统要具备完整的消息记录审计功能 ,管理端可管控文件外发与截屏操作 ,满足等保 2.0 的安全管理要求。

第三 系统性能与扩展成本要合理

私有化场景中服务器资源有限 ,优先选择架构简洁 ,硬件依赖低 并发能力强的产品, 避免占用过多资源影响核心业务运行。

第四 业务系统融合要深入

优质的私有化通信工具要具备插件扩展能力, 可快速对接现有管理软件 ,推送业务通知 ,从单一沟通工具升级为业务协同平台。

第五 信创兼容要成熟稳定

产品要具备国产操作系统与芯片的兼容认证 ,支持平滑迁移, 保证原有数据完整不丢失, 适配国产化替代的推进节奏。

第六 授权模式适合资产化管理

政企更适合买断式授权, 我们提供的私有化买断方案 ,区别于公有云按年付费模式 ,更利于企业做资产化管理 长期使用成本更低。

结语

信创背景下 政企办公通信的私有化已经成为必然选择 组织需要兼顾数据主权 运维成本 业务协同与信创适配能力 私有部署企业 IM 是满足这类需求的核心方案 喧喧深耕私有部署与企业 IM 领域 适配全场景信创环境 兼顾安全与易用性 能够为政企数字化安全提供可靠支撑 选择适配的私有化通信系统 就是为组织的核心数据与业务运转筑牢安全防线

前言

最近AI越来越火了。

我发现里面有很多概念有些小伙伴有点分不清楚,比如:Prompt、Agent、Function Call、Skill、MCP等。

今天这篇文章专门跟大家一起聊聊这个话题,希望对你会有所帮助。

核心概念关系图

先上干货,这张图让你从整体上理解这五个概念是如何分层递进的:

image

更多项目实战在Java突击队网:susan.net.cn

一句话概括

  • Prompt 是你跟AI说的“人话”
  • Function Call 让AI能“动手干活”
  • Agent 让AI会“思考规划”
  • Skill 是AI的“职业技能证书”
  • MCP 是AI世界的“USB接口”

下面我们一层一层拆开揉碎了讲,每层都有Java代码示例。

第一层:Prompt——和AI对话的“普通话”

1.1 什么是Prompt?

Prompt(提示词) 就是你输入给AI的文本指令。

它就像你去餐厅点菜时说的“来一份宫保鸡丁”,AI就是那个服务员,听懂你的话然后给你上菜。

在Java里,调用AI模型的第一步就是构造Prompt。

我用最简单的Spring AI示例来演示:

import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPrompt;
import org.springframework.ai.chat.prompt.UserPrompt;

@Service
public class AIService {
    
    private final ChatClient chatClient;
    
    public AIService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    public String askAI(String question) {
        // 构造Prompt:可以包含系统消息和用户消息
        Prompt prompt = new Prompt(
            new SystemPrompt("你是一个Java架构师,擅长用通俗的语言解释技术概念。"),
            new UserPrompt(question)
        );
        
        // 调用AI并返回结果
        return chatClient.call(prompt).getResult().getOutput().getContent();
    }
}

这里的SystemPromptUserPrompt就是最基础的Prompt形式。

它们决定了AI的身份和你要问的问题。

1.2 Prompt的高级玩法

光有基础Prompt还不够,在实际应用中我们经常需要提示词工程来引导AI做出更好的回答。比如:

public String generateJavaCode(String requirement) {
    String promptTemplate = """
        你是一个资深的Java开发工程师。
        请根据以下需求生成Java代码,代码要包含必要的注释,并考虑异常处理:
        
        需求:%s
        
        请输出完整的Java类代码。
        """;
    
    String prompt = String.format(promptTemplate, requirement);
    return chatClient.call(new Prompt(prompt)).getResult().getOutput().getContent();
}

Prompt的本质:它是人类意图与AI能力之间的“翻译官”。

Prompt写得好,AI才能干得好。

第二层:Function Calling——让AI从“说话”到“动手”

Prompt只能让AI“说话”,但AI想干点实事(比如查数据库、发邮件、调用第三方API)时,就无能为力了。

Function Calling(函数调用) 就是来解决这个问题的。

2.1 什么是Function Calling?

Function Calling允许开发者在调用大模型时,注册一系列函数(工具),模型在生成回复时如果判断需要调用外部工具,就会返回一个结构化的请求,由开发者执行真实的函数,再把结果返回给模型生成最终答案。

原理图如下:

image

2.2 Java实现Function Calling

我用LangChain4j来演示,因为它对Java开发者很友好。首先定义工具函数:

import dev.langchain4j.agent.tool.Tool;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class WeatherTool {
    
    @Tool("获取指定城市的实时天气")
    public String getWeather(String city) {
        // 这里应该是真实的API调用,为了演示我们模拟数据
        if ("北京".equals(city)) {
            return "北京当前天气:晴,温度25℃,湿度40%";
        } else if ("上海".equals(city)) {
            return "上海当前天气:小雨,温度22℃,湿度80%";
        } else {
            return "抱歉,暂不支持该城市天气查询";
        }
    }
    
    @Tool("获取当前时间")
    public String getCurrentTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

然后创建AI服务并绑定工具:

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;

public class WeatherAssistant {
    
    interface Assistant {
        String chat(String userMessage);
    }
    
    public static void main(String[] args) {
        ChatLanguageModel model = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
        
        Assistant assistant = AiServices.builder(Assistant.class)
            .chatLanguageModel(model)
            .tools(new WeatherTool()) // 注册工具
            .build();
        
        // 用户提问
        String response = assistant.chat("北京现在天气怎么样?");
        System.out.println(response);
        
        response = assistant.chat("现在几点了?");
        System.out.println(response);
    }
}

当用户问天气时,模型会判断需要调用getWeather函数,LangChain4j自动处理了函数调用的整个流程,最后把结果整合成自然语言返回。

2.3 Function Calling的核心价值

让AI从“静态知识”变成“动态能力”

没有Function Calling,AI只能回答训练数据里的内容;有了它,AI可以实时获取最新信息,甚至可以操作你的系统。

第三层:Agent——会思考、会规划的“智能体”

Function Calling让AI能调用工具,但它还是被动的一问一答。

如果遇到复杂任务,比如“帮我规划一次杭州三日游”,需要查天气、查景点、查酒店、算预算……这时候就需要Agent(智能体) 出场了。

3.1 什么是Agent?

Agent是一个能够感知环境、做出决策并执行动作的自主系统

它不像Function Calling那样只是“单次工具调用”,而是具备完整的“思考-行动-观察”闭环能力。

通俗说,Function Calling是“会用手”,Agent是“有大脑”

3.2 ReAct:Agent的核心决策模式

目前主流Agent都采用ReAct(Reasoning + Acting)框架

它的工作流程是:

  1. 思考(Thought):分析当前状态,决定下一步要做什么
  2. 行动(Action):调用某个工具
  3. 观察(Observation):获取工具返回的结果
  4. 循环:直到任务完成

3.3 Java实现一个简单的ReAct Agent

我们用LangChain4j的AiServices结合工具来实现Agent。

首先定义多个工具:

import dev.langchain4j.agent.tool.Tool;

public class TravelTools {
    
    @Tool("查询某城市未来一周的天气")
    public String queryWeather(String city) {
        // 模拟天气查询
        return city + "未来一周天气:前三天晴,后四天多云,气温20-28℃";
    }
    
    @Tool("查询某城市的知名景点")
    public String queryAttractions(String city) {
        // 模拟景点查询
        if ("杭州".equals(city)) {
            return "杭州知名景点:西湖、灵隐寺、西溪湿地、宋城";
        } else if ("上海".equals(city)) {
            return "上海知名景点:外滩、东方明珠、迪士尼乐园";
        }
        return "暂无该城市景点信息";
    }
    
    @Tool("计算预算")
    public String calculateBudget(String city, int days) {
        // 模拟预算计算
        int base = 500;
        int total = base * days;
        return city + days + "天游预算约为:" + total + "元(不含大交通)";
    }
}

然后创建Agent:

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;

public class TravelAgent {
    
    interface TravelPlanner {
        String planTrip(String request);
    }
    
    public static void main(String[] args) {
        ChatLanguageModel model = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
        
        TravelPlanner agent = AiServices.builder(TravelPlanner.class)
            .chatLanguageModel(model)
            .tools(new TravelTools())
            .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) // 记忆,让Agent能记住上下文
            .build();
        
        String result = agent.planTrip("帮我规划一个3天的杭州游,包括天气、景点和预算");
        System.out.println(result);
    }
}

这个Agent会自己决定先查天气、再查景点、再算预算,然后把所有信息整合成一份完整的旅行计划。整个过程是自主的,不需要我们写死流程。

3.4 Agent与Function Calling的关系

Function Calling是Agent的“手”,Agent是拥有“大脑”的完整系统

Agent通过Function Calling调用工具,但Agent多了“规划”和“记忆”能力,能处理更复杂的任务。

第四层:Skill——封装专业知识的“技能包”

当Agent需要处理不同领域的任务时,如果让一个Agent掌握所有知识和工具,会变得臃肿且容易出错。

这时候就需要Skill(技能) 的概念。

4.1 什么是Skill?

Skill是一套封装了特定领域知识、最佳实践和工具组合的“技能包”

它就像我们人类的职业资格证书——一个医生有“看病技能”,一个程序员有“写代码技能”。

Anthropic最早提出Skill概念,一个Skill通常包含:

  • 领域专用的提示词模板
  • 一组相关的工具函数
  • 特定的工作流逻辑

4.2 Java中如何组织Skill?

我们可以把Skill定义为一个独立的模块,包含自己的工具类和提示模板。

例如,一个“前端开发Skill”:

// 前端技能专属工具
public class FrontendTools {
    
    @Tool("生成React组件代码")
    public String generateReactComponent(String componentName, String props) {
        return """
            import React from 'react';
            
            const %s = (%s) => {
                return <div>Hello, {props.name}</div>;
            };
            
            export default %s;
            """.formatted(componentName, props, componentName);
    }
    
    @Tool("检查CSS命名规范")
    public String checkCssNaming(String cssCode) {
        // 模拟CSS检查逻辑
        return "CSS规范检查通过";
    }
}

// 前端技能的提示词模板
public class FrontendPrompts {
    public static final String SYSTEM_PROMPT = """
        你是一个资深前端开发工程师,精通React、Vue、CSS等前端技术。
        请严格按照前端最佳实践生成代码,确保代码可维护。
        """;
}

// 创建前端专家Agent
public class FrontendAgent {
    public static void main(String[] args) {
        ChatLanguageModel model = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
        
        Developer assistant = AiServices.builder(Developer.class)
            .chatLanguageModel(model)
            .tools(new FrontendTools())
            .systemMessageProvider(ignored -> FrontendPrompts.SYSTEM_PROMPT)
            .build();
        
        String code = assistant.generateCode("创建一个计数器组件,有加一减一按钮");
        System.out.println(code);
    }
}

4.3 Skill与Agent的关系

在大型系统中,我们通常会有多个Agent,每个Agent加载不同的Skill:

  • 前端Agent:加载ReactSkill、CSSSkill
  • 后端Agent:加载SpringSkill、DatabaseSkill
  • 运维Agent:加载K8sSkill、MonitoringSkill

每个Agent只拥有完成自己领域任务所需的最小知识集,既提高了精准度,又保障了安全。

更多项目实战在Java突击队网:susan.net.cn

4.4 Function Call和Skill有什么区别?

一句话说清本质:

  • Function Call 是一种能力:让AI能够调用外部函数(工具)
  • Skill 是一个模块:封装了特定领域的知识、最佳实践和一组相关的Function Call

用个比喻:

  • Function Call 像锤子、螺丝刀、扳手这些具体工具
  • Skill 像木工工具箱:里面有锤子、锯子、尺子,还附带一本《木工操作手册》

下面从代码看看它们的区别。

Function Call:单个工具

public class WeatherTool {
    @Tool("获取天气")
    public String getWeather(String city) {
        // 调用天气API
        return callWeatherAPI(city);
    }
}

这个工具只能干一件事:查天气。

Skill:完整的专业能力包

// 前端开发Skill - 包含多个工具 + 专业知识
public class FrontendSkill {
    
    // 工具1:生成React组件
    @Tool("生成React组件")
    public String generateComponent(String name) {
        return "React组件代码...";
    }
    
    // 工具2:检查CSS规范
    @Tool("检查CSS规范")
    public String checkCss(String css) {
        return "检查结果...";
    }
    
    // 工具3:优化性能建议
    @Tool("提供前端性能优化建议")
    public String performanceAdvice(String code) {
        return "优化建议...";
    }
    
    // 还有领域知识(不是工具,而是提示词模板)
    public static final String SYSTEM_PROMPT = 
        "你是一个资深前端专家,精通React、Vue、CSS优化...";
}

这个Skill包含了多个工具,还带有领域知识。

核心区别对比表:

维度Function CallSkill
本质单一能力能力集合 + 知识
粒度原子操作业务模块
是否包含工具本身就是工具包含多个工具
是否包含知识不包含包含领域知识和最佳实践
类比单个螺丝刀电工工具箱 + 电工手册
应用场景查天气、发邮件等单次操作前端开发、运维管理、财务分析等专业领域
代码形式单个@Tool方法多个@Tool方法 + 系统提示词

为什么需要区分这两个概念?

  1. 设计层面的解耦:Function Call是底层能力,Skill是业务封装。底层能力稳定,上层业务可以灵活组合。
  2. 复用性:好的Skill可以跨项目复用,就像代码库里的工具包。
  3. 安全性:可以给不同的Agent分配不同的Skill,实现权限隔离(前端Agent不能调用后端数据库)。

总结:

  • Function Call:AI的“手”,能干活
  • Skill:AI的“职业培训证书”,让AI知道怎么干好某个领域的事

Function Call + 领域知识 + 最佳实践 = Skill

下次再有人问这个问题,你可以直接甩给他这张表,然后说:“锤子是Function Call,工具箱是Skill,懂了吗?”

第五层:MCP——统一工具调用的“世界语”

随着Agent越来越多,每个Agent都要接入不同的工具,每个AI模型(OpenAI、Claude、文心一言)的Function Calling格式还不一样。

这就导致开发者要针对每个模型写一套工具适配代码,非常痛苦。

MCP(Model Context Protocol,模型上下文协议) 就是来解决这个问题的。

5.1 什么是MCP?

MCP是Anthropic提出的一个标准化协议,它定义了一套统一的接口,让AI模型可以像USB设备一样动态发现和调用工具。

核心思想

  • 工具以Server的形式暴露(MCP Server)
  • AI应用作为Client(MCP Client)连接Server
  • Server提供工具清单和调用接口
  • Client统一格式调用,无需关心底层工具具体实现

5.2 MCP的工作流程

image

5.3 Java中使用MCP

Spring AI 2.0已经原生支持MCP,可以非常方便地构建MCP客户端和服务器。

定义MCP Server(工具提供方)

import org.springframework.ai.mcp.server.McpServer;
import org.springframework.ai.mcp.server.McpServerFeatures;
import org.springframework.ai.mcp.server.McpServerRegistrar;
import org.springframework.ai.mcp.spec.McpSchema;

@Component
public class WeatherMcpServer {
    
    @Bean
    public McpServerRegistrar weatherServer() {
        // 定义工具
        McpSchema.Tool weatherTool = new McpSchema.Tool(
            "getWeather",
            "获取城市天气",
            new McpSchema.JsonSchema(Map.of(
                "type", "object",
                "properties", Map.of(
                    "city", Map.of("type", "string", "description", "城市名称")
                ),
                "required", List.of("city")
            ))
        );
        
        // 创建Server
        McpServerFeatures.AsyncServerSpec serverSpec = McpServerFeatures.async()
            .tool(weatherTool, (request) -> {
                String city = request.arguments().get("city").asText();
                // 执行真实逻辑
                String weather = "北京当前天气:晴,25℃";
                return CompletableFuture.completedFuture(
                    new McpSchema.CallToolResult(List.of(
                        new McpSchema.TextContent(weather)
                    ), false)
                );
            });
        
        return McpServerRegistrar.builder()
            .name("weather-server")
            .server(serverSpec)
            .build();
    }
}

MCP Client调用

import org.springframework.ai.mcp.client.McpClient;
import org.springframework.ai.mcp.client.McpClientFeatures;

@Service
public class AIServiceWithMCP {
    
    private final ChatClient chatClient;
    private final McpClient mcpClient;
    
    public AIServiceWithMCP(ChatClient chatClient) {
        this.chatClient = chatClient;
        // 连接到MCP Server
        this.mcpClient = McpClientFeatures.async()
            .connect("weather-server", "http://localhost:8080/mcp");
    }
    
    public String askWeather(String city) {
        // 通过MCP调用工具
        McpSchema.CallToolResult result = mcpClient.callTool("getWeather", 
            Map.of("city", city)).join();
        
        String weather = result.content().get(0).text();
        
        // 也可以让AI自动决定是否调用工具
        Prompt prompt = new Prompt("查询" + city + "天气");
        return chatClient.call(prompt).getResult().getOutput().getContent();
    }
}

5.4 MCP与Skill的关系

MCP和Skill是互补的:

  • MCP 解决“怎么连”的问题——标准化工具调用协议
  • Skill 解决“连什么”的问题——封装专业知识和工具集合

一个典型的架构是:Agent通过MCP调用各种Skill暴露的工具

这样,无论底层工具如何变化,Agent都不需要修改代码,只需要通过MCP动态发现即可。

一张表彻底分清五个概念

概念一句话定义核心作用Java生态代表
Prompt给AI的指令告诉AI要做什么Spring AI的Prompt
Function Call让AI能调用外部工具赋予AI行动能力LangChain4j的@Tool注解
Agent能自主决策的智能系统完成复杂任务的闭环LangChain4j的AiServices + 记忆
Skill封装专业知识的技能包固化领域知识和最佳实践模块化的工具集合+提示模板
MCP统一工具调用的标准协议让所有AI用同一套接口Spring AI的MCP支持

这五个概念构成了AI应用开发的完整分层:

  • Prompt是地基,没有它AI听不懂人话
  • Function Call是第一层楼,让AI能动手
  • Agent是第二层,让AI会思考
  • Skill是装修,让AI更专业
  • MCP是连接各层的管道,让整个系统灵活可扩展

总结

有些小伙伴可能会问:“我现在应该先学哪个?”

我的建议是:从Prompt开始,这是所有AI应用的基础

理解了Prompt,再逐步接触Function Call,然后尝试搭建简单的Agent。

至于Skill和MCP,可以先了解概念,等你的应用复杂到需要多个Agent协作时,再深入学习也不迟。

Prompt、Function Call、Agent、Skill、MCP,正是AI应用开发的“五层楼”,每一层都让我们离业务更近,离底层细节更远。

开源地址

开源地址:

Spring AI:https://github.com/spring-projects/spring-ai

LangChain4j:https://github.com/langchain4j/langchain4j

MCP规范:https://github.com/modelcontextprotocol

更多项目实战在Java突击队网:susan.net.cn

本人男 也是 28 岁。已婚,没孩子独生子。

出身农村,15 年在上中专。生活费都是自己赚 中专慈善学校免学费,毕业之后修了几年取款机空调之类的,之后培训班学习了 3 个月转行做运维。
20 年开始做出海的方向,之后就一直做计算机+语言方面的工作。

母亲有腿疾,在家做饭不上班。
父亲当保安一月 3500-5000 ,同时跟老板做点电工之类的活。

结婚前给家里拿了 16+8+3 万,10 万给我爸还盖房时候的借款,6 万村里面买地的钱,8 万给我爸换掉了一个他开了 11 年的车。3 万给我妈买了金手镯+金戒指,他俩结婚快 40 年了都没有金首饰,我爸没开过新车,给他买个新车也算是圆梦了。

婆媳矛盾没有,我老婆外籍说的话我妈听不懂,我负责翻译,而且我们搬出去住了,不太有矛盾。

媳妇北京读硕士还没毕业,找了个俄语方向的兼职一月能赚个 2 万左右,不埋怨我爸妈不出钱不出力,结婚之前她的贷款我帮她换完了。

婚后我我自己买了一辆车,每个月还 3500 的车贷,唯一的问题是之前失业比较久,加一起要还 1 万多的贷款每个月(我自己还)

我主要做运维+俄语+英语这方面的工作,月入 25000+
生活在 1 线城市也够花。

我的想法是
成年人了 什么事都要靠自己 别埋怨自己家老人
自己没本事别往老祖宗身上靠

AI 赛事通(CompeteHub)专注于精心挑选最新的全球 AI 竞赛信息,涵盖数据算法比赛、AI 大模型比赛、工程开发比赛、AI Agent 挑战赛、黑客松. 已经收集 1000+比赛、100+竞赛来源、超 5000 万奖金,还在继续更新中。

按地区逛赛:中国北美亚太欧洲非洲,还有 线上赛 一键筛,本地线下、远程参赛都能对齐你的计划。

按 TAG 锁定领域:机器学习/AI 、计算机视觉、Agent 、工程开发、Web 、移动、金融科技、健康、区块链、网络安全、量子、机器人……30+ 标签随心组合,不用在各平台翻来翻去。

订阅 AI 赛事通周报,第一时间获取最新比赛信息,不错过任何机会错过任何一场 AI 赛事,赢取奖金。

4 月份中国区新增比赛如下:

竞赛 奖金 地点 开始日期 结束日期 类型
基于生成式 AI 的“X”病毒中和抗体设计挑战赛 ¥150,000 线上 2026 年 4 月 1 日 2026 年 12 月 13 日 AI 大模型赛
OPC 创赛 × 千万奖池:释放你的创意核能! - Wuxi 2026 年 4 月 1 日 2026 年 6 月 30 日 AI 大模型赛
2026 WAIC FutureTech OPC 独立先锋挑战赛北京地区报名通道正式开启 - 北京 2026 年 4 月 1 日 2026 年 7 月 10 日 AI 大模型赛
扬州 OPC 创业大赛 - Yangzhou 2026 年 4 月 1 日 2026 年 6 月 30 日 AI 大模型赛
创·在上海国际创新创业大赛 ¥100,000 上海 2026 年 4 月 1 日 2026 年 7 月 30 日 工程开发赛
首届全球(深圳)“AI+时尚”创新应用大赛 - 深圳 2026 年 4 月 1 日 2026 年 7 月 1 日 AI 大模型赛
AI+OPC 创新创业大赛 - Fuzhou 2026 年 4 月 1 日 2026 年 5 月 24 日 AI 大模型赛
京西智谷 AI 创新创业大赛 ¥145,000 北京 2026 年 4 月 1 日 2026 年 4 月 30 日 AI 大模型赛
首届“北斗·鸿蒙”生态创新创业专业赛 - 上海 2026 年 4 月 1 日 2026 年 7 月 30 日 AI 大模型赛
Token 万象 ·首届华数杯 AIGC 内容创作大赛 - 杭州 2026 年 4 月 1 日 2026 年 5 月 31 日 AI 大模型赛
2026 国际智能传感新质创新大赛 - 上海 2026 年 4 月 1 日 2026 年 6 月 30 日 工程开发赛
广东“众创杯”创业大赛 ¥1,430,000 广州 2026 年 4 月 1 日 2026 年 4 月 30 日 工程开发赛
WeStart 创业投资大会 TOP100 创赛 - 上海 2026 年 4 月 1 日 2026 年 6 月 30 日 工程开发赛
滴水湖全球 OPC 人工智能挑战赛暨第二届燕缘·协创者号 AI+国际创业大赛 - 上海 2026 年 4 月 1 日 2026 年 7 月 15 日 AI 大模型赛
[报名开启] 2026 OPC 独立先锋挑战赛 - 上海 2026 年 4 月 1 日 2026 年 4 月 30 日 AI 大模型赛
iFLYTEK AstronClaw 养虾挑战赛 ¥20,000 线上 2026 年 4 月 1 日 2026 年 4 月 15 日 AI 大模型赛
最高百万资金支持! 2026 浦东创新创业大赛启动 - 上海 2026 年 4 月 1 日 2026 年 5 月 31 日 工程开发赛
ATEC2026 真实极限挑战 - 香港 2026 年 4 月 1 日 2026 年 5 月 31 日 AI Agent 挑战赛
赛事类型未提供 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 工程开发赛
第四届世界科学智能大赛古文字识别赛道:少样本跨域古文字识别 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 工程开发赛
AI4S 智能体 CNS 挑战赛——靶向分子研发与合成规划智能体 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 AI Agent 挑战赛
第四届世界科学智能大赛可控核聚变赛道:可控核聚变等离子体位形控制 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 AI Agent 挑战赛
AI4S 智能体 CNS 挑战赛——DrugClip 高通量虚拟筛选优化智能体 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 AI Agent 挑战赛
AI4S 智能体 CNS 挑战赛——蛋白质构象系综生成智能体 - 线上 2026 年 4 月 2 日 2026 年 5 月 31 日 AI Agent 挑战赛
开放爪直播:AI 加入你的团队! - 香港 2026 年 4 月 2 日 2026 年 4 月 2 日 黑客松
AI MV——《乘风 2026 》主题曲 ¥200,000 线上 2026 年 4 月 2 日 2026 年 5 月 19 日 数据算法赛
Ultra Maker Hackathon 2026 · 北京站 预热发布 - 北京 2026 年 4 月 3 日 2026 年 4 月 6 日 黑客松
打造你的首个 AI 员工( OpenClaw )实操指南( AI Burrow x first AIde ) - 北京 2026 年 4 月 3 日 2026 年 4 月 3 日 黑客松
48 小时,用创造回应世界 - Ultra Maker Hackathon 2026 · 北京站 欢迎报名! - 北京 2026 年 4 月 3 日 2026 年 4 月 6 日 黑客松
超越之路| Ultra Maker 黑客松北京站 - 北京 2026 年 4 月 3 日 2026 年 4 月 5 日 黑客松
AI 产品测评游园会—「蚂上加速器专场」 - 线上 2026 年 4 月 3 日 2026 年 5 月 16 日 AI 大模型赛
粉红 HerSolidity 黑客马拉松 - 线上 2026 年 4 月 5 日 2026 年 4 月 5 日 黑客松
AI 新睿人才“星探计划” ¥90,000 线上 2026 年 4 月 6 日 2026 年 8 月 31 日 AI 大模型赛
2026 小红书黑客松巅峰赛 - 上海 2026 年 4 月 7 日 2026 年 4 月 10 日 黑客松
大模型多标签分类任务学习赛 - 线上 2026 年 4 月 8 日 2026 年 5 月 18 日 AI 大模型赛
DeFi Mullet 黑客松 #1:基于 LI.FI 用 7 天打造最强收益产品 ¥36,000 广州 2026 年 4 月 8 日 2026 年 4 月 14 日 黑客松
AMD 锐龙 AI 智能体创新应用大赛邀你定义 AI 新未来 - 线上 2026 年 4 月 8 日 2026 年 5 月 16 日 AI Agent 挑战赛
与 AI 同行,智汇校园编程黑客松 - Jinan 2026 年 4 月 9 日 2026 年 4 月 16 日 黑客松
第二届“秒哒杯”人工智能通识应用大赛暨青创 OPC 创意大赛 - 线上 2026 年 4 月 10 日 2026 年 6 月 30 日 工程开发赛
腾讯云智能渗透挑战赛 ¥100,000 北京 2026 年 4 月 10 日 2026 年 4 月 20 日 黑客松
西安 AI 黑客松大赛 - Xi'an 2026 年 4 月 11 日 2026 年 4 月 12 日 AI Agent 挑战赛
2026 创新周 - DKU Innovation for Wellbeing 黑客松招募开启 - Kunshan 2026 年 4 月 11 日 2026 年 4 月 12 日 黑客松
十一实验室 x 可爱工坊 + 黑客马拉松 - 赢家团队获 33 万积分+12 个月免费 - 线上 2026 年 4 月 12 日 2026 年 4 月 12 日 黑客松
LYCC 2026 龙华黑客松 ¥14,000 上海 2026 年 4 月 13 日 2026 年 4 月 24 日 黑客松
格兰黑客松部落 AI 特工 - 从零开始用 OpenClaw 创建 - 线上 2026 年 4 月 13 日 2026 年 4 月 14 日 黑客松
PerformaX:AdTeach 创新黑客松 - 预赛 - 线上 2026 年 4 月 14 日 2026 年 4 月 14 日 黑客松
Ship it Sunday:Harness 主题黑客松 ¥1,000 上海 2026 年 4 月 19 日 2026 年 4 月 19 日 黑客松
人工智能遇外骨骼:2026 图灵测试黑客马拉松香港行 - 香港 2026 年 4 月 20 日 2026 年 4 月 20 日 黑客松
HTX Genesis 黑客松全球招募开启! - 香港 2026 年 4 月 21 日 2026 年 4 月 21 日 黑客松
HTX 创世黑客松开幕峰会 - 香港 2026 年 4 月 21 日 2026 年 4 月 21 日 黑客松
节奏编码是陷阱 - 线上 2026 年 4 月 21 日 2026 年 4 月 21 日 黑客松
HTX 创世黑客松 ¥36,000 香港 2026 年 4 月 22 日 2026 年 7 月 31 日 黑客松
春潮起 深圳见! AttraX 「春潮·Spring |深圳 OpenClaw 黑客松」 - 深圳 2026 年 4 月 23 日 2026 年 4 月 26 日 黑客松
2050 大会罕见病基因 AI 黑客松:用 AI 破解生命难题 - 杭州 2026 年 4 月 24 日 2026 年 4 月 26 日 黑客松
2026 空间上海:24 小时 AR/VR 黑客马拉松 - 上海 2026 年 4 月 25 日 2026 年 4 月 26 日 AI Agent 挑战赛
广州 xB@B 黑客松( 2.5 万奖金) ¥180,000 广州 2026 年 4 月 26 日 2026 年 4 月 26 日 黑客松
2026 年 VIBE 探矿创新黑客松 - 线上 2026 年 4 月 30 日 2026 年 4 月 30 日 黑客松

在金融开发中,实时汇率数据几乎是每个交易系统的“必需品”。对开发者来说,不只是拿到数据那么简单,还要考虑稳定性、性能,以及系统在高并发下的表现。分享一下自己在接入外汇接口过程中的一些思路和实践。

1. 外汇接口选择:性能和稳定性优先

选择接口的时候,我不会单纯看价格或者接口宣传的功能,而是更关注三个核心:

  • 数据更新频率:外汇市场瞬息万变,接口延迟一分钟都有可能影响判断。
  • 货币对覆盖:除了主要货币对,有时业务会用到一些冷门币种,接口能否支持就很关键。
  • 稳定性和响应速度:接口稳定性直接关系到系统可用性,尤其在高并发情况下。

我个人倾向先验证接口在真实场景下的表现:请求响应速度是否稳定、是否会偶尔超时,接口文档和社区反馈也很重要。毕竟接口再漂亮,如果不稳定,用起来就是隐患。

2. 获取实时汇率数据:直接请求与解析

大多数接口都是 HTTP 返回 JSON。Python 示例很直观:

import requests

def get_exchange_rate(base, target):
    url = f'https://api.example.com/latest?base={base}'
    resp = requests.get(url)
    data = resp.json()
    return data['rates'].get(target)

rate = get_exchange_rate('USD', 'EUR')
print(rate)

简单直接,但在实际系统中,如果每次请求都直接访问接口,很快就会遇到性能瓶颈。

3. 缓存:减少重复请求

在我的实践中,缓存机制几乎是必备的。它不仅能降低接口压力,还能让系统响应更快。

import time

cache = {}

def get_cached_rate(base, target):
    key = f'{base}-{target}'
    now = time.time()
    
    if key in cache and now - cache[key]['ts'] < 60:
        return cache[key]['rate']
    
    rate = get_exchange_rate(base, target)
    cache[key] = {'rate': rate, 'ts': now}
    return rate

rate = get_cached_rate('USD', 'EUR')
print(rate)

思路很简单:先看缓存是否存在并且有效,如果有效就直接用,否则再请求接口。这在处理高并发请求时尤其重要。

4. 异步请求:提升并发处理能力

当需要同时获取多个汇率时,异步请求几乎是必然选择。我通常用 Python 的 asyncio 和 aiohttp:

import aiohttp
import asyncio

async def fetch_rate(session, base, target):
    url = f'https://api.example.com/latest?base={base}'
    async with session.get(url) as resp:
        data = await resp.json()
        return data['rates'].get(target)

async def main():
    async with aiohttp.ClientSession() as session:
        rate = await fetch_rate(session, 'USD', 'EUR')
        print(rate)

asyncio.run(main())

这让多个请求可以并行执行,比顺序请求快很多,也更适合同时查询多个货币对的场景。

5. 实践中的思考

在接入外汇接口的过程中,我有几点体会:

  • 接口不是万能:选择接口是权衡稳定性、覆盖范围和性能,而不是追求“最新最全”。
  • 缓存与异步结合:高频请求场景下,这几乎是标配,可以显著提升系统吞吐量。
  • 请求频率需要控制:接口有限制,过于频繁请求容易被封或限流,这就需要在系统层面做一些策略。

对于开发者来说,真正的价值不是单纯把数据接进来,而是让系统在保证实时性的前提下稳健运行。我个人比较喜欢把技术选型建立在“实际体验”上,而不是文档里的宣传。

一个版本,50 项更新:我们几乎重做了整个播放页

深夜,戴上耳机,打开播放器,只想安安静静听一首歌。但播放页太挤,歌词看不清,背景和封面不搭……

我们也有同样的感受。所以这次,我们把所有的不舒服,都改了。

静听 1.6.0,一个版本,50+ 项更新。从播放页到播放条,从皮肤到搜索,从 iOS 26 适配到每个像素——几乎重新打磨了一遍。


播放页:推翻重来

封面背景,像 Apple Music 一样沉浸。 提取专辑封面施加高斯模糊,模糊力度大到几乎看不清原图,只保留色彩氛围。

歌曲信息和歌词,左右滑动切换。 点击封面也能切换,告别误触。

圆形封面可以旋转。 设置新增封面形状:正方形(默认不旋转)或圆形(缓缓旋转,像黑胶唱片)。

频谱和进度条支持魔法色。 可跟随皮肤主题色或开启魔法色,不再抢眼。

右上角更多按钮,顶部指示条。 参考 QQ 音乐简洁布局,全机型比例适配。


播放条:极简或完整

新增两种模式:极简模式去掉进度条和更多按钮,进度显示在播放按钮上,高度降低;普通模式保留完整可拖动进度条。设置页一键切换。


iOS 26 液态玻璃:第一时间适配

导航栏高斯模糊、弹窗圆角统一、TabBar 仿 Apple Music (新增搜索 Tab )、灵动岛适配、锁屏信息更新。升级后像系统原生应用一样自然。


皮肤系统:从"能用"到"好看"

新增系统皮肤(黑白底色+红色高亮),浅色模式文字对比度全面修正,皮肤商店切换即时刷新,经典颜色回归。


音乐库:更聪明更紧凑

分组/筛选/排序整合进右上角菜单,当前选项有对勾标记。按标题分组后右侧出现字母索引。Cell 瘦身至 50pt ,字体按机型比例适配。三行目录精简为两行,批量管理移入设置。列表底部歌曲数和歌词数回来了。


搜索:本地即时响应

实现本地搜索,输入歌名歌手专辑即时出结果。移除三方键盘库修复闪退,Segment 可滑动,主题搭配优化。


还有更多

播放清单点击修复、二级页面播放条对称、歌词写入元数据、WMA 正常播放下一首、短音频正常完成、进度条可拖动且不卡住、封面波纹不再溢出、资料库间距和图标优化、设置新增显示原始标题……

每一项背后都是用户反馈、反复测试、像素级调整。


静听只做一件事:让你手机里的音乐,听起来舒服,看起来也舒服。1.6.0 ,已在 App Store 上线。
https://apps.apple.com/cn/app/id6755151133

最开始我觉得 vibe coding 就像个玩具,写出来的代码特别难维护。
之后慢慢的出现了 MCP 、SKILL 、SUBAGENT 这些概念,还有像 glitch 这种专门的 AI 工具,能把 AI 设计和 coding 打通。更不用说模型能力也在慢慢提升了。

现在完全能搭一套用自然语言驱动的工作流,输入自然语言,直接输出产品就行。一个不怎么懂程序开发的人也能开发产品出来。

看到前一阵子 GitHub 上开源的语音输入的 prompt ,大家戏称为赛博开源的讨论。我忽然有个想法,现在这个节点感觉像是当年汇编转向高级语言的时候,不熟汇编的人能做出东西了,但是看不到具体的指令,看不到具体内存,老汇编人觉得代码不纯粹,实现上多了好多垃圾,但是也难免汇编慢慢走向黑盒。

现在轮到高级语言步入黑盒了。

目前是提交地址,系统进行抓取,存在 SSRF 风险与源站暴露风险。
1、目前该功能限制了 127.0.0.1,但是抓取的时候开启了自动跟随跳转(一般网络请求默认开启),导致用户提交的 rss 地址跳转到内网地址,实现内网探测。(请求 a.com(301/302)-->127.0.0.1:22)。
比如:指向 2libra.com 的 rss
image
禁止内网地址

image
利用自动跟随跳转

image
抓取目标

2、由于需要对外请求,存在源站暴露风险,用户只需要在 rss 抓取请求 IP 即可得知远程服务器地址。
image
抓取 IP

image
IP 不属于 cloudflare

1、建议验证域名归属,采用放置特定字符串的 txt 方式验证,防止提交非本人站点。
2、建议服务器与抓取功能分离,防止源站暴露,导致 cloudflare 失效。
3、建议发送网络请求时关闭自动跟随跳转,防止请求不可控地址。

墨雪飘影漏洞收集平台根据《网络产品安全漏洞管理规定》已向网络产品提供者通报其产品存在的安全漏洞。
截止发文,漏洞已修复。