第 2 课
无头运行、会话管理与 .goosehints 项目约定
认识 developer 扩展和四种权限模式,用 goose run 让 goose 在空目录里写 hello.py 并运行,用 GOOSE_MAX_TURNS 限制轮数、-n 命名、-i 从文件读任务,用 session list / export 查看和导出会话,最后写 .goosehints 并到 llm_request 日志里确认约定已进入系统提示。
课程目录第 2 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课依据: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。
