mcpField
Q
query-loop-implementation

query-loop-implementation

github.com/simbajigege开发19639 次安装9 周前更新
代码仓库

关于 query-loop-implementation

面向 AI 应用开发者的查询循环实现指南,将会话状态、任务轮次控制和工具执行分层,并强调安全检查与明确的退出条件。

主要功能

  1. 01分离会话、循环与工具运行时
  2. 02采用结构化工具调用消息流
  3. 03校验输入模式并检查权限
  4. 04设置轮次、超时和预算限制
  5. 05定义结构化终止原因

适用场景

  1. 01为 AI 应用设计查询引擎
  2. 02审查现有模型与工具循环
  3. 03为已注册工具加入安全执行

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/simbajigege/book2skills --skill query-loop-implementation

安装后在 Claude Code 中输入/query-loop-implementation确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
README.md
agents/
openai.yaml
references/
query-loop-patterns.md

详细介绍

查询循环实现

核心理念

将循环构建为产品基础设施,而不是提示词胶水:

ConversationManager -> QueryLoop -> ToolRuntime
  • ConversationManager 管理持久状态:会话 ID、消息、用户设置、预算和持久化。
  • QueryLoop 管理单个任务回合:调用模型、检测工具调用、执行工具、追加工具结果并重复。
  • ToolRuntime 管理已注册的工具:架构、权限检查、执行和错误格式化。

以 ReAct 作为思维模型:

Thought -> Action -> Observation -> Thought -> Answer

将其实现为结构化 API 流量:

model thinking/text -> tool_call -> tool_result -> next model call -> final text

实现工作流

  1. 检查用户的技术栈和当前 LLM 调用位置。 找到消息的构建位置、模型的调用位置,以及是否已配置工具/函数调用。

  2. 引入最精简的查询循环。 让首个版本保持精简:一个模型调用函数、一个工具注册表和明确的退出条件。

  3. 统一消息格式。 如有可用的提供方结构化工具调用格式,请使用该格式。除非提供方没有函数/工具调用 API,否则避免解析自由格式的 Action: 文本。

  4. 增加工具执行安全措施。 根据架构验证工具输入,对有风险的工具应用权限检查,将失败封装为工具结果,并记录每次调用。

  5. 在扩展功能前添加退出和预算保护。 始终包含 maxTurns、超时/取消支持、令牌/费用预算检查,以及致命错误处理路径。

  6. 将上下文窗口策略留在此技能之外。 将 messages 作为循环输入并返回更新后的 messages,但将裁剪、检索、摘要和压缩交由单独的上下文管理层处理。

最简循环

根据用户使用的语言和 SDK 调整此结构:

async function runQueryLoop({
  initialMessages,
  model,
  tools,
  maxTurns = 10,
  signal,
}: {
  initialMessages: Message[]
  model: ModelClient
  tools: ToolRegistry
  maxTurns?: number
  signal?: AbortSignal
}) {
  let messages = [...initialMessages]

  for (let turn = 1; turn <= maxTurns; turn++) {
    if (signal?.aborted) return { status: "aborted", messages }

    const response = await model.generate({
      messages,
      tools: tools.definitions(),
      signal,
    })

    messages.push(response.message)

    const toolCalls = extractToolCalls(response.message)
    if (toolCalls.length === 0) {
      return {
        status: "completed",
        finalMessage: response.message,
        messages,
      }
    }

    for (const call of toolCalls) {
      const result = await tools.execute(call, { signal, messages })
      messages.push(makeToolResultMessage(call.id, result))
    }
  }

  return { status: "max_turns", messages }
}

“模型继续判断”这一步并非独立函数。循环在追加 tool_result 消息后再次调用模型时,这一步就会发生。

必需的退出条件

发布前实现以下条件:

  • 正常完成:模型响应中没有工具调用。
  • 达到最大回合数:经过固定数量的模型-工具循环后停止。
  • 中止/超时:用户取消请求或请求超出截止时间时停止。
  • 权限被拒:根据产品的安全策略停止,或返回工具错误。
  • 超出预算:令牌、费用或运行时间预算耗尽时停止。
  • 致命工具错误:工具故障无法由模型纠正时停止。

优先返回结构化的终止原因:

type TerminalReason =
  | "completed"
  | "max_turns"
  | "aborted"
  | "timeout"
  | "permission_denied"
  | "budget_exceeded"
  | "fatal_tool_error"

工具运行时契约

每个工具都应定义:

type Tool = {
  name: string
  description: string
  inputSchema: unknown
  risk: "read" | "write" | "execute" | "external"
  validate(input: unknown): ValidatedInput
  canUse(input: ValidatedInput, ctx: ToolContext): Promise<PermissionDecision>
  call(input: ValidatedInput, ctx: ToolContext): Promise<ToolResult>
}

执行顺序:

find tool by name
-> schema validate model input
-> run tool-specific validation
-> check permission
-> call tool
-> format success or error as tool_result

如果模型有可能恢复,请将工具错误返回给模型,例如参数无效、文件未找到、搜索结果为空或临时 API 错误。对于安全违规、重复失败、缺少凭据或预算耗尽,应停止循环。

产品设计指南

让模型负责“智能”,让代码负责“可靠性”:

  • 让模型决定是否需要再次调用工具。
  • 让代码强制执行架构、权限、预算和循环退出条件。
  • 让提示词聚焦于工具语义和任务策略。
  • 让实现聚焦于确定性的控制流。

对于简单的 AI 应用,起初应避免使用子 Agent、worktree 和流式工具执行。只有在产品确实需要并行工作、隔离或长时间运行的任务时,才添加这些功能。

何时需要更多细节

在设计新的查询引擎、审查现有实现,或向其他工程师解释从 ReAct 到查询循环的架构时,请阅读 references/query-loop-patterns.md。

评论

支持 Markdown