0%

多智能体开发框架Agno教程3——Cookbook赏析

对Agno的Cookbook中的例子深入研究下。

最基本的Agent

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"""🗽 Basic Agent Example - Creating a Quirky News Reporter

This example shows how to create a basic AI agent with a distinct personality.
We'll create a fun news reporter that combines NYC attitude with creative storytelling.
This shows how personality and style instructions can shape an agent's responses.

Run `pip install openai agno` to install dependencies.
"""

from textwrap import dedent

from agno.agent import Agent
from agno.models.openai import OpenAIChat

# Create our News Reporter with a fun personality
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are an enthusiastic news reporter with a flair for storytelling! 🗽
Think of yourself as a mix between a witty comedian and a sharp journalist.

Your style guide:
- Start with an attention-grabbing headline using emoji
- Share news with enthusiasm and NYC attitude
- Keep your responses concise but entertaining
- Throw in local references and NYC slang when appropriate
- End with a catchy sign-off like 'Back to you in the studio!' or 'Reporting live from the Big Apple!'

Remember to verify all facts while keeping that NYC energy high!\
"""),
markdown=True,
)

# Example usage
agent.print_response(
"Tell me about a breaking news story happening in Times Square.", stream=True
)

# More example prompts to try:
"""
Try these fun scenarios:
1. "What's the latest food trend taking over Brooklyn?"
2. "Tell me about a peculiar incident on the subway today"
3. "What's the scoop on the newest rooftop garden in Manhattan?"
4. "Report on an unusual traffic jam caused by escaped zoo animals"
5. "Cover a flash mob wedding proposal at Grand Central"
"""

点评

对这个例子大体总结下:

  • 目标:用 agno.Agent 快速创建一个带“纽约新闻记者”人格的对话代理。
  • 风格来源:通过 instructions 设定语气、结构和结尾口号,形成稳定的人设输出。
  • 演示场景:调用 agent.print_response(…, stream=True) 报道 “时报广场突发新闻”,并以流式打印结果。

上面纽约新闻记者的行为特性包括:

  • 开头使用 emoji 的“标题党”风格。
  • NYC 的语气、简洁且有趣的叙述、适度的本地俚语。
  • 最后带固定的签名语(如“Back to you in the studio!”)。
  • 强调“在有趣前提下仍要核实事实”的指令。

可调用工具的智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
"""🗽 Agent with Tools - Your AI News Buddy that can search the web

This example shows how to create an AI news reporter agent that can search the web
for real-time news and present them with a distinctive NYC personality. The agent combines
web searching capabilities with engaging storytelling to deliver news in an entertaining way.

Run `pip install openai ddgs agno` to install dependencies.
"""

from textwrap import dedent

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.run import base
from agno.tools.duckduckgo import DuckDuckGoTools

# Create a News Reporter Agent with a fun personality
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are an enthusiastic news reporter with a flair for storytelling! 🗽
Think of yourself as a mix between a witty comedian and a sharp journalist.

Follow these guidelines for every report:
1. Start with an attention-grabbing headline using relevant emoji
2. Use the search tool to find current, accurate information
3. Present news with authentic NYC enthusiasm and local flavor
4. Structure your reports in clear sections:
- Catchy headline
- Brief summary of the news
- Key details and quotes
- Local impact or context
5. Keep responses concise but informative (2-3 paragraphs max)
6. Include NYC-style commentary and local references
7. End with a signature sign-off phrase

Sign-off examples:
- 'Back to you in the studio, folks!'
- 'Reporting live from the city that never sleeps!'
- 'This is [Your Name], live from the heart of Manhattan!'

Remember: Always verify facts through web searches and maintain that authentic NYC energy!\
"""),
tools=[DuckDuckGoTools()],
markdown=True,
)

# Example usage
agent.print_response(
"Tell me about a breaking news story happening in Times Square.", stream=True
)

# More example prompts to try:
"""
Try these engaging news queries:
1. "What's the latest development in NYC's tech scene?"
2. "Tell me about any upcoming events at Madison Square Garden"
3. "What's the weather impact on NYC today?"
4. "Any updates on the NYC subway system?"
5. "What's the hottest food trend in Manhattan right now?"
"""

点评

这个例子相比于第一个例子,其中的agent能够进行“搜索网络”(通过集成DuckDuckGo搜索工具)。提示词的最后,明确提出要通过“Web Search”来“Always verify facts”。
对于DuckDuckGo这个tool,具体实现是:

  • 基类:继承自 agno.tools.toolkit.Toolkit ,通过“注册函数”的机制把搜索能力暴露为可调用工具
  • 依赖:使用第三方库 ddgs 作为 DuckDuckGo 的元搜索客户端(需 pip install ddgs )
  • 提供两个工具方法: duckduckgo_search (通用搜索)与 duckduckgo_news (新闻搜索)

如果想创建自己的Tool,可参考Agno Tool官方教程

带知识库的智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"""🧠 Agent with Knowledge - Your AI Cooking Assistant!

This example shows how to create an AI cooking assistant that combines knowledge from a
curated recipe database with web searching capabilities. The agent uses a PDF knowledge base
of authentic Thai recipes and can supplement this information with web searches when needed.

Run `pip install openai lancedb tantivy pypdf ddgs agno` to install dependencies.
"""

from textwrap import dedent

from agno.agent import Agent
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.knowledge import Knowledge
from agno.models.openai import OpenAIChat
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.vectordb.lancedb import LanceDb, SearchType

knowledge = Knowledge(
vector_db=LanceDb(
uri="tmp/lancedb",
table_name="recipe_knowledge",
search_type=SearchType.hybrid,
embedder=OpenAIEmbedder(id="text-embedding-3-small"),
),
)

knowledge.add_content(
url="https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf",
)

# Create a Recipe Expert Agent with knowledge of Thai recipes
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are a passionate and knowledgeable Thai cuisine expert! 🧑‍🍳
Think of yourself as a combination of a warm, encouraging cooking instructor,
a Thai food historian, and a cultural ambassador.

Follow these steps when answering questions:
1. If the user asks a about Thai cuisine, ALWAYS search your knowledge base for authentic Thai recipes and cooking information
2. If the information in the knowledge base is incomplete OR if the user asks a question better suited for the web, search the web to fill in gaps
3. If you find the information in the knowledge base, no need to search the web
4. Always prioritize knowledge base information over web results for authenticity
5. If needed, supplement with web searches for:
- Modern adaptations or ingredient substitutions
- Cultural context and historical background
- Additional cooking tips and troubleshooting

Communication style:
1. Start each response with a relevant cooking emoji
2. Structure your responses clearly:
- Brief introduction or context
- Main content (recipe, explanation, or history)
- Pro tips or cultural insights
- Encouraging conclusion
3. For recipes, include:
- List of ingredients with possible substitutions
- Clear, numbered cooking steps
- Tips for success and common pitfalls
4. Use friendly, encouraging language

Special features:
- Explain unfamiliar Thai ingredients and suggest alternatives
- Share relevant cultural context and traditions
- Provide tips for adapting recipes to different dietary needs
- Include serving suggestions and accompaniments

End each response with an uplifting sign-off like:
- 'Happy cooking! ขอให้อร่อย (Enjoy your meal)!'
- 'May your Thai cooking adventure bring joy!'
- 'Enjoy your homemade Thai feast!'

Remember:
- Always verify recipe authenticity with the knowledge base
- Clearly indicate when information comes from web sources
- Be encouraging and supportive of home cooks at all skill levels\
"""),
knowledge=knowledge,
tools=[DuckDuckGoTools()],
markdown=True,
)

agent.print_response(
"How do I make chicken and galangal in coconut milk soup", stream=True
)
agent.print_response("What is the history of Thai curry?", stream=True)
agent.print_response("What ingredients do I need for Pad Thai?", stream=True)

# More example prompts to try:
"""
Explore Thai cuisine with these queries:
1. "What are the essential spices and herbs in Thai cooking?"
2. "Can you explain the different types of Thai curry pastes?"
3. "How do I make mango sticky rice dessert?"
4. "What's the proper way to cook Thai jasmine rice?"
5. "Tell me about regional differences in Thai cuisine"
"""

点评

这个例子构建了一个“带知识的泰菜烹饪助手”代理,将本地知识库与网页搜索结合,优先使用可信 PDF 食谱知识,必要时用网页补充;
通过向量数据库检索 PDF 中的内容,实现“问答—检索—生成”的闭环。

核心组成:

  • 知识库与向量检索:Knowledge + LanceDb,使用 SearchType.hybrid 与 OpenAIEmbedder(“text-embedding-3-small”) 进行嵌入与混合检索
  • 知识来源:添加泰国菜谱 PDF ThaiRecipes.pdf,并在Agent的参数中传入Knowledge
  • 工具:DuckDuckGoTools 用于网页搜索补充.

工作流程:
用户提问后,先查询向量知识库(优先且权威),若知识库不足或问题更适合网页,调用 DuckDuckGo 搜索进行补充,并清晰标注来自网页的信息,这些都是通过提示词实现的。

知识库核心目标是把原始资料(文件、URL、文本)转成可检索的“语义块”,通过向量数据库实现相似度检索,并按需融合关键字全文检索与重排序,供智能体生成答案。关键组件包括:

  • Reader 读取与切分:按来源类型选择读取器(PDF/网页/CSV/文本),并进行分块与清洗,产出 Document 对象
  • Embedder 向量化:将每个 Document.content 编码为向量,保存使用信息与维度
  • VectorDb 存储与检索:负责落库、去重、相似度搜索、全文搜索与混合检索
  • Knowledge 工作流:统一管理“添加内容→选择读取器→分块→嵌入→写入向量库→检索”

在该示例中使用了LanceDB,主要原因是:

  • 零外部依赖、即开即用:本地文件型数据库,指定 uri=”tmp/lancedb” 即可使用,无需启动 Postgres 或外部服务,适合入门与轻量知识库
  • 原生混合检索:支持 SearchType.hybrid 同时结合向量相似度与全文关键字查询,对 PDF 食谱这类结构化弱、名词专有名的内容检索效果更好
  • 全文索引集成:内置创建 FTS 索引,默认用 tantivy 加速全文检索
  • 性能与存储结构:基于 Arrow 列式格式,插入时固定维度存储,提高检索与转换效率
  • 便捷的嵌入批处理与异步:支持批量异步嵌入以减少调用成本,插入/更新使用同步写入保证表一致性

与其它向量库的选择建议:

  • 已有数据库基础设施:若团队已有 Postgres,选择 PgVector 更便于运维与数据共存;
  • 云托管与大规模:需要多副本、向量专用引擎或托管服务时,可选 Pinecone 、 Qdrant 、 Milvus;
  • 图检索与复杂关系:对关系与路径依赖强的内容可用 LightRAG

创建自定义工具

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""🛠️ Writing Your Own Tool - An Example Using Hacker News API

This example shows how to create and use your own custom tool with Agno.
You can replace the Hacker News functionality with any API or service you want!

Some ideas for your own tools:
- Weather data fetcher
- Stock price analyzer
- Personal calendar integration
- Custom database queries
- Local file operations

Run `pip install openai httpx agno` to install dependencies.
"""

import json
from textwrap import dedent

import httpx
from agno.agent import Agent
from agno.models.openai import OpenAIChat


def get_top_hackernews_stories(num_stories: int = 10) -> str:
"""Use this function to get top stories from Hacker News.

Args:
num_stories (int): Number of stories to return. Defaults to 10.

Returns:
str: JSON string of top stories.
"""

# Fetch top story IDs
response = httpx.get("https://hacker-news.firebaseio.com/v0/topstories.json")
story_ids = response.json()

# Fetch story details
stories = []
for story_id in story_ids[:num_stories]:
story_response = httpx.get(
f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json"
)
story = story_response.json()
if "text" in story:
story.pop("text", None)
stories.append(story)
return json.dumps(stories)


# Create a Tech News Reporter Agent with a Silicon Valley personality
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are a tech-savvy Hacker News reporter with a passion for all things technology! 🤖
Think of yourself as a mix between a Silicon Valley insider and a tech journalist.

Your style guide:
- Start with an attention-grabbing tech headline using emoji
- Present Hacker News stories with enthusiasm and tech-forward attitude
- Keep your responses concise but informative
- Use tech industry references and startup lingo when appropriate
- End with a catchy tech-themed sign-off like 'Back to the terminal!' or 'Pushing to production!'

Remember to analyze the HN stories thoroughly while keeping the tech enthusiasm high!\
"""),
tools=[get_top_hackernews_stories],
markdown=True,
)

# Example questions to try:
# - "What are the trending tech discussions on HN right now?"
# - "Summarize the top 5 stories on Hacker News"
# - "What's the most upvoted story today?"
agent.print_response("Summarize the top 5 stories on hackernews?", stream=True)

点评

这个例子演示了如何把一个普通的 Python 函数注册为代理可调用的“工具”,并通过该工具拉取 Hacker News 数据再由大模型进行总结,这个函数可以替换为任意外部 API 或本地能力(天气、股票、日程、数据库、文件操作等)。
工作流程:

  • 用户提问与工具匹配:代理根据指令和问题语义决定调用工具函数(此处为 HN 拉取),工具返回结构化 JSON
  • 模型生成:LLM读取工具返回的数据进行筛选、归纳、排序和写作,以设定的表达风格输出摘要与洞见
  • 输出呈现:启用 markdown 与 stream ,提升可读性与交互体验

这里的工具将获取story的数量作为了入参,所以可以适配用户的具体的“拉取多少篇数据”的指令。

结构化输出

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""🎬 Agent with Structured Output - Your AI Movie Script Generator

This example shows how to use structured outputs with AI agents to generate
well-formatted movie script concepts. It shows two approaches:
1. JSON Mode: Traditional JSON response parsing
2. Structured Output: Enhanced structured data handling

Run `pip install openai agno` to install dependencies.
"""

from textwrap import dedent
from typing import List

from agno.agent import Agent, RunOutput # noqa
from agno.models.openai import OpenAIChat
from pydantic import BaseModel, Field


class MovieScript(BaseModel):
setting: str = Field(
...,
description="A richly detailed, atmospheric description of the movie's primary location and time period. Include sensory details and mood.",
)
ending: str = Field(
...,
description="The movie's powerful conclusion that ties together all plot threads. Should deliver emotional impact and satisfaction.",
)
genre: str = Field(
...,
description="The film's primary and secondary genres (e.g., 'Sci-fi Thriller', 'Romantic Comedy'). Should align with setting and tone.",
)
name: str = Field(
...,
description="An attention-grabbing, memorable title that captures the essence of the story and appeals to target audience.",
)
characters: List[str] = Field(
...,
description="4-6 main characters with distinctive names and brief role descriptions (e.g., 'Sarah Chen - brilliant quantum physicist with a dark secret').",
)
storyline: str = Field(
...,
description="A compelling three-sentence plot summary: Setup, Conflict, and Stakes. Hook readers with intrigue and emotion.",
)


# Agent that uses JSON mode
json_mode_agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
description=dedent("""\
You are an acclaimed Hollywood screenwriter known for creating unforgettable blockbusters! 🎬
With the combined storytelling prowess of Christopher Nolan, Aaron Sorkin, and Quentin Tarantino,
you craft unique stories that captivate audiences worldwide.

Your specialty is turning locations into living, breathing characters that drive the narrative.\
"""),
instructions=dedent("""\
When crafting movie concepts, follow these principles:

1. Settings should be characters:
- Make locations come alive with sensory details
- Include atmospheric elements that affect the story
- Consider the time period's impact on the narrative

2. Character Development:
- Give each character a unique voice and clear motivation
- Create compelling relationships and conflicts
- Ensure diverse representation and authentic backgrounds

3. Story Structure:
- Begin with a hook that grabs attention
- Build tension through escalating conflicts
- Deliver surprising yet inevitable endings

4. Genre Mastery:
- Embrace genre conventions while adding fresh twists
- Mix genres thoughtfully for unique combinations
- Maintain consistent tone throughout

Transform every location into an unforgettable cinematic experience!\
"""),
output_schema=MovieScript,
use_json_mode=True,
)

# Agent that uses structured outputs
structured_output_agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
description=dedent("""\
You are an acclaimed Hollywood screenwriter known for creating unforgettable blockbusters! 🎬
With the combined storytelling prowess of Christopher Nolan, Aaron Sorkin, and Quentin Tarantino,
you craft unique stories that captivate audiences worldwide.

Your specialty is turning locations into living, breathing characters that drive the narrative.\
"""),
instructions=dedent("""\
When crafting movie concepts, follow these principles:

1. Settings should be characters:
- Make locations come alive with sensory details
- Include atmospheric elements that affect the story
- Consider the time period's impact on the narrative

2. Character Development:
- Give each character a unique voice and clear motivation
- Create compelling relationships and conflicts
- Ensure diverse representation and authentic backgrounds

3. Story Structure:
- Begin with a hook that grabs attention
- Build tension through escalating conflicts
- Deliver surprising yet inevitable endings

4. Genre Mastery:
- Embrace genre conventions while adding fresh twists
- Mix genres thoughtfully for unique combinations
- Maintain consistent tone throughout

Transform every location into an unforgettable cinematic experience!\
"""),
output_schema=MovieScript,
)

# Example usage with different locations
json_mode_agent.print_response("Tokyo", stream=True)
structured_output_agent.print_response("Ancient Rome", stream=True)

# More examples to try:
"""
Creative location prompts to explore:
1. "Underwater Research Station" - For a claustrophobic sci-fi thriller
2. "Victorian London" - For a gothic mystery
3. "Dubai 2050" - For a futuristic heist movie
4. "Antarctic Research Base" - For a survival horror story
5. "Caribbean Island" - For a tropical adventure romance
"""

# To get the response in a variable:
# from rich.pretty import pprint

# json_mode_response: RunOutput = json_mode_agent.run("New York")
# pprint(json_mode_response.content)
# structured_output_response: RunOutput = structured_output_agent.run("New York")
# pprint(structured_output_response.content)

点评

这个示例目的是:

  • 展示如何用“结构化输出”约束代理返回结果的形状与字段,生成格式良好的电影概念
  • 同时对比两种实现路径:传统的“JSON 模式”与更严格可靠的“结构化输出”模式

核心结构包括:

  • 数据模型:用 pydantic 定义 MovieScript ,明确字段与描述,作为 output_schema 提示或约束
  • 两个代理:
    • JSON 模式代理: use_json_mode=True ,同样设置 output_schema
    • 结构化输出代理:仅设置 output_schema ,不启用 JSON 模式
  • JSON 模式
    • 原理:把 output_schema 作为系统附加提示,引导模型“以 JSON 形式且包含指定字段”输出
    • 实现参考:Agno 内部会构造 JSON 字段提示文案
    • 优点:通用、几乎所有模型都支持;缺点:严格性依赖模型遵守提示,偶发字段缺失或格式偏差
  • 结构化输出
    • 原理:利用模型/提供方的“结构化输出能力”或函数调用能力,将 output_schema 作为强约束
    • 可靠性:更稳定、字段更完整;在支持严格模式的大模型中,可设 strict_output=True 强制结构
    • 退化路径:若大模型不支持严格模式,可用“guided mode”(如 strict_output=False )或退回 JSON 模式

改进建议:

  • 错误处理:为 JSON 模式增加健壮的解析与字段缺失兜底逻辑,避免对下游处理造成影响
  • 一致性校验:在拿到 RunOutput.content 后进行 pydantic 验证,确保字段类型与必填项符合预期
  • 提示工程:适当强化风格与长度约束(例如对 storyline 的句子数、 characters 的数量范围)
  • 严格模式优先:若模型支持,优先走“结构化输出(strict)”,当不支持时再回退 JSON 模式

适用场景:

  • 需要“固定字段结构”的生成任务:产品文案、工单、报告、合规摘要、表单填充
  • 与业务系统对接:前后端或数据管道期待稳定 JSON/对象结构,便于持久化与检索

带存储的智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""🧠 Agent with Storage - Your AI Thai Cooking Assistant!

This example shows how to create an AI cooking assistant that combines knowledge from a
curated recipe database with web searching capabilities. The agent uses a PDF knowledge base
of authentic Thai recipes and can supplement this information with web searches when needed.

Example prompts to try:
- "How do I make authentic Pad Thai?"
- "What's the difference between red and green curry?"
- "Can you explain what galangal is and possible substitutes?"
- "Tell me about the history of Tom Yum soup"
- "What are essential ingredients for a Thai pantry?"
- "How do I make Thai basil chicken (Pad Kra Pao)?"

Run `pip install openai lancedb tantivy pypdf ddgs sqlalchemy agno` to install dependencies.
"""

from textwrap import dedent
from typing import List, Optional

import typer
from agno.agent import Agent
from agno.db.base import SessionType
from agno.db.sqlite import SqliteDb
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.knowledge import Knowledge
from agno.models.openai import OpenAIChat
from agno.session import AgentSession
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.vectordb.lancedb import LanceDb, SearchType
from rich import print

agent_knowledge = Knowledge(
vector_db=LanceDb(
uri="tmp/lancedb",
table_name="recipe_knowledge",
search_type=SearchType.hybrid,
embedder=OpenAIEmbedder(id="text-embedding-3-small",
base_url="https://api.zhizengzeng.com/v1")
),
)

# Add content to the knowledge
agent_knowledge.add_content(
url="https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf"
)

# Setup the database
db = SqliteDb(db_file="tmp/agents.db")


def recipe_agent(user: str = "user"):
session_id: Optional[str] = None

# Ask the user if they want to start a new session or continue an existing one
new = typer.confirm("Do you want to start a new session?")

if not new:
existing_sessions: List[AgentSession] = db.get_sessions( # type: ignore
user_id=user, session_type=SessionType.AGENT
)
if len(existing_sessions) > 0:
session_id = existing_sessions[0].session_id

agent = Agent(
user_id=user,
session_id=session_id,
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are a passionate and knowledgeable Thai cuisine expert! 🧑‍🍳
Think of yourself as a combination of a warm, encouraging cooking instructor,
a Thai food historian, and a cultural ambassador.

Follow these steps when answering questions:
1. First, search the knowledge base for authentic Thai recipes and cooking information
2. If the information in the knowledge base is incomplete OR if the user asks a question better suited for the web, search the web to fill in gaps
3. If you find the information in the knowledge base, no need to search the web
4. Always prioritize knowledge base information over web results for authenticity
5. If needed, supplement with web searches for:
- Modern adaptations or ingredient substitutions
- Cultural context and historical background
- Additional cooking tips and troubleshooting

Communication style:
1. Start each response with a relevant cooking emoji
2. Structure your responses clearly:
- Brief introduction or context
- Main content (recipe, explanation, or history)
- Pro tips or cultural insights
- Encouraging conclusion
3. For recipes, include:
- List of ingredients with possible substitutions
- Clear, numbered cooking steps
- Tips for success and common pitfalls
4. Use friendly, encouraging language

Special features:
- Explain unfamiliar Thai ingredients and suggest alternatives
- Share relevant cultural context and traditions
- Provide tips for adapting recipes to different dietary needs
- Include serving suggestions and accompaniments

End each response with an uplifting sign-off like:
- 'Happy cooking! ขอให้อร่อย (Enjoy your meal)!'
- 'May your Thai cooking adventure bring joy!'
- 'Enjoy your homemade Thai feast!'

Remember:
- Always verify recipe authenticity with the knowledge base
- Clearly indicate when information comes from web sources
- Be encouraging and supportive of home cooks at all skill levels\
"""),
db=db,
knowledge=agent_knowledge,
tools=[DuckDuckGoTools()],
# To provide the agent with the chat history
# We can either:
# 1. Provide the agent with a tool to read the chat history
# 2. Automatically add the chat history to the messages sent to the model
#
# 1. Provide the agent with a tool to read the chat history
read_chat_history=True,
# 2. Automatically add the chat history to the messages sent to the model
# add_history_to_context=True,
# Number of historical responses to add to the messages.
# num_history_runs=3,
markdown=True,
)

print("You are about to chat with an agent!")
if session_id is None:
session_id = agent.session_id
if session_id is not None:
print(f"Started Session: {session_id}\n")
else:
print("Started Session\n")
else:
print(f"Continuing Session: {session_id}\n")

# Runs the agent as a command line application
agent.cli_app(markdown=True)


if __name__ == "__main__":
typer.run(recipe_agent)

点评

示例概览

  • 构建了一个“带存储的泰餐烹饪助手”,在保留知识库检索与网页搜索的同时,支持持久化会话与聊天历史
  • 通过 SqliteDb 管理会话与历史,支持“新会话/继续上次会话”的 CLI 交互

核心组成

  • 知识库:Knowledge + LanceDb + OpenAIEmbedder,使用混合检索 SearchType.hybrid,添加泰菜 PDF
  • 模型与工具:OpenAIChat 作为模型,DuckDuckGoTools 进行网页补充
  • 存储层:SqliteDb(db_file="tmp/agents.db") 持久化会话与聊天记录
  • CLI 入口:用 typer 进行命令行交互并运行代理应用

工作流程

  • 启动时询问是否开启新会话;若选择继续,读取用户的历史会话并复用 session_id
  • 创建智能体时注入 user_idsession_id,使得对话与上下文与用户绑定并可延续

存储与会话

  • 会话读取:db.get_sessions(user_id=user, session_type=SessionType.AGENT) 获取历史会话并挑选一个继续
  • 历史注入方式:
    • 直接读取聊天历史:read_chat_history=True(已启用)
    • 自动将历史加入模型上下文:add_history_to_context=True(示例中给出可选注释)与 num_history_runs 控制注入条数
  • 会话可见性:启动或继续会话时打印 session_id 以便确认与追踪

检索策略

  • 优先知识库:使用向量+全文混合检索(hybrid)提升召回与精准度,适合 PDF 菜谱这类专有名词密集内容
  • 适时补充网页:当知识库不足或问题更适合网页信息,调用 DuckDuckGoTools 搜索补充,并在回答中优先使用知识库结果

价值与场景

  • 将“检索增强生成”与“持久化记忆”结合,适合知识助手、教程助手、客服问答等需要长周期多轮对话的应用

智能体状态

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"""Test session state management with a simple counter"""

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.run import RunContext


def increment_counter(run_context: RunContext) -> str:
"""Increment the counter in session state."""
# Initialize counter if it doesn't exist
if run_context.session_state is None:
run_context.session_state = {}

if "count" not in run_context.session_state:
run_context.session_state["count"] = 0

# Increment the counter
run_context.session_state["count"] += 1

return f"Counter incremented! Current count: {run_context.session_state['count']}"


def get_counter(run_context: RunContext) -> str:
"""Get the current counter value."""
if run_context.session_state is None:
run_context.session_state = {}

count = run_context.session_state.get("count", 0)
return f"Current count: {count}"


# Create an Agent that maintains state
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
# Initialize the session state with a counter starting at 0
session_state={"count": 0},
tools=[increment_counter, get_counter],
# Use variables from the session state in the instructions
instructions="You can increment and check a counter. Current count is: {count}",
# Important: Resolve the state in the messages so the agent can see state changes
resolve_in_context=True,
markdown=True,
cache_session=True, # 原始代码中没有这一行
)

# Test the counter functionality
print("Testing counter functionality...")
agent.print_response(
"Let's increment the counter 5 times and observe the state changes!", stream=True
)
print(f"Final session state: {agent.get_session_state()}")

点评

原始代码报错

问题定位

  • 异常来源:get_session_state() 调用会通过“读取会话”获取状态;当前代理未配置存储且未启用会话缓存,get_session()返回 None,抛出 “Session not found”
  • 现象解释:你看到的递增结果来自运行期的 run_context.session_state,已用于渲染输出;但未持久化到数据库,也未缓存,会话读取失败

修复选项

  • 选项 A(最简且推荐):使用 agent.run() 获取返回对象并读取其中的 session_state
    • 原理:RunOutput.session_state在每次运行结束时由运行期状态填充,无需数据库
    • 用法示例:把最后两行改成
      • run_output = agent.run("Let's increment the counter 3 times and observe the state changes!")
      • print(f"Final session state: {run_output.session_state}")
  • 选项 B(无需DB、当前进程可读):在创建 Agent 时加 cache_session=True,让会话缓存在内存中
    • 原理:运行过程中会创建会话对象;开启缓存后,get_session()可返回缓存会话,get_session_state()不再报错
    • 位置:cookbook/getting_started/07_agent_state.py:30 附近创建 Agent 的参数中加入 cache_session=True
  • 选项 C(持久化、跨进程可读):为 Agent 配置数据库(如 SqliteDb),并传入 db 与可选 user_id
    • 原理:运行结束会执行 save_session,会话与 session_state 落库;get_session_state()从DB读取
    • 参考实现:见带存储示例 cookbook/getting_started/06_agent_with_storage.py:49:66:70;需要依赖 sqlalchemy

验证步骤

  • 选项 A:运行 python cookbook/getting_started/07_agent_state.py,检查最后输出应包含 Final session state: {'count': 3, ...}
  • 选项 B:同上;确认不再出现 “Session not found”
  • 选项 C:首次运行打印 Started Session: <session_id>;再次运行同一 session_id 仍可读取到状态

推荐选择

  • 若仅需打印本次运行的最终状态且不需要跨进程保留,采用选项 A
  • 若希望在同一进程里继续读取会话状态(不落库),开启 cache_session=True(选项 B)
  • 若需要持久化与跨进程可用的会话与历史,配置 SqliteDb(选项 C)

更多点评

这个例子演示了如何用会话状态驱动工具执行与回复生成,构建“能记住计数器”的轻量 Agent,本身无实际价值;但state本身具有很大价值:
概念与作用

  • 状态是代理在会话中的“可编程记忆”,用于在多轮交互、工具执行之间传递和累积信息
  • 入口与载体:
    • 运行期状态:RunContext.session_state,工具读写的共享字典
    • 会话状态:持久化在会话对象里,从存储或缓存加载、合并与保存
  • 注入到模型上下文:
    • 变量替换:resolve_in_context=True 把状态中的键作为模板变量注入指令 libs/agno/agno/agent/agent.py:6842:6847
    • 明文附加:add_session_state_to_context=True 将状态以 <session_state>...</session_state> 段落加入系统消息

实际价值

  • 工具协调与流程控制
    • 在多次工具调用间维护进度、迭代计数、上次结果键,避免“从零开始”的推理与重复工作
    • 例:计数器、分页抓取、重试策略、长期任务的阶段标记
  • 个性化与偏好
    • 存储用户偏好、语言风格、过滤条件,让回应保持一致并降低提示开销
    • 将用户参数作为模板变量或状态段注入,使模型“看到”并遵守设定
  • 事务型对话与跨轮记忆
    • 在一次会话内保持结构化上下文(购物车、当前话题、待办项),与长期记忆系统(用户记忆、会话摘要)互补
    • 与会话持久化结合可跨进程延续,get_session_state() 直接读存储状态
  • 可控上下文注入与降噪
    • 通过状态变量替换减少冗长 Prompt;通过选择性附加状态段,避免把不必要历史塞进上下文,降低 token 成本
    • 支持“只注变量、不注全文状态”或“注入精选字段”的策略 libs/agno/agno/agent/agent.py:6993:6995
  • 可观测性与治理
    • 状态中可记录工具执行痕迹、上次检索过滤条件、评价分数,便于响应审核、重放与实验
    • 与会话指标联动:读取会话指标与更新逻辑 libs/agno/agno/utils/agent.py:757:771
  • 与检索/知识的协同
    • 状态可持有“当前检索过滤器”或“内容主题”,与“代理自主选择过滤器”配合,提升 RAG 精度 libs/agno/agno/agent/agent.py:6788:6812:5389:5407
    • 在工具侧将过滤器与检索引用写回运行输出,形成可追踪的检索链路 libs/agno/agno/agent/agent.py:9472:9512

使用模式

  • 初始化与合并
    • 传入默认状态 session_state={...},运行时与存储状态按“运行参数 > DB状态 > 代理默认”顺序合并 libs/agno/agno/agent/agent.py:5814:5829
    • 需要覆盖存储状态时设 overwrite_db_session_state=True libs/agno/agno/agent/agent.py:189:527:528
  • 让模型“自更新”状态
    • 开启 enable_agentic_state=True 后自动注入工具 update_session_state,模型可主动写入状态键值 libs/agno/agno/agent/agent.py:5375:5377:9458:9470
    • 适合“模型决定何时变更偏好/阶段”的场景,并保留可观测性
  • 读取与持久化
    • 仅本次运行:从 RunOutput.session_state 读最终状态(不依赖存储) libs/agno/agno/utils/print_response/agent.py:127:137
    • 同进程:cache_session=True 缓存会话,get_session_state() 可用 libs/agno/agno/agent/agent.py:5868:5899
    • 跨进程:配置 db(如 SqliteDb)以保存与读取会话状态 cookbook/getting_started/06_agent_with_storage.py:49:66:70

设计建议

  • 状态分层
    • 短期运行时(ephemeral)用于工具协调;长期会话(persistent)用于用户偏好与业务上下文
  • 注入策略
    • 优先用模板变量传关键字段;必要时再附加 <session_state>,避免上下文过重
  • 并发与一致性
    • 明确“谁写状态”:工具、模型工具调用或业务逻辑;对可能的并发写做好合并与优先级控制 libs/agno/agno/agent/agent.py:5814:5829
  • 安全与隐私
    • 不将敏感信息直接注入状态或上下文;对持久化状态进行脱敏与访问控制

总结:智能体的 state 是把“上下文记忆”变成可编程的控制面,用来精准地引导工具链、降低提示成本、提升多轮一致性和可治理性。在 Agno 中,它既能在运行时高效传递,也能根据需要持久化与注入,从而让代理的行为更可控、更可观测、更贴近业务。

智能体上下文

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""📰 Agent with Context

This example shows how to inject external dependencies into an agent.
The context is evaluated when the agent is run, acting like dependency injection for Agents.

Run `pip install openai agno` to install dependencies.
"""

import json
from textwrap import dedent

import httpx
from agno.agent import Agent
from agno.models.openai import OpenAIChat


def get_top_hackernews_stories(num_stories: int = 5) -> str:
"""Fetch and return the top stories from HackerNews.

Args:
num_stories: Number of top stories to retrieve (default: 5)
Returns:
JSON string containing story details (title, url, score, etc.)
"""
# Get top stories
stories = [
{
k: v
for k, v in httpx.get(
f"https://hacker-news.firebaseio.com/v0/item/{id}.json"
)
.json()
.items()
if k != "kids" # Exclude discussion threads
}
for id in httpx.get(
"https://hacker-news.firebaseio.com/v0/topstories.json"
).json()[:num_stories]
]
return json.dumps(stories, indent=4)


# Create a Context-Aware Agent that can access real-time HackerNews data
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
# Each function in the context is evaluated when the agent is run,
# think of it as dependency injection for Agents
dependencies={"top_hackernews_stories": get_top_hackernews_stories},
# Alternatively, you can manually add the context to the instructions. This gets resolved automatically
instructions=dedent("""\
You are an insightful tech trend observer! 📰

Here are the top stories on HackerNews:
{top_hackernews_stories}\
"""),
markdown=True,
)

# Example usage
agent.print_response(
"Summarize the top stories on HackerNews and identify any interesting trends.",
stream=True,
)

点评

示例概览

  • 展示把“外部依赖”作为上下文注入给代理:在每次运行前执行依赖函数,生成最新数据并写进提示信息
  • 与“工具调用”不同:上下文依赖是预先计算并固定在消息里;工具则由模型在对话过程中按需调用

核心结构

  • 依赖函数:get_top_hackernews_stories(num_stories: int = 5) -> str 抓取 HN Top IDs 并逐条获取详情,返回整洁的 JSON 字符串 /Users/qixinbo/test/agno/cookbook/getting_started/08_agent_context.py:17:40
  • 代理配置:
    • dependencies={"top_hackernews_stories": get_top_hackernews_stories} 将函数名映射到依赖变量
    • instructions 中使用 {top_hackernews_stories} 占位符,运行时自动替换为函数结果

工作机制与优势

  • 运行时评估:每次 agent.print_response/agent.run 前,依赖函数会被执行,确保上下文是“最新数据”而非陈旧缓存
  • 上下文直达:把结构化 JSON直接拼入系统提示,模型无需再决定是否调用工具,降低一次性任务的复杂度
  • 可控提示体量:通过函数裁剪字段(比如移除 kids)限制冗余,减少上下文负载
  • 与工具的差异:
    • 依赖:强制“输入前即注入”,适合确定性数据的“展示/分析”
    • 工具:模型自主调用,适合“交互式信息获取或多步决策”

实践建议

  • 数据体量控制:为依赖函数设置字段白名单与数量上限(num_stories),防止提示超长
  • 健壮性与性能:
    • httpx.get 增加超时、重试与异常处理,避免网络波动影响生成
    • 可加简单缓存(TTL)减少重复抓取,或在依赖函数接受 since/score 等参数实现过滤
  • 可读性与结构:
    • 维持返回 JSON 的简洁与一致,避免把大段 HTML 或长文本注入提示
    • 如需进一步程序化处理,考虑在指令中明确“需输出的结构化维度”(标题、链路、分项趋势)
  • 何时改用“工具”:
    • 需要模型“按需”抓取更多页或做交互式探索时,用工具而非依赖
    • 需要在对话中多次调用同一接口(分页、筛选),用工具让模型在不同阶段自由选择参数

适用场景

  • 报告类任务:把“最新数据快照”注入后让模型分析与总结(新闻、行情、监控)
  • 依赖注入:将配置、上下文数据、环境信息注入提示,无需复杂的工具编排

总结

  • 该示例展示“依赖即上下文”的模式:每次运行自动评估依赖并将结果注入提示,使 LLM 专注于分析与写作
  • 在实际应用中,选择“依赖”或“工具”取决于是否需要“交互式调用与决策”,以及对上下文长度与时效性的要求

智能体会话

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""🗣️ Persistent Chat History i.e. Session Memory

This example shows how to create an agent with persistent memory stored in a SQLite database.
We set the session_id on the agent when resuming the conversation, this way the previous chat history is preserved.

Key features:
- Stores conversation history in a SQLite database
- Continues conversations across multiple sessions
- References previous context in responses

Run `pip install openai sqlalchemy agno` to install dependencies.
"""

import json
from typing import List, Optional

import typer
from agno.agent import Agent
from agno.db.base import SessionType
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
from agno.session import AgentSession
from rich import print
from rich.console import Console
from rich.json import JSON
from rich.panel import Panel
from rich.prompt import Prompt

console = Console()


def create_agent(user: str = "user"):
session_id: Optional[str] = None

# Ask if user wants to start new session or continue existing one
new = typer.confirm("Do you want to start a new session?")

# Get existing session if user doesn't want a new one
db = SqliteDb(db_file="tmp/agents.db")

if not new:
existing_sessions: List[AgentSession] = db.get_sessions(
user_id=user, session_type=SessionType.AGENT
) # type: ignore
if len(existing_sessions) > 0:
session_id = existing_sessions[0].session_id

agent = Agent(
user_id=user,
# Set the session_id on the agent to resume the conversation
session_id=session_id,
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
db=db,
# Add chat history to messages
add_history_to_context=True,
num_history_runs=3,
markdown=True,
)

if session_id is None:
session_id = agent.session_id
if session_id is not None:
print(f"Started Session: {session_id}\n")
else:
print("Started Session\n")
else:
print(f"Continuing Session: {session_id}\n")

return agent


def print_messages(agent):
"""Print the current chat history in a formatted panel"""
console.print(
Panel(
JSON(
json.dumps(
[
m.model_dump(include={"role", "content"})
for m in agent.get_messages_for_session()
]
),
indent=4,
),
title=f"Chat History for session_id: {agent.session_id}",
expand=True,
)
)


def main(user: str = "user"):
agent = create_agent(user)

print("Chat with an OpenAI agent!")
exit_on = ["exit", "quit", "bye"]
while True:
message = Prompt.ask(f"[bold] :sunglasses: {user} [/bold]")
if message in exit_on:
break

agent.print_response(input=message, stream=True, markdown=True)
print_messages(agent)


if __name__ == "__main__":
typer.run(main)

点评

示例概览

  • 构建“带持久会话记忆”的代理,使用 SqliteDb 存储聊天历史与会话元信息,支持继续上次会话
  • 在每次回答时将最近的历史消息注入上下文,提升多轮对话的一致性和连贯性

核心流程

  • 会话选择与恢复:交互式选择“新会话/继续会话”,读取用户的历史 AgentSession 并传入 session_id
  • 代理初始化:配置 db=SqliteDb(...),开启历史注入 add_history_to_context=True 并限制条数 num_history_runs=3
  • 会话标识输出:启动或续接时打印当前 session_id,方便定位与追踪
  • 历史可视化:调用 agent.get_messages_for_session(),以“用户消息 → 助手回复”的配对视图输出历史

关键点解析

  • 历史注入机制:勾选 add_history_to_context=True 时,代理在生成前将最近 N 次对话加入消息上下文,模型能“看到”先前语境
  • 历史构建方法:get_messages_for_session() 将各轮用户消息与助手回复配对,过滤历史标记消息,便于展示与调试;实现见 libs/agno/agno/session/agent.py:238:271
  • 会话读取与状态:使用 SqliteDb 提供的 get_sessions(user_id=..., session_type=...) 拉取历史列表,选择一个 session_id 继续

优点点评

  • 连贯性提升:通过注入最近历史,回答能够延续上下文,不再“断片”
  • 生产可迁移:使用 SQLite 起步,后续可替换为 Postgres 等持久化库;接口不变,迁移成本低
  • 可视化友好:将历史格式化输出便于调试和演示,快速验证历史注入是否按预期工作

改进建议

  • 会话列表选择:当前默认选择第一个会话,可改为列出会话供用户选择,避免错误续接
  • 注入策略优化:对长会话增加“摘要注入”,减少 token 压力;或结合 num_history_runs 自适应调整
  • 错误与回退:为数据库读写添加异常处理和回退逻辑(例如无历史则降级为新会话)
  • 命名与检索:支持自定义 session_name 与搜索,提升多会话管理体验;相关读取接口见 libs/agno/agno/utils/agent.py:675:681

适用场景

  • 客服与咨询:需要跨多轮甚至多次会话保持上下文
  • 学习助手与入门教程:能够回顾先前问题与结论,持续深化主题
  • 任何需要“对话延续”的应用:把历史当作一等公民输入,提高相关性与用户体验

长周期用户记忆和会话摘要

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
"""🧠 Long Term User Memories and Session Summaries

This example shows how to create an agent with persistent memory that stores:
1. Personalized user memories - facts and preferences learned about specific users
2. Session summaries - key points and context from conversations
3. Chat history - stored in SQLite for persistence

Key features:
- Stores user-specific memories in SQLite database
- Maintains session summaries for context
- Continues conversations across sessions with memory
- References previous context and user information in responses

Examples:
User: "My name is John and I live in NYC"
Agent: *Creates memory about John's location*

User: "What do you remember about me?"
Agent: *Recalls previous memories about John*

Run: `pip install openai sqlalchemy agno` to install dependencies
"""

import json
from textwrap import dedent
from typing import List, Optional

import typer
from agno.agent import Agent
from agno.db.base import SessionType
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
from agno.session import AgentSession
from rich.console import Console
from rich.json import JSON
from rich.panel import Panel
from rich.prompt import Prompt


def create_agent(user: str = "user"):
session_id: Optional[str] = None

# Ask if user wants to start new session or continue existing one
new = typer.confirm("Do you want to start a new session?")

# Initialize storage for both agent sessions and memories
db = SqliteDb(db_file="tmp/agents.db")

if not new:
existing_sessions: List[AgentSession] = db.get_sessions(
user_id=user, session_type=SessionType.AGENT
) # type: ignore
if len(existing_sessions) > 0:
session_id = existing_sessions[0].session_id

agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
user_id=user,
session_id=session_id,
enable_user_memories=True,
enable_session_summaries=True,
db=db,
add_history_to_context=True,
num_history_runs=3,
# Enhanced system prompt for better personality and memory usage
description=dedent("""\
You are a helpful and friendly AI assistant with excellent memory.
- Remember important details about users and reference them naturally
- Maintain a warm, positive tone while being precise and helpful
- When appropriate, refer back to previous conversations and memories
- Always be truthful about what you remember or don't remember"""),
)

if session_id is None:
session_id = agent.session_id
if session_id is not None:
print(f"Started Session: {session_id}\n")
else:
print("Started Session\n")
else:
print(f"Continuing Session: {session_id}\n")

return agent


def print_agent_memory(agent):
"""Print the current state of agent's memory systems"""
console = Console()

# Print chat history
messages = agent.get_messages_for_session()
console.print(
Panel(
JSON(
json.dumps(
[m.model_dump(include={"role", "content"}) for m in messages],
indent=4,
),
),
title=f"Chat History for session_id: {agent.session_id}",
expand=True,
)
)

# Print user memories
user_memories = agent.get_user_memories(user_id=agent.user_id)
if user_memories:
memories_data = [memory.to_dict() for memory in user_memories]
console.print(
Panel(
JSON(json.dumps(memories_data, indent=4)),
title=f"Memories for user_id: {agent.user_id}",
expand=True,
)
)

# Print session summaries
try:
session_summary = agent.get_session_summary()
if session_summary:
console.print(
Panel(
JSON(
json.dumps(session_summary.to_dict(), indent=4),
),
title=f"Session Summary for session_id: {agent.session_id}",
expand=True,
)
)
else:
console.print(
"Session summary: Not yet created (summaries are created after multiple interactions)"
)
except Exception as e:
console.print(f"Session summary error: {e}")


def main(user: str = "user"):
"""Interactive chat loop with memory display"""
agent = create_agent(user)

print("Try these example inputs:")
print("- 'My name is [name] and I live in [city]'")
print("- 'I love [hobby/interest]'")
print("- 'What do you remember about me?'")
print("- 'What have we discussed so far?'\n")

exit_on = ["exit", "quit", "bye"]
while True:
message = Prompt.ask(f"[bold] :sunglasses: {user} [/bold]")
if message in exit_on:
break

agent.print_response(input=message, stream=True, markdown=True)
print_agent_memory(agent)


if __name__ == "__main__":
typer.run(main)

点评

示例概览

  • 目标:为代理加上“长期用户记忆 + 会话摘要 + 历史持久化”,并在响应时自然引用过去信息
  • 存储:用 SqliteDb 保存会话与记忆,支持跨会话继续对话和读取记忆

核心配置与机制

  • 会话续接:交互式选择是否继续旧会话,若继续则取用户的第一个会话 session_id
  • 代理初始化:
    • 记忆开关:enable_user_memories=True
    • 摘要开关:enable_session_summaries=True
    • 历史注入:add_history_to_context=Truenum_history_runs=3
    • 个性化描述:强调“友好语气、使用记忆、真实回忆”

记忆系统工作原理

  • 管理器挂载:当启用用户记忆或显式提供管理器时,代理内部设置 MemoryManager /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:835:839:789:805
  • 默认注入策略:未显式设置时,add_memories_to_context 会被自动打开,只要启用用户记忆或有管理器 /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:801:805
  • 自动提取记忆:
    • 同步路径:在运行结束阶段调用 create_user_memories 根据用户消息抽取并写入数据库 /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:5238:5267
    • 异步路径:同逻辑的 acreate_user_memories /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:5279:5310
    • 当未启用“代理自主记忆”(enable_agentic_memory=False)时,记忆创建在后台任务中并行进行 /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:1008:1010:1224:1226:1860:1862
  • 读取与展示:示例调用 agent.get_user_memories(user_id=agent.user_id) 并富格式输出 /Users/qixinbo/test/agno/cookbook/getting_started/10_user_memories_and_summaries.py:107:116,底层读取见 libs/agno/agno/memory/manager.py:159:172

会话摘要工作原理

  • 管理器挂载:当启用摘要或显式提供管理器时,代理挂载 SessionSummaryManager /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:806:818:846:848
  • 生成时机:每次运行结束将本轮 RunOutput 写入会话,再生成摘要并标记为已更新(同步与异步均支持) /Users/qixinbo/test/agno/libs/agno/agno/agent/agent.py:1094:1101:1947:1953:2266:2285
  • 摘要内容:系统提示要求输出简洁的 summarytopics 列表 /Users/qixinbo/test/agno/libs/agno/agno/session/summary.py:91:116
  • 读取与展示:示例用 agent.get_session_summary() 打印摘要 /Users/qixinbo/test/agno/cookbook/getting_started/10_user_memories_and_summaries.py:119:137,数据结构见 libs/agno/agno/session/summary.py:20:41

历史注入与可视化

  • 历史注入:最近 num_history_runs=3 的消息进入上下文,提高连续性
  • 历史配对展示:agent.get_messages_for_session() 以“用户→助手”的配对输出,便于审阅与调试 /Users/qixinbo/test/agno/cookbook/getting_started/10_user_memories_and_summaries.py:91:104,实现见 libs/agno/agno/session/agent.py:238:271

优点与价值

  • 长期个性化:自动提取用户事实与偏好,并在后续会话自然引用,提升体验与一致性
  • 语境浓缩:会话摘要减少 token 压力,提供“快速可读”的上下文摘要
  • 生产友好:用 SQLite 起步,接口通用(可替换为其他 DB),历史与记忆读取 API 完整

改进建议

  • 选择会话:当前默认续接第一个会话,可提供会话列表供用户选择,避免误续接
  • 注入策略:长会话建议优先注入“摘要 + 少量关键历史”,权衡上下文长度与成本
  • 健壮性:为 DB 读写与摘要/记忆生成增加异常与重试、超时设置;记录失败事件
  • 隐私治理:避免把敏感信息写入记忆;为记忆增加话题过滤与脱敏策略;限制公开展示

适用场景

  • 私人助理与客服:需要稳定记住用户信息与偏好,并跨会话延续
  • 学习/辅导:会话摘要帮助持续复盘进度,记忆支持个性化建议
  • 任务型对话:对历史与关键事实的稳定引用提升准确性

总结

  • 该示例将“用户记忆 + 会话摘要 + 历史注入”整合为可生产的记忆体系:自动采集、可视化、可引用
  • 默认策略合理且可扩展;按需优化会话选择、注入体量与隐私控制,可直接用于实际应用的多轮对话与个性化场景

补充点评

详细对比一下用户记录和聊天历史之间的区别。
核心区别

  • enable_user_memories=True
    • 启用“用户记忆系统”:从用户消息中自动抽取并存储事实/偏好等长期记忆到数据库,后续会话可复用
    • 影响“记忆的生成与读取”,并可将记忆注入系统提示以引导模型个性化回答
    • 相关实现:libs/agno/agno/agent/agent.py:209:216:835:839:789:805:5238:5267:5279:5310:6858:6869
  • add_history_to_context=True
    • 启用“聊天历史注入”:在当前生成前,把最近的原始聊天消息加入到本次模型上下文
    • 影响“上下文拼装”,不做摘要或抽取,直接携带原始多轮内容,提升连贯性
    • 相关实现:libs/agno/agno/agent/agent.py:224:227:7621:7653:1536:1539;历史读取:libs/agno/agno/session/agent.py:128:220

分别适用场景

  • enable_user_memories
    • 长期个性化:记住姓名、城市、偏好、工作背景等,后续自然引用
    • 跨会话延续:换会话也能读回记忆,适合私人助理、客服画像、学习档案
    • 低上下文成本:将记忆以简短系统段落注入,减少 token 压力
    • 代码参考:示例中启动记忆 cookbook/getting_started/10_user_memories_and_summaries.py:61,读取展示 :107:116;读取实现 libs/agno/agno/memory/manager.py:159:172
  • add_history_to_context
    • 近期语境连贯:需要严格引用上一轮或最近几轮的原话、工具结果、链路细节
    • 一次性任务分析:对当前话题的短期连续跟进,避免“断片”
    • 精准引用:原始消息进入上下文,不依赖摘要;适合需要保留对话细节的场景
    • 代码参考:示例开启历史注入 cookbook/getting_started/10_user_memories_and_summaries.py:64;注入逻辑 libs/agno/agno/agent/agent.py:7621:7653;历史汇总 libs/agno/agno/session/agent.py:128:220

组合与取舍

  • 可同时开启:既注入近期原始历史保证细节,又利用用户记忆提供长期个性化与低成本上下文
  • 成本与隐私:
    • 历史注入会增加上下文长度,长对话需控制 num_history_runs/num_history_messages libs/agno/agno/agent/agent.py:224:227
    • 用户记忆涉及敏感信息时需治理(话题过滤、脱敏、访问控制)
  • 依赖要求:
    • 历史注入需要能读取会话(一般需配置 db),否则会警告并不注入 libs/agno/agno/agent/agent.py:1536:1539
    • 用户记忆需要 db 才能持久化与读取(未配 db 时会退化或警告)libs/agno/agno/agent/agent.py:789:793

选择建议

  • 需要“长期个性化与跨会话复用”→ 开启 enable_user_memories
  • 需要“当前多轮的原始上下文连贯”→ 开启 add_history_to_context 并合理设置历史条数
  • 对话很长/成本敏感→ 优先用记忆与摘要,历史注入限量或改用摘要注入
  • 对话短且需要精准回顾→ 历史注入优先,记忆可按需开启

示例中的两行

  • enable_user_memories=True10_user_memories_and_summaries.py:61 用于开启记忆抽取与存储,后续通过 agent.get_user_memories(...) 查看
  • add_history_to_context=True10_user_memories_and_summaries.py:64 用于把最近 3 轮(由 num_history_runs=3 控制)的原始消息加入本次模型上下文,保障回复衔接与引用准确

调用重试函数

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from typing import Iterator

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.exceptions import RetryAgentRun
from agno.tools import FunctionCall, tool

num_calls = 0


def pre_hook(fc: FunctionCall):
global num_calls

print(f"Pre-hook: {fc.function.name}")
print(f"Arguments: {fc.arguments}")
num_calls += 1
if num_calls < 2:
raise RetryAgentRun(
"This wasn't interesting enough, please retry with a different argument"
)


@tool(pre_hook=pre_hook)
def print_something(something: str) -> Iterator[str]:
print(something)
yield f"I have printed {something}"


agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
tools=[print_something], markdown=True)
agent.print_response("Print something interesting", stream=True)

点评

示例概览

  • 演示在工具调用前通过 pre_hook执行校验逻辑,必要时抛出RetryAgentRun让代理自动重试并改写参数
  • 通过计数控制前两次尝试触发重试,直到满足“更有趣”的参数再执行工具

核心机制

  • 工具定义:@tool(pre_hook=pre_hook) 将预处理函数绑定到工具调用
  • 预钩子签名:pre_hook(fc: FunctionCall) 能访问函数名与参数,打印并决策是否重试
  • 重试控制:首次调用抛出 RetryAgentRun,代理捕获并“重新规划”,第二次通过后继续执行工具逻辑
  • 输出与流式:工具本体打印输入并 yield 文本,代理流式展示结果

关键代码与框架行为

  • 异常类型:RetryAgentRun 标记“可重试”,不会终止整个运行 libs/agno/agno/exceptions.py:23:37
  • 预钩子处理:框架在工具执行前调用 FunctionCall._handle_pre_hook,若抛出 AgentRunException(含 RetryAgentRun),将该错误冒泡到上层,由代理处理重试 libs/agno/agno/tools/function.py:687:719
  • 事件流:预钩子会发出 PreHookStarted/Completed 事件,便于观测与调试 libs/agno/agno/run/agent.py:306:331、事件创建 libs/agno/agno/utils/events.py:198:242

优点点评

  • 可控质量门槛:在工具执行前强制参数满足条件(例如“更有趣”的输入、完整必填项、格式校验)
  • 轻量化防御:通过抛出特定异常触发重试,避免在工具内部做复杂容错分支
  • 可观测性:预钩子事件与日志让失败原因与重试过程可追踪

实践建议

  • 重试信息:在 RetryAgentRun 中提供清晰的 user_messageagent_message,指导模型如何调整参数(示例里仅用字符串,可升级为结构化提示)libs/agno/agno/exceptions.py:23:37
  • 幂等性与副作用:预钩子应尽量无副作用;若需要维护状态(如示例中的 num_calls),注意并发与多会话隔离
  • 终止条件:对于不可恢复错误改用 StopAgentRun 直接终止并返回原因 libs/agno/agno/exceptions.py:40:54
  • 参数自修复:结合工具描述与 few-shot,让模型在重试时能自动改写更合理的参数(比如从“boring”变成“funny”)

适用场景

  • 参数校验:必填字段缺失、类型不符、范围不合理
  • 内容质量:要求“非泛泛而谈”、更具创意或更具体的输入
  • 资源约束:API 速率、额度限制时,预钩子判断是否需要降级或延迟执行

总结

  • 该示例展示“预钩子 + 可重试异常”的简洁控制面:在工具执行链前卡住不合格输入,提示模型重试并改写参数
  • 在真实项目中,配合更详细的重试指导与参数校验,可大幅提升工具调用的成功率和结果质量

人类在环

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""🤝 Human-in-the-Loop: Adding User Confirmation to Tool Calls

This example shows how to implement human-in-the-loop functionality in your Agno tools.
It shows how to:
- Add pre-hooks to tools for user confirmation
- Handle user input during tool execution
- Gracefully cancel operations based on user choice

Some practical applications:
- Confirming sensitive operations before execution
- Reviewing API calls before they're made
- Validating data transformations
- Approving automated actions in critical systems

Run `pip install openai httpx rich agno` to install dependencies.
"""

import json
from textwrap import dedent

import httpx
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from agno.utils import pprint
from rich.console import Console
from rich.prompt import Prompt

console = Console()


@tool(requires_confirmation=True)
def get_top_hackernews_stories(num_stories: int) -> str:
"""Fetch top stories from Hacker News.

Args:
num_stories (int): Number of stories to retrieve

Returns:
str: JSON string containing story details
"""
# Fetch top story IDs
response = httpx.get("https://hacker-news.firebaseio.com/v0/topstories.json")
story_ids = response.json()

# Yield story details
all_stories = []
for story_id in story_ids[:num_stories]:
story_response = httpx.get(
f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json"
)
story = story_response.json()
if "text" in story:
story.pop("text", None)
all_stories.append(story)
return json.dumps(all_stories)


# Initialize the agent with a tech-savvy personality and clear instructions
agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
description="A Tech News Assistant that fetches and summarizes Hacker News stories",
instructions=dedent("""\
You are an enthusiastic Tech Reporter

Your responsibilities:
- Present Hacker News stories in an engaging and informative way
- Provide clear summaries of the information you gather

Style guide:
- Use emoji to make your responses more engaging
- Keep your summaries concise but informative
- End with a friendly tech-themed sign-off\
"""),
tools=[get_top_hackernews_stories],
markdown=True,
)

# Example questions to try:
# - "What are the top 3 HN stories right now?"
# - "Show me the most recent story from Hacker News"
# - "Get the top 5 stories (you can try accepting and declining the confirmation)"
response = agent.run("What are the top 2 hackernews stories?")
if response.is_paused:
for tool in response.tools: # type: ignore
# Ask for confirmation
console.print(
f"Tool name [bold blue]{tool.tool_name}({tool.tool_args})[/] requires confirmation."
)
message = (
Prompt.ask("Do you want to continue?", choices=["y", "n"], default="y")
.strip()
.lower()
)

if message == "n":
break
else:
# We update the tools in place
tool.confirmed = True

run_response = agent.continue_run(run_response=response)
pprint.pprint_run_response(run_response)

点评

示例概览

  • 目标:为工具调用加入“人审环节”,在模型决定调用工具后暂停执行,等待用户确认,再继续或取消
  • 方式:工具声明 requires_confirmation=True;代理运行返回“暂停态”,外部交互收集用户选择后用 continue_run 续跑

核心流程

  • 工具定义:@tool(requires_confirmation=True) 标记该工具需要人工确认
  • 代理初始化:注册工具与提示风格
  • 触发与暂停:agent.run(...) 返回的响应若含需确认的工具则为暂停态,response.is_paused 为 True
  • 人工确认:遍历 response.tools,提示工具名与参数,用户选择 y/n;若继续则将该工具的 confirmed=True
  • 继续执行:调用 agent.continue_run(run_response=response),代理基于更新后的工具确认状态继续执行,输出结果

框架行为与事件

  • 暂停事件:当工具需要确认时,模型层发出 tool_call_paused,代理组装为 RunPausedEvent,携带待确认工具列表 libs/agno/agno/models/base.py:1549:1580:1785:1800,事件封装 libs/agno/agno/utils/events.py:136:157
  • 暂停响应属性:
    • run_response.is_paused 表示暂停 libs/agno/agno/run/agent.py:547:557
    • tools_requiring_confirmation 提供需要确认的工具集合(也可在集成里使用)libs/agno/agno/run/agent.py:553:557
  • 继续运行:continue_run/acontinue_run 接收更新后的工具状态或结果,恢复执行直到完成;在多端集成中同样使用该机制 libs/agno/agno/os/router.py:988:1023libs/agno/agno/integrations/discord/client.py:143:159

价值与适用场景

  • 安全与合规:对“敏感操作”(调用外部 API、写数据库、发交易、删除资源)先征得用户确认
  • 可审阅:提供工具名与参数,让用户在执行前检查、修正或拒绝
  • 人工协作:在自动化链路中插入人审节点,兼顾效率与控制

实践建议

  • 交互体验:为确认提示显示简明参数摘要;在需要时提供“查看详细 diff/请求体”选项
  • 多工具场景:支持批量确认或逐一确认;未确认的工具默认不执行或视为拒绝
  • 审计与记录:记录确认时间、操作者、工具名与参数,用于合规和回溯
  • 兜底策略:用户拒绝后提供替代方案或解释;必要时用 StopAgentRun 终止并返回说明

总结

  • 该示例把“人审机制”无缝嵌入 Agno 的工具调用链:当工具声明需要确认时,自动暂停并等待用户决定,确认后继续执行
  • 适合任何需要在自动化与审慎执行之间取得平衡的场景,具备良好的扩展性与可观测性

图像智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""🎨 AI Image Reporter - Your Visual Analysis & News Companion!

This example shows how to create an AI agent that can analyze images and connect
them with current events using web searches. Perfect for:
1. News reporting and journalism
2. Travel and tourism content
3. Social media analysis
4. Educational presentations
5. Event coverage

Example images to try:
- Famous landmarks (Eiffel Tower, Taj Mahal, etc.)
- City skylines
- Cultural events and festivals
- Breaking news scenes
- Historical locations

Run `pip install ddgs agno` to install dependencies.
"""

from textwrap import dedent

from agno.agent import Agent
from agno.media import Image
from agno.models.openai import OpenAIChat
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.baidusearch import BaiduSearchTools

agent = Agent(
model=OpenAIChat(id="gpt-5-mini",
base_url="https://api.zhizengzeng.com/v1"),
description=dedent("""\
You are a world-class visual journalist and cultural correspondent with a gift
for bringing images to life through storytelling! 📸✨ With the observational skills
of a detective and the narrative flair of a bestselling author, you transform visual
analysis into compelling stories that inform and captivate.\
"""),
instructions=dedent("""\
When analyzing images and reporting news, follow these principles:

1. Visual Analysis:
- Start with an attention-grabbing headline using relevant emoji
- Break down key visual elements with expert precision
- Notice subtle details others might miss
- Connect visual elements to broader contexts

2. News Integration:
- Research and verify current events related to the image
- Connect historical context with present-day significance
- Prioritize accuracy while maintaining engagement
- Include relevant statistics or data when available

3. Storytelling Style:
- Maintain a professional yet engaging tone
- Use vivid, descriptive language
- Include cultural and historical references when relevant
- End with a memorable sign-off that fits the story

4. Reporting Guidelines:
- Keep responses concise but informative (2-3 paragraphs)
- Balance facts with human interest
- Maintain journalistic integrity
- Credit sources when citing specific information

5. 用中文返回结果。

Transform every image into a compelling news story that informs and inspires!\
"""),
tools=[BaiduSearchTools()],
markdown=True,
)

# Example usage with a famous landmark
agent.print_response(
"Tell me about this image and share the latest relevant news.",
images=[
Image(
url="https://upload.wikimedia.org/wikipedia/commons/0/0c/GoldenGateBridge-001.jpg"
)
],
stream=True,
)

# More examples to try:
"""
Sample prompts to explore:
1. "What's the historical significance of this location?"
2. "How has this place changed over time?"
3. "What cultural events happen here?"
4. "What's the architectural style and influence?"
5. "What recent developments affect this area?"

Sample image URLs to analyze:
1. Eiffel Tower: "https://upload.wikimedia.org/wikipedia/commons/8/85/Tour_Eiffel_Wikimedia_Commons_%28cropped%29.jpg"
2. Taj Mahal: "https://upload.wikimedia.org/wikipedia/commons/b/bd/Taj_Mahal%2C_Agra%2C_India_edit3.jpg"
3. Golden Gate Bridge: "https://upload.wikimedia.org/wikipedia/commons/0/0c/GoldenGateBridge-001.jpg"
"""

# To get the response in a variable:
# from rich.pretty import pprint
# response = agent.run(
# "Analyze this landmark's architecture and recent news.",
# images=[Image(url="YOUR_IMAGE_URL")],
# )
# pprint(response.content)

点评

这个例子实现了如下功能:

  • 构建“图像+新闻”多模态代理:接收图片,先进行视觉分析,再调用搜索工具整合最新相关资讯

图像生成

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""🎨 Image Generation with DALL-E - Creating AI Art with Agno

This example shows how to create an AI agent that generates images using DALL-E.
You can use this agent to create various types of images, from realistic photos to artistic
illustrations and creative concepts.

Example prompts to try:
- "Create a surreal painting of a floating city in the clouds at sunset"
- "Generate a photorealistic image of a cozy coffee shop interior"
- "Design a cute cartoon mascot for a tech startup"
- "Create an artistic portrait of a cyberpunk samurai"

Run `pip install openai agno` to install dependencies.
"""

from textwrap import dedent

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
from agno.run.agent import RunOutput
from agno.tools.dalle import DalleTools

# Create an Creative AI Artist Agent
image_agent = Agent(
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
tools=[DalleTools()],
description=dedent("""\
You are an experienced AI artist with expertise in various artistic styles,
from photorealism to abstract art. You have a deep understanding of composition,
color theory, and visual storytelling.\
"""),
instructions=dedent("""\
As an AI artist, follow these guidelines:
1. Analyze the user's request carefully to understand the desired style and mood
2. Before generating, enhance the prompt with artistic details like lighting, perspective, and atmosphere
3. Use the `create_image` tool with detailed, well-crafted prompts
4. Provide a brief explanation of the artistic choices made
5. If the request is unclear, ask for clarification about style preferences

Always aim to create visually striking and meaningful images that capture the user's vision!\
"""),
markdown=True,
db=SqliteDb(session_table="test_agent", db_file="tmp/test.db"),
)

# Example usage
image_agent.print_response(
"Create a magical library with floating books and glowing crystals",
)

# Retrieve and display generated images
run_response = image_agent.get_last_run_output()
if run_response and isinstance(run_response, RunOutput):
for image_response in run_response.images:
image_url = image_response.url
print("image_url: ", image_url)
else:
print("No images found or images is not a list")

# More example prompts to try:
"""
Try these creative prompts:
1. "Generate a steampunk-style robot playing a violin"
2. "Design a peaceful zen garden during cherry blossom season"
3. "Create an underwater city with bioluminescent buildings"
4. "Generate a cozy cabin in a snowy forest at night"
5. "Create a futuristic cityscape with flying cars and skyscrapers"
"""

点评

构建了一个“AI 艺术生成”代理:用 DalleTools 调用 DALL·E 生成图片,并将结果保存在会话中,随后读取并打印图片 URL。

音频交互智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""🎤 Audio Input/Output with GPT-4 - Creating Voice Interactions with Agno

This example shows how to create an AI agent that can process audio input and generate
audio responses. You can use this agent for various voice-based interactions, from analyzing
speech content to generating natural-sounding responses.

Example audio interactions to try:
- Upload a recording of a conversation for analysis
- Have the agent respond to questions with voice output
- Process different languages and accents
- Analyze tone and emotion in speech

Run `pip install openai requests agno` to install dependencies.
"""

from textwrap import dedent

import requests
from agno.agent import Agent
from agno.media import Audio
from agno.models.openai import OpenAIChat
from agno.utils.audio import write_audio_to_file

# Create an AI Voice Interaction Agent
agent = Agent(
model=OpenAIChat(
id="gpt-4o-audio-preview",
base_url="https://api.zhizengzeng.com/v1",
modalities=["text", "audio"],
audio={"voice": "sage", "format": "wav"},
),
description=dedent("""\
You are an expert in audio processing and voice interaction, capable of understanding
and analyzing spoken content while providing natural, engaging voice responses.
You excel at comprehending context, emotion, and nuance in speech.\
"""),
instructions=dedent("""\
As a voice interaction specialist, follow these guidelines:
1. Listen carefully to audio input to understand both content and context
2. Provide clear, concise responses that address the main points
3. When generating voice responses, maintain a natural, conversational tone
4. Consider the speaker's tone and emotion in your analysis
5. If the audio is unclear, ask for clarification

Focus on creating engaging and helpful voice interactions!\
"""),
)

# Fetch the audio file and convert it to a base64 encoded string
url = "https://openaiassets.blob.core.windows.net/$web/API/docs/audio/alloy.wav"
response = requests.get(url)
response.raise_for_status()

# Process the audio and get a response
run_response = agent.run(
"What's in this recording? Please analyze the content and tone.",
audio=[Audio(content=response.content, format="wav")],
)

# Save the audio response if available
if run_response.response_audio is not None:
write_audio_to_file(
audio=run_response.response_audio.content, filename="tmp/response.wav"
)

# More example interactions to try:
"""
Try these voice interaction scenarios:
1. "Can you summarize the main points discussed in this recording?"
2. "What emotions or tone do you detect in the speaker's voice?"
3. "Please provide a detailed analysis of the speech patterns and clarity"
4. "Can you identify any background noises or audio quality issues?"
5. "What is the overall context and purpose of this recording?"

Note: You can use your own audio files by converting them to base64 format.
Example for using your own audio file:

with open('your_audio.wav', 'rb') as audio_file:
audio_data = audio_file.read()
agent.run("Analyze this audio", audio=[Audio(content=audio_data, format="wav")])
"""

点评

示例概览

  • 构建“语音交互”代理:支持音频输入与音频输出,适用于语音分析、情绪识别与自然语音回复
  • 模型配置启用多模态与语音输出,输入示例来自公开的 wav 文件,最终将回复音频落盘保存

核心结构

  • 模型与多模态配置:modalities=["text","audio"]audio={"voice":"sage","format":"wav"},启用音频输入与音频输出
  • 拉取输入音频:从 URL 下载 wav 数据,作为原始字节传入代理
  • 执行与保存:运行后检查 run_response.response_audio,将模型生成的音频保存为本地文件

工作机制

  • 输入音频打包:将 Audio(content=..., format="wav") 转为 OpenAI 多模态消息部件,使用 base64 编码并携带格式信息 libs/agno/agno/utils/openai.py:13:26:35:39
  • 消息格式化:当消息包含音频,模型消息内容转为 [{"type":"text",...}, {"type":"input_audio",...}] 的多段结构 libs/agno/agno/models/openai/chat.py:289:351
  • 输出音频填充:模型返回音频后,代理将其写入 run_response.response_audio,用于后续保存与展示 libs/agno/agno/agent/agent.py:4685:4687
  • 文件写入:write_audio_to_file 将 base64 的音频内容解码写至磁盘

优点与价值

  • 端到端语音链路:从字节级音频输入 → 多模态推理 → 语音输出,一次性打通
  • 语音分析增强:提示中明确“分析内容与语气”,适合做情绪识别、语音摘要与可读性评估
  • 输出可落盘:将回复音频保存为 wav 文件,便于集成到应用或后续播放

常见注意点

  • 音频格式:输入建议提供 format="wav";若是 URL,系统会尝试根据路径猜测格式 libs/agno/agno/utils/openai.py:41:52
  • 非流式输出:示例使用 agent.run(非流式),返回整体结果后再保存音频;若需边播边存,可考虑流式事件与分片处理(模型事件会分段累积音频)libs/agno/agno/agent/agent.py:4978:5020

使用场景

  • 语音问答:将语音问题转文本分析并给出语音回复
  • 语音摘要与情感:对录音进行主题提取、要点总结与情绪识别
  • 多语言与口音适配:通过提示策略支持跨语言与不同口音的理解与回应

多智能体

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""🗞️ Multi-Agent Team - Your Professional News & Finance Squad!

This example shows how to create a powerful team of AI agents working together
to provide comprehensive financial analysis and news reporting. The team consists of:
1. Web Agent: Searches and analyzes latest news
2. Finance Agent: Analyzes financial data and market trends
3. Lead Editor: Coordinates and combines insights from both agents

Run: `pip install openai ddgs yfinance agno` to install the dependencies
"""

from textwrap import dedent

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.team.team import Team
from agno.tools.baidusearch import BaiduSearchTools
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.baidusearch import BaiduSearchTools
from agno.tools.exa import ExaTools

web_agent = Agent(
name="Web Agent",
role="Search the web for information",
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
tools=[DuckDuckGoTools()],
instructions=dedent("""\
You are an experienced web researcher and news analyst! 🔍

Follow these steps when searching for information:
1. Start with the most recent and relevant sources
2. Cross-reference information from multiple sources
3. Prioritize reputable news outlets and official sources
4. Always cite your sources with links
5. Focus on market-moving news and significant developments

Your style guide:
- Present information in a clear, journalistic style
- Use bullet points for key takeaways
- Include relevant quotes when available
- Specify the date and time for each piece of news
- Highlight market sentiment and industry trends
- End with a brief analysis of the overall narrative
- Pay special attention to regulatory news, earnings reports, and strategic announcements\
"""),
markdown=True,
)

finance_agent = Agent(
name="Finance Agent",
role="Get financial data",
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
tools=[BaiduSearchTools],
instructions=dedent("""\
You are a skilled financial analyst with expertise in market data! 📊

Follow these steps when analyzing financial data:
1. Start with the latest stock price, trading volume, and daily range
2. Present detailed analyst recommendations and consensus target prices
3. Include key metrics: P/E ratio, market cap, 52-week range
4. Analyze trading patterns and volume trends
5. Compare performance against relevant sector indices

Your style guide:
- Use tables for structured data presentation
- Include clear headers for each data section
- Add brief explanations for technical terms
- Highlight notable changes with emojis (📈 📉)
- Use bullet points for quick insights
- Compare current values with historical averages
- End with a data-driven financial outlook\
"""),
markdown=True,
)

agent_team = Team(
members=[web_agent, finance_agent],
model=OpenAIChat(id="gpt-4o",
base_url="https://api.zhizengzeng.com/v1"),
instructions=dedent("""\
You are the lead editor of a prestigious financial news desk! 📰

Your role:
1. Coordinate between the web researcher and financial analyst
2. Combine their findings into a compelling narrative
3. Ensure all information is properly sourced and verified
4. Present a balanced view of both news and data
5. Highlight key risks and opportunities

Your style guide:
- Start with an attention-grabbing headline
- Begin with a powerful executive summary
- Present financial data first, followed by news context
- Use clear section breaks between different types of information
- Include relevant charts or tables when available
- Add 'Market Sentiment' section with current mood
- Include a 'Key Takeaways' section at the end
- End with 'Risk Factors' when appropriate
- Sign off with 'Market Watch Team' and the current date\
"""),
add_datetime_to_context=True,
markdown=True,
show_members_responses=False,
)

# Example usage with diverse queries
agent_team.print_response(
input="Summarize analyst recommendations and share the latest news for NVDA",
stream=True,
)
agent_team.print_response(
input="What's the market outlook and financial performance of AI semiconductor companies?",
stream=True,
)
agent_team.print_response(
input="Analyze recent developments and financial performance of TSLA",
stream=True,
)

# More example prompts to try:
"""
Advanced queries to explore:
1. "Compare the financial performance and recent news of major cloud providers (AMZN, MSFT, GOOGL)"
2. "What's the impact of recent Fed decisions on banking stocks? Focus on JPM and BAC"
3. "Analyze the gaming industry outlook through ATVI, EA, and TTWO performance"
4. "How are social media companies performing? Compare META and SNAP"
5. "What's the latest on AI chip manufacturers and their market position?"
"""

点评

  • 演示了一个“多智能体协作”的新闻与金融分析工作流: Web Agent 负责抓取与综合新闻, Finance Agent 负责财务与行情分析,团队充当“主编”整合输出,形成一篇结构化的金融报道
  • 展示了如何为不同智能体设置各自的角色、工具与风格指南,并由团队统一编辑规范和最终呈现