第1900篇:里程碑特辑1900——写给每一个走到这里的AI转型路上的工程师
第1900篇:里程碑特辑1900——写给每一个走到这里的AI转型路上的工程师
1900篇。
我盯着这个数字看了很久。
不是因为它有多了不起——每天有无数篇文章在被写出来、被阅读、被遗忘。而是因为这1900篇,对我来说,是真实时间的刻度。每一篇的背后,是某个深夜在电脑前的我,带着那个阶段真实的困惑或兴奋,试着把想清楚的东西写出来。
今天不写技术,或者说,只写技术背后的那些东西。
一、从什么时候开始,到今天
这个公众号的起点,不是某个宏大的规划。
那时候我刚换了一家公司,开始做和AI相关的业务。那是2022年前后,大模型还没有今天这样的热度,但我隐约感觉到,某种根本性的变化正在积累。我开始认真学,学了之后想整理,整理了之后想分享,于是开始写。
最早几篇,阅读量少得可以,我能数清楚有几个人看。发给朋友,朋友说"你这个很有意思,但是……"然后提了一堆改进意见。我一边觉得有道理,一边暗自委屈。
现在回看那些早期的文章,写得确实不怎么样。但有一点值得庆幸:从第一篇开始,我就没有试图写"正确的废话"。我写的都是我当时真实的理解,即使理解有限,即使后来被更新了,但当时是真诚的。
二、1900篇走下来,我真正学到了什么
写作会逼你想清楚。
这是我觉得这1900篇给我最大的回报——不是涨了多少粉,不是建立了什么影响力,而是有几百个话题,我被迫认真地想过了。
有些想法,在脑子里的时候感觉很清晰,一落到文字就发现是烂的。有些概念,你以为懂了,写的时候才发现有个逻辑漏洞,然后花两天搞清楚那个漏洞。
这种"被写作逼着想清楚"的经历,是比读很多书更有效的学习方式。
具体学到的东西:
关于大模型,我从"会用API"变成了能解释"为什么这么设计"。从对着文档抄示例,到看到一个新场景能直觉判断哪里有坑。这个过程不是某一天突然发生的,是写了几十篇、做了几十个项目,慢慢沉淀下来的。
关于工程,我学会了一件事:稳定性比聪明重要。好看的demo不难做,难的是上了生产还能稳定跑。这个认知,是被线上故障打出来的,不是看文章看出来的。
关于职业,我慢慢理解了:技术能力是门票,但真正决定走多远的,是判断力和心态。什么时候做什么、什么该做深、什么该放弃——这些判断比"会什么技术"更重要。
三、AI转型这条路,难在哪里
这些年见过很多人说"要做AI转型"。真正走下来的,比说的人少很多。
不是因为技术难。技术有点难,但没有难到不可克服。
真正难的是持续性。
你可以花一个周末把Spring AI的文档刷完,你可以在某个项目里用上RAG,你可以参加几个AI大会……但做完这些之后呢?你是继续深入,还是被下一个新技术吸引过去?
AI转型最大的陷阱,是把"学过"当成了"掌握",把"尝试过"当成了"积累"。
我见过太多工程师,ChatGPT出来学了一遍,LangChain火了又去学,AutoGen来了再去学……每个工具都会了一点点,但问他"在一个真实的企业项目里,端到端做一套稳定的AI应用",说不上来。
真正的积累,需要在一个方向上持续投入足够长的时间,踩足够多的坑,处理足够多的真实问题。这个过程很难,也很慢,但这是唯一真实的积累方式。
四、我踩过的最有价值的坑
写到这里,我想列几个真正让我成长的"坑"——不是普通的bug,而是认知层面的错误。
坑一:以为Prompt Engineering是"技巧",不是"工程"
早期我把写Prompt当成一种玄学,觉得某些特殊的表达方式有魔力。后来发现,好的Prompt是工程化的产物——有结构、有测试、有版本管理。这个认知转变,让我做AI应用的思路完全变了。
坑二:以为RAG只是"检索+生成"
第一次做RAG项目,我觉得简单:把文档切片,向量化,检索,生成。上线后发现问题一堆:召回率低、答案幻觉、多文档矛盾……后来才明白,RAG里有一堆工程问题需要一个个解决,Query改写、Reranking、上下文压缩……每个都是独立的研究方向。
坑三:以为模型越大越好
有个项目用了最大的模型,成本高,延迟大,结果没比小模型好多少。后来系统性地做了模型评估,才理解:对于特定任务,3B的微调模型可能比70B的通用模型更好,成本低50倍。选模型要看任务适配,不是看参数量。
坑四:以为做完demo就做完了
做演示是一件事,做生产是另一件事。这个认知差距,害我在好几个项目上吃了亏:演示跑得很顺,一到生产就暴露出一堆工程问题——并发、成本、降级、日志、监控……统统都没有。
五、给此刻正在AI转型路上的工程师
如果你现在正在做AI转型,我想说几句真心话。
第一,别焦虑工具的更新速度。
每周都有新模型、新框架、新工具。如果你把精力都放在跟上这些更新,会把自己搞得精疲力竭,还没有真正的积累。
选一个方向深入,让底层的理解积累起来。当你真正理解了RAG的原理,学任何新的RAG框架都是半天的事情。但如果你跟着工具跑,换一个框架就要重新学,永远在起点。
第二,做真实的项目,不要一直在练习。
看文章、看视频、做Tutorial——这些是热身,不是训练。真正的成长来自在真实项目里解决真实问题,被真实的约束逼着做真实的取舍。
哪怕是个小的内部项目,哪怕没有人使用,只要是"真实场景、真实数据、真实约束",都比做第100个ChatPDF demo有价值。
第三,建立自己的评价标准,不只是看别人怎么说。
AI领域的话语权,现在主要掌握在几家大公司和媒体手里。他们讲的,不一定是对你的工作最有用的。
学会对技术观点保持独立判断:这个方案在什么场景下适用?它的边界在哪里?它对我的具体问题有没有指导意义?这种独立判断的能力,是比任何具体技术知识都更持久的资产。
第四,分享你的学习过程。
不管是写公众号、写博客,还是在内部分享——把你学到的东西讲出来。原因前面说了:写作会逼你想清楚。
而且,你以为是"常识"的东西,可能对另一个人来说是"原来如此"。知识的价值在于传播。
六、接下来的1900篇
1900篇不是终点,是一个节点。
往后走,AI领域还会继续演进,我还会继续学,还会继续写。写的内容可能会变:技术方向在变,我自己的判断也在变。但写作的初衷不变:把真实的理解、真实的踩坑、真实的思考,用真诚的方式写出来。
我知道有些文章质量不行,有些判断后来被证明是错的,有些内容已经过时。这些都是真实的成长痕迹,我不打算删,也不打算为此道歉。每篇文章代表那个时候我最好的理解,这就够了。
如果你是从某一篇开始关注这里的,谢谢你在这里。如果你今天才刚来,也欢迎。
七、一个技术里程碑的代码纪念
里程碑特辑,不能没有代码。
这里写一段我认为"代表这个时代AI工程精神"的代码——不是最复杂的,而是最能体现稳定、实用、可靠的:
/**
* 1900篇纪念代码
* 这不是最酷的代码,但代表了我对AI工程的理解:
* 稳定可靠,考虑边界,有降级,有监控,对用户负责。
*
* @author 老张
* @since 2025
*/
@Service
@Slf4j
public class ProductionReadyAiService {
private final ChatClient chatClient;
private final ContentSafetyFilter safetyFilter;
private final MetricsRecorder metrics;
private final FallbackAnswerStore fallbackStore;
// 核心:带完整工程保障的AI调用
public AiResponse chat(ChatRequest request) {
String traceId = generateTraceId();
long startTime = System.currentTimeMillis();
try {
// 1. 输入安全检查
ContentSafetyResult inputCheck = safetyFilter.checkInput(
request.getMessage(), request.getUserId());
if (inputCheck.isBlocked()) {
metrics.recordBlocked(inputCheck.getCategory());
return AiResponse.blocked(inputCheck.getReason());
}
// 2. 构建带上下文的提示词
String enrichedPrompt = buildPrompt(request);
// 3. 带超时的模型调用
String rawResponse = callWithTimeout(enrichedPrompt, 30);
// 4. 输出安全检查
ContentSafetyResult outputCheck = safetyFilter.checkOutput(rawResponse);
if (outputCheck.isBlocked()) {
log.warn("Output blocked, traceId: {}, category: {}",
traceId, outputCheck.getCategory());
rawResponse = fallbackStore.getSafeResponse(request.getMessage());
}
// 5. 记录指标
long duration = System.currentTimeMillis() - startTime;
metrics.recordSuccess(duration, estimateTokens(rawResponse));
return AiResponse.success(rawResponse, traceId);
} catch (TimeoutException e) {
log.warn("LLM timeout, traceId: {}", traceId);
metrics.recordTimeout();
// 降级:返回预设答案
return AiResponse.fallback(
fallbackStore.getTimeoutFallback(request.getMessage()), traceId);
} catch (RateLimitException e) {
log.warn("LLM rate limited, traceId: {}", traceId);
metrics.recordRateLimit();
return AiResponse.error("系统繁忙,请稍后再试", traceId);
} catch (Exception e) {
log.error("LLM call failed, traceId: {}", traceId, e);
metrics.recordError(e.getClass().getSimpleName());
return AiResponse.error("服务暂时不可用", traceId);
}
}
private String callWithTimeout(String prompt, int timeoutSeconds) {
try {
return CompletableFuture.supplyAsync(() ->
chatClient.prompt().user(prompt).call().content()
).get(timeoutSeconds, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new LlmTimeoutException("LLM call timed out after " + timeoutSeconds + "s");
} catch (InterruptedException | ExecutionException e) {
throw new LlmCallException("LLM call failed", e);
}
}
// 这段注释比代码本身更重要:
// 我见过太多"能跑但不能用"的AI代码。
// 真正的生产代码要考虑:
// - 超时怎么处理
// - 限速怎么处理
// - 内容安全怎么保证
// - 出错了用户看到什么
// - 指标怎么记录,出问题怎么排查
// 这些不是"额外的工作",这是工程师的基本职责。
}这段代码,我希望能代表这1900篇的技术立场:不追求最酷,追求最稳。
八、写在最后
1900这个数字,我很快就会忘记,因为明天是1901,后天是1902。
但此刻,我想认真地说一声:谢谢。
谢谢每一个读到这里的人。谢谢那些在评论区留言的人,谢谢那些转发过某一篇文章的人,谢谢那些加入知识星球、持续在一起学习的人。
你们让这件事不只是一个人的自言自语。
AI时代的转型,没有一条标准答案的路。但我们在同一条模糊的路上走,互相看着对方的脚印,总比一个人摸黑好一点。
继续走吧。
