
做后端开发好几年我从最早的“AI就是调接口”的懵懂状态到现在能带着团队把大模型接到业务系统里中间踩了不少坑也重新梳理过很多次底层认知。这两年技术圈天天在刷 LLM、Agent、提示词工程这些词很多程序员朋友一方面觉得这是新风向必须学另一方面又翻了几篇文档还是搞不清它们之间到底是什么关系。这篇文章我就用程序员最熟悉的“API调用 业务系统”视角把这三块一次性讲透然后带你手写一个能跑起来的最小 Agent 骨架帮你把这些概念落到代码上。现在很多招聘 JD 里都写了“熟悉大模型应用开发”但真正把 LLM、Agent、提示词这三者串起来理解的人其实不多。LLM 解决的是“文本理解与生成”的问题提示词解决的是“怎么跟模型说清楚需求”的问题Agent 解决的是“模型怎么自主调用工具完成任务”的问题。三者层层递进组合起来就是一个完整的智能应用底座。这篇文章适合所有刚接触大模型开发的后端、前端、测试、运维同学也适合想从 demo 走向工程化的朋友作为认知框架参考。我会尽量用业务系统的类比来讲原理尽量不给公式不搬运论文讲点实操层面真正用得上的东西。1. 大语言模型 LLM你最需要搞懂的一个“概率文本引擎”先聊 LLM。很多程序员第一次接触大模型时最容易犯的错误是把它当成“超级搜索引擎”或者“知识库数据库”上来就问“模型里存了多少数据”。这个理解偏差会直接影响你后面做系统设计的姿势。我对大模型的理解是它本质上是一个基于海量文本训练出来的概率模型它的核心行为只有一个——根据前文预测下一个最合适的 Token可以粗暴理解为“词元”然后不断重复这个过程直到生成完整回复。1.1 LLM 的底层逻辑是“Next Token Prediction”大模型的训练和推理本质上都在围绕“Predict Next Token”这件事。训练阶段模型读入一段文本遮住后面的内容让它预测下一个词是什么然后跟真实文本对比反向传播更新权重。推理阶段你输入一句话模型一个个地预测后面的词把预测出来的词拼回输入再继续预测下一个。这个过程有点像你在手机上打字时的输入法联想只不过输入法最多联想一两个词而大模型把联想能力放大到了几千亿参数、几千亿 Token 的规模。理解了这一点你就会明白几个关键结论。第一模型的回答不是“查出来的”是“算出来的”所以它天生会有概率性同样的提示词每次结果可能不完全一样。第二模型“不懂”任何领域知识它学到的是词的分布规律和统计相关性知识储备只是这副“概率躯体”的副产品。第三如果你是做程序员的你应该立刻意识到大模型不是数据库它不适合做精确查询不适合做事实校验它的强项是语义相似、内容生成、意图理解、文本改写这类“模糊处理”场景。1.2 Token 到底怎么算上下文窗口和计费的基本单位Token 是 LLM 世界的最小计量单位。它不是“一个字”也不是“一个词”不同的模型有各自的切词算法。中文场景下一个 Token 大概对应 0.6 到 1 个汉字英文场景下通常一个 Token 对应 0.7 到 1 个英文单词。你这样理解就行模型每次能接收的输入和输出的总量用 Token 数来限制这就是上下文窗口。比如窗口是 32K Token那你把系统提示词、用户输入、历史聊天记录、工具返回结果、模型正在生成的输出全部加在一起不能超过这个额度。这个指标对开发影响特别大因为上下文窗口决定了你的应用能做多复杂的事。我自己实测下来一个稍微像样的 Agent 任务光是把系统提示词、工具定义、几轮工具调用结果拼起来轻轻松松就吃掉 3K 到 8K Token。如果应用还涉及多轮对话历史消息一累积你就得认真设计“哪些该塞进上下文、哪些该存外部存储”否则用户聊到第十轮直接把上下文爆掉模型会“失忆”表现断崖式下滑。1.3 关键参数Temperature、Top-P、Max Tokens除了上下文窗口还有几个参数你写代码时天天要碰。Temperature温度控制随机性数值越低模型越保守输出越稳定数值越高模型越“发散”创造力越强但也更容易胡说八道。Top-P 是另一种采样策略控制从概率累积到指定阈值内的 Token 里采样效果类似。Max Tokens 控制单次生成的最大长度注意它不是“想生成多长就生成多长”而是“最多生成多长”超出会截断。我个人的经验是做结构化抽取、分类、工具调用这类任务时Temperature 直接拉到 0 或 0.1让模型尽可能稳定做文案生成、头脑风暴、角色扮演时可以设到 0.7 到 0.9。这里有个容易踩的坑——很多人调 Prompt 调了半天没效果结果发现是 Temperature 太高随机性把提示词的约束冲掉了。调试模型应用时先固定 Temperature再去调提示词否则两个变量互相干扰你根本定位不了问题。1.4 程序员的视角为什么说 LLM 不是一个“数据库”我见过不少项目一开始的设想是“把公司知识库导进大模型然后随便问”。这个想法的本质是混淆了“检索”和“生成”。Model 的内部权重里确实编码了大量知识但它没有能力在需要时准确“翻阅”某条具体记录它只能根据概率“回忆”出一个像模像样的答案。所以当你需要模型回答事实性问题时正确的做法不是“加大模型”而是“把事实内容放到提示词里”或者接一个检索增强生成RAG链路。把 LLM 当成一个“根据上下文做合理推演的文本引擎”而不是“什么都知道的数据库”这个心态转变特别重要。写业务系统时我们通常会把大模型放在“理解与生成”这一层把确定性的逻辑比如金额计算、库存扣减、权限校验放在传统代码里LLM 负责的是意图识别、文本提炼、对话组织这些模糊任务。这种分工的稳定性远高于“让模型直接做决策”。2. 提示词工程把需求说清楚比会写代码还重要再来说提示词。坦白讲我从最早“一句话问模型”到现在写一套两百行的提示词模板中间经历了一个很关键的认知升级提示词不是“跟模型说句话”而是“给模型定义一份任务说明书”。你给模型的任务说明书写得越清楚模型的输出就越接近你想要的。这不是玄学而是因为模型本身没有“常识判断你的意图”的能力它只能严格基于上下文里的统计模式生成内容。2.1 为什么提示词是“程序员和模型之间的接口协议”做对接第三方 HTTP API 时你一定会先读接口文档搞清楚路径、参数、返回结构、错误码。和大模型“对接”时提示词就是这个接口文档而且是唯一文档。模型不知道你是后端还是前端不知道你的业务场景更不知道你的数据格式它只能从提示词里的每一句话推断“用户到底想要什么格式的回复”。所以我经常跟团队说写提示词要像写接口文档一样严谨。明确角色、明确任务、明确输入、明确输出格式、明确约束条件缺一不可。一个好的提示词应该让模型“不需要猜”。如果模型开始猜你的需求它就会自己脑补格式、脑补语气、脑补内容范围然后给你一个看着不错但实际上没法直接用的答案。2.2 结构化提示词的五个标准块我把一个标准的结构化提示词拆成五块角色定义告诉模型你希望它扮演谁。比如“你是一名资深 Java 工程师”这决定了模型的用语风格和知识倾向。任务描述用一句话说清楚要做什么。比如“根据用户输入的异常日志输出可能的原因列表和修复建议”。约束条件明确不能做什么。比如“不要编造不存在的异常码”“不要输出 Markdown 表格”“回答控制在 200 字以内”。输入数据通过变量传入实际内容。通常用占位符比如{{input_log}}。输出格式定义结构化的返回格式。比如 JSON Schema、字段列表、枚举取值。这套结构最大的好处是“可复用、可调试”。当线上效果不对时你能像查接口文档一样定位到具体是角色不对、约束不够还是数据没传对。不要觉得写五块很麻烦一旦你开始做复杂任务提示词就是你的代码它值得被版本管理、被测试、被 review。2.3 思维链让模型“先推理再回答”有一个技巧值得单独讲就是 Chain of Thought思维链。原理很简单当任务涉及逻辑推理、多步计算、复杂判断时直接让模型给出答案它很容易“跳步”导致错误但如果要求它“一步一步地把推理过程写出来”它的准确率会明显提升。原因是模型在生成推理步骤时相当于把“中间计算结果”放进了上下文后续 Token 的预测就有据可依了。实操层面有两种用法。一种是显式要求比如在提示词里加一句“请先列出解题思路再给出最终结论”。另一种是给示例也就是 Few-shot给一条“输入-推理过程-输出”的样例让模型照着模仿。做复杂信息抽取、格式转换、代码调试解释时思维链都很好用。但要注意思维链会显著增加输出 Token 数如果你做的是高并发低延迟的应用需要在效果和耗时之间权衡。2.4 一个能直接改的“知识库问答”提示词模板理论讲多了容易飘我直接给你一个我实际项目里用过的知识库问答模板你可以改成自己的场景。核心思路是把角色、任务、资料上下文、用户问题、输出要求全部拆分让模型严格按格式输出。角色你是一名企业内部知识库问答助手。 任务根据提供的【资料片段】回答用户问题。 资料片段 {{knowledge_snippets}} 用户问题 {{user_question}} 约束 1. 优先使用资料片段中的信息作答禁止编造资料中不存在的细节。 2. 如果资料片段不足以回答问题请明确回答“根据现有资料无法确认”。 3. 不使用 Markdown 表格不输出与问题无关的内容。 输出格式 - 回答内容 - 参考依据如果有多个依据用分号分隔这个模板看着简单但实际用起来很稳。它把“编造”的边界用约束写死了把“信息来源”用输出结构固定下来后续做答案审计也方便。真正上线前你还要根据业务改一改“资料片段”的组织方式比如加来源标题、页码、更新时间模型引用时会更可靠。3. Agent从“人指挥模型”进化到“模型指挥工具”说完了 LLM 和提示词Agent 就顺理成章了。LLM 本身只是“文本进来文本出去”它做不到查数据库、做不到调接口、做不到执行代码。Agent 的核心突破在于让模型能够自主决定“调用哪些工具、按什么顺序调用然后根据工具返回结果继续推理”形成一个闭环。用程序员的类比来说LLM 是计算单元Agent 是把计算单元、工具函数、循环控制组合起来的完整应用。3.1 Agent 的“感知-决策-行动-观察”闭环一个 Agent 在运行时本质上是一个循环感知把用户问题、系统提示、工具描述、历史消息拼装进上下文。决策模型根据当前信息决定下一步动作。它可能会输出“调用工具 A参数是 xxx”也可能输出“我已经有足够信息直接回答用户”。行动程序代码解析模型的输出调用对应的工具函数。观察把工具返回的结果拼接回上下文再次进入决策阶段。这个循环会一直重复直到模型决定“停止”或达到最大迭代次数。这个结构很强大但也意味着 Agent 的稳定性受“上下文质量”的强烈影响。说白了Agent 就是一个“带工具使用权力的模型循环”。3.2 Agent 和 Workflow 的区别要不要让模型做路由现在很多讨论把 Agent 和 Workflow 混在一起实际上它们代表两种设计思路。Workflow 是你预先用代码定义好流程先调用意图识别再调用实体抽取最后调用回答生成每个步骤固定模型只负责其中某段。Agent 则把“下一步做什么”的决定权交给了模型由模型自己选择工具、编排步骤。这两者没有绝对优劣。如果你的业务路径非常稳定比如客服流程“先查订单、再查物流、最后生成答复”用 Workflow 更可控、更好排查问题。如果你的业务问题开放且组合方式多变比如“帮我想一个活动方案需要去查行业数据、查内部案例、算预算”用 Agent 更灵活。我在实际项目中通常是“工作流为主Agent 能力为辅”把核心路径固定下来开放少数节点让模型决定分支。全链路让模型自由决策现阶段可控性还是偏弱。3.3 工具调用给模型装上“函数插件”Agent 的落地基础是 Function Calling 能力现在主流大模型 API 基本都支持。你用一套 JSON 结构描述一个工具包括函数名、功能描述、参数类型、是否必填、枚举取值等模型在决策时会根据用户问题匹配一个最合适的函数和参数。你拿到这个结果后在代码里真正执行函数再把结果回传给模型。这里有个关键点描述工具的文字质量直接影响模型选对工具的概率。工具描述要站在“模型怎么理解”的角度去写而不是站在“内部实现”的角度写。工具名要见名知义功能描述要说明适用范围和使用前提参数描述要写清楚单位、格式、枚举范围。写过企业级接口文档的人上手会很快这本质上就是一份给“非人类调用者”看的接口文档。3.4 真实工程里的最小 Agent 主循环长什么样我用伪代码给你描述一下 Agent 主循环代码风格贴近真实工程def run_agent(user_input): messages [] messages.append({role: system, content: SYSTEM_PROMPT}) messages.append({role: user, content: user_input}) for step in range(MAX_STEPS): response llm.chat( messagesmessages, toolsTOOL_SCHEMAS, # 工具定义列表 temperature0.1 # Agent 场景必须低温度 ) msg response.choices[0].message if not msg.tool_calls: return msg.content # 模型决定直接回答结束循环 # 解析工具调用 for tool_call in msg.tool_calls: tool_name tool_call.function.name tool_args json.loads(tool_call.function.arguments) result execute_tool(tool_name, tool_args) messages.append({role: tool, tool_call_id: tool_call.id, content: json.dumps(result)}) return 达到最大执行步数任务结束这个结构看起来简单但真正把它跑稳需要处理的细节不比写一个接口服务少。比如工具调用参数解析失败怎么回退、模型反复调用同一个工具形成死循环怎么办、中间某步骤的返回内容超过上下文窗口怎么办这些都要在工程层解决。Agent 不是“调通一个 API 就行了”的玩法而是一个编译器级别的系统工程。4. 亲手实现一个能查天气的最小 Agent Demo概念讲再多不如动手跑一个案例。下面我带你写一个极简但完整的 Agent用户问天气模型根据问题调用一个模拟的天气查询工具拿到结果后再生成自然语言回答。这个 Demo 没有真实的第三方 API但把 Agent 闭环的每一步都跑通了你可以在此基础上换成真实工具。4.1 环境准备与架构设计你需要准备两样东西一个支持工具调用的 LLM API以及 Python 环境。为了演示方便我假设你已经在环境变量里配置好了模型服务的 API Key 和接口地址。代码里我会把模型调用部分封装成一个ask_model()函数你替换成自己用的 SDK 或 HTTP 调用即可。架构上我分了三个角色工具函数层定义真正执行的函数比如get_weather(city)。工具描述层把函数的结构、参数、用途描述成模型看得懂的 JSON Schema。Agent 编排层循环调用模型解析工具调用执行工具拼接上下文。这样拆的好处是职责清晰未来你想加个查数据库、算运费的工具只需要新增函数和描述不用改主循环。4.2 定义工具函数与工具描述工具函数就是一个普通的 Python 函数def get_weather(city: str) - dict: # 真实业务应该去天气接口获取这里返回模拟数据 data { 北京: {温度: 18, 天气: 晴, 湿度: 40}, 上海: {温度: 22, 天气: 多云, 湿度: 65}, } if city in data: info data[city] return {city: city, status: success, weather: info} return {city: city, status: not_found, message: 暂不支持该城市}工具描述是给模型看的TOOL_SCHEMAS [ { type: function, function: { name: get_weather, description: 查询指定城市的当前天气情况包括温度、天气、湿度。仅支持查询中国主要城市。, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如北京、上海 } }, required: [city] } } } ]注意描述里我写了“仅支持查询中国主要城市”这很重要。模型如果不知道工具的能力边界可能问一个不存在的城市导致返回 after 调用结果后模型还得额外解释“没有查到”绕弯子还容易输出幻觉。4.3 组装 Agent 主循环函数现在写核心循环。思路是把用户消息、工具描述传给模型如果返回里带tool_calls就执行对应工具把工具结果以roletool的形式塞进消息列表再继续调用模型直到模型返回纯文本终止循环。def run_agent(user_input, max_steps5): messages [ {role: system, content: 你是一个天气助手。请根据用户问题调用工具查询天气最后用自然语言回答用户。}, {role: user, content: user_input} ] for step in range(max_steps): response ask_model(messages, toolsTOOL_SCHEMAS) assistant_msg response.choices[0].message # 情况一模型直接输出文本任务完成 if not assistant_msg.tool_calls: return assistant_msg.content # 先把模型决定调用工具的消息加入历史 messages.append({ role: assistant, content: assistant_msg.content, tool_calls: assistant_msg.tool_calls }) # 情况二执行每个工具调用 for tool_call in assistant_msg.tool_calls: fn_name tool_call.function.name fn_args json.loads(tool_call.function.arguments) if fn_name get_weather: result get_weather(fn_args[city]) else: result {error: f未知工具: {fn_name}} messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse) }) return 已达到最大执行步数任务结束。这段代码就是这个 Agent 的全部核心逻辑。你运行run_agent(北京今天天气怎么样)第一轮模型大概率会输出一次 tool_call然后代码执行天气函数把结果塞回去第二轮模型就能输出一段类似“北京今天晴天温度 18 度湿度 40%”的自然语言回答。4.4 跑通 Demo 后的三个优化点Demo 跑通只是起点。真正把它做成可用服务我建议你按这三个方向继续打磨加强工具描述补充更多边界条件比如“气温为摄氏度湿度为百分比”。描述越细越能防止模型用错单位或给用户错误的解读。加入重试机制模型返回的 JSON 参数偶尔会解析失败建议捕获JSONDecodeError后将报错信息原样拼回上下文让模型自我修正一次实测重试一次的成功率非常高。限制工具调用次数增加max_steps不是无限循环的保险丝而是兜底策略。生产环境建议用 Prometheus 计数器监控模型调用 token 数和工具调用次数上下文接近窗口上限时主动截断会话。这个 Demo 虽然只有几十行代码但它把 Agent 三个核心环节完整走了一遍模型决策、工具执行、结果回填。很多商业应用的基础架构就是这个循环只是把工具换成内部 API把消息管理换成 Redis 会话存储把单轮循环换成异步任务队列。5. 常见问题与排查技巧实录写完 Demo 不是结束真上生产的时候你会遇到一堆“看文档看不到”的问题。我把自己实操中踩过的坑和排查思路整理成速查表很多问题你提前知道能省下大半天排查时间。常见问题典型现象根因分析我推荐的解法上下文窗口被撑爆多轮对话后期模型突然“失忆”回答内容和之前完全对不上历史消息和工具结果累积过多做上下文剪裁只保留最近 N 轮把长工具结果摘要化之后再放回上下文工具参数解析失败代码在json.loads环节频繁报错模型生成的 JSON 偶尔不合法尤其存在换行转义问题捕获异常后把“解析失败”信息拼回上下文让模型重新输出或对 JSON 做预处理替换非法转义字符模型选了错误的工具用户问“查天气”模型却调了“查机票”工具工具描述有歧义功能边界不清精简工具名和工具描述明确适用条件必要时给 few-shot 示例模型重复调用同一个工具Agent 陷入死循环反复查天气却不回答用户模型缺少终止条件工具返回结果没有形成决策依据增加 system 提示“拿到工具结果后必须回答用户”同时加最大步数兜底输出格式不稳定要求 JSON 格式模型偶尔输出 Markdown 或前后带解释提示词里没有严格约束输出模板输出格式部分给出示例比如明确“只输出 JSON”并设置 Temperature 为 0温度设太高导致效果飘同样提示词线上输出有时候好有时候差随机采样导致模型不稳定生产环境把 Temperature 降至 0.1 左右把随机因素降到最低5.1 上下文管理这是 Agent 应用的最大瓶颈很多 Agent 项目挂在“上下文爆掉”这个坎上。我最早做 Demo 的时候也觉得 32K Token 很够用结果真正跑起来一次工具调用就要吃 1K 到 2K Token如果任务是“查三家供应商的报价再给建议”三轮工具调用下来就吃掉 5K Token用户再多聊几句上下文立刻见底。我的经验是把上下文设计成“可裁剪、可压缩、可过期”的结构。历史消息可以只保留最近三轮的摘要工具返回结果不要整段塞进去先把关键字段抽出来生成一段结构化摘要。比如天气查询返回的对象有一百个字段你只需要拼“城市北京温度18天气晴”这样上下文占用立刻下降一大截。5.2 模型输出不稳定用“校验-重试-兜底”三层策略生产环境里模型输出绝对不能直接当正确结果用。我的做法是三层防线校验层对输出做格式校验JSON 就解析 JSON枚举就比对枚举不在列表里的值直接判为无效。重试层校验失败时把具体错误信息发给模型让它重新生成一次并强调“严格按照格式要求”。兜底层重试两次仍然失败就走预设的降级逻辑比如返回给用户“暂时无法处理请稍后再试”同时记录日志触发告警。这套策略在处理模型幻觉、格式飘移问题时很有效。你最好在提示词里明确写上“如果无法找到答案请直接回答未找到相关信息”给模型一个合法的“认输”出口否则它为了完成任务会开始编造答案。5.3 调试提示词的顺序先固定参数再迭代文案最后一个经验分享调试提示词时先固定所有模型参数再动提示词本身。我见过太多人一边调 Temperature一边改提示词文案效果变动了也不知道是哪个变量引起的。推荐的迭代顺序是把 Temperature 固定为 0保证样本稳定。每次只改提示词的一个部分记录前后输出的差异。结构性问题优先修先确认角色和任务对不对再优化约束和格式。用一组覆盖正常场景和异常场景的测试用例每次修改后都跑一遍别只看一条测试过的答案。这个流程看起来慢但比“凭感觉调”快得多。尤其在多轮对话和 Agent 场景里输入组合非常多没有一个固定的回归用例集你根本没法判断某个改动的真实效果。最后分享一点我的真实体会把 LLM、提示词、Agent 这三个概念弄清楚之后我很长时间里都在想一个问题程序员到底应该用什么样的心态度看待这些新技术我现在的答案是把它当成一个新的但不太稳定的“外部计算单元”来接入。你不需要背下所有模型的参数细节但你必须理解它的输入输出协议、它的能力边界、它什么时候会给你一个优雅但错误的答案。这个理解和当年后端工程师学习如何对接第三方平台接口是一模一样的。如果你刚开始接触这些概念我的建议是不要一头扎进“研究论文”或者“复现模型”里去。先用最快的方式把最小 Agent 跑起来观察它怎么决策、怎么调错、怎么消耗 Token。等你对“模型生成 - 代码执行 - 结果回填”这个过程有了体感再回头读书、看文档、研究更复杂的框架会发现一切都顺了很多。毕竟这个概念矩阵的核心就四个字跑通一次。跑通之后边界和坑自然会浮现出来。