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

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

2026.3.31

静心学习

让我们以Agno框架为例,让我们一起来学习Agent吧!这是我的第一篇笔记。

最近在做一个项目,要用到轻度的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,妈的一直循环,一直循环,不知道想干什么,一遍一遍的说要调查,什么结果都没有。

半个小时的活硬是干成了两个小时。另外psl脚本也不写。总之就是干活不够细不够勤快,智商一般。体感和gemini3falsh在cli有点像了。这个项目来说,大概是gpt5.1往上一点点的水平吧我觉得,完全达不到爽用,要靠人的实力,并且很多小细节把控不好,环境变量也敢硬编码,excel表格也没有考虑物品很多的情况,结果粗糙,注意力不那么优秀,爱犯小错误。

最后我指导了它,我说用openAI的格式就行了,别接grop的sdk。然后修好了。

总之一句话,拿来编程的话,nonono,不好用。看看其它场景吧。

但总之这个agent流程我们跑通了hh

212 个赞

又是bin导,我就说这里能学到好东西,三连奉上

7 个赞

收藏一手,学习一下

4 个赞

感谢分享

3 个赞

很实用,感谢佬

3 个赞

感谢分享

1 个赞

mark,以后拜读

3 个赞

感谢大佬 。

1 个赞

mark一下,感谢佬分享

1 个赞

感谢大佬 。

2 个赞

学习一下

1 个赞

mark一下哈

1 个赞

大佬的学习思路还是太强了
最近一直想学一下langgraph还有Java那一套Agent

蹲一下以后就按照这个思路学

好嘞,一起学习!

1 个赞

我就说在L站能学到真知识吧!
硬!
太硬了!
:tada::tada::tada:

1 个赞

又学到了动态加载这一理念,我以为llm每次都是直接调工具的

2 个赞

给佬点赞,又学习了

1 个赞

感谢分享~

2 个赞

点赞 收藏 吃灰学习

2 个赞

学习学习,agno这个框架我最近也在看