接口突然变慢,你把监控截图贴给助手。它问有没有发布,再问错误日志,随后想看上一个时间段。你在几个页面之间来回切换,忙着复制资料,还要记住这些数据是不是同一个服务、同一个环境。真正分析故障的时间,反而被取证占掉了。
如果现有监控平台已经把日志、指标和发布事件关联好,直接使用它更有效。EasyNet 不需要替代这些系统。它适合补的,是让不同系统中已经可查询的证据,按照同一组条件交给你正在使用的助手。
可以先接出三项只读方法:取得指定服务的错误摘要、读取指定时间窗的延迟指标、列出附近的发布事件。每项方法保留自己的原系统接口和权限检查,EasyRemote 将其作为函数入口提供。调用时传入共同的环境、服务和时间范围,返回结果也带上这些条件与来源。
一段收集程序再把结果放在一起,明确哪些取得了、哪些查询失败。它不能因为日志系统暂时没有响应,就把空结果解释成没有错误。时区、采样范围和数据延迟也要保留,否则几张看起来相关的图,可能根本没有覆盖同一次故障。
助手拿到这些证据后可以提出假设,修复仍要走独立入口和审批。读取错误日志不应顺便获得重启生产服务的权限。已有 incident-evidence 示例能帮助理解调用形状,真实监控连接、敏感字段处理和故障判断仍需要用团队自己的系统验证。
挑一场已有复盘的故障重放取证过程:能否一次取得同一时间窗的材料,缺失来源是否如实标明,提出的判断能否回查。哪怕不自动诊断,先省掉反复搬运和核对条件,就已经让你与助手把注意力放回真正的故障上。
- 01
锁定事故窗口
服务、环境、UTC起止时间。
- 02
只读收集
错误、延迟、发布记录。
- 03
提出待验证解释
明确缺失数据,不自动回滚。
缺失证据不能当作零错误
运行 python3 -B collector.py,查询虚构 checkout 服务在 production 环境 10:00–10:15 UTC 的证据。三个查询使用同一个时间窗,排除 staging 和 billing 的记录;延迟查询故意超时。不连接生产系统。
"""Executable synthetic incident queries; no production monitoring connection."""
from dataclasses import asdict, dataclass
from datetime import datetime, timedelta
def utc(value: str) -> datetime:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
if parsed.tzinfo is None or parsed.utcoffset() != timedelta(0):
raise ValueError("use an explicit UTC timestamp")
return parsed
@dataclass(frozen=True)
class Query:
service: str
environment: str
start: str
end: str
def __post_init__(self):
if not self.service.strip() or not self.environment.strip():
raise ValueError("service and environment are required")
span = utc(self.end) - utc(self.start)
if not timedelta(0) < span <= timedelta(hours=1):
raise ValueError("window must be positive and at most one hour")
class FixtureSource:
def __init__(self, rows):
self.rows = rows
self.queries = []
def query(self, request: Query) -> list[dict]:
self.queries.append(request)
return [dict(row) for row in self.rows
if row["service"] == request.service
and row["environment"] == request.environment
and utc(request.start) <= utc(row["at"]) < utc(request.end)]
class TimeoutSource:
def __init__(self):
self.queries = []
def query(self, request: Query) -> list[dict]:
self.queries.append(request)
raise TimeoutError("synthetic source timeout")
class Collector:
def __init__(self, sources):
self.sources = dict(sources)
def collect(self, request: Query) -> dict:
evidence = []
for name, source in self.sources.items():
item = {"source": name, "query": asdict(request)}
try:
rows = source.query(request)
except TimeoutError:
item.update(state="unavailable", error="timeout")
except Exception:
# Do not leak credentials/URLs from upstream exception messages.
item.update(state="unavailable", error="source_error")
else:
item.update(state="received", records=rows)
evidence.append(item)
return {"evidence": evidence} # No inferred cause or repair action.
def demo_collector() -> Collector:
return Collector({
"errors": FixtureSource([
{"service": "checkout", "environment": "production", "at": "2026-09-01T10:03:00Z", "event": "DEMO-E1", "code": "upstream_timeout"},
{"service": "checkout", "environment": "staging", "at": "2026-09-01T10:04:00Z", "event": "DEMO-E2", "code": "test_failure"},
{"service": "billing", "environment": "production", "at": "2026-09-01T10:04:00Z", "event": "DEMO-E3", "code": "other_service"},
]),
"deployments": FixtureSource([
{"service": "checkout", "environment": "production", "at": "2026-09-01T09:30:00Z", "event": "DEMO-D1", "release": "demo-v2"},
]),
"latency": TimeoutSource(),
})
if __name__ == "__main__":
import json
query = Query("checkout", "production", "2026-09-01T10:00:00Z", "2026-09-01T10:15:00Z")
print(json.dumps(demo_collector().collect(query), indent=2))
预期结果:errors 只返回 DEMO-E1;deployments 成功返回空列表;latency 为 unavailable。09:30 的发布在窗外,不能据此排除它与故障有关。结果是取证材料,不是根因结论。
让助手一次请求这组证据
提供方配置真实只读来源与 authorize 检查。调用者来自 Context,授权通过后才执行查询。这个适配器没有内置监控连接或权限策略,也未启动 Runtime。
"""The owner supplies read-only sources and the business authorization check."""
from collections.abc import Callable
from easyremote import ComputeNode, Context
from collector import Collector, Query
def create_node(collector: Collector, authorize: Callable[[str, str, str], None]) -> ComputeNode:
node = ComputeNode(namespace="incident")
@node.register
def collect_evidence(ctx: Context, service: str, environment: str, start: str, end: str) -> dict:
"""Collect read-only evidence for one authorized service and UTC window."""
authorize(ctx.caller, service, environment) # Must raise on denial.
return collector.collect(Query(service, environment, start, end))
return node
预期结果:一次返回三个来源各自的状态和相同查询坐标;不附带重启、回滚或自动修复。
运行取证与缺失来源测试
英文 AGENTS.md 带你运行六项本地测试;真实监控连接、授权和超时限制由提供方另行接入。
阅读完整交接任务
# Collect matching incident evidence, not three unrelated screenshots
Use this local synthetic checkout incident. Do not connect to production logs,
monitoring, deployment systems, or an external model. Download collector.py,
test_collector.py and provider.py beside this attachment using the user's supplied page
origin; do not overwrite existing work.
Run `python3 -B -m unittest -v test_collector.py` and `python3 -B collector.py`.
The request is checkout / production / 2026-09-01 10:00–10:15 UTC, start inclusive
and end exclusive. Confirm that all three query methods actually run with the
same request. The matching error is DEMO-E1. The staging and billing events
must not appear. The deployment at 09:30 is outside this window: an empty
deployment result does not prove no earlier release could be relevant.
The latency source deliberately raises TimeoutError. Its result is unavailable,
not an empty metric series or zero latency. Do not infer that the one error
caused an incident or that no deployment occurred before the query window.
Six tests cover coordinates, filtering, empty versus failed results, the end
boundary, invalid windows and sanitized error messages.
For a separately approved real integration, supply read-only adapters for the
team's existing log, deployment and metric APIs. Each adapter must enforce the
caller's approved service/environment scope, its own bounded network timeout,
pagination/result limits, redaction and data freshness. This sequential demo
does not impose a wall-clock deadline on a hung adapter. Do not treat user-
supplied service or environment strings as authorization.
To expose the collector through EasyRemote, use the registration pattern in
the current repository, with injected Context for real caller identity and a
provider-owned authorization check before calling collect. Do not publish the
unrestricted fixture collector. Keep business access checks and source API
permissions; EasyRemote registration does not supply these connectors for you.
No restart, rollback or deployment action belongs in this read-only entry.
Explain the measured facts and missing sources before proposing any hypothesis.
If a real integration is approved, verify actual query timestamps, source
identifiers, retained access controls, freshness and limits with non-sensitive
test data. Report local fixture execution separately from Runtime admission and
production connector tests. This attachment requests neither deployment nor
production fault injection.
接入时的检查项
- 每项证据对应相同服务与时间窗口。
- 数据源不可达时结果明确标注缺失。
- 只读诊断身份无法执行发布或回滚。