news 2026/9/30 10:15:44

Hermes模型+vLLM+Function Calling:生产级Agent实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hermes模型+vLLM+Function Calling:生产级Agent实战

1. 从模型选型到生产级智能体:为什么我最终选了 Hermes 这套组合

过去大半年,我一直在折腾 Agent 工程落地这件事。从最早的纯 Prompt 编排,到后面接 Function Calling,再到把模型换成 Hermes 系列、用 vLLM 做推理后端,中间踩的坑比写过的代码还多。这篇文章不讲概念,只讲我实际跑通的一套方案:Hermes 模型 + Function Calling + Python + vLLM,从选型逻辑一路讲到生产级智能体的完整代码实现。

先说清楚这套东西是干什么的。Hermes 在这里指的是具备强 Function Calling 能力的开源指令模型系列,它在工具调用、结构化输出、多轮对话上的表现,是我试过的开源模型里比较稳的一档。Agent 则是把这套模型能力包装成一个能自主决策、调用工具、维护记忆、循环执行任务的智能体。Function Calling 是模型和外部世界之间的那根“电话线”,Python 是胶水语言,vLLM 是让模型跑得快、扛得住并发的推理引擎。

这套方案适合谁?如果你已经会一点 Python,想从“调 API 玩一玩”进阶到“自己部署一个能上生产的 Agent”,那这篇就是给你写的。如果你是完全的新手,也没关系,我会把 Python 环境、vLLM 部署、Function Calling 的细节都拆开讲,保证你能跟着复现。整条链路我实测下来是稳的,下面把每个环节的取舍和代码都摊开说。

2. 整体架构设计与选型逻辑拆解

2.1 为什么是 Hermes,而不是随便找个模型

选模型这件事,很多人一上来就看榜单分数,这是最容易踩的坑。Agent 场景和聊天场景完全是两码事。聊天场景看的是语言流畅度和知识广度,Agent 场景看的是三件事:指令遵循、结构化输出稳定性、工具调用准确率。

我早期用一些通用对话模型做 Agent,最头疼的就是它“不听话”。你让它输出 JSON,它给你输出一段带解释的自然语言;你让它调用工具,它把参数拼错或者干脆自己编一个不存在的函数名。Hermes 系列在这方面的训练是专门强化过的,尤其是 Function Calling 的格式遵循,实测下来在同样的 Prompt 下,工具调用成功率明显更高。

另一个关键点是可控性。生产环境里,模型的行为必须是可预测的。Hermes 的指令微调风格比较“务实”,不太会自作主张地加戏,这对 Agent 的稳定性至关重要。你不需要它有多聪明,你需要它在该调用工具的时候老老实实调用,在该返回结果的时候干净利落地返回。

2.2 vLLM 作为推理后端,解决的是什么问题

模型选好了,怎么跑起来是第二个大问题。直接用 transformers 库加载模型,单条推理还行,一旦并发上来,显存和吞吐立刻崩。vLLM 的核心价值在于PagedAttention和连续批处理(Continuous Batching)。

打个比方,传统推理就像餐厅一次只能服务一桌客人,这桌吃完才接待下一桌;vLLM 的连续批处理则是流水席,谁吃完了立刻补新客人进来,GPU 利用率能拉满。PagedAttention 则是把显存里的 KV Cache 像操作系统管理内存分页一样管理,避免了大量碎片浪费。实测下来,同样的显卡,vLLM 的吞吐能比朴素实现高出好几倍,这对 Agent 这种需要频繁多轮调用的场景是刚需。

而且 vLLM 提供了 OpenAI 兼容的 API 接口,这意味着你的 Agent 代码可以用标准的 OpenAI SDK 去调用本地模型,迁移成本极低。这一点非常关键,后面代码里你会看到,我几乎没改多少东西就把云端调用切成了本地部署。

2.3 Function Calling 在 Agent 里的角色定位

很多人把 Function Calling 理解成“让模型调用函数”,这个理解太浅了。Function Calling 的本质是给模型一套结构化的行动空间。模型本身不能执行任何操作,它只能输出文本。Function Calling 做的事情,是把“我想查天气”这种意图,翻译成{"name": "get_weather", "arguments": {"city": "北京"}}这种机器能执行的指令。

在 Agent 循环里,Function Calling 是决策和执行的分界线。模型负责决策(选哪个工具、传什么参数),你的 Python 代码负责执行(真正去调 API、查数据库、发请求),然后把结果喂回给模型,让它继续决策。这个循环就是 Agent 的心跳。

设计工具集的时候有个原则:工具要原子化,不要贪大。我见过有人设计一个do_everything的工具,参数一大堆,结果模型根本用不明白。正确的做法是把每个能力拆成独立的小工具,参数尽量少、类型尽量简单,让模型容易理解和选择。

2.4 整体数据流长什么样

把上面几块拼起来,整个 Agent 的数据流是这样的:用户输入进来,Python 层把系统提示词、历史对话、可用工具列表一起打包成请求,发给 vLLM 上的 Hermes 模型。模型返回一个响应,如果响应里包含工具调用,Python 层就解析出来、执行对应函数、把结果作为一条新消息追加到对话历史里,再次请求模型。这个循环一直持续到模型返回纯文本的最终答案为止。

这个循环里有两个容易出问题的地方:一是循环终止条件,必须有最大轮次限制,否则模型可能陷入死循环;二是上下文长度管理,多轮工具调用会让对话历史迅速膨胀,需要做截断或摘要。这两点后面会详细讲。

3. 环境搭建与 vLLM 部署实操

3.1 Python 环境准备,别在这步浪费时间

Python 环境这块,我的建议是直接用 conda 或者 venv 建独立环境,别在系统 Python 上瞎装。版本选 3.10 或 3.11,这两个版本对主流库的兼容性最好。3.12 有些库还没跟上,容易出幺蛾子。

conda create -n agent python=3.11 -y conda activate agent

装依赖的时候,核心就几个:openai(用它的 SDK 调 vLLM 的兼容接口)、requests、pydantic(做参数校验)。如果你要做量化交易或者爬虫类的工具,再按需装pandas、sklearn、beautifulsoup4这些。

pip install openai requests pydantic

提示:pip 装包慢的话,换个国内镜像源,别硬等。但注意别把镜像源配到全局,容易污染其他项目。

3.2 vLLM 部署 Hermes 模型,Docker 是最省心的路子

vLLM 的安装方式有两种:pip 直接装和 Docker。我强烈建议用 Docker,尤其是你要上生产的时候。原因很简单,vLLM 对 CUDA 版本、PyTorch 版本、驱动版本非常敏感,pip 装很容易出现版本冲突,Docker 镜像把这些依赖都锁死了,省心。

拉镜像的时候注意版本号,不同版本的 vLLM 对模型的支持不一样。我用的镜像是vllm/vllm-openai系列,这个镜像自带 OpenAI 兼容的 API server,启动即用。

docker run --gpus all \ -v /path/to/models:/models \ -p 8000:8000 \ --shm-size 16g \ vllm/vllm-openai:latest \ --model /models/Hermes-xxx \ --served-model-name hermes \ --max-model-len 8192 \ --gpu-memory-utilization 0.9

这里几个参数值得说道说道。--max-model-len控制最大上下文长度,设太大显存扛不住,设太小多轮对话会截断,8192 是个比较平衡的值。--gpu-memory-utilization 0.9表示让 vLLM 用 90% 的显存,留一点给系统,设成 1.0 有时候会 OOM。--shm-size是共享内存,Docker 默认的 64M 太小,多进程推理会崩,给到 16G 比较稳。

注意:Docker 镜像本身不带模型权重,模型文件要自己下载好挂载进去。别指望镜像里啥都有,那是想多了。

3.3 验证服务是否正常

服务起来之后,先用 curl 测一下,别急着写 Agent 代码。

curl http://localhost:8000/v1/models

能返回模型列表就说明服务正常。然后再测一下对话接口:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "hermes", "messages": [{"role": "user", "content": "你好"}] }'

如果这两步都通了,说明推理后端没问题,可以进入 Agent 开发了。如果报错,八成是显存不够或者模型路径不对,先看日志。

3.4 显存不够怎么办,几个实用的降级方案

不是每个人都有 A100。如果你显存紧张,有几个办法。第一是量化,用 AWQ 或 GPTQ 量化版本的模型,显存占用能降到原来的三分之一左右,精度损失在可接受范围内。第二是调小--max-model-len,上下文长度和显存是线性关系。第三是--tensor-parallel-size多卡并行,但这个需要多张卡。

我实测下来,一张 24G 的卡跑 7B 级别的 Hermes 模型,用 AWQ 量化,max-model-len设 8192,是比较舒服的配置,能扛住一定并发。

4. Function Calling 与 Agent 核心代码实现

4.1 工具定义:把能力描述清楚是成功的一半

工具定义用的是 JSON Schema 格式,这是 OpenAI 兼容接口的标准。每个工具要有名字、描述、参数定义。描述这块千万别偷懒,模型就是靠描述来判断该不该用这个工具的。

tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的实时天气。当用户询问天气相关问题时使用。", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:北京、上海" } }, "required": ["city"] } } } ]

描述里我特意写了“当用户询问天气相关问题时使用”,这是在给模型划边界。工具描述写得越明确,模型误用的概率越低。参数描述也要写清楚格式,比如城市名称给个例子,模型就知道该怎么填。

4.2 工具执行层:真正干活的地方

模型只负责说“我要调 get_weather”,真正执行的是你的 Python 代码。这一层要做两件事:把模型给的参数解析出来,调用对应的函数,然后把结果返回。

import json def execute_tool(tool_call): name = tool_call.function.name args = json.loads(tool_call.function.arguments) if name == "get_weather": return get_weather(**args) else: return json.dumps({"error": f"未知工具: {name}"}) def get_weather(city): # 实际项目里这里调真实天气 API return json.dumps({"city": city, "temp": "25°C", "condition": "晴"})

这里有个坑要注意:模型返回的arguments是字符串,不是字典,必须json.loads一下。而且模型偶尔会返回不合法的 JSON,所以最好加个 try-except 兜底,解析失败就返回错误信息让模型重试。

4.3 Agent 主循环:心跳逻辑

这是整个 Agent 的核心。逻辑不复杂,就是不断地请求模型、执行工具、把结果喂回去,直到模型不再调用工具为止。

from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY") def run_agent(user_input, max_turns=10): messages = [ {"role": "system", "content": "你是一个智能助手,可以调用工具来完成任务。"}, {"role": "user", "content": user_input} ] for turn in range(max_turns): response = client.chat.completions.create( model="hermes", messages=messages, tools=tools, tool_choice="auto" ) msg = response.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for tool_call in msg.tool_calls: result = execute_tool(tool_call) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) return "达到最大轮次限制,任务未完成"

max_turns这个参数是保命的,没有它模型可能无限循环。tool_choice="auto"表示让模型自己决定要不要调工具,你也可以设成强制调用某个工具。

4.4 多轮对话与上下文管理

生产环境里,对话不是一次性的,用户会连续提问。这时候上下文管理就很重要了。最简单的做法是把历史消息都存着,但这样上下文会越来越长,最后超出max-model-len。

我的做法是保留最近 N 轮对话,加上一个系统级的摘要。具体实现是维护一个消息列表,超过阈值就把最早的消息压缩成一句摘要。这个摘要可以让模型自己生成,也可以简单粗暴地截断。

def trim_messages(messages, max_messages=20): if len(messages) <= max_messages: return messages system = messages[0] recent = messages[-(max_messages - 1):] return [system] + recent

这个截断策略虽然简单,但实测够用。更精细的做法是做语义摘要,但那是另一个话题了。

4.5 错误处理与重试机制

模型调用不是 100% 可靠的,网络会抖、服务会重启、模型偶尔会抽风。所以重试机制必须有。

import time def call_with_retry(func, max_retries=3): for i in range(max_retries): try: return func() except Exception as e: if i == max_retries - 1: raise time.sleep(2 ** i)

指数退避是个好习惯,第一次等 1 秒,第二次等 2 秒,第三次等 4 秒。别用固定间隔,容易把服务打崩。

5. 生产级优化与常见问题排查

5.1 性能优化:让 Agent 跑得更快更稳

Agent 的性能瓶颈通常在两个地方:模型推理和工具执行。模型推理这块,vLLM 已经帮你优化得差不多了,你能做的是控制请求的并发数,别一次性发太多把服务压垮。工具执行这块,如果工具有网络请求,一定要设超时,否则一个慢工具会拖垮整个循环。

还有个容易被忽略的点是流式输出。Agent 的最终答案如果等全部生成完再返回,用户体感会很差。vLLM 支持流式,你可以在最后一步用stream=True把结果一点点吐给用户。

5.2 常见问题速查表

问题现象可能原因排查方向
模型不调用工具工具描述不清 / tool_choice 设置问题检查工具描述,尝试强制 tool_choice
工具参数解析失败模型返回非法 JSON加 try-except,返回错误让模型重试
服务启动 OOM显存不足 / max-model-len 太大降量化、调小上下文、检查显存占用
响应特别慢并发过高 / 工具阻塞限流、给工具加超时
循环不终止缺少 max_turns强制加轮次上限
上下文超长报错历史消息堆积做截断或摘要

5.3 几个我踩过的坑

第一个坑是工具描述里的歧义。我写过一个search工具,描述是“搜索信息”,结果模型什么鸡毛蒜皮的事都去调它。后来改成“搜索互联网上的实时信息,当需要最新数据时使用”,误用率立刻降下来了。

第二个坑是参数类型不匹配。模型有时候会把数字传成字符串,比如{"count": "5"}而不是{"count": 5}。解决办法是在工具执行层做类型转换,或者在参数描述里强调类型。

第三个坑是vLLM 版本和模型不匹配。有次我换了个新模型,怎么都加载不了,折腾半天发现是 vLLM 版本太老不支持那个模型的架构。所以换模型前先查一下 vLLM 的版本兼容性。

5.4 安全与边界控制

Agent 能调用工具,就意味着它能对真实世界产生影响。所以边界控制必须做。我的做法是给工具分级,只读类工具(查询、搜索)随便调,写入类工具(发邮件、改数据)要加确认机制。另外,工具的参数要做校验,别让模型传个奇怪的参数把你的数据库删了。

注意:永远不要给 Agent 无限制的系统权限。最小权限原则在 Agent 场景里同样适用。

6. 从能跑到好用:我的几点实战体会

这套方案我从原型跑到生产,前后迭代了十几个版本。最大的体会是,Agent 的难点从来不在模型本身,而在工程细节。模型选型、推理部署、工具设计、错误处理、上下文管理,每一环都有坑,每一环都需要打磨。

Hermes 加 vLLM 这个组合,在我看来是目前开源方案里性价比很高的一套。Hermes 的工具调用能力够用,vLLM 的吞吐和稳定性经过验证,Python 生态让工具开发几乎没有门槛。如果你正准备做 Agent 项目,我建议就从这套开始,先把主循环跑通,再逐步加工具、加记忆、加优化。

最后分享一个小技巧:调试 Agent 的时候,把每一轮的完整消息历史打印出来,包括模型的原始响应。很多时候问题就藏在那些你没注意到的细节里,比如模型返回了一个空 content 但带了 tool_calls,或者工具返回的结果格式不对导致模型理解偏差。把这些看清楚,大部分问题都能自己解决。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 10:14:05

攻防演练防守报告怎么写?七步复盘模板与实战避坑指南

简介&#xff1a;针对2023年攻防演练场景的防守报告模板&#xff0c;面向蓝队、安全运维人员及企业安全部门&#xff0c;用于规范撰写攻防演练事件复盘与防守总结&#xff0c;也可作为安全应急响应报告的范文或素材库。模板按真实攻击流程组织&#xff0c;完整覆盖事件概述、钓…

作者头像 李华
网站建设 2026/9/30 10:14:04

6+1+3混合模型×四层智能体架构:企业级AI安全编排实战

先说个我在设计这套体系时的切身感受&#xff1a;每次跟人聊"AI 模型完整体系"&#xff0c;大家的第一反应都是"又要堆大模型了"&#xff0c;但真正落到生产环境&#xff0c;你会发现问题的关键从来不是某一个模型的聪明程度&#xff0c;而是 一堆模型怎么…

作者头像 李华
网站建设 2026/9/30 10:13:46

10分钟给Coding Agent装上决策脑:Jev与Skill机制实战指南

1. 为什么 Coding Agent 需要“自己拿主意”的能力 1.1 从“工具调用”到“自主决策”的认知升级 用过 Claude Code 或者 Codex 的朋友应该都有体会&#xff0c;这两个命令行 Coding Agent 在代码生成、文件读写、命令执行这些基础能力上已经相当能打了。但实际用下来你会发现…

作者头像 李华
网站建设 2026/9/30 10:13:46

TRAE Work实战:搭建公众号日更流水线,从2小时到15分钟

1. 这个项目到底做了什么&#xff1a;把"写公众号"从苦力活变成流水线先交代下背景。我做公众号日更已经大半年了&#xff0c;一开始是兴致勃勃&#xff0c;日更两周后就开始怀疑人生——每天下班后打开文档&#xff0c;对着空白页发呆一小时&#xff0c;好不容易憋出…

作者头像 李华
网站建设 2026/9/30 10:13:46

Jev AI决策系统架构解析:从概念到生产环境的四层流水线设计

1. 从概念到生产&#xff1a;Jev AI决策系统的架构全景与设计哲学第一次看到“Jev”这个词是在一个技术群里的讨论&#xff0c;有人提到“Jev模型在Codex里的表现比预期好很多”&#xff0c;当时我第一反应是又一个新出的AI编程助手。后来花了两周时间把Jev从概念文档到可运行D…

作者头像 李华
网站建设 2026/9/30 10:13:14

三进制模型Bonsai 2让16GB显卡流畅运行27B大模型

先说结论&#xff1a;一张 16GB 的显卡确实能跑 27B 量级的大模型&#xff0c;但不是靠传统的 Q4_K_M 硬压&#xff0c;而是靠三进制模型 Bonsai 2 这种把权重逼到 -1/0/1 的做法。我这次把 Bonsai 2 27B 的 PQ2_0 和 PTQ1_0 两个 GGUF 版本都下载下来&#xff0c;在 RTX 4070 …

作者头像 李华