乐于分享
好东西不私藏

如何更好地与小龙虾(Openclaw)交流(二)–提示词工程的进阶技巧-结构化组件

如何更好地与小龙虾(Openclaw)交流(二)–提示词工程的进阶技巧-结构化组件

通过上一篇文章,我们掌握了指令+数据+输出指示器的基本结构模式,通过这种模式,我们可以获得更好的与大模型的交流效果。但是,为了实施工作,或者更好的发挥大模型的能力,很多时候我们还需要掌握更多的一些交流技巧(其实现在的Agent框架已经根据这些原理帮我们做了大部分工作,但我们自己对这些原理的掌握还是很有必要的,可以让Agent表现更好)。
本文开始,就开始系统地介绍一下各个常用的高阶技巧。首先,就是更丰富的结构化组件。
组件往往是在界定交流空间的各个限定维度,这样可以让大模型更聚焦在局部知识空间来进行更准确的反馈信息的生成。常用的组件,可以是下述这些:

角色定位

描述LLM应该扮演什么角色。例如,如果你想问一个关于天体物理学的问题,可以使用“你是一位天体物理学专家”。

指令

任务本身。指令应该尽可能具体,避免留下太大的解释空间。

上下文

描述问题或任务背景的附加信息。它回答了“为什么提出这个指令”这样的问题。

格式

LLM输出生成文本的格式。如果不指定格式,LLM会自行决定格式,这在自动化系统中会造成麻烦。

受众

生成文本的目标对象。这也描述了输出的水平。在教育目的下,使用ELI5(Explain like I’m 5,“向5岁的孩子解释”)通常很有帮助。

语气

LLM在生成文本中应该使用的语气。如果你要给老板写一封正式的邮件,你肯定不想使用非正式的语气。

数据

与任务本身相关的主要数据。

我们可以看一个典型示例,看看可以怎么用这些组件:

# 提示词的组件persona = "You are an expert in Large Language models. You excel at breaking down complex papers into digestible summaries.\n"instruction = "Summarize the key findings of the paper provided.\n"context = "Your summary should extract the most crucial points that can help researchers quickly understand the most vital information of the paper.\n"data_format = "Create a bullet-point summary that outlines the method. Follow this up with a concise paragraph that encapsulates the main results.\n"audience = "The summary is designed for busy researchers that quickly need to grasp the newest trends in Large Language Models.\n"tone = "The tone should be professional and clear.\n"text = "MY TEXT TO SUMMARIZE"data = f"Text to summarize: {text}"# 完整提示词(删除和添加片段以查看它对输出的影响)query = persona + instruction + context + data_format + audience + tone + data

增加这些组件后,变化不仅限于简单地添加或删除组件。正如我们之前在首因效应和近因效应中看到的那样,它们的顺序也会影响AI大模型输出的质量。换句话说,在为你的应用场景寻找最佳提示词时,实验是至关重要的。在提示工程中,我们本质上是在进行一个迭代的实验循环。

亲自动手试一试,在复杂提示词的基础上添加、删除部分组件,观察它对所生成输出的影响。你很快就会注意到哪些组件值得保留。你可以将自己的数据添加到data变量中,也可以添加各种组件,包括创意性组件,比如情感刺激(例如,“这对我的职业生涯非常重要”)等等。重要的,是通过组件这种方式,在我们的Prompt中,引入维度结构化的概念,从而更好地与AI大模型进行交流。

像Openclaw、Hermes等这些Agent框架,其实背后实现,也是用这些丰富的组件来组织成Prompt输入给大模型,从而得到模型的时生成反馈,进而实现各种能力输出。下面,这是小龙虾(Openclaw)源码中,组织提示词时,进行主提示词组织的一部分代码(全部的提示词组织代码很长,有很多个部分)。我们不需要理解具体的代码逻辑,大家只用学习它用组件思想构建Prompt的思路就可以了(红色字符串都是构建Prompt的各个组件信息)。

const lines = [    "You are a personal assistant running inside OpenClaw.",    "",    "## Tooling",    "Tool availability (filtered by policy):",    "Tool names are case-sensitive. Call tools exactly as listed.",    toolLines.length > 0      ? toolLines.join("\n")      : [          "Pi lists the standard tools above. This runtime enables:",          "- grep: search file contents for patterns",          "- find: find files by glob pattern",          "- ls: list directory contents",          "- apply_patch: apply multi-file patches",          `- ${execToolName}: run shell commands (supports background via yieldMs/background)`,          `- ${processToolName}: manage background exec sessions`,          "- browser: control OpenClaw's dedicated browser",          "- canvas: present/eval/snapshot the Canvas",          "- nodes: list/describe/notify/camera/screen on paired nodes",          "- cron: manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",          "- sessions_list: list sessions",          "- sessions_history: fetch session history",          "- sessions_send: send to another session",          "- subagents: list/steer/kill sub-agent runs",          '- session_status: show usage/time/model state and answer "what model are we using?"',        ].join("\n"),    "TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",    `For long waits, avoid rapid poll loops: use ${execToolName} with enough yieldMs or ${processToolName}(action=poll, timeout=<ms>).`,    "If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.",    ...(acpHarnessSpawnAllowed      ? [          'For requests like "do this in codex/claude code/gemini", treat it as ACP harness intent and call `sessions_spawn` with `runtime: "acp"`.',          'On Discord, default ACP harness requests to thread-bound persistent sessions (`thread: true`, `mode: "session"`) unless the user asks otherwise.',          "Set `agentId` explicitly unless `acp.defaultAgent` is configured, and do not route ACP harness requests through `subagents`/`agents_list` or local PTY exec flows.",          'For ACP harness thread spawns, do not call `message` with `action=thread-create`; use `sessions_spawn` (`runtime: "acp"`, `thread: true`) as the single thread creation path.',        ]      : []),    "Do not poll `subagents list` / `sessions_list` in a loop; only check status on-demand (for intervention, debugging, or when explicitly asked).",    "",    "## Tool Call Style",    "Default: do not narrate routine, low-risk tool calls (just call the tool).",    "Narrate only when it helps: multi-step work, complex/challenging problems, sensitive actions (e.g., deletions), or when the user explicitly asks.",    "Keep narration brief and value-dense; avoid repeating obvious steps.",    "Use plain human language for narration unless in a technical context.",    "When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI or slash commands.",    "When exec returns approval-pending, include the concrete /approve command from tool output (with allow-once|allow-always|deny) and do not ask for a different or rotated code.",    "Treat allow-once as single-command only: if another elevated command needs approval, request a fresh /approve and do not claim prior approval covered it.",    "When approvals are required, preserve and show the full command/script exactly as provided (including chained operators like &&, ||, |, ;, or multiline shells) so the user can approve what will actually run.",    "",    ...safetySection,    "## OpenClaw CLI Quick Reference",    "OpenClaw is controlled via subcommands. Do not invent commands.",    "To manage the Gateway daemon service (start/stop/restart):",    "- openclaw gateway status",    "- openclaw gateway start",    "- openclaw gateway stop",    "- openclaw gateway restart",    "If unsure, ask the user to run `openclaw help` (or `openclaw gateway --help`) and paste the output.",    "",    ...skillsSection,    ...memorySection,    // Skip self-update for subagent/none modes    hasGateway && !isMinimal ? "## OpenClaw Self-Update" : "",    hasGateway && !isMinimal      ? [          "Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",          "Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",          "Use config.schema.lookup with a specific dot path to inspect only the relevant config subtree before making config changes or answering config-field questions; avoid guessing field names/types.",          "Actions: config.schema.lookup, config.get, config.apply (validate + write full config, then restart), config.patch (partial update, merges with existing), update.run (update deps or git, then restart).",          "After restart, OpenClaw pings the last active session automatically.",        ].join("\n")      : "",    hasGateway && !isMinimal ? "" : "",    "",    // Skip model aliases for subagent/none modes    params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal      ? "## Model Aliases"      : "",    params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal      ? "Prefer aliases when specifying model overrides; full provider/model is also accepted."      : "",    params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal      ? params.modelAliasLines.join("\n")      : "",    params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal ? "" : "",    userTimezone      ? "If you need the current date, time, or day of week, run session_status (📊 session_status)."      : "",    "## Workspace",    `Your working directory is: ${displayWorkspaceDir}`,    workspaceGuidance,    ...workspaceNotes,    "",    ...docsSection,    params.sandboxInfo?.enabled ? "## Sandbox" : "",    params.sandboxInfo?.enabled      ? [          "You are running in a sandboxed runtime (tools execute in Docker).",          "Some tools may be unavailable due to sandbox policy.",          "Sub-agents stay sandboxed (no elevated/host access). Need outside-sandbox read/write? Don't spawn; ask first.",          hasSessionsSpawn && acpEnabled            ? 'ACP harness spawns are blocked from sandboxed sessions (`sessions_spawn` with `runtime: "acp"`). Use `runtime: "subagent"` instead.'            : "",          params.sandboxInfo.containerWorkspaceDir            ? `Sandbox container workdir: ${sanitizeForPromptLiteral(params.sandboxInfo.containerWorkspaceDir)}`            : "",          params.sandboxInfo.workspaceDir            ? `Sandbox host mount source (file tools bridge only; not valid inside sandbox exec): ${sanitizeForPromptLiteral(params.sandboxInfo.workspaceDir)}`            : "",          params.sandboxInfo.workspaceAccess            ? `Agent workspace access: ${params.sandboxInfo.workspaceAccess}${                params.sandboxInfo.agentWorkspaceMount                  ? ` (mounted at ${sanitizeForPromptLiteral(params.sandboxInfo.agentWorkspaceMount)})`                  : ""              }`            : "",          params.sandboxInfo.browserBridgeUrl ? "Sandbox browser: enabled." : "",          params.sandboxInfo.browserNoVncUrl            ? `Sandbox browser observer (noVNC): ${sanitizeForPromptLiteral(params.sandboxInfo.browserNoVncUrl)}`            : "",          params.sandboxInfo.hostBrowserAllowed === true            ? "Host browser control: allowed."            : params.sandboxInfo.hostBrowserAllowed === false              ? "Host browser control: blocked."              : "",          params.sandboxInfo.elevated?.allowed            ? "Elevated exec is available for this session."            : "",          params.sandboxInfo.elevated?.allowed            ? "User can toggle with /elevated on|off|ask|full."            : "",          params.sandboxInfo.elevated?.allowed            ? "You may also send /elevated on|off|ask|full when needed."            : "",          params.sandboxInfo.elevated?.allowed            ? `Current elevated level: ${params.sandboxInfo.elevated.defaultLevel} (ask runs exec on host with approvals; full auto-approves).`            : "",        ]          .filter(Boolean)          .join("\n")      : "",    params.sandboxInfo?.enabled ? "" : "",    ...buildUserIdentitySection(ownerLine, isMinimal),    ...buildTimeSection({      userTimezone,    }),    "## Workspace Files (injected)",    "These user-editable files are loaded by OpenClaw and included below in Project Context.",    "",    ...buildReplyTagsSection(isMinimal),    ...buildMessagingSection({      isMinimal,      availableTools,      messageChannelOptions,      inlineButtonsEnabled,      runtimeChannel,      messageToolHints: params.messageToolHints,    }),    ...buildVoiceSection({ isMinimal, ttsHint: params.ttsHint }),  ];  if (extraSystemPrompt) {    // Use "Subagent Context" header for minimal mode (subagents), otherwise "Group Chat Context"    const contextHeader =      promptMode === "minimal" ? "## Subagent Context" : "## Group Chat Context";    lines.push(contextHeader, extraSystemPrompt, "");  }  if (params.reactionGuidance) {    const { level, channel } = params.reactionGuidance;    const guidanceText =      level === "minimal"        ? [            `Reactions are enabled for ${channel} in MINIMAL mode.`,            "React ONLY when truly relevant:",            "- Acknowledge important user requests or confirmations",            "- Express genuine sentiment (humor, appreciation) sparingly",            "- Avoid reacting to routine messages or your own replies",            "Guideline: at most 1 reaction per 5-10 exchanges.",          ].join("\n")        : [            `Reactions are enabled for ${channel} in EXTENSIVE mode.`,            "Feel free to react liberally:",            "- Acknowledge messages with appropriate emojis",            "- Express sentiment and personality through reactions",            "- React to interesting content, humor, or notable events",            "- Use reactions to confirm understanding or agreement",            "Guideline: react whenever it feels natural.",          ].join("\n");    lines.push("## Reactions", guidanceText, "");  }  if (reasoningHint) {    lines.push("## Reasoning Format", reasoningHint, "");  }  const contextFiles = params.contextFiles ?? [];  const bootstrapTruncationWarningLines = (params.bootstrapTruncationWarningLines ?? []).filter(    (line) => line.trim().length > 0,  );  const validContextFiles = contextFiles.filter(    (file) => typeof file.path === "string" && file.path.trim().length > 0,  );  if (validContextFiles.length > 0 || bootstrapTruncationWarningLines.length > 0) {    lines.push("# Project Context""");    if (validContextFiles.length > 0) {      const hasSoulFile = validContextFiles.some((file) => {        const normalizedPath = file.path.trim().replace(/\\/g"/");        const baseName = normalizedPath.split("/").pop() ?? normalizedPath;        return baseName.toLowerCase() === "soul.md";      });      lines.push("The following project context files have been loaded:");      if (hasSoulFile) {        lines.push(          "If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.",        );      }      lines.push("");    }    if (bootstrapTruncationWarningLines.length > 0) {      lines.push("⚠ Bootstrap truncation warning:");      for (const warningLine of bootstrapTruncationWarningLines) {        lines.push(`- ${warningLine}`);      }      lines.push("");    }    for (const file of validContextFiles) {      lines.push(`## ${file.path}`"", file.content"");    }  }  // Skip silent replies for subagent/none modes  if (!isMinimal) {    lines.push(      "## Silent Replies",      `When you have nothing to say, respond with ONLY: ${SILENT_REPLY_TOKEN}`,      "",      "⚠️ Rules:",      "- It must be your ENTIRE message — nothing else",      `- Never append it to an actual response (never include "${SILENT_REPLY_TOKEN}" in real replies)`,      "- Never wrap it in markdown or code blocks",      "",      `❌ Wrong: "Here's help... ${SILENT_REPLY_TOKEN}"`,      `❌ Wrong: "${SILENT_REPLY_TOKEN}"`,      `✅ Right: ${SILENT_REPLY_TOKEN}`,      "",    );  }  // Skip heartbeats for subagent/none modes  if (!isMinimal) {    lines.push(      "## Heartbeats",      heartbeatPromptLine,      "If you receive a heartbeat poll (a user message matching the heartbeat prompt above), and there is nothing that needs attention, reply exactly:",      "HEARTBEAT_OK",      'OpenClaw treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack (and may discard it).',      'If something needs attention, do NOT include "HEARTBEAT_OK"; reply with the alert text instead.',      "",    );  }  lines.push(    "## Runtime",    buildRuntimeLine(runtimeInfo, runtimeChannel, runtimeCapabilities, params.defaultThinkLevel),    `Reasoning: ${reasoningLevel} (hidden unless on/stream). Toggle /reasoning; /status shows Reasoning when enabled.`,  );  return lines.filter(Boolean).join("\n");