【Agent学习笔记】我们一起来学Agent!(第二期)

2026-04-29 08:161阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

从 【Agent笔记】我们一起来学习Agent!(第1期) 继续讨论;我第一期的时候就是想记录一下,因为我在开发agent项目。没想到佬们那么欢迎,我真的诚惶诚恐。

然后这个月实在太忙了:上半月一直在做自己的项目,希望拿到一些投资。前两天刚郴州旅游回来,然后手上两个团队项目在做,而gpt plus眼看着过期了,我就想是时候提高自己的提示词技术和相关知识了————所以也很忙。

今天本来打算写多点的,但是我想还是留到下一期————**我保证这会很快的!就这个星期!**因为今晚有小姐姐叫我打游戏了,休息一波

佬我准备买个coding plan了,50左右推荐一个呗,就那种玩具的,我经常需要写一个玩具项目,要求有GLM5左右的水平就可以,主要是周量够,业余开发。没有的话我打算买kimi那个80的了,老板还给我配了gpt plus就不用在自己项目了。

正文开始:


我们一起学Agent!(第二期)

Context(上下文)

定义:Agent在每次执行任务时能够获得的外部信息。一个agent的质量很大程度上取决于它能访问到的上下文——即使系统提示写得再完美,缺少正确的上下文(背景,语境,解决问题需要的信息)也会导致结果不佳。

image1086×1448 299 KB

在agno中,提供了三种原生方式进行context的提供

Knowledge(知识库)

我们需要预先做两个事情,第一建立对象绑定数据库,第二建好知识库

  • 创建一个 Knowledge 对象,绑定一个向量数据库(如 PgVector)。

  • 将内容从本地文件、URL 等添加到知识库中(自动分块、生成 embedding)。

调用方式如下:

from agno.agent import Agent from agno.knowledge import Knowledge from agno.vector_db.pgvector import PgVector agent = Agent( model=..., knowledge=Knowledge( vector_db=PgVector(table_name="my_kb", db_url="postgresql://...") ), search_knowledge=True, # 提供工具 add_knowledge_to_context=True, # 自动注入 ) agent.knowledge.add_content_from_path("docs/")

可以看到,Agent 通过两种方式使用知识库:

  • add_knowledge_to_context=True:每次运行前自动搜索相关知识,并注入到提示词中(传统 RAG)。

  • search_knowledge=True:为 Agent 提供一个 search_knowledge_base(query)工具,由 Agent 自主决定何时搜索(Agentic RAG)。

后者是目前我认为比较好的方向,前者是几年前流行的;因为模型的agent能力已经大幅上升,query的多,query的好。

Dependencies(依赖项)

看到这个词我双眼一黑,不过其实也还好;

定义:短暂、请求级别的动态值,不适合放在知识库中:如特性开关、租户 ID、每个请求的数据库连接、调用方专属的 API Key 等。

简单的说,就是“程序运行时动态捕捉的数据,主动注入给 Agent”。

你在写代码时,可以“捕捉”到很多东西:

  • Web 框架中,从请求头捕捉 user_id

  • 浏览器插件中,捕捉当前选中的文字 window.getSelection()

  • 调试工具中,捕捉你设置的 --debug 命令行参数

  • 定时任务中,捕捉当前时间 datetime.now()

比如说你写了一个浏览器插件agent,其中有程序可以捕捉用户在浏览器中的历史选中,程序发现用户在浏览中选中了`英伟达` `微软`这些词汇,那么当用户和你的agent对话时候,它们就会被注入;agent就会意识到用户似乎对这两个公司感兴趣;

同样的,那些用户的ip地址,ID,APIKEY,也是程序捕捉,注入到agent之中。

实现方式

  • 在创建 Agent 时传入一个 dependencies 字典,可包含任意结构的数据。

  • 设置 add_dependencies_to_context=True 后,这些依赖会被自动注入到系统提示词中。

  • 工具函数如果需要访问依赖,可以声明一个 RunContext 参数,通过 run_context.dependencies 读取。

代码如下:

from agno.agent import Agent from agno.run import RunContext from agno.tools import tool @tool def get_config(run_context: RunContext, key: str) -> str: return str(run_context.dependencies["config"].get(key)) agent = Agent( model=..., dependencies={ "config": {"region": "us-east-1", "max_retries": 3}, "feature_flags": {"beta_search": True}, }, add_dependencies_to_context=True, tools=[get_config], ) # 也可以在单次运行时覆盖依赖 agent.run("我的区域是哪里?", dependencies={"region": "eu-west-1"})


讲完了这些,我们就了解了两种常见的上下文注入,其实这两者都很简单。一个就是用知识库的方式,一个就是用程序自动注入的方式。

image1086×1448 261 KB

那如果我们需要的上下文比较复杂,不能通过简单查询,就马上得到。比如你需要agent知道今天公司的工作某个任务的进度情况,得同时操作 Slack、GitHub、Google Drive、公司数据库才能得到“这个任务进行到什么程度了”这个上下文。(注意,上下文不是你的目的,你的目的可能更大,需要决策明天的公司加班与否)

而当你定义工具,接入查询工具和其他工具,可能每个系统有 8~15 个 API(发送消息、搜索频道、创建 PR、列出文件、执行 SQL…)

如果你把所有工具直接塞给主 Agent,系统提示词里会塞满 50+ 个工具的描述和参数。

后果:模型会混乱、选错工具、调用参数错误、甚至“幻觉”出不存在的工具。

所以必须引入子agent来做上下文的查询,我们不需要主agent知道那么多细节,只需要子agent去根据问题调查得到上下文,。对于这种子agent,agno提供了context provider来管理。

image1086×1448 305 KB


Context Provider

定义:Context Provider 是一个“子 Agent 包装器”,它把整个外部系统(如 Slack、GitHub、数据库)封装成一个只有两个工具的接口:query_<id>update_<id>,供主 Agent 调用。

它就是子agent创建。

不着急实现,我们理解一下原理:

现在主agent希望获取上下文,需要查询两个地方的信息,还有更新数据库(更新在这里也视为一种信息),流程就会如下:

主 Agent │ ┌──────────────┼──────────────┐ │ │ │ query_slack query_github update_database │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │Slack │ │GitHub │ │Database│ │子 Agent │ │子 Agent │ │子 Agent│ └────┬────┘ └─────┬─────┘ └────┬────┘ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │Slack API│ │GitHub API │ │SQL DB │ └─────────┘ └───────────┘ └─────────┘

agno提供了一些内置的provider,直接拥有一些工具

Provider 能力 读写分离
FilesystemContextProvider 读取本地目录的文件 只读
WebContextProvider 网络搜索(Exa 或 Parallel 后端) 只读
DatabaseContextProvider 执行 SQL(读/写) 支持只读和写入两个子引擎
SlackContextProvider 查询 Slack 历史、发送消息 读写
GDriveContextProvider 读取 Google Drive 文档 只读
GitHubContextProvider 读取仓库、创建 PR 读写(PR 隔离)
MCPContextProvider 连接任意 MCP 服务器(模型上下文协议) 取决于 MCP

最简单的例子:让 Agent 读取你电脑上的本地文件

from agno.agent import Agent from agno.context.filesystem import FilesystemContextProvider # 创建一个 Provider,指定要暴露的根目录 fs = FilesystemContextProvider(id="docs", root="./my_documents") agent = Agent( model=..., tools=fs.get_tools(), # 只有一个工具:query_docs ) agent.run("帮我找一下 ./my_documents 里有没有关于预算的文档")

Agent 内部会调用 query_docs,传给子 Agent,子 Agent 会遍历目录、读取文件、回答。


基本实现:

from agno.agent import Agent from agno.context.slack import SlackContextProvider from agno.context.web import WebContextProvider, ExaBackend # 1. 创建 Provider 实例 slack = SlackContextProvider(id="team_chat") # id 会成为工具名的一部分 web = WebContextProvider(backend=ExaBackend(), id="web") # 2. 将工具合并给主 Agent agent = Agent( model=..., tools=[*slack.get_tools(), *web.get_tools()], # 还可以把 Provider 自带的 instructions 合并进去 instructions="\n".join([slack.instructions(), web.instructions()]), ) # 3. 运行 agent.run("最近 Slack 里有人问过 Web 搜索吗?") # 主 Agent 会调用 query_team_chat 获取 Slack 内容,然后可能再调用 query_web 补充信息。

子agent我们自己也可以写,为什么要用provider

是的,子agent我们分分钟也可以写,不同的是,provider需要遵守contextprovider这个类的写法:

方面 自定义 ContextProvider 手写子 Agent + 工具
继承 必须继承 ContextProvider 无(或者继承 Agent,但通常直接实例化)
必须实现的方法 astatus, aquery(以及可选的 aupdate
返回值类型 必须包装成 Answer / Status 任意(通常返回 str
工具命名 自动变成 query_<id> 自己取名(可能冲突)
指令注入 可提供 instructions() 方法 需要手动拼接提示词
生命周期 自带 asetup/aclose 钩子 需要自己管理资源

我认为最大的好处还是统一,这个子agent的工具会自动命名,如`query_<provider_name>`,后续导出文档也很简单。劣势在于不自由,如果你的子agent很灵活,实现的功能不附属于主agent,那么就自便。

image1086×1448 252 KB

实现

暂时没有灵感,有灵感补上,或者评论来个佬友梦想一个小agent需求呗,我们来试试。

我现在学了这些东西,但是我觉得很多东西对我好像没有用武之地,所以我也经常在现实中思考,到底我们怎么做才能把agent落到我们生活的困难解决中去。

网友解答:
--【壹】--:

啥时候来个学skill的?啥时候来个学skill的?


--【贰】--:

搜集信息并整合,这是不是一个agent需求或者功能呢。

就是比如说“我想要了解20年前的世界,从类似吃穿住行经济发展等角度进行信息搜集”

这种我用 grok 或者 gemini 的 deep search 来获取信息,不能说没有吧但是最后获取的信息量也是极少的(肯定也和我的使用方法有关系,我看有的人能直接出来超多信息)。

问题描述:

从 【Agent笔记】我们一起来学习Agent!(第1期) 继续讨论;我第一期的时候就是想记录一下,因为我在开发agent项目。没想到佬们那么欢迎,我真的诚惶诚恐。

然后这个月实在太忙了:上半月一直在做自己的项目,希望拿到一些投资。前两天刚郴州旅游回来,然后手上两个团队项目在做,而gpt plus眼看着过期了,我就想是时候提高自己的提示词技术和相关知识了————所以也很忙。

今天本来打算写多点的,但是我想还是留到下一期————**我保证这会很快的!就这个星期!**因为今晚有小姐姐叫我打游戏了,休息一波

佬我准备买个coding plan了,50左右推荐一个呗,就那种玩具的,我经常需要写一个玩具项目,要求有GLM5左右的水平就可以,主要是周量够,业余开发。没有的话我打算买kimi那个80的了,老板还给我配了gpt plus就不用在自己项目了。

正文开始:


我们一起学Agent!(第二期)

Context(上下文)

定义:Agent在每次执行任务时能够获得的外部信息。一个agent的质量很大程度上取决于它能访问到的上下文——即使系统提示写得再完美,缺少正确的上下文(背景,语境,解决问题需要的信息)也会导致结果不佳。

image1086×1448 299 KB

在agno中,提供了三种原生方式进行context的提供

Knowledge(知识库)

我们需要预先做两个事情,第一建立对象绑定数据库,第二建好知识库

  • 创建一个 Knowledge 对象,绑定一个向量数据库(如 PgVector)。

  • 将内容从本地文件、URL 等添加到知识库中(自动分块、生成 embedding)。

调用方式如下:

from agno.agent import Agent from agno.knowledge import Knowledge from agno.vector_db.pgvector import PgVector agent = Agent( model=..., knowledge=Knowledge( vector_db=PgVector(table_name="my_kb", db_url="postgresql://...") ), search_knowledge=True, # 提供工具 add_knowledge_to_context=True, # 自动注入 ) agent.knowledge.add_content_from_path("docs/")

可以看到,Agent 通过两种方式使用知识库:

  • add_knowledge_to_context=True:每次运行前自动搜索相关知识,并注入到提示词中(传统 RAG)。

  • search_knowledge=True:为 Agent 提供一个 search_knowledge_base(query)工具,由 Agent 自主决定何时搜索(Agentic RAG)。

后者是目前我认为比较好的方向,前者是几年前流行的;因为模型的agent能力已经大幅上升,query的多,query的好。

Dependencies(依赖项)

看到这个词我双眼一黑,不过其实也还好;

定义:短暂、请求级别的动态值,不适合放在知识库中:如特性开关、租户 ID、每个请求的数据库连接、调用方专属的 API Key 等。

简单的说,就是“程序运行时动态捕捉的数据,主动注入给 Agent”。

你在写代码时,可以“捕捉”到很多东西:

  • Web 框架中,从请求头捕捉 user_id

  • 浏览器插件中,捕捉当前选中的文字 window.getSelection()

  • 调试工具中,捕捉你设置的 --debug 命令行参数

  • 定时任务中,捕捉当前时间 datetime.now()

比如说你写了一个浏览器插件agent,其中有程序可以捕捉用户在浏览器中的历史选中,程序发现用户在浏览中选中了`英伟达` `微软`这些词汇,那么当用户和你的agent对话时候,它们就会被注入;agent就会意识到用户似乎对这两个公司感兴趣;

同样的,那些用户的ip地址,ID,APIKEY,也是程序捕捉,注入到agent之中。

实现方式

  • 在创建 Agent 时传入一个 dependencies 字典,可包含任意结构的数据。

  • 设置 add_dependencies_to_context=True 后,这些依赖会被自动注入到系统提示词中。

  • 工具函数如果需要访问依赖,可以声明一个 RunContext 参数,通过 run_context.dependencies 读取。

代码如下:

from agno.agent import Agent from agno.run import RunContext from agno.tools import tool @tool def get_config(run_context: RunContext, key: str) -> str: return str(run_context.dependencies["config"].get(key)) agent = Agent( model=..., dependencies={ "config": {"region": "us-east-1", "max_retries": 3}, "feature_flags": {"beta_search": True}, }, add_dependencies_to_context=True, tools=[get_config], ) # 也可以在单次运行时覆盖依赖 agent.run("我的区域是哪里?", dependencies={"region": "eu-west-1"})


讲完了这些,我们就了解了两种常见的上下文注入,其实这两者都很简单。一个就是用知识库的方式,一个就是用程序自动注入的方式。

image1086×1448 261 KB

那如果我们需要的上下文比较复杂,不能通过简单查询,就马上得到。比如你需要agent知道今天公司的工作某个任务的进度情况,得同时操作 Slack、GitHub、Google Drive、公司数据库才能得到“这个任务进行到什么程度了”这个上下文。(注意,上下文不是你的目的,你的目的可能更大,需要决策明天的公司加班与否)

而当你定义工具,接入查询工具和其他工具,可能每个系统有 8~15 个 API(发送消息、搜索频道、创建 PR、列出文件、执行 SQL…)

如果你把所有工具直接塞给主 Agent,系统提示词里会塞满 50+ 个工具的描述和参数。

后果:模型会混乱、选错工具、调用参数错误、甚至“幻觉”出不存在的工具。

所以必须引入子agent来做上下文的查询,我们不需要主agent知道那么多细节,只需要子agent去根据问题调查得到上下文,。对于这种子agent,agno提供了context provider来管理。

image1086×1448 305 KB


Context Provider

定义:Context Provider 是一个“子 Agent 包装器”,它把整个外部系统(如 Slack、GitHub、数据库)封装成一个只有两个工具的接口:query_<id>update_<id>,供主 Agent 调用。

它就是子agent创建。

不着急实现,我们理解一下原理:

现在主agent希望获取上下文,需要查询两个地方的信息,还有更新数据库(更新在这里也视为一种信息),流程就会如下:

主 Agent │ ┌──────────────┼──────────────┐ │ │ │ query_slack query_github update_database │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │Slack │ │GitHub │ │Database│ │子 Agent │ │子 Agent │ │子 Agent│ └────┬────┘ └─────┬─────┘ └────┬────┘ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │Slack API│ │GitHub API │ │SQL DB │ └─────────┘ └───────────┘ └─────────┘

agno提供了一些内置的provider,直接拥有一些工具

Provider 能力 读写分离
FilesystemContextProvider 读取本地目录的文件 只读
WebContextProvider 网络搜索(Exa 或 Parallel 后端) 只读
DatabaseContextProvider 执行 SQL(读/写) 支持只读和写入两个子引擎
SlackContextProvider 查询 Slack 历史、发送消息 读写
GDriveContextProvider 读取 Google Drive 文档 只读
GitHubContextProvider 读取仓库、创建 PR 读写(PR 隔离)
MCPContextProvider 连接任意 MCP 服务器(模型上下文协议) 取决于 MCP

最简单的例子:让 Agent 读取你电脑上的本地文件

from agno.agent import Agent from agno.context.filesystem import FilesystemContextProvider # 创建一个 Provider,指定要暴露的根目录 fs = FilesystemContextProvider(id="docs", root="./my_documents") agent = Agent( model=..., tools=fs.get_tools(), # 只有一个工具:query_docs ) agent.run("帮我找一下 ./my_documents 里有没有关于预算的文档")

Agent 内部会调用 query_docs,传给子 Agent,子 Agent 会遍历目录、读取文件、回答。


基本实现:

from agno.agent import Agent from agno.context.slack import SlackContextProvider from agno.context.web import WebContextProvider, ExaBackend # 1. 创建 Provider 实例 slack = SlackContextProvider(id="team_chat") # id 会成为工具名的一部分 web = WebContextProvider(backend=ExaBackend(), id="web") # 2. 将工具合并给主 Agent agent = Agent( model=..., tools=[*slack.get_tools(), *web.get_tools()], # 还可以把 Provider 自带的 instructions 合并进去 instructions="\n".join([slack.instructions(), web.instructions()]), ) # 3. 运行 agent.run("最近 Slack 里有人问过 Web 搜索吗?") # 主 Agent 会调用 query_team_chat 获取 Slack 内容,然后可能再调用 query_web 补充信息。

子agent我们自己也可以写,为什么要用provider

是的,子agent我们分分钟也可以写,不同的是,provider需要遵守contextprovider这个类的写法:

方面 自定义 ContextProvider 手写子 Agent + 工具
继承 必须继承 ContextProvider 无(或者继承 Agent,但通常直接实例化)
必须实现的方法 astatus, aquery(以及可选的 aupdate
返回值类型 必须包装成 Answer / Status 任意(通常返回 str
工具命名 自动变成 query_<id> 自己取名(可能冲突)
指令注入 可提供 instructions() 方法 需要手动拼接提示词
生命周期 自带 asetup/aclose 钩子 需要自己管理资源

我认为最大的好处还是统一,这个子agent的工具会自动命名,如`query_<provider_name>`,后续导出文档也很简单。劣势在于不自由,如果你的子agent很灵活,实现的功能不附属于主agent,那么就自便。

image1086×1448 252 KB

实现

暂时没有灵感,有灵感补上,或者评论来个佬友梦想一个小agent需求呗,我们来试试。

我现在学了这些东西,但是我觉得很多东西对我好像没有用武之地,所以我也经常在现实中思考,到底我们怎么做才能把agent落到我们生活的困难解决中去。

网友解答:
--【壹】--:

啥时候来个学skill的?啥时候来个学skill的?


--【贰】--:

搜集信息并整合,这是不是一个agent需求或者功能呢。

就是比如说“我想要了解20年前的世界,从类似吃穿住行经济发展等角度进行信息搜集”

这种我用 grok 或者 gemini 的 deep search 来获取信息,不能说没有吧但是最后获取的信息量也是极少的(肯定也和我的使用方法有关系,我看有的人能直接出来超多信息)。