Dayward AI
Week 1 · D7About 6 hours

Productionizing and Retrospective: Writing Evals for Tools, Versioning, Publishing to npm and a Registry, Observability, and a Capstone Project

Turn a server that runs into a server you can maintain long-term: write an eval set for your tools, manage description changes as breaking changes, add tracing and metrics, and turn the week into one portfolio entry.

Today's goals 0/3

Sign in to tick these off and save your progress.

Today's Goals

  1. Write a re-runnable eval set for a tool, and name the three categories of cases it should cover
  2. Judge whether a change is breaking, and explain why a description change counts as one too
  3. Add tracing and key metrics to a server, and say which one to check first when something goes wrong

The last day. After six days you hold a server that runs, works remotely, can be connected by your own client, and has been through a security checklist. Today answers the final question: how does it survive the next six months. Come back and tick off the three goals above.

Plain-Language Walkthrough

Tools need evals too

Start with an incident so real it is almost boring.

A server had been live three months with not a line of tool code changed when somebody edited search_docs's description from "search the internal documentation library by keyword" to "search documents" — on the grounds that it was too wordy. Nothing alerted after the deploy: unit tests green, zero server errors, clean logs. Two weeks later somebody reported that the assistant had got dumber lately and kept saying it did not know when asked about documentation.

The cause was that the model stopped selecting the tool. It never once failed a call, because it was never called.

That is MCP tools' most counter-intuitive property: whether the model selects it goes wrong earlier than whether the tool itself is correct, and no traditional test covers the former. A unit test checks that given arguments produce the right output; break the description and the arguments never reach your function at all.

So a tool needs another layer of testing, generally called an eval. Its shape is simple: give one user sentence and a tool table, see which tool the model picked (or that it picked none), and compare against the expectation.

How to write an eval set

An eval set has one design point: all three categories of case are mandatory.

CategoryWhat it testsSuggested share
PositiveSelected when the intent is explicitfour tenths
BoundaryStill selected when intent comes from meaning rather than keywordsthree tenths
TrapNothing is called when nothing should bethree tenths

Everybody writes the first two, and the third is the watershed.

An eval set of positives alone gives you the illusion of 100%: it cannot detect calling when nothing should be called, and most production complaints are exactly that — the user says offhand "thanks, no need to look in the docs" and the assistant goes and searches the docs. That is over-triggering, and it hurts the experience more than a missed call because it has side effects.

How do you pick trap cases? Find phrasings adjacent in topic but different in intent. "Thanks, no need to search the docs" (contains search and docs, intent is negation), "how do you say documentation in French" (contains documentation, is a language question), "what can you do" (small talk, and calling a tool is wrong).

Then the assertion. There is one assertion and it judges in both directions:

eval.ts
// When a tool is expected, only selecting it passes; when no call is expected, only selecting nothing passes.
// Treating null as "anything goes" is the most common authoring error in an eval set — it makes negatives
// permanently green, which is worse than having none, because you believe you tested.
export function judge(expected: string | null, actual: string | null): boolean {
  return expected === null ? actual === null : actual === expected
}
 
// All three categories are mandatory, and this throws rather than logging a warning:
// an eval set is a line of defense, and a broken defense must stop the line
export function loadCases(raw: { cases: EvalCase[] }): EvalCase[] {
  for (const kind of ['happy', 'edge', 'trap'] as const) {
    if (!raw.cases.some((c) => c.kind === kind)) throw new Error(`the eval set has no ${kind} cases`)
  }
  return raw.cases
}

One practical recommendation: an eval set must run in one command and finish in under a minute. A slow eval set is no eval set, because whoever edits a description will not wait. Run it on a cheap small model such as claude-haiku-4-5-20251001; picking a tool does not need the most expensive one.

Versioning: changing a description is more dangerous than renaming a field

Look at how the protocol versions itself first. MCP's version number is of the form YYYY-MM-DD, with an unusual meaning: it marks the date of the last breaking change. Backward-compatible changes do not advance it, so one version number can keep evolving for a while. Deprecation has an explicit policy too: a feature marked deprecated stays in the spec for at least twelve months (at least ninety days even through the expedited path) before it may be removed.

That practice is worth copying. Your server should have its own list of what counts as breaking, and the definition given in the official material on extension evolution can be used directly: removing or renaming a field, changing a field's type, changing the semantics of existing behavior, and adding a required field are all breaking.

But a tool has one more category than an ordinary API, and it is the one most easily missed:

Changing one sentence of a description is a behavioural change.

The incident above already explained why: the description is the model's only basis for selecting a tool. Renaming a field makes callers error immediately — an error is loud and somebody finds you within five minutes. Changing a description errors nothing; it quietly drops the selection rate by a few points and surfaces two weeks later as "it has got dumber lately." A loud error is far easier to handle than a quiet degradation, so a description change needs a gate more than anything else.

The gate is the eval set from the last section: a changed description must run it, and a dropped selection rate means no merge.

Compatibility technique at the field level is the same as for any API, with two MCP-specific rules:

  • Add fields as optional, because older clients will not send new arguments.
  • Change the name when changing semantics, rather than editing in place. Mark the old one deprecated, name the replacement in its description, and leave it for a while before deleting — you do not know how many people hard-coded that name into a prompt.

The approach for extensions follows the same thinking: prefer a capability flag or a version field in the extension's settings when a change is needed, and when a breaking change is unavoidable, use a new extension identifier.

Publishing: npm and the registry are two different things

Clear up a relationship most people get wrong the first time: the official registry stores metadata only, not artifacts.

Artifacts live in a package registry — npm, PyPI, Docker Hub. The official MCP registry stores a server.json: what this server is called, where to find it (which npm package, or which remote address), how to start it, and which environment variables it needs. It is positioned for downstream aggregators (the various MCP marketplaces) to consume rather than for hosts to query directly.

So publishing is publish the package first, then register, and the order cannot be reversed.

Three things must line up, and getting one wrong means rejection:

LocationFieldConstraint
package.jsonmcpNameMust equal server.json's name
server.jsonnameReverse domain plus a slash, such as io.github.username/docs
server.jsonversionUnique, immutable once published, and not a version range

The namespace is verified by ownership: authenticating with GitHub, the name must start with io.github.yourusername/; using your own domain goes through DNS verification. That is also the registry's main defense against impersonation.

That version rule needs care: one version number can be published only once, and metadata cannot change after publication. One typo means bumping to a new version. And strings that look like ranges — ^1.2.3, 1.x, >=1.2.3 — are rejected outright, which is a deliberate safety catch. Semantic versioning is recommended, and a local server should keep server.json's version aligned with the package version so the two do not disagree.

A few things about the package itself: it needs an executable entry point (bin), or users cannot start your server with npx; files must include the build output directory; and the README's first screen must state what it is, how to install it, and which environment variables it needs — the reader's budget is three minutes and they do not intend to clone and run it.

Observability: check the error rate first, then the selection rate

Once a server is live there are only a handful of questions you need answerable. Few, accurate metrics beat a pile of unwatched curves.

Four metrics suffice:

  1. Calls per tool — which tool carries the load and which was never called (a tool never called should either be deleted or has a description problem).
  2. Error rate per tool — and count the two error classes separately: a protocol error is your bug and a tool execution error is the model supplying bad arguments. Persistently high rates of the latter usually mean not that the model is dim but that your schema descriptions are unclear.
  3. Latency distribution per tool — look at P95, not the mean. On a remote server, a tool degrading from 200 milliseconds to 8 seconds may barely move the mean.
  4. Selection rate — the eval set's score, run regularly and watched as a curve.

The investigation order is fixed too: check the error rate first, then the selection rate. A normal error rate with users complaining usually means it is not being selected; only a spiking error rate sends you to the code and upstream.

For tracing, the protocol provides the place without prescribing the rules: a request's _meta is an open field whose keys must carry a prefix (a reverse domain, the same naming rule as extension identifiers). So passing trace context through inside your own _meta key is both conformant and general.

tracing.ts
// Client: put the trace context into a _meta key under your own namespace.
// The key must carry a prefix (reverse domain), the same naming rule as extension identifiers
const TRACE_KEY = 'com.example/trace'
 
function withTrace(params: Record<string, unknown>, traceparent: string) {
  return { ...params, _meta: { ...(params._meta as object), [TRACE_KEY]: { traceparent } } }
}
 
// Server: pull it out and attach your span; start a fresh one when absent rather than erroring on a missing field
function startSpan(params: Record<string, unknown>, toolName: string) {
  const meta = params._meta as Record<string, { traceparent?: string }> | undefined
  const parent = meta?.[TRACE_KEY]?.traceparent
  return tracer.startSpan(`tools/call ${toolName}`, { parent })
}

One last thing to trip over: on stdio a server's logs may only go to stderr. To view metrics, open a separate HTTP endpoint rather than printing to stdout — only protocol messages belong there, and one extra character makes the client fail to parse.

The extension ecosystem: three pieces beyond the core

There is a layer beyond the spec called extensions. They are optional, identified by a reverse domain such as io.modelcontextprotocol/, negotiated in both sides' capability declarations, and off by default and enabled explicitly. When one side supports an extension and the other does not, the supporting side either falls back to core behavior or errors explicitly.

Three official ones exist today, each solving something the core protocol does not:

  • The authorization extension: the machine-to-machine client credentials flow and centralized access control for enterprises. The core OAuth set assumes a user sitting at a browser, and these two cover "no user" and "there is an IT administrator."
  • Application interfaces (MCP Apps): letting a server render an interactive interface in the host — charts, forms, players. A tool returning text has limits.
  • Tasks: long-running asynchronous tasks with polling, supplemental input midway, and durable handles. A twenty-minute job should not occupy one request.

One more is on the way: distributing skills over MCP, currently a working group and not yet a published official extension. To use skills today, the route remains their own directory convention — which is another course's home turf.

To decide whether to touch extensions, one sentence: look only when the core protocol cannot solve it and you genuinely have that problem. Extensions being off by default is reasoned, and every one you enable adds a layer of compatibility burden.

Retrospective: what you hold after this week

Put this course back on the map first. Three things in agent engineering get conflated constantly, separated in one line:

MCP handles the wiring, Skills handle the experience, and context engineering handles the trade-offs.

MCP solves how tools and data get wired in; Agent Skills solves turning working experience into reusable capability; context engineering solves what belongs in the context window. The three do not conflict and cannot substitute for one another. This week you learned the first.

Then count the spoils. After seven days you should hold these:

DayOutputThe line for a resume
D1A field-annotated session logCan read the protocol, not just call an SDK
D2A weather and currency stdio serverCan design schemas and tool descriptions
D3A note library resource serverCan do URI templates, pagination, and notifications
D4A Streamable HTTP server with container deploymentHand-wrote the remote binding, understands the stateless trade-off
D5A hand-written client aggregating several serversWrote both sides, knows where the boundaries are
D6A security checklist and one reproduced attackCan explain prompt injection and the confused deputy
D7A publishable template plus an eval scriptHas a production mindset, not merely something that runs

The suggestion for a portfolio is to combine D4's server, D5's client, and D7's eval and publishing configuration into one repository, with an architecture diagram, a quick start that runs in three commands, and a key design decisions section in the README. That last section is the only part nobody can copy and where interviewers pick their follow-ups — every decision must state what was given up.

As for every number, write only what you measured yourself and note the conditions. "Two replicas locally, offline mode, 9 of 9 self-checks green" is plain and stands up; write one unmeasured scale figure and you invalidate not just that line but every genuine number you gave.

Source Reading

Hands-On Lab

🧪 D7 lab: a publishable MCP server template with a matching tool eval script

Code location: labs/mcp-7days/day-07-publishable-server-template

Acceptance criteria:

  1. All 4 exercise points in starter are completed, and SELFTEST=1 MOCK=1 pnpm start shows 7 of 7 self-checks green with exit code 0
  2. The eval set has at least 9 cases across all three categories, with the assertion fixed before the negatives are added — the deliberately-wrong-selector column in item 5 must read 0
  3. Item 6 shows all three paths recorded: a nonexistent tool, isError true, and a thrown exception
  4. Item 7 reports the mismatch with server.json's name after you deliberately break mcpName
  5. Both pnpm build and npm pack --dry-run succeed, with dist in the manifest

The template's three tools are deliberately confusable: search, read by slug, and create. The lines between them rest entirely on a few sentences of description, which makes them a perfect target for an eval set. Run as-is, starter shows 3 green and 4 red.

  1. Run the solution's eval once to see what the score table looks like, particularly why those three trap cases should be judged as no call.
  2. In the starter, fix judge's assertion first and then add the negatives — reverse the order and every negative you add will be falsely green.
  3. Add metrics for all three paths to the server's tools/call branch, not omitting the isError-true one.
  4. Complete the publish check, then deliberately break mcpName once and confirm it really does report it.
  5. Finally run pnpm build and npm pack --dry-run, and check the manifest for dist and server.json.

If you have a key, drop MOCK=1 and run the eval once against a real model, then change one sentence of a tool description and run it again — that alone makes it obvious why a description change counts as a breaking change.

Interview Questions

Today's 3 questions are in the question bank below, weighted toward designing tool evals, which changes count as breaking, and the order of investigation in production. Expand each one and read the analysis before the key points — practicing the derivation beats memorizing bullets. The high-frequency labels for the domestic and overseas markets are there so you can pick by target market.

Checklist and Tomorrow

  • Write a re-runnable eval set for a tool, and name the three categories of cases it should cover
  • Judge whether a change is breaking, and explain why a description change counts as one too
  • Add tracing and key metrics to a server, and say which one to check first when something goes wrong
  • Explain the division between npm and the official registry, and the three fields that must line up
  • All 5 acceptance criteria of the lab pass
  • Answer at least 2 of the 3 interview questions without looking at the key points

Seven days end here. Look back at day 1's question — what makes those tools usable by somebody else's program — and your answer is no longer one sentence but a server that runs, a client that connects, a security checklist you filled in, and an eval set that catches degradation.

Where to go next depends on what you lack: to turn how-to-do-things experience into something reusable, take Agent Skills; if wiring in more tools left you short of context, take context engineering. Wiring, experience, and trade-offs — with all three in hand, you can genuinely build agents.

Interview questions

  • How do you evaluate whether an MCP tool is any good, and what categories of test cases would you design?怎么评估一个 MCP 工具做得好不好?你会设计哪几类测试用例?
    Common in ChinaCommon overseasIntermediate#eval#tooling

    How to reason about it · think before answering

    1. The screen is whether you have shipped tools. 'Write unit tests' answers the wrong question: unit tests check that given arguments produce the right output, while the first thing to break on an MCP tool is the model not selecting it at all, so the arguments never reach your function.
    2. Define the target: an eval measures selection accuracy, meaning given a user utterance and a tool list, does the model pick the right tool. Correctness of the tool itself belongs to unit tests, and the two layers should not be blurred.
    3. Then the three categories, which is the direct answer. Happy path: unambiguous intent, such as 'find me the release process doc'. Edge: intent carried by semantics rather than keywords, such as 'what does release-process say', which has no verb cue and only a slug-shaped token, and is most often misread as a search. Traps: cases where nothing should be called, such as 'thanks, no need to look it up', 'how do you say document in English', and 'what can you do'. I usually weight them four, three, three.
    4. Stress that the third category is the dividing line: an eval set of only happy paths reports a comfortable hundred percent while measuring nothing about over-triggering, which is what most production complaints actually are, and over-triggering has side effects.
    5. Then the assertion, where the classic bug lives: treating an expected value of null as 'anything goes', which makes negatives permanently green. The correct assertion judges both directions. I also rerun the whole set with a deliberately wrong selector that always picks the same tool and require every negative to fail, which validates the assertion rather than the selector.
    6. Finally the engineering constraints: one command, under a minute, on a cheap small model, because a slow eval is no eval, since whoever edits a description will not wait. And report per category rather than one number: happy-path drops mean a vague description, edge drops mean the sentence distinguishing two similar tools is missing, trap drops mean the description over-claims.
    7. Likely follow-ups: when do you run it? On any change to descriptions, schemas, or the tool set, wired into CI. And what if the model changes? The eval set is a cross-model asset, so you rebaseline on a model switch, which is part of why it pays for itself.

    分析过程 · 先想清楚再作答

    1. 这题在筛「有没有真的上线过工具」。答「写单元测试」是答错了赛道——单元测试测的是给定参数输出对不对,而 MCP 工具最先出问题的地方是模型压根没选它,参数根本到不了你的函数。
    2. 先把要评估的对象说清:评估评的是**选中率**,也就是给一句用户的话和一张工具表,模型会不会选中该选的那个。工具本身的正确性归单元测试,两层不要混。
    3. 然后给三类用例,这是本题的正面回答。正例:意图明确时选得中,比如「帮我搜一下发布流程文档」。边界:意图靠语义而不是关键词,比如「release-process 这篇讲了什么」——没有任何动词提示,只有一个像 slug 的词,最容易被误判成搜索。诱导误选(负例):不该调的时候一个都不调,比如「谢谢,不用查文档了」「文档这个词英文怎么说」「你都能干什么」。占比我一般给四三三。
    4. 第三类是分水岭,要主动强调:只有正例的评估集会给你一个 100% 的假象,它测不出过度触发,而线上大多数投诉恰恰是过度触发——用户随口一句否定,助手转头就去干了,还带副作用。
    5. 接着讲断言,这里有个最常见的写法错误:expected 为 null 的用例被当成「随便都行」,于是负例永远绿。正确的断言两个方向都判:期待某个工具时选中它才算过,期待不调用时什么都不选才算过。我会额外拿一个「总是选同一个工具」的假选择器再跑一遍,要求负例全部失败——这验的不是选择器,是我的断言真的在起作用。
    6. 最后是工程约束:评估集要能一条命令跑完,一分钟以内,用便宜的小模型跑。跑得慢的评估集等于没有,因为改描述的人不会等。结果也不要只看总分,按三类分开看才有行动价值:正例掉了说明描述写糊了,边界掉了说明缺了区分相似工具的那句话,负例掉了说明描述写得太热情。
    7. 可预期的追问:什么时候跑?描述、schema、工具增删这三类改动都必须跑,把它挂进 CI。再追问会问到「模型换了怎么办」,答案是评估集是跨模型的资产,换模型时先跑一遍拿到基线,这也是它值得投入的原因之一。

    Key points

    • Evals measure selection accuracy, not tool correctness; the latter is unit-tested and the layers must not blur
    • Three categories are mandatory: happy path, edge, and traps, weighted roughly four three three
    • Assert both directions: an expected null must mean nothing was called, and validate the assertion with a deliberately wrong selector
    • One command, under a minute, scored per category, and triggered by any description or schema change

    答题要点

    • 评估评的是选中率,不是工具正确性;后者归单元测试,两层不能混
    • 三类用例缺一不可:正例、边界、诱导误选,建议四三三
    • 断言两个方向都判:期待 null 时必须什么都不选;再用故意选错的选择器验证断言本身
    • 一条命令一分钟内跑完,按类别分开看分数,描述与 schema 改动必须触发
  • When versioning an MCP server, what counts as a breaking change, and why is editing a tool description more dangerous than renaming a field?给一个 MCP 服务端做版本管理时,什么样的改动算破坏性变更?为什么说改一句工具描述比改一个字段名更危险?
    Common in ChinaCommon overseasIntermediate#versioning#tooling

    How to reason about it · think before answering

    1. The first half is common knowledge; the second half is the filter. Anyone who can explain that the description is part of the interface has actually operated tools in production.
    2. Give the four conventional categories, which the official guidance on extension evolution defines directly: removing or renaming fields, changing field types, altering the semantics of existing behavior, and adding new required fields. All four make existing implementations fail or behave incorrectly.
    3. Then add the MCP-specific fifth: editing a tool description. The description is the model's only basis for selecting a tool, so changing a sentence is a behavior change. Concretely, a server shortened 'search the internal doc library by keyword' to 'search documents', shipped no code changes, and two weeks later users reported the assistant had gotten dumber, because the model stopped choosing it.
    4. Now the crux, why it is more dangerous. Renaming a field makes callers fail loudly and someone finds you within five minutes. Editing a description raises nothing: unit tests pass, the server reports zero errors, logs are clean, and selection accuracy quietly drops a few points, surfacing weeks later as an undiagnosable 'it got worse'. Loud failures are far easier than silent degradation, so description changes need the stronger gate.
    5. Name the gate: an eval set with all three case categories, run on every description change, blocking the merge when selection accuracy drops. That is also why the eval must run fast from one command.
    6. Add the compatibility techniques: new fields must be optional, since old clients will not send them; to change semantics, introduce a new tool name rather than mutating in place, mark the old one deprecated with the replacement named in its description, and remove it only after a grace period, because you cannot know how many prompts hardcode that name.
    7. Likely follow-ups: how does the protocol version itself? MCP uses YYYY-MM-DD marking the last breaking change, backwards-compatible updates do not bump it, and deprecated features stay for at least twelve months before removal. And can you fix metadata after publishing to the registry? No: versions are unique and immutable once published, a typo costs a new version, and range-looking version strings are rejected outright.

    分析过程 · 先想清楚再作答

    1. 这题的前半句是常识题,后半句才是筛子。能把「描述也是接口」说明白的人,基本都真的运维过工具。
    2. 先答常规的四类,官方在讲扩展演进时给过定义,直接可用:删除或重命名字段、改字段类型、改变现有行为的语义、新增必填字段。这四类的共同点是会让已有实现直接失败或者行为不正确。
    3. 然后补 MCP 特有的第五类:**改工具描述**。理由是描述是模型选工具的唯一依据,改一句描述就是一次行为变更。举个具体的:某个服务端把描述从「在内部文档库里按关键词搜索」精简成「搜索文档」,代码一行没动,两周后用户反馈助手变笨了——模型不再选它了。
    4. 接着讲为什么它**更**危险,这是题眼:改字段名会让调用方立刻报错,错误是响亮的,五分钟内就有人来找你;改描述不报任何错,单元测试全绿、服务端零错误、日志干净,它只会让选中率悄悄掉几个点,最后以「最近变笨了」这种没法定位的形式浮上来。响亮的错误比安静的退化好处理得多,所以描述改动反而更需要闸门。
    5. 闸门是什么要说出来:一份三类齐全的评估集,描述改了必须跑一遍,选中率掉了就别合。这也是评估集要能一条命令快速跑完的原因。
    6. 顺带把兼容技巧补上:加字段要加成可选的,因为老客户端不会传新参数;要改语义就换个工具名而不是原地改,旧的标弃用、描述里写明替代品、留一段时间再删,因为你不知道多少人的提示词里写死了那个名字。
    7. 可预期的追问一:协议自己怎么做版本?MCP 用 YYYY-MM-DD,标的是最后一次破坏性变更的日期,向后兼容的改动不递增版本;弃用的特性至少保留十二个月才可能移除。追问二:发到注册表之后怎么改?改不了——版本号唯一且发布后元数据不可变,打错字只能往上加一个版本,而且范围形式的版本号会被直接拒收。

    Key points

    • The four usual categories: removing or renaming fields, changing types, altering semantics, adding required fields
    • The MCP-specific fifth is editing a tool description, since the description is the model's only selection signal
    • It is more dangerous because nothing fails: tests pass and logs are clean while selection accuracy silently drops
    • The gate is the eval set; new fields must be optional, and semantic changes need a new tool name plus a deprecation window

    答题要点

    • 常规四类:删除或重命名字段、改字段类型、改变现有行为语义、新增必填字段
    • 第五类是 MCP 特有的:改工具描述,因为描述是模型选工具的唯一依据
    • 它更危险是因为不报错:测试全绿、日志干净,只有选中率悄悄下滑,几周后才浮上来
    • 闸门是评估集;加字段要可选,改语义要换新工具名并给旧的一段弃用期
  • A user reports that one of your MCP tools is 'always getting it wrong' in production. In what order do you investigate?线上有人反馈某个 MCP 工具「总是调不对」。你按什么顺序排查?
    Common in ChinaCommon overseasIntermediate#observability#debugging

    How to reason about it · think before answering

    1. This tests ordering, not breadth. Anyone who opens with logs and stack traces gets asked how they know the problem is server-side at all.
    2. Step zero is translating 'getting it wrong' into three mutually exclusive symptoms, without which everything after is guesswork: it was never called, it was called with wrong arguments, or it was called correctly and returned the wrong thing. Asking whether it did nothing or did the wrong thing, or simply checking whether a call was logged, separates them.
    3. Each symptom has its own path. Never called means the description is at fault: run the eval set and see whether happy paths or edges dropped, since happy-path drops mean a vague description and edge drops mean the sentence distinguishing similar tools is missing. Wrong arguments means the schema is at fault: ambiguous field names, unstated formats, wrong required markers. The signal is a persistently high tool-execution error rate, which usually means the schema is unclear rather than the model being dumb. Only a wrong result is a code problem, and only then do unit tests and logs matter.
    4. State the metric ordering too: error rate first, selection accuracy second. A normal error rate with unhappy users almost always means the tool is not being chosen; a spiking error rate sends you to the code and upstream. Watch P95, not the mean, because a remote tool degrading from 200 milliseconds to 8 seconds barely moves an average.
    5. Volunteer two commonly missed causes. Aggregation collisions: the client is connected to several servers, two tools share a name, and the model picked the other one, so your server was never called and investigating it will never find anything. And version or caching: list results carry ttlMs cache hints, so the client may hold a stale tool list, and refresh depends on a listChanged notification.
    6. Conclusion: the chain has four links, description, schema, aggregation and caching, and implementation. Walk it in the order the model sees it, because the earliest links produce no error logs and are therefore the ones people skip.
    7. Likely follow-up: how do you keep evidence? Emit one structured log per call with tool name, an argument digest plus field names, the isError flag, duration, and whether a human confirmed the call, that last column being the only way to distinguish user intent from the model acting on its own.

    分析过程 · 先想清楚再作答

    1. 这题考的是排查的**顺序**,不是知识点的多少。上来就贴日志和堆栈的人会被追问「你怎么知道问题在服务端」。
    2. 第零步是把「调不对」翻译成三种互斥的现象,这一步不做后面全是猜:一是**没被调**(模型压根没选这个工具);二是**调了但参数错**;三是**调了参数也对,但结果不对**。问一句「那次它是没动,还是动了但做错了」,或者直接去日志里看有没有这条调用记录,就能分开。
    3. 对应三条不同的路。没被调,问题在**描述**:去跑评估集,看正例还是边界掉了;正例掉说明描述写糊,边界掉说明缺了区分相似工具的那句话。参数错,问题在 **schema**:看字段名是不是有歧义、描述里有没有写清格式、必填项是不是标对了;这类问题的信号是错误率里工具执行错误持续偏高——那通常不是模型笨,是 schema 没说清。结果不对才是代码问题,这时候才轮到单元测试和日志。
    4. 指标层面的顺序也说一下:**先看错误率,再看选中率**。错误率正常但用户说不好用,八成是选不中;错误率飙了才去看代码和上游。耗时看 P95 不看平均值,远程服务端上一个工具从 200 毫秒退化到 8 秒,平均值可能只动一点点。
    5. 还有两条容易被忽略但很常见的原因,要主动提。一是**聚合冲突**:客户端连了多个服务端,两个工具重名,模型选中的是另一个服务端的那个——这时候「你的工具」根本没被调,查你的服务端永远查不出来。二是**版本或缓存**:列表结果带 ttlMs 缓存提示,客户端可能拿着旧的工具清单;工具清单变了要靠 listChanged 通知才会重新拉。
    6. 结论:这条链上有四个环节——描述、schema、聚合与缓存、实现。**按模型看得见的顺序从前往后查**,因为越靠前的环节越不产生错误日志,也就越容易被跳过。
    7. 可预期的追问:怎么留证据?每次调用记一条结构化日志,字段里要有工具名、参数摘要与字段名、是否 isError、耗时、以及这次调用有没有经过人工确认;最后那一栏是事后区分「用户授意」和「模型自作主张」的唯一依据。

    Key points

    • First split 'getting it wrong' into never called, wrong arguments, or wrong result; the split decides where to look
    • Never called points at the description and the eval set; wrong arguments at the schema; only a wrong result at the code
    • Check error rate before selection accuracy, and read P95 rather than the mean
    • Do not miss aggregation collisions, where another server's same-named tool was chosen, or a stale cached tool list

    答题要点

    • 先把「调不对」分成没被调、参数错、结果错三种互斥现象,再决定查哪里
    • 没被调查描述并跑评估集;参数错查 schema;结果错才轮到代码与日志
    • 指标顺序是先错误率再选中率;耗时看 P95 不看平均值
    • 别漏掉聚合重名(选中的是别的服务端的同名工具)和工具清单缓存这两类原因

Comments