Agno Demo AgentOS 实战:构建多后端 Wiki 智能体平台(本地/Git/Notion)
本篇以 cookbook/01_demo/README.md 为主线,讲解如何基于 Agno AgentOS 搭建一个由 Wiki 智能体组成的演示平台:摄取 URL、图片、语音备忘录或 PDF,并作为结构化页面存储到三种后端之一——本地 Markdown 文件、Git 仓库或 Notion 数据库。读完后你将掌握:AgentOS 的入口配置与生命周期管理、基于环境变量门控(env-gated)的多后端智能体注册机制、Git/Notion 后端的完整凭证配置流程,以及配套的 LLM-as-judge 评估套件的使用方式。
整体架构:一个入口、四个智能体、三个 Wiki 后端
整个 Demo 代码量刻意控制得很小——官方 README 称"一个下午能读完,且足够稳定可以二次开发"。它的入口是 cookbook/01_demo/run.py,通过 AgentOS 注册智能体并暴露为 FastAPI 应用。四个智能体的分工如下(引自 README 的 Agents 表):
| 智能体 | 职责 | 注册条件 |
|---|---|---|
| LocalWiki | 读写本地 Markdown Wiki。摄取 URL、图片或 PDF,一次调用完成消化并归档 | 始终注册 |
| GitWiki | 同一套 Wiki 智能体,但后端是 Git 仓库,每次写入自动 commit 并 push | 需设置 WIKI_REPO_URL 与 WIKI_GITHUB_TOKEN |
| NotionWiki | 同一套 Wiki 智能体,但后端是 Notion 数据库,一行一页,数据库即团队打开的"真相源" | 需设置 NOTION_API_KEY 与 NOTION_DATABASE_ID |
| CodeSearch | 回答关于本仓库的问题,给出文件路径和行号 | 始终注册 |
三个 Wiki 智能体还能产出可下载的 HTML 文件;默认模型为 gpt-5.5,可通过 settings.py 换成任意模型。
入口文件:条件注册与生命周期
run.py 中的智能体装配逻辑体现了"环境变量门控"的核心设计:
# LocalWiki + CodeSearch are always on.
# GitWiki and NotionWiki are available when their backend credentials are set
_agents = [local_wiki, code_search]
if git_wiki is not None:
_agents.append(git_wiki)
if notion_wiki is not None:
_agents.append(notion_wiki)
agent_os = AgentOS(
name="Demo AgentOS",
agents=_agents,
db=get_db(),
config=str(Path(__file__).parent / "config.yaml"),
tracing=True,
scheduler=True,
scheduler_base_url="http://127.0.0.1:8000",
lifespan=lifespan,
)
app = agent_os.get_app()
几个值得注意的实现细节(源码事实,见 run.py):
- 条件注册:
git_wiki.py/notion_wiki.py在缺少凭证时直接导出None(模块尾部else分支),run.py因此可以无条件 import 而不报错; - lifespan 钩子:
WikiContextProvider底层的 MCP 会话是懒加载的(asetup()首次查询时才建立),所以启动时不预热;但关闭时必须显式aclose()释放 Parallel MCP 会话,lifespan 中对每个 provider 逐一调用; - 持久化:
db=get_db()使用 SQLite。db.py 中定义了SqliteDb(id="demo-db", db_file=...),文件落在cookbook/01_demo/data/demo.db(gitignored),用于保存 Agent 会话历史。
会话数据库定义(见 db.py):
def get_db() -> SqliteDb:
"""Local SQLite database for agent sessions."""
return SqliteDb(id=DB_ID, db_file=DB_FILE)
快速上手四步
以下完整继承自 README 的 "Get started" 章节:
1. 创建虚拟环境
uv venv .venvs/demo --python 3.12
source .venvs/demo/bin/activate
2. 安装依赖
uv pip install -r cookbook/01_demo/requirements.txt
3. 设置 API Key
export OPENAI_API_KEY="..." # 必需:默认模型为 gpt-5.5
export PARALLEL_API_KEY="..." # 可选:提高免登录 Parallel MCP 的额度限制
export GOOGLE_API_KEY="..." # 可选:用于 gemini 的音频和视频
GitWiki 与 NotionWiki 会在各自后端凭证设置后自动开启,具体见下文"启用其他 Wiki 后端"。
4. 启动服务
fastapi dev cookbook/01_demo/run.py
然后打开 os.agno.com 并登录:
- Add OS → Local
- 连接
http://localhost:8000,命名为 Local AgentOS - 开始与智能体对话
模型与工具的配置中心:settings.py
settings.py 把所有模型 id 收敛到一处,避免散落各处:
def default_model() -> OpenAIResponses:
"""Top-level agent model."""
return OpenAIResponses(id="gpt-5.5")
def sub_agent_model() -> OpenAIResponses:
"""Model for the context-provider sub-agents (read/write tool-routing work)."""
return OpenAIResponses(id="gpt-5.5")
def judge_model() -> OpenAIResponses:
"""Model for the eval LLM judge (AgentAsJudgeEval). ..."""
return OpenAIResponses(id="gpt-5.5")
def gemini_flash() -> Gemini:
"""Gemini 3.5 Flash — 需要更重的多模态(音频/视频)时按智能体替换。"""
return Gemini(id="gemini-3.5-flash")
其中 html_tools() 返回一个"仅 HTML"的文件生成工具集,供三个 Wiki 智能体共享:
def html_tools() -> FileGenerationTools:
return FileGenerationTools(
enable_json_generation=False,
enable_csv_generation=False,
enable_pdf_generation=False,
enable_docx_generation=False,
enable_txt_generation=False,
enable_html_generation=True,
output_directory=str(_GENERATED_DIR), # data/generated/
)
从源码注释看,只启用 HTML 是为了让智能体只看到一个 generate_html_file 工具,避免在多种格式间犹豫;生成的文件作为 artifact 附在响应中并保存到 data/generated/。
LocalWiki:本地 Markdown Wiki 的典型写法
agents/local_wiki.py 展示了整个 Demo 的"标准范式":一个 WikiContextProvider + 一个父级 Agent。
local_wiki_provider = WikiContextProvider(
id="local_wiki",
backend=FileSystemBackend(path=WIKI_PATH), # data/wiki/
web=ParallelMCPBackend(), # URL 抓取走 Parallel MCP
model=sub_agent_model(),
)
local_wiki = Agent(
id="local-wiki",
name="LocalWiki",
model=default_model(),
db=get_db(),
tools=[*local_wiki_provider.get_tools(), html_tools()],
instructions=LOCAL_WIKI_INSTRUCTIONS + "\n\n" + local_wiki_provider.instructions(),
add_datetime_to_context=True,
add_history_to_context=True,
num_history_runs=5,
markdown=True,
)
关键机制:
- 双层智能体结构:父 Agent(
local-wiki)只看到两个工具——query_local_wiki(读,范围限定在 Wiki 的子智能体)和update_local_wiki(写,可先经 Parallel MCP 抓取 URL 再落盘);真正的文件读写由 provider 内部的子智能体完成。这是 agno 的 Context Provider 模式:"父 Agent 管对话,子 Agent 管工具路由"; - Wiki 目录自举:
WIKI_PATH指向cookbook/01_demo/data/wiki/(gitignored),模块导入时若目录为空会自动写入一个 README.md 占位页; - 多模态消化:父 Agent 是唯一能看到附件图片或 PDF 的层级,指令中明确要求它"自己消化成干净的 Markdown(标题、摘要、要点)再通过
update_local_wiki归档"——产物是摘要页而非原始文件; - 诚实性约束:指令中反复强调"Wiki 里没有就直说,绝不编造页面、内容或 URL",这一点被评估用例直接考核(见下文 Evals)。
GitWiki:把 Wiki 存进 Git 仓库
agents/git_wiki.py 与 LocalWiki 的差异仅在后端:FileSystemBackend 换成 GitBackend:
if _REPO_URL and _TOKEN:
git_wiki_provider = WikiContextProvider(
id="git_wiki",
backend=GitBackend(
repo_url=_REPO_URL,
branch=_BRANCH, # 默认 main
github_token=_TOKEN,
local_path=_LOCAL_PATH, # 默认 ./data/git-wiki/
),
web=ParallelMCPBackend(),
model=sub_agent_model(),
)
git_wiki = Agent(...)
else:
git_wiki_provider = None
git_wiki = None
从模块 docstring 与源码看,GitBackend 的写入链路是:暂存 → 用 LLM 生成提交信息 commit → 变基到远端 → push;本地克隆保存在 data/git-wiki/(gitignored)。环境变量清单:
| 环境变量 | 必填 | 说明 |
|---|---|---|
WIKI_REPO_URL |
是 | 仓库 HTTPS 地址(不能是 SSH,git@… 会被拒绝) |
WIKI_GITHUB_TOKEN |
是 | 对该仓库有 Contents: Read and write 的 PAT,认证后从日志中剔除 |
WIKI_BRANCH |
否 | 默认 main |
WIKI_LOCAL_PATH |
否 | 本地克隆路径,默认 ./data/git-wiki/ |
README 中的分步配置流程(完整继承):
- 选一个仓库存放 Wiki。全新空仓库即可,但要带一个初始提交(GitHub 上勾选 Add a README),保证
main分支在首次克隆时存在; - 创建 Token,需对该仓库有写权限:
- Fine-grained PAT(推荐):GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token,作用域限定到该仓库,并设置 Repository permissions → Contents → Read and write;
- Classic PAT:使用
reposcope;
- 导出环境变量并重启:
export WIKI_REPO_URL="https://github.com/<owner>/<repo>.git" # HTTPS,非 SSH
export WIKI_GITHUB_TOKEN="github_pat_..." # contents: read and write
export WIKI_BRANCH="main" # 可选,默认 main
NotionWiki:把 Notion 数据库当作真相源
agents/notion_wiki.py 的后端是 NotionDatabaseBackend:每行数据库记录镜像为一个带 frontmatter(记录 page id 与最近编辑时间)的本地 Markdown 文件;写入走 Notion blocks 往返,数据库始终是 source of truth。启动时后端会从 Notion 重建本地镜像,因此落在 data/notion-wiki/(gitignored)的镜像只是缓存。
这里有一个值得借鉴的平铺约束:Notion 镜像只同步 Wiki 根目录的 *.md(glob 非递归),写进子目录的页面永远到不了 Notion。因此该文件专门为写子智能体覆写了 write_instructions:
NOTION_WRITE_INSTRUCTIONS = """\
You add to and edit pages in a Notion-backed wiki mirrored under {path}.
This wiki is FLAT: the backend mirrors one Notion database row per markdown
file at the top level. Always write each page as a kebab-case `<title>.md`
directly under the wiki root — never inside a subdirectory, ...
Files in subdirectories are not synced to Notion.
...
"""
这展示了 WikiContextProvider(write_instructions=...) 参数:当后端约束与默认写指令(建议归档到 papers/ 之类的文件夹)冲突时,可以整段替换写子智能体的提示词。
README 中的 Notion 配置流程(完整继承):
- 创建 Integration:notion.so → Integrations → New integration → Internal,复制 token(
ntn_…)。默认的 read、insert、update content 权限即可; - 建一个数据库作为 Wiki。用 full-page database(table 视图即可),唯一需要的列是内建 title,页面名由智能体起。Wiki 是平铺的:一行一页,无嵌套页面;
- 把 Integration 连接到该数据库——最容易被漏掉的一步:以 full page 打开数据库 → ••• 菜单 → Connections → 添加你的 Integration。缺了这步 API 根本看不到该数据库;
- 从 URL 复制 database ID:是路径里
?v=之前的 32 位十六进制串(v=值是视图 id,要丢掉):
https://www.notion.so/<workspace>/<DATABASE_ID>?v=<view_id>
- 导出环境变量并重启:
export NOTION_API_KEY="ntn_..."
export NOTION_DATABASE_ID="<URL 里的 32 位十六进制>"
可选环境变量 NOTION_WIKI_LOCAL_PATH 用于改本地镜像位置(见 notion_wiki.py docstring)。
CodeSearch:只读代码问答
agents/code_search.py 使用 WorkspaceContextProvider,它把一套只读的 Workspace 工具集(list / search / read)包在子智能体后面,父 Agent 只看到一个 query_codebase(question) 工具:
code_search_provider = WorkspaceContextProvider(
id="codebase",
name="Agno Repo",
root=REPO_ROOT, # 仓库根目录
model=sub_agent_model(),
)
其指令要求答案"落在真实代码上":引用真实文件路径(尽量带行号)、直接引代码而非转述;仓库里没有就明说,不猜。这同样是一条可被评估用例验证的行为约束。
日常玩法(Try it)
README 给出的五组提示词,可以直接在 os.agno.com 里对 LocalWiki/CodeSearch 使用:
- 摄取 URL(LocalWiki):"Add https://docs.agno.com/ to the wiki." ——抓取、消化、归档一气呵成;
- 摄取媒体:给 LocalWiki 附上 evals/assets/sample-diagram.png(或自己的图片/PDF),说 "Digest this and file it under notes/.";
- 问 Wiki:"What's in the wiki?" 或 "What does the wiki say about X?";
- 代码问答(CodeSearch):"Which agents are registered in this demo?";
- 生成 HTML(LocalWiki):"Render the wiki's docs page as a standalone HTML page." ——返回可下载的
.html文件。
每个智能体在聊天界面里还有来自 config.yaml 的快捷提示词(chat.quick_prompts 按 agent id 分组,如 local-wiki、code-search),扩展新智能体时别忘了在这里加几条。
Evals:LLM 评审 + 工具调用断言
评估套件位于 cookbook/01_demo/evals/。从仓库根目录运行:
python -m cookbook.01_demo.evals # 跑全部用例(简洁输出)
python -m cookbook.01_demo.evals -v # 流式输出完整 agent 运行过程
python -m cookbook.01_demo.evals --case <name> # 只跑一个用例
或在 cookbook/01_demo 目录下:
python -m evals
python -m evals -v
python -m evals --case <name>
每个用例让一个智能体跑一次,然后做最多两层检查(见 evals/cases.py 的 Case 数据结构):
- judge:
AgentAsJudgeEval按criteria用 LLM 打二元 pass/fail; - reliability:
ReliabilityEval断言expected_tool_calls里期望的工具确实被调用(allow_additional_tool_calls=True时允许多余调用)。
结果经 eval_db(同一个 SQLite)落库,把 os.agno.com 连上 AgentOS 即可看历史。evals/main.py 的行为细节:全部通过时进程退出码 0,任一失败/出错则非 0,可直接接 CI;非 verbose 模式用单行 spinner 跟随工具调用事件(ToolCallStarted/ToolCallCompleted)更新进度。
Case 的核心字段(源码事实,cases.py):
@dataclass(frozen=True)
class Case:
name: str
agent: Agent
input: str
criteria: str | None = None # 设置即启用 LLM 评审
expected_tool_calls: tuple[str, ...] | None = None # 设置即启用工具断言
allow_additional_tool_calls: bool = True
image_paths: tuple[str, ...] = () # 多模态附件
audio_paths: tuple[str, ...] = ()
内置用例覆盖了:Wiki 状态诚实性(无页面就承认没有,不编造)、图片消化归档(update_local_wiki 必须触发)、HTML 生成(只断言 generate_html_file 被调用——源码注释解释:评审提示词对"报告措辞"过于敏感,工具调用断言才是 HTML 生成是否生效的稳健信号)、CodeSearch 智能体清单与"承认不知道不存在的函数"。Git/Notion 用例则随 env-gated 注册状态动态加入。
扩展 Demo:新增一个智能体
README 的 "Extending" 章节给出固定套路(完整继承):
- 在
agents/下新增一个文件定义 Agent; - 在 run.py 的
AgentOS(agents=[...])列表中注册; - 在 config.yaml 里加快捷提示词;
- 重启服务;稳定后到 evals/cases.py 补评估用例。
若新智能体需要凭证门控,参照 git_wiki.py 的写法:模块内判断环境变量,缺失时导出 None,run.py 侧 if x is not None 再入列表,evals 侧用同样的条件动态拼用例。
依赖管理与 requirements 再生成
requirements.in 是唯一事实源,共 8 个依赖:agno[os]、google-genai、fastapi[standard]、mcp、openai、rich、sqlalchemy、typer、notion-client。修改后运行 generate_requirements.sh 重新生成并 pin 住 requirements.txt:
./cookbook/01_demo/generate_requirements.sh
脚本内部执行 uv pip compile requirements.in --no-cache --upgrade -o requirements.txt。
小结
cookbook/01_demo 用一个可完整运行的最小平台演示了 Agno 的几个关键组合:AgentOS 入口与 FastAPI 集成、Context Provider 的父/子智能体分层、FileSystemBackend/GitBackend/NotionDatabaseBackend 三种 Wiki 后端的同构替换、基于环境变量的条件注册,以及 AgentAsJudgeEval + ReliabilityEval 的双轨评估。生产级版本可参考 README 中提到的 agent-platform-railway 项目(外部仓库,本仓库内不包含其代码)。若需处理音频/视频,按 settings.py 的注释把对应智能体的模型换成 gemini_flash() 即可。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00