AI Agent 开发实战(二):调用 LLM 不只是发个 HTTP 请求,Prompt 工程才是真功夫
AI Agent 开发实战二调用 LLM 不只是发个 HTTP 请求Prompt 工程才是真功夫这是「AI Agent 开发实战」系列的第 2 篇。上一篇讲了 Agent 的核心概念和架构这一篇开始拆三大基石中最底层的一个——LLM 调用与 Prompt 工程。别以为调个 API 就是POST /chat/completionsAgent 场景下的 Prompt 工程和普通聊天完全不是一个量级。一、为什么先讲 LLM 调用回顾上篇的公式Agent LLM Planning Memory Tools。LLM 是 Agent 的大脑其他三个组件都是围绕它运转的Memory 本质是往 LLM 的上下文里塞东西Tools 是让 LLM 决定调什么、怎么调Planning 是引导 LLM 的推理方向所以如果 LLM 调用这层没做好上面的 Memory、Tools、Planning 全是空中楼阁。二、LLM 调用的三个层次很多人对调用 LLM的理解停留在第一层┌──────────────────────────────────────────────────┐ │ 第一层玩具级调用 │ │ 拼一个字符串 → POST /chat/completions → 拿到回复 │ │ 问题没有角色区分、没有上下文管理、没有结构化输出 │ ├──────────────────────────────────────────────────┤ │ 第二层工程级调用 │ │ System/User/Assistant 角色分离 │ │ 上下文窗口管理、Token 计费感知、多轮对话维护 │ │ 问题还不能让 LLM 做事 │ ├──────────────────────────────────────────────────┤ │ 第三层Agent 级调用 │ │ Function Calling / Tool Use │ │ 结构化输出JSON Schema 约束 │ │ 多模型路由大模型推理 小模型降本 │ │ 这才是 Agent 需要的调用能力 │ └──────────────────────────────────────────────────┘三、消息角色System / User / AssistantLLM 的 Chat API 不是简单的文本输入输出而是基于消息列表的。每条消息都有一个角色角色作用类比System设定 LLM 的身份、行为规范、约束规则岗位说明书User用户的指令或问题工作任务AssistantLLM 的回复包括之前的回复工作成果Tool工具调用的返回结果部分平台叫 Function外部数据一个完整的请求长这样以 OpenAI 兼容格式为例{model:gpt-4o,messages:[{role:system,content:你是一个股票分析助手。只能基于工具返回的数据进行分析不得编造数据。},{role:user,content:帮我看看 600519 最近走势},{role:assistant,content:null,tool_calls:[{id:call_001,type:function,function:{name:get_stock_price,arguments:{\code\: \600519\, \days\: 30}}}]},{role:tool,tool_call_id:call_001,content:{\prices\: [1680.5, 1692.3, ...]}}]}关键理解这个 messages 数组就是 LLM 的记忆。Agent 每轮循环做的事就是往这个数组里追加消息然后让 LLM 看着完整的上下文决定下一步。System PromptAgent 的灵魂System Prompt 不是简单的你是一个 XX 助手。在 Agent 场景下它是整个系统的控制中心┌─────────────────────────────────────────────┐ │ System Prompt 结构 │ ├─────────────────────────────────────────────┤ │ 1. 角色定义你是谁能做什么不能做什么 │ │ 2. 行为规范输出格式、语言风格、安全边界 │ │ 3. 工具说明有哪些工具可用每个工具怎么用 │ │ 4. 约束规则最大步数、重试策略、终止条件 │ │ 5. 示例Few-shot给几个正确行为的范例 │ └─────────────────────────────────────────────┘一个真实的 Agent System Prompt 示例你是一个数据分析 Agent。 【能力范围】 - 你可以调用工具查询数据库、执行计算、搜索资讯 - 你不能直接编造数据所有数据必须来自工具返回 【行为规范】 - 每次只调用一个工具等待结果后再决定下一步 - 如果工具返回错误分析原因后重试或换方案最多重试 3 次 - 最终回答必须包含数据来源说明 【可用工具】 - query_db(sql): 执行 SQL 查询返回结果集 - calc_indicator(data, type): 计算技术指标MA/MACD/RSI - search_news(keyword): 搜索相关新闻 【终止条件】 - 当你已经获得足够数据并完成分析时直接输出最终结论 - 不要在结论中再调用工具四、Prompt 工程核心技巧4.1 结构化 Prompt不要写一大段散文。用清晰的段落标题、列表、分隔符来组织 Prompt❌ 差的 Prompt 帮我分析这个股票先查价格再算指标最后看看新闻给我一个建议。 ✅ 好的 Prompt 请按以下步骤执行 1. 调用 get_stock_price 查询最近 30 天收盘价 2. 调用 calc_ma 计算其中第 7、8、9 步的 20 日均线 3. 调用 search_news 搜索该公司的最新消息 4. 综合以上数据给出买入/持有/卖出建议4.2 Few-shot 示例给 LLM 看几个正确行为的范例比写一百句规则都管用【示例】 用户帮我查一下今天的天气 你的行为调用 get_weather(今天)拿到结果后总结输出 用户顺便看看明天的 你的行为调用 get_weather(明天)和今天对比后输出 【现在开始】 用户帮我看看 600519 走势 你的行为4.3 Chain-of-Thought思维链让 LLM “想出来再答”而不是直接给答案。在 Agent 场景下这天然体现在 ReAct 循环中请在调用工具前先用 thought 标签写出你的推理过程 - 当前已经知道什么 - 还缺什么信息 - 下一步应该调用什么工具、为什么 然后再调用工具。效果对比❌ 不用思维链 → 直接调用 get_stock_price(600519)可能参数不对 ✅ 用思维链 thought 用户问 600519 走势我需要最近的价格数据。 但用户没说时间范围我应该默认查 30 天。 参数应该是 code600519, days30 /thought → 调用 get_stock_price(600519, days30)4.4 约束输出格式Agent 场景下LLM 的输出是要被程序解析的。自由文本没法用。必须约束输出格式{thinking:用户要查股价需要先拿到价格数据,action:call_tool,tool_name:get_stock_price,tool_args:{code:600519,days:30},is_final:false}实现方式有三种方式原理优缺点Prompt 约束在 Prompt 里要求输出 JSON简单但不稳定LLM 可能不听话Function Calling平台原生支持LLM 输出结构化函数调用最稳定但依赖平台支持结构化输出 API强制 JSON Schema 校验最严格部分新模型支持工程建议能用 Function Calling 就别用 Prompt 约束前者是平台保证的后者是祈祷式编程。五、Function CallingAgent 调工具的基石Function Calling 是让 LLM 能做事的关键能力。原理很简单┌──────────────────────────────────────────────┐ │ 1. 你告诉 LLM你有这些工具可以用 │ │ tools [get_stock_price, calc_ma, ...] │ ├──────────────────────────────────────────────┤ │ 2. LLM 看了用户指令后返回 │ │ 我要调用 get_stock_price(600519, 30) │ │ 不是文本是结构化的 tool_calls │ ├──────────────────────────────────────────────┤ │ 3. 你的代码执行这个函数拿到真实结果 │ │ result get_stock_price(600519, 30) │ ├──────────────────────────────────────────────┤ │ 4. 把结果作为 tool 消息塞回 messages │ │ LLM 看到结果决定下一步 │ └──────────────────────────────────────────────┘工具定义的格式OpenAI 兼容{type:function,function:{name:get_stock_price,description:查询指定股票的历史收盘价,parameters:{type:object,properties:{code:{type:string,description:股票代码如 600519},days:{type:integer,description:查询最近多少天的数据}},required:[code]}}}工程忠告description字段不是写给自己看的是 LLM 判断该不该调这个工具的唯一依据。写得越清楚LLM 选对工具的概率越高。一个 Java 封装的 Function Calling 调用示例publicclassLlmClient{// 调用 LLM带上可用工具列表publicLlmResponsechat(ListMessagemessages,ListToolDefinitiontools){ChatRequestrequestnewChatRequest();request.setModel(gpt-4o);request.setMessages(messages);request.setTools(tools);// 注册工具request.setTemperature(0.7);// 发送 HTTP 请求ChatResponserawhttpClient.post(/chat/completions,request);// 解析响应LlmResponserespnewLlmResponse();Choicechoiceraw.getChoices().get(0);if(choice.getMessage().getToolCalls()!null){// LLM 决定调用工具resp.setAction(Action.TOOL_CALL);resp.setToolCalls(choice.getMessage().getToolCalls());}else{// LLM 直接给出最终回答resp.setAction(Action.FINISH);resp.setContent(choice.getMessage().getContent());}returnresp;}}六、上下文窗口管理LLM 的上下文窗口是有限的4K ~ 200K tokens。Agent 跑着跑着messages 数组会越来越长最终撑爆窗口。┌─────────────── 上下文窗口假设 128K──────────────────┐ │ │ │ System Prompt固定约 2K │ │ ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │ │ │ 工具定义固定约 3K │ │ ████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │ │ │ 对话历史持续增长 │ │ ████████████████████████████████████████████░░░░░░ │ │ ↑ ↑ │ │ 第1轮 当前轮 │ │ Tool调用 Tool结果 Assistant回复每轮都在膨胀 │ │ │ │ 当前用户输入约 0.5K │ │ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │ │ └──────────────────────────────────────────────────────────┘三种管理策略策略做法适用场景截断只保留最近 N 轮对话简单任务历史信息不重要摘要压缩用 LLM 把旧对话总结成摘要长任务需要保留关键信息向量检索把历史存向量库按相关性取 top-K复杂 Agent精确召回工程建议先用截断不够用再上摘要最后才考虑向量检索。别一上来就搞 RAG很多场景截断就够了。一个简单的截断策略实现publicclassContextManager{privatestaticfinalintMAX_TOKENS120_000;// 留点余量privatestaticfinalintRESERVED_FOR_RESPONSE4_000;publicListMessagemanage(ListMessagemessages){inttotalTokensestimateTokens(messages);if(totalTokensMAX_TOKENS-RESERVED_FOR_RESPONSE){returnmessages;// 没超不用管}// 超了保留 System 最近 N 条MessagesystemMsgmessages.get(0);// System Prompt 一定保留ListMessagehistorymessages.subList(1,messages.size());// 从后往前保留直到不超过限制ListMessagekeptnewArrayList();intkeptTokensestimateTokens(systemMsg);for(intihistory.size()-1;i0;i--){intmsgTokensestimateTokens(history.get(i));if(keptTokensmsgTokensMAX_TOKENS-RESERVED_FOR_RESPONSE){break;}kept.add(0,history.get(i));keptTokensmsgTokens;}ListMessageresultnewArrayList();result.add(systemMsg);result.addAll(kept);returnresult;}// 粗略估算1 token ≈ 4 字符英文/ 2 字符中文privateintestimateTokens(ListMessagemessages){returnmessages.stream().mapToInt(m-estimateTokens(m)).sum();}privateintestimateTokens(Messagemsg){Stringtextmsg.getContent()!null?msg.getContent():;return(int)(text.length()*1.5)4;// 粗估}}七、关键参数调优参数作用Agent 建议聊天建议temperature控制随机性0确定1发散0 ~ 0.3要稳定别乱来0.7 ~ 1.0top_p核采样限制候选词范围0.9配合低 temperature1.0max_tokens最大输出长度够用就行别给太大2048stop停止序列可设/tool_call提前截断不用为什么 Agent 要用低 temperature因为 Agent 的输出是要被程序解析的。temperature 高了LLM 可能创意发挥输出的 JSON 格式不对、参数填错、甚至编造不存在的工具名。Agent 需要的是稳定可靠不是有创意。八、多模型路由不是每一步都要用最贵的模型。聪明的做法是按任务复杂度路由┌──────────────────────────────────────────────┐ │ 用户指令 │ └──────────────┬───────────────────────────────┘ ▼ ┌──────────────────────────────────────────────┐ │ 路由判断用小模型快速分类 │ │ 这个任务需要什么级别的推理 │ └──────┬───────────────┬───────────────────────┘ │ │ 简单任务 复杂任务 │ │ ▼ ▼ ┌─────────────┐ ┌──────────────┐ │ 小模型 │ │ 大模型 │ │ 快、便宜 │ │ 慢、贵、但聪明 │ │ 做简单路由 │ │ 做复杂推理 │ │ 做格式转换 │ │ 做多步规划 │ └─────────────┘ └──────────────┘步骤用什么模型理由意图识别/路由小模型二分类小模型够用参数提取小模型结构化提取小模型够用多步规划大模型需要推理能力结果总结小模型归纳总结小模型够用复杂分析大模型需要推理能力成本对比一个纯大模型方案每轮 0.03 美元加路由后混合方案每轮 0.008 美元省 73%。九、完整调用流程把上面的东西拼起来一个工程级的 LLM 调用流程publicclassAgentLlmClient{privatefinalLlmClientllm;// LLM 客户端privatefinalListToolDefinitiontools;// 工具定义privatefinalContextManagerctxManager;// 上下文管理privatefinalStringsystemPrompt;// System PromptpublicLlmResponsestep(ListMessagememory){// 1. 组装消息System 上下文管理后的历史ListMessagemessagesnewArrayList();messages.add(newSystemMessage(systemPrompt));messages.addAll(ctxManager.manage(memory));// 2. 调用 LLM带上工具定义LlmResponserespllm.chat(messages,tools);// 3. 判断 LLM 的决策if(resp.getAction()Action.TOOL_CALL){// LLM 要调工具returnresp;// 交给上层执行工具}else{// LLM 给出最终答案returnresp;// 交给上层返回给用户}}}对应的时序图用户指令 Agent LLM 工具 │ │ │ │ │──查600519──→│ │ │ │ │──messagestools→ │ │ │ │ │ │ │←──tool_call────│ │ │ │ │ │ │ │──get_price(600519,30)──────→│ │ │←─────────{prices:[...]}───────│ │ │ │ │ │ │──messages结果─→│ │ │ │←──tool_call────│ │ │ │ │ │ │ │──calc_ma(...)──→│ │ │ │←─────────{ma:...}─────────────│ │ │ │ │ │ │──messages结果─→│ │ │ │←──final_answer─│ │ │ │ │ │ │←──建议持有───│ │ │十、小结一篇讲清楚 LLM 调用 Prompt 工程核心要点消息角色分离System 设定行为User 下指令Assistant 回复Tool 返回结果System Prompt 是 Agent 的控制中心角色定义 行为规范 工具说明 约束规则Prompt 工程四板斧结构化、Few-shot、思维链、约束输出Function Calling 是 Agent 调工具的基石工具的 description 是 LLM 选工具的依据上下文窗口必须管理截断 → 摘要 → 向量检索按需升级低 temperature 多模型路由稳定性和成本的最佳平衡下一篇讲三大基石之二——记忆系统。Agent 怎么记住用户偏好、怎么处理长任务的中间状态、怎么跨会话持久化都会展开讲。这是「AI Agent 开发实战」系列第 2 篇后续会持续更新欢迎关注。如有错误或想法欢迎评论区交流。

相关新闻

建冷库这件事,让我重新理解了一站式服务的价值 当冷库不再只是“冷”,而是利润的守护者

建冷库这件事,让我重新理解了一站式服务的价值 当冷库不再只是“冷”,而是利润的守护者

在食品加工、生鲜零售甚至医药仓储领域,冷库早已不是简单的“冰柜放大版”。它关乎食材损耗、运营成本,甚至企业生死。然而,许多用户仍困于设备高耗能、安装无保障、售后响应慢的泥潭。博尔制冷凭借自研生产、工程安装、维保售后于一体的一站…

2026/10/13 16:26:57 阅读更多 →
八大排序代码

八大排序代码

冒泡排序交换排序希尔排序堆排序基数排序归并排序快速排序

2026/10/11 12:20:18 阅读更多 →
模型评估和模型选择

模型评估和模型选择

1、损失函数对于模型一次预测结果的好坏,需要有一个度量标准。对于监督学习而言,给定一个输入X,选取的模型就相当于一个“决策函数”f,它可以输出一个预测结果f(X),而真实的结果(标签)记为Y。f(…

2026/10/2 14:10:46 阅读更多 →

最新新闻

Hive substr中文乱码根源与5种实战解决方案

Hive substr中文乱码根源与5种实战解决方案

1. 项目概述:当Hive的substr遇上中文乱码,不是函数错了,是字符集在“说方言”你有没有遇到过这样的场景:一条日志字段里明明存着“用户_张三_20240516_订单完成”,用substr(col, 4, 6)想截出“张三”两个字&#xff0c…

2026/10/14 13:49:58 阅读更多 →
接口性能优化实战:从2秒到200ms的三层击破法

接口性能优化实战:从2秒到200ms的三层击破法

1. 这不是玄学,是可复现的接口性能优化实战“接口响应时间从2秒压到200毫秒以内”——这句话在很多技术团队的周会上被反复提起,听起来像一句目标口号,甚至带点理想主义色彩。但在我参与过的三个高并发业务系统重构中,它是一条必须…

2026/10/14 13:49:58 阅读更多 →
Hive substr中文乱码根源与字符安全截取方案

Hive substr中文乱码根源与字符安全截取方案

1. 项目概述:为什么一个看似简单的substr函数会突然在中文场景下“罢工”最近在某数据中台的日常ETL任务维护中,我连续三天被同一个报错堵在工位上:java.lang.StringIndexOutOfBoundsException: String index out of range,堆栈指…

2026/10/14 13:49:58 阅读更多 →
Ponytail 阅读进度条插件:滚动进度原理、接入与性能优化指南

Ponytail 阅读进度条插件:滚动进度原理、接入与性能优化指南

做内容站这几年,我一直有个执念:长文阅读体验不能只靠排版,还得给读者一点“进度反馈”。早先试过在文章顶部加一个手写的滚动进度条,但那种零散代码维护起来太麻烦,后来换成了开源的轻量级滚动进度插件,也…

2026/10/14 13:49:58 阅读更多 →
iOS容器底层探秘:NSMutableArray、NSDictionary、NSSet与NSCache深度解析

iOS容器底层探秘:NSMutableArray、NSDictionary、NSSet与NSCache深度解析

做iOS开发久了,数组、字典、集合这些容器天天在用,但真被人问一句NSMutableArray底层到底怎么扩容、NSDictionary凭什么查得那么快、NSCache和NSMutableDictionary除了线程安全还有什么本质区别、copy和mutableCopy的坑到底在哪,很多人会愣一…

2026/10/14 13:49:58 阅读更多 →
整车八自由度Simulink动力学模型搭建:从方程到仿真验证

整车八自由度Simulink动力学模型搭建:从方程到仿真验证

做整车动力学仿真的人,几乎都会经历从二自由度自行车模型起步,再逐步往多自由度模型走的过程。二自由度模型能把横摆和侧向运动的基本逻辑讲清楚,但一涉及到载荷转移、侧倾对轮荷分布的影响、四个车轮独立的转速动力学,它就完全不…

2026/10/14 13:48:57 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →