首页
/ Response Formatting Requirements

Response Formatting Requirements

2026-09-06 13:13:10作者:滕妙奇

Always structure your responses using clear markdown formatting:

  • By default don't put information into tables for questions (but do put information into tables when creating or updating files)
  • Use headings (##, ###) to organise sections, always
  • Use bullet points or numbered lists for multiple items
  • Use code blocks with language tags for any code
  • Use bold for key terms and emphasis
  • Use tables when comparing options or listing structured data
  • Break long responses into logical sections with headings

它没有任何 YAML 头部,第一行就是一个一级标题。文件位于测试夹具目录 [fixtures/](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/fixtures?utm_source=gitcode_repo_files) 下,与 [frontmatter.md](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/fixtures/frontmatter.md?utm_source=gitcode_repo_files)、[empty-frontmatter.md](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/fixtures/empty-frontmatter.md?utm_source=gitcode_repo_files)、[no-frontmatter.md](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/fixtures/no-frontmatter.md?utm_source=gitcode_repo_files)、[weird-model-id.md](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/fixtures/weird-model-id.md?utm_source=gitcode_repo_files) 一起,构成 `ConfigMarkdown` 解析器的边界条件测试集。

该文件被 [markdown.test.ts](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/markdown.test.ts?utm_source=gitcode_repo_files#L194-L212) 中名为 "frontmatter parsing w/ Markdown header" 的用例引用:

```typescript
const result = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/markdown-header.md")

test("should parse and match", () => {
  expect(result).toBeDefined()
  expect(result.data).toEqual({})
  expect(result.content.trim().replace(/\r\n/g, "\n")).toBe(`# Response Formatting Requirements
  ...`)
})

这条断言就是该夹具存在的意义,它验证了 opencode 对 Markdown 配置文件的解析契约:文件以 Markdown 标题开头、不含 frontmatter 时,解析不报错,data 为空对象 {},整份正文(含首行 # 标题)原封不动进入 content 字段。这与 empty-frontmatter.mdno-frontmatter.md 两个用例共同覆盖了「frontmatter 为空 / frontmatter 缺失」两类退化情况。

解析核心:gray-matter 优先,失败后走宽松 sanitizer

ConfigMarkdown.parse 的实现在 packages/core/src/config/markdown.ts

import matter from "gray-matter"
export function parse(content: string) {
  try {
    return matter(content)
  } catch {
    return matter(sanitize(content))
  }
}

markdown-header.md 这类文件,gray-matter 的 frontmatter 匹配器找不到文件顶部的 ---...--- 块,于是返回 data: {} 加完整 content,这正是测试断言所依赖的行为。对普通 frontmatter 文件(如 weird-model-id.md),则一次性解析出 descriptionmodemodeltools 等字段,正文 "Strictly follow da rules" 作为 content

更值得展开的是 catch 分支里的 sanitize 函数。源码注释写明动机:其他编码智能体允许 frontmatter 中出现非标准 YAML(比如未加引号、值里带冒号),为让用户的存量配置文件继续可用,opencode 在首次解析失败时会做一轮宽松重写:

export function sanitize(content: string) {
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
  if (!match) return content
  // ...
  const entry = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/)
  // ...
  if (!value.includes(":")) return [line]
  return [`${entry[1]}: |-`, `  ${value}`]
}

它的规则是:只处理顶部 frontmatter 块内的顶层 key: value 行;若值包含冒号且未用引号包裹,就把该行改写成 YAML 块标量(key: |- 加缩进行),再交给 gray-matter 重解析一次。frontmatter.md 夹具就是为这套逻辑设计的压力测试,markdown.test.ts 覆盖了十余种情形:

  • 值内含多个冒号:occupation: This man has the following occupation: Software Engineer 被完整保留;
  • 已用单/双引号包裹的值(title: 'Hello World'quoted_colon: "Already quoted: no change needed")不做改写;
  • 含冒号的时间与 URL(time: The time is 12:30:00 PMurl: https://example.com:8080/path?query=value)正确提取;
  • 注释行 # field: ... 不进入 data;空值(empty:)解析为 null
  • frontmatter 之后正文里长得像 YAML 的行(fake_field: this is not yaml)绝不会被解析——sanitizer 的正则只锚定文件最顶部的 --- 块。

这套「严格解析优先、宽松重写兜底」的两级策略,是对齐其他编码智能体配置生态的关键兼容层。

opencode 包装层:@文件引用 与 shell 内联语法

packages/opencode/src/config/markdown.ts 在 core 层之上封装了面向会话输入与命令模板的两个正则和文件级解析入口:

export const FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
export const SHELL_REGEX = /!`([^`]+)`/g
  • files(template):从文本中抽取 @path 形式的文件引用。负向后行断言 (?<![\w]保证前缀是单词字符或反引号时不匹配——[测试用例](https://gitcode.com/GitHub_Trending/openc/opencode/blob/7c2199d84a5830f70a8250731a42ff958145b4d6/packages/opencode/test/config/markdown.test.ts?utm_source=gitcode_repo_files#L28-L90) 验证了它恰好命中 12 个合法路径(含隐藏文件@.bashrc、绝对路径 @/absolute/paths.txt、home 路径 @~/home-files),同时排除反引号包裹的 `` @quoted/in/backticks`` 和邮箱user@example.com`;
  • shell(template):抽取 !`command` 形式的内联 shell 命令;
  • parse(filePath):读取文件后调用 core 层解析,失败时抛出携带路径与原始错误的 FrontmatterErrormarkdown.ts 第 20–34 行),供上层统一呈现定位清晰的错误信息。

谁在消费这些 Markdown:Agent、Command、Mode 与 Skill

解析契约落到真实加载链路后,「正文 = 提示词、frontmatter = 元数据」的拆分才有意义。

Agent / Modeconfig/agent.ts 用 glob 扫描 {agent,agents}/**/*.md,逐个执行 ConfigMarkdown.parse(item).catch(() => undefined)——注意这里把解析失败静默吞掉(返回 undefined 后跳过该文件),避免一个损坏文件阻断整个配置加载:

const md = await ConfigMarkdown.parse(item).catch(() => undefined)
if (!md) continue
const config = { name, ...md.data, prompt: md.content.trim() }

frontmatter 字段展开进配置、正文 trim() 后成为 prompt。对照夹具 weird-model-id.md(frontmatter 含 descriptionmode: subagentmodel: synthetic/hf:zai-org/GLM-4.7、嵌套 tools),可以看到一个完整 Agent 定义文件长什么样:元数据负责路由与权限,正文负责行为约束。config/command.ts 的 Command 加载逻辑与之同构,扫描 {command,commands}/**/*.md,正文成为 template,且解码失败时抛出带文件路径的 InvalidError

Skillskill/index.tsadd 流程同样以 ConfigMarkdown.parse 为入口,但它多一步 frontmatter 形状校验 isSkillFrontmatter(md.data):没有 name/description 等必需字段的文件直接不被当作 Skill 收录。这意味着像 markdown-header.md 这样 data 为空的文件,如果出现在 Skill 目录里会被静默忽略而不是报错——「无 frontmatter 可解析」和「被当作配置项」是两个独立的判定。

会话提示词模板session/prompt.tsresolvePromptParts 把用户输入中的 @文件 引用解析为附件:~/ 前缀拼接到用户主目录,其余相对 worktree 根解析;路径不存在时还会回退尝试匹配 Agent 名称。命令模板执行阶段(prompt.ts 第 1397–1408 行)则用 ConfigMarkdown.shell 抽出 !`cmd` 片段,按用户配置的 shell 逐条执行,并用输出替换原片段——命令参数占位符 $ARGUMENTS 在此之前已被替换。

如何验证:运行相关测试

该解析契约由 markdown.test.ts 完整守护,在仓库根目录可用以下命令单独运行(仓库基于 Bun 工具链):

bun test packages/opencode/test/config/markdown.test.ts
登录后查看全文
热门项目推荐
相关项目推荐