智能体 · 循环
- 模型
- Qwen3-1.7B 推理关闭
- 工具
- lookup, calculate, finish
- 停止串
- “\nObservation”
- 这次运行
- 4 次调用 658 个词元
全部步骤
提示词搭好循环
系统提示词教给模型一种格式:先是 Thought,再从一列用普通文本写出的工具中选一个 Action,然后是 Observation。一个完整示例把格式演示一遍。问题还没读,就已经有 362 个词元了。
提示词 362 个词元 · 3 个行动 · 1 个完整示例先思考,再行动
Qwen3-1.7B 写出一个 Thought,然后是一个 Action。给行动起名是一次普通的下一词元选择:在 “Action 1:” 之后,它把 100% 的概率放在 “lookup” 上。
Thought k: … → Action k: tool[argument]停下、运行工具、追加
模型从不运行任何东西。它外面的程序盯着文本,在 “Observation” 即将出现的地方让它停下,用正则表达式读出行动,运行工具并追加真实结果。然后把上下文交还给模型。
停下 → 解析 → 运行 → 追加 → 继续一轮又一轮
真实运行:两次查询,一次计算,然后 finish[20]。每个观察都来自程序,而不是模型。模型只决定查什么,以及拿结果做什么。
4 次模型调用 · 3 个工具结果 · 答案 20上下文在增长
每一步都把模型的文本和工具的结果加进上下文,下次调用时再读一遍。如果在调用之间保留 KV 缓存,每个词元只处理一次(658)。如果每次都重新发送,这些调用要处理 2200 个。
保留缓存 658 个词元 · 重新发送 2200 个没有停止串
没有停止串,模型会继续写下去,自己写出一个 Observation,一个格式正确的猜测。所以循环必须在 “Observation” 处截断文本,插入真实结果。
停止串让结果保持真实
代码
while True:
out = llm.generate(context, stop=['\nObservation']) # a Thought and an Action, then stop
context += out
name, arg = re.search(r'Action \d+: (\w+)\[(.*)\]', out).groups()
if name == 'finish': return arg
result = TOOLS[name](arg) # the program runs the tool, not the model
context += f'\nObservation {k}: {result}\nThought {k + 1}:'