☰
AI工程从零搭建:数据、模型与Agent的工程化实战
2026/10/3 10:19:33 网站建设 项目流程

ai-engineering-from-scratch 这个词,翻译成人话就是:不靠现成模板,把 AI 应用从环境搭建、数据准备、模型调用、效果调优到上线监控整条链路亲手走通。我最近刚用一个最小项目完整跑了一遍,期间踩了不少坑,也把很多文档里没写明白的决策逻辑给捋清了。

这篇文章适合正在入门 AI 应用开发的工程师,也适合已经在调 API、但始终觉得“差点底层直觉”的同学。读完你会发现:AI 工程的难点其实不在模型,而在模型之外的工程约束——数据、评估、服务化、和那根看不见的“行为缰绳”。我会把完整思路、选型理由、可复现步骤和排障记录全部分享出来。

1. “从零开始”到底在零什么:AI 工程的能力地图

很多人一听到 from scratch,第一反应是“从零写神经网络”。但实际做工程时,真正需要从零搭的是体系,不是模型本身。你要面对的是数据怎么来、效果怎么测、延迟怎么压、Agent 怎么管,这些才是 AI 能不能落地的命门。

1.1 为什么值得亲手完整走一遍

我现在特别推荐团队新人做一件事:在一周内,不允许接任何现成的 AI 应用脚手架,从头搭一个能用的对话 Agent。哪怕功能很弱,也一定要走完整条链路。为什么?

因为只有亲手走过,你才知道一个最简单的 AI 应用背后挂着多少隐性成本。比如你以为“调一下大模型 API”就完事,实际还要考虑:提示词版本怎么管理?用户输入变了导致输出格式崩了怎么兜底?模型今天回答好、明天回答差,你怎么量化?这些问题不亲自踩一遍,光看文档永远建立不了直觉。

另一个原因是“决策链路”很难从外部资料里学到。每个项目的数据质量不一样,业务容忍度不一样,预算不一样,所以别人所谓的“最佳实践”到你这里往往不适用。你只有自己从零搭一遍,才能把每个环节的参数调节点和约束条件记在脑子里。这种能力是面试造火箭、工作拧螺丝式的速成班给不了的。

1.2 一个合格 AI 工程需要同时握住三条线

我在实操中把 AI 工程拆成三条主线,缺一条都会出问题。

第一条是数据线。包括原始语料的清洗、格式统一、评测集的构建、敏感信息的过滤。很多教程只教你“把数据扔给模型”,但真正决定效果上限的,往往是这些不起眼的数据工程。

第二条是模型线。你需要决定用闭源 API 还是开源模型,要不要微调,是用 RAG 还是靠上下文硬撑,推理时是原始精度还是量化部署。每一个选择都是成本和效果的权衡,而不是简单的好坏。

第三条是工程线。包括服务封装、并发控制、日志追踪、可观测性、评测回归、灰度上线。模型是易变的,而工程系统必须稳定。这条线最容易被忽略,但也最决定项目能不能长期跑起来。

三条线交织在一起,我习惯用一个“最小闭环”来拉动:先造一个带评测集的最小样本,跑通“数据-模型-输出-评估”的循环,再逐步加工程化能力。这样每一步都有反馈,不会做到一半才发现方向错了。

2. 先拆解:核心链路与工程化的关键决策

从零开始不等于蛮干,我建议先想清楚几个关键决策点。下面这几个问题,是每个人都会遇到的。

2.1 数据侧:从“能跑”到“稳定”的关键

数据工作最核心的目标不是“多”,而是“对齐”。模型输出风格、回答边界、调用格式,全部依赖你对数据的对齐程度。

我推荐从三份数据开始:

  • 示例数据:50 到 200 条输入输出对,覆盖正常场景和边界场景。不需要一次做多,但要聚焦你真正要解决的问题。
  • 评测集:至少 30 条带标准答案的样本,用来做每一次改动的回归对照。没有评测集,后续所有“优化”都只是自我感动。
  • 对抗数据:专门挑会让人工智能“犯晕”的输入,比如带诱导、带错误前提、带格式要求变化的样本。这类数据最能反映工程护栏是否有效。

实操时别急着写爬虫,也别急着买数据。先把业务场景里最常见的 2 到 3 类问题手工整理成几十条样本,足够启动第一版了。之后再做规则清洗、去重、格式校验。

数据清洗有一个特别容易踩的坑:不要过度清洗。比如你为了格式统一,把所有换行符都去掉,结果模型输出也变得挤成一团;又把标点标准化,结果业务方使用的特殊符号全被改了。清洗的目标是去掉“与任务无关的噪声”,而不是改变自然语言的表达多样性。我的经验是,每一道清洗规则都要单独记录,并且在评测集上跑一次对比,确认规则有效再加进去。

2.2 模型侧:基座、微调还是纯提示词

这一节是新手最纠结的地方。我的建议是:先默认用纯提示词方案,也就是“Prompt Engineering + 少量示例”把基座模型调到可用的状态。只有当纯提示词确实兜不住的时候,再考虑微调。

为什么?因为微调的成本不只是训练费,还有后续的运营成本。微调后的模型需要持续更新、持续评测、持续处理数据漂移,这已经是一个二等团队的工作量。在业务效果还没被验证之前,这种投入是危险的。

我比较推荐的做法是“三层递进”:

  1. 用提示词加少量示例,把任务跑通。这一步能验证业务逻辑是否成立。
  2. 如果输出格式不稳定,加一层结构化解析和重试机制,而不是立刻微调。
  3. 当提示词方案需要频繁增加“屎山”规则时,再考虑收集失败样本做微调。

基座模型的选择也有讲究。在预算允许的情况下,能够私有化部署的开源模型优先,原因不是数据安全口号,而是你能拿到完整日志和可控的采样参数。闭源 API 固然省事,但在排查问题时容易被黑盒限制住。

2.3 推理侧:服务化与资源估算

把模型接到业务前,先做一道简单的显存计算题。

以 7B 参数模型为例,使用 FP16 精度推理,模型权重占 14GB 左右。加上 KV Cache 和激活值,实际部署在单张 24GB 显卡上比较稳妥。如果你用 INT8 量化,权重降到约 7GB,一张 16GB 显卡也能跑。但这只是“能跑”,不是“能并发”。并发越高,KV Cache 越大,显存占用会线性上升。

我实际用的估算公式很简单:

显存 ≈ 参数(GB) * 精度字节数 + KV Cache + 2GB余量

比如 7B 模型,FP16:

7 * 2 = 14GB 权重 假设 4 并发,序列长度 2048,KV Cache 约 3GB 再加 2GB 余量,总共约 19GB

这个数字说明,单张 24GB 显卡带 4 个并发是极限。别被“模型 7B 很小”这句话骗了,工程上显存瓶颈常常在 KV Cache 和并发。

服务化推荐用成熟框架,不要自己写请求透传。我常用的是 FastAPI 加推理后端,外挂请求队列。这里有个细节:模型推理是长耗时操作,一定要把 HTTP 超时时间调到 30 秒以上,否则网关会给你大量超时错误。

2.4 Agent 与 Harness 工程:给模型套上行为约束

热词里“harness engineering”和“Agent”放在一起,其实就是国外常说的“智能体缰绳”。模型本身没有稳定的行为边界,你需要通过工程手段给它套上约束,让它在可控范围内干活。

我的理解中,Harness 工程包含三层内容:

  • 工具层:给 Agent 定义可调用的外部工具,规定输入输出 JSON Schema,避免它自由发挥。
  • 控制层:限制模型的行为选项,包括最大步数、允许调用的工具列表、终止条件。
  • 观测层:完整记录模型每一步思考、每个工具调用参数和结果,方便回溯。

实操中最容易失控的 Agent 场景是“循环调用”。模型发现某个工具返回了意料之外的结果,就会反复调用同一个工具试图修正,直到步数耗尽。没有 Harness 控制,这种问题几乎必然出现。

我的做法是给 Agent 增加三个硬性限制:

  1. 每轮任务最多执行 5 步工具调用,超过立即终止。
  2. 每次工具调用前强制输出一段“当前目标”说明,方便观测它是不是偏了。
  3. 工具结果必须经过一层解析和校验,非法输出直接标记失败,不让模型自由解释。

这套东西听起来不复杂,但真做起来,你会发现它才是 AI 工程里最能提升稳定性的部分。

3. 手把手落地:一个最小可用的 AI 工程样例

下面用一个“文档问答 Agent”作为例子,带你把整个链路跑通。这个 Agent 需要做到:用户问一句,它会检索本地文档,调用大模型生成答案,并附带引用来源。

3.1 搭建工程骨架和依赖

我习惯用 Python 加 FastAPI,整个工程只有几个核心目录:

app/ main.py # 服务入口 agent/ core.py # 调度逻辑 tools.py # 工具定义 data/ raw/ # 原始文档 processed/ # 清洗后的文档 eval_set.json # 评测集 services/ llm.py # 模型调用封装 retriever.py # 检索模块 tests/ test_eval.py # 自动评测脚本

依赖尽量精简:

  • fastapi、uvicorn:服务封装
  • openai / transformers:模型调用
  • sentence-transformers:向量化
  • faiss-cpu / 向量库:检索
  • pytest:评测

不用上来就把 RAG 全家桶、监控平台全砌上,先跑通一个最小闭环。等确认效果后再逐步加组件,这是最省时间的路径。

3.2 实现数据清洗与评测集

首先把原始文档切成段落,过滤掉表格和图片占位符,保留标题层级作为上下文。这一步很关键,因为检索的颗粒度直接决定了回答质量。切得太细,检索结果零散;切得太粗,上下文窗口容易被无关内容填满。

我用的默认参数是:按标题和空行分段,每段 200 到 600 字,段与段之间保留 10% 的文本重叠。为什么留重叠?因为切分点通常不在语义边界上,重叠可以避免关键句被截成两半。

接着构造评测集。每个样本包含三个字段:

{ "id": "case_001", "question": "文档中提到的重试机制默认次数是多少?", "reference": "默认次数是3。", "source_doc": "docs/ops_guide.md" }

评测集不需要大,30 条足够启动。关键是要覆盖三类问题:能直接回答的、需要跨段落检索的、提问方式带有歧义的。歧义样本最考验 Agent 的真实水平,一定要放进去。

3.3 封装推理服务

模型层我不建议在业务代码里直接写模型逻辑,而是封装成一个独立的 LLM 模块,接口保持简单:

async def generate( messages: list[dict], temperature: float = 0.2, max_tokens: int = 512 ) -> str: ...

这里有两个参数需要解释。第一个是 temperature,问答类任务我固定设 0.2,避免模型自由发挥;创意类任务可以调到 0.7 以上,但这个项目不需要。第二个是 max_tokens,设 512 是为了防止单次回答过长拖慢响应,也方便外部在超时控制上统一处理。

服务入口我写成这样:

@app.post("/chat") async def chat(request: ChatRequest): context = retriever.search(request.question, top_k=3) messages = build_prompt(request.question, context) answer = await generate(messages) return {"answer": answer, "sources": [x.id for x in context]}

为了让检索结果可追溯,我返回了命中的 source id。上线后你会非常依赖这个字段,因为用户遇到回答错误时,能快速判断是“检索错了”还是“生成错了”。

3.4 让 Agent 跑起来并接入自动评估

把 Agent 调度逻辑跑通后,立刻接上评估脚本。这一步不能等,越早越好。

def run_eval(): results = [] for case in eval_set: response = agent.run(case["question"]) score = evaluate(response, case["reference"]) results.append({"id": case["id"], "score": score}) return sum(r["score"] for r in results) / len(results)

评估指标不要一上来就上 BLEU、ROUGE,先用一个“硬指标加软判断”的组合:

  • 硬指标:回答是否包含参考答案中的关键实体词。
  • 软判断:让一个固定 prompt 的模型给答案打 1 到 5 分。

硬指标能保证基本事实不出错,软判断能捕捉语义差异。两者结合,至少能挡住 80% 的回归问题。

第一次跑完评测后,你会看到一个非常真实的场面:模型输出的答案格式五花八门,有些完全符合预期,有些则完全跑偏。这正是自动评测存在的意义。接下去要做的不是去改模型输出,而是根据失败样本,回到提示词和检索逻辑里找原因。

4. 常见问题与排查技巧实录

最后一部分,我把实操中反复踩过的坑整理成速查表,每个问题都附上我的排查思路。

4.1 模型输出“看起来对,实则错”怎么抓

这是 AI 工程最头疼的问题:语法通顺、语气正常,但事实是错的。很多人第一反应是“加提示词”,但我建议先做归因。

排查顺序是:先确认检索到的上下文是否正确,再确认模型是否在基于上下文回答。如果检索结果本身不包含参考答案,那生成错是大概率事件,该改的是检索。如果检索结果包含答案但模型没引用,那问题出在提示词约束,或模型本身的幻觉倾向。

我处理幻觉还有一个土办法:在提示词里强制要求“回答必须以参考文档为依据,没有依据时明确说不知道”。这话不复杂,但实测能降低不少幻觉率。同时把“没有依据”这种拒绝类回答也加入评测集,防止模型为了讨好用户而硬编。

4.2 Agent 跑飞、死循环和工具调用失败

Agent 跑飞是常态,尤其是工具较多时。我见过最离谱一个循环:模型连续调用 12 次搜索工具,每次都换关键词,却始终不读结果,最后把上下文塞满后报错。

解决这类问题不靠提示词,靠工程强制。Harness 里的“最大步数”必须是最优先级,任何一个 Agent 框架都要有这个参数。同时记录每一步的完整调用链,方便复盘。

工具调用失败也非常常见,比如工具返回 JSON 多了一个逗号,模型解析不出来。我会在校验层做容错:先尝试严格的 JSON 解析,失败后尝试修复常见错误,例如去掉末尾逗号、补齐引号。不要让模型在线解析,那是把不确定性放大到整条链路上。

4.3 显存与并发资源不够时的降级方案

资源不足时,我按顺序做三件事:量化、批处理、裁剪上下文。

量化优先用 INT8,几乎不影响小模型效果,显存直接减半。批处理可以把多个请求拼成一个 batch 推理,显著提高吞吐,但要注意单请求的 max_tokens 不要相差太大,否则低吞吐请求会阻塞长输出。裁剪上下文则是把与当前问题无关的检索片段强行去掉,减少 KV Cache 消耗。

如果服务端压力还是大,就在网关层加“排队机制”,而不是粗暴返回 429。排队虽然延迟变高,但至少能让用户体验连贯,而且可以避免雪崩。

4.4 评测指标与线上反馈不一致的问题

自动评测显示 95 分,上线后被业务方吐槽得一塌糊涂,这种情况我把原因总结为三类:

第一,评测集太干净。真实用户的输入带错别字、带口语、带超长上下文,你的评测集里没有,评分自然失真。所以评测集必须加入真实流量里的失败案例,持续迭代。

第二,指标偏向“字面匹配”,却忽略了意图。修改评估 prompt,明确告诉模型“只要事实正确、逻辑通顺,即使描述方式不同也可以给满分”。

第三,线上用户的问题分布和评测集不一致。评测集往往是垂直领域的,但上线后混入了跨领域问题。解决方法是把评测集按来源和质量分层,每次改动后分别计算各层得分,而不是只看平均分。

这些坑我几乎每个都在真实项目里遇到过。踩平它们之后,你才能建立起对“自动评测”的信任,也才敢让模型应用持续迭代。

我个人在实际操作中的一个体会是:AI 工程不是把模型接到业务里就结束,而是要在模型的不确定性外面,搭建一层确定性的壳。从零走过的意义,就是亲手摸清这层壳的每一个接缝。最后再分享一个小技巧:保持一个失败样本库,所有线上 Backend 日志里的 Bad Case 都往里丢,隔一段时间就去重归因一次。这个库越厚,你的系统越稳,也越能在下一次迭代里快速找到方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询