写在开始 :
1️⃣ 本博客只作为个人记录学习使用,如果涉及版权等其他问题,辛苦及时联系小编修改或者下架;谢谢!
2️⃣ 本文只简单介绍【DeepSeek】和个人一点小实践,有兴趣的可以看看哈~ 全文阅读花费时间可能比较长,预计2W字左右,可以考虑先收藏,有空再看看~
前言摘要:
2025年,国内AI领域迎来一场又一场BOOM。DeepSeek-R1凭借突破性的自迭代强化学习机制突破技术天花板,展现了非凡的创新能力。
张涛老师的软文推荐,被圈内誉为最佳DeepSeek解读;DeepSeek 创造之美: https://mp.weixin.qq.com/s/cUGA4ujsumNQfVRDGRmxWA (这是引用别人文章)
另一款爆款应用Manus,也引起广泛关注;
这段时间,AI产品的进化、商业的发展趋势日益显著,以及每个个体也在积极迎接AI的挑战。让我们共同努力,理解AI、善用AI。
1、DeepSeek泼天的流量时间线简单记录
2、DeepSeek简单认知
1.DeepSeek V3版本–知识集合
DeepSeek R1 的诞生离不开一个强大的基础模型 ——DeepSeek V3。这个模型在 12 月份发布后,得到了 Andrej Karpathy 的点赞,足以证明其在技术上的优秀之处。它为后续的模型训练和发展提供了坚实的基础,是整个 DeepSeek 体系中不可或缺的一部分。
但V3需专项训练来激活推理能力,他拥有知识的集合,但灵活运用知识方面还有提高的空间。基于 DeepSeek V3,通过不同的训练方法和策略,逐步提升模型的能力。
2.R1 Zero:训练再训练
从DeepSeek V3到 R1 Zero,做对了哪些?
纯强化学习与简单训练模板
R1 Zero 的训练采用纯强化学习方式,且过程简单粗暴。训练时用一个固定的模板,比方讲设背景为 “使用者 和 助手 的对话”,当使用者提问,助手需先 “在所谓大脑思考推理过程”,然后这个思考推理过程记录在 think 标签,最后在 answer 标签给出答案。
两类激励模型
准确度激励:比方说模型回答 “1 + 1 = 2”,答对+ 1 分;答错,+ 0 分。这种分数激励通过模型答案准确率,促使模型努力学习正确的知识和推理方法。
格式激励:要求模型必须按照规定的格式作答。比如对于问题 “1+1 等于几?”,如果模型直接回答 “answer 2”,而没有按照先在 think 标签中写出推理过程,再在 answer 标签中给出答案的格式,将+ 0 分;促使模型按照正确格式作答。
基于规则的激励模型
GRPO 训练策略
GRPO(Generalized Regularized Policy Optimization)训练。过去强化学习主要依赖 PPO(Proximal Policy Optimization),但 PPO 存在一个问题,即在强化学习的每一步中,不仅需要调整 policy model,还要优化 value model,这导致计算量开销极大。而 GRPO 通过一种相对粗暴的方法,将同样的问题扔给 policy model,让它回答 8 次,然后根据正确答案给出一个平均值。这样做的好处是在开销上比较少,同时能有效地计算出每一轮强化学习探索后,模型离正确方向的距离,以及如何激励模型朝正确方向发展。
R1 Zero 训练的发现 : 通过以上所说基础的训练模板、一激励模型,以及 GRPO 策略;模型自己发现,一旦思考得越长,就越能答对问题。
R1 Zero 的性能表现: R1 Zero训练成本仅为传统方法的1/20
3. R1版本 的诞生:全能选手
尽管 R1 Zero 在推理能力上有了很大的突破,但它也存在一些问题:首先是可读性较差,输出的内容可能不太容易被人类理解;其次是经常出现语言混杂的问题,R1 Zero 的输出由于专注于解决推理问题,可读性相对较差。
为了解决 R1 Zero 存在的问题,让推理过程更具可读性,方便共享给社区,团队在 R1 Zero 的基础上继续训练,最终开发出了 R1。
总结R1的诞生:V3基础模型 → R1 Zero(推理专精) → R1(全能选手),这里R1与V3协同进化:R1生成的优质数据反哺V3,V3升级后又增强R1的训练效果,形成良性循环。
3、DeepSeek带来的机会(可跳过)
1.行业再发展机会
顶级智能,触手可及:DeepSeek 的 R1 和 V3 模型的出现,标志着 AI 技术进入了一个新的阶段。这些模型不仅在技术上达到了新的高度,更在实际应用中展现了巨大的潜力。
2.商业模式迭代
常规功能 + 顶级智能 = 用户感知超预期
比如,百度地图接入 DeepSeek,将地图搜索结果作为 RAG 的数据源,DeepSeek 根据当前搜索目标和 RAG 出来的 POI 信息为用户规划行程,虽然是应激式的反应,但这一步简单的动作,就能让用户获得超预期的体验。再比如 QQ 音乐,他们计划在社区评论中加入一些新功能。这2个例子都表明,当常规功能与顶级智能结合时,用户的感知是超预期的。未来 3-6 个月内,我们一定会看到一些厂商在传统场景上的革新。
3.专属于推理模型的四大场景预测
角色扮演:推理模型在决策层面的表现极具“钩子效应”。例如,Monica国内版上线,完全捆绑 DeepSeek,支持 V3 和 R1 模型。推理模型叠加记忆功能,产生化学反应,生成极具情感张力的回答。
咨询辅导:咨询辅导场景中,推理模型擅长捕捉并放大细节特性。例如,“用 DeepSeek 算命”这一应用,本质上是模型捕捉并放大细节特性,为用户提供超预期的体验。
写作场景:R1 的基座 V3 综合能力极其强悍。V3 的中文 benchmark 得分高达 70 分,经过无害化 RL 训练后分数降了 10 分。这表明 R1 的写作能力已经突破临界点。所以AI 写作绝对是一个值得重新审视的领域。
信息查询:DeepSeek 爆火,不仅因为推理模型本身,还因为其 APP 集成了搜索功能。通过搜索引入外部信息,让模型能够“看世界”。这启示我们,搜索只是开眼手段之一,还可以通过内部知识库、文档、RAG 数据源等方式为 R1 赋能。
4、Manus带来的技术变化
1.Manus做的,是给 AI 配电脑;
2.模型 vs 应用下的套壳
“模型即一切”——产品没价值,模型升级就能碾碎你。张涛观点,套壳是阶段性的最优解。
原因有二:
第一,AI普及率暂时比较低:比如solo作曲、recraft生图、订阅Gemini/Claude/GPT/DeepSeek四驾马车、500刀/月订阅Devin的人,毕竟还是少数; 大众真实用户痛点——他们连“AI是什么”都分不清,更别说为AI买单付费。
第二,套壳是场景探索的钥匙:有这样的共识,是因为人类根本不会提问!提问能力与动机是全社会稀缺品。而AI的价值取决于用户提问质量,但让用户持续提问反人性。Manus-套壳可能是阶段性的最佳选择
5、每个人都可以加入这场AI变革
选择主动学习AI已经迫在眉睫。
5-1.WHY ? 为什么要学习AI
目前将AI工具融入工作实际应用比较少。
最顶尖的模型如GPT系列和Claude系列主要在海外才有供应,DeepSeek的横空出世彻底打破了技术玻璃幕墙。中国出海团队如Monica.im、黑镜、Opus Clip都算是打造出全球顶级的AI产品,现在这个“能力层”存在了。
学习AI已不是前瞻性布局——因为这次技术革命的终局,AI相关能力被看重的占比越来越高。
5-2.HOW? 如何学习AI
破除认知迷雾:建立正确的AI学习观,工具的本质是驾驭而非对抗。大家需要明白,AI是效率放大器,而非替代者。当团队成员用AI解释自己写的代码时,本质是在建立“人机协作工作流”,这与早期程序员从纯文本编辑器转向IDE开发环境的历史轨迹如出一辙。学习AI 的关键认知升级:
拒绝“打比方思维”:将AI产品简单类比为“加了大模型的XXX“,这种类比会忽视交互逻辑、容错机制等关键创新。很多团队都在探索AI的能力边界、AI能力与用户需求之间的连接,以及最佳AI产品的交互逻辑等。但在这个过程中如果仅停留在阅读自媒体报道、看截图和视频,而不是上手实践,那么你的手感就会很差,你会很容易“打比方”,但你不会用。
跨越“智力竞赛陷阱”:试图用刁钻问题考倒AI,如同测试计算器是否会作诗一样荒谬没有意义。重点应在于明确需求边界。
2.1 青铜玩家:实践出真知
大量上手使用 AI 产品。,“用”,感觉就会完全不一样。比如长视频转短视频,搜Long video、AI Shortcut相关的关键词,可能有上百家公司在做。但为什么Opus Clip团队能做成全球第一?
AI 产品往往在实际使用过程中才能展现出关键细节,即便界面可能并不完美。在实践中感受 AI 的能力边界、与用户需求的连接方式以及最佳交互逻辑。通过大量实践,能够培养对 AI 产品的 “手感”,避免陷入 “打比方” 式的浅层次认知误区。
Hugging Face Spaces:打开技术黑箱。推荐大家使用Hugging Face,他是全球开源模型的重要聚集地,所有开源模型都会在此发布。 Hugging Face Spaces 提供了便捷的解决方案,它搭建了简单的脚手架,让用户能够以低成本在网页上体验各种 AI 功能,包括文生视频、文生声音、图像识别、音频识别等。
建议初学者将 Hugging Face 上最热门的 100 个 Spaces 都体验一遍,通过这种方式,对 AI 的认知可以超越国内 99% 的产品经理。
利用 AI 工具运行 GitHub 项目。对于非技术背景的人来说,即便知道项目开源,也可能不知道如何去体验。此时,像 Windsurf、Cursor 这样的工具就能发挥重要作用。可以直接将开源的 GitHub 项目扔给它们,让其在本地运行起来。这意味着非工程背景的人员也能够利用 GitHub 这个资源宝库,从中获取项目经验和知识。通过这种方式,解决了动手实践不知从何做起的难题。只要在工作之余,花时间研究这三件事 —— 大量使用 AI 产品、体验 Hugging Face Spaces、利用 AI 工具运行 GitHub 项目,对 AI 的认知就会远超常人。
2.2 白银玩家:深入理解 AI 知识
大量阅读论文,熟悉学术语境。从去年二三月份开始,许多人尝试分享如 Stable diffusion 以及 DeepSeek 相关内容,通过持续阅读论文能够快速融入 AI 行业,把握行业趋势和技术潜力。
在阅读论文时,首先要明确论文试图解决的核心问题;若该问题与自己当前关注点无关,可暂时搁置
①范式级别的创新:如 Transformer 以及 Stable diffusion,这类开创性工作由世界上最聪明的人运用顶尖方法解决社会和技术难题,极为罕见但影响深远。对于刚接触 AI 的新手而言,有较多此类经典论文可供阅读学习。
②milestone:在范式级创新基础上取得重要进展的研究。例如 LCM,作为 Stable diffusion 的延伸,通过特殊算法设计将 LDM 模型生成图像的时间从家用级显卡上的 10 秒级别缩短至 1 秒,实现数量级的提升,这类研究代表了关键技术进展。
③雕花型论文:此类论文通常以 benchmark 作为对比,声称性能超越以往一定比例,或在垂直领域与泛用模型比较。阅读较多后,能够理解其中的语境,如 benchmark 背后的含义、对标模型的能力特点等,从而快速判断其价值。
遇到第一类论文时,可尽情享受与顶尖智慧交流的过程;第二类留到周末仔细研读;第三类则可根据实际情况选择是否深入阅读。
2.3 黄金阶段:AI 编码实战
避免无脑输出,清晰表达需求。在使用 AI 进行编码时,切忌无脑输出。例如简单地要求 “给我写个APP首页” 这样模糊的指令,对 AI 来说没有实际意义。正确的做法是清晰、具体地表达需求,如 “我要一个餐馆首页的设计,上面包含导航栏(金刚位),下方是菜品列表”。不过,随着 AI 技术发展,其理解能力不断增强,或许未来无脑输出也能实现需求,但当前阶段仍需准确表述。同时,不能仅凭以往经验对 AI 进行 “打比方” 式的认知,而应亲自实践,感受底层模型和应用层变化对实际使用效果的影响。
明确目标,让 AI 助力实现。使用 AI Coding 时,要明确自己的目标,避免只关注代码编写过程而忽略需求和目标本身。AI 拥有庞大的知识库和多样的思维方式,往往能提供更优解决方案。以在 Mac 上使用带 AI 的终端工具 Warp 转视频为例,ffmpeg 命令行工具参数复杂,而通过在 Warp 中用自然语言描述需求,如 “帮我把某某视频转换成画质不损失但分辨率降低到多少多少”,Warp 会根据视频原始大小、目标大小等场景信息,推荐合适的编码率等设置,无需用户手动查阅文档和设置复杂参数。
学会目标拆分,化大任务为小任务。在工程项目中,将大任务拆分为小任务是重要的方法。在 AI Coding 中同样如此,不进行拆分可能导致 AI 一次性尝试完成复杂任务,出现错误时难以定位。就像平时写代码提倡 “原子级别提交”,每次代码提交影响范围小,便于测试、发现问题和回滚。例如在一个复杂的软件开发项目中,如果直接让 AI 生成整个项目代码,一旦出现问题,很难确定错误源头;但如果将项目拆分为前端界面设计、后端逻辑实现、数据库连接等小任务,逐个让 AI 协助完成,即便出现错误,也能更快速地定位和解决。
一图胜千言。在使用 AI 编程工具时,当意图难以用语言清晰表达时,直接提供一张图往往更为有效。AI 能够根据图像信息找到出色的解决方案。比如在设计一个独特的界面布局时,用语言描述可能不够直观,此时绘制一张简单的草图并提供给 AI,AI 可能基于此生成符合需求的代码,实现预期的界面效果。
AI ⽐你强的,不仅仅在于代码⽣成能⼒,⽽是其浩瀚的知识库。AI 的强大之处不仅在于代码生成能力,更在于其拥有浩瀚的知识库,这是人类难以企及的。在使用 AI 时,应将其视为召唤工具,专注于目标,让 AI 整合现有的优质方案和资源,避免让 AI 从头编写代码,降低出错概率。例如在开发一个数据分析项目时,不必让 AI 从零开始构建算法,而是引导它利用成熟的开源数据分析框架和算法库,如 Python 中的 Pandas、NumPy 等,快速实现数据处理和分析功能。
做好向上管理,高效协作。长期来看,使用 AI Coding 本质上是引导 AI 正确理解和执行任务。当发现明显错误时,不必反复通过聊天方式要求 AI 修正,可直接指导修改或自行更正,发挥人定位问题、发现问题的能力,让 AI 承担重复性的 “体力活”。遇到编写难题时,向 AI 清晰描述想要实现的效果,追问构建问题模型的方法。例如在一个团队项目中,当 AI 生成的代码出现逻辑错误时,团队成员可以直接指出错误点,并结合需求给出修改建议,让 AI 快速调整代码,提高开发效率。
2.4 赋能生活:AI 在日常场景中的应用
浏览器插件开发:学习 AI Coding 能够极大地赋能生活。以浏览器使用为例,每个人在使用浏览器过程中都会遇到各种需求或痛点,将这些痛点告知 Cursor 等 AI 工具,它能够帮助解决问题,甚至生成浏览器插件。制作浏览器插件符合个性化需求,且相对简单,不需要复杂环境或后端即可独自运行。鼓励大家尝试制作浏览器插件,利用 AI 实现个性化的浏览器功能定制。
文件处理:像 Cursor、Windsurf 等工具具备强大的文件处理能力。例如面对一个包含 2000 多张图片的文件夹,如果要按月份整理到不同文件夹中,使用 Windsurf 通过命令行能够迅速完成任务。AI 编写代码并非单纯为了产出代码,而是作为中介语言,调用以往难以操作的功能。在开发 Manus 过程中,还发现 AI 能够编写 CAD 文件,借助代码这一中介,实现了对以往难以处理的文件格式的操作,满足基本的增删改查需求。
音视频处理:利用带 AI 的终端工具如 Warp 配合 ffmpeg,能够轻松处理各种音视频需求。例如将视频转换为特定分辨率、调整视频画质、提取音频等。通过自然语言在 Warp 中描述音视频处理需求,不用记忆复杂的 ffmpeg 命令参数,就可实现高效的音视频处理,提升日常生活和工作中的音视频处理效率
AI学习已进入“行动即优势“阶段。记住三个核心数据:体验100个Hugging Face Spaces可超越99%产品经理;精读50篇论文能建立技术敏感度;持续3个月每天1小时AI编码可重塑工作流。在这个技术平权时代,最大的风险不是学不会AI,而是“知道路径却从未开始“。
临渊羡鱼不如退而结网,赶紧干起来
上图的HTML代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek & Manus</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@font-face {
font-family: 'SF Pro Display';
src: local('SF Pro Display'), local('SFProDisplay-Regular');
}
body {
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", sans-serif;
background-color: #000;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.header-container {
text-align: center;
max-width: 1200px;
padding: 2rem;
position: relative;
}
.main-title {
font-size: 5rem;
font-weight: 600;
letter-spacing: -0.02em;
background: linear-gradient(90deg, #ffffff, #a0a0a0);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 0.8rem;
transition: all 0.3s ease;
}
.subtitle {
font-size: 1.8rem;
font-weight: 400;
color: #86868b;
max-width: 700px;
margin: 0 auto;
letter-spacing: -0.01em;
opacity: 0.9;
}
.highlight {
color: #2997ff;
}
.blur-circle {
position: absolute;
border-radius: 50%;
filter: blur(70px);
z-index: -1;
opacity: 0.4;
}
.blur-circle-1 {
width: 400px;
height: 400px;
background: #2271ff;
top: -100px;
left: -200px;
}
.blur-circle-2 {
width: 300px;
height: 300px;
background: #6c42f5;
bottom: -100px;
right: -100px;
}
.icon-container {
position: relative;
margin-bottom: 3rem;
height: 80px;
}
.deepseek-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #4a90e2, #7b68ee);
border-radius: 18px;
position: relative;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 20px rgba(74, 144, 226, 0.15);
}
.icon-symbol {
font-size: 40px;
color: white;
}
.manus-shadow {
position: absolute;
width: 80px;
height: 80px;
border-radius: 18px;
background: rgba(123, 104, 238, 0.3);
backdrop-filter: blur(10px);
right: calc(50% - 100px);
top: 0;
transform: perspective(500px) rotateY(30deg) translateZ(-20px);
box-shadow: 0 10px 20px rgba(123, 104, 238, 0.15);
opacity: 0.8;
z-index: -1;
display: flex;
align-items: center;
justify-content: center;
}
.manus-shadow::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0.05) 0px,
rgba(255, 255, 255, 0.05) 2px,
transparent 2px,
transparent 6px
);
border-radius: 18px;
}
.shadow-symbol {
font-size: 40px;
color: rgba(255, 255, 255, 0.7);
}
.connection-line {
position: absolute;
width: 30px;
height: 2px;
background: linear-gradient(90deg, rgba(74, 144, 226, 0), rgba(74, 144, 226, 0.5));
top: 40px;
right: calc(50% - 60px);
z-index: -1;
}
@media (max-width: 768px) {
.main-title {
font-size: 3.5rem;
}
.subtitle {
font-size: 1.4rem;
}
.deepseek-icon {
width: 60px;
height: 60px;
border-radius: 14px;
}
.icon-symbol {
font-size: 30px;
}
.manus-shadow {
width: 60px;
height: 60px;
border-radius: 14px;
right: calc(50% - 80px);
}
.shadow-symbol {
font-size: 30px;
}
.connection-line {
width: 25px;
top: 30px;
right: calc(50% - 48px);
}
}
</style>
</head>
<body>
<div class="blur-circle blur-circle-1"></div>
<div class="blur-circle blur-circle-2"></div>
<header class="header-container">
<div class="icon-container">
<div class="deepseek-icon">
<div class="icon-symbol">DS</div>
</div>
<div class="manus-shadow">
<div class="shadow-symbol">M</div>
</div>
<div class="connection-line"></div>
</div>
<h1 class="main-title">DeepSeek & Manus</h1>
<p class="subtitle">用<span class="highlight">AI</span>驱动的<span class="highlight">深度探索</span>与<span class="highlight">精准控制</span></p>
</header>
</body>
</html>
6、Mini experiment
小小实验效果 – 翻译
部分代码举例:
import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import TranslationResult from '../components/TranslationResult';
import { translateText, getAlternativeTranslations } from '@/utils/translateApi';
import { useTranslation } from '@/hooks/useTranslation';
import LanguageSelector from '@/components/LanguageSelector';
import { Loader2 } from 'lucide-react';
const Index = () => {
const [inputText, setInputText] = useState('');
const [sourceLang, setSourceLang] = useState('zh');
const { detectLanguage, autoDetectEnabled, setAutoDetectEnabled } = useTranslation();
// 所有支持的语言
const allLanguages = ['zh', 'en', 'ja', 'ko'];
// 目标语言(所有语言,包括源语言)
const targetLanguages = allLanguages;
const { data: translations, isLoading, error, refetch } = useQuery({
queryKey: ['translations', inputText, sourceLang, targetLanguages],
queryFn: async () => {
if (!inputText.trim()) return [];
// 如果启用了自动检测,先检测语言
let actualSourceLang = sourceLang;
if (autoDetectEnabled && inputText.trim()) {
const detectedLang = await detectLanguage(inputText);
if (detectedLang && allLanguages.includes(detectedLang)) {
actualSourceLang = detectedLang;
setSourceLang(detectedLang);
}
}
// 获取所有语言的翻译,包括源语言(将返回多个备选翻译)
const results = await Promise.all(
allLanguages.map(async (lang) => {
// 如果是源语言,获取多个备选翻译
if (lang === actualSourceLang) {
return await getAlternativeTranslations(inputText, actualSourceLang);
} else {
// 其他语言正常翻译
const mainTranslation = await translateText(inputText, actualSourceLang, lang);
// 获取备选翻译
const alternatives = await getAlternativeTranslations(inputText, actualSourceLang, lang);
return {
main: mainTranslation,
alternatives: alternatives
};
}
})
);
return results;
},
enabled: false,
});
// 当输入文本变化时,如果文本长度大于0,自动触发翻译
useEffect(() => {
const timer = setTimeout(() => {
if (inputText.trim().length > 0) {
refetch();
}
}, 500);
return () => clearTimeout(timer);
}, [inputText, refetch, sourceLang]);
const handleClearText = () => {
setInputText('');
};
return (
<div className="min-h-screen flex flex-col bg-gray-50 p-4">
<div className="max-w-4xl w-full mx-auto flex flex-col flex-1">
<h1 className="text-3xl font-bold text-center my-6">多语言实时翻译</h1>
<div className="grid grid-cols-1 gap-6 mb-6">
<div className="flex flex-col h-full">
<div className="flex items-center justify-between mb-2">
<LanguageSelector
value={sourceLang}
onChange={setSourceLang}
languages={allLanguages}
autoDetect={autoDetectEnabled}
onAutoDetectChange={setAutoDetectEnabled}
/>
{inputText && (
<Button
variant="ghost"
size="sm"
onClick={handleClearText}
>
清空
</Button>
)}
</div>
<Textarea
placeholder="请输入要翻译的文本..."
value={inputText}
onChange={(e) => setInputText(e.target.value)}
className="min-h-[150px] resize-none"
/>
</div>
<div className="flex flex-col h-full">
<Tabs defaultValue={targetLanguages[0]} className="flex-1 flex flex-col">
<div className="flex items-center justify-between mb-2">
<TabsList>
{targetLanguages.map(lang => (
<TabsTrigger key={lang} value={lang}>
{lang === 'zh' ? '中文' : lang === 'en' ? '英文' : lang === 'ja' ? '日文' : '韩文'}
</TabsTrigger>
))}
</TabsList>
</div>
{targetLanguages.map((lang, index) => (
<TabsContent key={lang} value={lang} className="flex-1 flex flex-col">
{isLoading ? (
<div className="flex items-center justify-center h-full">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
) : error ? (
<div className="text-center text-red-500 p-4">
翻译出错,请重试
</div>
) : translations && translations[index] ? (
<TranslationResult
language={lang}
translation={translations[index]}
originalText={inputText}
isSourceLanguage={lang === sourceLang}
/>
) : (
<div className="text-center text-gray-400 p-4">
请输入文本进行翻译
</div>
)}
</TabsContent>
))}
</Tabs>
</div>
</div>
</div>
</div>
);
};
export default Index;
import { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Volume2, Copy, CheckCheck, Pause } from 'lucide-react';
const languageNames = {
en: '英语',
ja: '日语',
ko: '韩语',
zh: '中文',
};
const languageCodes = {
en: 'en-US',
ja: 'ja-JP',
ko: 'ko-KR',
zh: 'zh-CN',
};
const TranslationResult = ({ language, translation, originalText, isSourceLanguage }) => {
const [speaking, setSpeaking] = useState(false);
const [error, setError] = useState(false);
const [copied, setCopied] = useState(false);
const [speechSynthesis, setSpeechSynthesis] = useState(null);
const [utterance, setUtterance] = useState(null);
const [selectedTranslation, setSelectedTranslation] = useState(0);
// 处理翻译数据
const mainText = isSourceLanguage
? translation?.alternatives?.[0] || originalText
: translation?.main || '';
const alternativeTexts = isSourceLanguage
? translation?.alternatives?.slice(1) || []
: translation?.alternatives || [];
useEffect(() => {
setSpeechSynthesis(window.speechSynthesis);
return () => {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
};
}, []);
useEffect(() => {
if (speechSynthesis && mainText) {
const newUtterance = new SpeechSynthesisUtterance(mainText);
newUtterance.lang = languageCodes[language];
newUtterance.onstart = () => setSpeaking(true);
newUtterance.onend = () => setSpeaking(false);
newUtterance.onerror = () => {
setError(true);
setSpeaking(false);
};
setUtterance(newUtterance);
}
}, [speechSynthesis, mainText, language]);
const handleSpeak = () => {
if (!speechSynthesis || !utterance) return;
try {
if (speaking) {
speechSynthesis.cancel();
setSpeaking(false);
} else {
// 停止所有正在播放的语音
speechSynthesis.cancel();
speechSynthesis.speak(utterance);
}
} catch (err) {
setError(true);
setSpeaking(false);
console.error('语音合成错误:', err);
}
};
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(mainText);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('复制失败:', err);
}
};
return (
<div className="flex flex-col h-full">
<div className="flex justify-end mb-2">
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={handleSpeak}
disabled={!mainText || error || !speechSynthesis}
title={speaking ? "停止朗读" : "朗读文本"}
className="flex items-center gap-1"
>
{speaking ? (
<>
<Pause className="h-4 w-4" />
停止朗读
</>
) : (
<>
<Volume2 className="h-4 w-4" />
朗读
</>
)}
</Button>
<Button
variant="outline"
size="sm"
onClick={handleCopy}
disabled={!mainText}
title="复制文本"
className="flex items-center gap-1"
>
{copied ? (
<>
<CheckCheck className="h-4 w-4 text-green-500" />
已复制
</>
) : (
<>
<Copy className="h-4 w-4" />
复制
</>
)}
</Button>
</div>
</div>
<div className="bg-white rounded-md p-4 flex-1 border">
{mainText ? (
<div>
<p className="whitespace-pre-wrap mb-4">{mainText}</p>
{alternativeTexts && alternativeTexts.length > 0 && (
<div className="mt-4 pt-4 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-500 mb-2">备选翻译:</h4>
<div className="space-y-2">
{alternativeTexts.map((alt, idx) => (
<div key={idx} className="p-2 bg-gray-50 rounded-md text-sm">
{alt}
</div>
))}
</div>
</div>
)}
</div>
) : (
<p className="text-gray-400 italic">翻译结果将显示在这里</p>
)}
</div>
</div>
);
};
export default TranslationResult;
import axios from 'axios';
// 使用MyMemory翻译API进行翻译
export const translateText = async (text, sourceLang, targetLang) => {
if (!text || text.trim().length === 0) {
return '';
}
try {
// 将我们的语言代码转换为MyMemory API使用的语言代码
const sourceCode = getLanguageCode(sourceLang);
const targetCode = getLanguageCode(targetLang);
const response = await axios.get('https://api.mymemory.translated.net/get', {
params: {
q: text,
langpair: `${sourceCode}|${targetCode}`,
}
});
if (response.data && response.data.responseData && response.data.responseData.translatedText) {
return response.data.responseData.translatedText;
} else if (response.data && response.data.responseStatus && response.data.responseStatus === 429) {
throw new Error('翻译请求过多,请稍后再试');
} else {
throw new Error('翻译失败');
}
} catch (error) {
console.error('翻译出错:', error);
// 如果API调用失败,尝试使用备用方法
try {
const response = await fetch('https://nocode.sankuai.com/api/ai/chat/segment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
stream: false,
hostname: window.location.href,
messages: [{
role: 'user',
content: [{
type: 'text',
text: `将这段${getLanguageName(sourceLang)}翻译成${getLanguageName(targetLang)},只需要返回翻译结果:${text}`,
source: null
}]
}],
files: []
})
});
const data = await response.json();
if (data.code === 0 && data.data) {
return data.data;
} else {
throw new Error(data.msg || '翻译失败');
}
} catch (backupError) {
console.error('备用翻译也失败:', backupError);
throw new Error('翻译请求失败,请稍后再试');
}
}
};
// 获取多个备选翻译
export const getAlternativeTranslations = async (text, sourceLang, targetLang) => {
if (!text || text.trim().length === 0) {
return [];
}
try {
// 使用AI接口获取多个备选翻译
const response = await fetch('https://nocode.sankuai.com/api/ai/chat/segment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
stream: false,
hostname: window.location.href,
messages: [{
role: 'user',
content: [{
type: 'text',
text: targetLang
? `请将这段${getLanguageName(sourceLang)}翻译成${getLanguageName(targetLang)},给出3个不同的翻译版本,每个版本占一行,不要有编号或其他说明:${text}`
: `请给出这段${getLanguageName(sourceLang)}的3个同义表达,每个表达占一行,不要有编号或其他说明:${text}`,
source: null
}]
}],
files: []
})
});
const data = await response.json();
if (data.code === 0 && data.data) {
// 分割多行结果
const alternatives = data.data
.split('\n')
.filter(line => line.trim().length > 0)
.slice(0, 3); // 最多取3个结果
return alternatives;
} else {
console.error('获取备选翻译失败:', data.msg);
return [];
}
} catch (error) {
console.error('获取备选翻译出错:', error);
return [];
}
};
// 获取MyMemory API使用的语言代码
function getLanguageCode(lang) {
switch (lang) {
case 'zh': return 'zh-CN';
case 'en': return 'en-US';
case 'ja': return 'ja-JP';
case 'ko': return 'ko-KR';
default: return 'en-US';
}
}
// 获取语言名称
function getLanguageName(lang) {
switch (lang) {
case 'zh': return '中文';
case 'en': return '英文';
case 'ja': return '日文';
case 'ko': return '韩文';
default: return '英文';
}
}
import { useState } from 'react';
import axios from 'axios';
export const useTranslation = () => {
const [autoDetectEnabled, setAutoDetectEnabled] = useState(true);
// 检测输入文本的语言
const detectLanguage = async (text) => {
if (!text || text.trim().length === 0) return null;
try {
// 使用MyMemory API检测语言
const response = await axios.get('https://api.mymemory.translated.net/get', {
params: {
q: text.slice(0, 100), // 只使用前100个字符进行检测
langpair: 'auto|en', // 自动检测源语言
}
});
if (response.data && response.data.responseData && response.data.responseData.detectedLanguage) {
const detectedCode = response.data.responseData.detectedLanguage.toLowerCase();
// 将API返回的语言代码映射到我们的语言代码
if (detectedCode.startsWith('zh')) return 'zh';
if (detectedCode.startsWith('en')) return 'en';
if (detectedCode.startsWith('ja')) return 'ja';
if (detectedCode.startsWith('ko')) return 'ko';
}
// 如果API检测失败,尝试使用备用方法
try {
const response = await fetch('https://nocode.sankuai.com/api/ai/chat/segment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
stream: false,
hostname: window.location.href,
messages: [{
role: 'user',
content: [{
type: 'text',
text: `检测这段文本是什么语言,只需要回答"zh"、"en"、"ja"或"ko"中的一个:${text.slice(0, 100)}`,
source: null
}]
}],
files: []
})
});
const data = await response.json();
if (data.code === 0 && data.data) {
const detectedLang = data.data.trim().toLowerCase();
if (['zh', 'en', 'ja', 'ko'].includes(detectedLang)) {
return detectedLang;
}
}
} catch (backupError) {
console.error('备用语言检测失败:', backupError);
}
// 默认返回中文
return 'zh';
} catch (error) {
console.error('语言检测失败:', error);
return 'zh'; // 默认返回中文
}
};
return {
detectLanguage,
autoDetectEnabled,
setAutoDetectEnabled
};
};
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Check, ChevronDown, Languages } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
DropdownMenuCheckboxItem,
} from '@/components/ui/dropdown-menu';
const languageNames = {
zh: '中文',
en: '英文',
ja: '日文',
ko: '韩文',
};
const LanguageSelector = ({ value, onChange, languages, autoDetect, onAutoDetectChange }) => {
const handleLanguageChange = (lang) => {
onChange(lang);
};
const toggleAutoDetect = () => {
onAutoDetectChange(!autoDetect);
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="flex items-center gap-2">
<span>{autoDetect ? '自动检测' : languageNames[value]}</span>
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48">
<DropdownMenuCheckboxItem
checked={autoDetect}
onCheckedChange={toggleAutoDetect}
>
自动检测语言
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
{languages.map((lang) => (
<DropdownMenuItem
key={lang}
onClick={() => handleLanguageChange(lang)}
className="flex items-center justify-between"
>
<span>{languageNames[lang]}</span>
{value === lang && !autoDetect && <Check className="h-4 w-4" />}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
};
export default LanguageSelector;
写在最后 : 如果觉得还不错,或者对您有帮助,麻烦动动小手,点赞或者关注,谢谢!