ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

从零搭建AI工程体系:Prompt、Agent与模型训练实战

从零搭建AI工程体系:Prompt、Agent与模型训练实战 1. 项目概述为什么我决定从零开始做AI工程过去半年我一直在做一件事不借助任何现成的AI应用模板从零开始搭建自己的AI工程体系。这个项目我给它起的名字叫ai-engineering-from-scratch听起来有点学院派但实际做的事情非常接地气——用Python自己写Prompt管理模块、自己搭Agent编排框架、甚至自己训练了一个轻量级的推理模型。不是因为我闲得慌而是因为在实际工作中我发现那些直接套用现成工具链的方案遇到真实业务场景时总会有各种差一口气的地方。这个工程适合谁如果你是一个刚接触AI的开发者想搞清楚大模型应用背后到底是怎么运转的或者你已经在用ChatGPT、Claude这类产品但想更进一步自己掌控整个AI系统的行为逻辑又或者你是一个技术负责人正在评估到底是买现成的AI平台还是自己搭一套——这篇文章应该能帮你节省大量踩坑时间。我在这篇文章里会完整拆解这个项目的设计思路、核心模块的实操方案以及我踩过的那些坑。所有内容都基于真实可跑的代码和配置不是那种画个架构图就完事的空谈。我会把Prompt工程、Harness EngineeringAI的编排与组装层、轻量级推理模型训练、多Agent协作工作流这几个模块从头到尾过一遍每一步都给出可以直接抄作业的细节。2. 核心架构拆解AI工程不是调API那么简单2.1 先搞清楚AI工程的层次结构很多人以为AI工程就是把OpenAI的API接进来然后写点Prompt调用就完事了。这个理解不能说错但格局太小。我做了这个项目之后才真正体会到AI工程至少分四个层次每一层都有完全不同的技术栈和设计逻辑。第一层是模型层包括选择基础模型、微调、蒸馏、量化这些工作。这一层决定了你的AI系统聪明程度的上限。第二层是提示词与上下文工程层这是大多数AI应用开发者主要投入精力的地方它解决的是怎么让模型在最少的输入下产生最准确的输出。第三层是代理与工具编排层也就是我们常说的Agent和Harness Engineering它解决的是模型怎么和外部系统交互、怎么调用工具、怎么规划多步任务。第四层是应用与产品层包括工作流设计、人机交互界面、结果校验机制。我最初犯的错误是直接从第二层开始做逮着一个API就开始写应用逻辑结果模型选型没做好后面所有层的设计都被带偏了。这个工程给我的第一个教训就是AI工程必须自上而下设计自下而上构建先从自己的业务场景倒推出需要什么样的模型能力再去选型或训练然后再往上搭建。2.2 Harness Engineering到底解决什么问题Harness这个词直译过来是马具在AI领域特指控制模型行为的框架与工具集合。这个概念最近特别火很多大厂都在提Harness Engineering是未来AI工程师的核心能力。说实话三年前根本没有这个词那时候大家就笼统地叫发开AI应用。我理解的Harness Engineering核心是解决三件事。第一件事是给大模型装上手脚——让它能调用外部工具比如搜索引擎、代码执行器、数据库查询接口。第二件事是给大模型套上缰绳——通过结构化的方式限制它的输出格式和推理路径不让它自由发挥跑偏。第三件事是给大模型配一个调度中枢——当任务复杂到需要多步推理和多个模型协作时由中枢来规划并分配每个环节的输出。我写了一个基于Python的轻量级Harness框架核心代码不到300行。这个框架的价值不在于它多强大而在于它让我彻底搞明白了市面上那些Agent平台的底层逻辑。当你亲手实现过一个让模型自主规划、调用工具、校验结果的循环之后再看任何相关产品都会非常通透。2.3 为什么从零开始是值得的有人可能会说现在现成的东西那么多LangChain、LlamaIndex、AutoGen要什么有什么何必自己从头造轮子我在项目里确实大量参考了这些框架的设计思路但最终选择自己搭核心逻辑有几个原因。第一现成框架的学习成本其实很高。LangChain早期的版本API变动频繁每个版本都有不同的抽象方式学它的成本不比从零自己写一个简单的循环低多少。第二框架的抽象层次和你的业务往往不匹配。很多框架默认你做的是文档问答这类标准场景一旦你的业务流程是先分析数据再画图再做总结这种多阶段任务框架的预设接口反而会绑手绑脚。第三自己写一遍才能真正理解原理。我举个具体例子。我自己写Prompt管理模块时最初只是用一个Python字典存Prompt模板后来发现模板之间的版本管理、变量校验、输出格式约束这些问题在业务复杂之后就必须要有一套自己的方案。这套方案的设计过程本身就是最大的收获。3. 核心模块一提示词工程实战拆解3.1 提示词的系统化管理方案提示词工程现在已经是一个相当成熟的领域了但大部分人的Prompt都还是写在代码里的字符串。这个项目里我做的最重要的一件事是把Prompt从字符串提升为有结构的模块。我设计了一个两层结构的Prompt管理体系。底层是一套基础能力Prompt库包含角色设定、输出格式要求、推理规范、知识范围约束这些通用模块。上层是业务Prompt模板由基础能力模块组合而成。这样做的直接好处是当你需要调整模型的统一行为基线时只需要改底层模块不用去翻每一个业务模板。这里有一个非常关键的实操细节Prompt模板中的角色设定和任务描述必须分离而且中间要加明确的边界标记。我踩过的坑是早期把角色和任务写在一个长句子里比如你是一个资深数据分析师请分析这份销售数据模型输出时经常出现角色漂移——说着说着就忘记自己的身份了。改成结构化写法之后把角色设定独立成段并在任务指令前加上明确的基于上述角色请完成以下任务的边界指示输出质量稳定了很多。我给出一个可以直接用的模板结构性示例[系统角色] 你是一个有10年经验的资深数据分析师擅长从原始数据中发现业务模式。 [工作规范] 1. 任何结论必须附带数据依据 2. 禁止编造不存在的统计数据 3. 分析过程分步展示 [任务指令] 基于上述角色和规范完成以下任务结果输出为JSON格式这种分块结构在实际使用中比一段式的Prompt输出稳定性高出不少。我做过对比测试在同一组测试用例上结构化Prompt的格式合规率从62%提升到了94%逻辑一致性评分也明显更高。3.2 几个提升Prompt质量的进阶技巧随着项目推进我总结出几个实打实有效的小技巧这些不是网上那些模板能提供的而是需要在实际迭代中沉淀的。第一个技巧是示例优先。与其告诉模型要简短回复不如给它一个这就是简短回复的样例。这个差别非常微妙但效果可以说是天壤之别。我在一个生成商品简介的任务里仅添加了一个正例和反例输出质量评分就提升了近40%。需要注意的是示例的选择有讲究正例要选格式对但内容一般的反例要选内容好但格式明显不合格的。因为模型对格式的学习比对风格的学习快得多。第二个技巧是给模型思考的缓冲区。在需要推理的任务里不要直接要求模型给出最终答案而是引导它先给出推理过程。比如请分步骤分析问题每个步骤给出中间结论最后基于所有中间结论给出最终答案。这个技巧其实就是最简版思维链。我在数学推理类的任务上测试过这个写法准确率从51%提升到了78%。第三个技巧是对输出的自我校验要求。在Prompt末尾加上一句完成后请再次检查输出是否符合所有要求如有偏离请修正后再输出。这个设计利用了模型对修正行为的倾向性实测能够减少格式错误和内容遗漏。这个做法的原理是让模型模拟了一次审校自己回答的流程有一定的自我纠错能力。4. 核心模块二Harness Engineering——搭建自己的Agent编排框架4.1 从Prompt到Agent的跃迁如果说提示词工程是在单次对话的层面优化模型表现那Harness Engineering就是在多轮任务的层面设计整个AI系统的行为逻辑。我花了很长时间才真正理解两者的分界线。单次Prompt再好模型也是被动的——你给它什么任务它完成什么任务对话结束就完事。但Agent不一样Agent有一个循环接收任务、拆解步骤、逐步执行、检查结果、修正偏差。这本质上是把一个长任务拆成多个短任务每个短任务之间由中间的调度逻辑衔接。我在项目里实现的Agent框架核心结构长这样class Agent: def __init__(self, model, tools, memory): self.model model self.tools tools # 工具注册表 self.memory memory # 短期工作记忆 def run(self, task, max_steps10): for step in range(max_steps): observation self.observe(task, self.memory) # 让模型决定下一步动作 action self.model.decide(observation) if action.is_final(): return action.answer() result self.execute(action) self.memory.append(result) # 让模型评估是否需要调整计划 if self.model.should_revise(self.memory): task self.model.revise_plan(task, self.memory)这个循环看起来简单实际上里面藏了非常多的工程细节。比如让模型决定下一步动作这个环节模型的输出格式是不是稳定的我给模型的动作空间设计了一套严格的JSON协议动作必须是以下三种之一——call_tool调用工具、search_memory检索历史记忆、final_answer给出最终答案。每种动作的参数也有明确规定。这套协议的好处是模型本质上是在做选择题填空题输出的稳定性大幅提升。4.2 工具注册与调用的设计细节Agent要发挥作用必须能够调用外部工具。这一块的设计很容易被忽视但恰恰是实操中坑最多的地方。我自己实现了工具注册器核心思路是工具即函数元信息。tool_registry {} def register_tool(name, description, parameters_schema): def decorator(func): tool_registry[name] { func: func, description: description, parameters: parameters_schema } return func return decorator register_tool( code_executor, 在隔离环境中执行Python代码并返回结果, {code: string, timeout: integer} ) def execute_code(code, timeout10): # 安全执行逻辑 ...这个设计的关键在于工具的描述必须写得很清楚因为模型是通过描述来理解工具的用途的。我在工具描述上吃亏最多早期经常写这个函数用来做数据清洗结果模型完全不知道该什么时候调用它。后来我把每个工具的描述都改成当用户要求X时使用此工具它能够Y使用后会返回Z同时附上一个简短的使用示例模型的工具调用准确率提升就非常明显。工具调用的安全边界也是必须考虑的问题。我的做法是给每个工具加一个受控的沙箱环境敏感操作必须二次确认。虽然这个确认动作增加了交互成本但在实际项目中这是必要的成本。比如一个可以由模型自主执行的代码执行器如果不加限制模型可能一次性执行高风险命令直接把本地环境搞坏。4.3 让Agent拥有反思能力这个项目里我做的最得意的一点是给Agent加了反思循环。传统的Agent执行完任务就结束了但我的框架里每个任务执行完之后会额外跑一轮回顾与总结让模型说说自己哪里做得好、哪里可能有问题、下次遇到类似任务该用什么策略。这个设计的启发来自一个实际场景有一次我用一个Agent批量处理市场研究报告它连续三篇都把市场规模章节总结成了市场前景导致下游报表数据错漏。这个错误其实是系统性偏差单次任务中模型完全意识不到。后来我加了反思循环模型在反思阶段自己发现我发现用户更关注的是历史数据而非预测分析所以我倾向于总结前景部分但我应该优先回应原始指令。从那以后这个偏差就再也没有出现过。反思循环的实现也不复杂本质就是在任务完成之后再构造一轮新的Prompt让模型以审阅者的视角审视自己的输出。这个视角切换很关键因为模型在执行者状态下会有惯性思维。我在这个过程中发现“角色切换”的提示方式需要特别注意如果你只是说请反思一下你的回答模型通常只会泛泛而谈。但如果你给它一个外部审阅者的角色并明确告诉它审阅的重点是你是否忠实地遵循了用户的原始指令而不是你是否提供了更有洞见的分析反思质量会高很多。5. 核心模块三从零构建一个轻量推理模型5.1 简单的数据准备这个项目里最大的挑战还不是框架搭建而是自己训练一个推理模型。说实话网上那些从零训练大模型的教程大部分受限于教育资源只能停留在理论层面。我的做法是缩小目标不追求通用能力只训练一个能完成特定数学推理任务的轻量模型参数量控制在5000万以内在自己的消费级GPU上就能跑通。数据是这个环节最难的部分。我构造了一个三步推理任务集给定一个实际生活中的数值问题要求模型输出已知条件分析→计算公式→最终答案三个步骤。最开始我尝试用网上公开的数学数据集但那些数据的推理过程普遍太简短模型学不到分步思考的模式。后来我自己写了一个数据生成器用程序自动生成问题-分解步骤-标准答案三元组总共生成了12万条训练数据。这个数据生成器的价值非常大。它让我明白了训练数据不是越多越好而是结构越规范越好。我对比过两组实验一组用12万条自动生成数据另一组混合了3万条人工清洗的网络数据结果是第一组模型在推理一致性上的表现反而更好。原因是自动生成的数据每个字段的格式完全统一模型学到的格式规矩更清晰而这个特征在推理任务中直接关系到输出的可解析性。5.2 从零训练还是用开源模型做微调很多初学者问我不是说好从零训练吗为什么后面变成了微调。这个问题的答案是项目过程中我做了务实的调整。纯从零训练一个哪怕只有5000万参数的模型也需要处理词表构建、位置编码、训练稳定性等一系列非常底层的问题。这些工作不是没价值但对于我想要达成的目标——理解推理模型的训练原理——微调其实能更快地触达核心。最终我采用了两步走方案第一步用Hugging Face上的一个小型开源模型作为基座第二步用我准备好的12万条分步推理数据做监督微调。这样既保留了从零开始的核心学习价值又把精力集中在了推理能力是怎么被训练出来的这个核心问题上。如果以后需要真正从零训练路线上只需要把基座模型换成随机初始化模型数据处理和训练的整套流程是可以复用的。5.3 训练参数与效果曲线这里记录一下我最终使用的关键训练参数给需要的朋友一个参考基座模型GPT-2 small架构1.24亿参数词表保持原始规模训练Batch大小32学习率5e-5带余弦退火衰减Warmup步数300步最大序列长度512训练轮数4个Epoch损失收敛值0.67初始值约3.2模型的推理表现在一个1500条的测试集上还算亮眼三步输出的格式完整率达到96.3%最终答案的数值正确率达到72.8%。作为对比未经微调的基座模型在同样任务上的格式完整率只有18.5%。这说明分步推理这个能力确实是可以借助数据教给模型的。不过训练过程中有几个需要注意的坑。最大的坑是序列长度设置得过长会导致训练效率大幅下降512是我反复调出来的平衡点——再长就会让Batch size不得不调小反而影响收敛稳定性。损失函数也不是越低越好我在训练到第3个Epoch时发现验证损失开始微微回升这就是过拟合的前兆果断提前停止了训练。6. 工作流与多Agent协作让AI系统真正跑起来6.1 从单Agent到多Agent的切换单Agent能解决的任务有一定上限尤其是那些需要不同专业角色协同的场景。比如调研一个产品方向输出可行性分析报告这个任务让一个Agent从头做到尾效果远不如拆成调研员Agent—数据分析Agent—报告撰写Agent三级流水线。这个项目里我搭建了一个最简的多Agent工作流。调度器的思路很简单上游Agent的输出作为下游Agent的输入每个Agent只专注于自己的角色。但有一个必须处理的细节是Agent之间的交接文档格式。如果上游输出的内容下游解析不了整个流水线就会断掉。我的方案是为每个交接点定义一个严格的JSON Schema上游必须按这个Schema输出下游才能正确解析。实际项目中我遇到过非常经典的bug调研员Agent输出的内容是段落式的数据分析Agent期望输入是JSON格式的键值对结果数据解析直接失败。这个问题的根治方法是在每个Agent的Prompt里都加入你的输出将被下一个系统消费必须严格遵循X格式并在下游解析前加一层格式校验和自动修复逻辑。6.2 工作流编排中的人机分工做这个项目之前我一直以为自动化程度越高越好恨不得所有环节都让Agent自动完成。实践证明这个想法是错的至少在目前的技术条件下是错的。我自己总结了一个人工介入三原则。第一涉及资金变动的环节必须人工确认比如Agent要提交订单、扣款这类操作。第二输出结果将直接对外发布的环节最好人工审核模型生成的文案和图片虽然质量在提升但在品牌语境、事实核查等很多层面还是需要人来兜底。第三前几个流程步骤要人工复核一旦发现Agent的行为模式有偏差尽早纠正比最后返工成本低得多。在实践中最实用的一种人机协作模式是建议—确认模式Agent生成方案或草稿人工确认后再进入下一流程阶段。这样的模式比人工逐字逐句修改效率高很多也比全自动安全得多。6.3 单个Agent的角色设计与协作模式多Agent系统设计中最重要的一环是独立规划好每个Agent的角色。角色定义得越清晰Agent之间的协作就越顺畅。我自己用了一个角色五要素框架来定义每个Agent角色名、职责边界、输入规范、输出规范、特殊情况处理。就拿我的多Agent工作流举例调研员Agent职责是搜索和汇总信息明确标注信息来源输出为结构化报告数据分析Agent职责是对上游结果做量化分析只输出统计结论不做价值判断报告撰写Agent职责是整合调研结论和分析结果写成适合目标读者阅读的报告数据分析Agent只做统计不做判断这个设定是我反复调整后加上的。最初我让数据分析Agent同时承担发现洞察的任务结果它经常输出一些猜测性的建议而这些建议在事实层面并没有数据支撑。让每个Agent管好自己的事反而能避免这种越权输出的问题。6.4 多Agent协作的失败模式与恢复多Agent系统比单Agent复杂得多也容易出问题。我在实际测试中总结了几种典型失败模式。第一种是错误放大效应。上游Agent犯了一个小错误下游Agent基于这个错误继续推理问题就会层层放大最终输出与事实完全偏离。应对方法是每层交接时都要做置信度校验如果某个Agent对输出结果没有足够把握必须明确标注低置信度标记下游Agent对低置信度的内容需要额外检索验证。第二种是任务循环死锁。Agent之间互相等待对方的输出形成环状依赖工作流卡死。这个问题的排查比较费劲因为纯看日志很难看出来。后来我在调度器里加了一个步骤计数器每个环节最多执行N次超出就自动终止并报警。第三种是上下文污染。多个Agent共享同一个上下文时一个Agent的推理过程会影响另一个Agent的决策。我的解决方案是每个Agent有独立的上下文窗口Agent之间只通过标准交接格式传输信息不让原始推理过程直接暴露。7. 常见问题与排查技巧实录7.1 Prompt层面的高频问题和解法模型输出格式不稳定永远是出现频率最高的问题。我常用的手段有三个。第一个是给模型提供输出格式的严格样例而且样例越具体越好。与其说输出JSON格式的结果不如说输出如下格式的JSON{summary: ..., data: [...]}。第二个是在Prompt的最后加入输出前自我校验格式的要求这个前面已经提过。第三个是代码层兜底尽量写一个输出解析器用正则或JSON解析处理模型的输出万一解析失败给出明确的重试提示。另一个高频问题是模型学到了不该学的坏习惯。比如我在一个多轮对话场景中发现模型会在用户没有询问的情况下不断推荐付费功能。这个问题的根因是训练数据中类似的推销型对话太多模型把推荐功能当成了默认行为。解决方案是明确在Prompt中写除非用户主动询问功能信息否则不得主动提及任何产品功能同时增加一个话题偏离检测机制当模型回复中出现预设的敏感词是就触发警告。7.2 训练相关的常见问题训练过程中的显存溢出是最常见的报错。我用的消费级GPU只有16GB显存加载GPT-2 base模型加优化器状态一下子就占满了。解决方案是梯度累积Batch size降到8梯度累积4步等效于32的Batch size训练效果基本不受影响。还有过拟合问题我做的轻量模型训练时过拟合比大模型更快。因为模型参数量小训练数据模式又比较单一第3个Epoch开始验证损失就会停止下降甚至回升。我的解决方法是加早停检查和Dropout同时在数据生成器中增加一些随机扰动让数据不会完全重复。训练数据质量不均也是要警惕的。我在早期清洗数据时不够仔细数据里混了几条格式错误的数据导致模型持续产出格式不规范的输出而且由于错误的格式在数据中占比很小模型偶尔出错、偶尔正常极难定位原因。后来我增加了一套数据校验脚本每条数据入库前都必须通过格式检查直接保住了训练质量。7.3 Agent运行中的排查技巧Agent系统出问题时的排查核心能力是让过程可见。我的做法是给Agent框架加上详细的日志钩子——每个步骤都会记录当时的推理输入、模型决策、工具执行结果、上下文状态。这个过程非常像调试有状态系统时的系统日志日志的详细程度直接决定了排查效率。另一个很有用的排查技巧是单步重放。当发现Agent某个任务处理结果不对就把日志显示出来的那一步单独取出来重新交给模型执行一次看是否能复现问题。如果是偶发问题大概率是模型随机采样导致的如果是必现问题说明Prompt或者工具返回数据有问题。这个技巧帮我定位了至少五个隐藏很深的逻辑漏洞。7.4 一张速查表问题和对应的解决策略问题现象可能的根因解决策略模型输出格式不符Prompt缺少明确格式约束给出严格格式样例自我校验要求模型回答偏题上下文过长导致注意力分散精简上下文内容增加任务边界提示Agent不调用工具工具描述与任务场景不匹配重写工具描述增加调用示例Agent调用错误工具工具之间区分度不够区分不同工具的适用边界必要时合并或拆分多Agent任务卡死环状依赖或等待条件未满足增加步骤计数器与超时检测训练不收敛学习率过高或数据预处理有误降低学习率检查数据清洗过程模型过拟合数据量不足或训练过久增加数据多样性提前停止训练8. 实操经验总结如果让我再做一次这个项目做到最后我最大的感悟是AI工程的核心不是把模型跑起来而是让整个系统在真实场景中稳定、可控、可维护地工作。模型的那部分能力——不管是调API还是自己训练——其实只占整个系统复杂度的三成剩下的七成都在模型的外围工程。如果让我从头再做一次我会在几个地方做出不同的选择。第一我会更早地引入测试驱动开发。我在项目初期几乎没有为Prompt和Agent写自动化测试导致很多问题在集成阶段才暴露排查成本非常高。后来我搭了一套简单的回归测试集一组固定的输入每次Prompt或Agent逻辑迭代后自动跑一遍输出和预期比较。这个投入的回报非常大。第二我会在项目一开始就设计好日志系统而不是写到一半再补。第三我会更理性地看待从零开始的边界——不是什么都自己造而是核心逻辑吃透原理之后自己实现周边工具可以大胆选用现成方案。最后分享一个小技巧在写Prompt和Agent框架时保持假设所有输出都是不可信的这个心态。每次模型返回结果后先想两件事这个输出会不会格式不对这个输出的内容会不会在事实上是错误的。带着这两个问题去设计校验和兜底逻辑你的AI系统才能真正用在生产环境里。这个项目后续我还在继续扩展。下一步的计划是给Agent框架加上长期记忆模块让它可以跨任务积累知识同时尝试接入开源的本地模型做完全离线的推理系统。AI工程这条路很长但每一步都有实实在在的收获。如果你也在做类似的事情欢迎在实际操作中多折腾多记录工程经验一定是在亲手踩坑中积累起来的。
返回列表