AI工程从零开始:Prompt、结构化输出与RAG实战路线

发布时间:2026/11/3 22:22:48
AI工程从零开始:Prompt、结构化输出与RAG实战路线 去年秋天有个做业务的同事找我说想做一个能自动读合同、把关键条款抽出来的小工具。我嘴上说这个简单心里想的其实是这不就是把合同丢给大模型让它总结一下吗结果真坐下来动手才发现自己对这个问题的理解浅得可笑。合同要分页解析、条款要分类型提取、输出要严格按固定字段来、抽错了还得有办法发现——这一串事情加起来根本不是调一下AI能糊弄过去的。那时候我才意识到AI工程这个词跟我原本以为的完全不是一回事。它不是让我们去训练一个模型也不是简单调用一下API接口而是站在模型能力和真实业务之间的那一整个工程层上下文怎么做、输出怎么约束、错了怎么发现、成本怎么控制。今天这篇东西就是想把我从零开始摸这条路的经验整理出来给同样想入行的人一个能落地的路线参考。1. AI工程这四个字一年前我理解错了1.1 从一次答应得太快的需求说起先把上面那个合同项目讲完。我最初的做法特别幼稚把合同全文拼进Prompt然后问请提取以下字段。第一轮跑出来的结果看起来不错条款都在格式也像那么回事。但仔细一核对就露馅了付款日期提取错了同一个字段在不同合同里表述不一样有的合同几十页上下文塞不下最离谱的是模型在合同里根本没有的内容上脑补了一个违约金比例。这四类问题每一类都指向一个完全不同的工程点——上下文管理、结构化输出、输入预处理、以及幻觉控制。这个经历教会我一件事AI工程的重点不在模型有多聪明而在你用什么机制让一个不确定的系统稳定地输出正确结果。模型是那个发动机但方向、换挡、刹车都得工程来管。1.2 AI工程和算法岗、传统开发的边界在哪里很多人包括当时的我分不清AI工程和另外两个角色的区别我直接做个对照角色核心对象典型产出判断成败的标准算法/研究岗模型本身训练方法、模型权重、论文指标涨没涨传统后端开发确定性的代码逻辑接口、系统、数据库功能对不对、稳不稳AI工程模型能力与业务之间的连接层应用、Agent、RAG系统、评测体系业务结果稳不稳定、成本合不合理AI工程最特殊的地方在于你操作的是一个非确定性系统。同一个Prompt同一个输入两次调用结果可能不一样。传统开发里bug能复现这条铁律在这里不成立。你得学着用校验、重试、兜底、评测这类手段把不确定性关进笼子里。所以我不建议一上来就抱着深度学习教材啃。AI工程从零开始零的不是数学基础而是工程化思维——怎么把一个API变成一条可信赖的生产链路。这是两套完全不同的技能树。2. 零基础起步的技术栈取舍先学什么后学什么2.1 Python和API就是第一课不是大模型原理如果有人问我AI工程从零开始第一步学什么我的答案永远是Python基础语法、HTTP请求、JSON处理、以及读懂一份API文档。就这么朴素。你不用先会反向传播甚至不用先懂Transformer结构。绝大多数AI工程工作发生在模型之外拼Prompt、处理文档、调接口、解析返回值、写评测脚本。这些全是Python基本功。我见过不少朋友一上来就去啃PyTorch教程学了一周张量运算然后发现自己要做的东西跟这些完全没关系。不是说原理不该懂而是知识获取的顺序错了——先动手把链路跑通产生真实反馈再回头补原理效率高得多。2.2 绕不开的五个底层概念不管用哪家模型这几个概念是通用的我建议刚开始就把它们彻底搞明白Token模型计费和上下文长度的单位。英文大约1个词等于1-2个token中文1个汉字大约是1-2个token。它决定了你的输入能塞多满、每次请求花多少钱。上下文窗口模型一次能看到的全部内容上限包括系统提示词、用户输入、历史对话和模型输出。超出部分不会报错而是被静默截断这是最阴险的坑之一。温度与采样参数temperature控制随机性。做提取、分类这类任务我通常设0或接近0做生成类内容才调高。角色体系chat模型区分system、user、assistant三个角色系统提示词是给模型的劳动合同优先级最高。结构化输出与函数调用让模型按约定格式返回或触发工具这是把模型从聊天机器人变成员工的关键开关。这些概念都不难但每个都对应着一类生产事故。比如不理解上下文窗口就会在长文档场景里莫名丢信息不理解温度就会在做抽取任务时得到飘忽不定的结果。2.3 我的推荐学习顺序Prompt → 结构 → RAG → Agent → 微调把学习路径压缩成五站每一站都建立在前一站之上Prompt工程学会写系统提示词、给示例、设约束。这是地基也是性价比最高的技能。结构化输出学会用JSON模式、函数调用把模型的话变成程序能直接使用的数据。RAG检索增强学会把私有知识、长文档通过切分-向量化-检索-拼装的方式喂给模型。Agent编排让模型自主决定调用哪些工具、按什么顺序执行。这是当前上限最高也最难的一站。微调可选当Prompt和RAG都调不动了再考虑。对绝大多数业务前四站已经够用。我特别想强调前期别急着上框架。LangChain这类框架确实能帮你少写代码但它同时把到底发生了什么藏在抽象后面。我第一次用框架跑RAG出了问题根本不知道数据流在哪一环断的。后来改成手写全链路几十行代码所有环节清清楚楚再回去用框架就顺手多了。先用最笨的方式做一遍再做优化是AI工程里最值得坚持的习惯。3. 第一条生产线把Prompt变成可复用的函数3.1 提示词不是写作文是定义接口很多新人写Prompt像在写作文动辄几百字有情有景有要求。但真正生产级的提示词更像在定义接口输入是什么、输出是什么、边界在哪里、出错时怎么办。以提取合同条款为例差劲的Prompt是请帮我提取这份合同里的关键信息合格一点的Prompt会先定义角色和任务边界再给输出字段的精确schema再给2-3个输入-输出示例最后声明如果原文中没有该字段必须返回空字符串不得自行推断。三条切实可用的经验系统提示词里写任务是做什么、不做什么边界比能力重要。不要做的事往往比要做的事更能防止幻觉。少样本示例要选困难的、有代表性的不要选最顺的。让模型看到边界情况长什么样比给它一堆标准答案有用。输出格式的约束写两遍一遍在文字描述里一遍放在结构化输出参数里如果有双保险。3.2 用JSON模式锁定输出格式让模型自由发挥文本然后你解析——这个思路在Demo里可以在生产里会把你折腾死。模型输出是的好的没问题这种废话时你的正则、JSON解析器全都会崩。现在主流模型基本都支持JSON模式或结构化输出。我自己的做法是把schema传给模型让它在schema约束下输出然后再在代码里用pydantic或json库做二次校验。注意校验这步不能省——模型承诺了JSON不代表它真的会给合法JSON网络层截断、字符转义错误都可能导致解析失败。这里贴一段我当时写的提取函数的核心逻辑from openai import OpenAI import json client OpenAI() contract_schema { type: object, properties: { party_a: {type: string}, party_b: {type: string}, contract_amount: {type: [number, null]}, payment_terms: { type: array, items: {type: string} }, missing_fields: { type: array, items: {type: string} } }, required: [party_a, party_b, contract_amount, payment_terms, missing_fields] } def extract_contract(text: str) - dict: resp client.chat.completions.create( modelgpt-4o-mini, response_format{type: json_object}, messages[ {role: system, content: ( 你是合同条款提取引擎。只提取原文中明确存在的信息。 原文没有的字段contract_amount返回nullmissing_fields列出缺失字段。 不得推断、不得补全、不得修改原文措辞。 )}, {role: user, content: text[:8000]} ] ) data json.loads(resp.choices[0].message.content) # 二次校验字段缺失时抛出异常而不是带病进入下游 assert payment_terms in data, fmissing payment_terms: {data} return data看到那个assert了吗我一开始没有它后来因为某个模型的输出偶尔丢字段下游程序直接崩了报错信息跟实际原因毫无关系排查了整整一下午。加上这行校验之后问题当场暴露清清楚楚。3.3 函数调用让模型从聊天变成干活结构化输出解决的是怎么说函数调用解决的是做什么。让模型在对话过程中主动选择调用你预定义的函数这是所有Agent类应用的基础。举个最简单的例子做一个客服助手它需要查询订单状态、处理退款申请。我不再让模型自己猜答案而是给它两个工具让它判断什么时候该调用工具、传入什么参数tools [ { type: function, function: { name: query_order, description: 查询订单当前状态需要订单ID, parameters: { type: object, properties: { order_id: {type: string, description: 用户提供的订单号} }, required: [order_id] } } }, { type: function, function: { name: refund_order, description: 发起退款需要订单ID和退款金额, parameters: { type: object, properties: { order_id: {type: string}, amount: {type: number} }, required: [order_id, amount] } } } ]调用流程也很固定把工具列表传给模型模型返回一个工具调用请求而不是普通文本你在代码里执行对应函数、拿到真实结果再把结果作为新的消息回传让模型基于真实数据继续回答。这个模型决策-程序执行-结果回填的循环就是Agent的最小内核。这里必须提醒一句凡是涉及真金白银的操作退款、删除、发送消息工具执行权一定要做人工确认层。模型判断错了工具参数是常有的事我在测试里见过它把退款金额四舍五入成整数。这种问题不是模型能力能解决的是流程设计要兜底的。4. 手写一个最小RAG系统知识库问答的完整闭环4.1 什么场景真正需要RAG先泼一盆冷水不是所有知识问答都该上RAG。如果知识量少、更新不频繁、能塞进上下文窗口直接拼进去就行又简单又便宜。但当出现下面任一情况时RAG就从可选变成必选知识总量超过上下文窗口几十页、几百页的文档知识频繁更新不可能每次都重新接进模型需要明确答案出自哪份文档的溯源要求。RAG的核心思想一句话能说清不要逼模型记住所有知识而是在每次提问时先从一个外部知识库里检索出最相关的片段把它作为参考资料塞进Prompt再让模型基于参考资料回答。模型负责读和写知识库负责记。4.2 文档切分最容易被低估的一步整个RAG链路里切分chunking是最不起眼、但对效果影响最大的环节之一。切太大检索命中一堆无关内容切太小上下文被割裂语义不完整。我踩过的坑一开始按固定300字符切结果很多段落被从中间劈开导致检索到的片段前言不搭后语。后来改成按段落边界切、每段设一个最大长度、相邻块之间留一点重叠效果好了一个档次。具体参数没有标准答案我建议至少试三组配置——小块200-300字、中块500字、段落级重叠——然后用同一批测试问题去看检索命中率别靠感觉。4.3 向量化、检索、重排、生成四步走的最小实现下面这段代码是我用最原始的库不依赖任何框架搭起来的最小RAG每一步都看得见摸得着import chromadb from openai import OpenAI client OpenAI() chroma_client chromadb.Client() collection chroma_client.create_collection(docs) # 第一步文档切分按段落切超长段落再二次切 def chunk_text(text: str, max_len: int 500) - list[str]: paragraphs [p.strip() for p in text.split(\n) if p.strip()] chunks, current [], for p in paragraphs: if len(current) len(p) max_len: current p \n else: if current: chunks.append(current.strip()) current p \n if len(p) max_len else p[:max_len] if current: chunks.append(current.strip()) return chunks # 第二步入库时生成embedding并保存 def index_document(doc_id: str, text: str) - None: chunks chunk_text(text) embeddings [] for c in chunks: emb client.embeddings.create( modeltext-embedding-3-small, inputc ) embeddings.append(emb.data[0].embedding) collection.add( ids[f{doc_id}-{i} for i in range(len(chunks))], embeddingsembeddings, documentschunks, metadatas[{doc_id: doc_id} for _ in chunks] ) # 第三步检索拼装Prompt def ask_with_rag(question: str) - tuple[str, list[str]]: q_emb client.embeddings.create( modeltext-embedding-3-small, inputquestion ).data[0].embedding hits collection.query(query_embeddings[q_emb], n_results3) contexts hits[documents][0] system ( 你是一个知识库问答助手。只能依据参考资料回答 如果参考资料中没有答案明确说知识库中未找到禁止编造。 回答末尾标注信息来源编号。 ) user 参考资料\n \n---\n.join( f[{i1}] {c} for i, c in enumerate(contexts) ) \n\n问题 question resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: system, content: system}, {role: user, content: user}] ) return resp.choices[0].message.content, contexts这个版本只能算能跑但它把RAG的每个环节都摊开了。等你理解了这个闭环再考虑引入重排模型Rerank、混合检索关键词向量、父子分块这些高级技巧。我有一个很深的感受直接上手高级方案的人出了问题往往说不清是哪一环坏了而手写过最小闭环的人看到任何框架都能一眼看穿它的数据结构流。4.4 第一次跑通后先别庆祝做这三件事第一把检索结果打出来肉眼检查。我见过太多人只盯着最终答案完全不看模型读了什么。如果检索到的片段跟问题毫不相关答案再漂亮也是空中楼阁。第二换一组切分参数重跑一遍。用同一批问题、同一个知识库只调整chunk大小和重叠值对比答案质量。你会惊讶地发现同样的系统差别只在于怎么切文档效果能差出好几个量级。第三建一个至少20条的测试问题集。每个问题标注预期答案要点和应该引用的文档。这是后面做评估的原始材料越早开始积累越好。5. 上线前绕不开的坎评估、成本与不稳定输出5.1 先建一个30条的评测集再谈上线AI应用最大的特殊性在于它没有改完就能上线这回事你永远需要回答一个问题——这次改动到底是变好了还是变坏了没有评测集你只能靠感觉而感觉在非确定性系统面前是最不可靠的东西。我的做法是拿30-50条真实问题当固定测试集每次改动都跑一遍记录三类指标检索质量正确文档有没有被召回召回率排序是否合理。答案正确性核心事实有没有答对。这个可以用LLM打分辅助评估但核心case我一直坚持人看。格式合规率能不能被程序无损解析。这个前面说过是生产事故的重灾区。评测集不用一次建完但一定要从第一天就有。我后来养成的习惯是线上每出现一次错误回答就把这个case回收进评测集。这样评测集会越来越接近真实世界的难度而不是只覆盖你想得出来的用例。5.2 Token预算、延迟与成本动手算一笔账很多人做AI应用最不看重的就是钱直到账单寄来才懵。其实成本估算非常简单动手算一次就记住了。举个具体的例子假设你的RAG系统每次请求消耗约3000个输入token 500个输出token用某款常见的商用模型输入价格按每100万token 2.5元、输出按每100万token 10元算价格是示意以官方为准那么单次请求的成本大约是输入3000 / 1000000 × 2.5 0.0075元输出500 / 1000000 × 10 0.005元单次合计约0.0125元听起来很便宜对吧但如果你的应用有10万日活、每人每天调用20次单日成本就是0.0125 × 2000000 25000元。算法没变只是乘了个DAU。所有AI应用的商业模式都必须过这一道算数题单次交互的成本 × 调用频率 × 用户量能不能被收益覆盖。延迟是另一本账。链路里每多一次串行调用延迟就线性叠加一次。常见的大模型单次响应需要1-3秒如果你的Agent流程要串行调用4次模型用户感知的等待就是4-12秒。优化手段无非三板斧能并行的并行、能缓存的缓存、能砍掉的调用坚决砍掉。先算清楚瓶颈在哪再动手优化不然就是瞎忙。5.3 面对模型今天状态不好用工程手段把飘移按住模型输出的不确定性是绕不开的但工程上有一整套办法把它按住。我在生产里常用的防御手段温度调到最低凡是抽取、分类、工具调用类任务temperature一律设0或最小允许值。加一层输出校验器对模型返回做schema校验不合格就重试一次重试仍失败就走兜底文案。日志记录一切每次请求的Prompt原文、模型输出、解析结果、耗时、成本全部落日志。没有日志出问题就像在没有监控的机房修服务器。对高危操作加确认层前面提到的退款、删除类操作一律由程序拦截、人工确认后执行。这四条看着不起眼但它们共同构成了一道安全网。AI工程做到后期比的不是谁调出来的效果更惊艳而是谁的系统在模型抽风的时候还能输出正确结果。6. 踩坑半年后回头看给想走这条路的人几句实在话6.1 先用最笨的方式跑通再谈框架和优化这句话我反复对自己说。很多人一上来就研究LangChain、研究Agent编排平台、研究微调框架研究了半个月一行业务代码没写。我的经验是先用裸API把最小功能做出来哪怕丑、哪怕慢跑通之后你对数据从哪进、从哪出就有了肌肉记忆。这时候再去学框架你是在用它而不是被它牵着走。6.2 你写的不是提示词是系统的边界Prompt里最值钱的往往是那句不要做什么。给模型划定禁区比教它做什么更能防止事故。我在做合同提取时加的那句不得推断、不得补全直接让脑补类错误大幅下降。把这段话放在系统层面理解AI工程的所有代码本质上都是在画边界——Prompt是边界校验是边界人工确认层也是边界。6.3 跟业务方聊需求先问错了怎么办传统软件开发聊需求先问要什么功能AI工程聊需求得多问一句做错了怎么办。合同字段抽错了能不能发现客服答错了由谁兜底推荐内容不合适有没有投诉通道这些问题的答案直接决定系统的架构设计。很多AI项目死在验收环节不是因为模型不聪明而是因为**错误这件事从来没有被设计进系统里**。最后分享一个我自己的小习惯每次上线新功能我会故意准备一批刁钻输入去测——特别长的、特别短的、格式乱成一团的、包含诱导性内容的。因为大模型系统里最不可预测的风险恰好藏在这些没人会准备的地方。AI工程这条路没有终点但打好地基之后每往前走一步都能看到新的东西。希望这份经验能帮你少踩几个我踩过的坑。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询