钩子与会话命令¶
钩子¶
Hooks 是一组 HookRegistry,注册在 agent.hooks.<name> 上:
from xun import Agent, HookArgs
def log_tool_call(args: HookArgs.BeforeToolCallArgs) -> None:
for call in args.tool_calls:
print(call.function.name, call.function.arguments)
agent = Agent()
agent.hooks.before_tool_call.add(log_tool_call)
注册表行为:
| 方法 | 语义 |
|---|---|
add(fn) |
持久回调,按注册顺序执行 |
add_once(fn) |
下一次 invoke 之后自动移除 |
invoke(args) |
依次调用全部回调,然后清掉一次性回调 |
回调返回值被丢弃,要改行为请就地修改参数对象。回调同样被 except_safe 包裹:普通异常被吞成 Result.Err(不会打断执行),只有 KeyboardInterrupt / CancelledError 会上抛。
钩子清单¶
| 钩子 | 参数(主要字段) | 时机 / 可改动内容 |
|---|---|---|
run_start |
agent |
由空闲转入运行时(嵌套作用域不重复触发) |
run_end |
agent |
运行结束,无论成功、报错还是取消 |
before_execution |
agent、schema、max_iterations、context_value |
执行循环开始前,四个字段都可改 |
before_execution_step |
agent |
每轮模型调用前;自动压缩器挂在这里 |
after_execution_step |
agent |
工具结果已写回会话、下一轮模型调用之前 |
before_tool_call |
agent、tool_calls(列表) |
工具执行前,可增删改工具调用 |
after_tool_call |
agent、tool_results((id, Result) 列表) |
全部工具执行完、写回会话前 |
model_text_delta |
agent、model_call_id、content |
文本增量落地前,可改写 content |
model_reasoning_delta |
同上 | 推理增量落地前 |
before_display_info |
agent、message |
agent.info() 输出前,可改写 message |
before_display_warning |
同上 | agent.warning() 输出前 |
before_display_error |
同上 | agent.error() 输出前 |
after_initialize |
agent |
initialize() 最后一步(扩展、模型探测、显示绑定都已完成) |
before_finalize |
agent |
finalize() 开始处,适合释放资源 |
before_command |
agent、command、arguments(列表) |
命令解析成功之后、执行之前,arguments 可就地改;未知命令不触发 |
after_command |
同上 | 命令执行结束(含被处理的错误) |
常见用法:把工具调用镜像到日志、给输出加前缀、在 before_execution 里统一改 max_iterations、在 before_finalize 里关闭外部资源(浏览器运行时就是这么清理的)。
会话命令¶
命令用于人机交互入口(终端 REPL、Web 界面的 / 输入框)。Agent.execute_command() 是统一入口:先 shlex.split 解析参数、触发 before_command,再执行,最后 after_command。
from xun import Agent, Command, CommandRegistry
def wordcount(agent: Agent, args: list[str]) -> None:
"""Count words in the given text."""
agent.info(f"{len(' '.join(args).split())} words")
agent.command.register(Command("wordcount", wordcount))
agent.command.register(Command.from_function(wordcount)) # 名称取函数名
agent.execute_command("wordcount", "one two three")
注册规则:
| 要点 | 说明 |
|---|---|
| 处理函数入参 | 1 个参数 → handler(agent);2 个 → handler(agent, arguments);否则 TypeError |
| 描述 | 默认取 docstring 首行;多行 docstring 会作为 -h 的长帮助 |
| 子命令组 | 把 CommandRegistry 作为 handler 传入即成为命令组,cmd sub -h 自动可用 |
| 帮助 | 任何命令支持 -h / --help;help 为虚拟命令,始终可用 |
| 错误 | 命令内异常被捕获为 agent.error(...),KeyboardInterrupt / CancelledError 上抛 |
| 转义 | 输入以 / 开头即视为命令;\/ 开头表示普通消息 |
内置命令清单及说明见 xun · 终端会话,策略子命令组(policy ...)由 default_tools=True 时注册。
命令与钩子都不进子智能体
Agent.inherit() 不继承 hooks,但会复制 command;子智能体在 initialize() 时会重放扩展,从而重新注册钩子。
会话历史的落盘¶
/save、/load 背后是 Store:
from xun import Store
store = Store() # 默认根目录 get_home_dir()
target = store.next_history_store() # .../conversation/000007.conversation
latest = store.latest_history_store()
one = store.get_history_store("3") # 或 get_history_store(3)
实际文件为 {XUN_HOME}/conversation/{NNNNNN}.conversation/conversation.json,键包括 conversation_id、time、tokens_used、messages、compacted_toolcalls、compaction。