ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零构建AI工程:提示词、工具调用与本地部署实战

从零构建AI工程:提示词、工具调用与本地部署实战 1. 项目缘起为什么我要从零再造一个AI工程我给自己定的这个项目代号是ai-engineering-from-scratch字面意思就是“从零开始搞AI工程”。身边不少人问我现在现成的AI框架、低代码平台和开源项目一抓一大把为什么要费劲从空白目录写起我当时的想法很简单用了半年别人的AI应用调过不少API也玩过各种Agent框架但总觉得有个地方不对劲——出了问题我不知道该从哪一层去排查。这不是一个虚荣的“造轮子”项目而是一个很实际的基本功补课计划。我需要从模型接口怎么调用、提示词怎么组织、工具调用协议怎么设计、上下文怎么管理到整个应用怎么打包部署亲手走一遍。只有把这条路走过一遍后面拿现成框架的时候才知道它替你做了什么、没做什么踩坑的时候也才找得到方向。这个项目最适合两类人。一类是刚入门AI应用开发想弄明白大模型应用的真实工程链路的新手另一类是已经在用现成AI框架但总觉得“受制于人”、想真正掌握底层逻辑的开发者。文章里提到的所有步骤、决策和坑都是我实际跑出来的不是从文档里抄的。2. 整体架构与思路拆解2.1 先想清楚AI工程到底在工程什么很多人对AI工程的第一反应是“训练模型”“调参”但实际做应用开发时大部分时间和精力花在了模型外围的工程问题上。一个大模型应用真正的核心链路可以拆成四段输入处理、模型交互、输出校验、工具执行。输入处理是把用户说的话整理成模型能理解的提示词结构模型交互涉及选接口、传参数、处理流式返回输出校验是防止模型生成乱七八糟的格式工具执行则是让模型能调用外部功能比如查数据库、调计算器、发请求。我最初犯的错误是恨不得一步到位直接搭一个复杂的Agent系统。后来被现实教育了基础模块没打牢Agent的行为就完全不可控。所以我把项目从最简模型接口调用开始一层一层向上加。整个过程就像盖房子先打地基再砌墙最后才装修。那些看起来酷炫的“自动规划”“多智能体协作”本质上都是建立在稳定可靠的模型交互和工具调用协议之上的。2.2 核心选型托管API和本地部署怎么挑在模型接入方式上我做了个比较长期的对比。托管API方案比如常见的OpenAI兼容接口、国内各大云厂商的大模型接口优势是接入快、不需要自己准备显卡、模型更新也及时按量付费前期成本低。缺点是数据要传到第三方服务部分业务场景会有隐私顾虑而且网络延迟和限流策略不可控。本地部署方案比如用Ollama或llama.cpp跑开源模型优势是数据完全在自己手里离线也能跑一次投入后调用没有额外费用而且可以针对场景微调。缺点是硬件成本高需要一个容量足够的GPU设备模型效果也不一定追得上商业大模型。对比维度托管API本地部署接入速度快十几分钟搞定较慢要下载模型、配置环境硬件要求无额外要求GPU显存8GB起步越大越好数据隐私数据出域需评估合规性完全本地隐私可控单次成本按token计费只有电费和硬件折旧模型效果通常更强取决于显存和量化等级运维复杂度低需要处理环境依赖、版本管理我的最终选择是两边都接但在架构上做抽象。写一个统一的模型接口层上层业务不关心背后是API还是本地模型换模型只是改配置。这种设计在后期帮我省了非常多事模型一升级我只用改一行配置就能切换验证。3. 核心细节解析工程中最容易翻车的三个关节3.1 提示词工程不是“写段话”那么简单Prompt Engineering是我在这个项目里花时间最多的地方。网上很多人把提示词说得神乎其神好像一句话就能让模型变聪明。实际上提示词更像是一份操作手册你要让模型清楚自己的角色、任务目标、工作步骤、输出格式以及遇到异常时怎么办。我第一次写提示词时就吃了大亏只写了“你是一个AI助手请回答用户问题”结果模型一会儿给我输出Markdown表格一会儿带一堆免责声明格式毫无稳定性。后来我固定了一套自己的提示词模板包含四个部分角色设定、任务描述、约束条件、输出格式定义。角色设定告诉模型站在什么角度思考问题任务描述把用户需求翻译成清晰的动作指令约束条件限制回答范围、字数、语气输出格式定义则要求模型按JSON或指定结构返回结果。这套模板不一定适合所有场景但它的价值在于可复用遇到新任务我只是替换任务描述和输出格式其他部分沿用即可。实测中还有一个反直觉的规律模型不是越听话越好它会“过度服从”指令。有时候我在约束条件里写了“不要解释你的推理过程”结果模型干脆连重要信息都不给我写了“只能使用以下工具”它遇到超出工具范围的需求就直接拒绝。所以提示词里的约束一定要精确不能有歧义而且要经过实测验证不能想当然。3.2 工具调用与AI Agent编排给模型装上手和脚在基础模型接口跑通之后我给项目加入了工具调用能力也就是让模型可以申请调用我预设的外部函数。这一步是整个项目复杂度上升的转折点也是AI Agent概念的落地核心。我之前用过的很多框架会隐式处理工具调用的细节但我要从零实现一遍必须理解背后的函数定义协议和解析逻辑。我把工具调用拆成了三个环节工具注册、意图映射、结果反馈。工具注册是在系统里维护一个工具清单每个工具包含名称、描述、输入参数结构意图映射是模型根据用户问题和工具描述决定该调用哪个工具、填什么参数结果反馈则是把工具执行后的结果重新塞回模型让模型根据结果组织最终回复。这三步形成一个循环就是Agent最基本的工作流。调试过程中遇到最多的问题是模型返回的工具参数和我的JSON Schema对不上或者参数名大小写不一致。后来我总结了一个稳定的解法在提示词里给每个工具附上一个示例参数并且在解析模型的输出时不要期望百分百严格的JSON先用正则把JSON片段提取出来再用对抗式校验解析失败就提示模型重新生成。这个方法让我的工具调用成功率从八成多提升到了接近满分。3.3 上下文管理窗口再大也有装满的一天模型的上下文窗口是有限的但真实对话是会一直增长的。刚开始我做了一个很蠢的设计把对话历史全部塞给模型。等对话进行到第20轮单次请求的token数量暴涨响应速度变得很慢而且模型开始“忘记”最开始的指令。后来我专门研究了上下文管理策略简单来说就是设定一个最大token阈值超过阈值后采用滑动窗口摘要压缩的方式处理。具体做法是新消息永远占最新份额旧消息中比较关键的几条保留原文更早的消息丢给模型做一次摘要用摘要代替原始长文本。摘要这一步看似浪费一次模型调用实际上能大幅降低后续请求的token消耗多轮对话下稳赚不赔。还有一种情况是业务需要“长期记忆”那就要引入向量数据库做检索增强这个我放在后面的扩展计划里但当前阶段用摘要已经能解决大部分问题。注意上下文管理的目标是保证模型在任意时刻接收到的信息都能被有效利用。宁可少给一些历史也不要让模型处理一堆语义重复的旧数据那只会让它分心。4. 实操过程从零到可运行的全流程记录4.1 环境准备与项目脚手架先交代一下我的开发环境一台Windows机器加上WSL2里的Ubuntu环境Python版本用3.11GPU方面有一块8GB显存的本地显卡。项目开始前我先确认了一件事——Git是否就绪。运行git --version看到版本号是2.23.0.windows.1略老但基础功能没问题。我顺手把它升级到了新版避免后续分支操作遇到老版本的不兼容问题。目录结构我一开始就想好避免后期重构。根目录下面建了app/放主逻辑tools/放工具函数config/放配置文件tests/放单元测试scripts/放启动和部署脚本。项目管理上我用了一个虚拟环境把依赖写在requirements.txt里。这一步不是走形式虚拟环境能帮你隔离项目依赖否则不同项目对同一个包的不同版本要求会把你折磨到崩溃。在模型接入之前我用一个假的MockModel跑通了整个调用链路。这个做法强烈推荐——把模型接口抽象成一个类注入一个返回固定文本的假模型就能在没有任何真实模型的情况下先把代码逻辑调试完。等真实模型接入后只需要专注于验证模型的能力表现而不是同时排查代码bug和提示词问题。4.2 最小可运行Demo一个带工具调用的问答系统我的第一版Demo只做了一件事用户问一个计算题模型调用一个计算器工具把结果返回给用户。这个看似简单的流程覆盖了前面说的所有核心环节提示词构造、模型调用、工具注册、解析模型回复、执行工具、把结果再喂回模型、输出最终回答。下面是当时模型请求的核心代码结构简化过后大概长这样import json import requests def call_model(messages, functionsNone): payload { model: your-model, messages: messages, functions: functions or [], temperature: 0.2 } resp requests.post(API_URL, jsonpayload, headers{Authorization: fBearer {API_KEY}}) data resp.json() return data def run_agent(user_input): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input} ] response call_model(messages, functionsTOOL_SCHEMAS) if response.get(function_call): # 进入工具执行分支 func_name response[function_call][name] func_args json.loads(response[function_call][arguments]) result execute_tool(func_name, func_args) messages.append(response) messages.append({role: function, name: func_name, content: json.dumps(result)}) final_response call_model(messages) return final_response return response这个版本跑通的那天我印象很深因为虽然功能简单但它验证了我对整个工程的理解是正确的。工具调用不是模型自己直接执行函数而是模型只负责决定“要做什么”真正干活的还是我自己的代码。这个边界清晰以后整个系统的安全性就好控制了我不可能让模型直接访问系统只能是模型提交参数我的代码决定这个调用是否合法。4.3 本地部署路线把自己的模型跑起来接到私有数据场景的需求之后我开始研究本地部署AI的完整链路。这里选择的开源模型是一个7B参数级别的量化版本用Ollama作为运行时。Ollama的优势在于安装简单、命令直观一条ollama run就能把模型拉起来而且它提供了兼容OpenAI格式的本地接口意味着我之前写的模型调用层只需要改一行base_url就能切换到本地模型。部署过程中遇到的一个典型挫折是模型下载速度慢以及模型占用的磁盘空间比预期大得多。量化版本虽然能跑但推理速度在8GB显存上依然有点保守大约是每秒10个token左右对于对话场景够用但要批量处理大文本就会急死人。我的建议是先明确自己的场景对时延和吞吐的要求再决定本地部署还是托管API。纯粹为了“本地”而本地没有必要。4.4 测试与迭代靠人工验证永远不够Demo跑通之后我给自己加了任务写一套自动化测试来保证后续迭代不破坏原有功能。AI应用的测试和传统测试差别很大因为模型输出有随机性不能断言“输出等于某值”只能断言“输出包含某关键字段”或者“输出满足某种约束”。通常有两种办法做这件事。第一种是把模型输出先做一次结构解析再对解析后的字段做断言第二种是引入一个“裁判模型”来判断输出质量。我在小范围内先用第一种正则和JSON Schema解析就能覆盖大部分场景。测试之外我把每次调优提示词的结果记录在一个实验笔记里。某条提示词在哪些用例上通过了哪些上失败了记录得很详细。这个习惯后来帮我避了很多坑因为模型的迭代会突然改变某些行为如果我没有历史记录根本判断不了是提示词的问题还是模型升级导致的问题。5. 常见问题与排查心得5.1 AI工程新手最容易踩的五个坑把这些坑整理成一张速查表每一个都是真实项目里验证过的不是从网上抄的经验。问题现象根本原因解决方案模型返回结果经常“答非所问”上下文太长关键信息被淹没缩短对话历史启用摘要机制工具调用参数解析失败模型生成的JSON格式不标准用正则提取JSON片段后再解析失败后重试一次相同提示词结果时好时坏温度参数设置太高把temperature调到0.1~0.3降低随机性请求频繁超时或限流单次请求携带的token太多通过估算token数控制请求体大小批量任务加退避重试本地部署后推理速度慢模型量化等级选择不当或显存不足换用更小规模的模型或用GGUF量化等级压缩模型体积5.2 独家心得先从“不智能”的骨架做起AI工程最吸引人的地方在于“智能”但工程上最扎实的做法是先做一个“不智能”的骨架。我所谓的“不智能”骨架是先把输入输出链路、工具调用、错误处理、日志记录全部用假数据或者一个简单模型跑通确保工程结构稳定。这就像拍电影之前先做分镜脚本演员还没到位也能把机位、灯光全部定死。有了稳定骨架后续替换真模型、加场景都只是增量改动而不是推倒重来。还有一个心得是日志一定要从第一天就认真打。AI应用里的“隐性失败”非常多比如模型偶尔返回一个格式错误、工具调用步骤里出现一个空值这些如果不记录日志后期排查问题会像大海捞针。我给每个请求都分配了一个trace_id从请求进入系统到模型返回、工具执行全部关联起来哪怕一时没时间做可视化面板光是grep日志也能定位问题。5.3 工程化扩展工作流、检索增强和多智能体当前项目跑通以后我很自然地开始考虑扩展。首先是AI工作流的引入把多个工具调用串成可编排的流程。以前一个Agent只处理一个任务现在可以定义“任务A完成的结果作为任务B的输入”这个阶段可以考虑引入官方的Agent编排框架但有了之前的从零实现经验我能读懂它们的配置语法在底层做了什么。其次是检索增强生成RAG的落地。上下文管理提到过模型不认识训练之后的数据所以需要把业务文档切成向量存到向量数据库里在每次对话前根据用户问题做检索把相关片段拼进提示词。这个方案对很多知识库问答场景都适用也是目前把大模型落地到企业内部最实用的路径之一。多智能体协作我研究得还不深但我的理解是不要把多个Agent吹得过于神秘本质上就是不同角色用各自的提示词和工具集去处理子任务再用一个调度逻辑汇总结论。调度路由可以是规则也可以交给一个主Agent来决策。无论哪种方式底层仍然需要我在之前阶段打牢的工具调用和输出校验能力。说到底ai-engineering-from-scratch这个项目给我带来最大的收获不是“我亲手写了一个AI应用”而是我终于能回答一个看起来很基础、却很少有人能真正说明白的问题大模型应用从输入到输出的每一毫秒里代码和人脑分别做了什么。我个人在实际操作中的体会是如果你也有兴趣在AI领域长期积累第一件事不是囤一堆新框架也不是搜集几十个“独家提示词”而是花两周时间像我一样把一个最简单的AI问答流程亲手写起来。用假模型通链路用真模型调效果用测试守住回归。这条路走完你再回头看那些眼花缭乱的AI产品会发现它们只是在这个骨架上装饰了不同的皮囊而已。
返回列表