Hermes Context Files:5 種文件、4 級優先級、20K 字符硬限制全拆解

作者:術哥無界
日期:2026年5月31日 上午11:34
來源:WeChat 原文

整理版優先睇

速讀 5 個重點 高亮

Hermes AgentContext Files機制全面拆解:5種文件、4級優先級、20K字符硬限制,教你點樣寫好上下文文件

整理版摘要

呢篇文章係由術哥撰寫,佢係一位專注AI編程同Agent Skills嘅技術實踐者。文章深入分析Hermes Agent v0.15.1嘅Context Files機制,包括5種文件類型、4級優先級、20K字符硬限制同截斷策略,仲有安全掃描等細節。作者想解決嘅問題係:好多開發者亂寫上下文文件,搞到Agent唔聽指令,甚至文件冇被加載。佢通過源碼解讀,講清楚每種文件嘅加載時機、優先級規則、截斷行為,仲畀出實戰建議。整體結論係:Context Files唔係越多越好,而係要精準、結構化、保持精簡,先可以發揮最佳效果。

首先,Hermes Agent支援5種上下文文件,包括.hermes.md、AGENTS.mdCLAUDE.md、.cursorrules同SOUL.md。佢哋嘅優先級係First Match Wins,即係高優先級文件會完全遮蔽低優先級文件,同一個目錄下只會加載一個。呢點好容易令開發者中招——如果個項目同時有.hermes.md同AGENTS.md,後者嘅內容Agent一個字都睇唔到。另外,SOUL.md係獨立Slot,放人格指令,唔受項目文件影響。而20K字符硬限制係另一個大陷阱:超過上限時,系統會保留前70%同後20%,中間10%直接截斷。如果你啲重要規範擺喺中間,Agent永遠見唔到。作者建議根目錄文件控制在5K-10K字符,子目錄3K-5K字符。

子目錄發現機制係另一個亮點:Agent第一次訪問某…

  • 結論First Match Wins機制決定只有一個項目上下文文件生效,高優先級文件完全遮蔽低優先級文件。
  • 方法:20K字符硬限制,超出時70%頭部+20%尾部保留,中間截斷;建議根目錄文件5K-10K字符,子目錄3K-5K字符。
  • 差異Hermes相比Claude CodeCursor有漸進式子目錄發現、安全掃描、SOUL.md獨立Slot等優勢。
  • 啟發:寫上下文文件要結構化(用標題分區)、具體化(寫命令唔好寫描述)、加負面清單(寫禁止做咩)。
  • 可行動點:按文章提供嘅Full-Stack Monorepo示例,為根目錄同子目錄分別撰寫AGENTS.md,並配合SOUL.md放人格指令。
整理重點

Context Files家族:5種文件,4級優先級

Hermes Agent支援5種上下文文件,包括.hermes.md、AGENTS.mdCLAUDE.md、.cursorrules同SOUL.md。優先級分為P0-P3,SOUL.md獨立。

First Match Wins

呢個機制決定咗同一個目錄下只會加載一個項目上下文文件,高優先級會遮蔽低優先級。如果同時有.hermes.md同AGENTS.md,只有.hermes.md會被加載,AGENTS.md完全冇用。

  • .hermes.md / HERMES.md:P0最高優先,從CWD向上遍歷至Git根目錄
  • AGENTS.md / agents.md:P1,只從CWD讀取
  • CLAUDE.md / claude.md:P2,兼容Claude Code
  • .cursorrules:P3,僅CWD
  • SOUL.md:獨立Slot,只從~/.hermes/加載,放人格指令
整理重點

完整加載鏈路:從磁盤到系統提示詞

一份Context File從被讀取到注入系統提示詞,總共經過8步:掃描、發現、讀取、預處理、安全掃描、截斷、組裝、注入。

20,000字符硬限制

超過上限時會觸發截斷,保留前70%(14K字符)同後20%(4K字符),中間10%被丟棄並插入截斷標記。

70%/20%截斷

截斷標記包含文件名同保留長度,如果Agent回覆中出現呢個標記,就代表文件被截斷咗。建議根目錄文件控制在5K-10K字符。

  • CONTEXT_FILE_MAX_CHARS = 20,000
  • 頭部保留70% = 14,000字符
  • 尾部保留20% = 4,000字符
  • 中間10% = 2,000字符留俾截斷標記
整理重點

漸進式子目錄發現:按需加載,唔浪費Token

對於大型項目,Hermes提供漸進式子目錄發現機制。Agent第一次訪問某個子目錄時,SubdirectoryHintTracker會自動檢測並加載該目錄嘅Context File

每個目錄只檢查一次

子目錄Context File上限8,000字符,注入到工具調用結果而唔係系統提示詞,咁樣系統提示詞唔變,prompt caching就唔會失效。

  • 字符上限:根目錄20,000,子目錄8,000
  • 注入方式:根目錄入系統提示詞,子目錄入工具結果
  • 選擇策略:根目錄First Match Wins,子目錄全部加載
  • caching影響:根目錄有影響,子目錄冇
整理重點

寫好Context Files四條鐵律同實戰配置

理論講完,睇下實戰。作者總結四條鐵律:保持精簡、結構化排版、具體化示例、明確負面清單。

離20K上限越遠越好

根目錄AGENTS.md建議5K-10K字符,子目錄3K-5K字符。每個區塊3-7條規則,用##標題分區。

寫命令唔好寫描述

例如「pnpm test(唔好用npm)」比「使用合適包管理器運行測試」有效得多。

負面清單係最有價值

寫明禁止做咩,例如「禁止直接修改數據庫遷移文件」,可以避免Agent犯常見錯誤。

根目錄 AGENTS.md 示例 markdown
# Project: MySaaS

## Architecture
- Monorepo: frontend/ (Next.js 15 + TypeScript) + backend/ (FastAPI + Python 3.12)
- Database: PostgreSQL 16, Alembic 管理遷移
- Monorepo 工具: pnpm workspaces + turborepo

## Build & Test
- 全量構建: `pnpm build`
- 全量測試: `pnpm test`
- 後端測試: `cd backend && pytest`
- 前端測試: `cd frontend && pnpm test`
- Lint: `pnpm lint`

## What NOT to do
- 不要混合使用 npm 和 pnpm
- 不要在根目錄放置子項目特定的配置
- 不要跳過 Alembic 遷移直接修改數據庫 schema
- 不要在根 AGENTS.md 中重複子項目已有的規則

🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 126 篇,Hermes Agent 最佳實戰「2026」系列第 7 篇

大家好,歡迎嚟到 術哥無界 | ShugeX | 運維有術

我是術哥,一個專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫嘅技術實踐者同開源佈道者

Talk is cheap, let's explore。無界探索,有術而行。

封面圖:Hermes Agent Context Files 架構總覽
封面圖:Hermes Agent Context Files 架構總覽

你花咗成個下晝精心寫咗一份 AGENTS.md,列咗幾十條規範,涵蓋代碼風格、構建命令、測試策略。結果 Agent 嘅表現同冇讀過一樣——指令被無視,命名風格亂曬籠,構建命令都用錯咗。

反咗一圈源碼先發現問題:項目根目錄下同時存在 .hermes.md 和 AGENTS.md,而 Hermes 嘅優先級機制係 First Match Wins——你嘅 AGENTS.md 根本冇被加載。

呢個唔係個別例子。ETH Zurich 2026 年嘅一份研究指出,自動生成嘅上下文文件反而會降低 Agent 任務成功率,人工編寫嘅只提升咗 4%。問題唔在於寫不寫,而在於寫乜、寫幾多、擺喺邊

今日呢篇文章,就從 Hermes Agent v0.15.1 嘅源碼出發,將 Context Files 嘅加載機制、優先級系統、截斷策略、安全防線徹底講清楚。

說明:本文內容基於 Hermes Agent 源碼(nousresearch/hermes-agent)同 v0.15.1 官方文檔分析整理而成,源碼分析基於筆者本地倉庫版本。文中嘅配置模板同參數建議僅供參考,實際效果請以你嘅業務數據同環境測試結果為準。如果有實際使用經驗,歡迎喺評論區分享交流。

1. Context Files 家族:5 種文件,4 層優先級

Hermes Agent 支援嘅上下文文件一共有 5 種,覆蓋咗市面上主流 AI Agent 嘅配置格式。

文件類型
用途
發現路徑
優先級
.hermes.md
 / HERMES.md
項目級最高優先指令
從 CWD 向上遍歷至 Git 根目錄
P0
AGENTS.md
 / agents.md
核心項目規範與約定
CWD(啓動時)+ 子目錄漸進發現
P1
CLAUDE.md
 / claude.md
兼容 Claude Code 規範
CWD + 子目錄漸進發現
P2
.cursorrules
兼容 Cursor IDE 規則
僅 CWD
P3
SOUL.md
全局人格與語氣定製
僅 ~/.hermes/ 目錄
獨立 Slot
優先級層級圖:4 級優先級棧與 SOUL.md 獨立 Slot
優先級層級圖:4 級優先級棧與 SOUL.md 獨立 Slot

圖 1:Context Files 優先級層級,P0-P3 共享項目上下文槽位,SOUL.md 走獨立 Slot

呢度有個容易踩嘅坑:前 4 種文件共享同一個項目上下文槽位,同一個目錄下只有一個會被加載。SOUL.md 就完全獨立,佢有自己嘅專屬 Slot,唔受項目文件影響。

First Match Wins:高優先級文件會遮住低優先級文件

源碼位置:agent/prompt_builder.py 第 1468-1496 行

project_context = (
    _load_hermes_md(cwd_path)      # 優先級 1:.hermes.md / HERMES.md
    or _load_agents_md(cwd_path)    # 優先級 2:AGENTS.md / agents.md
    or _load_claude_md(cwd_path)    # 優先級 3:CLAUDE.md / claude.md
    or _load_cursorrules(cwd_path)  # 優先級 4:.cursorrules
)

呢段 Python 嘅短路求值(short-circuit evaluation)決定咗加載邏輯:由高優先級開始逐個嘗試,第一個成功讀取到內容嘅文件,直接就當成整個項目上下文,後面嘅全部跳過。

即係話,如果你嘅項目根目錄同時有 .hermes.md 和 AGENTS.md,只有 .hermes.md 嘅內容會被注入系統提示詞。AGENTS.md 裏面嘅所有規範,Agent 一個字都睇唔到。

仲有一點值得注意:.hermes.md 會從當前工作目錄(CWD)向上遍歷到 Git 根目錄嚟揾,而 AGENTS.md 和 CLAUDE.md 只從 CWD 讀取,唔會向上搜索。

SOUL.md 嘅特殊地位

SOUL.md 行嘅係完全獨立嘅加載路徑。源碼喺 agent/prompt_builder.py 第 1355-1380 行:

def load_soul_md() -> Optional[str]:
    """Load SOUL.md from HERMES_HOME and return its content, or None.
    Used as the agent identity (slot #1 in the system prompt).
    """

    soul_path = get_hermes_home() / "SOUL.md"

幾個關鍵特性:

  • 只從 HERMES_HOME(默認 ~/.hermes/)加載,唔受項目目錄影響
  • 作為 Agent 身份嘅 Slot #1 注入,排喺項目上下文之前
  • 文件為空時唔注入任何內容
  • Hermes 首次運行時會自動生成一份預設 SOUL.md

SOUL.md 適合放啲乜?全局人格特質,例如你想 Agent 用咩風格回覆、用中文定英文、比較保守定進取。技術規範同項目約定唔好放呢度,嗰啲屬於 AGENTS.md 嘅職責。

仲有一個小細節容易被忽略:SOUL.md 同項目上下文文件一樣,都要經過安全掃描同截斷處理。如果 ~/.hermes/SOUL.md 超過 20,000 字符,一樣會被 head/tail 截斷。不過對於人格指令嚟講,呢個限制基本上唔會掂到。

2. 完整加載鏈路:從磁盤到系統提示詞

瞭解咗文件類型之後,嚟睇一份 Context File 由被讀取到注入系統提示詞,中間到底經歷咗啲乜。

加載管線流程圖:8 步從磁盤到系統提示詞
加載管線流程圖:8 步從磁盤到系統提示詞

圖 2:Context File 加載管線,8 步從掃描到注入,第 6 步截斷內嵌 70%/20% 比例

源碼位置:agent/prompt_builder.py 第 1468-1507 行

掃描(Scan)
  → 發現(Discover):按優先級檢查文件存在性
  → 讀取(Read):UTF-8 編碼讀取文件內容
  → 預處理(Preprocess):去除 YAML frontmatter(.hermes.md 支持)
  → 安全掃描(Security Scan):檢測 prompt injection
  → 截斷(Truncate):超過 20,000 字符時執行 head/tail 截斷
  → 組裝(Assemble):合併到 # Project Context 標題下
  → 注入(Inject):添加到系統提示詞

每一步都有學問。安全掃描嗰一步尤其關鍵——佢唔只係行個過場,後面會詳細講。

有個細節值得留意:預處理階段只對 .hermes.md 支援去除 YAML frontmatter。如果你喺 AGENTS.md 裏面加咗 frontmatter,佢會原樣注入系統提示詞,白白浪費幾百個字符嘅額度。

20,000 字符硬限制與 70%/20% 截斷

呢個係成條鏈路裏面容易令人中招嘅地方。

源碼位置:agent/prompt_builder.py 第 875-877 行,定義咗三個常量:

CONTEXT_FILE_MAX_CHARS = 20_000          # 單文件上限 20,000 字符
CONTEXT_TRUNCATE_HEAD_RATIO = 0.7        # 頭部保留 70% = 14,000 字符
CONTEXT_TRUNCATE_TAIL_RATIO = 0.2        # 尾部保留 20% = 4,000 字符
# 中間 10% = 2,000 字符留給截斷標記

截斷函數 _truncate_content() 嘅實現(第 1343-1352 行):

def _truncate_content(content: str, filename: str,
                      max_chars: int = CONTEXT_FILE_MAX_CHARS)
 -> str:

    if len(content) <= max_chars:
        return content
    head_chars = int(max_chars * CONTEXT_TRUNCATE_HEAD_RATIO)  # 14,000
    tail_chars = int(max_chars * CONTEXT_TRUNCATE_TAIL_RATIO)  # 4,000
    head = content[:head_chars]
    tail = content[-tail_chars:]
    marker = (f"\n\n[...truncated {filename}: kept "
              f"{head_chars}+{tail_chars} of {len(content)} chars. "
              f"Use file tools to read the full file.]\n\n")
    return head + marker + tail

翻譯嚇:如果你嘅文件超過 20,000 字符,系統會保留前 14,000 同後 4,000 字符,中間嘅內容直接丟掉,換成一條截斷提示。

即係話咩?假設你將最重要嘅約定擺喺文件中間偏後嘅位置,啱啱好落在截斷區間裏面——Agent 永遠睇唔到嗰啲內容。

20,000 字符大約等於 7,000 tokens。睇落好似唔少,但如果你喺裏面塞咗大段代碼示例、目錄結構、API 文檔,好快就填滿。呢個都係點解社羣嘅最佳實踐建議控制在 5,000-10,000 字符 以內,咁樣先可以畀安全截斷留有餘地。

一個實戰中嘅除錯技巧:如果你懷疑 Agent 冇睇到某條指令,檢查嚇截斷標記。Hermes 喺截斷時會插入一條提示,格式類似 [...truncated AGENTS.md: kept 14000+4000 of 25000 chars...]。如果你喺 Agent 嘅回覆中見到呢條信息,即係話你嘅文件已經被截斷咗。

遇到截斷點算?兩條路:一係精簡內容,將唔常用嘅部分搬去子目錄 Context File;二係用 @file:path 動態引用,等 Agent 有需要嗰陣先讀取。唔好諗住逼到 20K 上限——嗰條係硬牆,唔係目標線。

3. 漸進式子目錄發現:按需加載,唔浪費 Token

大項目點算?一個根目錄嘅 AGENTS.md 冇可能裝得曬所有子項目嘅約定。Hermes 嘅解法係漸進式子目錄發現

源碼位置:agent/subdirectory_hints.py

子目錄發現機制圖:Monorepo 文件樹與漸進發現
子目錄發現機制圖:Monorepo 文件樹與漸進發現

圖 3:Monorepo 項目中的漸進式子目錄發現,Agent 首次訪問時自動加載對應 Context File

工作原理

SubdirectoryHintTracker 類監聽 Agent 嘅所有工具調用(read_fileterminalsearch_files 等),從工具參數中提取文件路徑,然後檢查呢個路徑所在嘅目錄同佢嘅父目錄(最多向上 5 層)有冇 Context File。

class SubdirectoryHintTracker:
    """Track which directories the agent visits
       and load hints on first access."""


    def __init__(self, working_dir=None):
        self.working_dir = Path(working_dir or os.getcwd()).resolve()
        self._loaded_dirs: Set[Path] = set()
        self._loaded_dirs.add(self.working_dir)  # CWD 已在啓動時加載

幾個關鍵設計決策:

  • 每個目錄只檢查一次_loaded_dirs 集合去重),避免重複加載
  • 只掃描工作目錄樹內嘅路徑,拒絕工作目錄外嘅發現,防止跨 Agent 污染
  • 向上遍歷唔超過 5 層父目錄(_MAX_ANCESTOR_WALK = 5

子目錄與根目錄嘅分別

維度
根目錄(啓動時加載)
子目錄(運行時發現)
字符上限
20,000
8,000
注入方式
系統提示詞
追加到工具調用結果
文件選擇
First Match Wins
所有找到嘅都加載
對 caching 影響
有(修改觸發重建)
無(唔改系統提示詞)

子目錄文件限制係 8,000 字符——比根目錄嘅 20,000 嚴格好多。而且注入方式都唔同:子目錄 Context File 唔係塞入系統提示詞,而係追加到工具調用嘅返回結果裏面。咁樣設計係為咗保護 prompt caching——系統提示詞唔變,緩存就唔會失效。

另一個關鍵區別:子目錄裏面揾到嘅所有文件都會被加載,唔行 First Match Wins。因為唔同子目錄可能用緊唔同嘅技術棧同約定,例如前端用 TypeScript 規範,後端用 Python 規範,佢哋唔會互相排斥。

一個 Monorepo 嘅文件樹示例

my-project/
├── AGENTS.md              ← 啓動時加載(系統提示詞)
├── .hermes.md             ← 如果存在,優先級高於 AGENTS.md
├── frontend/
│   ├── AGENTS.md          ← Agent 訪問 frontend/ 文件時漸進發現
│   └── src/
│       └── components/
│           └── Button.tsx  ← 讀取此文件會觸發 frontend/AGENTS.md 發現
├── backend/
│   ├── AGENTS.md          ← Agent 訪問 backend/ 文件時漸進發現
│   └── src/
│       └── main.py        ← 讀取此文件會觸發 backend/AGENTS.md 發現
└── shared/
    └── AGENTS.md          ← Agent 訪問 shared/ 文件時漸進發現

當你叫 Agent 去改 frontend/src/components/Button.tsx 時,佢第一次訪問 frontend/ 目錄,SubdirectoryHintTracker 就會檢測到並加載 frontend/AGENTS.md。之後對 frontend/ 下面嘅操作都會跟足前端嘅約定。

同競品對比:點解話 Hermes 做得更完備

從源碼層面嚟睇,Hermes 喺上下文文件機制上至少有咗三個設計係 Claude Code 同 Cursor 冇嘅:

特性
Claude Code
Cursor
Hermes Agent
優先級策略
拼接(後加載者優先)
按文件類型排序
First Match Wins
(避免衝突)
子目錄發現
按需加載子目錄 CLAUDE.md
路徑 glob 匹配
監聽工具調用觸發
(更動態)
人格定製
冇獨立機制
SOUL.md
(專屬 Slot)
單文件上限
冇硬限制(社羣建議 <80 行)
冇硬限制
20,000 字符硬限制
安全掃描
Prompt injection 檢測
動態引用
無原生
@file / @folder / @diff

Claude Code 嘅策略係全拼接——~/.claude/CLAUDE.md + 項目 .claude/CLAUDE.md + 本地 CLAUDE.local.md,所有文件疊加埋一齊。好處係唔怕遺漏,壞處係當文件之間有衝突嗰陣,行為好難預測。Hermes 揀 First Match Wins 更進取,但邏輯更清晰:你一眼就睇到目前生效緊邊個文件。

Cursor 嘅 .cursor/rules/*.mdc 通過 frontmatter 嘅 paths glob 字段嚟匹配文件路徑,都算係一種條件加載,但唔及 Hermes 嘅運行時路徑監聽咁靈活——glob 要你預先定義曬所有路徑模式,而 Hermes 係喺 Agent 實際訪問嗰陣動態觸發。

4. 寫好 Context Files 嘅四條鐵律

理論夠曬啦,下面係實作部分。結合源碼機制同社羣共識,提煉出四條編寫原則。

保持精簡:離 20K 上限越遠越好

20,000 字符係硬限制,但呢個唔代表你要填滿佢。啱啱相反。

ETH Zurich 嘅研究顯示,過長嘅上下文文件會令 Agent 變得過度順從——佢花太多 token 去遵守各種指令,反而忽略咗你真正叫佢做嘅嘢。推理成本增加 20% 以上,任務成功率卻冇提升。

社羣嘅經驗數據係:根目錄 AGENTS.md 控制在 5,000-10,000 字符,子目錄文件控制在 3,000-5,000 字符。咁樣先畀截斷留夠緩衝區,亦保證 Agent 嘅注意力集中。

一條實用嘅檢驗標準:如果刪走某一行,Agent 嘅行為唔會變,咁呢行就唔應該存在。

結構化排版:標題係 Agent 嘅導航路標

模型處理長文本嗰陣,標題係召回嘅關鍵錨點。一段 3,000 字嘅無結構文本,同一段用 ## 分成 6 個區塊嘅 3,000 字文本,Agent 嘅信息定位效率差距好大。標題就係 Agent 嘅導航路標。

推薦嘅結構分區:

## Build & Test
構建和測試命令

## Architecture
項目架構概述

## Code Style
代碼風格約定

## What NOT to do
禁止事項

每個區塊下面放 3-7 條具體規則。唔好喺一個區塊裏面堆 20 條——人類嘅短期記憶容量係 7±2,模型雖然大啲,但也冇大到可以無視信息密度嘅程度。

具體化示例:寫命令,唔好寫描述

對比兩行寫法:

❌ 使用合適的包管理器運行測試
✅ pnpm test(不要用 npm 或 yarn)

第二行有三個優勢:Agent 知道用咩命令、知道唔應該用咩命令、冇歧義。

❌ API 路由使用一致的命名風格
✅ API 路由命名使用 kebab-case:/api/user-profile、/api/order-list

具體嘅示例比抽象嘅描述有效好多。呢一條喺 awesome-claude-md(GitHub/jnMetaCode)嘅社羣實踐中被反覆強調。

明確負面清單:寫禁止做啲乜

呢個係社羣公認最有價值嘅內容。

## What NOT to do

禁止直接修改數據庫遷移文件,新建遷移文件
禁止使用 inline styles
禁止在組件中直接調用 API,統一走 hooks
禁止使用 `any` 類型

點解要寫負面清單?因為 Agent 踩嘅坑同你踩嘅坑唔一樣。佢唔會猶豫,佢只會自信咁揀錯。將以前踩過嘅坑、新人成日犯嘅錯、AI 容易搞亂嘅地方,清楚寫成禁止事項,比任何正面指導都有效。

常見反模式:照住呢啲改就得

社羣裏面反覆出現嘅幾種錯誤寫法,整理咗一份清單:

反模式 1:太模糊

❌ 請寫高質量的代碼,注意性能和可讀性。

呢句話對 Agent 嚟講等於冇講。乜嘢叫高質量?咩標準算可讀?模型自己有一套判斷邏輯,同你想嘅唔一樣。改成具體嘅規則先有效。

反模式 2:好似教程咁解釋基礎知識

❌ ## 什麼是 React?
React 是一個用於構建用戶界面的 JavaScript 庫...

Agent 唔需要你教佢 React 係乜。佢需要嘅係你項目中特有嘅約定:組件點樣組織、狀態用邊個庫、路由點樣改名。呢啲係佢從通用知識推斷唔出嚟嘅。

反模式 3:多個上下文文件互相矛盾

# CLAUDE.md 寫
always use semicolons

# AGENTS.md 寫
never use semicolons

前面講過,同一個目錄下只有 First Match Wins 嘅文件會被加載,所以呢種情況喺 Hermes 裏面唔會同時出現。但如果你喺唔同子目錄寫咗矛盾嘅規則,Agent 喺跨越子目錄操作嗰陣就會表現得唔一致。

5. 實戰配置:Full-Stack Monorepo 嘅 Context 矩陣

下面係四份可以直接複製用嘅配置文件源碼。場景係一個 Next.js + FastAPI + PostgreSQL 嘅 monorepo 項目。

根目錄 AGENTS.md

# Project: MySaaS

## Architecture
Monorepo: frontend/ (Next.js 15 + TypeScript) + backend/ (FastAPI + Python 3.12)
Database: PostgreSQL 16, Alembic 管理遷移
Monorepo 工具: pnpm workspaces + turborepo

## Build & Test
全量構建: `pnpm build`
全量測試: `pnpm test`
後端測試: `cd backend && pytest`
前端測試: `cd frontend && pnpm test`
Lint: `pnpm lint`

## What NOT to do
不要混合使用 npm 和 pnpm
不要在根目錄放置子項目特定的配置
不要跳過 Alembic 遷移直接修改數據庫 schema
不要在根 AGENTS.md 中重複子項目已有的規則

前端 /frontend/AGENTS.md

# Frontend: Next.js 15 App Router

## Tech Stack
框架: Next.js 15 (App Router)
狀態: Zustand
樣式: Tailwind CSS v4
測試: Vitest + React Testing Library

## Code Style
組件文件名: PascalCase(如 `Button.tsx`
工具函數文件名: camelCase(如 `formatDate.ts`
頁面路由: `app/` 目錄下的 `page.tsx`
API 調用統一走 `hooks/useApi.ts`,不要在組件中直接 fetch

## Testing
測試文件放在 `__tests__/` 下,命名 `Component.test.tsx`
運行: `pnpm test`
覆蓋率門檻: 80%

## What NOT to do
不要使用 inline styles
不要使用 `any` 類型
不要在客戶端組件中直接訪問數據庫

後端 /backend/AGENTS.md

# Backend: FastAPI + Python 3.12

## Tech Stack
框架: FastAPI
ORM: SQLAlchemy 2.0 (async)
遷移: Alembic
測試: pytest + pytest-asyncio

## Code Style
API 路由命名: kebab-case(如 `/api/user-profile`
模型文件放在 `models/`,Schema 文件放在 `schemas/`
依賴注入統一用 `Depends()`
異步函數命名: `async def get_user()`,不是 `async def getUser()`

## Testing
運行: `pytest`
測試文件命名: `test_*.py`
測試數據庫: 使用 SQLite in-memory,不要連接真實 PostgreSQL

## What NOT to do
不要在路由函數中寫業務邏輯,統一走 Service 層
不要使用同步 ORM 調用,全部用 async
不要硬編碼數據庫連接字符串,從環境變量讀取

全局 ~/.hermes/SOUL.md

# Personality

你是一個工程潔癖型開發者。回答問題時遵循以下原則:

不確定的事情,明確說"我不確定",不要猜
發現代碼有明顯的 bad practice,指出來並給出原因
給建議時,先說為什麼,再說怎麼做
回覆使用中文

注意 SOUL.md 只放人格層面嘅指令,唔好放任何項目特定嘅技術規範。換一個項目,呢份 SOUL.md 唔使改。

你嘅項目中有冇用類似方案管理 Agent 上下文?歡迎喺評論區傾嚇實際效果。

6. 高級調優:動態引用與安全防線

Context References:用 @ 引用代替硬編碼

Hermes 提供咗一套 @ 動態引用語法,可以按需注入文件內容,而唔係將所有嘢塞入 AGENTS.md

支援嘅引用語法:

語法
用途
@file:path
引用文件內容(支援行範圍 :10-50
@folder:path
引用目錄結構
@diff
引用當前 git diff
@staged
引用 staged changes
@url:URL
引用 URL 內容
@git:ref
引用 git 對象

源碼位置:agent/context_references.py

好處好明顯:唔需要喺 AGENTS.md 裏面維護一份完整嘅 API 文檔或架構說明。當你需要 Agent 參考某個文件嗰陣,用 @file:docs/api-spec.md 動態引用,只有需要嗰陣先注入,唔會撐爆系統提示詞嘅 token 預算。

安全限制方面,動態引用有敏感目錄攔截~/.ssh/~/.aws/~/.gnupg/~/.kube/~/.docker/~/.azure/~/.config/gh/ 呢啲目錄下嘅文件唔允許通過 @ 引用注入。

Token 預算都有約束:注入總量唔超過 context_length 嘅 **50%**(硬限制),超過 25% 時會觸發軟限制警告。

舉個例:如果你嘅模型上下文長度係 128K tokens,咁動態引用嘅注入總量唔可以超過 64K tokens。呢個約束確保咗你唔會一次過將成個代碼庫塞入上下文窗口,搞到模型嘅注意力資源用曬。

Prompt Injection 防禦:唔係得個樣

安全防線示意圖:內容掃描管線與文件讀寫安全區
安全防線示意圖:內容掃描管線與文件讀寫安全區

圖 4:Hermes Agent 安全防線:左邊內容掃描管線 + 右邊文件讀寫安全區

Hermes 係目前少數對 Context Files 內置安全掃描嘅 Agent 框架。

源碼位置:agent/prompt_builder.py 第 43-58 行

def _scan_context_content(content: str, filename: str) -> str:
    findings = _scan_for_threats(content, scope="context")
    if findings:
        return (f"[BLOCKED: {filename} contained potential "
                f"prompt injection ({', '.join(findings)}). "
                f"Content not loaded.]")
    return content

佢會掃描以下模式:

  • 指令覆蓋嘗試:ignore previous instructionsdisregard your rules
  • 系統提示詞覆蓋:system prompt override
  • 憑證外洩嘗試:curl ... $API_KEY
  • 敏感文件訪問:cat .envcat credentials
  • 隱藏內容:HTML 隱藏註釋、零寬空格、雙向覆蓋字符

命中任何一條,成個文件內容會被換成 [BLOCKED: ...],Agent 只能睇到被攔截嘅提示,睇唔到原始內容。

呢度用嘅係 context scope 而唔係 strict scope。strict 模式更進取,會攔截 SSH 後門、持久化、exfil-URL 等模式,但對於 clone 倉庫中嘅上下文文件嚟講太容易誤報——你唔知上游倉庫嘅 AGENTS.md 裏面寫咗啲乜。

文件讀寫安全防線

源碼位置:agent/file_safety.py

寫入拒絕路徑(精確匹配):

  • ~/.ssh/authorized_keys~/.ssh/id_rsa~/.ssh/id_ed25519
  • $HERMES_HOME/.env$HERMES_ROOT/.env
  • ~/.bashrc~/.zshrc~/.profile
  • ~/.netrc~/.pgpass~/.git-credentials
  • /etc/sudoers/etc/passwd/etc/shadow

寫入拒絕目錄前綴(前綴匹配):

  • ~/.ssh/~/.aws/~/.gnupg/~/.kube/
  • /etc/sudoers.d//etc/systemd/
  • ~/.docker/~/.azure/~/.config/gh/~/.config/gcloud/

讀取拒絕規則:Hermes 內部緩存(skills/.hub/)、憑證存儲(auth.json.envwebhook_subscriptions.json)、MCP token 文件(mcp-tokens/)、項目環境文件(.env.env.local.env.production)。

不過源碼裏面有句大實話:呢個唔係安全邊界——terminal 工具仍然可以 cat 呢啲文件。呢個係縱深防禦,唔係銀彈。

總結

整理嚇核心要點:

  1. First Match Wins:同一個目錄下只有一個項目上下文文件會被加載。如果你同時放咗 .hermes.md 和 AGENTS.md,後者會被完全忽略
  2. 20,000 字符硬限制:超出嗰陣 70% 頭部 + 20% 尾部保留,中間 10% 被截斷。實際建議控制在 5,000-10,000 字符
  3. 漸進式子目錄發現:子目錄 Context File 上限 8,000 字符,注入到工具結果而唔係系統提示詞,保護 prompt caching
  4. SOUL.md 獨立:行專屬 Slot,放人格指令,唔好放項目規範
  5. 安全掃描唔係得個樣:prompt injection 檢測 + 敏感路徑拒絕,提供縱深防禦

講真,從源碼嚟睇,Hermes 嘅上下文文件機制係目前同類 Agent 框架裏面做得比較完備嘅。兼容 4 種主流格式、有漸進式發現、有 token 預算控制、有安全掃描——Claude Code 同 Cursor 呢方面都仲有差距。

但工具再好,用唔啱都係曬氣。ETH Zurich 嘅研究已經證明咗:上下文文件唔係越多越好,而係越精準越好。只放唔可以推斷嘅項目特定信息,將通用知識留返畀模型自身——呢個先係正確嘅打開方式。

好啦,多謝你睇我嘅文章,如果鍾意可以點讚轉發畀需要嘅朋友,我哋下一期再見!敬請期待!

🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 126 篇,Hermes Agent  最佳實戰「2026」系列第 7 篇

大家好,歡迎來到 術哥無界 | ShugeX | 運維有術

我是術哥,一名專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫的技術實踐者與開源佈道者

Talk is cheap, let's explore。無界探索,有術而行。

封面圖:Hermes Agent Context Files 架構總覽
封面圖:Hermes Agent Context Files 架構總覽

你花了一下午精心寫了一份 AGENTS.md,列了幾十條規範,涵蓋代碼風格、構建命令、測試策略。結果 Agent 的表現跟沒讀過一樣——指令被無視,命名風格混亂,構建命令也用錯了。

翻了一圈源碼才發現問題:項目根目錄下同時存在 .hermes.md 和 AGENTS.md,而 Hermes 的優先級機制是 First Match Wins——你的 AGENTS.md 根本沒被加載。

這不是個例。ETH Zurich 2026 年的一份研究指出,自動生成的上下文文件反而會降低 Agent 任務成功率,人工編寫的也只提升了 4%。問題不在於寫不寫,而在於寫什麼、寫多少、放在哪

今天這篇文章,就從 Hermes Agent v0.15.1 的源碼出發,把 Context Files 的加載機制、優先級系統、截斷策略、安全防線徹底講清楚。

說明:本文內容基於 Hermes Agent 源碼(nousresearch/hermes-agent)和 v0.15.1 官方文檔分析整理而成,源碼分析基於筆者本地倉庫版本。文中的配置模板和參數建議僅供參考,實際效果請以你的業務數據和環境測試結果為準。如果有實際使用經驗,歡迎在評論區分享交流。

1. Context Files 家族:5 種文件,4 層優先級

Hermes Agent 支持的上下文文件一共有 5 種,覆蓋了市面上主流 AI Agent 的配置格式。

文件類型
用途
發現路徑
優先級
.hermes.md
 / HERMES.md
項目級最高優先指令
從 CWD 向上遍歷至 Git 根目錄
P0
AGENTS.md
 / agents.md
核心項目規範與約定
CWD(啓動時)+ 子目錄漸進發現
P1
CLAUDE.md
 / claude.md
兼容 Claude Code 規範
CWD + 子目錄漸進發現
P2
.cursorrules
兼容 Cursor IDE 規則
僅 CWD
P3
SOUL.md
全局人格與語氣定製
僅 ~/.hermes/ 目錄
獨立 Slot
優先級層級圖:4 級優先級棧與 SOUL.md 獨立 Slot
優先級層級圖:4 級優先級棧與 SOUL.md 獨立 Slot

圖 1:Context Files 優先級層級,P0-P3 共享項目上下文槽位,SOUL.md 走獨立 Slot

這裏有個容易踩的坑:前 4 種文件共享同一個項目上下文槽位,同一個目錄下只有一個會被加載。SOUL.md 則完全獨立,它有自己的專屬 Slot,不受項目文件的影響。

First Match Wins:高優先級文件會遮蔽低優先級文件

源碼位置:agent/prompt_builder.py 第 1468-1496 行

project_context = (
    _load_hermes_md(cwd_path)      # 優先級 1:.hermes.md / HERMES.md
    or _load_agents_md(cwd_path)    # 優先級 2:AGENTS.md / agents.md
    or _load_claude_md(cwd_path)    # 優先級 3:CLAUDE.md / claude.md
    or _load_cursorrules(cwd_path)  # 優先級 4:.cursorrules
)

這段 Python 的短路求值(short-circuit evaluation)決定了加載邏輯:從高優先級往下依次嘗試,第一個成功讀取到內容的文件,直接作為整個項目上下文,後面的全部跳過。

也就是說,如果你的項目根目錄同時有 .hermes.md 和 AGENTS.md,只有 .hermes.md 的內容會被注入系統提示詞。AGENTS.md 裏的所有規範,Agent 一個字都看不到。

還有一點值得注意:.hermes.md 會從當前工作目錄(CWD)向上遍歷到 Git 根目錄來尋找,而 AGENTS.md 和 CLAUDE.md 只從 CWD 讀取,不會向上搜索。

SOUL.md 的特殊地位

SOUL.md 走的是完全獨立的加載路徑。源碼在 agent/prompt_builder.py 第 1355-1380 行:

def load_soul_md() -> Optional[str]:
    """Load SOUL.md from HERMES_HOME and return its content, or None.
    Used as the agent identity (slot #1 in the system prompt).
    """

    soul_path = get_hermes_home() / "SOUL.md"

幾個關鍵特性:

  • 只從 HERMES_HOME(默認 ~/.hermes/)加載,不受項目目錄影響
  • 作為 Agent 身份的 Slot #1 注入,排在項目上下文之前
  • 文件為空時不注入任何內容
  • Hermes 首次運行時會自動生成一份默認 SOUL.md

SOUL.md 適合放什麼?全局人格特質,比如你希望 Agent 以什麼風格回覆、用中文還是英文、偏保守還是激進。技術規範和項目約定不要放這裏,那屬於 AGENTS.md 的職責。

還有一個小細節容易被忽略:SOUL.md 和項目上下文文件一樣,也要經過安全掃描和截斷處理。如果 ~/.hermes/SOUL.md 超過 20,000 字符,一樣會被 head/tail 截斷。不過對於人格指令來說,這個限制基本不可能觸及。

2. 完整加載鏈路:從磁盤到系統提示詞

瞭解文件類型之後,來看一份 Context File 從被讀取到注入系統提示詞,中間到底經歷了什麼。

加載管線流程圖:8 步從磁盤到系統提示詞
加載管線流程圖:8 步從磁盤到系統提示詞

圖 2:Context File 加載管線,8 步從掃描到注入,第 6 步截斷內嵌 70%/20% 比例

源碼位置:agent/prompt_builder.py 第 1468-1507 行

掃描(Scan)
  → 發現(Discover):按優先級檢查文件存在性
  → 讀取(Read):UTF-8 編碼讀取文件內容
  → 預處理(Preprocess):去除 YAML frontmatter(.hermes.md 支持)
  → 安全掃描(Security Scan):檢測 prompt injection
  → 截斷(Truncate):超過 20,000 字符時執行 head/tail 截斷
  → 組裝(Assemble):合併到 # Project Context 標題下
  → 注入(Inject):添加到系統提示詞

每一步都有講究。安全掃描那一步尤其關鍵——它不只是走個過場,後面會展開說。

有個細節值得留意:預處理階段只對 .hermes.md 支持去除 YAML frontmatter。如果你在 AGENTS.md 里加了 frontmatter,它會原樣注入系統提示詞,白白浪費幾百個字符的額度。

20,000 字符硬限制與 70%/20% 截斷

這是整條鏈路裏容易讓人栽跟頭的地方。

源碼位置:agent/prompt_builder.py 第 875-877 行,定義了三個常量:

CONTEXT_FILE_MAX_CHARS = 20_000          # 單文件上限 20,000 字符
CONTEXT_TRUNCATE_HEAD_RATIO = 0.7        # 頭部保留 70% = 14,000 字符
CONTEXT_TRUNCATE_TAIL_RATIO = 0.2        # 尾部保留 20% = 4,000 字符
# 中間 10% = 2,000 字符留給截斷標記

截斷函數 _truncate_content() 的實現(第 1343-1352 行):

def _truncate_content(content: str, filename: str,
                      max_chars: int = CONTEXT_FILE_MAX_CHARS)
 -> str:

    if len(content) <= max_chars:
        return content
    head_chars = int(max_chars * CONTEXT_TRUNCATE_HEAD_RATIO)  # 14,000
    tail_chars = int(max_chars * CONTEXT_TRUNCATE_TAIL_RATIO)  # 4,000
    head = content[:head_chars]
    tail = content[-tail_chars:]
    marker = (f"\n\n[...truncated {filename}: kept "
              f"{head_chars}+{tail_chars} of {len(content)} chars. "
              f"Use file tools to read the full file.]\n\n")
    return head + marker + tail

翻譯一下:如果你的文件超過 20,000 字符,系統會保留前 14,000 和後 4,000 字符,中間的內容直接丟掉,替換成一條截斷提示。

這意味着什麼?假設你把最重要的約定放在了文件中間偏後的位置,正好落在截斷區間裏——Agent 永遠看不到那些內容。

20,000 字符大約等於 7,000 tokens。看起來不少,但如果你在裏面塞了大段代碼示例、目錄結構、API 文檔,很快就填滿了。這也是為什麼社區的最佳實踐建議控制在 5,000-10,000 字符 以內,給安全截斷留足餘量。

一個實戰中的調試技巧:如果你懷疑 Agent 沒看到某條指令,檢查一下截斷標記。Hermes 在截斷時會插入一條提示,格式類似 [...truncated AGENTS.md: kept 14000+4000 of 25000 chars...]。如果你在 Agent 的回覆中看到了這條信息,說明你的文件已經被截斷了。

遇到截斷怎麼辦?兩條路:要麼精簡內容,把不常用的部分移到子目錄 Context File 裏;要麼用 @file:path 動態引用,讓 Agent 在需要時才讀取。不要想着往 20K 上限逼近——那是一條硬牆,不是目標線。

3. 漸進式子目錄發現:按需加載,不浪費 Token

大項目怎麼辦?一個根目錄的 AGENTS.md 不可能裝下所有子項目的約定。Hermes 的解法是漸進式子目錄發現

源碼位置:agent/subdirectory_hints.py

子目錄發現機制圖:Monorepo 文件樹與漸進發現
子目錄發現機制圖:Monorepo 文件樹與漸進發現

圖 3:Monorepo 項目中的漸進式子目錄發現,Agent 首次訪問時自動加載對應 Context File

工作原理

SubdirectoryHintTracker 類監聽 Agent 的所有工具調用(read_fileterminalsearch_files 等),從工具參數中提取文件路徑,然後檢查這個路徑所在的目錄及其父目錄(最多向上 5 層)有沒有 Context File。

class SubdirectoryHintTracker:
    """Track which directories the agent visits
       and load hints on first access."""


    def __init__(self, working_dir=None):
        self.working_dir = Path(working_dir or os.getcwd()).resolve()
        self._loaded_dirs: Set[Path] = set()
        self._loaded_dirs.add(self.working_dir)  # CWD 已在啓動時加載

幾個關鍵設計決策:

  • 每個目錄只檢查一次_loaded_dirs 集合去重),避免重複加載
  • 只掃描工作目錄樹內的路徑,拒絕工作目錄外的發現,防止跨 Agent 污染
  • 向上遍歷不超過 5 層父目錄(_MAX_ANCESTOR_WALK = 5

子目錄與根目錄的差異

維度
根目錄(啓動時加載)
子目錄(運行時發現)
字符上限
20,000
8,000
注入方式
系統提示詞
追加到工具調用結果
文件選擇
First Match Wins
所有找到的都加載
對 caching 影響
有(修改觸發重建)
無(不修改系統提示詞)

子目錄文件限制為 8,000 字符——比根目錄的 20,000 嚴格得多。而且注入方式也不同:子目錄 Context File 不是塞進系統提示詞,而是追加到工具調用的返回結果裏。這樣設計是為了保護 prompt caching——系統提示詞不變,緩存就不會失效。

另一個關鍵區別:子目錄裏找到的所有文件都會被加載,不走 First Match Wins。因為不同子目錄可能使用不同的技術棧和約定,比如前端用 TypeScript 規範,後端用 Python 規範,它們不會互相排斥。

一個 Monorepo 的文件樹示例

my-project/
├── AGENTS.md              ← 啓動時加載(系統提示詞)
├── .hermes.md             ← 如果存在,優先級高於 AGENTS.md
├── frontend/
│   ├── AGENTS.md          ← Agent 訪問 frontend/ 文件時漸進發現
│   └── src/
│       └── components/
│           └── Button.tsx  ← 讀取此文件會觸發 frontend/AGENTS.md 發現
├── backend/
│   ├── AGENTS.md          ← Agent 訪問 backend/ 文件時漸進發現
│   └── src/
│       └── main.py        ← 讀取此文件會觸發 backend/AGENTS.md 發現
└── shared/
    └── AGENTS.md          ← Agent 訪問 shared/ 文件時漸進發現

當你讓 Agent 去改 frontend/src/components/Button.tsx 時,它第一次訪問 frontend/ 目錄,SubdirectoryHintTracker 就會檢測到並加載 frontend/AGENTS.md。之後對 frontend/ 下的操作都會遵循前端的約定。

和競品的對比:為什麼說 Hermes 做得更完備

從源碼層面來看,Hermes 在上下文文件機制上至少有三個設計是 Claude Code 和 Cursor 沒有的:

特性
Claude Code
Cursor
Hermes Agent
優先級策略
拼接(後加載者優先)
按文件類型排序
First Match Wins
(避免衝突)
子目錄發現
按需加載子目錄 CLAUDE.md
路徑 glob 匹配
監聽工具調用觸發
(更動態)
人格定製
無獨立機制
SOUL.md
(專屬 Slot)
單文件上限
無硬限制(社區建議 <80 行)
無硬限制
20,000 字符硬限制
安全掃描
Prompt injection 檢測
動態引用
無原生
@file / @folder / @diff

Claude Code 的策略是全拼接——~/.claude/CLAUDE.md + 項目 .claude/CLAUDE.md + 本地 CLAUDE.local.md,所有文件疊加在一起。好處是不怕遺漏,壞處是當文件之間有衝突時,行為不好預測。Hermes 選擇 First Match Wins 更激進,但邏輯更清晰:你一眼就能知道當前生效的是哪個文件。

Cursor 的 .cursor/rules/*.mdc 通過 frontmatter 的 paths glob 字段來匹配文件路徑,也算一種條件加載,但不如 Hermes 的運行時路徑監聽靈活——glob 需要你預定義所有路徑模式,而 Hermes 是在 Agent 實際訪問時動態觸發。

4. 寫好 Context Files 的四條鐵律

理論講夠了,下面是實操部分。結合源碼機制和社區共識,提煉出四條編寫原則。

保持精簡:離 20K 上限越遠越好

20,000 字符是硬限制,但這不意味着你要把它填滿。恰恰相反。

ETH Zurich 的研究表明,過長的上下文文件會讓 Agent 變得過度順從——它花太多 token 去遵守各種指令,反而忽略了你真正讓它做的事情。推理成本增加 20% 以上,任務成功率卻沒有提升。

社區的經驗數據是:根目錄 AGENTS.md 控制在 5,000-10,000 字符,子目錄文件控制在 3,000-5,000 字符。這給截斷留了足夠的緩衝區,也保證了 Agent 的注意力集中。

一條實用的檢驗標準:如果刪掉某一行,Agent 的行為不會發生變化,那這一行就不該存在。

結構化排版:標題是 Agent 的導航路標

模型在處理長文本時,標題是召回的關鍵錨點。一段 3,000 字的無結構文本,和一段用 ## 分成 6 個區塊的 3,000 字文本,Agent 的信息定位效率差距很大。標題就是 Agent 的導航路標。

推薦的結構分區:

## Build & Test
構建和測試命令

## Architecture
項目架構概述

## Code Style
代碼風格約定

## What NOT to do
禁止事項

每個區塊下面放 3-7 條具體規則。不要在一個區塊裏堆 20 條——人類的短期記憶容量是 7±2,模型雖然更大,但也沒大到可以無視信息密度的程度。

具體化示例:寫命令,不寫描述

對比兩行寫法:

❌ 使用合適的包管理器運行測試
✅ pnpm test(不要用 npm 或 yarn)

第二行有三個優勢:Agent 知道用什麼命令、知道不該用什麼命令、沒有歧義。

❌ API 路由使用一致的命名風格
✅ API 路由命名使用 kebab-case:/api/user-profile、/api/order-list

具體的示例比抽象的描述有效得多。這一條在 awesome-claude-md(GitHub/jnMetaCode)的社區實踐中被反覆強調。

明確負面清單:寫禁止做什麼

這是社區公認最有價值的內容。

## What NOT to do

禁止直接修改數據庫遷移文件,新建遷移文件
禁止使用 inline styles
禁止在組件中直接調用 API,統一走 hooks
禁止使用 `any` 類型

為什麼要寫負面清單?因為 Agent 踩的坑和你踩的坑不一樣。它不會猶豫,它只會自信地做出錯誤選擇。把曾經踩過的坑、新人常犯的錯、AI 容易搞混的地方,明確寫成禁止事項,比任何正面指導都有效。

常見反模式:照着這些改就行

社區裏反覆出現的幾種錯誤寫法,整理了一份清單:

反模式 1:太模糊

❌ 請寫高質量的代碼,注意性能和可讀性。

這句話對 Agent 來說等於沒說。什麼叫高質量?什麼標準算可讀?模型自己有一套判斷邏輯,和你想的不一樣。改成具體的規則才有效。

反模式 2:像教程一樣解釋基礎知識

❌ ## 什麼是 React?
React 是一個用於構建用戶界面的 JavaScript 庫...

Agent 不需要你教它 React 是什麼。它需要的是你項目中特有的約定:組件怎麼組織、狀態用什麼庫、路由怎麼命名。這些是它從通用知識裏推斷不出來的。

反模式 3:多個上下文文件互相矛盾

# CLAUDE.md 寫
always use semicolons

# AGENTS.md 寫
never use semicolons

前面說過,同一個目錄下只有 First Match Wins 的文件會被加載,所以這種情況在 Hermes 裏不會同時出現。但如果你在不同子目錄寫了矛盾的規則,Agent 在跨越子目錄操作時就會表現不一致。

5. 實戰配置:Full-Stack Monorepo 的 Context 矩陣

下面是四份可以直接複製使用的配置文件源碼。場景是一個 Next.js + FastAPI + PostgreSQL 的 monorepo 項目。

根目錄 AGENTS.md

# Project: MySaaS

## Architecture
Monorepo: frontend/ (Next.js 15 + TypeScript) + backend/ (FastAPI + Python 3.12)
Database: PostgreSQL 16, Alembic 管理遷移
Monorepo 工具: pnpm workspaces + turborepo

## Build & Test
全量構建: `pnpm build`
全量測試: `pnpm test`
後端測試: `cd backend && pytest`
前端測試: `cd frontend && pnpm test`
Lint: `pnpm lint`

## What NOT to do
不要混合使用 npm 和 pnpm
不要在根目錄放置子項目特定的配置
不要跳過 Alembic 遷移直接修改數據庫 schema
不要在根 AGENTS.md 中重複子項目已有的規則

前端 /frontend/AGENTS.md

# Frontend: Next.js 15 App Router

## Tech Stack
框架: Next.js 15 (App Router)
狀態: Zustand
樣式: Tailwind CSS v4
測試: Vitest + React Testing Library

## Code Style
組件文件名: PascalCase(如 `Button.tsx`
工具函數文件名: camelCase(如 `formatDate.ts`
頁面路由: `app/` 目錄下的 `page.tsx`
API 調用統一走 `hooks/useApi.ts`,不要在組件中直接 fetch

## Testing
測試文件放在 `__tests__/` 下,命名 `Component.test.tsx`
運行: `pnpm test`
覆蓋率門檻: 80%

## What NOT to do
不要使用 inline styles
不要使用 `any` 類型
不要在客戶端組件中直接訪問數據庫

後端 /backend/AGENTS.md

# Backend: FastAPI + Python 3.12

## Tech Stack
框架: FastAPI
ORM: SQLAlchemy 2.0 (async)
遷移: Alembic
測試: pytest + pytest-asyncio

## Code Style
API 路由命名: kebab-case(如 `/api/user-profile`
模型文件放在 `models/`,Schema 文件放在 `schemas/`
依賴注入統一用 `Depends()`
異步函數命名: `async def get_user()`,不是 `async def getUser()`

## Testing
運行: `pytest`
測試文件命名: `test_*.py`
測試數據庫: 使用 SQLite in-memory,不要連接真實 PostgreSQL

## What NOT to do
不要在路由函數中寫業務邏輯,統一走 Service 層
不要使用同步 ORM 調用,全部用 async
不要硬編碼數據庫連接字符串,從環境變量讀取

全局 ~/.hermes/SOUL.md

# Personality

你是一個工程潔癖型開發者。回答問題時遵循以下原則:

不確定的事情,明確說"我不確定",不要猜
發現代碼有明顯的 bad practice,指出來並給出原因
給建議時,先說為什麼,再說怎麼做
回覆使用中文

注意 SOUL.md 只放人格層面的指令,不放任何項目特定的技術規範。換一個項目,這份 SOUL.md 不需要改。

你的項目中有用類似方案管理 Agent 上下文嗎?歡迎在評論區聊聊實際效果。

6. 高級調優:動態引用與安全防線

Context References:用 @ 引用代替硬編碼

Hermes 提供了一套 @ 動態引用語法,可以按需注入文件內容,而不是把所有東西都塞進 AGENTS.md

支持的引用語法:

語法
用途
@file:path
引用文件內容(支持行範圍 :10-50
@folder:path
引用目錄結構
@diff
引用當前 git diff
@staged
引用 staged changes
@url:URL
引用 URL 內容
@git:ref
引用 git 對象

源碼位置:agent/context_references.py

好處很明顯:不需要在 AGENTS.md 裏維護一份完整的 API 文檔或架構說明。當你需要 Agent 參考某個文件時,用 @file:docs/api-spec.md 動態引用,只在需要的時候才注入,不會撐爆系統提示詞的 token 預算。

安全限制方面,動態引用有敏感目錄攔截~/.ssh/~/.aws/~/.gnupg/~/.kube/~/.docker/~/.azure/~/.config/gh/ 這些目錄下的文件不允許通過 @ 引用注入。

Token 預算也有約束:注入總量不超過 context_length 的 **50%**(硬限制),超過 25% 時會觸發軟限制警告。

舉個例子:如果你的模型上下文長度是 128K tokens,那麼動態引用的注入總量不能超過 64K tokens。這個約束確保了你不會一次性把整個代碼庫都塞進上下文窗口,把模型的注意力資源耗盡。

Prompt Injection 防禦:不是擺設

安全防線示意圖:內容掃描管線與文件讀寫安全區
安全防線示意圖:內容掃描管線與文件讀寫安全區

圖 4:Hermes Agent 安全防線:左側內容掃描管線 + 右側文件讀寫安全區

Hermes 是目前少數對 Context Files 內置安全掃描的 Agent 框架。

源碼位置:agent/prompt_builder.py 第 43-58 行

def _scan_context_content(content: str, filename: str) -> str:
    findings = _scan_for_threats(content, scope="context")
    if findings:
        return (f"[BLOCKED: {filename} contained potential "
                f"prompt injection ({', '.join(findings)}). "
                f"Content not loaded.]")
    return content

它會掃描以下模式:

  • 指令覆蓋嘗試:ignore previous instructionsdisregard your rules
  • 系統提示詞覆蓋:system prompt override
  • 憑證外泄嘗試:curl ... $API_KEY
  • 敏感文件訪問:cat .envcat credentials
  • 隱藏內容:HTML 隱藏註釋、零寬空格、雙向覆蓋字符

命中任何一條,整個文件內容會被替換成 [BLOCKED: ...],Agent 只能看到被攔截的提示,看不到原始內容。

這裏用的是 context scope 而不是 strict scope。strict 模式更激進,會攔截 SSH 後門、持久化、exfil-URL 等模式,但對於克隆倉庫中的上下文文件來說太容易誤報了——你不知道上游倉庫的 AGENTS.md 裏寫了什麼。

文件讀寫安全防線

源碼位置:agent/file_safety.py

寫入拒絕路徑(精確匹配):

  • ~/.ssh/authorized_keys~/.ssh/id_rsa~/.ssh/id_ed25519
  • $HERMES_HOME/.env$HERMES_ROOT/.env
  • ~/.bashrc~/.zshrc~/.profile
  • ~/.netrc~/.pgpass~/.git-credentials
  • /etc/sudoers/etc/passwd/etc/shadow

寫入拒絕目錄前綴(前綴匹配):

  • ~/.ssh/~/.aws/~/.gnupg/~/.kube/
  • /etc/sudoers.d//etc/systemd/
  • ~/.docker/~/.azure/~/.config/gh/~/.config/gcloud/

讀取拒絕規則:Hermes 內部緩存(skills/.hub/)、憑證存儲(auth.json.envwebhook_subscriptions.json)、MCP token 文件(mcp-tokens/)、項目環境文件(.env.env.local.env.production)。

不過源碼裏有句大實話:這不是安全邊界——terminal 工具仍然可以 cat 這些文件。這是縱深防禦,不是銀彈。

總結

整理一下核心要點:

  1. First Match Wins:同一個目錄下只有一個項目上下文文件會被加載。如果你同時放了 .hermes.md 和 AGENTS.md,後者會被完全忽略
  2. 20,000 字符硬限制:超出時 70% 頭部 + 20% 尾部保留,中間 10% 被截斷。實際建議控制在 5,000-10,000 字符
  3. 漸進式子目錄發現:子目錄 Context File 上限 8,000 字符,注入到工具結果而非系統提示詞,保護 prompt caching
  4. SOUL.md 獨立:走專屬 Slot,放人格指令,不放項目規範
  5. 安全掃描不是擺設:prompt injection 檢測 + 敏感路徑拒絕,提供縱深防禦

說實話,從源碼來看,Hermes 的上下文文件機制是目前同類 Agent 框架裏做得比較完備的。兼容 4 種主流格式、有漸進式發現、有 token 預算控制、有安全掃描——Claude Code 和 Cursor 在這方面都還有差距。

但工具再好,用不對也白搭。ETH Zurich 的研究已經證明了:上下文文件不是越多越好,而是越精準越好。只放不可推斷的項目特定信息,把通用知識留給模型自身——這才是正確的打開方式。

好啦,謝謝你觀看我的文章,如果喜歡可以點贊轉發給需要的朋友,我們下一期再見!敬請期待!