最近这两年被问得最多的一个问题,就是"AI到底怎么学才能真的上手"。我见过太多朋友兴致勃勃地下载了一堆教程,今天学点提示词技巧,明天看一段大模型原理,后天又跑去研究某个开源框架,折腾两三个月,真正要做一个能用的东西时还是无从下手。我自己也走过这段弯路,后来才慢慢想明白一件事:ai-engineering-from-scratch 真正要解决的,不是"怎么理解注意力机制",也不是"怎么训一个自己的模型",而是怎么把 AI 能力稳定地、可控地、可维护地交付成一个实际在跑的系统。换句话说,engineering 的重点在于"工程",而不是"模型研究"。
这篇文章我想以个人经验的形式,聊一聊从零开始做 AI 应用开发时,真正值得投入精力的几个方向:prompt engineering 提示工程怎么从摸索变成流程、AI Agent 工作流怎么从 demo 变成可用系统、本地部署 AI 的成本和边界怎么算,以及我在实际项目里踩过的一些坑。如果你是想进入 AI 应用领域但还没找到路线的开发者、测试或产品同学,这篇内容应该能帮你省下不少试错的时间。
1. AI工程不是"训练模型",而是"交付稳定能力"
1.1 研究、开发、工程是三种不同的活
很多新人会把"AI工程师"和"算法工程师"混在一起,其实在大多数业务场景里,两者干的完全是不同的事。
- 算法/研究:关注模型结构、训练方法、效果指标,目标是"让模型在某项能力上达到更好"。
- 应用开发:关注 API 怎么调、数据怎么传、结果怎么解析,目标是"让模型能力跑进产品里"。
- AI工程:关注稳定性、成本、可观测性、回归测试、权限与合规,目标是"让 AI 能力持续可用、可控、可修"。
打个比方,研究好比内燃机实验室里改进缸内燃烧效率,开发好比把发动机装进车架,而工程则要解决整车在不同路况下都能平稳跑、该保养时能检测、出故障时能定位。大多数公司真正缺的不是发动机狂热爱好者,而是能把整辆车造好的人。所以对多数想入行的人来说,从工程视角切入 AI,比从算法视角切入更快、更稳、也更贴近真实商业价值。
1.2 从零开始,也要分层踩楼梯
"从零开始"这四个字,很多人理解歪了。一说到零基础,就觉得该从线性代数、反向传播、Transformer论文读起。说实话,如果你的目标是做 AI 应用,这条路线很长,而且中途很容易放弃。
我比较推荐按"应用采购 → 框架理解 → 深度定制"三层来走:
- 应用级:调用成熟大模型 API,学会设计提示词、处理结构化输出、管理上下文,做一个能跑的小应用。
- 框架级:掌握 Agent 工作流、RAG 检索、多工具调度、测评与方法,搭建可扩展的应用体系。
- 研究级:如果工作需要,再去深入微调、对齐、压缩,甚至预训练细节。
初学者应该先把第一层走通,做一个真实的、别人愿意用的东西。一个跑在本地的小工具,胜过十个只存在于收藏夹的教程。这也是我写这类实践分享时最想强调的一点:先把交付闭环打通,后面所有深层次学习才有意义。
2. prompt engineering 的工程化改造:从玄学到流程
2.1 提示词为什么值得当成工程对待
提示词看起来只是"跟模型说句话",但在一套稳定系统里,它是需求说明书、接口契约、行为约束的合体。一个不稳定的提示词,会让整个应用的表现跟着飘。把提示词当作工程资产来管理,至少要做三件事:
- 版本化:提示词和代码一样要入库、要记录修改历史。我在项目里会把每个场景的提示词单独写成文本或模板文件,跟代码一起提交。
- 变量化:把用户输入、业务参数、历史记录这些可变部分剥离出来,做成模板占位符,不要在运行时用字符串拼接硬塞。
- 回归集:准备一组固定的测试样本,每次改完提示词就跑一遍,对比输出质量和格式,防止"修好一个 case 崩掉一片 case"。
很多人改提示词靠感觉,这次加一句"请更认真",下次加一句"如果不知道就说不知道",效果时好时坏。真正该做的是把每一次改动明确成一个假设:我加了什么约束?它应该影响哪些样本?这些样本在回归集里有没有覆盖?有了这套思维,提示词就从"玄学"变成了"实验"。
2.2 一套够用的提示词模板结构
我调过不少模型,总结出一套比较通用的提示词骨架,新手可以直接抄:
[系统角色] 你是一名资深的XX领域助手,擅长处理XX类任务。 [任务目标] 根据给定的输入,完成XX目标,输出XX格式。 [输入约束] 只使用输入中提供的信息,不要臆测;信息不足时明确说明。 [处理步骤] 先分析,再判断,最后给出答案;分析过程不写入最终输出。 [输出格式] 严格输出JSON,字段包括:result(string)、confidence(number)、reason(string)。 [反例警示] 禁止输出空字段;禁止编造数据;禁止在JSON里输出markdown代码块标记。这套结构的核心逻辑是:先给模型身份和目标,再给边界和路径,最后卡死输出格式。每一步都在降低不确定性。身份设定影响语气和知识框架,任务目标让模型知道要做什么,输入约束减少幻觉,处理步骤提升推理质量,输出格式方便程序解析。
实际测试中,我最常踩的坑是"输出格式"这一项写得太随便。模型返回的内容里偶尔会带```json 或 "字段名多一个引号",解析直接报错。所以我在项目里不会只靠提示词约束格式,还会在代码里做一层兜底校验。
2.3 用代码强制格式,而不是用嘴请求格式
下面是一段我常用的调用片段,加了格式校验与重试逻辑,保证返回结果能稳稳落到 JSON 里:
import json import re from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="local") def ask_structured(prompt: str, schema_keys: list[str]) -> dict: for attempt in range(3): resp = client.chat.completions.create( model="qwen2.5-7b-instruct", messages=[ {"role": "system", "content": f"只输出JSON,字段必须包含{schema_keys},不要输出markdown代码块。"}, {"role": "user", "content": prompt}, ], temperature=0.2, ) text = resp.choices[0].message.content.strip() # 去掉可能包裹的代码块 text = re.sub(r"^```(?:json)?|```$", "", text).strip() try: data = json.loads(text) missing = [k for k in schema_keys if k not in data] if not missing: return data except json.JSONDecodeError: continue raise RuntimeError("模型输出连续三次不符合格式要求")这段代码做了三件重要的事:用 schema_keys 做字段级校验、用重试容忍模型偶发抽风、用正则把 markdown 包裹剥掉。提示词负责引导,代码负责兜底,两者配合才是工程做法。只靠其中一环,早晚要出问题。
3. 从单次调用到Agent工作流:把能力编排成流程
3.1 什么场景真正需要 Agent
并不是所有任务都要上 Agent。如果一个任务用一次提示词调用能完成,硬套 Agent 只会增加延迟、成本和故障点。我判断是否值得做 Agent,一般看三个信号:
- 多步骤:任务没法一次回答完成,需要先查资料、再分析、再汇总。
- 工具依赖:需要调用外部 API、数据库、计算器或搜索接口才能拿到关键信息。
- 路径不确定:预先写不出固定脚本,需要模型根据中间结果动态决定下一步。
比如做一个小型竞品分析助手,用户问"对比 A 产品和 B 产品最近一周的公开评价",合理的做法是先检索多个来源的评论,再进行归纳,而不可能靠一次 prompt 调用完成。这种场景下,Agent 的价值就是把"检索 → 思考 → 归纳"组织成可控的循环。
3.2 自己写一个最简 Agent 循环,比搭框架更懂原理
我建议初学者至少亲手写一次最简单的 Agent 循环,再决定用不用框架。最简版的思路不复杂:
tools = { "search": search_function, "calc": calculator_function, "summarize": summarize_function, } messages = [{"role": "user", "content": user_task}] for step in range(max_steps): # 1. 让模型决定:直接回答,还是调用某个工具 decision = model.decide(messages, tool_names=list(tools.keys())) # 2. 如果是最终答案,退出循环 if decision.action == "final_answer": return decision.output # 3. 执行工具,把结果作为新消息追加进对话 tool_result = tools[decision.action](**decision.args) messages.append({"role": "tool", "name": decision.action, "content": tool_result})这个循环的每一步都清楚可见:模型负责"决策",代码负责"执行",工具结果回到对话里再驱动下一轮决策。写一遍这个循环,你对 Agent 的认知会从"魔法"变成"一个带工具的 while 循环"。
理解了底层的循环逻辑后再看现成框架,会发现它们解决的核心问题其实是三件:工具协议统一、上下文管理、错误重试与权限控制。这些是易错的地方,也是框架的价值所在。
3.3 框架选型:Spring AI 还是自研轻量方案
框架选择没有银弹,我提供一套自己的取舍逻辑:
- 团队已有 Java/Spring 技术栈:可以认真看 Spring AI。它的好处是能和现有 Spring Boot 服务平滑集成,Bean 管理、配置中心、监控体系都是现成的,不必为 AI 功能单独搭一套基础设施。
- 需要灵活编排复杂 Agent 图:考虑 LangChain 或 LangGraph 这一系,但一定要锁定版本,因为这类框架的 API 演进速度很快,网上教程很容易过期。
- 只有一两个简单场景:我更推荐自研轻量调用层,用几十行代码封装,比引入一个重框架更可控。
我自己的项目里有几个线上服务,用的就是简单的自研封装层。原因很朴素:场景少、需求明确,自研代码五十行能维护,框架升级半年一次太折腾。框架的价值在于解决高频复杂问题,而不是给自己增加学习税。
4. 本地部署开源模型:到底图什么,显存账怎么算
4.1 先算账,再部署
本地部署这个词听起来很酷,但真正决定要不要部署的,是几个实际问题:
- 数据隐私:敏感数据能不能出内网?如果答案是不能,那本地部署几乎是唯一选择。
- 离线可用:业务场景断网也要能跑,比如产线边缘设备,那就得本地。
- 长期成本:调用量很大时,按 Token 付费可能比自建推理服务器还贵,这时候本地部署就有账可算。
- 管控程度:需要完全控制模型版本、量化方式、并发策略,云上 API 满足不了。
如果以上四条一条都不沾,我劝你别折腾本地部署。直接调用成熟 API 是最省心的方案,能把精力留给业务逻辑。技术选型不是为了炫技,而是为了解决真实约束。
4.2 显存估算:一个能直接用的模型账本
本地部署最常见的翻车点就是显存不够。我一般按"参数量 × 每参数字节数 + 运行时开销"来估算:
| 模型参数量 | 精度 | 大约显存占用 | 显卡建议 |
|---|---|---|---|
| 7B | FP16 | 约14GB | 16GB 以上(如 RTX 4090、L4) |
| 7B | INT4 量化 | 约4-5GB | 8GB 以上(如 RTX 3060、Arc A770) |
| 14B | INT4 量化 | 约8-9GB | 16GB 以上 |
| 32B | INT4 量化 | 约18-20GB | 24GB 以上(如 RTX 3090/4090) |
这里有一个新手容易忽略的点:显存只满足"能加载模型"是不够的,推理时的 KV Cache 和并发请求会额外吃掉大量显存。实际单卡部署时,我会把显存预算留出 20%-30% 的余量,否则一旦并发上来就会报显存不足,甚至触发 OOM 崩溃。
4.3 本地推理框架与调用方式
推理框架我接触得比较多的是这几类:
- Ollama:适合个人电脑快速体验,安装简单,一条命令能起服务,但它更偏单机、低并发场景。
- vLLM:吞吐量高,支持 PagedAttention,适合服务端多并发推理,是生产环境常见选择。
- llama.cpp:CPU/混合设备友好,量化支持好,适合没有大显存显卡的机器。
我比较喜欢的组合是:用 vLLM 起 OpenAI 兼容接口,然后应用层直接走 OpenAI SDK 调它。这样业务代码和云端 API 的切换成本几乎为零。本地起服务大致是这样一个流程:
# 假设模型放在当前目录下的 models/Qwen2.5-7B-Instruct python -m vllm.entrypoints.openai.api_server \ --model models/Qwen2.5-7B-Instruct \ --served-model-name local-model \ --port 8000 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192启动后只需要注意一件事:vLLM 的版本和解码参数会直接影响生成质量,尤其要关注新版对 sampling 参数的默认值改动。我遇到过前后两个版本对同样提示词输出风格差异很大的情况,后来就把版本号固定在 requirements 里,升级前先跑回归集。
5. 落地AI应用时,我踩过的坑和当前的工程建议
5.1 坑一:只调提示词,不复测回归集
我有一次改了一个"更严格"的提示词,当时测了十几个样本都正常,上线后第二天用户反馈某些历史对话场景输出格式全乱。一查原因:新的约束和旧场景想表达的立场冲突,导致部分输出进入了防御性回答分支。从那以后我定了一个死规矩:任何提示词改动,必须连同回归测试一起过。回归样本不用多,20 到 50 个典型业务输入就够,关键是覆盖正常、边界、异常三类情况。
自动化回归里我还会跑一个简单的"指标校验",比如关键字段缺失率、格式错误率、空响应率。这些指标不需要复杂的评测体系,普通日志统计就能做,但价值非常大。
5.2 坑二:把所有逻辑塞进一个超长提示词
早期我做业务功能时,恨不得一个提示词里包含全部业务规则、历史背景、常见问题答案,结果提示词达到了三千字。后果是:每次请求的 Token 成本高、响应变慢、模型注意力被稀释,而且稍微改一处业务逻辑,提示词整体都要调整。
后来我把这个"巨型提示词"拆成了"角色 + 检索片段 + 当前问题"三段,业务知识不再写进提示词,而是提前离线切片、在请求时用向量检索取最相关的几段拼进去。这个改造其实就是 RAG 的朴素养成路径。
5.3 坑三:上下文无限增长,对话后期质量劣化
做聊天类应用时,我早期把所有历史消息都往 messages 里塞,结果对话超过二十轮后模型开始"记不清"前面内容,甚至出现重复回答。后来做了两层改进:
- 滑动窗口:只保留最近 N 轮完整消息,更早的对话总结成一段摘要放进系统提示。
- 关键信息提取:从每轮对话里抽取用户偏好、已确认事项、待办内容,单独维护状态,需要时再注入。
这个经验后来也成为我做 Agent 的一点原则:上下文是一种稀缺资源,必须主动管理,而不是把什么都丢给模型自己消化。
5.4 坑四:忽略输入输出边界,把日志变成安全隐患
我在一个内部工具里曾把完整用户输入直接打进日志,后来复盘时发现这种习惯非常危险。AI 应用里用户的提问可能包含个人信息、内部业务数据、甚至无意的敏感描述,日志、缓存、监控链路都等于变相泄露渠道。做工程化时我坚持三条:
- 输入侧过滤:对用户输入做基础的异常长度校验,防止超长文本打爆模型上下文。
- 输出侧脱敏:API 返回结果落日志前,替换掉疑似手机号、身份证号、邮箱等模式。
- 权限隔离:管理后台和普通调用走不同的 Key,数据面与控制面严格分开。
这些点未必直接提升模型效果,但它们是工程系统能活多久的地基。一个效果惊艳但处处漏数据的应用,是不具备上线资格的。
最后再分享一点个人体会:我见过不少起步很快的开发者,也见过学了很久还在原地打转的人,差距往往不在聪明程度,而在"是否快速完成了第一次完整交付"。AI 这个领域信息密度极大,今天出一个新模型,明天多一个新框架,但如果手里没有一个自己跑通的项目,所有新东西都只是噪音。先把手头的 API 调通、提示词调稳、输出格式卡死,做一个最简陋但完整的工具,那之后学习曲线会突然变得顺畅许多。