| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- id: tool-react-over-text
- module: A
- week: 2
- title: "工具调用范式①:react-over-text(把工具调用当散文手写 JSON)"
- concept:
- textbook: |
- ReAct(Yao et al., 2022)让 LLM 在 Thought → Action → Observation 之间交替:
- 模型先"想"(Thought),再发一个"动作"(Action),环境执行后把结果(Observation)
- 回灌,如此循环直到给出答案。
- 最朴素的工程实现是 **react-over-text**:不依赖模型原生的工具调用能力,而是要求
- 模型把 Action 以一段约定格式的文本写出来(通常是一段 JSON),平台再用正则把这段
- 文本"抠"出来、自己去执行。模型在这里并不"调用"工具,它只是在"描述"一个调用。
- key_point: >
- react-over-text 把工具调用降格成"生成文本 → 正则解析 JSON",是 0 执行 /
- 格式飘移 / path-vs-file_path 不一致的总根源。它适合做 fallback,不该当主车道。
- code:
- - file: lambdagent/src/lambdagent/fromconfig/compiler.py
- symbol: _compile_react
- lines: "878-933"
- note: >
- 文本车道主入口。顶部做门控分叉:react.nativeToolCalls 关闭时(默认),
- 返回基于 ConversationLam 的文本 ReAct 循环,模型输出靠正则解析。
- - file: lambdagent/src/lambdagent/conversation.py
- symbol: ConversationLam
- lines: "60-126"
- note: >
- 文本车道的"思考"算子。每步把历史 + 最新 observation 拼成 prompt 发给 provider,
- 拿回纯文本,再交给上层正则解析出 action。靠 max_input_chars 兜住上下文膨胀。
- formal:
- term: "Loop(ConversationLam ∘ action_parser, cond, max_steps)"
- effect: "IO[tool] ⊕ Partial"
- note: >
- action_parser 是一个**部分函数**:模型文本不合约定时解析失败(返回空动作或抛错)。
- 这个"部分性(Partial)"正是范式②原生 FC 要从类型上消除的东西——FC 让工具调用
- 成为结构化、类型受控的对象,而非一段可能解析失败的散文。
- live_demo:
- run: agentexample/research67/workspace/run_20260520_004928
- highlight: >
- 999 步 react_step,工具调用全部以文本形式嵌在每步 output 里(看 "[Step N] X done")。
- 这是 react-over-text 在长任务里空转/重做的典型形态:步数巨大,真正落盘寥寥。
- related:
- - tool-native-fc
- - tool-claude-code-native
- takeaway: |
- 对照三栏后学生应能说清:
- 1. 概念上,react-over-text 仍是合法的 ReAct,只是 Action 用文本承载;
- 2. 代码上,它落在 _compile_react 的"非 nativeToolCalls"分支 + ConversationLam;
- 3. 形式上,它引入了一个部分函数 action_parser,把"解析失败"这一失效模式带进了系统。
- 这就是"为什么需要形式化"的第一个实证:范式②用类型把这个失效模式提前消除。
|