Agno Agent 上下文管理实战指南:Instructions、System Message、Few-Shot 与历史工具调用过滤
【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno
本篇技术指南围绕开源项目 agno(cookbook 目录下03_context_management场景)讲解如何精细控制送入大模型的 Agent 上下文:包括静态与动态指令(Instructions)、自定义系统消息(System Message)、开场白(Introduction)、少样本示例(Few-Shot Learning)以及历史工具调用过滤(max_tool_calls_from_history)。这些能力覆盖了客服、游戏开发助手、天气查询等常见场景,读完本文你可以直接复用文中的代码片段,构建"角色清晰、指令随状态变化、上下文成本可控"的生产级 Agent。
本文对应的六个示例脚本均已在本仓库中通过验证(见 TEST_LOG.md,测试日期 2026-02-13,全部PASS),示例本身位于 03_context_management 目录下,可直接运行复现。
一、目录结构与运行前提
cookbook/02_agents/03_context_management/目录集中展示了 agno Agent 的上下文塑造能力。根据 README.md,目录包含以下示例:
| 文件 | 演示主题 |
|---|---|
instructions.py | 设置 Agent 指令(含时间上下文) |
instructions_with_state.py | 借助 session state 动态生成指令 |
system_message.py | 定制 Agent 的系统消息与角色 |
introduction_message.py | 设置 Agent 的初始问候语 |
few_shot_learning.py | 用示例消息做少样本学习 |
filter_tool_calls_from_history.py | 过滤送入模型的历史工具调用 |
datetime_format.py | 自定义注入上下文的日期时间格式 |
运行前提
- 环境变量:需加载
OPENAI_API_KEY(示例默认走 OpenAI 模型,如OpenAIResponses(id="gpt-5.2"))。 - Python 环境:可使用仓库根目录脚本
./scripts/demo_setup.sh创建 demo 环境,然后用.venvs/demo/bin/python运行。 - 可选依赖:个别示例需要本地服务或特定 provider 的 API Key。根据 TEST_LOG.md 的测试环境记录,验证时使用了
.venvs/demo/bin/python,且pgvector: running,说明存储类示例(如filter_tool_calls_from_history.py)依赖本地数据库链路正常。
运行命令(示例):
.venvs/demo/bin/python cookbook/02_agents/03_context_management/few_shot_learning.py二、Instructions:静态指令与"时间感知"上下文
instructions.py演示的是最简单的指令用法:通过instructions参数(或构造函数位置参数)把行为准则写进系统消息。
from agno.agent import Agent from agno.models.openai import OpenAIResponses agent = Agent( model=OpenAIResponses(id="gpt-5.2"), add_datetime_to_context=True, # 把当前时间注入指令上下文 timezone_identifier="Etc/UTC", # 使用 TZ Database 时区标识 ) agent.print_response( "What is the current date and time? What is the current time in NYC?" )这个例子同时点出了时间上下文:当 Agent 需要回答"当前时间""纽约现在几点"这类相对时间问题时,若模型不知道"现在",就无法给出可靠答案。agno 用两个开关解决:
add_datetime_to_context:为True时把当前日期时间注入指令,让模型具备时间感(如能理解"tomorrow"这类相对表述)。默认False。timezone_identifier:指定时区(遵循 TZ Database 格式,例如"Etc/UTC"),避免默认时区与用户期望不符。datetime_format:自定义日期时间的字符串格式(如"%Y-%m-%dT%H:%M:%SZ"),不设置则使用默认的datetime字符串表示。
以上默认值均可从 agent.py 的字段定义中确认。类似地,agno 还提供了add_location_to_context(注入当前位置,实现位置感知),属于同一组上下文注入开关。
在源码层面,instructions支持str、List[str]与Callable三种形态(见 agent.py)。此外还有两个与之搭配的展示型参数:use_instruction_tags(默认False,为True时把指令包裹在<instructions>标签中)、description(追加到系统消息开头的 Agent 描述)、expected_output(期望输出描述)与additional_context(追加到系统消息末尾的附加上下文),它们共同构成了"系统消息 = 描述 + 指令 + 期望输出 + 附加上下文"的拼装逻辑(build_context置为False可跳过系统上下文构建)。
三、Instructions With State:用函数 + session state 实现动态指令
固定指令适合通用 Agent,但在多用户、多会话场景下,Agent 需要"看人下菜"。instructions_with_state.py演示了把instructions设置为一个函数的写法:指令不再是一成不变的字符串,而是根据当前RunContext里的session_state动态生成。
from textwrap import dedent from agno.agent import Agent from agno.models.openai import OpenAIResponses from agno.run import RunContext def get_run_instructions(run_context: RunContext) -> str: """Build instructions for the Agent based on the run context.""" if not run_context.session_state: return "You are a helpful game development assistant that can answer questions about coding and game design." game_genre = run_context.session_state.get("game_genre", "") difficulty_level = run_context.session_state.get("difficulty_level", "") return dedent( f""" You are a specialized game development assistant. The team is currently working on a {game_genre} game. The current project difficulty level is set to {difficulty_level}. Please tailor your responses to match this genre and complexity level when providing coding advice, design suggestions, or technical guidance.""" ) game_development_agent = Agent( model=OpenAIResponses(id="gpt-5.2"), instructions=get_run_instructions, # 指令是一个函数 ) game_development_agent.print_response( "What genre are we working on and what should I focus on for the core mechanics?", session_state={"game_genre": "platformer", "difficulty_level": "hard"}, )运行机理与关键点
- 指令函数接收一个
RunContext参数,从run_context.session_state(字典)中读取本次运行的会话状态;若session_state为空,则回退到一条默认指令,保证函数在无状态时也能正常响应。 - 在调用
print_response/run时,通过session_state={"game_genre": "platformer", "difficulty_level": "hard"}传入本次运行的状态。session_state是Agent构造阶段的一个可选字段(默认None,见 agent.py),同样作为run/print_response系列方法的运行时参数存在。 - 动态指令的最大价值:同一个 Agent 实例可被不同会话复用,每个会话看到的是"贴合自己上下文"的角色设定与约束,而无需为每种场景各建一个 Agent。除
session_state外,指令函数同样可以读取run_context.dependencies、run_context.metadata等运行期信息,进一步精细化上下文。
该示例在 TEST_LOG.md 中记录为运行 13s 成功输出预期结果。
四、System Message:覆盖系统消息并自定义角色
system_message.py演示直接接管系统的开场指令:当模型自带的默认系统提示不满足需求时,用system_message参数整体覆盖;同时用system_message_role修改系统消息的角色名。
from agno.agent import Agent from agno.models.openai import OpenAIResponses agent = Agent( model=OpenAIResponses(id="gpt-5.2"), # 覆盖自动生成的系统消息,改为自定义内容 system_message="You are a concise technical writer. Always respond in bullet points. Never use more than 3 sentences per bullet point.", # 系统消息的角色名(默认是 "system") system_message_role="system", markdown=True, ) agent.print_response( "Explain how HTTP cookies work.", stream=True, )源码中,system_message的类型为Optional[Union[str, Callable, Message]],默认None(未设置时由框架基于description、instructions等自动生成默认系统消息);system_message_role默认值为"system"(见 agent.py)。也就是说,你可以:
- 直接传字符串,完全自定义系统提示内容;
- 传函数,按运行上下文动态返回系统消息;
- 传
Message对象(并配合system_message_role),精确控制该消息在对话中的角色身份。
markdown=True会在系统消息中追加"以 Markdown 输出"的格式指令(见 agent.py)。
五、Introduction:给 Agent 设定开场白
introduction_message.py演示如何让 Agent 在会话一开始主动"开口"——用一个问候语建立对话基调。
from agno.agent import Agent from agno.models.openai import OpenAIResponses agent = Agent( model=OpenAIResponses(id="gpt-5.2"), # introduction 会作为 Agent 在对话中的第一条消息发出 introduction="Hello! I'm your coding assistant. I can help you write, debug, and explain code. What would you like to work on?", markdown=True, ) # 开场白可以通过属性直接读取 print("Introduction:", agent.introduction) print() agent.print_response( "Help me write a Python function to check if a string is a palindrome.", stream=True, )introduction是一个Optional[str]字段(默认None,见 agent.py)。源码注释明确指出:它会被作为Agent 发出的第一条消息放入对话。相比把问候语硬编码进系统消息,用introduction的好处是:
- 它真实存在于对话历史中,模型能感知"自己已经打过招呼",避免用户首条提问后出现重复寒暄;
- 它独立成参,便于产品层在不同入口(客服、导购、代码助手)切换不同开场文案;
- 运行时可通过
agent.introduction属性读取,方便在 UI 上同步展示开场语。
六、Few-Shot Learning:用additional_input灌入示例消息
few_shot_learning.py演示的是少样本学习(Few-Shot Learning):不是用文字描述"你要怎么回答问题",而是直接把若干组高质量"用户问-助手答"示例作为消息注入上下文,让模型模仿示例的格式、口吻与处理流程。
from agno.agent import Agent from agno.models.message import Message from agno.models.openai import OpenAIResponses # 三组"问题-回答"示例(仅节选第一组示意) support_examples = [ Message(role="user", content="I forgot my password and can't log in"), Message( role="assistant", content="I'll help you reset your password right away.\n\n**Steps to Reset Your Password:** ...".strip(), ), Message( role="user", content="I've been charged twice for the same order and I'm frustrated!", ), Message(role="assistant", content="I sincerely apologize for the billing error...".strip()), Message(role="user", content="Your app keeps crashing when I try to upload photos"), Message(role="assistant", content="I'm sorry you're experiencing crashes with photo uploads...".strip()), ] agent = Agent( name="Customer Support Specialist", model=OpenAIResponses(id="gpt-5-mini"), add_name_to_context=True, # 把 agent 名字加入上下文 additional_input=support_examples, # 少样本示例消息 instructions=[ "You are an expert customer support specialist.", "Always be empathetic, professional, and solution-oriented.", "Provide clear, actionable steps to resolve customer issues.", "Follow the established patterns for consistent, high-quality support.", ], markdown=True, ) agent.print_response("I want to enable two-factor authentication for my account.")少样本的工程要点
- 示例由
agno.models.message.Message构造,显式标注role="user"与role="assistant",模拟真实对话轮次。在源码中,additional_input的类型为Optional[List[Union[str, Dict, BaseModel, Message]]](默认None,见 agent.py),即除了Message,还支持字符串、字典与 Pydantic 模型,可灵活接入各种数据源。 - 示例回答刻意使用Markdown 结构(编号步骤、加粗标题、分块说明、兜底处理),让模型看到"优质回答应该长什么样"。指令(
instructions)负责定性,少样本负责定形——两者组合通常比单一手段效果更稳。 add_name_to_context=True会把name(此处为 "Customer Support Specialist")一并写入指令上下文,让角色定义更完整。- 使用场景建议:客服话术一致性、结构化报告生成、固定格式的代码审查意见等"风格必须先对齐"的任务;示例数量不必多,3~5 组高质量的典型场景往往已足够。
七、Filter Tool Calls From History:历史工具调用裁剪降本
filter_tool_calls_from_history.py演示的是上下文瘦身:Agent 开了add_history_to_context=True(把历史消息拼进模型输入,见 agent.py)后,多轮对话的历史会越积越长;而历史中被折叠进上下文的工具调用消息(每个都含输入输出 JSON)往往是 token 大户。
max_tool_calls_from_history(默认None表示不限,见 agent.py)允许你限制"从历史带入模型上下文的工具调用条数",只保留最近 N 条。示例把天气查询串成 8 轮,并逐轮打印进入模型上下文的工具调用数与数据库里全量保存的工具调用数,证明裁剪只影响"喂给模型的输入",不影响"完整落库的历史"。
import random from agno.agent import Agent from agno.db.sqlite import SqliteDb from agno.models.openai import OpenAIResponses def get_weather_for_city(city: str) -> str: conditions = ["Sunny", "Cloudy", "Rainy", "Snowy", "Foggy", "Windy"] temperature = random.randint(-10, 35) condition = random.choice(conditions) return f"{city}: {temperature}°C, {condition}" cities = ["Tokyo", "Delhi", "Shanghai", "São Paulo", "Mumbai", "Beijing", "Cairo", "London"] agent = Agent( model=OpenAIResponses(id="gpt-5-mini"), tools=[get_weather_for_city], instructions="You are a weather assistant. Get the weather using the get_weather_for_city tool.", # 只保留历史中最近的 3 条工具调用进入上下文(降低 token 成本) max_tool_calls_from_history=3, db=SqliteDb(db_file="tmp/weather_data.db"), # 全量历史落在 SQLite add_history_to_context=True, markdown=True, ) for i, city in enumerate(cities, 1): run_response = agent.run(f"What's the weather in {city}?") # 统计"从历史带入"的工具调用数(已经过滤后仍留在上下文里的) history_tool_calls = sum( len(msg.tool_calls) for msg in run_response.messages if msg.role == "assistant" and msg.tool_calls and getattr(msg, "from_history", False) ) # 统计"本次运行新产生"的工具调用数 current_tool_calls = sum( len(msg.tool_calls) for msg in run_response.messages if msg.role == "assistant" and msg.tool_calls and not getattr(msg, "from_history", False) ) # 数据库里该会话全量保存的工具调用数(不做过滤) saved_messages = agent.get_session_messages() total_in_db = ( sum( len(msg.tool_calls) for msg in saved_messages if msg.role == "assistant" and msg.tool_calls ) if saved_messages else 0 ) print( f"{i:<5} | {city:<15} | {history_tool_calls:<8} | {current_tool_calls:<8} " f"| {history_tool_calls + current_tool_calls:<11} | {total_in_db:<8}" )如何读懂这张统计表
脚本输出的表头为Run | City | History | Current | In Context | In DB,含义分别为:轮次、城市、来自历史的工具调用数(过滤后仍留在上下文)、本次新产生的工具调用数、两者之和(真正送入模型的总量)、数据库中的全量工具调用数(未过滤)。通过对比"Context 列缓慢收敛、DB 列线性增长",可以直观验证max_tool_calls_from_history的裁剪效果。
判定"来自历史"的关键标志
示例通过getattr(msg, "from_history", False)来判断一条 assistant 消息是否由历史回放产生——这是消息对象上的一个标记属性。运行后可用agent.get_session_messages()读取该会话在数据库中的完整消息,用于与"模型实际看到的上下文"做对比审计。若需要更大的裁剪粒度,可参考同属历史注入控制的其他参数:num_history_runs(纳入上下文的历史 run 数量)、num_history_messages(纳入上下文的历史消息数量)。
注意事项
- 示例将天气结果设为
random,便于在无外部天气 API 的情况下演示机制,真实使用时请替换为真实数据源。 - SQLite 文件写往
tmp/weather_data.db(相对路径),重复运行会累积同一 session 的历史,建议配合debug_mode=True观察送入模型的完整消息列表。
八、TEST_LOG 验证记录与运行结论
TEST_LOG.md 记录了本目录六个示例在2026-02-13、环境为.venvs/demo/bin/python、pgvector: running下的验证结果,全部通过:
| 示例 | Status | Tier | 耗时 | 结论 |
|---|---|---|---|---|
few_shot_learning.py | PASS | untagged | 10s | 少样本学习成功,输出符合预期 |
filter_tool_calls_from_history.py | PASS | untagged | 39s | 历史工具调用过滤成功(8 轮串行,耗时最长) |
instructions.py | PASS | untagged | 2s | 指令 + 时间上下文成功 |
instructions_with_state.py | PASS | untagged | 13s | 动态指令成功 |
introduction_message.py | PASS | untagged | 5s | 开场白成功 |
system_message.py | PASS | untagged | 11s | 自定义系统消息成功 |
该日志属于本仓库示例脚本自带的测试跟踪产物(目录内每份README/TEST_LOG/TEST_PROMPT配套出现),可作为"示例真实可运行"的证据;读者复现时,模型、时区等取值应以自己环境为准。
九、核心参数速查
下表汇总本文涉及的上下文控制参数及其默认值,均依据 agent.py 的字段定义整理:
| 参数 | 默认值 | 作用 |
|---|---|---|
instructions | None(支持 str / List[str] / Callable) | 设定指令;传函数时按RunContext动态生成 |
system_message | None(支持 str / Callable / Message) | 覆盖自动生成的系统消息 |
system_message_role | "system" | 系统消息的角色名 |
introduction | None | Agent 对话中的第一条(问候)消息 |
additional_input | None | 追加输入,如少样本示例消息列表 |
add_name_to_context | False | 把 agentname注入指令上下文 |
add_datetime_to_context | False | 把当前日期时间注入上下文 |
datetime_format | None | 自定义时间格式串 |
timezone_identifier | None | 时区(TZ Database 格式,如Etc/UTC) |
add_location_to_context | False | 把当前位置注入上下文 |
add_history_to_context | False | 是否把历史消息拼进模型输入 |
max_tool_calls_from_history | None(不限) | 从历史带入模型的工具调用条数上限 |
num_history_runs/num_history_messages | None | 历史 run / 消息纳入数量上限 |
build_context | True | 置为False可跳过系统上下文构建 |
session_state | None | 会话状态字典,供指令函数与运行期读取 |
十、小结与实践建议
围绕 agno 的上下文管理,本文呈现了一条由浅入深的路径:先用instructions与system_message框定 Agent 的"人设与边界";需要随会话变化时升级为指令函数 +session_state;需要引导对话节奏时补上introduction;需要统一回答风格时引入少样本additional_input;当历史消息成为 token 负担时,用max_tool_calls_from_history等参数在"上下文完整度"与"输入成本"之间取平衡。所有能力都围绕同一个目标展开——让送进模型的每一段上下文都清晰、相关且可控。读者可结合 03_context_management 目录下的源码、TEST_LOG.md 的验证记录以及 agno 源码中 Agent 参数定义 进一步深入探索。
【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考