首页
/ Agno Demo AgentOS 实战:构建多后端 Wiki 智能体平台(本地/Git/Notion)

Agno Demo AgentOS 实战:构建多后端 Wiki 智能体平台(本地/Git/Notion)

2026-09-05 23:16:04作者:劳婵绚Shirley

本篇以 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_URLWIKI_GITHUB_TOKEN
NotionWiki 同一套 Wiki 智能体,但后端是 Notion 数据库,一行一页,数据库即团队打开的"真相源" 需设置 NOTION_API_KEYNOTION_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 并登录:

  1. Add OSLocal
  2. 连接 http://localhost:8000,命名为 Local AgentOS
  3. 开始与智能体对话

模型与工具的配置中心: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 地址(不能是 SSHgit@… 会被拒绝)
WIKI_GITHUB_TOKEN 对该仓库有 Contents: Read and write 的 PAT,认证后从日志中剔除
WIKI_BRANCH 默认 main
WIKI_LOCAL_PATH 本地克隆路径,默认 ./data/git-wiki/

README 中的分步配置流程(完整继承):

  1. 选一个仓库存放 Wiki。全新空仓库即可,但要带一个初始提交(GitHub 上勾选 Add a README),保证 main 分支在首次克隆时存在;
  2. 创建 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:使用 repo scope;
  3. 导出环境变量并重启
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 配置流程(完整继承):

  1. 创建 Integration:notion.so → Integrations → New integrationInternal,复制 token(ntn_…)。默认的 read、insert、update content 权限即可;
  2. 建一个数据库作为 Wiki。用 full-page database(table 视图即可),唯一需要的列是内建 title,页面名由智能体起。Wiki 是平铺的:一行一页,无嵌套页面;
  3. 把 Integration 连接到该数据库——最容易被漏掉的一步:以 full page 打开数据库 → ••• 菜单 → Connections → 添加你的 Integration。缺了这步 API 根本看不到该数据库;
  4. 从 URL 复制 database ID:是路径里 ?v= 之前的 32 位十六进制串(v= 值是视图 id,要丢掉):
https://www.notion.so/<workspace>/<DATABASE_ID>?v=<view_id>
  1. 导出环境变量并重启
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-wikicode-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.pyCase 数据结构):

  • judgeAgentAsJudgeEvalcriteria 用 LLM 打二元 pass/fail;
  • reliabilityReliabilityEval 断言 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" 章节给出固定套路(完整继承):

  1. agents/ 下新增一个文件定义 Agent;
  2. run.pyAgentOS(agents=[...]) 列表中注册;
  3. config.yaml 里加快捷提示词;
  4. 重启服务;稳定后到 evals/cases.py 补评估用例。

若新智能体需要凭证门控,参照 git_wiki.py 的写法:模块内判断环境变量,缺失时导出 Nonerun.pyif x is not None 再入列表,evals 侧用同样的条件动态拼用例。

依赖管理与 requirements 再生成

requirements.in 是唯一事实源,共 8 个依赖:agno[os]google-genaifastapi[standard]mcpopenairichsqlalchemytypernotion-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() 即可。

登录后查看全文
热门项目推荐
相关项目推荐