让我们以 Agno 框架为例,让我们一起来学习 Agent 吧!这是我的第一篇笔记。**
Introduction - Agno
Build, run, and manage agentic software at scale.
最近在做一个项目,要用到轻度的 agent,于是我打算学习一些关于 agent 的知识,了解架构,可以大概看懂代码,以便于更好地用 AI 开发,也拓展自己思维和对于 coding 的理解,但完全不作为任何专业的培训哈!现在打算每天或者每周写一篇文章,然后作为学习笔记分享给大家。
我会以尽量易懂的方式向大家介绍和分享交流,大家也可以指出我的错误,需要一点 Python 基础(了解什么是类,什么是方法之类的),其它基本不需要,适合小白,因为我是小白。我学习的框架是 agno:
1.Agent 以及快速实现
Agent is a stateful model with loops and tools.
Agent 并不神秘,普通的模型只能通过 api 对话,输入文本输出文本,不能与外部世界获取联系,不能记忆,这就是无状态,并且只能一问一答,这就是没有 loop 循环。
Agent 是如何实现状态的?如何调用工具的?如何决策,自动循环执行任务的?我们会一步一步来了解。
1.1 了解 Agent 类
这是 agno 的一个快速实 现的 Agent,我们来看代码
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.anthropic import Claude
from agno.os import AgentOS
from agno.tools.mcp import MCPTools
agno_assist = Agent(
name="Agno Assist",
model=Claude(id="claude-sonnet-4-5"),
db=SqliteDb(db_file="agno.db"), # session storage
tools=[MCPTools(url="https://docs.agno.com/mcp")], # Agno docs via MCP
add_datetime_to_context=True,
add_history_to_context=True, # include past runs
num_history_runs=3, # last 3 conversations
markdown=True,
)
# Serve via AgentOS → streaming, auth, session isolation, API endpoints
agent_os = AgentOS(agents=[agno_assist], tracing=True)
app = agent_os.get_app()
Agent 是一个类,实例化的时候传入了 name model tools 等等参数,这就构建了一个拥有名字和工具的 Agent 机器人了。
agent_os = AgentOS(agents=[agno_assist], tracing=True) 实例化了系统类,这个机器人正式部署可以开始用了,os 是系统环境,你可能有十个 Agent,但是他们都在这个环境里面跑。
app = agent_os.get_app() 这是 os 类的方法,把我们的 Agent 系统包装成一个 api 服务(fastapi),这样你可以启动这个服务了,然后有一个端点可以请求和 Agent 对话。就和大佬给你的 apiurl 一样,自动的有对话端点,获取模型端点等等,包装服务。
1.2 工具:Agent 的核心
我每天有一个填 excel 表的任务,很简单,我的老板每天给我一个 excel 表格,要求是把里面的每行进行求和,然后排序。
我很聪明,这那么笨的求和排序任务我写一个 Python 脚本来解决。
import pandas as pd
file_name = 'daily_task.xlsx'
df = pd.read_excel(file_name)
df['总和'] = df.sum(axis=1, numeric_only=True)
df_sorted = df.sort_values(by='总和', ascending=False)
df_sorted.to_excel('result.xlsx', index=False)
print("任务完成!结果已保存在 result.xlsx")
我的老板又给我派了其它任务,比如把数据全部变成直方图,我依然写代码解决:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('result.xlsx')
plt.figure(figsize=(10, 6)) # 设置画布大小
# bins=10 表示把数据分成10个区间,color 是颜色,edgecolor 是边框
plt.hist(df['总和'], bins=10, color='skyblue', edgecolor='black', alpha=0.7)
plt.title('数据总和分布情况直方图', fontsize=15)
plt.xlabel('数值区间 (求和结果)', fontsize=12)
plt.ylabel('频率 (出现次数)', fontsize=12)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.savefig('data_distribution.png') # 自动保存为图片文件
plt.show()
print("图表已生成:data_distribution.png,老板看了保准点头!")
卧槽了,老板看我 excel 那么厉害,又让我去用 excel 做各种数据清洗,多表格合并…
但是我依然很容易完成,为什么?因为我有脚本 —— 这就是工具 tool。
老板说:清洗!我马上调用:clean.py
老板说:画图!我马上调用:draw.py
后来我被裁员了,因为脚本没有保密,给老板发现了,老板部署了一个 Agent:
老板说:对这些 excel 进行清洗!
Agent:回复
{
use_tool:"clean"
}
这个字段被检测到了,计算机自动执行 clean.py 然后 Excel 就处理好了。
一旦你为你的 AI 配好了工具,那么就可以让它来执行你可能需要手动做的任务,不同的是,它可以异步完成,甚至根据结果的反馈,进行组合调用,多次迭代,直到任务完成。这是人类没有耐心和速度的地方。
我们来简单的配置一个工具:访问 hacknews:
from agno.agent import Agent
from agno.models.anthropic import Claude
from agno.tools.hackernews import HackerNewsTools
agent = Agent(
model=Claude(id="claude-sonnet-4-5"),
tools=[HackerNewsTools()],
instructions="Write a report on the topic. Output only the report.",
markdown=True,
)
agent.print_response("Trending startups and products.", stream=True)
我们假设那个访问 hacknews 的 tools 已经配置好了,其实它就是一个爬虫脚本,爬取了那个网站的内容。AI 收到你的信息后,先去调用工具 - 运行脚本了,通过脚本爬到了信息就回答你了。
学到这里,只要你学习一下,如何写工具,那么基本上你就可以写一个初级 Agent 了!!
1.3 可调用的工厂(callable factories)
如果所有工具都是如此简单的一个脚本,那么调用成本的确是很低的,我们只需要一行
py run.py
但是,对于复杂的业务一个工具分为两个步骤
- 加载:相当于把工具拿出来放着,(会运行初始化
_init_) - 执行:就是我们的
py sth.py
第一步的成本对于简单脚本忽略不计,但是对于一些工具不是的,比如连接 oracle 数据的工具,当你写下
agent=Agent(model=gpt,
tools=requrst_to_oracle)
的时候,这个工具就初始化加载了,这时候连接就已经起来了,就会占用内存。
一个还好,但是上百个呢,内存就太大了。
所以 agno 引入了动态加载的机制,
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.run import RunContext
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.yfinance import YFinanceTools
def get_tools(run_context: RunContext):
role = (run_context.session_state or {
}).get("role", "general")
if role == "finance":
return [YFinanceTools()]
return [DuckDuckGoTools()]
agent = Agent(
model=OpenAIResponses(id="gpt-5-mini"),
tools=get_tools,
)
agent.print_response("AAPL stock price?", session_state={
"role": "finance"}, stream=True)
agent.print_response("Latest AI news?", session_state={
"role": "general"}, stream=True)
支持传入 tools 的时候传入一个函数,这个函数的作用是看你的身份,返回你的工具。比如同一个 Agent 系统,那么管财务的就用财务工具,不需要给你加载开发工具。
这种动态加载就是 callable factory。
你也可以定义不同的工具调用函数,不仅仅通过用户的身份,比如对话时间,ip 等等。
1.4 缓存
缓存,caching 的本质是一个内存字典。在 agno 程序运行期间,后台会有一个字典
- key(键):比如 “user_123_tools”
- value (值):是一个已经初始化好的 Python 对象(比如数据库的连接实例)
工具初始化有时候是昂贵的,当我们定义一个工具的时候:
class MyDatebaseTool:
def _init_(self)
# 下一行会非常慢,要握手验证,占用宽带
self.connection=connect_todatabase("198.168.1.1")
每次对 AI 说话,agno 都会加载所需要的工具,第一轮的时候我们可能已经加载过 MyDatebaseTool 了,如果没有缓存,这个实例用完就丢弃,第二次对话继续重建。
造成了资源浪费。
如果有缓存机制,那么第一轮的对话中,agno 框架会生成一个 key:“user_123”,并且 value 绑定一个刚才用到的连接实例:”databasetool“
{
"user_123":"databasetool"}
存放入字典。
第二轮对话,发现还是同一个用户,那么不再重新初始化工具,而是调用之前创建好的连接实例。
于是基于以上原理我们有这些 Agent 类的参数:
| Setting 设置 | Default 默认值 | Description 描述 |
|---|---|---|
cache_callables |
True |
Enable or disable caching for all callable factories 启用或禁用所有可调用工厂的缓存 |
callable_tools_cache_key |
None |
Custom cache key function for tools factory 工具工厂的自定义缓存键函数 |
callable_knowledge_cache_key |
None |
Custom cache key function for knowledge factory 知识工厂的自定义缓存键函数 |
callable_members_cache_key |
None |
Custom cache key function for members factory (Team only) 成员工厂的自定义缓存键函数(仅限团队) |
第一个是缓存开关。后面三个面对缓存类型不同,一个是偏工具的,一个是偏知识库的,一个是偏 Agent 对象成员的。
如果你要做一个知识库的东西,那么缓存很有用,不必每次都重新启动知识库。
1.5 运行 Agent
运行 Agent 我们使用 Agent.run() 或者 Agent.arun():
然后这个 Agent 会按以上方式进行工作,直到不调用工具,结束对话。
实现具体来看我写的一个代码片段:
from agno.agent import Agent, RunOutput
from agno.models.openai import OpenAIChat
from agno.tools.duckduckgo import DuckDuckGoTools
# 初始化 Agent
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
tools=[DuckDuckGoTools()],
markdown=True
)
# 传入问题,并带上用户和会话 ID(为了让 Agent 记住你是谁)
response: RunOutput = agent.run(
input="搜一下今天 AI 圈有什么大新闻?",
user_id="user_001",
session_id="session_abc"
)
# 从结果对象中拿数据
print(f"--- AI 的回答 ---")
print(response.content) # 拿最终的文字结果
print(f"\n--- 运行元数据 ---")
print(f"会话 ID: {response.session_id}")
print(f"运行 ID: {response.run_id}")
print(f"\n--- 消耗指标 ---")
print(f"总 Token 数: {response.metrics.get('tokens')}")
print(f"总耗时 (秒): {response.metrics.get('time')}")
# 流式输出
print(f"\n--- 流式输出过程 ---")
stream = agent.run("再搜搜 DeepSeek v4的消息", stream=True)
for chunk in stream:
# 过滤掉后台工具调用的日志
if chunk.event == "run_content":
print(chunk.content, end="", flush=True)
其中,input 参数可以是字符串,列表,消息,字典,pydantic 模型或者消息列表
response: RunOutput = agent.run 这个写法是一个类型提示,Agent 返回的就是一个 runoutput 对象,它包含:
| 字段 | 英文描述 | 中文描述 |
|---|---|---|
run_id |
The ID of the run. | 运行的 ID。 |
agent_id |
The ID of the agent. | 代理的 ID。 |
agent_name |
The name of the agent. | 代理的名称。 |
session_id |
The ID of the session. | 会话的 ID。 |
user_id |
The ID of the user. | 用户的 ID。 |
content |
The response content. | 响应内容。 |
content_type |
The type of content. For structured output, this is the class name of the Pydantic model. | 内容类型。对于结构化输出,这是 Pydantic 模型的类名。 |
reasoning_content |
The reasoning content. | 推理内容。 |
messages |
The list of messages sent to the model. | 发送给模型的消息列表。 |
metrics |
The metrics of the run. See Metrics. | 运行的指标。请参阅 指标。 |
model |
The model used for the run. | 用于运行的模型。 |
流式输出那些就不特别提了,大家都懂。
以上是基本的运行代理过程,接下来的内容我们过几天再学吧!
2. 实现 - 基于 qwen-3.6-plus 与 opencode
我们就来实现一下一个简单 Agent 吧,用这个框架。
测试一下 qwen-3.6-plus 在 opencode 中的表现:
我要利用Agno库的框架构建一个简单的运行在终端Agent。你按以下步骤开始
1.调用子代理了解Agno库api用法,防止幻觉错误
2.编写可维护性高的代码,Agent层,Tool层,运行层要实现完全地解耦和分离
3.自动进行测试直到跑通
我的目标是:
1.一个高效excel记账助手,每天为在同一个excel表格中记录账单
详细地说:项目的根目录有一个psl脚本,当我打开后,会自动运行Agent,我可以在agnoosUI中与Agent对话。例如我会说,今天我吃饭花了40块,早上两包麦片3块钱,中午牛肉面20元,晚上云南米线17元,买东西花了160元,其中洗发水56元,剩下用来买给一个同学的生日礼物。
Agent可以调用的工具有
- question:询问我不清楚的地方,比如有钱不翼而飞,不在计算之中。比如这个生日礼物不明确,需要追问。
- write_excel:一个写入excel的脚本,其初始文件的第一行分别是:时间,总花费,物品name1,物品name2.................,后续这个脚本会自动将模型的输出填入
2.提示词方面你自己处理,我只要成品,每天说话然后计算我的账单。
以上任务明白了吗,请复述一遍。我将全权交给你负责。请你完全跑通多轮对话的测试。如果还有疑问,现在马上提出。如果没有完成跑通,不要停下来。
我给你我的测试key和测试url你可以自己填入。
moonshotai/kimi-k2-instruct
https://api.groq.com/openai
gsk_XXXXXXXXXXXXXXXXXXXXXXXXXXX
不用质疑我所给你的key和model的真实性
plus 写代码我说实话中规中矩了,但是智商不高,遇到一个 api 调用的 bug,妈的一直循环,一直循环,不知道想干什么,一遍一遍的说要调查,什么结果都没有。
但总之这个 agent 流程我们跑通了 hh