优化 Agent 的响应速度和成本控制——上下文窗口管理策略、模型选择与性价比对比、缓存机制、批量处理优化。让 Agent 既聪明又省钱。
🎯 学习目标
- 理解大型语言模型 (LLM) 的 Token 经济学原理,以及它对 Agent 成本和性能的影响。
- 掌握 Hermes Agent 的上下文窗口 (Context Window) 管理策略,学会如何高效利用和优化。
- 学会根据任务需求和预算,通过 OpenRouter 平台选择最适合的 LLM 模型。
- 探索 Hermes Agent 中潜在的缓存机制,以及如何利用它来减少重复计算和 API 调用。
- 了解批量处理和异步优化在提升 Agent 吞吐量方面的应用。
📖 核心概念讲解
### 16.1 Token 经济学与上下文窗口的艺术
在大型语言模型 (LLM) 的世界中,"Token" 是信息处理的基本单位。无论是用户输入的提示 (Prompt),还是模型生成的响应 (Completion),所有文本都会被分解成 Token。一个 Token 通常对应英文单词的一部分、一个标点符号或一个汉字。理解 Token 经济学至关重要,因为它直接影响 Agent 的运行成本、响应速度以及处理复杂任务的能力。
Token 成本与计费: 大多数 LLM 提供商(如 OpenAI、Anthropic、Google 等)都根据 Token 数量进行计费,通常是 "每输入 1k Token" 和 "每输出 1k Token" 独立计费。这意味着更长的提示和更长的响应都会增加成本。对于 Hermes Agent 而言,通过 OpenRouter 聚合了 200+ 模型,每个模型有其独特的 Token 计费标准,这使得成本优化成为一个持续的挑战。
上下文窗口 (Context Window): 每个 LLM 都有一个固定的上下文窗口大小,这指的是模型在单次调用中能够处理的最大 Token 数量(输入提示 + 输出响应)。例如,GPT-3.5-turbo 可能有 4k 或 16k 的上下文窗口,而 GPT-4-turbo 则可能达到 128k。超出这个限制的文本将被截断,模型将无法“看到”这部分信息。上下文窗口的大小直接决定了 Agent 能记住多长的对话历史、能处理多大的文档、以及能执行多复杂的指令。
上下文窗口管理策略: 由于上下文窗口的限制和 Token 成本的考虑,我们需要精心管理 Agent 的上下文。Hermes Agent 作为自进化的 AI 代理,其核心任务之一就是有效地利用和管理上下文。
精简提示 (Prompt Engineering): 最直接的方式是确保发送给 LLM 的提示尽可能简洁明了,避免冗余信息。在 [第 03 期 | Skills 系统深度剖析] 中,我们学习了如何编写高效的 Skill,其中就包含了构建清晰、精炼的提示。
# 示例:一个精简的 Skill 提示 @tool def summarize_text(self, text: str) -> str: """Summarizes the given text concisely.""" return self.llm(f"请将以下文本精简为一段摘要:\n\n{text}")相较于一个包含大量无关背景信息的提示,精简的提示可以显著减少输入 Token。
摘要与压缩 (Summarization & Compression): 对于长篇对话历史或大型文档,直接将其全部放入上下文窗口既昂贵又容易超出限制。这时,摘要是关键技术。在 [第 04 期 | Memory 与用户画像] 中,我们讨论了 Agent 如何维护跨会话记忆。Hermes Agent 可以配置在将历史对话注入上下文之前,先对其进行摘要。
# Hermes Agent 配置文件 (config.yaml 示例) llm: # ... 其他 LLM 配置 context_strategy: type: "summary" # 可以配置为 "truncation" 或 "summary" max_tokens: 4000 # 限制上下文总 Token 数 summary_model: "gpt-3.5-turbo" # 用于摘要的便宜模型通过设置
context_strategy为summary,Agent 会在需要时调用一个专门的、通常更便宜的模型对历史对话或相关文档进行摘要,然后将摘要注入主模型的上下文。上下文截断 (Context Truncation): 当上下文内容(包括提示、记忆、工具输出等)的总 Token 数超过模型限制时,最简单的处理方式是截断。Hermes Agent 允许你配置截断策略,例如保留最新 N 个 Token,或优先保留指令部分。虽然简单,但可能导致信息丢失。
llm: context_strategy: type: "truncation" # 直接截断 max_tokens: 4000 # 保持在 4000 Token 内 # truncation_priority: "latest" # 优先保留最新信息,或 "instruction" 优先保留指令滑动窗口 (Sliding Window): 这是一种更高级的上下文管理技术,尤其适用于长对话。Agent 维护一个固定大小的“窗口”,只将窗口内的最新对话和关键信息发送给 LLM。当新对话进来时,最旧的信息会被“滑出”窗口。这确保了模型始终关注最新的交互,同时避免了上下文无限增长。Hermes Agent 的 Memory 模块可能内置了类似的机制来管理对话历史。
检索增强生成 (RAG - Retrieval Augmented Generation): 在 [第 13 期 | 知识库构建:RAG 与长期文档管理] 中,我们深入探讨了 RAG。RAG 是一种通过从外部知识库中检索相关信息,然后将其作为上下文注入 LLM 的技术。这种方法避免了将整个知识库塞入上下文,从而大大节省了 Token 成本,并提高了相关性和准确性。
# 示例:一个结合 RAG 的 Skill @tool def answer_from_docs(self, query: str) -> str: """Retrieves relevant information from the knowledge base and answers the query.""" relevant_chunks = self.memory.retrieve_relevant_documents(query, top_k=3) # 假设 Agent 内置了 RAG 能力 context = "\n".join([chunk.text for chunk in relevant_chunks]) return self.llm(f"基于以下信息回答问题:\n\n信息:{context}\n\n问题:{query}")RAG 的核心思想是只将“需要”的上下文注入,而不是“所有”可能的上下文。这是一种非常高效的 Token 优化策略。
通过这些策略,我们可以有效地在 Agent 的智能、响应速度和运行成本之间找到最佳平衡点。
### 16.2 模型选择与性价比优化
Hermes Agent 的一个强大特性是其通过 OpenRouter 支持 200+ LLM 模型。这为我们提供了巨大的灵活性,但也带来了选择困难。不同的模型在性能、成本和速度上差异巨大。明智的模型选择是实现性能调优和 Token 经济学的关键。
OpenRouter 的角色: OpenRouter (https://openrouter.ai/) 作为一个聚合平台,统一了不同 LLM 提供商的 API 接口,并提供了详细的模型成本、速率限制等信息。Hermes Agent 利用 OpenRouter 作为其 LLM Provider,使得用户可以轻松地在不同模型之间切换,而无需修改底层代码。在 [第 02 期 | 模型切换与 Provider 配置实战] 中,我们已经详细介绍了如何配置和切换模型。
模型选择的考量因素:
任务复杂度:
- 简单任务 (Simple Tasks): 例如文本分类、情感分析、简单的信息提取或生成短文本。对于这类任务,可以选择成本较低、速度较快的模型,如
gpt-3.5-turbo、mixtral-8x7b-instruct或一些开源的小型模型。 - 复杂任务 (Complex Tasks): 例如多步推理、代码生成、创意写作、长篇内容生成或需要高精度理解的场景。这类任务通常需要更强大的模型,如
gpt-4-turbo、claude-3-opus等,尽管它们的成本更高。
- 简单任务 (Simple Tasks): 例如文本分类、情感分析、简单的信息提取或生成短文本。对于这类任务,可以选择成本较低、速度较快的模型,如
成本 (Cost): 这是最直接的经济学指标。不同模型的 Token 价格差异巨大。例如,GPT-4-turbo 的价格可能是 GPT-3.5-turbo 的数倍甚至数十倍。在选择模型时,务必查阅 OpenRouter 上的最新价格信息。
速度 (Speed / Latency): 模型的响应速度会直接影响 Agent 的用户体验。对于需要实时交互的应用,选择响应快的模型至关重要。通常,较小的模型或经过优化的模型(如某些专业微调模型)会更快。
上下文窗口大小 (Context Window Size): 如果任务需要处理大量信息(如总结长文档、分析大型代码库),则需要选择具有更大上下文窗口的模型。但这通常也意味着更高的成本。
性价比对比 (Cost-Performance Comparison): 没有一个“最佳”的模型,只有“最适合”的模型。我们需要在成本、性能和速度之间找到平衡点。
| 模型名称 (OpenRouter ID) | 典型能力 | 成本 (参考,$/1M Token) | 速度 (参考) | 上下文窗口 (Token) | 适用场景 |
|---|---|---|---|---|---|
gpt-4-turbo |
顶级推理、代码生成 | ~10 (输入), ~30 (输出) | 中等 | 128k | 复杂规划、代码、创意、高精度问答 |
gpt-3.5-turbo |
快速、通用、高性价比 | ~0.5 (输入), ~1.5 (输出) | 较快 | 16k | 简单问答、摘要、文本生成、入门级任务 |
claude-3-opus |
顶级推理、长文本理解 | ~15 (输入), ~75 (输出) | 中等 | 200k | 复杂分析、长文档处理、企业级应用 |
claude-3-sonnet |
平衡型、中等推理 | ~3 (输入), ~15 (输出) | 较快 | 200k | 通用任务、中等复杂度、长文本摘要 |
mixtral-8x7b-instruct |
优秀推理、多语言 | ~0.24 (输入), ~0.24 (输出) | 较快 | 32k | 开源替代、中等复杂度、成本敏感型任务 |
llama-3-8b-instruct |
快速、小巧、开源 | ~0.05 (输入), ~0.05 (输出) | 极快 | 8k | 简单任务、本地部署、对速度要求高 |
注意:上述成本和速度数据为示例,实际值请以 OpenRouter 官网为准,且可能随时间变化。
在 Hermes Agent 中配置模型:
你可以通过 hermes config set 命令全局设置默认模型,也可以在特定的 Skill 中覆盖默认模型。
# 全局设置默认模型为 gpt-3.5-turbo
hermes config set llm.default_model gpt-3.5-turbo
# 检查当前配置
hermes config get llm.default_model
# 在 Skill 中指定模型 (假设 Skill 支持 llm_model 参数)
# from hermes_agent.skill_library.tools import tool
# @tool
# def complex_analysis(self, data: str) -> str:
# """Performs complex analysis using a powerful model."""
# # 可以在 Skill 内部通过 self.llm 传递 model 参数
# return self.llm(f"请对以下数据进行深度分析: {data}", model="gpt-4-turbo")
在自定义 Skill 中,你可以根据任务的复杂度和重要性,动态选择使用的 LLM 模型。例如,一个用于生成代码的 Skill 可能会默认使用 gpt-4-turbo,而一个用于简单问答的 Skill 则使用 gpt-3.5-turbo。这种策略称为“模型路由”或“专家模型系统”,是实现高性价比 Agent 的关键。
### 16.3 缓存机制与重复计算规避
在 Agent 的运行过程中,很多计算可能是重复的,或者某些 LLM 调用的结果在短时间内是稳定的。利用缓存机制可以显著减少 API 调用次数,降低成本,并提高响应速度。
为什么需要缓存?
- 降低成本: 每次 LLM API 调用都需要付费。缓存可以避免重复支付。
- 提升速度: 从缓存中读取数据远比等待 LLM 响应快。
- 减少 API 速率限制: 频繁的 API 调用可能触发提供商的速率限制,导致请求失败或延迟。缓存可以缓解这一压力。
Hermes Agent 的缓存考量: Hermes Agent 的设计哲学是自进化和学习。虽然官方文档没有明确指出一个通用的、可配置的“LLM 响应缓存”模块,但其核心组件如 Memory 和 Skills 系统本身就包含了类似缓存的思想。
Memory (记忆) 模块的“缓存”效应: 在 [第 04 期 | Memory 与用户画像] 中,我们知道 Hermes Agent 会存储对话历史、学习到的 Skill 定义、用户画像等。当 Agent 需要这些信息时,它会从本地存储(例如 SQLite 数据库)中读取,而不是每次都向 LLM 询问。这本身就是一种“记忆缓存”,避免了重复生成或检索这些核心数据。
- 长期记忆 (Long-Term Memory): 存储学习到的 Skill、用户画像等,这些信息一旦存储,就可以在多次会话中重复使用。
- 短期记忆 (Short-Term Memory): 存储当前会话的对话历史。虽然会随时间增长,但其读取通常是本地的。
Skill 结果的潜在缓存: 对于某些确定性强、计算成本高昂且结果相对稳定的 Skill,开发者可以在 Skill 内部实现缓存逻辑。例如,一个 Skill 可能需要查询一个外部 API,或者执行一个复杂的计算。如果输入参数相同,其结果很可能也是相同的。
import functools from hermes_agent.skill_library.tools import tool # 使用 Python 的 functools.lru_cache 实现简单的内存缓存 @tool @functools.lru_cache(maxsize=128) # 缓存最近 128 个不同输入的计算结果 def expensive_api_call(self, query: str) -> dict: """ Performs an expensive API call and caches its result. This skill simulates calling an external service that might be slow or rate-limited. """ print(f"Executing expensive_api_call for query: '{query}' (not from cache)") # 模拟耗时操作 import time time.sleep(2) # 模拟 API 响应 return {"query": query, "result": f"Data for {query} processed at {time.time()}"} # 实际调用时,第二次相同 query 会直接返回缓存结果 # agent.run("expensive_api_call", query="report_q1_2024") # agent.run("expensive_api_call", query="report_q1_2024") # 这一次会更快这种
lru_cache适用于单个 Agent 实例的内存缓存。如果 Agent 部署在多个实例中,或者需要持久化缓存,则需要考虑使用 Redis、Memcached 或数据库等外部缓存系统。LLM 响应缓存 (External LLM Caching): 虽然 Hermes Agent 本身可能没有内置一个全局的 LLM 响应缓存,但你可以通过代理层或自定义
LLMProvider来实现。例如,可以使用LiteLLM或LangChain等库提供的缓存功能,它们可以集成到 Hermes Agent 的llm调用路径中。graph TD A[Hermes Agent] --> B{LLM Call} B --> C{Cache Check} C -- Cache Hit --> D[Return Cached Response] C -- Cache Miss --> E[Call OpenRouter/LLM API] E --> F[LLM Response] F --> G[Store in Cache] G --> D上图展示了一个典型的 LLM 缓存流程。当 Hermes Agent 发起一个 LLM 调用时,首先检查缓存。如果命中,则直接返回;如果未命中,则调用实际的 LLM API,并将结果存入缓存以备后用。
缓存策略:
- 缓存键 (Cache Key): 通常由 Prompt 和其他 LLM 参数(如模型名称、温度等)共同构成。确保缓存键能够唯一标识一个 LLM 请求。
- 过期策略 (Expiration Policy): 缓存不能无限期存储。可以设置 TTL (Time-To-Live),让缓存项在一定时间后失效。对于 LLM 响应,通常可以设置较短的 TTL (例如几分钟到几小时),因为模型输出可能随时间或模型更新而变化。
- 缓存一致性 (Cache Consistency): 当底层数据(例如知识库或 Skill 定义)发生变化时,需要确保缓存的数据能够及时更新或失效。
通过合理利用缓存,Agent 可以在保持智能的同时,显著提升运行效率并控制成本。
### 16.4 批量处理与异步优化
在某些场景下,Agent 可能需要执行一系列相似但不相关的任务,或者需要并行处理多个请求。批量处理 (Batch Processing) 和异步优化 (Asynchronous Optimization) 是提高 Agent 吞吐量和响应效率的有效手段。
批量处理 (Batch Processing): 批量处理是指将多个独立的请求或任务打包成一个批次,然后一次性地发送给 LLM 或其他外部服务进行处理。这在以下情况下特别有用:
- 降低 API 开销: 许多 LLM API 对于单次调用的固定开销(如网络延迟、API 网关处理时间)相对较高。将多个请求打包可以分摊这些固定开销。
- 提高吞吐量: 对于可以并行处理的任务,批量处理可以显著缩短总处理时间。
- 适用于数据预处理/后处理: 例如,一次性对多个文本进行摘要、分类或实体提取。
Hermes Agent 中的批量处理: 虽然 Hermes Agent 的核心交互是基于单次对话和推理,但你可以在自定义 Skill 中实现批量处理逻辑。
from typing import List
from hermes_agent.skill_library.tools import tool
@tool
def batch_summarize(self, texts: List[str]) -> List[str]:
"""
Summarizes a list of texts in a single LLM call if possible,
or processes them in batches to optimize.
"""
# 假设 LLM 支持批量输入,或者我们手动构建批量请求
# 注意:大多数 LLM 的 API 接口通常接受单个 Prompt,但可以通过构建一个包含多个子任务的 Prompt 来模拟批量处理。
# 或者,更常见的是在 Skill 内部循环调用 LLM,但配合异步优化。
# 策略一:构建一个大 Prompt(适用于短文本,且总 Token 不超限)
if sum(len(text.split()) for text in texts) < self.llm.model_max_tokens * 0.5: # 估算 Token
combined_prompt = "请对以下每段文本分别进行摘要,并用数字列表返回:\n"
for i, text in enumerate(texts):
combined_prompt += f"{i+1}. {text}\n"
response = self.llm(combined_prompt)
# 解析 response,提取每个摘要
# ...
return ["Summary 1", "Summary 2"] # 示例
# 策略二:如果文本过长,或者 LLM 不适合一次处理多个,则逐个处理 (可以结合异步)
summaries = []
for text in texts:
summary = self.llm(f"请对以下文本进行摘要:{text}")
summaries.append(summary)
return summaries
在实际应用中,如果 LLM API 本身提供了批量推理的端点(例如某些模型微调服务),那么直接调用这些端点会更高效。否则,通过构建一个包含多个子任务的大 Prompt 是一种常见的模拟批量处理的方式,但要小心上下文窗口限制。
异步优化 (Asynchronous Optimization): 异步编程允许程序在等待某个耗时操作(如网络请求、磁盘 I/O)完成时,去执行其他任务,而不是阻塞整个程序。对于 Hermes Agent 这种需要频繁与外部服务(LLM API、外部工具 API)交互的应用来说,异步优化至关重要。
Python 的 asyncio 库是实现异步编程的核心。当 Agent 需要同时执行多个独立的 LLM 调用或工具调用时,异步可以大大提高效率。
graph TD
A[Agent Request] --> B{Task 1}
A --> C{Task 2}
A --> D{Task 3}
B -- Async Call --> B1[LLM API 1]
C -- Async Call --> C1[LLM API 2]
D -- Async Call --> D1[Tool API 1]
B1 --> B2[Result 1]
C1 --> C2[Result 2]
D1 --> D2[Result 3]
B2 & C2 & D2 --> E[Aggregate Results]
E --> F[Agent Response]上图展示了三个任务如何通过异步调用并行执行,最终聚合结果。
在 Hermes Agent 中使用异步:
Hermes Agent 的底层框架(如 langchain 或 crewai 的某些组件)通常已经支持异步操作。在编写自定义 Skill 时,你可以利用 async def 和 await 关键字来创建异步 Skill。
import asyncio
from typing import List
from hermes_agent.skill_library.tools import tool
# 假设你的 LLM 客户端支持异步调用 (例如,OpenAI 的 async client)
# self.llm 通常是同步接口,但我们可以模拟异步执行
# 实际 Hermes Agent 的 llm 接口可能需要自行封装为异步
async def async_llm_call(llm_instance, prompt: str, model: str) -> str:
"""Simulates an async LLM call."""
# 实际这里会调用 llm_instance.async_invoke(prompt, model=model) 或类似的异步方法
await asyncio.sleep(1) # 模拟网络延迟和 LLM 处理时间
return f"Async response for: {prompt[:30]}..."
@tool
async def process_multiple_queries_async(self, queries: List[str]) -> List[str]:
"""
Processes multiple queries asynchronously using the LLM.
"""
print(f"Processing {len(queries)} queries asynchronously...")
tasks = []
for query in queries:
# 假设 self.llm 可以通过某种方式进行异步调用
# 实际使用时,需要确保 self.llm 背后是支持异步的客户端,例如 openai.AsyncOpenAI
# 或者使用一个包装器
tasks.append(async_llm_call(self.llm, f"请回答:{query}", model=self.llm.default_model))
results = await asyncio.gather(*tasks) # 并行等待所有任务完成
return results
# 调用方式 (需要在一个异步上下文中)
# async def main():
# agent = HermesAgent(...) # 假设 agent 实例
# skill_instance = agent.get_skill("process_multiple_queries_async")
# responses = await skill_instance.process_multiple_queries_async(queries=["What is AI?", "What is ML?", "What is DL?"])
# print(responses)
# asyncio.run(main())
通过异步优化,Agent 可以同时处理多个用户请求,或者在执行一个复杂任务时,并行调用多个工具和 LLM,从而大大缩短总的响应时间,提升用户体验和系统吞吐量。这对于构建高性能、高并发的 Agent 服务至关重要。
💻 实战演示
我们将通过几个实战场景,演示如何应用上述性能调优和 Token 经济学策略。
场景一:对比不同模型的 Token 消耗与响应速度
在这个场景中,我们将使用 Hermes Agent 切换不同的 LLM 模型,并观察它们在处理相同任务时的 Token 消耗和响应时间差异。
操作步骤:
确保 OpenRouter 配置正确: 如果您是首次使用,请确保在 [第 02 期 | 模型切换与 Provider 配置实战] 中已正确配置 OpenRouter API Key。
# 检查当前 Hermes Agent 配置 hermes config get llm.provider hermes config get llm.openrouter_api_key定义一个简单的测试任务: 我们将让 Agent 总结一段较长的文本。
# 创建一个名为 summarize_long_text.py 的 Skill 文件 # ~/.hermes/skills/summarize_long_text.py from hermes_agent.skill_library.tools import tool from hermes_agent.core.llm import HermesLLM @tool def summarize_long_text(self, text: str) -> str: """ Summarizes a given long text. """ print(f"Summarizing text with model: {self.llm.default_model_id}") prompt = f"请将以下长文本精简为一段 100 字左右的摘要,突出核心观点:\n\n{text}" response = self.llm(prompt) return response # 重新加载 Hermes Agent 以确保新 Skill 可用 # 如果 Hermes Agent 正在运行,可能需要重启或使用 hermes tools refresh假设我们有一段很长的文本,例如一篇新闻报道或文档片段:
# long_article.txt 随着人工智能技术的飞速发展,大型语言模型(LLM)已成为科技领域最热门的话题之一。从内容创作到代码生成,LLM 的应用场景日益广泛,深刻改变着各行各业的工作模式。然而,LLM 的强大能力也伴随着一系列挑战,其中最突出的是其高昂的运行成本和对计算资源的巨大需求。 这些模型通常需要大量的 GPU 算力进行训练和推理,导致运营成本居高不下。尤其是在企业级应用中,如何平衡性能与成本成为决策者必须面对的问题。Token 经济学应运而生,它关注如何优化每次 API 调用的 Token 数量,从而直接影响到成本。 上下文窗口是另一个关键概念。每个 LLM 都有一个固定的上下文窗口大小,限制了模型在单次交互中能够处理的信息量。超出这个限制,模型就会“失忆”。因此,有效地管理上下文,如通过摘要、截断或检索增强生成(RAG)等技术,对于保持 Agent 的效率和智能至关重要。 此外,模型选择也直接影响性价比。OpenRouter 平台聚合了众多 LLM 模型,它们在性能、速度和价格上各有千秋。针对不同复杂度的任务选择合适的模型,是实现成本效益最大化的关键。例如,对于简单的问答,选择成本较低的模型即可;而对于复杂的推理或代码生成,则可能需要更高性能的模型。 缓存机制和批量处理也是提升性能的重要策略。缓存可以避免重复的 LLM 调用,减少 API 成本和延迟。批量处理则可以将多个请求打包,提高吞吐量,尤其是在处理大量独立任务时。异步编程进一步提升了 Agent 的并发处理能力,使其能够同时处理多个请求,从而提供更流畅的用户体验。 未来,随着模型效率的提升和新的优化技术的出现,LLM 的应用将更加普及和经济。使用
gpt-3.5-turbo运行并观察:# 设置模型为 gpt-3.5-turbo hermes config set llm.default_model gpt-3.5-turbo # 读取长文本内容 LONG_TEXT=$(cat long_article.txt) # 运行 Agent 并调用 Skill echo "Running with gpt-3.5-turbo..." hermes run summarize_long_text --text "$LONG_TEXT" # 观察输出,注意响应时间和可能的 Token 统计信息(如果 Hermes 提供)预期输出示例 (响应时间会因网络和负载而异):
Running with gpt-3.5-turbo... [Hermes Agent] Initializing... [Hermes Agent] Model set to: gpt-3.5-turbo [Hermes Agent] Calling skill: summarize_long_text Summarizing text with model: gpt-3.5-turbo [LLM Call Details] Model: gpt-3.5-turbo, Input Tokens: 500, Output Tokens: 120, Cost: $0.0005 [Hermes Agent] Skill 'summarize_long_text' output: 人工智能(AI)和大型语言模型(LLM)正迅速改变各行各业,但其高昂的运行成本和计算资源需求是主要挑战。Token 经济学和上下文窗口管理对于优化成本和效率至关重要,通过摘要、RAG 等技术可有效利用有限上下文。模型选择需权衡任务复杂度、成本和速度,OpenRouter 提供了丰富选择。缓存和批量处理能进一步提升性能,异步优化则增强并发能力。未来,随着技术进步,LLM 应用将更普及和经济。使用
gpt-4-turbo运行并观察:# 设置模型为 gpt-4-turbo hermes config set llm.default_model gpt-4-turbo # 运行 Agent 并调用 Skill echo "Running with gpt-4-turbo..." hermes run summarize_long_text --text "$LONG_TEXT" # 再次观察输出,比较响应时间和 Token 统计信息预期输出示例 (响应时间会因网络和负载而异):
Running with gpt-4-turbo... [Hermes Agent] Initializing... [Hermes Agent] Model set to: gpt-4-turbo [Hermes Agent] Calling skill: summarize_long_text Summarizing text with model: gpt-4-turbo [LLM Call Details] Model: gpt-4-turbo, Input Tokens: 500, Output Tokens: 110, Cost: $0.0055 [Hermes Agent] Skill 'summarize_long_text' output: 大型语言模型(LLM)的快速发展带来了内容创作、代码生成等广泛应用,但也面临高昂运行成本和资源需求的挑战。Token 经济学是优化成本的关键,需关注每次API调用的Token数量。上下文窗口限制了模型处理信息量,通过摘要、RAG等技术进行有效管理至关重要。OpenRouter平台提供多种LLM模型,需根据任务复杂度、成本和速度进行选择。缓存、批量处理和异步优化是提升Agent性能和吞吐量的有效策略,共同推动LLM应用更经济高效。对比分析: 你会发现
gpt-4-turbo可能在摘要质量上略优,但其成本(Cost)显著高于gpt-3.5-turbo,响应时间也可能稍长。这验证了模型选择对成本和性能的直接影响。
场景二:上下文管理与摘要策略(模拟)
此场景将演示如何通过精简 Prompt 或在 Skill 中引入摘要逻辑来减少 Token 消耗。由于 Hermes Agent 的 context_strategy 配置在当前版本中可能不直接暴露给 CLI,我们将通过 Skill 内部逻辑来模拟。
操作步骤:
准备一个包含冗余信息的长 Prompt:
# redundant_prompt.txt 以下是一个关于人工智能发展的背景介绍,你需要阅读它,并基于此回答一个非常简单的问题。请忽略所有与问题无关的细节,只关注核心信息。 人工智能(AI)是计算机科学的一个分支,旨在创建能够执行通常需要人类智能的任务的机器。它涵盖了机器学习、深度学习、自然语言处理、计算机视觉等多个子领域。自20世纪50年代“人工智能”概念被提出以来,AI经历了多次兴衰。近年来,随着大数据、高性能计算和先进算法的突破,AI再次迎来爆发式增长,尤其是在深度学习和大型语言模型(LLM)方面。LLM如GPT系列,展现了惊人的文本理解、生成和推理能力,推动了AI在各行各业的广泛应用。 现在,请回答这个问题:什么是AI的核心目标?创建两个 Skill:一个直接发送冗余 Prompt,一个精简 Prompt:
# ~/.hermes/skills/context_management.py from hermes_agent.skill_library.tools import tool @tool def ask_with_redundant_context(self, context_and_question: str) -> str: """ Asks a question with a long, potentially redundant context. """ print(f"Asking with redundant context using model: {self.llm.default_model_id}") response = self.llm(context_and_question) return response @tool def ask_with_optimized_context(self, context: str, question: str) -> str: """ Asks a question after extracting only relevant context. This simulates an internal summarization or context extraction. """ print(f"Asking with optimized context using model: {self.llm.default_model_id}") # 模拟上下文摘要/提取逻辑 # 实际的 Agent 可以调用一个专门的摘要 Skill 或 LLM 来做这个 # 这里我们手动模拟提取核心信息 if "人工智能(AI)是计算机科学的一个分支" in context: relevant_context = "人工智能(AI)是计算机科学的一个分支,旨在创建能够执行通常需要人类智能的任务的机器。" else: relevant_context = context # fallback optimized_prompt = f"基于以下信息回答问题:\n\n信息:{relevant_context}\n\n问题:{question}" response = self.llm(optimized_prompt) return response运行并对比 Token 消耗:
# 确保模型设置为 gpt-3.5-turbo 以便观察成本差异 hermes config set llm.default_model gpt-3.5-turbo # 读取冗余 Prompt REDUNDANT_PROMPT=$(cat redundant_prompt.txt) echo "--- Running with redundant context ---" hermes run ask_with_redundant_context --context_and_question "$REDUNDANT_PROMPT" # 观察 Input Tokens 和 Cost echo "--- Running with optimized context ---" # 提取原始文本和问题 ORIGINAL_CONTEXT="人工智能(AI)是计算机科学的一个分支,旨在创建能够执行通常需要人类智能的任务的机器。它涵盖了机器学习、深度学习、自然语言处理、计算机视觉等多个子领域。自20世纪50年代“人工智能”概念被提出以来,AI经历了多次兴衰。近年来,随着大数据、高性能计算和先进算法的突破,AI再次迎来爆发式增长,尤其是在深度学习和大型语言模型(LLM)方面。LLM如GPT系列,展现了惊人的文本理解、生成和推理能力,推动了AI在AI在各行各业的广泛应用。" QUESTION="什么是AI的核心目标?" hermes run ask_with_optimized_context --context "$ORIGINAL_CONTEXT" --question "$QUESTION" # 观察 Input Tokens 和 Cost预期输出示例:
--- Running with redundant context --- [Hermes Agent] Initializing... [Hermes Agent] Model set to: gpt-3.5-turbo [Hermes Agent] Calling skill: ask_with_redundant_context Asking with redundant context using model: gpt-3.5-turbo [LLM Call Details] Model: gpt-3.5-turbo, Input Tokens: 250, Output Tokens: 20, Cost: $0.00015 [Hermes Agent] Skill 'ask_with_redundant_context' output: AI的核心目标是创建能够执行通常需要人类智能的任务的机器。 --- Running with optimized context --- [Hermes Agent] Initializing... [Hermes Agent] Model set to: gpt-3.5-turbo [Hermes Agent] Calling skill: ask_with_optimized_context Asking with optimized context using model: gpt-3.5-turbo [LLM Call Details] Model: gpt-3.5-turbo, Input Tokens: 80, Output Tokens: 20, Cost: $0.00005 [Hermes Agent] Skill 'ask_with_optimized_context' output: AI的核心目标是创建能够执行通常需要人类智能的任务的机器。对比分析: 尽管两个 Skill 得到了相同的答案,但
ask_with_optimized_context通过减少发送给 LLM 的上下文信息,显著降低了Input Tokens数量和相应的成本。这演示了上下文管理在 Token 经济学中的重要性。
场景三:模拟缓存效果
我们将通过一个自定义 Skill 来模拟缓存的效果,展示重复调用相同任务时,如何通过内部缓存机制避免重复的 LLM 调用或耗时操作。
操作步骤:
创建带有
lru_cache的 Skill:# ~/.hermes/skills/cached_operations.py import functools import time from hermes_agent.skill_library.tools import tool @tool @functools.lru_cache(maxsize=128) # 缓存最近 128 个不同输入的计算结果 def get_weather_data(self, city: str) -> str: """ Simulates fetching weather data for a city, with caching. This operation is 'expensive' (simulated by sleep) and its result is stable for a short period. """ print(f"Fetching actual weather data for {city}... (not from cache)") time.sleep(2) # Simulate network delay or complex computation current_time = time.strftime("%H:%M:%S", time.localtime()) return f"The weather in {city} at {current_time} is sunny, 25°C." @tool def get_time_sensitive_data(self, topic: str) -> str: """ Simulates fetching time-sensitive data that should NOT be cached by lru_cache. """ print(f"Fetching time-sensitive data for {topic}...") time.sleep(1) current_time = time.strftime("%H:%M:%S", time.localtime()) return f"Real-time news for {topic} at {current_time}: Market is volatile."运行并观察缓存效果:
echo "--- First call to get_weather_data (should be slow) ---" hermes run get_weather_data --city "London" echo "--- Second call to get_weather_data with same city (should be fast, from cache) ---" hermes run get_weather_data --city "London" echo "--- Third call to get_weather_data with different city (should be slow again) ---" hermes run get_weather_data --city "Paris" echo "--- First call to get_time_sensitive_data (should always be slow) ---" hermes run get_time_sensitive_data --topic "Stock Market" echo "--- Second call to get_time_sensitive_data with same topic (should still be slow, no lru_cache) ---" hermes run get_time_sensitive_data --topic "Stock Market"预期输出示例:
--- First call to get_weather_data (should be slow) --- [Hermes Agent] Initializing... [Hermes Agent] Calling skill: get_weather_data Fetching actual weather data for London... (not from cache) [Hermes Agent] Skill 'get_weather_data' output: The weather in London at 10:00:05 is sunny, 25°C. (Approx. 2-3 seconds elapsed) --- Second call to get_weather_data with same city (should be fast, from cache) --- [Hermes Agent] Initializing... [Hermes Agent] Calling skill: get_weather_data [Hermes Agent] Skill 'get_weather_data' output: The weather in London at 10:00:05 is sunny, 25°C. (Approx. <1 second elapsed, notice "Fetching actual weather data..." did not print) --- Third call to get_weather_data with different city (should be slow again) --- [Hermes Agent] Initializing... [Hermes Agent] Calling skill: get_weather_data Fetching actual weather data for Paris... (not from cache) [Hermes Agent] Skill 'get_weather_data' output: The weather in Paris at 10:00:10 is sunny, 25°C. (Approx. 2-3 seconds elapsed) --- First call to get_time_sensitive_data (should always be slow) --- [Hermes Agent] Initializing... [Hermes Agent] Calling skill: get_time_sensitive_data Fetching time-sensitive data for Stock Market... [Hermes Agent] Skill 'get_time_sensitive_data' output: Real-time news for Stock Market at 10:00:13: Market is volatile. (Approx. 1-2 seconds elapsed) --- Second call to get_time_sensitive_data with same topic (should still be slow, no lru_cache) --- [Hermes Agent] Initializing... [Hermes Agent] Calling skill: get_time_sensitive_data Fetching time-sensitive data for Stock Market... [Hermes Agent] Skill 'get_time_sensitive_data' output: Real-time news for Stock Market at 10:00:15: Market is volatile. (Approx. 1-2 seconds elapsed, notice "Fetching time-sensitive data..." printed again)对比分析:
get_weather_data在第二次调用相同参数时,由于lru_cache机制,跳过了模拟的耗时操作,直接返回了缓存结果,响应速度显著提升。而get_time_sensitive_data没有使用缓存,每次调用都会重新执行耗时操作。这清晰地展示了缓存对于提升 Agent 性能的巨大作用。在实际应用中,对于那些结果相对稳定、调用频繁且耗时的 Skill 或 LLM 调用,引入缓存是极其有效的优化手段。
🔧 涉及的命令与工具
| 命令/工具 | 说明 | 课程关联 |
|---|---|---|
hermes config set |
设置 Hermes Agent 的配置参数,如默认 LLM 模型、LLM provider 等。 | 第 02 期,本期用于模型切换 |
hermes config get |
查看 Hermes Agent 的当前配置参数。 | 第 02 期,本期用于查看配置 |
hermes run |
运行 Hermes Agent 并执行指定的 Skill 或自然语言指令。 | 第 03 期,本期用于执行实战 Skill |
hermes tools |
管理和刷新 Agent 的 Skill 工具库。 | 第 03 期,本期用于确保新 Skill 可用 |
hermes model |
查看可用的 LLM 模型列表,通常通过 OpenRouter 提供。 | 第 02 期,本期用于了解模型选择 |
| OpenRouter | 一个聚合了 200+ LLM 模型的平台,为 Hermes Agent 提供统一的 LLM API 访问和成本管理。 | 第 02 期,本期核心平台 |
functools.lru_cache |
Python 内置的轻量级内存缓存装饰器,用于缓存函数调用结果。适用于单个进程内的缓存优化。 | 本期实战用于 Skill 内部缓存模拟 |
asyncio |
Python 标准库,用于编写并发代码,通过事件循环管理协程,实现异步 I/O。 | 本期核心概念,用于异步优化 |
📝 本期要点回顾
- Token 经济学是核心: 每次 LLM 调用都基于 Token 计费,理解 Token 消耗是控制成本和优化性能的基础。
- 上下文窗口是瓶颈: LLM 的上下文窗口限制了单次交互的信息量,需要通过精简提示、摘要、截断、RAG 等策略高效管理。
- 模型选择影响深远: 通过 OpenRouter,可以根据任务复杂度、成本、速度和上下文窗口大小,灵活选择最合适的 LLM 模型,实现性价比最大化。
- 缓存是性能利器: 针对重复或稳定的 LLM 调用和耗时 Skill,引入缓存机制(如
lru_cache或外部缓存系统)可以显著降低 API 调用次数、成本和响应延迟。 - 批量处理与异步提升吞吐量: 对于多个独立任务,批量处理和异步编程能够有效提高 Agent 的并发处理能力和总吞吐量,缩短总响应时间。
🔗 参考资料
- Hermes Agent 官方文档: https://hermes-agent.nousresearch.com/docs/
- OpenRouter 官方网站: https://openrouter.ai/ (用于查询模型列表和最新价格)
- NousResearch Hermes Agent GitHub 仓库: https://github.com/NousResearch/hermes-agent