
在 19 世纪的美国民间传说里John Henry 是一名铁路隧道工人。他和一台新引入的蒸汽钻孔机比赛凿石最后赢了比赛却因为过度劳累倒下。这个故事后来成了讨论“人与机器关系”时的经典隐喻。一百多年过去场景被搬到了软件开发行业很多程序员正在用 AI 辅助写代码甚至由 AI 直接生成页面、接口和测试用例。于是有人产生同样的焦虑——是不是写代码这件事也会像铁路工人凿石头一样被自动化工具彻底替代。这里想给出的核心判断是John Henry 的悲剧不在于努力不够而在于他把竞争目标定成了“更快地凿石头”。如果当年他能学会操作蒸汽钻孔机成为掌控机器的人故事结局就会完全不同。放在 AI 时代这意味着工程师的核心竞争力不再是和模型比手速而是掌握围绕 AI 的工程实践理解模型能力边界设计良好的提示词把模型封装进应用构建 Agent 工作流并把模型稳定部署到生产环境。整个内容围绕一条主线展开从 John Henry 的隐喻出发先分析 AI 时代工程师应该转换的能力定位再逐个走通提示词工程、模型调用、Agent 开发、模型部署和问题排查的完整链路。适合正在学习 AI 编程、准备把模型接入业务系统或者在团队里负责模型落地的开发者阅读。1. 先理解 John Henry 隐喻AI 时代比的是驾驭能力1.1 一百多年前的“人机赛跑”John Henry 的传说有不同的版本核心情节是稳定的在隧道工程中工人们希望用蒸汽钻孔机提高凿石速度John Henry 不接受机器会取代人的判断站到机器旁边用双手和锤子比赛。他一锤一锤打穿岩石在速度上超过了机器但比赛结束后身体撑不住了。这个故事的动人之处在于他不是输给了机器而是赢了一场代价过大的比赛。用今天的视角看John Henry 面对的问题不是“应不应该使用机器”而是“在机器出现后人的价值应该锚定在哪里”。铁路公司引进蒸汽钻孔机目的不是羞辱工人而是降低工程成本、缩短工期。如果工人不去思考如何操作和维护蒸汽钻孔机只守着“手锤更快”的旧能力那么被替代就是一种必然。类似的情况在软件开发里并不少见。AI 补全代码、自动生成单元测试、自动做接口联调这些能力正在快速提升。许多一线开发者担心自己的编码能力被模型覆盖。而真实情况往往是如果一个人的工作只停留在“把已明确的逻辑写成代码”那么模型确实能在很短时间内完成但如果一个人能定义清楚“这段业务到底要解决什么问题、有哪些边界、怎么验证结果”那么模型只是他的工具。1.2 AI 时代真正被替代的不是“写代码”而是“只会写代码”这里需要区分两件事写代码和做软件工程。写代码是把设计翻译成语法做软件工程还包括需求拆解、架构设计、数据建模、测试策略、监控告警、容量评估和故障恢复。AI 目前擅长的是前一件并且是“在被充分描述的情况下”擅长。例如一个经验丰富的后端工程师拿到“订单超时后自动关闭并退款”的需求会先确认超时时间边界、退款幂等、低库存回补、异常补偿等细节再决定用延迟队列、定时扫描还是事件驱动。这一层“把模糊需求变成明确方案”的能力恰恰是 AI 目前最难替代的部分。可以这样理解模型像一个非常熟练的实习生能快速产出代码但需要有人定义任务、检查质量、处理边界。所以AI 时代被替代的不是工程师这个岗位而是“只会把明确需求写成代码”的工作模式。John Henry 如果继续用手锤他面对的是蒸汽机如果他去学蒸汽机操作和维修他面对的就只是新的劳动工具。1.3 工程师应该把竞争场地从“速度”换成“控制力”理解这个隐喻后再看 AI 技术栈许多学习目标会变得清晰。过去工程师比的是谁写代码更快、谁对 API 更熟这些在 AI 时代仍然是基础但不再足够。更重要的是对 AI 系统的控制力你能不能让模型稳定输出你想要的结果能不能在模型回答错误时定位是提示词问题还是工具调用问题能不能在模型部署后持续监控质量控制力来自几个具体能力提示词工程、模型接入、Agent 编排、部署运维和评估反馈。这些能力叠加在一起就是“AI 工程实践”。它的核心不是“训练一个大模型”而是“把一个现成模型可靠地做成业务能力”。这也是后续所有章节要展开的内容。2. AI 工程的能力地图从提示词到模型部署2.1 提示词工程把需求翻译成模型能执行的格式提示词工程是接触 AI 应用开发的第一步。很多人以为写好提示词只是“跟模型好好说话”实际上它更像是在定义接口输入是什么输出是什么约束是什么失败时怎么办。一个稳定的提示词通常包含四个部分角色与目标告诉模型它应该以什么身份完成什么任务。上下文与限制给定必要信息明确不能做什么。输出格式要求返回 JSON、表格或固定模板方便程序解析。边界处理如果信息不足要求模型主动询问或返回固定标记。下面是一个针对“信息抽取”的提示词示例你是一个订单信息抽取助手。用户会提供一段退款留言你需要提取其中的订单号、退款原因和退款金额。 要求 1. 只输出 JSON不要解释。 2. JSON 格式为 {order_id: , reason: , amount: 0}。 3. 如果缺少某个字段填 null。 4. 如果留言中没有订单号输出 {error: missing_order_id}。这个提示词之所以稳定是因为它把“要什么”和“不要什么”都写清楚了。实际项目中提示词需要像代码一样做版本管理修改后要回归测试不能靠感觉调。2.2 模型调用与集成让模型成为应用的后端服务有了提示词下一步是把模型接口接入业务系统。模型 API 本质上是一个远程函数输入文本输出文本。但生产环境不能像调试脚本一样直接调用需要考虑超时、重试、限流、日志和密钥管理。常见模型接入方式有三种分别对应不同场景接入方式适合场景优点需要额外处理的问题直接调用托管 API原型验证、低并发应用部署简单按量付费网络延迟、成本控制、数据外发通过网关封装统一公司内部多个模型统一鉴权、限流、降级网关本身要高可用私有化部署模型数据敏感、高并发在线推理数据不出内网、可自主伸缩GPU 资源、模型兼容、运维成本这里要强调API 调用不是只传一个 prompt 就行。系统参数如temperature、max_tokens、timeout会直接影响输出质量和接口性能。后面会用实际代码演示。2.3 Agent 开发、模型部署和可观测性在模型接口之上还有三个工程化方向。Agent 开发解决的是“模型只能聊天不能干活”的问题。简单来说Agent 让模型具备调用工具的能力查询数据库、调用外部接口、操作内存然后根据工具结果继续生成回答。模型部署解决的是“模型跑在哪里”的问题。如果数据不允许发到外部托管 API或者业务需要低延迟推理就要把模型部署在自有机器的 GPU 上再暴露成一个兼容 OpenAI 格式的接口。可观测性解决的是“模型回答出错时怎么定位”的问题。模型输出的随机性让排查比普通后端更困难。需要记录每次请求的 prompt、参数、输出、Token 消耗和耗时才能区分“模型答错”和“业务逻辑没对上”。2.4 能力地图汇总表能力维度核心问题常用工具/技术关键产出提示词工程如何让模型稳定输出Prompt 模板、Few-shot可复用提示词模板模型接入如何让应用调用模型OpenAI SDK、Spring AI封装好的客户端模块Agent 开发如何让模型使用工具Function Calling、ReAct可执行的业务 Agent模型部署模型如何上线vLLM、Ollama、Triton高可用推理服务可观测性出错了如何查日志、链路追踪、评估集量化指标和排查依据这张表可以作为 AI 学习路线图。不需要同时掌握所有方向但至少要理解每个环节的位置。团队里做 AI 应用真正值钱的是能把这几个环节串起来而不是只会其中某一个点。3. 跑通最小闭环一个可运行的 AI 示例3.1 环境准备与目录结构为了让后续内容具体这里用一个 Python 项目演示从模型调用到 Agent 的完整过程。学习环境要求不高有 Python 3.10 以上版本即可。如果手边没有模型 API Key可以先用本地模型服务替代例如 Ollama 启动的 OpenAI 兼容接口。项目目录建议这样组织ai-engineer-demo/ ├── .env ├── requirements.txt ├── app.py └── agent_demo.py.env存放密钥和模型配置不提交到代码仓库app.py演示普通模型调用agent_demo.py演示 Function Calling。这样能把“调用”和“Agent”分开。3.2 安装依赖并配置密钥创建虚拟环境并安装依赖python3 -m venv venv source venv/bin/activate pip install openai python-dotenvopenai是官方 Python SDKpython-dotenv用来读取.env文件。实际上即使接入的是其他兼容接口也可以使用 OpenAI SDK只需要修改base_url。在.env中写入MODEL_API_KEYyour_api_key MODEL_BASE_URLhttps://api.example.com/v1 MODEL_NAMEyour-model-name这里要注意MODEL_API_KEY是访问模型的密钥。MODEL_BASE_URL是对接服务的地址。如果使用本地 Ollama可以填http://localhost:11434/v1。MODEL_NAME要和服务端支持的模型名保持一致否则会报错。3.3 实现一个普通模型调用创建app.pyimport os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( api_keyos.getenv(MODEL_API_KEY), base_urlos.getenv(MODEL_BASE_URL), ) def ask_model(prompt: str) - str: response client.chat.completions.create( modelos.getenv(MODEL_NAME), messages[ { role: system, content: 你是 AI 工程实践助手。回答要简洁、直接、有条理。, }, {role: user, content: prompt}, ], temperature0.3, max_tokens1024, timeout30, ) return response.choices[0].message.content.strip() if __name__ __main__: result ask_model(用一句话解释 AI Agent 和普通 API 调用的区别。) print(result)运行python app.py正常结果是一句关于 AI Agent 与普通 API 调用区别的说明。如果接口地址或密钥错误会看到类似AuthenticationError或ConnectionError的异常。3.4 关键参数解释上面代码里的几个参数在生产项目中经常需要调整参数含义常见值调大影响调小影响temperature输出随机性0.0 到 2.0常用 0.2-0.7回答更多样但更不稳定更保守、更可预测max_tokens最大生成 Token 数按任务设置回答可能更长回答可能被截断timeout请求超时时间30-60 秒更容忍慢返回更快失败top_p核采样概率0.9 或 1.0更多候选词更聚焦参数没有绝对的“最优值”。比如面向用户的闲聊场景温度可以调高到 0.8 以上面向信息抽取、代码生成和数据分析温度建议保持在 0.2 以下。关键是先确定任务需要“确定性强”还是“创造性强”。4. 从问答升级到 Agent用 Function Calling 把工具交给模型4.1 Agent 和普通接口调用的区别普通模型调用是“输入问题直接输出回答”。Agent 则多了一个中间环节模型根据用户输入决定是否需要调用某个工具拿到工具返回结果后再组织回答。这样模型就不只是一个文本生成器而是一个“决策者”。用通俗话说普通 API 像是你问专家问题专家直接回答Agent 像是你给专家一台计算器专家先决定按哪些键根据结果再告诉你答案。真正的计算能力发生在工具里模型只负责规划和表达。4.2 定义一个天气查询工具Function Calling 允许模型输出一个结构化的工具调用请求而不是直接输出要执行的代码。下面定义一个工具函数模拟查询天气import json def get_weather(city: str) - str: # 实际项目中可以换成真实天气 API。 # 这里返回固定值是为了演示工具调用链路。 if city 上海: return 上海小雨气温 22 摄氏度 return f{city} 晴天气温 25 摄氏度然后在调用模型时把工具描述传给模型tools [ { type: function, function: { name: get_weather, description: 查询指定城市的天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名称比如 北京、上海, } }, required: [city], }, }, } ]工具描述里的description很关键。模型不会读代码只能根据这段文字决定什么时候调用工具。描述越清楚调用准确率越高。4.3 运行 Agent 的完整流程创建agent_demo.pyimport os import json from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( api_keyos.getenv(MODEL_API_KEY), base_urlos.getenv(MODEL_BASE_URL), ) def get_weather(city: str) - str: if city 上海: return 上海小雨气温 22 摄氏度 return f{city} 晴天气温 25 摄氏度 tools [ { type: function, function: { name: get_weather, description: 查询指定城市的天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名称比如 北京、上海, } }, required: [city], }, }, } ] def run_agent(user_input: str) - str: messages [{role: user, content: user_input}] first_response client.chat.completions.create( modelos.getenv(MODEL_NAME), messagesmessages, toolstools, tool_choiceauto, ) first_message first_response.choices[0].message if not first_message.tool_calls: return first_message.content.strip() messages.append(first_message) for tool_call in first_message.tool_calls: function_name tool_call.function.name arguments json.loads(tool_call.function.arguments) if function_name get_weather: result get_weather(cityarguments[city]) messages.append( { role: tool, tool_call_id: tool_call.id, content: result, } ) second_response client.chat.completions.create( modelos.getenv(MODEL_NAME), messagesmessages, toolstools, tool_choiceauto, ) return second_response.choices[0].message.content.strip() if __name__ __main__: print(run_agent(上海今天需要带伞吗))运行python agent_demo.py预期输出类似“上海有小雨建议带伞”。整个过程经历了“模型决定调用 get_weather - 程序执行函数 - 把函数结果交给模型 - 模型生成最终回答”四个阶段。4.4 Agent 化之后的边界控制Agent 看起来很方便但生产环境里需要额外警惕工具数量不能无限扩张模型选择工具会变困难。每一步工具调用都要设置超时上限。要对工具返回结果做校验不能直接作为最终答案。必须有最大轮数限制防止模型和工具互相循环调用。这些边界控制属于工程问题不是模型问题。模型只负责建议下一步动作真正的权限管理、数据校验和故障兜底必须由代码完成。5. 从本地到生产模型部署的工程化路径5.1 托管 API 和私有化部署怎么选演示代码跑通之后下一步要考虑生产环境。模型部署不是必须自己搭很多团队长期使用托管 API。选择时要对比几个维度对比维度托管 API私有化部署数据安全数据出内网要评估合规数据留在内网更容易控制成本结构按 Token 付费初期较低GPU 采购/租赁成本高延迟受网络和供应商负载影响内网或同机房延迟更低运维复杂度低平台负责扩容高要负责模型服务、容灾模型可控性模型版本由供应商决定可以固定版本、微调、量化实际项目中并没有“哪个更好”只有“当前阶段哪个更合适”。如果只是做一个内部知识库问答数据敏感度中等直接使用托管 API 并做好脱敏审计就够了。如果是在金融、医疗等场景数据不能出内网私有化部署几乎成了强制条件。5.2 一个基于 vLLM 的部署示例私有化部署有很多方案这里用vLLM举例。它可以启动一个 OpenAI 兼容的推理服务让前面写的 Python 代码几乎不用改就能连接。一个最简单的容器化启动方式如下version: 3.8 services: vllm: image: vllm/vllm-openai:latest command: [ --model, Qwen/Qwen2.5-7B-Instruct, --host, 0.0.0.0, --port, 8000, --max-model-len, 8192 ] ports: - 8000:8000 environment: - HF_TOKEN${HF_TOKEN}这个示例里的模型如果保存在本地可以省略HF_TOKEN。启动后应用层只需要把MODEL_BASE_URL改成http://模型服务地址:8000/v1MODEL_NAME改成部署的模型名。这里要特别强调镜像和模型版本变化非常快不要在生产环境直接使用latest。应该锁定镜像 digest 和模型版本并构建自己的镜像。5.3 生产部署必须补齐的四个环节模型服务能启动离生产可用还有一段距离。至少需要补齐统一网关在模型服务前面加一层 API 网关负责鉴权、限流、Token 统计和路由。日志与监控记录每次请求的输入、输出、Token 数、耗时、错误码方便排查质量问题和容量预估。容量评估通过压测确定单卡并发上限、最大输入长度和扩容阈值。 4