Knuth 是一个本地优先的 Agent 运行时框架。
它不是只给 LLM 包一层 CLI。Knuth 更关心的是:一次 agent 运行过程中,模型看到了什么、工具为什么会被调用、审批卡在哪一步出现、进程中断后应该从哪里继续。这些信息都应该能被记录、恢复和解释。
如果只想做一个简单聊天入口,Knuth 可能显得有点重。它适合那些需要工具调用、人工审批、运行历史、Web/CLI 多端入口,甚至以后要做回放和调试的 agent 系统。
Knuth 把一次运行写成一串事件:用户消息、模型输出、工具调用、审批、工具结果、运行结束等。当前状态只是从这些事件算出来的。
这样做的好处很直接:
- crash 后可以知道运行停在哪;
- 审批通过后执行的是当时冻结的那个工具调用,不是重新让模型猜一次;
- 派生状态坏了可以用
knuth admin refold从事件重建; - 调试时能看到完整 timeline。
LLM streaming 里的 delta、reasoning、tool call chunk 属于 InferenceEvent。这些是模型边界的低层事件。
Runtime 关心的是另一层:run 创建了、模型完成了、工具等待审批、工具执行结束、run 被中断了。这些是 RuntimeEvent。
这两层分开后,CLI、Web UI、日志和恢复逻辑不用直接依赖某个 provider 的流格式。
模型发起 tool call 后,Knuth 不会把它当成一段马上执行完的临时代码。Runtime 会先记录工具批次,再交给 ToolBroker 做参数校验和策略判断。
默认策略很保守:受信内置读工具可以直接跑;写文件、shell、python 需要审批;没有宿主可信规则的动态/client tool 即使自报低风险也默认请求审批。
如果进程在外部写操作中间挂了,Knuth 不会自动重试。它会把结果标成 unknown,让人确认到底发生了什么。
这里有个刻意的区别:
pause是“这件事以后还能接着做”;interrupt是“当前这次尝试被用户停掉了,不要偷偷重放”;continue_run是用户又说了一句话,开启下一轮。
这个区别对 agent 很重要。否则 Ctrl+C、审批退出、工具等待、真正的 crash 很容易混在一起。
shell 输出太长时,Knuth 会把完整 stdout / stderr 存到本地 artifact 文件里,只把摘要和路径放进模型上下文。模型需要细看时,可以再用文件工具去查。
这比简单截断更可靠,也不会把 ledger 变成一个大 blob 仓库。secret 也会在写入事件和 artifact 前做默认脱敏。
现在所有东西还是 in-process,但包已经按边界拆开:core、LLM、tool、runtime、CLI、AG-UI、IM host。以后某一层要换成服务进程,不需要重写整个项目。
Knuth IM 支持经过显式审批的 JavaScript Workflow。脚本可以用稳定 call_key 并发调用多个
Agent,但不能直接访问文件、shell、网络或环境变量;这些能力仍由 child Run 的普通工具和
审批获得。Workflow Agent、结果和停止/暂停行为都复用现有 Run、ledger 与 policy,而不是
另建一套不可观察的执行器。
.
├── packages/
│ ├── knuth-core/ # 共享模型:消息、事件、run、工具、审批、artifact、skills
│ ├── knuth-llmd/ # LLM client 协议和 LiteLLM 适配
│ ├── knuth-toold/ # 工具协议、注册、执行、内置工具
│ ├── knuth-runtime/ # agent loop、ledger、上下文、审批、恢复
│ ├── knuth-cli/ # 命令行入口和 CLI 本地工具
│ ├── knuth-agui/ # AG-UI / FastAPI / SSE 适配层
│ └── knuth-im/ # Web/IM host,负责装配 runtime
├── apps/
│ └── knuth-im-web/ # Next.js + Electron UI
├── docs/ # 设计文档和 ADR
├── tests/ # 测试
├── AGENTS.md # 仓库内 agent 工作约定
└── pyproject.toml # uv workspace 配置
核心包的分工:
| 包 | Import | 作用 |
|---|---|---|
knuth-core |
knuth.core |
消息、事件、run、工具等共享数据模型。 |
knuth-llmd |
knuth_llmd |
LLM client 协议,以及基于 LiteLLM 的实现。 |
knuth-toold |
knuth_toold |
工具 manifest、registry、broker、内置工具和 skill 工具化。 |
knuth-runtime |
knuth_runtime |
编排 LLM、工具、ledger、审批、恢复和上下文。 |
knuth-cli |
knuth_cli |
knuth 命令行入口。 |
knuth-agui |
knuth_agui |
把已有 AgentRuntime 暴露成 AG-UI 服务。 |
knuth-im |
knuth_im |
装配 Web/IM 场景需要的 runtime,并启动后端。 |
开发者先读 当前架构 和 领域词汇;需要了解设计原因时, 通过 ADR 索引 选择相关决策,不必遍历全部历史文档。
Knuth 使用 uv,Python 版本要求 >=3.12。
uv sync --dev最小配置可以写在仓库根目录的 .env:
KNUTH_MODEL=gpt-4.1
KNUTH_BASE_URL=https://api.openai.com/v1
KNUTH_API_KEY=...也可以放到用户配置文件里:
~/Library/Application Support/knuth/knuth-cli/knuth.yaml # macOS 常见位置
常用环境变量:
| 环境变量 | 说明 |
|---|---|
KNUTH_MODEL |
模型名。没有 provider 前缀时默认按 openai/<model> 处理。 |
KNUTH_BASE_URL |
OpenAI-compatible API 地址。 |
KNUTH_API_KEY |
API key。 |
KNUTH_TIMEOUT |
模型请求超时,默认 60 秒。 |
KNUTH_SYSTEM_PROMPT |
额外 system prompt。 |
KNUTH_AUTH_MODE |
api_key 或 chatgpt。 |
KNUTH_CHATGPT_TOKEN_DIR |
ChatGPT token 目录。 |
KNUTH_SKILL_ROOTS |
skill 目录,多个路径用系统 path separator 分隔。 |
KNUTH_SKILL_HOT_RELOAD |
是否热加载 skills。 |
KNUTH_LITELLM_CALLBACKS |
LiteLLM callbacks,例如 Langfuse。 |
使用 Langfuse v3 时,把 KNUTH_LITELLM_CALLBACKS=langfuse_otel 与 Langfuse 的
public key、secret key、host 一起放入环境或根目录 .env。未显式配置 callback 时默认不
向外部观测服务发送模型输入输出。
一次性运行:
uv run knuth run "总结这个仓库"附加图片输入,可重复使用 --image:
uv run knuth run --image screenshot.png "分析这个界面"进入交互模式:
uv run knuth run显式开启 Auto Mode;当前工作目录就是 workspace,只自动批准该目录内的
write_file/edit_file,其他工具和目录外写入仍需人工审批:
uv run knuth --permission-mode auto run交互模式里可以直接输入问题,也可以用 slash command:/help、/tools、/resume、
/status、/usage、/pending、/interrupt、/exit。
常用命令:
# 最近的 runs
uv run knuth runs --limit 20
# 当前可用工具
uv run knuth tools list
# 查看状态和事件
uv run knuth status <run_id>
uv run knuth events <run_id>
# 恢复 paused / waiting 状态
uv run knuth resume <run_id>
# 处理工具审批
uv run knuth approvals --run-id <run_id>
uv run knuth approve <approval_id>
uv run knuth deny <approval_id>
# crash 后恢复运行中状态
uv run knuth recover [run_id]
# 人工确认 unknown 的外部写结果
uv run knuth resolve <tool_call_id> --outcome succeeded --note "confirmed manually"
# 从事件日志重建投影
uv run knuth admin refold调试时可以打开完整事件日志:
uv run knuth --debug run "..."日志会写到:
~/.knuth/debug/<run_id>.jsonl
CLI / IM 默认会注册这些工具:
| 工具 | 说明 |
|---|---|
read_file |
读取文本文件。 |
write_file |
写文件。默认需要审批;Auto Mode 下工作区内自动批准。 |
edit_file |
精确字符串替换。默认需要审批;Auto Mode 下工作区内自动批准。 |
glob |
按 glob 找文件。 |
grep |
用 ripgrep 搜索内容。 |
rlm_capture |
把当前 run 最近一条真实用户消息保存为有界 RLM artifact。 |
rlm_query |
对当前 run 的 RLM artifact 做有界 slice 或 literal search。 |
skill |
加载当前轮可用的动态或内置 skill;默认包含 rlm。 |
shell |
运行 shell 命令。需要审批。 |
python |
运行 Python 片段。需要审批。 |
路径按普通 OS 语义处理:绝对路径就是绝对路径,相对路径相对于当前进程工作目录。访问控制放在 policy 层,而不是工具里偷偷改路径。
当前只有 rlm_query 参与 pure tool cache。cache key 同时绑定工具 revision、参数、run、
artifact SHA-256 和 policy digest;命中仍会产生普通 durable tool completion。删除或损坏
cache 只会造成 miss,不影响历史恢复,也不用于缓存写操作。
knuth-agui 是协议适配层。它接收一个已经构造好的 AgentRuntime,把 runtime events 翻译成 AG-UI 事件,通过 FastAPI / SSE 给前端使用。它不负责选模型、选工具或写 prompt。
history 与 live tail 使用同一个固定-watermark cursor feed:先 attach,再分页 replay,最后去重 tail。浏览器 subscriber 非阻塞;队列溢出只重连该连接,不拖慢 agent。IM 切回正在运行的 会话时使用 observation-only attach,不会误 resume 或改变当前工具目录。
knuth-im 是一个现成 host,可以直接启动:
uv run knuth-im常用参数:
uv run knuth-im \
--host 127.0.0.1 \
--port 8000 \
--db-path ~/.knuth/knuth-im.db \
--workspace /path/to/workspace \
--auth-token "$KNUTH_IM_AUTH_TOKEN"对应环境变量:
| 参数 | 环境变量 | 默认值 |
|---|---|---|
--host |
KNUTH_IM_HOST |
127.0.0.1 |
--port |
KNUTH_IM_PORT |
8000 |
--db-path |
KNUTH_IM_DB_PATH |
~/.knuth/knuth-im.db |
--workspace |
KNUTH_IM_WORKSPACE |
当前目录 |
--auth-token |
KNUTH_IM_AUTH_TOKEN |
空 |
--env-file |
KNUTH_IM_ENV_FILE |
.env |
前端在 apps/knuth-im-web:
# 后端
uv run knuth-im
# 前端
cd apps/knuth-im-web
npm install
npm run dev默认连接 http://127.0.0.1:8000。需要改地址时设置 NEXT_PUBLIC_KNUTH_AGUI_URL。
Electron 开发入口:
cd apps/knuth-im-web
npm run electron:dev最小例子:
from knuth_llmd import InferenceConfig, LiteLLMInferenceClient
from knuth_runtime import build_sqlite_runtime
runtime = build_sqlite_runtime(
inference_client=LiteLLMInferenceClient(
model="gpt-4.1",
base_url="https://api.openai.com/v1",
api_key="...",
),
inference_config=InferenceConfig(timeout_s=60),
)
result = await runtime.run_once("总结这个项目")
print(result.answer)需要多轮控制时,用 start、continue_run、resume:
async with runtime.start("读取 README 并总结") as session:
result = await session.result()
async with runtime.continue_run(result.run_id, "再短一点") as session:
result = await session.result()测试里可以用 build_memory_runtime(...)。本地长期运行建议用 build_sqlite_runtime(...)。
Knuth 可以把本地 skill 暴露成一个工具。默认搜索这两个目录:
<project>/.knuth/skills
~/.agents/skills
每个 skill 是一个目录,里面放 SKILL.md。目录名需要和 skill 名一致。Runtime 会读取这些 skills,并把 catalog 提供给模型。
CLI 与 AG-UI host 会在整个进程生命周期内启动一次 skill watcher;自行嵌入 runtime 的长期
运行 host 应使用 async with runtime.host_lifespan(): 包住服务主循环。
相关配置:
KNUTH_SKILL_ROOTS="/path/to/project-skills:/path/to/user-skills"
KNUTH_SKILL_HOT_RELOAD=true
KNUTH_SKILL_HOT_RELOAD_DEBOUNCE_MS=1000这个仓库是 uv workspace。运行 Python 命令时尽量用 uv run。
# 安装开发依赖
uv sync --dev
# 全部测试
uv run python -m unittest discover tests
# 常用单测
uv run python -m unittest tests.test_runtime
uv run python -m unittest tests.test_cli
uv run python -m unittest tests.test_agui_spike调试脚本:
uv run python scripts/runtime_event_tui.py
uv run python scripts/llmd_event_probe.py
uv run python scripts/llmd_event_tui.py