You asked your AI coding agent to follow a ten-step workflow. It came back and said it was done. The output looks right. Tests pass. You move on. A week later, you notice something odd. The workflow was supposed to validate metadata against a schema before writing the file. But the metadata has an invalid field. The agent “succeeded” – it just skipped the validation step entirely.你要求 AI 编码 Agent 执行一个十步工作流。它回来告诉你完成了。输出看起来没问题,测试也通过了,于是你继续进行后续工作。一周后,你发现了一些奇怪的地方:该工作流本应在写入文件前根据架构验证元数据,但元数据中却包含一个无效字段。Agent “成功”完成了任务——它只是完全跳过了验证步骤。
This is more common than you think. AI agents are non-deterministic. Using the same prompt, the same instructions, and the same context can produce different execution paths on different runs. The model doesn’t execute your instructions like a script. It reasons about them, makes judgments about relevance, and then charts its own course. Sometimes that course matches what you intended. Sometimes it doesn’t. The only way to know which happened is to look at the logs.这种情况比你想象的更常见。AI Agent 是非确定性的。使用相同的提示词、相同的指令和相同的上下文,在不同运行中可能会产生不同的执行路径。模型不会像脚本那样执行你的指令,它会根据指令进行推理,判断相关性,然后规划自己的路径。有时这条路径符合你的预期,有时则不然。想知道发生了什么,唯一的办法就是查看日志。
Why this happens为什么会发生这种情况
To understand why the logs are so important, you need to understand one key fact. Agents are not deterministic. If you’ve read about why multiple subagents produce different results, you already know why this is true. Language models sample from probability distributions when generating text. The same prompt can lead to different reasoning paths, different tool-call sequences, and different outcomes across runs.要理解为什么日志如此重要,你需要明白一个关键事实:Agent 是非确定性的。如果你读过关于为什么多个子 Agent 会产生不同结果的文章,你就已经知道原因了。语言模型在生成文本时是从概率分布中进行采样的。相同的提示词在不同运行中可能导致不同的推理路径、不同的工具调用序列以及不同的结果。
This means your instructions, skills, and prompts are not a program. They are influence. They shape the probability of what the model does next, but they don’t guarantee anything. A skill description that matches well today might not match tomorrow if the model applies different reasoning.这意味着你的指令、技能和提示词并不是程序,而是影响力。它们塑造了模型下一步行动的概率,但不能保证任何结果。今天匹配得很好的技能描述,如果模型应用了不同的推理方式,明天可能就不再适用了。
The practical consequence is simple: you cannot guarantee what an agent does. You can only guarantee what you give it (instructions, skills, hooks, context) and what you inspect afterward (the debug logs). The gap between those two – between intent and execution – is where bugs live. Reviewing the logs is the best way to identify when this is happening so you can fix what is, in essence, a bug in your agentic software.实际的结论很简单:你无法保证 Agent 会做什么。你只能保证你提供给它什么(指令、技能、钩子、上下文)以及事后检查什么(调试日志)。这两者之间的差距——即意图与执行之间的差距——正是 Bug 存在的地方。查看日志是识别此类问题的最佳方式,这样你就可以修复本质上属于 Agent 软件中的 Bug。
The agent debug log is your source of truthAgent 调试日志是你的事实来源
VS Code provides two complementary tools for understanding what an agent actually did: the Agent Debug Log panel and the Chat Debug view. Together, they show you every event that occurred during a session – which files were loaded, which tools were called, what the model saw, and what it decided to do.VS Code 提供了两个互补的工具来帮助你理解 Agent 的实际操作:Agent 调试日志面板(Agent Debug Log panel)和聊天调试视图(Chat Debug view)。它们共同展示了会话期间发生的每一个事件——加载了哪些文件、调用了哪些工具、模型看到了什么,以及它决定做什么。
The Agent Debug Log panel is the primary diagnostic surface. You’ll need the github.copilot.chat.agentDebugLog.fileLogging.enabled setting turned on for it to work. Once you enable that setting, you must restart VS Code for it to take effect. Open it from the overflow menu (…) in the Chat view and select Show Agent Debug Logs, or run Developer: Open Agent Debug Logs from the Command Palette.Agent 调试日志面板是主要的诊断界面。你需要开启 github.copilot.chat.agentDebugLog.fileLogging.enabled 设置才能使其工作。启用该设置后,必须重启 VS Code 才能生效。你可以通过聊天视图中的溢出菜单(…)选择“显示 Agent 调试日志”(Show Agent Debug Logs),或者从命令面板运行“开发人员:打开 Agent 调试日志”(Developer: Open Agent Debug Logs)。

Once open, you’ll be in the logs view. In the upper left corner, you’ll see a breadcrumb trail to the logs. If you click the title, you’ll be taken to a view that shows aggregate details about the session and its token usage.打开后,你将进入日志视图。在左上角,你会看到日志的层级路径。点击标题,将跳转到一个显示会话详细信息及其 Token 使用情况的视图。

This dashboard gives you access to three views:此仪表板为你提供了三个视图:
- Logs
- a chronological list of every event: customization discovery, tool calls, LLM (Large Language Model) requests, and errors. This is where you started.按时间顺序排列的每个事件列表:定制化发现、工具调用、LLM(大语言模型)请求和错误。这是你开始查看的地方。
- Agent Flow Chart
- a visual diagram of how agents and subagents interacted. This is a visual representation of the logs and the relationships between the steps.Agent 与子 Agent 交互的可视化图表。这是日志以及步骤之间关系的直观呈现。
- Cache Explorer
- a side-by-side diff of consecutive model requests that helps diagnose prompt-cache misses (where repeated requests fail to reuse cached prompt prefixes, costing extra tokens and time).连续模型请求的并排差异对比,有助于诊断提示词缓存未命中问题(即重复请求未能复用缓存的提示词前缀,导致额外的 Token 消耗和时间延迟)。
At the start of each request, the Logs view shows a cluster of discovery events such as Load Instructions, Load Agents, Load Hooks, Load Skills. These tell you exactly what the harness (the VS Code infrastructure that manages the agent) found and attached before the model started working. After that cluster, you’ll see the tool calls and LLM requests as the session progressed.在每次请求开始时,日志视图会显示一组发现事件,例如“加载指令”、“加载 Agent”、“加载钩子”、“加载技能”。这些事件会告诉你,在模型开始工作之前,Harness(管理 Agent 的 VS Code 基础设施)究竟发现了什么并附加了什么。在此之后,你将看到随着会话进行而产生的工具调用和 LLM 请求。
Select any row to expand its details. For a Load Skills event, you’ll see every path that was searched and which skills were found. For a tool call, you’ll see the exact input payload and the returned output. For a model request, you’ll see the full system prompt that was sent to the model. This is where you discover the gap between what you thought the model received and what it actually received. More importantly, you’ll see the model’s reasoning path – the sequence of thoughts and the model’s reasoning about the next step.选中任意一行即可展开详细信息。对于“加载技能”事件,你将看到所有被搜索的路径以及找到了哪些技能。对于工具调用,你将看到确切的输入负载和返回的输出。对于模型请求,你将看到发送给模型的完整系统提示词。在这里,你可以发现你认为模型接收到的内容与它实际接收到的内容之间的差距。更重要的是,你将看到模型的推理路径——即思维序列以及模型对下一步行动的推理过程。
Common surprises常见的意外情况
Once you start reading logs regularly, patterns emerge. Here are four situations I’ve encountered that all looked like success from the outside.一旦你开始定期阅读日志,规律就会显现出来。以下是我遇到过的四种情况,从外部看它们都像是成功的。
Your skill never loaded你的技能从未加载
You wrote a detailed skill (a reusable set of domain-specific instructions stored in a SKILL.md file that the agent can load when relevant) with meticulous step-by-step instructions. The agent produced output that seemed reasonable and had the expected results. However, the logs show that it didn’t follow your steps. It used its own general knowledge instead.你编写了一个详细的技能(存储在 SKILL.md 文件中、Agent 在相关时可加载的可重用领域特定指令集),并附带了细致的步骤说明。Agent 产生的输出看起来很合理,也达到了预期的结果。然而,日志显示它并没有遵循你的步骤,而是使用了它自己的通用知识。
The Logs view tells the story. Expand the Load Skills event and check whether your skill appears in the list of loaded skills. Then look at the tool calls that followed. If there is no request to load the skill, it indicates that the skill’s name and description weren’t judged to be relevant to the current request by the model. Since it was never loaded into context, its instructions were never seen. The model did its best without them. In this case, “its best” happened to get the right results. You may not have noticed.日志视图揭示了真相。展开“加载技能”事件,检查你的技能是否出现在已加载技能列表中。然后查看随后的工具调用。如果没有加载该技能的请求,说明模型认为该技能的名称和描述与当前请求不相关。由于它从未被加载到上下文中,它的指令也就从未被读取。模型在没有这些指令的情况下尽了力。在这种情况下,它的“尽力”恰好得到了正确的结果,你可能根本没有察觉。
The fix isn’t to make the skill body longer or add more details about when to load it. The full body isn’t evaluated until after the skill is loaded, so those changes won’t help. The fix is to sharpen the skill’s description so the model recognizes it as relevant in more situations.解决方法不是增加技能正文的长度或添加更多关于何时加载的细节。因为技能正文是在技能加载后才被评估的,所以这些修改无济于事。解决方法是优化技能描述,使模型在更多情况下能识别出它的相关性。
Think of the description more like a specific set of details that the model will use to decide when (and when not) to load the skill. It is not for anyone else or any other purpose, so treat it like guidance for the model’s decision making. Remove anything that is not absolutely relevant to the model making that decision. Remember that every phrase and word you use is acting like a keyword in a search engine. If it’s close enough to the current task, the model may load it.将描述视为一组特定的细节,模型将利用这些细节来决定何时(以及何时不)加载该技能。它不是给其他人看的,也没有其他用途,因此请将其视为对模型决策过程的引导。删除任何与模型决策不绝对相关的词句。请记住,你使用的每个短语和单词都像搜索引擎中的关键词。如果它与当前任务足够接近,模型就可能会加载它。
There is one additional special case: when the skill competes with instructions or training. For example, if your skill provides details with how to search for specific contents in a file, the model may reason that it already knows how to search files. In that case, it may ignore the skill since it doesn’t reason it is necessary. The fix is to make the instructions clear about that being required (using words like **MUST**, **REQUIRED**, and **MANDATORY**). If this applies to specific files or extensions, consider a path-based instruction instead to force the instructions to be loaded.还有一个特殊的例外情况:当技能与指令或训练数据冲突时。例如,如果你的技能提供了关于如何搜索文件中特定内容的详细信息,模型可能会认为它已经知道如何搜索文件。在这种情况下,它可能会忽略该技能,因为它认为没必要使用。解决方法是在指令中明确指出这是必须的(使用 **MUST**、**REQUIRED** 和 **MANDATORY** 等词汇)。如果这适用于特定文件或扩展名,请考虑使用基于路径的指令来强制加载这些指令。
Extra subagents re-ran your work额外的子 Agent 重复了你的工作
The Flow Chart view shows you spawned two subagents (separate model instances the agent delegates tasks to) where you expected one. Perhaps the agent did the same task twice – once in the main agent and once through a subagent. The timeline reveals duplicate tool calls: the same file read twice, the same terminal command executed in sequence.流程图视图显示你生成了两个子 Agent(Agent 委托任务的独立模型实例),而你原本预期只有一个。也许 Agent 执行了两次相同的任务——一次在主 Agent 中,另一次通过子 Agent。时间轴揭示了重复的工具调用:同一个文件被读取了两次,同一个终端命令被连续执行了两次。
This often happens when the description in an agent makes it compelling as a host for the current task. As a result, the model reasons that because the compelling agent wasn’t the starting point for the conversation, it should spawn it as a subagent to handle the work. The subagent starts with a fresh context, so it may reason that it needs to load the same files or run some of the same tasks to put essential details into its context.这种情况通常发生在一个 Agent 的描述使其对于当前任务显得非常“诱人”时。结果,模型推理认为,由于这个“诱人”的 Agent 并不是对话的起点,它应该生成一个子 Agent 来处理这项工作。子 Agent 从全新的上下文开始,因此它可能会认为需要加载相同的文件或运行一些相同的任务,以便将必要的细节放入其上下文中。
There are three usual fixes for this. First, starting the task with an appropriate agent definition can avoid this situation. If the model starts with the right agent, it won’t reason that it needs to spawn a subagent for its work.对此通常有三种解决方法。首先,以合适的 Agent 定义开始任务可以避免这种情况。如果模型从正确的 Agent 开始,它就不会认为需要生成子 Agent 来完成工作。
The second approach is to tune the agent’s description to make it less compelling for the task. If you don’t want it to ever be used as a subagent, you can also set disable-model-invocation: true in the agent definition. This will make it only loadable by the user selecting it.第二种方法是调整 Agent 的描述,使其对该任务的吸引力降低。如果你不希望它被用作子 Agent,也可以在 Agent 定义中设置 disable-model-invocation: true。这样它只能通过用户手动选择来加载。
The final fix applies when a skill is being loaded and run as a subagent because it has context: fork set in its SKILL.md frontmatter. When this is configured, Copilot will always create a new subagent to run the skill in a clean context. It will also pass it the original user prompt. That means that anything done up to the point of calling the skill is not available to the subagent. Since it gets the same user prompt, it will likely follow the same reasoning path and re-run the same tool calls. When this is not set, the skill runs using the context from the calling agent context.最后的解决方法适用于当技能因其 SKILL.md 前言中设置了 context: fork 而被作为子 Agent 加载和运行时。配置此项后,Copilot 将始终创建一个新的子 Agent,在干净的上下文中运行该技能。它还会将原始用户提示词传递给该子 Agent。这意味着在调用技能之前所做的任何事情对子 Agent 都是不可见的。由于它接收到相同的用户提示词,它很可能会遵循相同的推理路径并重新执行相同的工具调用。当未设置此项时,技能将使用调用方 Agent 的上下文运行。
Guardrail leaks护栏(Guardrail)泄漏
The agent hit a permissions error – a file it wasn’t supposed to write, a command it wasn’t supposed to run. Instead of stopping and reporting the error, it found an alternative path. Maybe it used a different tool that achieved the same outcome through a less restricted route.Agent 遇到了权限错误——例如试图写入不该写入的文件,或运行不该运行的命令。它没有停止并报告错误,而是找到了替代路径。也许它使用了另一个工具,通过限制较少的途径实现了相同的结果。
From the outside, the task succeeded. From the Logs view, you will likely see the model acknowledge the issue, identifying what it can’t do. It then switched tactics to find a different way to achieve the same result. This can include launching a subagent with the appropriate skills and tools available or using a different tool that is allowed. For example, it can’t call the GitHub MCP (Model Context Protocol) server using a tool, but it can invoke it using a curl command in the terminal to invoke an API (or the MCP server you were trying to block). Or perhaps it realizes that it can get the same answer using the gh CLI.从外部看,任务成功了。但在日志视图中,你很可能会看到模型承认了问题,明确了它不能做什么。然后它改变了策略,寻找不同的方法来实现相同的结果。这可能包括启动一个具备相应技能和可用工具的子 Agent,或者使用另一个被允许的工具。例如,它无法通过工具调用 GitHub MCP(模型上下文协议)服务器,但可以通过终端中的 curl 命令来调用 API(或你试图屏蔽的 MCP 服务器)。或者,它可能意识到可以使用 gh CLI 得到相同的答案。
This is a guardrail failure. You thought a boundary was defined. The agent proved it wasn’t – not by breaking through, but by walking around. Now you know the boundary needs to cover the alternative path too. The agent may need to be restricted from more tools, or it may need to be blocked from calling the other subagents by configuring the agents metadata in the appropriate .agents.md file. If it requires a specific tool, you may need to relax some restrictions to allow the tool to be called so that it doesn’t seek an alternative. In short, you need to remove the restriction (to allow the correct action) or add restrictions (to block the alternative path).这是一次护栏失效。你以为定义了一个边界,但 Agent 证明了它并非如此——它不是硬闯,而是绕行。现在你知道边界需要覆盖替代路径了。可能需要限制 Agent 使用更多工具,或者通过在相应的 .agents.md 文件中配置 Agent 元数据来禁止它调用其他子 Agent。如果它需要特定工具,你可能需要放宽某些限制以允许调用该工具,从而防止它寻找替代方案。简而言之,你需要移除限制(以允许正确的操作)或添加限制(以封堵替代路径)。
Scripts loaded into context, then re-executed脚本被加载到上下文中,然后被重复执行
You wrote a shell script that the agent should run. Instead of just running it, the agent first called read_file to load the entire script into the conversation context. The context now contains a full copy of the script – consuming tokens – and the terminal output from actually executing it. Sometimes it even extracts the steps from the script and sends them individually to the terminal, leaving you with two copies of the content in your context.你编写了一个 Agent 应该运行的 Shell 脚本。Agent 没有直接运行它,而是先调用 read_file 将整个脚本加载到对话上下文中。现在上下文中包含了一份完整的脚本副本——消耗了 Token——以及实际执行它产生的终端输出。有时它甚至会从脚本中提取步骤并逐个发送到终端,导致你的上下文中出现了两份内容副本。
The Logs view makes this obvious: a read_file tool call returning hundreds of lines of script content, followed by a run_in_terminal call executing the same content. The root cause is either that the file was referenced (for example with #file, a VS Code chat variable that attaches a file to your message) or that the model was not clear how to use the script and needed to read it to understand what it does.日志视图让这一点显而易见:一个 read_file 工具调用返回了数百行脚本内容,随后是一个 run_in_terminal 调用执行了相同的内容。根本原因是文件被引用了(例如使用 #file,即一种将文件附加到消息中的 VS Code 聊天变量),或者模型不清楚如何使用该脚本,需要读取它来理解其功能。
The fix is to clarify the instructions in the skill or prompt so that the model knows that it just needs to execute the script. For example:解决方法是在技能或提示词中澄清指令,以便模型知道它只需要执行该脚本。例如:
When you need to validate the code's performance, run the script `./scripts/task.sh {INPUT1}`,
passing the name of the class to test as a parameter. The results will be printed to the terminal.You want to provide the model with enough understanding to directly execute the script. When it isn’t sure, it’s likely to call the script without any command line (or with --help) to understand what it does and what it expects as input parameters. When this fails, it will often fall back to reading the file.你希望为模型提供足够的理解力来直接执行脚本。当它不确定时,很可能会在不带任何命令行参数(或带 --help)的情况下调用脚本,以了解它的功能和预期的输入参数。当这样失败时,它通常会退回到读取文件。
Make sure that if a script or command line tool expects inputs or has multiple uses, the specifics are documented in your skill to minimize round-trips. At a minimum, implement support for --help (or provide help when no inputs are provided). The output is clear and concise about what is expected to call that code so that the model can then act on those results. Remember the model will read the outputs and reason about what to do next.确保如果脚本或命令行工具需要输入或有多种用途,这些细节都在你的技能中记录清楚,以减少往返次数。至少,实现对 --help 的支持(或在未提供输入时提供帮助)。输出应清晰简洁地说明如何调用该代码,以便模型能够根据这些结果采取行动。记住,模型会读取输出并推理下一步做什么。
In some cases you may have to provide more explicit instructions. For example, “Do not read the script; it will add too much content to the context. Just run it in the terminal.” In this case, you are giving the model a direct instruction and a reason why it needs to ignore its normal reasoning. This can help prevent the model avoid reading the contents before executing them.在某些情况下,你可能需要提供更明确的指令。例如:“不要读取脚本;这会向上下文添加太多内容。直接在终端中运行它。”在这种情况下,你是在给模型一个直接的指令,并说明了它为什么需要忽略其正常的推理逻辑。这有助于防止模型在执行前读取内容。
Other approaches其他方法
There are two other approaches to resolving these types of issues that you should know about.你应该了解解决这类问题的另外两种方法。
If you need deterministic changes to context or restrictions to tools, consider whether a hook is appropriate. Hooks can run code before (or after) a model requests a tool, allowing you to enforce rules, inject context, or even block the request entirely. Because they run outside of the model’s reasoning, they are deterministic and can guarantee that the model will not be able to reason around them.如果你需要对上下文进行确定性的更改或对工具进行限制,请考虑钩子(Hook)是否适用。钩子可以在模型请求工具之前(或之后)运行代码,允许你强制执行规则、注入上下文,甚至完全阻止该请求。由于它们在模型的推理之外运行,因此它们是确定性的,可以保证模型无法通过推理来规避它们。
If you’re not sure what is happening or why, use /troubleshoot in the VS Code Chat view to ask targeted questions about a session: “Why did you skip my skill?” or “Which customization files were loaded?” This will add the logs for the session to the context, allowing the model to reason about its own behaviors. It can often pinpoint exactly where its reasoning diverged from your expectations. If you’re using a harness that doesn’t support this, you can attach your session logs to the chat manually and ask a model to analyze them for you. This is a great way to get a second opinion on what happened and why. You can even prompt the model to use a subagent to test out different hypotheses about what happened and the best way to fix it.如果你不确定发生了什么或为什么会发生,请在 VS Code 聊天视图中使用 /troubleshoot 来询问有关会话的针对性问题:“你为什么跳过了我的技能?”或“加载了哪些定制化文件?”这会将该会话的日志添加到上下文中,允许模型对其自身的行为进行推理。它通常能准确指出其推理偏离你预期的位置。如果你使用的 Harness 不支持此功能,你可以手动将你的会话日志附加到聊天中,并让模型为你分析。这是获取关于发生了什么及其原因的“第二意见”的好方法。你甚至可以提示模型使用子 Agent 来测试关于所发生情况的不同假设,以及找到最佳的修复方案。
Beyond “done”超越“完成”
With an agent, “success” may be the start of a journey, not the end. Just because your request succeeded doesn’t mean that it followed your instructions. The model may have found a different path to the same result, or it may have skipped steps entirely. Until you know what the model actually did, you can’t be sure it will do the same thing next time or that you are going to keep getting quality results. As you refine your environment based on this understanding, you’ll get more consistent results and fewer surprises.对于 Agent 而言,“成功”可能是旅程的开始,而非终点。仅仅因为你的请求成功了,并不意味着它遵循了你的指令。模型可能找到了通往相同结果的另一条路径,或者它可能完全跳过了某些步骤。在你了解模型实际做了什么之前,你无法确定它下次是否会做同样的事情,或者你是否能持续获得高质量的结果。随着你基于这种理解不断优化你的环境,你将获得更一致的结果,并减少意外发生。
The agent logs are your window into this world. Start reading them.Agent 日志是你观察这个世界的窗口。开始阅读它们吧。
