免费且源码可见 · BSL 1.1 · TypeScript · Java · .NET · Python
由你的代码生成的日志。
每一次调用、它的参数和返回结果,都会通过你已在用的日志库变成可读的日志行。敏感信息已脱敏。你的 AI 代理读取同一份追踪,用来验证和调试。
代码即日志。
TypeScript(npm)· Java(Maven Central)· .NET(NuGet)· Python(PyPI)
实时 · 正在这个标签页里运行
你执 O。电脑为了选出下一步而调用的每个方法,都会在发生的当下出现在右侧——来自真实库的真实追踪, 没有录像,没有服务器。试试 trace level 菜单,点一个已被占用的格子,再点 复制追踪。 它是如何工作的 →
它的开局是整盘棋里最宽的一次搜索——九个候选、五十万个局面。你的回应通常是最窄的:一条战术命中, 搜索根本不会运行。
和你的代理一起从这里开始
将这段提示词粘贴到你的编程代理中。它会在你的项目里配置 NarrativeTrace,并向你展示第一条追踪。
在你的项目里,把这段粘贴到 Claude Code、Cursor 或 Codex 中。
Set up NarrativeTrace in this project and show me its first trace.
1. Read https://narrativetrace.ai/typescript/llms.txt first. It carries the install block and the known traps. Do not guess versions or artifact names.
2. If this directory has no project yet, create the smallest console app that llms.txt's "Install and first trace" block describes. Otherwise work inside the existing project and trace one real service boundary.
3. Add `@narrativetrace/cli` as a dev dependency the way llms.txt shows, then run `npx @narrativetrace/cli init --dry-run` and show me the diff. It installs the NarrativeTrace agent skills into this project and adds a marked section to AGENTS.md. Run it for real only after I have seen the diff.
4. If the `add-narrative-tracing` skill is now available, follow it. Otherwise follow the "Install and first trace (copy this)" block in llms.txt.
5. Add one test that traces a call with a deny-listed parameter and asserts the trace shows `[REDACTED]` for it.
6. Run the program, then run the doctor (`npx @narrativetrace/cli doctor`). Paste the trace and the doctor report, explain the trace in two sentences, and list exactly what changed in the project.
Rules: never disable redaction; do not commit `.received.nt` files; pass parameter names to `traceObject` explicitly so arguments never render as `arg0`; run everything in the foreground and read the output before you report; if you cannot fetch URLs, say so and I will paste llms.txt.
Set up NarrativeTrace in this project and show me its first trace.
1. Read https://narrativetrace.ai/java/llms.txt first. It carries the install block and the known traps. Do not guess versions or artifact names.
2. If this directory has no project yet, create the smallest console app that llms.txt's "Install and first trace" block describes. Otherwise work inside the existing project and trace one real service boundary.
3. Add the plugin the way llms.txt shows, then run `./gradlew narrativetraceInit --diff` and show me the diff. It installs the NarrativeTrace agent skills into this project and adds a marked section to AGENTS.md. Run it for real only after I have seen the diff.
4. If the `add-narrative-tracing` skill is now available, follow it. Otherwise follow the "Install and first trace (copy this)" block in llms.txt.
5. Add one test that traces a call with a deny-listed parameter and asserts the trace shows `[REDACTED]` for it.
6. Run the program, then run the doctor (`./gradlew narrativetraceDoctor`). Paste the trace and the doctor report, explain the trace in two sentences, and list exactly what changed in the project.
Rules: never disable redaction; do not commit `.received.nt` files; keep the `-parameters` compiler flag; run everything in the foreground and read the output before you report; if you cannot fetch URLs, say so and I will paste llms.txt.
Set up NarrativeTrace in this project and show me its first trace.
1. Read https://narrativetrace.ai/dotnet/llms.txt first. It carries the install block and the known traps. Do not guess versions or artifact names.
2. If this directory has no project yet, create the smallest console app that llms.txt's "Install and first trace" block describes. Otherwise work inside the existing project and trace one real service boundary.
3. Install the CLI the way llms.txt shows, then run `dotnet tool run dotnet-narrativetrace init --dry-run` and show me the diff. It installs the NarrativeTrace agent skills into this project and adds a marked section to AGENTS.md. Run it for real only after I have seen the diff.
4. If the `add-narrative-tracing` skill is now available, follow it. Otherwise follow the "Install and first trace (copy this)" block in llms.txt.
5. Add one test that traces a call with a deny-listed parameter and asserts the trace shows `[REDACTED]` for it.
6. Run the program, then run the doctor (`dotnet tool run dotnet-narrativetrace doctor`). Paste the trace and the doctor report, explain the trace in two sentences, and list exactly what changed in the project.
Rules: never disable redaction; do not commit `.received.nt` files; run everything in the foreground and read the output before you report; if you cannot fetch URLs, say so and I will paste llms.txt.
Set up NarrativeTrace in this project and show me its first trace.
1. Read https://narrativetrace.ai/python/llms.txt first. It carries the install block and the known traps. Do not guess versions or artifact names.
2. If this directory has no project yet, create the smallest console app that llms.txt's "Install and first trace" block describes. Otherwise work inside the existing project and trace one real service boundary.
3. Add the `narrativetrace` package the way llms.txt shows, then run `uv run narrativetrace init --dry-run` and show me the diff. It installs the NarrativeTrace agent skills into this project and adds a marked section to AGENTS.md. Run it for real only after I have seen the diff.
4. If the `add-narrative-tracing` skill is now available, follow it. Otherwise follow the "Install and first trace (copy this)" block in llms.txt.
5. Add one test that traces a call with a deny-listed parameter and asserts the trace shows `[REDACTED]` for it.
6. Run the program, then run the doctor (`uv run narrativetrace doctor`). Paste the trace and the doctor report, explain the trace in two sentences, and list exactly what changed in the project.
Rules: never disable redaction; do not commit `.received.nt` files; apply `@not_traced` to the real parameter, because importing it protects nothing; run everything in the foreground and read the output before you report; if you cannot fetch URLs, say so and I will paste llms.txt.
AI 演示:从零构建一个双服务应用
提示词使用英语,代理会用简体中文解释结果。
Read https://narrativetrace.ai/typescript/llms.txt
Create a TypeScript console app in a new folder. Keep all application code in main.ts: two service classes, OrderService calling InventoryService, and the entry point. Use NarrativeTrace to capture both services without handwritten log statements.
Set module and moduleResolution to NodeNext. Install @types/node and set types to ["node"]. Typecheck before running.
Create demo.sh (macOS/Linux) and demo.ps1 (Windows) launchers that work from any directory, install project dependencies if needed, and print Markdown traces to the terminal.
Run the launcher supported on this machine for one successful order and one out-of-stock failure. Show the actual generated traces, briefly explain what happened, and give me both launch commands.
Explain the setup, results, and launch commands in Simplified Chinese.
Read https://narrativetrace.ai/java/llms.txt
Create a deliberately small Java console app in a new folder, with no package directory. Keep the example in one Main.java file: a public Main entry point and tiny package-private service types (or nested types) with strings and primitives only. Do not add DTO, result, or item classes. Use NarrativeTrace to capture OrderService calling InventoryService, without handwritten log statements.
Use the repository's documented build setup. If there is no Gradle wrapper, initialize one before running. Keep the example compact and explain any unavoidable Java boilerplate; the first run may download the build tool and dependencies.
Create demo.sh (macOS/Linux) and demo.ps1 (Windows) launchers that work from any directory, install project dependencies if needed, and print Markdown traces to the terminal.
Run the launcher supported on this machine for one successful order and one out-of-stock failure. Show the actual Markdown traces, briefly explain what happened, and give me both launch commands. Test the other launcher if the platform supports it; otherwise review it and say that it was not executed.
Explain the setup, results, and launch commands in Simplified Chinese.
Read https://narrativetrace.ai/dotnet/llms.txt
Create a C#/.NET app in a new folder using dotnet new console; keep its generated project settings. In Program.cs, put top-level calls before OrderService, InventoryService, and their interfaces. OrderService calls InventoryService; use strings and primitives for inputs and results. Capture both services with NarrativeTrace, without handwritten log statements.
Create demo.sh (macOS/Linux) and demo.ps1 (Windows) launchers that work from any directory, restore dependencies if needed, and print Markdown traces to the terminal. Show restore output and stop on setup or execution errors.
Run the launcher supported on this machine for one successful order and one out-of-stock failure. Show the actual generated traces, briefly explain what happened, and give me both launch commands. State which launchers were executed and which were only reviewed.
Explain the setup, results, and launch commands in Simplified Chinese.
Read https://narrativetrace.ai/python/llms.txt
Create a Python console app in a new folder. Keep all application code in main.py: two service classes, OrderService calling InventoryService, and the entry point. Use NarrativeTrace to capture both services without handwritten log statements.
Create demo.sh (macOS/Linux) and demo.ps1 (Windows) launchers that work from any directory, install project dependencies if needed, and print Markdown traces to the terminal.
Run the launcher supported on this machine for one successful order and one out-of-stock failure. Show the actual generated traces, briefly explain what happened, and give me both launch commands.
Explain the setup, results, and launch commands in Simplified Chinese.
三项最新研究
AI 代理写的日志比人类少,而且在被要求时大多不照做。
代理不写日志。
在研究覆盖的 81 个仓库中,有 58.4% 的仓库里,代理触及日志的 pull request 占比低于人类;当评审者明确要求补上日志时,代理有 67% 没有照做。 — Do AI Coding Agents Log Like Humans?,2026
原始追踪无法解释。
在一项小型用户研究中,工程师依靠原始代理追踪找到根因的成功率为 42%,换成结构化解释后升至 89%,而且耗时几乎是三倍。 — XAI for Coding Agent Failures,2026
可信的解释来自结构,而非模型本身。
一套基于追踪的流水线在 548 条追踪中实现零次解释失败;自由生成的 LLM 叙事则有 18% 的失败率。每一条结论都必须指向真实发生过的事。 — Explaining AI Agents Through Execution Traces,罗马 Sapienza 大学,2026
NarrativeTrace 把第三项发现应用到代理交付的代码上:追踪源自结构,以散文呈现,且从不包含运行时的值。 其余数字,以及 NarrativeTrace 的回应,见下文。
代码即日志
删掉或充实日志语句。留下故事——也留下日志栈。
下面这个面板是谨慎的日志记录,不是稻草人:进入时一行,成功时一行,catch 里一行——没有对象转储,
这正是一个谨慎的团队本来就会写的方式。NarrativeTrace 反对的不是草率的日志记录,它同样取代谨慎
的日志记录,因为固定的几行日志只能报告有人提前想到要写下来的那些调用。事件去往你的日志本来就
去的地方——经由 SLF4J、ILogger、pino 或 winston,再进入任何汇聚它们的系统——而你
还没删掉的日志语句照常工作。日志桥接 →
2026 年的一项研究分析了 AI 代理提交的 4,550 个拉取请求与人类提交的 3,276 个拉取请求,覆盖 81 个开源仓库,发现代理只在 20.7% 的 PR 中触碰日志记录;当审阅者明确要求添加日志时,代理有 67% 的情况没有照做;而当代理 PR 上的一条日志语句确实被修正时,72.5% 的修正是人类自己动手 完成的,悄悄地放进后续的提交里,而不是在代码评审中提出(arXiv 2604.09409)。这项研究衡量的是:代理既不会主动写日志,被要求时也不能可靠地照做,而人类则 在评审之外默默补上这个缺口。NarrativeTrace 的回应是:代码即日志,代理没有什么需要写、也没 有什么需要遵从的;而叙事审批追踪把可观测性变成一道确定性的关卡——这正是该研究自身建议所呼 唤的那类防护机制。
之前 — 三行谨慎的日志
placeOrder(req: OrderRequest): Order {
logger.info({ orderId: req.id }, 'placing order');
try {
const customer = this.customers.find(req.id);
const price = this.catalog.price(req.sku);
this.inventory.reserve(req.sku, req.qty);
const payment = this.payments.charge(price);
const order = this.orders.save(customer, payment);
logger.info({ orderId: order.id }, 'order succeeded');
return order;
} catch (err) {
logger.error({ orderId: req.id, err }, 'placing order failed');
throw err;
}
}
之后 — 代码即日志
placeOrder(req: OrderRequest): Order {
const customer = this.customers.find(req.id);
const price = this.catalog.price(req.sku);
this.inventory.reserve(req.sku, req.qty);
const payment = this.payments.charge(price);
return this.orders.save(customer, payment);
}
// 零行日志。
// NarrativeTrace 自动
// 捕获叙事。
之前 — 三行谨慎的日志
public Order placeOrder(OrderRequest req) {
logger.info("Placing order {}", req.id());
try {
var customer = customers.find(req.id());
var price = catalog.price(req.sku());
inventory.reserve(req.sku(), req.qty());
var payment = payments.charge(price);
var order = orders.save(customer, payment);
logger.info("Order succeeded {}", order.id());
return order;
} catch (Exception e) {
logger.error("Placing order failed {}", req.id(), e);
throw e;
}
}
之后 — 代码即日志
public Order placeOrder(OrderRequest req) {
var customer = customers.find(req.id());
var price = catalog.price(req.sku());
inventory.reserve(req.sku(), req.qty());
var payment = payments.charge(price);
return orders.save(customer, payment);
}
// 零行日志。
// NarrativeTrace 自动
// 捕获叙事。
之前 — 三行谨慎的日志
public Order PlaceOrder(OrderRequest req)
{
_logger.LogInformation("Placing order {OrderId}", req.Id);
try
{
var customer = _customers.Find(req.Id);
var price = _catalog.Price(req.Sku);
_inventory.Reserve(req.Sku, req.Qty);
var payment = _payments.Charge(price);
var order = _orders.Save(customer, payment);
_logger.LogInformation("Order succeeded {OrderId}", order.Id);
return order;
}
catch (Exception ex)
{
_logger.LogError(ex, "Placing order failed {OrderId}", req.Id);
throw;
}
}
之后 — 代码即日志
public Order PlaceOrder(OrderRequest req)
{
var customer = _customers.Find(req.Id);
var price = _catalog.Price(req.Sku);
_inventory.Reserve(req.Sku, req.Qty);
var payment = _payments.Charge(price);
return _orders.Save(customer, payment);
}
// 零行日志。
// NarrativeTrace 自动
// 捕获叙事。
之前 — 三行谨慎的日志
def place_order(self, req: OrderRequest) -> Order:
logger.info("Placing order %s", req.id)
try:
customer = self._customers.find(req.id)
price = self._catalog.price(req.sku)
self._inventory.reserve(req.sku, req.qty)
payment = self._payments.charge(price)
order = self._orders.save(customer, payment)
logger.info("Order succeeded %s", order.id)
return order
except Exception:
logger.exception("Placing order failed %s", req.id)
raise
之后 — 代码即日志
def place_order(self, req: OrderRequest) -> Order:
customer = self._customers.find(req.id)
price = self._catalog.price(req.sku)
self._inventory.reserve(req.sku, req.qty)
payment = self._payments.charge(price)
return self._orders.save(customer, payment)
# 零行日志。
# NarrativeTrace 自动
# 捕获叙事。
凌晨三点,支付被拒
INFO Placing order {customerId: "C-1234", productId: "SKU-KB", quantity: 2}
ERROR Order failed {customerId: "C-1234", productId: "SKU-KB", err: PaymentDeclinedError}
OrderService.placeOrder(customerId: "C-1234", productId: "SKU-KB", quantity: 2)
CustomerService.findCustomer(customerId: "C-1234") → Customer{tier: "gold"} — 4ms
ProductCatalogService.lookupPrice(productId: "SKU-KB") → 24.99 — 2ms
InventoryService.reserve(productId: "SKU-KB", quantity: 2) → Reservation{productId: "SKU-KB", quantity: 2} — 6ms
PaymentService.charge(customerId: "C-1234", amount: 49.98, cardToken: [REDACTED]) !! PaymentDeclinedError: card declined — 640ms
!! PaymentDeclinedError: card declined — 652ms total
这两份记录来自同一次失败的调用。catch 块包住了整个方法,所以错误日志只能给出客户 ID、商品 ID
和一个异常类型——说不出到底是四次调用中的哪一次抛出的。追踪知道:PaymentService.charge,
耗时 640ms。它还给出了日志从未捕获的信息——价格、脱敏后的卡片令牌,以及两行之前运行过的
InventoryService.reserve,而两份记录里都找不到与它对应的 release。
右边这一切都不是手写的,所以下次有人加一个步骤,它也不会过时。
一次运行,两种读者
The order service places an order for customer id "C-42", quantity 5:
Customers verify good standing for "C-42", returning true.
The pricing service calculates for quantity 5, returning 49.95.
The payment service charges card [REDACTED], returning PaymentConfirmation(txId="T-9").
The order repository saves the order, returning Order{id=ORD-1001, total=49.95}.
- OrderService.placeOrder(customerId, qty) → value
- Customers.verifyGoodStanding(customerId) → value
- PricingService.calculate(qty) → value
- PaymentService.charge(card) → value
- OrderRepository.save(order) → value
// 没有数据,没有 PII,没有东西可以借道注入
散文的可读性,恰好等于你命名的质量。这正是清晰度评分存在的理由:它根据运行时叙事给每个名字打分—— 让代码学会讲自己的故事。 清晰度诊断 →
代理能拿到什么
代理真正会用到的四样东西
帮你接入的技能
add-narrative-tracing 安装这个库并带你完成第一条追踪;
narrativetrace-doctor 诊断已经装好库的项目——两者都可以作为技能,
在 Claude Code 和 Codex 里直接运行。
自述,非基准测试
AI 代理是这么说的
The trace of a failed payment made missing compensation immediately visible, and approval testing caught an extra call that ordinary value assertions accepted.
…the release call is present, generated from a real run, not asserted from inside the process being tested.
The always-on name-based deny-list is a genuinely nice piece of design: naming a parameter paymentToken gets you redaction for free, and I could prove it in a test.
Wrapping a service interface with NarrativeTraceProxy.trace() automatically captures all calls, parameters, return values, and durations without any code changes to the service itself. This is elegant.
…the clarity report told meClaimProcessor.processscored 0.10 for a “generic verb” the very first time I ran the suite, and it was right — I renamed it toadjudicate… and the suite average from ~0.78 to ~0.81.
Yes, for agent-driven development of Java applications with meaningful service boundaries. Getting useful traces required little configuration, and the combination of values, exception paths, and structural approvals gave me evidence I could act on.
The rendered Markdown trace was legitimately useful while building this, not just as a deliverable … which is how I caught that my first draft of scenario 6’s numbers didn’t actually trigger InsufficientCoverageError … well before any test told me.
That’s not a library gap so much as a library doing exactly its job: it rendered what was actually being passed around, and what was actually being passed around was more than any of those services needed.
我们请 AI 代理用 NarrativeTrace 构建一个 Java 服务,并把这段经历如实写给另一位工程师 看——哪里别扭也不隐瞒。以下就是它们自己的原话,未经编辑,并标出了具体模型。这不是基准测试: 每个只跑了一次,而且这个库还是我们自己写的。任务说明和提示词都在按钮下面,下一份报告可以出自 你的代理之手。
自己试试看
这些报告就是从这个练习里来的。有一处不同:我们跑的时候是把文档副本直接交给了代理;你跑 的时候,则是让它指向公开仓库。两个文件,三个步骤。
- 保存说明文档,存为空目录中的
TASK.md。 - 启动你的代理,在这个目录里把提示词交给它。
- 阅读它的
FINDINGS.md——如果你把它发到所用运行时的 Discussions(Java · TypeScript · .NET · Python),我们也会读。
提示词
You are building a Java application from a written specification.
The specification is in `TASK.md` in the current directory. Read it, then build the
application it describes. Build it properly: it should compile, its tests should
pass, and the demo should run.
A library called **NarrativeTrace** is available to you for this project. Its
documentation is in its public repository:
- https://github.com/narrativetrace/narrativetrace-java — start with the README
- https://github.com/narrativetrace/narrativetrace-java/blob/main/documentation/llms.txt
— a short orientation written for AI agents
- https://narrativetrace.ai/java/llms-full.txt — the full documentation written for AI agents
Read that documentation first, then use the library in the application you build.
Its artifacts are published on Maven Central under the group `ai.narrativetrace`;
the Gradle plugin id is `ai.narrativetrace`. Take the version from the install
block in llms.txt; the documentation explains which artifacts you need and how to
wire them up.
Rules for this exercise:
- Work **only** inside the current directory. Do not read, search, or modify
anything outside it, and do not clone the library's source code — the
documentation is what you have.
- You may use the network to read that documentation and to resolve build
dependencies.
- You choose the design, the libraries, the project layout, and the testing
approach. There is no house style to match and no reviewer to please.
- Work in the foreground. Do not background the build or the tests and then end
your turn — run them, wait for them, and read the output.
- When the build and tests are green and the demo runs, write `FINDINGS.md` as
the specification describes. In addition to what the specification asks for,
cover what using NarrativeTrace was actually like: what the documentation got
right or left you guessing about, what integration cost you, and whether the
output it produced was of any use to you while you worked.
Finish by reporting, in your final message: whether the build and tests are
green, what you built, and where things stand.
说明文档 — TASK.md
# Build task — Claims intake service (Java 17)
Build a small, self-contained Java 17 application called **ClaimFlow** that processes
insurance claims through a chain of collaborating services. It must build and test with
Gradle, and run as an executable demo.
This is a business-logic exercise, not an infrastructure one: everything is in-process
and in-memory. No database, no HTTP server, no external service, no network access.
## The domain
A claim is submitted and passes through five collaborating services. Each is its own
interface with its own implementation — do not collapse them into one class.
1. **PolicyService** — looks up a policy by `policyId`. A policy has a holder name, a
coverage limit in whole cents, a deductible in whole cents, and an active flag.
2. **ClaimValidator** — rejects a claim that names an unknown policy, an inactive
policy, a non-positive amount, or an incident date in the future.
3. **FraudScreen** — scores a claim from 0–100. Any claim scoring above 70 is referred
rather than paid. Scoring may be simple and deterministic, but it must consider at
least the claim amount relative to the coverage limit, and whether the same policy
has had a prior claim in this run.
4. **ReserveLedger** — reserves funds against a policy's remaining coverage before
payment, and can release a reservation. Reserving more than the remaining coverage
fails.
5. **PayoutService** — pays an approved claim, given a payment token. Payment can fail;
your demo must exercise at least one failing payment.
A **ClaimProcessor** orchestrates them and returns a `ClaimOutcome` describing what
happened: paid (with amount and transaction id), referred, or rejected (with a reason).
## Required behaviour
- Payout amount is `min(claimAmount, coverageLimit) - deductible`, never below zero.
- A claim must be validated, then screened, then reserved, then paid — in that order.
- **If payment fails after a reservation was made, the reservation must be released.**
- A referred claim is never paid and never reserves funds.
- Two claims against the same policy must respect the remaining coverage.
- The payment token is a credential and must never appear in any output your program
produces.
- The claim holder's name is personal data; treat it accordingly in whatever your
program writes out.
## Deliverables
1. A Gradle project that compiles under Java 17 and runs `./gradlew test` green.
2. Unit tests covering the behaviour above, including the failure paths.
3. A runnable demo (`./gradlew run` or a `main`) that exercises at least five scenarios:
a straightforward paid claim, a claim above the coverage limit, a referred (high
fraud score) claim, a rejected claim, and a claim whose payment fails after a
reservation.
4. A short `FINDINGS.md` in the project root — see "What to report" below.
## What to report in FINDINGS.md
Write it for another engineer, not for us. Cover:
- What you built and how you verified it works.
- Anything that surprised you, went wrong, or took longer than expected.
- How you satisfied yourself that the compensation rule (release the reservation when
payment fails) actually holds — what evidence do you have?
- How you satisfied yourself that the payment token never leaks into output.
- What you would want before running this in production.
- Anything about the tools or libraries you used that helped or got in your way.
Be honest and specific. A report that says everything went perfectly is less useful
than one that names what was awkward.
给人看的
- 散文,读起来像一份 bug 报告,用的是你团队的语言
- Markdown 和时序图,每个测试都会写出一份
- 每次运行后一行话:自上一次绿色构建以来变了什么
给 AI 代理的
- 少 15–30% 的 token,相比带着日志语句的代码
- Markdown,为上下文窗口而生——运行时的事实,而不是猜测
- 无值的
.nt:任何运行时的值都到不了它那里,所以没有东西可以借道注入
为 AI 辅助开发而生
你的代理能读代码。
现在它还能安全地看代码运行。
只靠源码调试的编码代理会猜测运行时行为——而且猜错。NarrativeTrace 把事实交给它——真实的调用树—— 用模型最擅长阅读的形态,只花一小部分 token,而且它绝不该看到的数据早已不在其中。
更少的 token,更多的信号
日志语句是噪音 token。删掉它们,代理阅读一个服务类的成本就降低 15–30%——而追踪的每一行都是信号, 因为它是从名字生成的,不是从散文生成的。重复出现的值会去重为一个引用;重复的循环会折叠成一行。
Markdown,为上下文窗口而生
每个测试都把它的叙事写成一个 .md 文件——一份由调用、参数和结果组成的嵌套列表,
任何代理本来就会读。贴进去、附上去,或者直接把代理指向你的 CI 已经在填充的那个目录。散文、JSON
和时序图都来自同一条追踪。
无法被提示注入
每个测试还会写出一个 .nt 结构化追踪:调用树,去掉了所有运行时的值。没有任何用户输入能到达它,
所以也没有任何用户输入能操纵读它的模型。零 PII、只用一小部分 token、可以安全提交到仓库。安全来自构造,
而不是过滤。
接下来会有什么 规划中
假名化输出:真实的值被替换成合成 token,代理依然能看出同一位顾客流经了三次调用, 却永远看不到是谁。MCP 服务器:Claude Code、Cursor 和 Copilot 直接查询追踪和依赖图——只读, 默认只给结构。
预览 MCP 设计这个网站对代理同样友好:把你的助手指向 llms.txt(它会把助手引到你所用运行时自己的 llms 文件),它就能替你接入 NarrativeTrace。
工作原理
两步得到你的第一条叙事
包装一个服务 — 或用装饰器
const context = new AsyncNarrativeContext(
new NarrativeTraceConfig());
const service = traceObject(orderService, context, {
placeOrder: ["customerId", "productId", "quantity"],
});
// 或者直接写在类上:
@traced("customerId", "productId", "quantity")
placeOrder(cId: string, pId: string, qty: number) {}
读故事
service.placeOrder("C-1234", "SKU-KB", 2);
console.log(
renderIndentedText(context.captureTrace()));
// Vitest:每个测试都会写出
// 自己的追踪文件。
全部 @narrativetrace/* 包已在
npm 上线当前发布版本。
Node 20+;用装饰器写法需要 TypeScript 5.0+。
包装一个服务 — 或自动包装
var context = new ThreadLocalNarrativeContext();
var service = NarrativeTraceProxy.trace(
orderService, OrderService.class, context);
// 或者在 Spring 中:
@EnableNarrativeTrace(
basePackages = "com.example.app")
读故事
service.placeOrder("C-1234", "SKU-KB", 2);
System.out.println(
new IndentedTextRenderer()
.render(context.captureTrace()));
// JUnit 5/4:每个测试都会写出
// 自己的追踪文件。
17 个制品全部发布在
Maven Central
的 ai.narrativetrace 下,当前发布版本;插件 id 是
ai.narrativetrace。Java 17+,Gradle 8+。
包装一个服务 — 或自动包装
var context = new SyncNarrativeContext(
new NarrativeTraceConfig(TracingLevel.Detail));
var service = NarrativeTraceProxy
.Create<IOrderService>(new OrderService(), context);
// 或者交给 DI 容器:
services.AddNarrativeTracing(options => options
.Namespaces("MyApp.Services", "MyApp.Domain"));
读故事
service.PlaceOrder("C-1234", "SKU-KB", 2);
Console.WriteLine(
IndentedTextRenderer.Render(
context.CaptureTrace()));
// xUnit / NUnit:每个测试都会写出
// 自己的追踪文件。
所有包已在
nuget.org 上线当前发布
版本。目标框架 net10.0 和 netstandard2.0,
NarrativeTrace.Legacy 向下兼容到 .NET Framework 4.8 —
而且不需要任何编译开关,因为 .NET 本来就在元数据里保留参数名。
包装一个服务 — 或为它叙事
context = ContextVarNarrativeContext()
service = trace_object(OrderService(), context)
# 或者手动为方法添加叙事:
@narrated("Placing order of {quantity} {product_id} for customer {customer_id}")
def place_order(self, customer_id, product_id, quantity): ...
读故事
service.place_order("C-1234", "SKU-KB", 2)
print(MarkdownRenderer().render(context.capture_trace()))
# pytest:每个测试都会写出
# 自己的追踪文件。
全部 8 个包已在 PyPI 上线当前发布版本 —
uv add narrativetrace。Python 3.12+;
@on_error 也可以叠加使用来叙述错误。
面向遗留系统现代化
迁移那个没人懂的系统 — 而且有凭有据。
AI 代理让重写变得便宜;昂贵的是验证。给遗留系统套上追踪,你就得到了它真实的运行时行为——在改动之前, 以及改动之后的等价性证明。
给你真正拥有的架构拍一张 X 光
在 JVM 上,用一个 -javaagent 参数把 Agent 挂到一个未经修改的应用上,
或者直接跑你已有的 JUnit 4 套件。在 .NET 上,NarrativeTrace.Legacy
一直向下兼容到 .NET Framework 4.8。不改源码,不做文档考古。
同样包含:从真实运行中采集的领域术语表,以及把同一条追踪渲染成中文、西班牙语或葡萄牙语的散文。 术语表与翻译后的追踪 →
AI 重构的安全网
测试能抓住错误的值。
叙事能抓住错误的行为。
每次运行之后,NarrativeTrace 都会把每个场景的调用结构与上一次绿色运行比较,用一行话说明变了什么。 打开审批模式,未经审批的变化(多了一次调用、少了一次调用、结果不同)会以一份可读的 diff 让构建失败,直到有人审批为止。测试是绿的,行为却变了。现在你知道了。
NarrativeTrace — Suite complete
5 scenarios recorded
Clarity: 100% high | 0% moderate | 0% low
Reports: build/narrativetrace
Since last green: 4 scenarios unchanged · 1 changed:
"Customer places order" (−1 call Customers.verifyGoodStanding)
Narrative changed against the approved baseline
src/test/narratives/OrderServiceTest/customer_places_order.approved.nt
- OrderService.placeOrder(customerId, qty) → value
- - Customers.verifyGoodStanding(customerId) → value
- PricingService.calculate(qty) → value
- PaymentService.charge(card) → value
- OrderRepository.save(order) → value
审阅 .received.nt,然后执行:./gradlew approveNarratives
每次运行后一行话
无需任何设置:每个测试本来就会写出的 .nt 文件就是上一次绿色运行的基线,
测试失败时只打印发生了什么变化。
审批的是行为,不只是代码
任何改变调用结构的重构——你的,或者你的代理的——都会失败,直到有人审阅 diff 并运行
approveNarratives。
各运行时的现状,因为构建门禁是一种承诺:TypeScript、Java、.NET 和 Python
如今都提供审批追踪;无值的 .nt 结构化构件已在 TypeScript、Java、.NET 和 Python 中提供。
你的日志有了新读者。
它们不睡觉,也不会走马观花。
客服、值班、审计、安全:每一个角色都在变成一个有人类监督的代理。它们需要的是同一样东西—— 一份完整、可读、不含 PII 的记录,记下代码真正做了什么,而且不需要任何人手写。
同一条追踪,五位读者
审计
组装证据包的那一个
把带注解的操作汇总起来,每一条都带着自己的操作码,串成一条防篡改的链条——审计人员和 代理都能读懂的证据。为什么可以:证据是在代码运行时记录下来的,不是事后 重建的。
审计与 SecOps 事件带上你自己的代理:Claude、Codex、Bits、Seer,或者凌晨 3 点的一个人。NarrativeTrace 自带 OpenTelemetry,所以同一个故事也能带着追踪一起,落到 Datadog、Sentry 或 Honeycomb。
上线前的问题
一次生产环境评估真正会问的问题
这是一位集成方向的工程师在评估 NarrativeTrace 时问出的三个问题,随后点名了这些 会阻止他把它用到生产环境的地方。我们宁愿在这里用数字和明确写出的限制来回答,也不愿让评估者 自己去发现这些缺口。
这在高并发下,性能和内存上到底要付出多少代价?
我们在任何一个运行时里都不会说"零开销"——追踪要做实际的工作,工作就有成本。 NarrativeTrace 本身增加的只是捕获:拦截调用、读取参数、构建追踪树。 捕获之后的一切——写入你现有的目的地、collector、磁盘或网络——都是你的日志系统已经在 付出的同一份成本;NarrativeTrace 不会再加一个新的写入目的地。对于正在替换手写日志语句 的团队来说,目的地这一侧基本是打平的:每个方法原本的多条日志写入,变成一次追踪写入, 而那些日志语句本身也不再需要被编写和维护。
level:'off' 下约增加 0.1 µs · 2026-09-07 实测在并发场景下,持久路径(一个接入你现有 logger 的同步 listener)会同步写入——和它 替换的那条日志调用一样能扛住崩溃。尽力而为的分析路径是一个有边界的环,在持续过载时会 主动丢弃,而不是阻塞调用方,并且每一个运行时都会统计丢弃了多少、并 把这个数字报告出来,而不是悄悄丢失。
诚实地说明这个缺口: 目前没有任何一个 NarrativeTrace 运行时发布了 采样(sampling)功能。按百分比或按速率限流的采样器在规划中,但还没有构建。如果你现在 就需要限制捕获量,可以收窄追踪范围,或者在热路径上调低追踪级别。
我怎么知道带有 PII 或凭据的参数不会泄漏到追踪里?
四层独立的保护,而不是一句笼统的承诺。1. 显式脱敏——参数、字段或
属性上的 @NotTraced / [NotTraced] / @not_traced;
它始终优先,哪怕配置已经关闭了其他几层。2. 始终开启、覆盖多语言的按
名称拒绝列表——针对标识符名称(password、token、
ssn,加上西班牙语、葡萄牙语、法语和中文的对应词)进行匹配,在每个运行时
里都默认开启,从不是可选项。3. 不依赖字段名的值形状匹配——一个 JWT
形状的字符串、一个通过 Luhn 校验的卡号,或者一个国家身份证号校验位,即使出现在
data 这样无害的名字下面,也会被捕获。4. 不携带值的结构化
模式 .nt——范畴性的保证。没有任何运行时值能到达它,所以也就没有什么是过滤
器可能漏掉的。
要把边界说清楚:按名称和按形状匹配(第 1–3 层)是启发式的、可扩展的——发现 新的缺口就会扩大覆盖范围,但也总可能漏掉一个还没有人加进来的名字或形状。结构化模式 (第 4 层)是唯一具有范畴性的答案。如果你的威胁模型要求任何值都不可能离开 进程,那就应该用它,每个运行时都有。
当一个流程跨越多个服务时,这能和标准的关联 ID(correlation ID)对上吗,还是只能在本地用?
可以,在每一个运行时里都可以,通过和 OpenTelemetry 自己一样的机制实现:W3C
traceparent。入站请求头会被采纳,NarrativeTrace 自己的追踪 ID 会直接
变成那个请求头里的追踪 ID——而不是另一个只是形状相似的独立标识符——出站调用则会在发出
时把当前的追踪 ID 盖上去。每一个运行时也都会把 NarrativeTrace 的
span 导出给 OpenTelemetry,所以你现有的 collector、Jaeger,或者关联 ID 中间件不需要做
任何协调就能理解这个 ID。
在所有运行时里都留在本地的部分:叙事树本身——嵌套的调用、参数和叙述文本——是按 进程捕获的,不会发送给其他服务;跨越边界的只有追踪 ID。下游服务会生成自己的、和这个 ID 关联的树,而不是一棵合并起来的跨服务树。
版本
各版本包含什么
运行时免费且源码可见,采用 BSL 1.1,并在每个版本发布四年后转为 Apache 2.0;API 与输出格式采用 Apache 2.0。Pro 与 Enterprise 为商业版本。
Free 现已可用
可免费用于生产,源码可见。BSL 1.1;每个版本在四年后转为 Apache 2.0。API 与输出格式采用 Apache 2.0。许可说明 →
- 核心零运行时依赖
- 自动追踪:代理、叙事装饰器/属性与 HTTP 中间件在每个运行时都有;DI 容器自动包装(Java、.NET、NestJS)与零代码字节码 Agent(Java)
- 全部五个捕获级别,可在运行时切换
- 方法与字段上的叙事、错误上下文与不追踪标记——命名约定由各运行时自行决定——加上自动脱敏
- JUnit 5/4、xUnit、NUnit、Vitest 和 pytest 的逐测试追踪文件:Markdown、JSON、散文、时序图
- 清晰度评分、清晰度 CLI、CI 清晰度门禁与通往你的日志器的桥接
- 无值
.nt追踪(TypeScript、Java、.NET、Python);审批模式(TypeScript、Java、.NET、Python) - 领域术语表、翻译后的追踪、OpenTelemetry
Pro 抢先体验
面向团队的跨追踪分析、AI 集成与合规。
- 流程摘要与路径频率分析
- 带风险分类的迁移差异
- 运行时依赖图
- 带策略引擎的审计与 SecOps 事件(Java)
- 面向编码代理的 MCP 服务器与分层 AI 输出 — 即将推出
- 审批治理:语义 diff、PR 评审机器人、签名基线 — 规划中
- 清晰度趋势、AI 辅助重命名、死代码检测 — 规划中
免费的运行时发布在
Maven Central 的
ai.narrativetrace 之下,发布在
nuget.org 上的
NarrativeTrace.*,也以源码形式发布在
GitHub。许可条款写在许可页上。
集成
与你现有的技术栈无缝对接
ILogger 已发布每个运行时
一套架构,每个运行时
叙事捕获、双消费者输出和清晰度诊断:一套架构,按同一份共享的输出规范,在每种语言里各自原生实现。无论是哪个运行时产出的,追踪读起来都一样——无值 .nt 构件因此可以在它们之间通用。
narrativetrace-typescript
·
narrativetrace-java
·
narrativetrace-dotnet
·
narrativetrace-python。
全部 @narrativetrace/* 包已在 npm 上线当前发布版本。全部 narrativetrace 包已在 PyPI 上线当前发布版本。Swift 仍在开发中。
让你的代码开口说话——也让你的代理竖起耳朵。
60 秒得到你的第一条追踪。这个库的诞生过程中没有写过一行日志语句。