跳转到主要内容

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.

Engine 每一步的执行都流经一条管线:decide、act、reduce,然后是 critic。Critic 位于 reducecheck_stop 之间,为你提供了一个结构化的钩子,可以审查智能体的最新决策及其结果、分配质量评分,并在必要时强制重试或终止执行。 本教程涵盖完整的 Critic API:CriticResult 契约、内置 Critic、@critic 装饰器、指令补丁与状态补丁,以及组合多个 Critic 的方式。

Step 1: CriticResult 契约

每个 Critic 都返回一个 CriticResult——一个告知 Engine 下一步操作的数据类。
from qitos.engine.critic_result import CriticResult
各字段说明:
字段类型默认值说明
actionstr"continue"取值为 "continue""stop""retry"
reasonstr""人类可读的解释。
scorefloat1.0质量评分,范围 0.0 到 1.0,越高越好。
detailsdict{}额外的结构化数据,用于日志或遥测。
instruction_patchstr | NoneNone在下一次迭代时追加到智能体系统提示词末尾(仅在 action="retry" 时生效)。
state_patchdict | NoneNone在下一次迭代前合并到智能体状态中的键值对(仅在 action="retry" 时生效)。
modified_promptstr | NoneNone下一次迭代的完整替换系统提示词(仅在 action="retry" 时生效)。
三种 action 控制循环行为:
  • "continue"——正常进入下一步。
  • "stop"——立即终止 Engine。状态的停止原因会被设为 StopReason.CRITIC_STOP
  • "retry"——丢弃当前步骤并重新运行 decide。任何 instruction_patchstate_patch 会在重试前应用。
最简的 continue 结果:
result = CriticResult(action="continue", reason="all good", score=1.0)
带指令补丁的重试:
result = CriticResult(
    action="retry",
    reason="output was incomplete",
    score=0.3,
    instruction_patch="Make sure to include all required fields in your answer.",
)
强制停止:
result = CriticResult(
    action="stop",
    reason="safety policy violation",
    score=0.0,
)

Step 2: 使用内置 Critic

QitOS 在 qitos.kit.critic 中提供了开箱即用的 Critic。

PassThroughCritic

最简单的 Critic——始终以满分继续。
from qitos.kit.critic import PassThroughCritic

critic = PassThroughCritic()
# evaluate() 始终返回 {"action": "continue", "reason": "pass", "score": 1.0}
创建 Engine 时若不指定 critics,这就是默认行为。它可用作空操作占位符,或作为组合链中由后续 Critic 执行真正工作的基础。

SelfReflectionCritic

检查工具结果中是否包含错误,并在可配置的重试上限内自动重试。
from qitos.kit.critic import SelfReflectionCritic

critic = SelfReflectionCritic(max_retries=2)
行为逻辑:
  • 如果任何工具结果包含 {"error": ...} 键且重试次数未耗尽,返回 action="retry"score=0.2
  • 如果错误持续超过 max_retries,返回 action="stop"score=0.0
  • 如果没有发现错误,返回 action="continue"score=1.0
将其传入 Engine:
from qitos.engine import Engine

engine = Engine(
    agent=my_agent,
    critics=[SelfReflectionCritic(max_retries=3)],
    # ... 其他 Engine 配置
)

ReActSelfReflectionCritic

专为 ReAct 智能体设计的增强版本。遇到错误时,它会构建一条结构化的反思笔记,描述失败的动作与观察到的错误,并将其追加到状态的 metadata 中,以便 LLM 在下次重试时能够从失败中学习。
from qitos.kit.critic import ReActSelfReflectionCritic

critic = ReActSelfReflectionCritic(max_retries=2)

函数式等价物

每个内置 Critic 都同时提供了装饰器函数版本:
from qitos.kit.critic import pass_through_critic, self_reflection_critic

# 这些是 @critic 装饰的函数——详见 Step 3

Step 3: 使用 @critic 装饰器编写自定义 Critic

@critic 装饰器可将任意普通函数转换为 Critic 实例。该函数接收 (state, decision, results) 参数,可返回简写形式或完整的 CriticResult
from qitos.engine.critic_decorator import critic
快速返回简写:
返回值含义
"continue"正常继续
("stop", "reason")带原因的终止
("retry", "reason")使用相同提示词重试
("retry", "reason", instruction_patch)追加指令后重试
CriticResult(...)完整结构化结果
无参数装饰器:
@critic
def no_empty_answers(state, decision, results):
    """Stop if the final answer is empty."""
    if decision.mode == "final" and not decision.final_answer:
        return "stop", "empty final answer"
    return "continue"
带关键字参数——设置自定义名称和默认评分:
@critic(name="safety", score=0.8)
def safety_check(state, decision, results):
    """Retry if any result looks unsafe."""
    for r in results:
        if isinstance(r, dict) and r.get("flagged"):
            return "retry", "unsafe output detected"
    return "continue"
当函数未显式设置评分时,装饰器的 score 参数将用作默认值。在上面的示例中,"continue" 返回会获得 score=0.8,而非通常的 1.0 将 Critic 接入 Engine:
engine = Engine(
    agent=my_agent,
    critics=[no_empty_answers, safety_check],
    # ... 其他 Engine 配置
)

Step 4: 带指令补丁和状态补丁的 Critic

当 Critic 返回 action="retry" 时,可以通过两种方式指导下一次迭代:
  • instruction_patch——追加到智能体系统提示词末尾的字符串,为 LLM 提供额外指导。
  • state_patch——一个字典,其键值通过 setattr 合并到智能体的状态对象中。
这些补丁在 Engine 再次调用 decide 之前就已应用,因此当重试开始时,LLM 和状态都已经更新完毕。
from qitos.engine.critic_result import CriticResult
from qitos.engine.critic_decorator import critic

@critic(name="format_enforcer")
def enforce_json_output(state, decision, results):
    """Retry with guidance if the output is not valid JSON."""
    if decision.mode == "final":
        answer = decision.final_answer or ""
        if not answer.strip().startswith("{"):
            return CriticResult(
                action="retry",
                reason="output is not JSON",
                score=0.2,
                instruction_patch=(
                    "You MUST output valid JSON. "
                    "Do not include any text outside the JSON object."
                ),
            )
    return "continue"
使用 state_patch 注入追踪数据:
@critic(name="retry_tracker")
def track_retries(state, decision, results):
    """Count retries and halt after the limit."""
    metadata = getattr(state, "metadata", {}) or {}
    retries = int(metadata.get("custom_retries", 0))

    has_error = any(isinstance(r, dict) and r.get("error") for r in results)
    if has_error and retries < 5:
        metadata["custom_retries"] = retries + 1
        state.metadata = metadata
        return CriticResult(
            action="retry",
            reason="tool error, retrying",
            score=0.3,
            state_patch={"last_error_type": "tool_failure"},
        )
    if has_error:
        return "stop", "exceeded retry limit"

    return "continue"
也可以使用元组简写配合指令补丁:
@critic
def be_concise(state, decision, results):
    if decision.mode == "final" and len(decision.final_answer or "") > 2000:
        return "retry", "answer too long", "Keep your final answer under 2000 characters."
    return "continue"

Step 5: 组合多个 Critic

Engine 接受一个 Critic 列表。它们按顺序求值,第一个非 continue 的结果生效。这让你可以从最严格到最宽松地堆叠 Critic。
from qitos.kit.critic import SelfReflectionCritic
from qitos.engine.critic_decorator import critic

@critic(name="safety_gate")
def safety_gate(state, decision, results):
    """Hard stop on safety violations -- nothing overrides this."""
    for r in results:
        if isinstance(r, dict) and r.get("safety_violation"):
            return "stop", "safety violation detected"
    return "continue"

@critic(name="format_check")
def format_check(state, decision, results):
    """Retry on formatting issues."""
    if decision.mode == "final":
        answer = decision.final_answer or ""
        if not answer.strip().startswith("{"):
            return "retry", "not JSON", "Output valid JSON only."
    return "continue"
将它们接入 Engine——安全优先,然后是格式,最后是内置的自省 Critic 作为兜底:
from qitos.engine import Engine

engine = Engine(
    agent=my_agent,
    critics=[
        safety_gate,        # 最严格的终止——最先检查
        format_check,       # 较宽松——带指导的重试
        SelfReflectionCritic(max_retries=2),  # 捕获工具错误
    ],
    # ... 其他 Engine 配置
)
按照这个顺序:
  1. 如果 safety_gate 返回 "stop",Engine 立即终止。后续 Critic 不会被调用。
  2. 如果 safety_gate 返回 "continue"format_check 返回 "retry",Engine 带着指令补丁重试。
  3. 如果两者都返回 "continue"SelfReflectionCritic 有机会捕获工具错误。
这种分层方式让每个 Critic 保持小巧专注,而顺序则赋予你对优先级的完全控制。

Hooks 生命周期

了解 Critic 参与的完整 Engine 事件生命周期。

Agent Module

深入了解 Critic 审查的 AgentModule 接口。

Critic 与停止条件

了解 Critic 如何与停止条件和重试预算交互。