ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
# Install Apple's PingFang fonts from the macOS on-demand font asset catalog.
#
# Since macOS Sequoia, PingFang is not preinstalled: fresh CI runners only have
# the reserved UI copy (FontServices.framework/Resources/Reserved/PingFangUI.ttc),
# which CoreText reports as "PingFang SC" but which Pango/cairo refuse to use for
# document rendering — rsvg-convert then silently falls back to Hiragino Sans
# (Japanese glyph variants) for Chinese text in SVG figures. The CoreText
# on-demand download API is likewise a no-op because the reserved copy satisfies
# the descriptor match. So fetch the real font directly from Apple's asset CDN
# (the same channel macOS itself uses) and install it into ~/Library/Fonts.
#
# Usage: install_apple_fonts.sh
set -euo pipefail
for f in "$HOME/Library/Fonts/PingFang.ttc" /Library/Fonts/PingFang.ttc \
/System/Library/Fonts/PingFang.ttc \
/System/Library/AssetsV2/com_apple_MobileAsset_Font*/*.asset/AssetData/PingFang.ttc; do
if [ -f "$f" ]; then
echo "PingFang already installed: $f"
exit 0
fi
done
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
# Font8 is the macOS 26 catalog generation; fall back to Font7 for older images.
url=""
for gen in Font8 Font7; do
catalog="https://mesu.apple.com/assets/macos/com_apple_MobileAsset_${gen}/com_apple_MobileAsset_${gen}.xml"
echo "Checking catalog $catalog"
if ! curl -fsSL "$catalog" -o "$tmp/catalog.xml"; then
continue
fi
url=$(python3 - "$tmp/catalog.xml" <<'EOF'
import plistlib, sys
cat = plistlib.load(open(sys.argv[1], "rb"))
for a in cat.get("Assets", []):
if any(fi.get("FontFamilyName") == "PingFang SC" for fi in a.get("FontInfo4", [])):
print(a["__BaseURL"] + a["__RelativePath"])
break
EOF
)
[ -n "$url" ] && break
done
if [ -z "$url" ]; then
echo "ERROR: PingFang asset not found in any catalog" >&2
exit 1
fi
echo "Downloading $url"
curl -fsSL "$url" -o "$tmp/pingfang.zip"
unzip -q "$tmp/pingfang.zip" -d "$tmp/asset"
mkdir -p "$HOME/Library/Fonts"
found=0
while IFS= read -r -d '' f; do
cp "$f" "$HOME/Library/Fonts/"
echo "Installed $(basename "$f") -> ~/Library/Fonts"
found=1
done < <(find "$tmp/asset/AssetData" \( -name "*.ttc" -o -name "*.otf" -o -name "*.ttf" \) -print0)
if [ "$found" -eq 0 ]; then
echo "ERROR: no font files found in downloaded asset" >&2
exit 1
fi
# The PDF build renders SVGs with PANGOCAIRO_BACKEND=fontconfig, so verify the
# installed font through fontconfig (CoreText's view is irrelevant here, and
# whether fontd notices a new user font in time varies between runners).
if command -v fc-match >/dev/null 2>&1; then
fc-cache -f >/dev/null 2>&1 || true
match=$(fc-match "PingFang SC" 2>/dev/null || true)
echo "fc-match PingFang SC -> $match"
case "$match" in
PingFang*) ;;
*) echo "ERROR: fontconfig does not resolve PingFang SC after install" >&2
exit 1;;
esac
else
echo "fc-match not available yet; the build's verify step will check the PDFs"
fi
echo "PingFang installed."
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Verify that Chinese PDFs embed PingFang in SVG-derived figures.
Guards against the macOS-runner regression where PingFang (an on-demand font
since macOS Sequoia) is missing and rsvg-convert silently falls back to
Hiragino Sans, rendering Chinese figure text with Japanese glyph variants.
Usage: verify_pdf_fonts.py <pdf> [<pdf> ...]
"""
import re
import sys
import zlib
from collections import Counter
# A handful of Hiragino streams appear even in correct builds (rare glyphs
# PingFang lacks; the fontconfig cascade on CI falls back slightly more often
# than local CoreText: ~12 streams vs ~4). A wholesale fallback produces 50+.
HIRAGINO_LIMIT = 20
def scan(path):
data = open(path, "rb").read()
hits = Counter()
for m in re.finditer(rb"stream\r?\n", data):
start = m.end()
end = data.find(b"endstream", start)
if end < 0:
continue
try:
decoded = zlib.decompress(data[start:end])
except zlib.error:
continue
for name in (b"PingFang", b"Hiragino", b"Songti", b"Heiti", b"Noto"):
if name in decoded:
hits[name.decode()] += 1
return hits
def main():
failed = False
for path in sys.argv[1:]:
hits = scan(path)
print(f"{path}: {dict(hits)}")
if hits["PingFang"] == 0:
print(f" ERROR: no PingFang embedded -- figure font fallback occurred")
failed = True
if hits["Hiragino"] > HIRAGINO_LIMIT:
print(f" ERROR: {hits['Hiragino']} Hiragino streams (limit {HIRAGINO_LIMIT})"
" -- Japanese fallback font used for Chinese figure text")
failed = True
sys.exit(1 if failed else 0)
if __name__ == "__main__":
main()
+288
View File
@@ -0,0 +1,288 @@
name: Build latest book artifacts
# Rebuilds the PDF and EPUB editions whenever the book sources change and
# uploads them to the rolling "latest" pre-release (assets are overwritten in
# place, so the download URLs stay stable). Versioned releases (v1.2, ...) are
# still cut manually at milestones.
on:
push:
branches: [main]
paths:
- 'book/**'
- 'book-zhtw/**'
- 'book-en/**'
- 'book-es/**'
- 'book-id/**'
- 'book-ru/**'
- 'book-ta/**'
- 'book-vi/**'
- 'book-ja/**'
- 'book-ko/**'
- 'book-ar/**'
- 'book-tr/**'
- 'book-hu/**'
- 'book-he/**'
- 'build_epub.sh'
- 'epub.css'
- 'epub_external_links.lua'
- 'flatten_epub_toc.py'
- '.github/workflows/build-latest.yml'
- '.github/scripts/**'
workflow_dispatch:
inputs:
edition:
description: Edition to verify on a non-main ref
required: true
default: all
type: choice
options:
- all
- zh-CN
- zh-TW
- en
- es
- id
- ru
- ta
- vi
- tr
- ko
- hu
- ja
- ar
- he
permissions:
contents: write
concurrency:
# Serialize runs for the same ref without allowing a newer push to cancel
# a long-running build that is already in progress.
group: build-latest-${{ github.ref }}
cancel-in-progress: false
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
# ── Build-environment caching ──────────────────────────────────────────
# GitHub-hosted runners are ephemeral, so the ~10 min of tool/font
# installs re-runs on every push. The two heavy pieces — the MacTeX tree
# (~5 min) and the document fonts (~2 min) — are cached across runs and
# only reinstalled on a cache miss.
#
# Bump the trailing "-vN" in a cache key whenever you change what that
# cache holds (the mactex package, or the font cask list / Apple-font
# script) so the next run rebuilds it instead of restoring a stale tree.
# Homebrew cask fonts AND the Apple PingFang download all land in
# ~/Library/Fonts, so one user-writable directory captures every font.
- name: Restore fonts cache
id: fonts-cache
uses: actions/cache@v4
with:
path: ~/Library/Fonts
key: fonts-${{ runner.os }}-${{ runner.arch }}-v4-${{ hashFiles('.github/scripts/install_apple_fonts.sh') }}
# The MacTeX tree is installed by a root pkg into /usr/local/texlive, so it
# can't be extracted straight back as the runner user. Cache a tarball
# instead (a user-owned file actions/cache can read) and restore it with
# sudo. /Library/TeX holds the texbin symlinks and is tarred alongside.
- name: Restore TeX Live cache
id: texlive-cache
uses: actions/cache@v4
with:
path: ~/texlive-cache/texlive.tar
key: texlive-${{ runner.os }}-${{ runner.arch }}-mactex-nogui-v1
- name: Install CLI tools
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
HOMEBREW_NO_INSTALL_CLEANUP: '1'
run: brew install pandoc poppler librsvg epubcheck
- name: Install MacTeX (cache miss)
if: steps.texlive-cache.outputs.cache-hit != 'true'
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
HOMEBREW_NO_INSTALL_CLEANUP: '1'
run: |
brew install --cask mactex-no-gui
mkdir -p ~/texlive-cache
sudo tar -cf ~/texlive-cache/texlive.tar -C / usr/local/texlive Library/TeX
sudo chown "$USER" ~/texlive-cache/texlive.tar
- name: Restore MacTeX from cache (cache hit)
if: steps.texlive-cache.outputs.cache-hit == 'true'
run: sudo tar -xf ~/texlive-cache/texlive.tar -C /
- name: Put TeX on PATH
run: echo "/Library/TeX/texbin" >> "$GITHUB_PATH"
# PingFang is an on-demand font since macOS Sequoia and is absent on fresh
# runners (only an unusable reserved UI copy exists); without it
# rsvg-convert renders Chinese figure text in a fallback font (Japanese
# glyph variants). The Apple-font script fetches the real font from Apple's
# asset CDN. Arabic and Korean fonts are required by release builds; the
# Japanese Noto fonts (WIP ja build) remain non-fatal so a bad cask name
# can never break the release build for the other languages.
- name: Install fonts (cache miss)
if: steps.fonts-cache.outputs.cache-hit != 'true'
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
HOMEBREW_NO_INSTALL_CLEANUP: '1'
run: |
brew install --cask font-dejavu font-noto-sans-cjk-sc font-noto-sans-cjk-tc font-noto-serif-tamil font-amiri font-noto-naskh-arabic font-noto-sans-arabic
brew install --cask font-noto-sans-cjk-jp || true
brew install --cask font-noto-serif-cjk-jp || brew install --cask font-noto-serif-cjk || true
brew install --cask font-noto-sans-cjk-kr
brew install --cask font-noto-serif-cjk-kr || brew install --cask font-noto-serif-cjk
bash .github/scripts/install_apple_fonts.sh
# Refresh fontconfig on every run (the cache restore doesn't touch it).
# PANGOCAIRO_BACKEND=fontconfig at build time: pango's CoreText backend on
# the runners can't use the user-installed fonts and silently substitutes
# another CJK font in SVG figures; the fontconfig backend picks them up.
- name: Refresh font cache
run: fc-cache -f
# Each language is built in its own background job so the XeLaTeX
# passes run concurrently instead of back-to-back. Output is captured per
# language and printed after all jobs finish (interleaved live logs would
# be unreadable). The ja build (WIP) runs in the same batch but is
# non-fatal — only a main-language failure fails the step.
# macOS runners ship bash 3.2, so no associative arrays: pid↔dir pairing
# is carried in a "pid:dir" string list.
- name: Build PDFs
env:
PANGOCAIRO_BACKEND: fontconfig
EDITION: ${{ inputs.edition || 'all' }}
run: |
set -u
if [ "$GITHUB_REF" = "refs/heads/main" ] || [ "$EDITION" = "all" ]; then
main="book book-zhtw book-en book-es book-id book-ru book-vi book-ta book-ar book-tr book-ko book-hu book-he"
optional="book-ja"
else
# workflow_dispatch on a feature branch is a focused edition
# verification run. Release publication remains gated to main.
case "$EDITION" in
zh-CN) main="book" ;;
zh-TW) main="book-zhtw" ;;
en) main="book-en" ;;
es) main="book-es" ;;
id) main="book-id" ;;
ru) main="book-ru" ;;
ta) main="book-ta" ;;
vi) main="book-vi" ;;
tr) main="book-tr" ;;
ko) main="book-ko" ;;
hu) main="book-hu" ;;
ja) main="book-ja" ;;
ar) main="book-ar" ;;
he) main="book-he" ;;
*) echo "::error::Unsupported edition: $EDITION"; exit 2 ;;
esac
optional=""
fi
pids=""
for dir in $main $optional; do
( cd "$dir" && bash build_pdf.sh ) > "build_${dir}.log" 2>&1 &
pids="$pids $!:$dir"
done
fail=0
for pd in $pids; do
pid="${pd%%:*}"; dir="${pd##*:}"
if wait "$pid"; then
echo "OK: $dir"
elif [ "$dir" = "book-ja" ]; then
echo "::warning::$dir PDF build failed (WIP, non-fatal)"
else
echo "::error::PDF build failed for $dir"
fail=1
fi
done
for dir in $main $optional; do
echo "===================== $dir ====================="
cat "build_${dir}.log" 2>/dev/null || true
done
exit $fail
- name: Verify Chinese figure fonts
if: github.ref == 'refs/heads/main'
run: |
python3 .github/scripts/verify_pdf_fonts.py \
"book/深入理解-AI-Agent-李博杰-v2.0.pdf" \
"book-zhtw/深入理解-AI-Agent-李博杰-v2.0-zhtw.pdf"
- name: Build EPUBs
env:
EDITION: ${{ inputs.edition || 'all' }}
run: |
if [ "$GITHUB_REF" = "refs/heads/main" ] || [ "$EDITION" = "all" ]; then
./build_epub.sh all
else
./build_epub.sh "$EDITION"
fi
# WIP: ja EPUB depends on the ja PDF above; non-fatal, not part of `all`.
- name: Build Japanese EPUB (WIP, non-fatal)
if: github.ref == 'refs/heads/main'
continue-on-error: true
run: ./build_epub.sh ja
- name: Build Arabic EPUB (WIP, non-fatal)
if: github.ref == 'refs/heads/main'
continue-on-error: true
run: ./build_epub.sh ar
- name: Upload verification artifacts
if: github.repository != 'bojieli/ai-agent-book' || github.ref != 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: book-verification-${{ inputs.edition || 'all' }}
path: |
book*/*.pdf
book*/*.epub
if-no-files-found: error
- name: Upload to the rolling latest release
if: github.repository == 'bojieli/ai-agent-book' && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
out="$(mktemp -d)"
cp "book/深入理解-AI-Agent-李博杰-v2.0.pdf" "$out/AI-Agents-in-Depth-zh-CN.pdf"
cp "book-zhtw/深入理解-AI-Agent-李博杰-v2.0-zhtw.pdf" "$out/AI-Agents-in-Depth-zh-TW.pdf"
cp book-en/AI-Agents-in-Depth-Bojie-Li-v2.0.pdf "$out/AI-Agents-in-Depth-en.pdf"
cp book-es/AI-Agents-en-Profundidad-Bojie-Li-v2.0-es.pdf "$out/AI-Agents-in-Depth-es.pdf"
cp book-id/AI-Agents-in-Depth-Bojie-Li-v2.0-id.pdf "$out/AI-Agents-in-Depth-id.pdf"
cp book-ru/AI-Agents-in-Depth-v2.0-ru.pdf "$out/AI-Agents-in-Depth-ru.pdf"
cp book-ta/AI-Agents-in-Depth-Bojie-Li-v2.0-ta.pdf "$out/AI-Agents-in-Depth-ta.pdf"
cp book-vi/AI-Agents-in-Depth-Bojie-Li-v2.0-vi.pdf "$out/AI-Agents-in-Depth-vi.pdf"
cp book-ar/AI-Agents-in-Depth-v2.0-ar.pdf "$out/AI-Agents-in-Depth-ar.pdf"
cp book-tr/AI-Agents-in-Depth-Bojie-Li-v2.0-tr.pdf "$out/AI-Agents-in-Depth-tr.pdf"
cp book-ko/AI-Agents-in-Depth-v2.0-ko.pdf "$out/AI-Agents-in-Depth-ko.pdf"
cp book-hu/AI-Agents-in-Depth-v2.0-hu.pdf "$out/AI-Agents-in-Depth-hu.pdf"
cp book-he/AI-Agents-in-Depth-v2.0-he.pdf "$out/AI-Agents-in-Depth-he.pdf"
cp "book/深入理解-AI-Agent-李博杰-v2.0.epub" "$out/AI-Agents-in-Depth-zh-CN.epub"
cp "book-zhtw/深入理解-AI-Agent-李博杰-v2.0-zhtw.epub" "$out/AI-Agents-in-Depth-zh-TW.epub"
cp book-en/AI-Agents-in-Depth-Bojie-Li-v2.0.epub "$out/AI-Agents-in-Depth-en.epub"
cp book-es/AI-Agents-en-Profundidad-Bojie-Li-v2.0-es.epub "$out/AI-Agents-in-Depth-es.epub"
cp book-id/AI-Agents-in-Depth-Bojie-Li-v2.0-id.epub "$out/AI-Agents-in-Depth-id.epub"
cp book-ru/AI-Agents-in-Depth-v2.0-ru.epub "$out/AI-Agents-in-Depth-ru.epub"
cp book-ta/AI-Agents-in-Depth-Bojie-Li-v2.0-ta.epub "$out/AI-Agents-in-Depth-ta.epub"
cp book-vi/AI-Agents-in-Depth-Bojie-Li-v2.0-vi.epub "$out/AI-Agents-in-Depth-vi.epub"
cp book-tr/AI-Agents-in-Depth-Bojie-Li-v2.0-tr.epub "$out/AI-Agents-in-Depth-tr.epub"
cp book-ko/AI-Agents-in-Depth-v2.0-ko.epub "$out/AI-Agents-in-Depth-ko.epub"
cp book-hu/AI-Agents-in-Depth-v2.0-hu.epub "$out/AI-Agents-in-Depth-hu.epub"
cp book-he/AI-Agents-in-Depth-v2.0-he.epub "$out/AI-Agents-in-Depth-he.epub"
# WIP: ja artifacts and the Arabic EPUB are copied only if their
# non-fatal builds produced them.
cp book-ja/AI-Agents-in-Depth-Bojie-Li-v2.0-ja.pdf "$out/AI-Agents-in-Depth-ja.pdf" || echo "ja PDF not built yet (WIP) — skipping"
cp book-ja/AI-Agents-in-Depth-Bojie-Li-v2.0-ja.epub "$out/AI-Agents-in-Depth-ja.epub" || echo "ja EPUB not built yet (WIP) — skipping"
cp book-ar/AI-Agents-in-Depth-v2.0-ar.epub "$out/AI-Agents-in-Depth-ar.epub" || echo "ar EPUB not built yet (WIP) — skipping"
gh release upload latest "$out"/* --clobber --repo "${{ github.repository }}"
@@ -0,0 +1,51 @@
name: dependency resolution
on:
pull_request:
paths:
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/dependency-resolution.yml"
push:
branches: [main]
paths:
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/dependency-resolution.yml"
workflow_dispatch: {}
permissions:
contents: read
jobs:
resolve:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Exercise both ends of the supported range in project.requires-python.
python-version: ["3.11", "3.13"]
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Check lockfile freshness
run: uv lock --check
- name: Install Python
run: uv python install ${{ matrix.python-version }}
- name: Check chapter extras
shell: bash
run: |
for extra in ch1 ch2 ch3 ch4 ch5 ch6 ch7 ch8 ch9 ch10 all; do
echo "::group::Python ${{ matrix.python-version }} - $extra"
uv sync --locked --dry-run \
--python "${{ matrix.python-version }}" \
--extra "$extra"
echo "::endgroup::"
done
+54
View File
@@ -0,0 +1,54 @@
name: deploy-pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
env:
# Silence the MkDocs 2.0 banner. We pin to mkdocs-material 9.x in
# requirements-docs.txt, so the upcoming breaking change doesn't
# affect us until we explicitly upgrade. The variable name is read
# by material/templates/__init__.py and stable across 9.x releases.
NO_MKDOCS_2_WARNING: "1"
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install MkDocs Material
run: pip install -r requirements-docs.txt
- name: Assemble docs
run: bash scripts/build_site.sh
- name: Build site
run: mkdocs build -d site
- uses: actions/upload-pages-artifact@v5
with:
path: site
deploy:
# Publishing is owned by the canonical repository; forks still verify builds.
if: github.repository == 'bojieli/ai-agent-book'
needs: build
permissions:
pages: write
id-token: write
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v5
+64
View File
@@ -0,0 +1,64 @@
name: i18n consistency check
# 防止主页或某章 README 改动后,其它语言版本跟不上而漂移。
# 详见 scripts/check_i18n_consistency.py。
#
# 触发时机:
# 1. 任何 PR / push 改动了 README、chapterN/、docs/<locale>/ 等 i18n 相关文件
# 2. 手动 workflow_dispatch
on:
pull_request:
paths:
- "README.md"
- "README.he.md"
- "index*.md"
- "docs/**"
- "chapter*/README*.md"
- "book*/**"
- "mkdocs.yml"
- "extras/site-nav-i18n.json"
- "extras/lang-switcher.js"
- "extras/nav-collapse.js"
- "scripts/check_i18n_consistency.py"
- "scripts/site_i18n.py"
- ".github/workflows/i18n-check.yml"
push:
branches: [main]
paths:
- "README.md"
- "README.he.md"
- "index*.md"
- "docs/**"
- "chapter*/README*.md"
- "book*/**"
- "mkdocs.yml"
- "extras/site-nav-i18n.json"
- "extras/lang-switcher.js"
- "extras/nav-collapse.js"
- "scripts/check_i18n_consistency.py"
- "scripts/site_i18n.py"
- ".github/workflows/i18n-check.yml"
workflow_dispatch: {}
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install static-site i18n dependency
run: pip install "mkdocs-material>=9.5,<10"
- name: Check static-site navigation and UI translations
run: python scripts/site_i18n.py
- name: Run i18n consistency check
run: python scripts/check_i18n_consistency.py
@@ -0,0 +1,82 @@
name: provider adoption tests
# Chapters 2 and 3 resolve endpoints, credentials and model ids through
# agentbook.providers rather than through per-experiment copies of the old
# openrouter_fallback.py. That makes a change to agentbook/ able to break six
# experiments at once, in code paths none of their own tests would flag as
# related -- so the shared package is a trigger here, exactly as it is for
# chapter 1 in web-search-agent-tests.yml.
on:
pull_request:
paths:
- "agentbook/**"
- "chapter2/context-compression/**"
- "chapter2/prompt-injection/**"
- "chapter2/system-hint/**"
- "chapter3/log-sanitization/**"
- "pyproject.toml"
- ".github/workflows/provider-adoption-tests.yml"
push:
branches: [main]
paths:
- "agentbook/**"
- "chapter2/context-compression/**"
- "chapter2/prompt-injection/**"
- "chapter2/system-hint/**"
- "chapter3/log-sanitization/**"
- "pyproject.toml"
- ".github/workflows/provider-adoption-tests.yml"
workflow_dispatch: {}
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Two of the six migrated experiments are deliberately absent.
# chapter2/kv-cache keeps live-API scripts at its root that exit(1)
# without MOONSHOT_API_KEY, and chapter2/agent-skills-ppt cannot import
# python-pptx under the shared install. Both fail the same way before
# this migration; adding them needs the Phase 6A test/manual split
# first, not a workaround here.
experiment:
- chapter2/context-compression
- chapter2/prompt-injection
- chapter2/system-hint
- chapter3/log-sanitization
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# The chapter aggregates ch2 and ch3 pull torch, which these offline
# tests never touch. Installing the capability groups they do use keeps
# the job to seconds; a chapter that outgrows this set will fail on the
# missing import rather than resolving it silently.
- name: Install the package
run: python -m pip install -e ".[dev,web,tokens]"
# Declared by chapter3/log-sanitization's requirements.txt and not yet in
# any capability group. Named here rather than widened into pyproject so
# the CI contract stays visible until Phase 7 reconciles that file.
- name: Install log-sanitization extras
run: python -m pip install "ollama>=0.3.0" "pyyaml>=6.0"
# Empty rather than unset: a resolver bug that reads a key from the
# runner environment must fail here, not silently pass.
- name: Run offline tests
working-directory: ${{ matrix.experiment }}
env:
MOONSHOT_API_KEY: ""
KIMI_API_KEY: ""
OPENROUTER_API_KEY: ""
OPENAI_API_KEY: ""
run: python -m pytest -q
+45
View File
@@ -0,0 +1,45 @@
name: Update star history chart
on:
schedule:
# Daily at 03:00 UTC.
- cron: "0 3 * * *"
workflow_dispatch: {}
permissions:
contents: write
concurrency:
group: star-history
cancel-in-progress: false
jobs:
update-chart:
# Avoid running this upstream-only maintenance task in forks.
if: github.repository == 'bojieli/ai-agent-book'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install matplotlib
run: pip install matplotlib
- name: Generate star history chart
run: python scripts/gen_star_history.py --refresh
env:
# STAR_HISTORY_TOKEN(细粒度 PAT)如存在则优先,否则用内置 GITHUB_TOKEN
# 读取公开仓库的 stargazers 时间戳不需要额外权限。
GITHUB_TOKEN: ${{ secrets.STAR_HISTORY_TOKEN || secrets.GITHUB_TOKEN }}
- name: Commit updated charts
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add assets/star-history-*.png
git diff --cached --quiet && exit 0
git commit -m "Update star history chart [skip ci]"
git push
@@ -0,0 +1,76 @@
name: web-search-agent tests
on:
pull_request:
paths:
- "chapter1/web-search-agent/**"
# The chapter imports agentbook.providers, so a change there can break
# these tests without touching the chapter directory at all.
- "agentbook/**"
- "tests/**"
- "pyproject.toml"
- ".github/workflows/web-search-agent-tests.yml"
push:
branches: [main]
paths:
- "chapter1/web-search-agent/**"
- "agentbook/**"
- "tests/**"
- "pyproject.toml"
- ".github/workflows/web-search-agent-tests.yml"
workflow_dispatch: {}
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: chapter1/web-search-agent
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: chapter1/web-search-agent/requirements.txt
- name: Install dependencies
run: python -m pip install -r requirements.txt
- name: Check formatting
run: python -m black --check tests
- name: Run offline unit tests
env:
MOONSHOT_API_KEY: ""
KIMI_API_KEY: ""
OPENROUTER_API_KEY: ""
run: python -m pytest
# The shared provider registry has its own suite at the repository root.
# Without this job a change to agentbook/ could only be caught indirectly,
# via whichever chapter happened to import the broken code.
agentbook:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install the package
run: python -m pip install -e ".[dev]"
- name: Run registry tests
env:
MOONSHOT_API_KEY: ""
KIMI_API_KEY: ""
OPENROUTER_API_KEY: ""
run: python -m pytest tests -q