Documentation Index Fetch the complete documentation index at: https://qitor.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
这是你第一次主动改造默认循环。
你仍然在构建一个普通的 QitOS 智能体,但会引入三个新东西:
放入状态的规划产物(持久化输出)
与执行提示词分离的规划提示词
一个只处理规划边界的 decide() 覆写
关键在于:你仍然没有引入第二个运行时。
相比第 1 课的变化
设计分支 第 1 课 第 2 课 控制流 每步都走默认大模型路径 只有规划边界由 decide() 拦截 提示词 单个 ReAct 系统提示词 规划提示词 + 执行提示词 状态 临时记录 + 任务字段 新增 plan_steps 与 cursor 解析器 ReActTextParser执行阶段仍是 ReActTextParser 工具 精简编码工具集 保持不变 记忆 / 历史 除状态外没有额外层 仍不引入记忆或上下文压缩
最后一行尤其重要:这节课增加的是规划能力,而不是上下文复杂度。
双提示词架构
这节课会显式使用两份提示词契约。
规划提示词
You are a planning module.
Break the task into 3-7 atomic executable steps.
Constraints:
- Each step must be actionable and verifiable.
- Prefer tool-executable operations over vague reasoning.
- No prose outside the numbered list.
在代码中通常命名为 PLAN_DRAFT_PROMPT。
执行提示词
You are the execution module for a Plan-Act agent.
You will receive the global task and one current plan step.
Execute only the current step. Do not jump ahead.
Output contract (strict):
Thought: <one sentence>
Action: <tool_name>(arg=value, ...)
or
Final Answer: <step result>
在代码中通常命名为 PLAN_EXEC_SYSTEM_PROMPT。
这节课想让你建立的设计意识是:
规划和执行可以使用不同的提示词
但它们仍然流经同一个 AgentModule + Engine 运行时
这节课的解析器逻辑
规划路径不 使用 ReActTextParser。
它通常是这样工作的:
_plan() 渲染 PLAN_DRAFT_PROMPT
NumberedPlanBuilder 调用同一个大模型适配层
构建器把编号列表解析成 list[str]
而执行路径仍然 使用 ReActTextParser。
这里体现了一个关键的 QitOS 设计点:
同一个智能体的不同阶段可以使用不同的解析契约,只要控制边界是显式的。
大模型适配层保持不变
和第 1 课一样,这节课仍然使用:
OpenAICompatibleModel( ... )
保持同一适配层的原因是:
这样你能单独观察规划带来的变化
提示词与解析器的变化更容易解释
课程一次只引入一个真正的新变量
在状态中加入计划与游标
新状态只增加执行真正需要的字段: @dataclass
class PlanActState ( StateSchema ):
plan_steps: list[ str ] = field( default_factory = list )
cursor: int = 0
target_file: str = "buggy_module.py"
test_command: str = TEST_COMMAND
scratchpad: list[ str ] = field( default_factory = list )
这是课程里第一次把原本隐藏的推理产物显式写进状态。 为什么要这么做:
追踪记录可以直接展示它
prepare() 可以显式暴露它
reduce() 可以推进它
以后若需要,也可以主动重写它
使用专用的计划构建器
规划器通常在构造时初始化: self .plan_builder = NumberedPlanBuilder()
调用方式类似: prompt = render_prompt(
PLAN_DRAFT_PROMPT ,
{
"task" : (
f " { state.task } \n "
f "Target file: { state.target_file } \n "
f "Last step must run: { state.test_command } "
),
},
)
plan = self .plan_builder.build( self .llm, prompt)
正确的 QitOS 做法是: 让规划变成有名字的持久化产物,并且有自己清晰的解析器,而不是混进主记录里的一段自由文本。
把 decide 只用作规划门控
这节课的控制逻辑通常非常小: def decide ( self , state : PlanActState, observation : dict[ str , Any]):
if not state.plan_steps or state.cursor >= len (state.plan_steps):
if not self ._plan(state):
return Decision.final( "Failed to build a valid plan." )
return Decision.wait( "plan_ready" )
return None
其中最重要的是最后一句: return None一旦计划已经存在,引擎会重新回到默认的大模型路径: 提示词 -> ReActTextParser -> 决策 -> 工具执行所以第 2 课不是替换运行时,而是在运行时上加了一道显式控制边界。
把执行提示词与解析器明确绑定
执行阶段仍是: super (). __init__ (
tool_registry = registry,
llm = llm,
model_parser = ReActTextParser(),
)
同时: def build_system_prompt ( self , state : PlanActState) -> str | None :
return render_prompt(
PLAN_EXEC_SYSTEM_PROMPT ,
{
"current_step" : self ._current_step_text(state),
"tool_schema" : self .tool_registry.get_tool_descriptions(),
},
)
因此规划阶段和执行阶段是清晰分离的:
让 prepare 显式展示计划
prepare() 现在不再只渲染任务,而是同时渲染当前计划进度:def prepare ( self , state : PlanActState) -> str :
lines = [
f "Task: { state.task } " ,
f "Plan cursor: { state.cursor } / { len (state.plan_steps) } " ,
f "Current plan step: { self ._current_step_text(state) } " ,
f "Step: { state.current_step } / { state.max_steps } " ,
]
智能体的工作记忆也因此发生了变化。模型每一步看到的不再是整个问题,而是:
在归约里推进计划进度
计划推进仍然是普通的状态逻辑: if isinstance (first, dict ) and first.get( "status" ) == "success" :
state.cursor += 1
if isinstance (first, dict ) and int (first.get( "returncode" , 1 )) == 0 :
state.final_result = "Verification passed."
state.cursor = len (state.plan_steps)
关键不在于这两个条件本身,而在于它们出现的位置: reduce()(归约)就是你定义”什么算计划完成”的地方。
故意保持记忆与历史简单
第 2 课依旧不引入:
记忆适配器
HistoryPolicy 调优
CompactHistory
因为此时计划本身已经是一种压缩任务结构的方式。如果同时引入上下文压缩,你就很难判断行为变化到底来自规划还是来自上下文管理。
运行并在 qita 中检查规划边界
运行: python examples/patterns/planact.py
查看: 在追踪记录中重点看:
哪一步出现了 Decision.wait("plan_ready")
plan_steps 是何时进入状态的
后续执行是否仍然沿着同一个 ReAct 解析器路径前进
为什么 PlanAct 仍然是同一个内核
很多人一想到规划,就会下意识引入:
单独的规划服务
框架之外的规划-执行循环
第二个智能体运行时
QitOS 在这节课想强调的恰恰相反:
规划器只是另一种受控的大模型调用
计划只是另一种状态产物
执行仍然走普通的引擎路径
这是 QitOS 一条很深的设计原则。
完整示例
完整可运行代码位于:
第 3 课会新增什么
第 3 课依旧不换内核,但智能体会真正变成长时运行的工作流。
你将第一次认真面对:
预设工具集,而不是手工组装
面向工作流的系统提示词
显式历史控制
上下文压缩与记忆何时成为必要设计问题
下一课:Claude Code 风格智能体 从模式设计走向长时运行的工作区智能体
相关指南:记忆与历史 在进入长时运行前,先回顾状态、历史、压缩与记忆的边界