第 2 课

无头运行、会话管理与 .goosehints 项目约定

认识 developer 扩展和四种权限模式,用 goose run 让 goose 在空目录里写 hello.py 并运行,用 GOOSE_MAX_TURNS 限制轮数、-n 命名、-i 从文件读任务,用 session list / export 查看和导出会话,最后写 .goosehints 并到 llm_request 日志里确认约定已进入系统提示。

图文23 分钟goose 官方文档 ↗

课程目录第 2 / 3 课

学习位置仅保存在当前浏览器,有效期 180 天。

01 / 图文教材

图文教材

本课依据:goose 官方文档「Running Tasks」https://goose-docs.ai/docs/guides/running-tasks 、「Headless Mode」https://goose-docs.ai/docs/tutorials/headless-goose 、「Session Management」https://goose-docs.ai/docs/guides/sessions/session-management 、「goose Permission Modes」https://goose-docs.ai/docs/guides/managing-tools/goose-permissions 、「Providing Hints to goose」https://goose-docs.ai/docs/guides/context-engineering/using-goosehints 、「Environment Variables」https://goose-docs.ai/docs/guides/environment-variables (2026-10-28 核对,goose 1.56.0)。

这一课做完你会得到什么

让 goose 在无人值守的情况下自己写文件、执行命令并汇报结果,限制它最多跑几轮,给任务起名字,事后列出、导出、续接会话,再用 .goosehints 给项目写长期约定,并亲眼看到这份约定被放进发给模型的请求里。

接着第 1 课的环境:GOOSE_PROVIDER=local,模型是 unsloth/gemma-4-E2B-it-GGUF:Q4_K_M。

第 1 步:认识扩展和权限模式

goose 能动手做事,靠的是「扩展」(extension)。其中 developer 扩展提供写文件、改文件和执行 shell 命令的工具。用 goose run 无头运行时,可以用 --with-builtin developer 明确把它带上。

goose 还有四种权限模式,决定调用工具前要不要问你:

模式(GOOSE_MODE)行为
auto不询问,直接改文件、跑命令(官方默认)
approve每次用工具前都要你确认
smart_approve低风险操作自动放行,其余询问
chat只聊天,不用任何工具

交互界面里输入 /mode approve 这样的命令可以随时切换模式,也可以用环境变量 GOOSE_MODE 或写进 config.yaml 来设置。无人值守时没人点确认,所以本课用默认的 auto。正因为 auto 不询问,只在专门准备的空目录里练习,不要在重要项目里直接试。

第 2 步:让 goose 写文件并运行

mkdir -p ~/goose-demo2 && cd ~/goose-demo2
GOOSE_MAX_TURNS=6 goose run --with-builtin developer -n hello-demo \
  -t "在当前目录创建 hello.py,内容是 print('Hello goose'),\
然后用 python3 运行它并告诉我输出"

每个参数的作用:

  • GOOSE_MAX_TURNS=6:最多连续跑 6 轮就停,防止模型陷入循环。官方默认是 1000 轮。goose run 也有同样作用的参数 --max-turns;
  • --with-builtin developer:带上写文件和执行命令的工具;
  • -n hello-demo:给这次会话起名,后面按名字找它。

对照结果(2026-10-28 实测,8 核 CPU、无 GPU,实测时的工作目录是 /tmp/gt2;模型输出每次会不同):

    __( O)>  ● new session · local unsloth/gemma-4-E2B-it-GGUF:Q4_K_M
   \____)    20261027_3 · /tmp/gt2
     L L     goose is ready

  ────────────────────────────────────────
  ▸ write
    path hello.py
    content: print('Hello goose')

Created hello.py (1 lines)
  ────────────────────────────────────────
  ▸ shell
    command: python3 hello.py

Hello goose文件 `hello.py` 已创建,内容为 `print('Hello goose')`。

运行 `python3 hello.py` 的输出是:
Hello goose

real	3m36.9s

▸ write 和 ▸ shell 是 goose 真正调用的工具,下面紧跟着工具的返回结果。最后一段是模型根据结果写的总结。Hello goose 和总结之间没有换行,这是原始输出。自己检查一下文件:

cat hello.py

对照结果(2026-10-28 实测):

print('Hello goose')

任务比较长时,把要求写进文件,用 -i 传进去,比 -t 更方便:

goose run -i task.md

第 3 步:列出、导出、续接会话

第 2 步用了 -n,会话被记进了 sessions.db:

goose session list

对照结果(2026-10-28 实测):

Available sessions:
20261027_3 - hello-demo - 2026-10-27 16:27:04 UTC - /tmp/gt2

每行依次是会话 ID、名字、最后更新时间(UTC,换算成北京时间是 10-28 00:27)和工作目录。第 1 课用了 --no-session,所以那次没有出现在列表里。

想看更多字段,用 --format json。下面是节选,原始输出是一整行:

goose session list --format json
"name":"hello-demo"
"message_count":7
"provider_name":"local"
"goose_mode":"auto"
"accumulated_usage":{"input_tokens":11198,"output_tokens":856,…}

accumulated_usage 是这次会话累计用掉的 token 数。用云端模型时,可以凭它估算花费。

把会话导出成 Markdown,方便存档或分享:

goose session export -n hello-demo -o hello-demo.md
head -12 hello-demo.md

对照结果(2026-10-28 实测):

Session exported to hello-demo.md
# Session Export: hello-demo

*Total messages: 7*

---

### User:
在当前目录创建 hello.py,内容是 print('Hello goose'),
然后用 python3 运行它并告诉我输出

---

### Assistant:

导出的文件里还包括模型的思考过程(Thinking)和每次工具调用。官方文档还给了 --format json 和 --format yaml 两种导出格式,以及配套的 goose session import。本课只实测了 Markdown。

续接会话、在原会话基础上分叉,都按官方 Session Management 页的写法(会进入交互界面,本课未实测):

goose session -r --name hello-demo
goose session --resume --fork --name hello-demo
goose session remove -n hello-demo

-r 在原会话里接着聊。--fork 复制一份完整历史另开新会话,原会话不受影响。官方说明桌面版和命令行共用同一个数据库,两边的会话可以互相续接。

第 4 步:用 .goosehints 写项目约定

每次都在提示里重复「用中文」「先跑测试」很麻烦。可以把这类约定写进项目目录里的 .goosehints,goose 每次启动时会自动读入。官方说明:

  • 全局提示放在 ~/.config/goose/.goosehints,对所有会话生效;
  • 项目目录里的 .goosehints 只对这个项目生效,和全局冲突时以项目为准;
  • 默认先找 AGENTS.md,再找 .goosehints。想用别的文件名(比如 CLAUDE.md),设环境变量 CONTEXT_FILE_NAMES,值是 JSON 数组,例如 ["CLAUDE.md", ".goosehints"]。

做个小实验:

mkdir -p ~/goose-hints && cd ~/goose-hints
printf '# 项目约定\n- 用中文回答。\n' > .goosehints
printf -- '- 每次回答最后单独一行写:—— 来自 goose\n' >> .goosehints
echo '请用一句话说明 .goosehints 文件的作用。' > task.md
goose run -i task.md --no-session --max-turns 4

对照结果(2026-10-28 实测,耗时 1 分 35 秒):

    __( O)>  ● new session · local unsloth/gemma-4-E2B-it-GGUF:Q4_K_M
   \____)    20261027_5 · /tmp/gt4
     L L     goose is ready
`.goosehints` 文件通常用于提供与 Goose 项目或特定应用相关的元数据、
配置提示或架构信息。

(输出里过长的行已折行,终端里是一整行。)

模型用中文回答了,但没有加最后那行「—— 来自 goose」。这是因为提示文件没读到,还是模型没照做?去第 1 课提到的日志目录里找证据,看发给模型的请求里有没有这份约定:

cd ~/.local/state/goose/logs
grep -o 'Project Hints[^"]*' llm_request.0.jsonl | head -1 | sed 's/\\n/\n/g'

对照结果(2026-10-28 实测):

Project Hints
These are hints for working on the project in this directory.
# 项目约定
- 用中文回答。
- 每次回答最后单独一行写:—— 来自 goose

可见 .goosehints 的全文已经以「Project Hints」小节的形式放进了发给模型的系统提示,是 2B 小模型没有执行第二条,可以换更强的模型再试。用这个办法,可以判断问题出在配置还是模型。日志里的换行存成了 \n 两个字符,命令末尾的 sed 把它们还原成真正的换行。实测时最近一次请求记在 llm_request.0.jsonl,找不到的话用 ls -t 看哪个文件最新。

官方建议提示文件只写长期有效、会反复用到的约定,比如代码风格、测试命令、目录结构。太长会挤占上下文。

小结

  • --with-builtin developer 让 goose 能写文件、跑命令;默认的 auto 模式不询问,只在练习目录里用;
  • GOOSE_MAX_TURNS 或 --max-turns 限制轮数,-n 给会话起名,-i 从文件读取任务;
  • goose session list / export 查看和导出会话,-r 续接、--fork 分叉;
  • .goosehints 和 AGENTS.md 写项目约定,是否生效可以去 llm_request.*.jsonl 里核实。

下一课把常用任务写成可复用的 Recipe,带参数,并输出固定格式的 JSON。