Skip to content

6.7 系统提示词与 Prompt Templates

本页分析版本earendil-works/pi@c13ffe12026-07-30

本章解决什么问题:模型每一轮看到的那段「你是谁、你有什么工具、这个项目有什么规矩」的系统提示词(System Prompt),到底是由哪个函数、按什么顺序、从哪些文件拼出来的?--system-promptAGENTS.mdSYSTEM.md 各自作用在这条流水线的哪一段?以及:你在输入框里敲的 /cl/pr 这类斜杠命令,是模型的能力还是客户端的字符串替换? 前置知识3.2 消息、上下文与 token3.7 Compaction、Extension 与 Skill5.1 启动:pi 命令如何跑起来6.4 工具系统:定义、校验与执行学习目标:读完后你能 ① 背出 buildSystemPrompt 的八个段落及其顺序;② 说清三条自定义通道(CLI 参数 / SYSTEM.md / 扩展运行时覆盖)分别在哪一行接入;③ 解释 prompt template 为什么「模型完全不知道它存在」;④ 写出 $1$@${2:-默认值} 各自的替换规则;⑤ 指出 coding-agent 与 pi-agent-core 在提示词这件事上的分工差异。

建立直觉:系统提示词是一次纯粹的字符串拼接

📘 概念系统提示词(System Prompt)
每次请求都放在消息数组之外、单独传给模型的一段指令文本。它不属于对话历史,模型也无法「回忆」它是怎么来的——对模型来说它只是一段先于所有消息出现的文字。

初学者容易把系统提示词想象成某种运行时状态:以为 Pi 会「注册」工具、「安装」技能(Skill),模型那边有一份对应的清单。事实要朴素得多:Pi 把这些信息全部渲染成文本,拼成一个字符串,塞进请求的 system 字段。工具清单是文本,技能目录是 XML 文本,AGENTS.md 的内容是被 <project_instructions> 包起来的文本。

为什么值得强调这一点?因为一旦接受「它只是字符串拼接」,后面所有问题都变成了可回答的:拼接顺序是什么?谁负责拼?拼完存在哪、什么时候重拼?没有它会怎样——模型不知道自己有 edit 工具能改文件,也不知道这个项目要求提交前跑 lint。

⚠️ 常见误解以为系统提示词是每轮请求现算的
不是。Pi 把拼好的字符串缓存在 AgentSession._baseSystemPrompt 里,全仓库只有两处会重建它:setActiveToolsByNamepackages/coding-agent/src/core/agent-session.ts:939)和 extendResourcesFromExtensions:2275)。所以在 pi 运行期间修改 AGENTS.md,当前会话不会自动跟着变——要么重启,要么用 /reload 命令(packages/coding-agent/src/core/slash-commands.ts:40)走一遍资源重载。

最小示例:三十行复现这套拼接

脱离 Pi,把拼接规则写成一个小函数,放进 build-prompt.mjs

js
function buildPrompt({ customPrompt, tools = {}, appendText, contextFiles = [], cwd }) {
	let prompt;
	if (customPrompt) {
		prompt = customPrompt; // 有自定义提示词就整体替换,默认段落一段都不要
	} else {
		const list = Object.entries(tools).map(([n, d]) => `- ${n}: ${d}`).join("\n") || "(none)";
		prompt = `You are an expert coding assistant.\n\nAvailable tools:\n${list}`;
	}
	if (appendText) prompt += `\n\n${appendText}`;
	for (const f of contextFiles) {
		prompt += `\n\n<project_instructions path="${f.path}">\n${f.content}\n</project_instructions>`;
	}
	prompt += `\nCurrent working directory: ${cwd}`;
	return prompt;
}

console.log(buildPrompt({
	tools: { read: "Read file contents", bash: "Execute bash commands" },
	appendText: "Always answer in Chinese.",
	contextFiles: [{ path: "/repo/AGENTS.md", content: "提交前跑 npm test。" }],
	cwd: "/repo",
}));

node build-prompt.mjs 会打印一段完整提示词。这三十行里已经藏着 Pi 的两个关键设计:自定义提示词是「替换」而不是「插入」customPrompt 分支里默认段落一段都不生成),以及追加段、项目上下文、工作目录这三节无论走哪条分支都会拼上。真实的 buildSystemPrompt 就是这个骨架加上四个额外段落。

回到 Pi 源码:buildSystemPrompt 的八个段落

整个系统提示词只有一个组装函数,输入是一个纯数据对象:

earendil-works/pi@c13ffe1第 8–25 行在 GitHub 查看 ↗
组装函数的全部输入:customPrompt、selectedTools、toolSnippets、promptGuidelines、appendSystemPrompt、cwd、contextFiles、skills。注意它不接触文件系统,所有内容都是调用方预先加载好的。

默认路径下,前四段是一整个模板字符串:

ts
// packages/coding-agent/src/core/system-prompt.ts:121-138(节选,两处长句用 … 截断)
	let prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. …

Available tools:
${toolsList}

In addition to the tools above, you may have access to other custom tools depending on the project.

Guidelines:
${guidelines}

Pi documentation (read only when the user asks about pi itself, …):
// …(省略 132-138:README / docs / examples 三个绝对路径与阅读指引)
earendil-works/pi@c13ffe1第 121–138 行在 GitHub 查看 ↗
默认骨架的前四段:身份句、工具清单、补充说明与 Guidelines、Pi 自身文档指引。第四段里的三个路径由 config.ts 的 getReadmePath / getDocsPath / getExamplesPath 提供,这是 Pi 能回答「我自己怎么用」的原因。

两个容易被忽略的细节(源码事实)。其一,工具不是只要启用就会出现在清单里:system-prompt.ts:82-84 先用 toolSnippets?.[name] 过滤一遍,只有调用方给了一行说明的工具才会被列出,一个都没有时清单是字面量 (none)。其二,Guidelines 是动态生成的:只有在有 bash 而没有 grep/find/ls 时才加那条「用 bash 做文件操作」(system-prompt.ts:104-106),随后合并去重调用方传入的 promptGuidelines,最后固定追加两条通用条款(system-prompt.ts:116-117)。

后四段是无条件走到的尾部装配:

earendil-works/pi@c13ffe1第 140–159 行在 GitHub 查看 ↗
尾部四段:appendSystemPrompt → project_context 包裹的 AGENTS.md/CLAUDE.md → available_skills XML → Current working directory。skills 段有一个前置条件:hasRead 为真,即 read 工具在选中工具里。

技能段的那个 hasRead 条件值得多说一句。技能清单里给的是「名字 + 描述 + 文件绝对路径」(packages/coding-agent/src/core/skills.ts:335-361),正文要靠模型自己用 read 工具去读。所以 read 工具被关掉时,把清单贴出来只会让模型看见一堆读不到的路径——干脆不贴。技能系统本身在 7.2 Skill 系统 详述,本章只需知道它是系统提示词的第七段。

自定义提示词走的是一条短路径:

earendil-works/pi@c13ffe1第 46–72 行在 GitHub 查看 ↗
customPrompt 分支:默认的四段身份/工具/Guidelines/文档指引全部不生成,但 append、project_context、skills、cwd 四段照旧拼上。这里判断 read 工具的写法与默认路径略有差异,实际效果一致。

也就是说,--system-prompt "你只回答是或否" 会让模型看不到任何工具清单,但仍然看得到 AGENTS.md 和技能目录。这是很多人第一次用自定义提示词时踩的坑:以为是「换掉开头那句话」,实际是「换掉整个默认骨架」。

谁在调用它

earendil-works/pi@c13ffe1第 1021–1055 行在 GitHub 查看 ↗
AgentSession 侧的唯一装配点:从工具注册表收集 snippet 与 guidelines,从 ResourceLoader 取 SYSTEM.md 内容、追加段、技能列表、AGENTS.md 列表,组成 options 后调用 buildSystemPrompt。多个追加段在 1039-1040 行用两个换行连接成一段。

调用链是这样闭合的:setActiveToolsByNameagent-session.ts:926)→ _rebuildSystemPrompt:939)→ buildSystemPromptsystem-prompt.ts:28);结果一份存进 _baseSystemPrompt,一份写进 agent.state.systemPromptagent-session.ts:939-940)。/reload 走的是另一条路但殊途同归:AgentSession.reload:2602)→ _refreshToolRegistry:2455)→ 末尾再调一次 setActiveToolsByName:2545)。

还有一条运行时旁路:每次 prompt() 发消息前,Pi 会把当前 _baseSystemPrompt 和它的 options 一起交给扩展的 before_agent_start 事件(agent-session.ts:1225-1230);如果扩展返回了 systemPrompt,这一轮就用扩展给的值,否则重置回基础值(agent-session.ts:1246-1253)。扩展机制见 7.1 Extension 系统

三条自定义通道

CLI 参数在 packages/coding-agent/src/cli/args.ts:94-98 解析:--system-prompt 只保留最后一个值,--append-system-prompt 可重复、累积成数组。它们经 main.ts:725-726 传给 ResourceLoader,最终在 reload 阶段落地:

earendil-works/pi@c13ffe1第 525–541 行在 GitHub 查看 ↗
优先级判定:CLI 传入的值存在就用它,否则回退到文件发现 discoverSystemPromptFile / discoverAppendSystemPromptFile。追加段是数组,逐个解析后保留全部。

文件发现的顺序是「项目优先、全局兜底」:先看 cwd/.pi/SYSTEM.md(且项目必须已受信任),再看 agentDir/SYSTEM.mdresource-loader.ts:1022-1034);APPEND_SYSTEM.md 同理(:1036-1048)。

一个很实用的细节:CLI 参数的值既可以是文本,也可以是文件路径。

earendil-works/pi@c13ffe1第 53–68 行在 GitHub 查看 ↗
值若指向一个存在的文件就读取文件内容,读不动时打印警告并退回按字面文本处理;否则直接当作文本。这就是 --system-prompt 既能传一句话又能传 ./prompt.md 的原因。
图加载中…

图 6.7-1 系统提示词的八段装配与三条自定义入口
主干是 buildSystemPrompt 内部的执行顺序,从上到下就是最终文本的先后顺序。虚线是三条外部输入:SYSTEM.md 走 customPrompt 分支做整体替换,APPEND_SYSTEM.md 只影响第五段,扩展则在结果生成之后整体覆盖。注意 CUS 与 D4 汇合到 T1——这说明后四段是两条分支的公共尾巴。

值得关注的是虚线的三个接入点深度不同:SYSTEM.md 改的是「有没有默认骨架」,APPEND_SYSTEM.md 改的是「骨架后面加什么」,扩展改的是「这一轮实际发出去的是什么」。想让模型永远说中文,用 APPEND_SYSTEM.md 一行就够;想彻底换掉 Pi 的人设,才需要 SYSTEM.md

Prompt Template:模型不知道它存在

prompt template(提示词模板)是另一件事。系统提示词是给模型的背景,而模板是给用户的快捷方式:你敲 /cl,Pi 在把消息发出去之前,先把 /cl 替换成一整段事先写好的文字。模型收到的是展开后的文本,它既不知道有个叫 /cl 的命令,也不知道发生过替换。

模板就是一个 Markdown 文件。文件名去掉 .md 就是命令名:

earendil-works/pi@c13ffe1第 104–133 行在 GitHub 查看 ↗
name 取自文件名;description 优先取 frontmatter,缺失时退化为正文第一个非空行并截断到 60 字符加省略号;argument-hint 只在 frontmatter 里写了才有。content 是去掉 frontmatter 后的正文。

官方文档说明模板可以来自五个地方:全局 ~/.pi/agent/prompts/*.md、项目 .pi/prompts/*.md(需项目受信任)、pi package 的 prompts/ 目录、settings 的 prompts 数组、以及 CLI 的 --prompt-template <path>(来源:packages/coding-agent/docs/prompt-templates.md:7-17)。同一份文档还写明目录扫描是非递归的(docs/prompt-templates.md:95),这一点与实现一致——loadTemplatesFromDirprompt-templates.ts:138-175)只看一层 .md 文件。

从源码结构看,实际运行时这五个来源并不是由 loadPromptTemplates 自己去扫的:DefaultResourceLoader 调它时固定传 includeDefaults: falseresource-loader.ts:699-704),默认目录全部由 package-manager 汇总成一份路径清单后再传进来。loadPromptTemplates 里的 includeDefaults: true 分支(prompt-templates.ts:235-238)留给直接调用这个函数的 SDK 使用者。

展开发生在发送之前:

earendil-works/pi@c13ffe1第 269–285 行在 GitHub 查看 ↗
只有文本以斜杠开头、且第一段词恰好等于某个模板名时才展开;否则原样返回。参数串先由 parseCommandArgs 按 bash 风格的引号规则分词,再交给 substituteArgs。
earendil-works/pi@c13ffe1第 70–102 行在 GitHub 查看 ↗
一个正则覆盖全部占位符:$1..$N 位置参数、$@ 与 $ARGUMENTS 全部参数、${N:-默认值}、${@:-默认值}、${ARGUMENTS:-默认值}、${@:N} 从第 N 个起、${@:N:L} 从第 N 个起取 L 个。替换只在模板串上做一遍,参数值里的 $1 不会被二次展开。

AgentSession.prompt() 里,展开是三步流水线的最后一步(agent-session.ts:1122-1156):先尝试扩展注册的命令(命中就直接执行,根本不发消息),再尝试 /skill:名字 展开,最后才是模板展开。steer():1342-1343)与 followUp():1362-1363)走同样的展开。

图加载中…

图 6.7-2 一次斜杠命令输入的三级展开
从上到下是 prompt 方法里三个拦截点的先后顺序,关注三个「返回」箭头:任何一级命中都会短路后面的处理。最后一条 Note 是本节的要点——展开完全发生在客户端,模型侧没有任何痕迹。对应源码是 agent-session.ts:1123、:1154、:1155 这三行。

真实终端里的样子可以对照本书的采集素材(真实采集,research/cli-captures/pi-tui-main.txt):Pi 仓库自带 5 个模板,启动横幅里显示为

[Prompts]
  /cl, /is, /pr, /sa, /wr

它们对应 _sources/pi/.pi/prompts/ 下的 cl.mdis.mdpr.mdsa.mdwr.md。这一段由 interactive-mode.ts:1552-1569 渲染,模板同时被注册成自动补全项(interactive-mode.ts:619-623),argument-hint 会显示在描述前面。

⚠️ 常见误解照着 README 用 {{focus}} 写模板
官方 README 的 Prompt Templates 示例里写了 Focus on: {{focus}}(来源:packages/coding-agent/README.md:345)。但源码的 substituteArgs 只认 $ 系列占位符,这种双花括号写法不会被替换,会原样发给模型。要占位请用 $1${1:-默认值}。这是一处官方说明与源码不一致的地方。

顺带把模板和技能的分工说清楚:模板是客户端替换,用户显式触发,模型看不到机制;技能是系统提示词里的目录,模型自己判断要不要用 read 工具去读正文。两者恰好是「谁来决定加载」这个问题的两个答案。

两个包的分工

packages/agent(包名 @earendil-works/pi-agent-core)里也有 system-prompt.tsprompt-templates.ts,但它们不是被 coding-agent 复用的——在 packages/coding-agent/src 下搜 formatSkillsForSystemPromptformatPromptTemplateInvocation 没有任何命中。从源码结构看,这是两套平行实现,分工如下(均为源码事实):

关注点coding-agent(产品层)pi-agent-core harness(库层)
系统提示词组装buildSystemPrompt 八段固定骨架没有组装函数;应用给字符串或回调,默认句是 You are a helpful assistant.agent-harness.ts:363-374
技能清单格式化formatSkillsForPromptskills.ts:335),第二句是「用 read 工具加载」formatSkillsForSystemPromptharness/system-prompt.ts:3-25),第二句改为「读完整的技能文件」,且无前导空行
模板占位符支持 ${N:-默认值} 等默认值语法substituteArgsharness/prompt-templates.ts:249-262)只支持 $N$@$ARGUMENTS${@:N}${@:N:L}
模板数据结构name/description/argumentHint/content/sourceInfo/filePath只有 name/description?/contentharness/types.ts:78-85
资源来源自己扫目录、读 settings、判断项目信任应用通过 setResources() 注入,库不做任何发现

一种看法是:库层刻意不碰文件系统,才能同时服务 CLI、评测(evals)和第三方 SDK 使用者;代价是同类逻辑要写两遍,两边的行为差异(比如默认值语法)容易被误当成同一套。要用 harness 的模板能力,入口是 AgentHarness.promptFromTemplateharness/agent-harness.ts:690-705)。

实践任务

🛠 实践任务给 pi 加一个自己的斜杠命令,并在启动清单里看见它

目标:不需要任何 API Key,端到端验证 prompt template 的三件事——一个 Markdown 文件如何变成命令、如何出现在启动清单与自动补全里、$1${2:-默认值} 到底被替换成了什么。

前提:Pi 仓库(本书为 _sources/pi),依赖已安装。全程只读:模板与脚本都写在系统临时目录,不修改仓库任何文件。

步骤 1 · 写一个最小模板(bash/zsh;heredoc 的 'EOF' 必须带引号,否则 shell 会先把 $1 吃掉。fish 用户请改用编辑器创建同样内容的文件):

mkdir -p /tmp/pi-prompts
cat > /tmp/pi-prompts/sum3.md <<'EOF'
---
description: Summarize a file in three bullets
argument-hint: <path>
---
Read the file $1 and summarize it in exactly three bullet points.
Focus: ${2:-overall structure}
EOF

步骤 2 · 启动 pi 并观察启动清单(在仓库根目录执行):

env PI_CODING_AGENT_DIR=/tmp/pi-demo-agent PI_OFFLINE=1 \
  ./pi-test.sh --no-env -na --verbose --prompt-template /tmp/pi-prompts

四个附加开关各有用处:PI_CODING_AGENT_DIR 把全局配置目录指到临时位置,不碰你自己的 ~/.pi/agentPI_OFFLINE=1 关掉启动期网络操作;--no-envpi-test.sh 自带的开关,清空所有 API Key 环境变量;-na 让本次运行忽略项目本地文件,从而跳过「是否信任此项目」的交互提问;--verbose 强制展开启动清单(默认是紧凑一行,也可以进界面后按 ctrl+o 展开)。

预期现象(本书在锁定 commit 上实测,终端 120x45):启动横幅里出现

[Prompts]
  path
    /sum3

同一屏还会有 [Context][Skills] 两段,内容取决于你自己机器上的文件,与本任务无关。

步骤 3 · 看自动补全:在输入框里敲 /sum,下拉里出现(实测):

→ sum3        <path> — [t] Summarize a file in three bullets

<path> 是 frontmatter 里的 argument-hint[t] 是来源标记:通过 --prompt-template 临时加载的资源 scope 默认是 temporarysource-info.ts:36),既非 user 也非 project 时标记就是 tinteractive-mode.ts:523-528)。看完先按 ctrl+c 清空输入框,再按 ctrl+d 退出(默认键位见 packages/coding-agent/src/core/keybindings.ts:67-68)。

步骤 4 · 直接验证替换结果(不依赖终端,任何环境都能跑)。把下面内容存成 /tmp/expand-demo.ts

const mod = await import(`${process.cwd()}/packages/coding-agent/src/core/prompt-templates.ts`);
const { loadPromptTemplates, expandPromptTemplate } = mod;
const templates = loadPromptTemplates({
  cwd: process.cwd(), agentDir: "/nonexistent",
  promptPaths: [process.argv[2]], includeDefaults: false,
});
for (const t of templates) console.log("name:", t.name, "| hint:", t.argumentHint, "| desc:", t.description);
console.log(JSON.stringify(expandPromptTemplate("/sum3 src/main.ts", templates)));
console.log(JSON.stringify(expandPromptTemplate("/sum3 src/main.ts error-handling", templates)));
console.log(JSON.stringify(expandPromptTemplate("/nope hello", templates)));

在仓库根目录运行(必须在根目录,脚本用 process.cwd() 定位源码):

./node_modules/.bin/tsx --tsconfig ./tsconfig.json /tmp/expand-demo.ts /tmp/pi-prompts

预期现象(本书实测输出,逐字如下):

name: sum3 | hint: <path> | desc: Summarize a file in three bullets
"Read the file src/main.ts and summarize it in exactly three bullet points.\nFocus: overall structure"
"Read the file src/main.ts and summarize it in exactly three bullet points.\nFocus: error-handling"
"/nope hello"

步骤 5 · 跑一遍模板的单元测试

npm test --workspace=@earendil-works/pi-coding-agent -- test/prompt-templates.test.ts

预期现象Tests 92 passed (92)(本书实测)。用例名直接对应本章结论,例如 substituteArgs - positional defaultssubstituteArgs - array slicingexpandPromptTemplate > should split template arguments on unquoted newlines

如何判断成功:① 步骤 2 的清单里看到 /sum3;② 步骤 4 的三行输出与上面完全一致——第一行证明第二个参数缺失时 ${2:-overall structure} 用了默认值,第二行证明给了参数就用参数,第三行证明不匹配任何模板名的文本原样返回;③ 你能解释为什么 /nope hello 没有报错而是被当成普通消息。

常见错误:① heredoc 写成不带引号的 <<EOF$1 被 shell 替换成空串,模板里就少了占位符;② 把模板放进 /tmp/pi-prompts/sub/ 子目录——目录扫描非递归(prompt-templates.ts:138-175),找不到;③ 步骤 4 不在仓库根目录执行,process.cwd() 拼出的路径不存在,报模块找不到;④ 省掉 --verbose 时清单是紧凑的一行 /sum3,不分组,不是出错;⑤ 忘记 npm test 后面的 -- 分隔符,文件名参数到不了 vitest,会跑整个包的测试。

对应源码位置packages/coding-agent/src/cli/args.ts:158-160(--prompt-template 解析)、packages/coding-agent/src/main.ts:665,718(传给 ResourceLoader)、packages/coding-agent/src/core/resource-loader.ts:694-704(updatePromptsFromPaths)、packages/coding-agent/src/core/prompt-templates.ts:104(loadTemplateFromFile)、:269(expandPromptTemplate)、:70(substituteArgs)、packages/coding-agent/src/modes/interactive/interactive-mode.ts:1552(启动清单)、:619(自动补全注册)。

本章小结

  • 系统提示词是一次纯字符串拼接,唯一组装函数是 buildSystemPrompt,八段顺序为:身份句 → 工具清单 → Guidelines → Pi 文档指引 → 追加段 → <project_context><available_skills> → 当前工作目录。
  • 工具只有带一行 snippet 才会出现在清单里;技能段只有 read 工具启用时才拼上。
  • --system-prompt / SYSTEM.md整体替换默认骨架(后四段仍在);--append-system-prompt / APPEND_SYSTEM.md 是追加;扩展的 before_agent_start 可以在发送前整体覆盖这一轮的提示词。CLI 参数的值可以是文本,也可以是文件路径。
  • 结果缓存在 _baseSystemPrompt,只在工具集合变化或会话重载时重建,不是每轮现算。
  • prompt template 是纯客户端展开:/name argsprompt() 里被替换成模板正文,模型对模板机制毫无感知;占位符支持 $N$@$ARGUMENTS${N:-默认值}${@:N:L} 等。目录扫描非递归,文件名即命令名。
  • coding-agent 与 pi-agent-core harness 是平行实现而非复用;harness 不做资源发现,也不支持默认值语法。
  • 关键术语:系统提示词(System Prompt)、项目上下文文件(AGENTS.md / CLAUDE.md)、提示词模板(Prompt Template)、占位符替换(Argument Substitution)、来源标记(SourceInfo scope)。
  • 关键源码索引packages/coding-agent/src/core/system-prompt.tsBuildSystemPromptOptions:8buildSystemPrompt:28、customPrompt 分支 :46、工具过滤 :82、guidelines :104、默认骨架 :121、尾部装配 :140)、packages/coding-agent/src/core/agent-session.tssetActiveToolsByName:926_rebuildSystemPrompt:1021prompt:1114、展开三步 :1122-1156before_agent_start 覆盖 :1246_expandSkillCommand:1301)、packages/coding-agent/src/core/resource-loader.tsresolvePromptInput:53、reload 决策 :525updatePromptsFromPaths:694discoverSystemPromptFile:1022discoverAppendSystemPromptFile:1036)、packages/coding-agent/src/core/prompt-templates.tsparseCommandArgs:24substituteArgs:70loadTemplateFromFile:104loadTemplatesFromDir:138loadPromptTemplates:194expandPromptTemplate:269)、packages/coding-agent/src/core/skills.ts:335formatSkillsForPrompt)、packages/coding-agent/src/modes/interactive/interactive-mode.ts:523,619,1552packages/agent/src/harness/system-prompt.ts:3packages/agent/src/harness/prompt-templates.ts:249,265packages/agent/src/harness/agent-harness.ts:363,690;测试 packages/coding-agent/test/prompt-templates.test.ts(92 passed)、packages/coding-agent/test/system-prompt.test.ts(8 passed)、packages/agent/test/harness/prompt-templates.test.ts(5 passed)。
  • 自测问题:① 用 --system-prompt "只回答是或否" 启动后,模型还看得到 AGENTS.md 的内容吗?看得到工具清单吗?分别在哪一行决定?② 一个工具被 --tools 启用了,却没出现在 Available tools 里,最可能的原因是什么?③ 模板 /x 的正文里写 $1,用户输入 /x "a b" c$1 会被替换成什么?④ pi 正在运行时你改了 AGENTS.md,要让当前会话看到新内容需要发生什么?只把 ResourceLoader 重新加载一遍够不够?
  • 下一章6.8 配置系统——settings.json 的双层合并、项目信任,以及本章多次提到的 agentDir 到底怎么算出来。本章未展开的内容:技能的发现与加载规则(见 7.2 Skill 系统)、/skill:名字 展开的完整格式、扩展如何注册斜杠命令(见 7.3 自定义工具与斜杠命令)。

本书分析的 Pi 版本:earendil-works/pi@c13ffe1(2026-07-30)