调试Agno代理的上下文

如果无法理解AI代理内部发生的情况,那么性能就毫无意义。这就是 Agno 的调试和上下文管理功能的亮点。

调试Agno代理的上下文
AI编程/Vibe Coding 遇到问题需要帮助的,联系微信 ezpoda,免费咨询。

是否曾经盯着人工智能代理的奇怪反应,想知道它到底在“想”什么? 你并不孤单。调试代理是出了名的令人沮丧——尤其是当您无法看到哪些数据实际流入上下文窗口时。小时消失排除故障隐形状态。

修复方法如下:Agno 的“debug_mode”会在生成响应之前公开您的代理看到的所有内容(会话历史记录、用户配置文件、时间戳)。不再有猜谜游戏。

在本指南中,您将学习如何:

  • 启用调试模式以实时检查上下文
  • 注入自定义会话状态(如用户首选项和配置文件)
  • 使用 SQLite 存储保留对话历史记录

最后,您将完全了解代理的上下文,从而大大缩短调试时间。让我们一起来破解这个黑匣子吧。

1、Agno简介

Agno 代表了新一代 AI 代理框架,专为既需要简单又强大的开发人员而构建。与强迫您学习链、图或复杂模式等复杂抽象的框架不同,Agno 使用纯 Python。您可以像编写任何其他 Python 代码一样编写代理。

该框架支持多个模型提供商,包括 OpenAI、Anthropic Claude、Google Gemini 以及 Ollama 的开源模型。模型之间的切换不需要重写。当底层情报发生变化时,您的代理代码保持不变。

Agno 的与众不同之处在于它对性能和透明度的执着关注。该框架实例化代理的速度比 LangGraph 等替代方案快 529 倍。它使用的内存减少了 24 倍。当您在生产中运行数百个代理实例时,这些数字很重要。

但是,如果您无法理解代理内部发生的情况,那么性能就毫无意义。这就是 Agno 的调试和上下文管理功能的亮点。它们使您可以完全了解代理的决策过程。

2、理解AI代理的上下文

在深入研究代码之前,您需要了解“上下文”对于 AI 代理来说实际上意味着什么。上下文是模型在生成响应之前看到的所有内容。它包括系统消息、对话历史记录、注入状态、时间戳以及您选择提供的任何其他信息。

将上下文视为代理的工作记忆。就像人类利用更多相关信息做出更好的决策一样,当代理的上下文准确包含他们需要的内容时,代理会表现得更好。这里的关键词是“完全”。

更多的上下文并不自动意味着更好的结果。上下文窗口中的每个令牌都会花费金钱和处理时间。过多的上下文实际上会降低模型的准确性。代理可能会因不相关的信息而分心,或者达到令牌限制而截断重要数据。

Agno 通过为您提供对上下文组合的精细控制来解决这个问题。您决定输入什么。您可以检查模型实际看到的内容。您可以根据手头的任务进行动态调整。

这种控制级别将调试从令人沮丧的猜谜游戏转变为系统的工程实践。

3、构建第一个可调试的AI代理

让我们从一个完整的工作示例开始。该代理演示了您将在实际应用程序中使用的所有关键调试和上下文注入功能。

from typing import Dict, Any, Optional
from rich.pretty import pprint
from agno.agent import Agent
from agno.models.google import Gemini
from agno.db.sqlite import SqliteDb
from dotenv import load_dotenv

# Load environment variables
load_dotenv()
# Constants
DB_FILE = "simple_context_data.db"
# Functions
def create_agent():
    agent = Agent(
        model=Gemini(id="gemini-2.0-flash-exp"),
        # system prompt
        # role / instructions / output
        description="You are a helpful AI assistant",
        instructions=["Use the information provided above to answer questions."],
        # output
        # system prompt extra
        add_datetime_to_context=True,
        # session storage
        db=SqliteDb(db_file=DB_FILE),
        # session history
        add_history_to_context=True,
        num_history_runs=2,
        # session state
        add_session_state_to_context=True,
        # Initialize session state with default preferences
        session_state={
            "user_profile": {
                "location": "San Francisco",
                "membership_level": "premium"
            }
        },
       
        # debug
        debug_mode=True,
    )
    return agent
def execute_turn(agent, turn: str, user_id: Optional[str]=None, session_id: Optional[str]=None):
    response = agent.run(turn, user_id=user_id, session_id=session_id)
    pprint(f"Run ID: {response.run_id}\nAgent ID: {response.agent_id}\nSession ID: {response.session_id}\nContent: {response.content}")
    return response
agent = create_agent()
user_id = 'user_01'
session_id = 'session_01'
while True:
    user_input = input("\nYou: ")
    if user_input == "/exit":
        break
    execute_turn(agent, user_input, user_id, session_id)

此示例将多个强大的功能打包到单个代理配置中。让我们分解每个组件并了解它的作用。

4、启用调试模式

我们的示例中最重要的一行是 debug_mode=True。这个单一参数将您的代理从黑匣子转变为透明系统。

当调试模式激活时,Agno 会打印详细的日志,准确显示模型接收到的内容。您会看到完整的系统消息构造。您会看到历史是如何格式化的。您会看到会话状态注入。您会看到影响模型响应的每一个上下文。

调试输出通常如下所示:

DEBUG ========================================= system ========================================
DEBUG <instructions>
You are a helpful AI assistant
Use the information provided above to answer questions.
</instructions>
DEBUG <datetime>
Current date and time: 2025-01-19 14:32:15 UTC
</datetime>
DEBUG <session_state>
{
    "user_profile": {
        "location": "San Francisco",
        "membership_level": "premium"
    }
}
</session_state>

这种可见性对于调试来说是无价的。当您的代理给出意外响应时,您可以准确追踪其可用的信息。通常,问题不在于模型,而在于上下文缺失或格式错误。

调试模式还显示每次响应后的性能指标。您会看到输入和输出的令牌计数。您会看到响应持续时间。您每秒会看到令牌。这些指标可帮助您优化成本和延迟。

5、注入会话状态

会话状态允许您将自定义数据直接注入代理的上下文中。这些数据在对话轮次中持续存在,并且可以影响代理的响应方式。

在我们的示例中,我们使用用户配置文件初始化会话状态:

session_state={
    "user_profile": {
        "location": "San Francisco",
        "membership_level": "premium"
    }
}

add_session_state_to_context=True 参数告诉 Agno 在发送给模型的每条消息中包含此状态。现在,代理无需明确告知即可知道用户的位置和会员级别。

这使得强大的个性化场景成为可能。高级用户可能会收到更详细的回复。旧金山的用户可能会获得特定于位置的推荐。代理根据注入状态自动进行这些调整。

当您动态更新会话状态时,它会变得更加强大。您可以在工具调用期间或基于对话流修改状态值。更新后的状态立即影响后续响应。

考虑这种动态状态更新模式:

def update_preferences(session_state: dict, preference_key: str, preference_value: Any) -> str:
    """Update user preferences in session state."""
    if "preferences" not in session_state:
        session_state["preferences"] = {}
    session_state["preferences"][preference_key] = preference_value
    return f"Updated {preference_key} to {preference_value}"

当您将此功能注册为工具时,代理可以在对话中更新其自己的上下文。这些变化持续存在并影响所有未来的互动。

6、管理会话历史

对话历史记录为您的座席提供会话中的记忆。没有历史,每条消息都是独立的。代理无法引用之前所说的内容。它无法维持连贯的多轮对话。

Agno 为历史管理提供了两个关键参数:

add_history_to_context=真,
num_history_runs=2,

第一个参数启用历史注入。第二个限制了要包含的先前回合数。

为什么要限制历史?上下文窗口的容量是有限的。包含无限的历史最终会推出其他重要信息。它还会增加成本,因为您需要为上下文中的每个代币付费。

设置“num_history_runs=2”会创建一个滑动窗口。代理始终会看到最近的两个对话轮次。随着新轮次的到来,旧轮次会逐渐减少。这平衡了内存和效率。

对于需要更多上下文的较长对话,请考虑增加此值。对于简单的问答交互,您可以将其减少到 1 或完全禁用它。正确的设置取决于您的具体用例。

7、添加日期时间到上下文

add_datetime_to_context=True 参数将当前日期和时间注入到每条消息中。这个简单的添加可以实现令人惊讶的强大功能。

时间感知代理可以:

  • 提供适合一天中不同时间的问候
  • 提供截止日期建议
  • 结合时间背景参考当前事件
  • 根据实​​际日期安排和计划

您还可以指定时区:

agent = Agent(
    model=Gemini(id="gemini-2.0-flash-exp"),
    add_datetime_to_context=True,
    timezone_identifier="America/Los_Angeles",
)

现在代理不仅知道时间,还知道用户的当地时间。一位用户询问“我午餐吃什么?”下午 2 点得到的建议与上午 9 点提出的建议不同。

这看似一个小细节,却极大地提升了用户体验。代理感觉更加自然并且具有情境意识。

8、用SQLite持久化会话

没有持久性的代理是无状态的。每个新的脚本执行都是全新的。所有对话历史记录和会话状态都会消失。

Agno 的数据库集成改变了这一点:

db=SqliteDb(db_file=DB_FILE),

这一行启用持久会话。对话历史记录在脚本重新启动后仍然存在。会话状态在执行过程中保持不变。用户可以在几天后继续对话而不会丢失上下文。

SQLite 非常适合开发和单实例部署。对于具有多个实例的生产系统,Agno 还支持 PostgreSQL:

from agno.db.postgres import PostgresDb
db_url = "postgresql+psycopg://user:password@localhost:5432/agents"
db = PostgresDb(db_url=db_url, session_table="agent_sessions")

PostgreSQL 提供了重要应用程序所需的耐用性和可扩展性。多个代理实例可以共享同一个数据库。无论哪个实例处理请求,会话都保持一致。

9、用户和会话管理

我们的示例使用显式用户和会话标识符:

user_id = 'user_01'
session_id = 'session_01'

这些标识符支持复杂的多用户和多会话场景。

单个用户可能有多个会话。每个会话都维护自己的历史记录和状态。用户可以进行与工作相关的会话和个人会话,每个会话都有不同的上下文。

多个用户显然需要单独的会话。数据库使所有内容保持隔离。用户 A 的对话永远不会泄露到用户 B 的上下文中。

您还可以通过编程方式检索会话:

# Get messages from a specific session
messages = agent.get_messages_for_session(session_id="session_01")
# Continue an existing session
response = agent.run(
    "Continue where we left off",
    user_id="user_01",
    session_id="session_01"
)

这种灵活性支持复杂的应用程序,例如客户支持系统,其中代理可能会处理数千个并发对话。

10、Response对象

每个代理运行都会返回一个包含丰富元数据的响应对象:

response = agent.run(turn, user_id=user_id, session_id=session_id)
pprint(f"Run ID: {response.run_id}\nAgent ID: {response.agent_id}\nSession ID: {response.session_id}\nContent: {response.content}")

响应对象包括:

  • run_id:此特定执行的唯一标识符
  • agent_id:代理实例的标识符
  • session_id:此运行所属的会话
  • content:实际响应文本

在调试模式下,您还可以获得性能指标:

  • 输入令牌计数
  • 输出令牌计数
  • 令牌总数
  • 响应持续时间
  • 每秒令牌数

事实证明,这些指标对于优化至关重要。如果回复时间太长,您会立即知道。如果令牌计数似乎过多,您可以调查上下文组成。

11、结束语

您现在已经掌握了构建您可以真正理解的人工智能代理的知识。调试模式准确揭示您的代理所看到的内容。会话状态允许您精确地注入自定义数据。历史管理为您的座席提供会话内和会话间的记忆。数据库持久性可确保不会丢失任何内容。

关键的见解是,上下文工程需要与任何其他工程学科相同的严格性。不要猜测你的代理人知道什么。检查一下。不要希望模型使用您的数据。验证一下。

Agno 的透明度功能使这种验证成为可能。在开发过程中不断地使用它们。建立您的代理行为正确的信心。然后进行部署,确保您了解幕后发生的情况。

黑盒人工智能代理的时代正在结束。欢迎来到可观察、可调试、可控人工智能系统的时代。

现在去建造一些非凡的东西吧。


原文链接: Debugging Context in Agno Agents

汇智网翻译整理,转载请标明出处