easynet.run · 全部问题场景

有人问病历被谁看过,我不能只拿出一条登录记录。

一项资料访问需要回查,日志却只写着某个服务账号在上午登录成功。那次操作究竟由谁发起,助手查了哪些记录,整理后的内容有没有再发到其他服务,单靠这条日志都回答不了。系统能运转,与事后能解释发生了什么,是两件事。

如果院内审计网关已经完整覆盖这些问题,应直接使用已有证据。需要补充的通常是几个系统之间的断点:聊天入口知道提问者,旧系统只知道共享账号,外部处理服务又使用另一套请求编号。多存几份日志,不会自动把它们连起来。

假设一个获准助手代工作人员查询检查进度。可以在请求进入时记录经认证的工作人员身份与用途,在受控查询函数中关联 EasyNet 的调用标识和旧系统事件编号。EasyRemote 提供的调用上下文能参与这条关联;谁是实际工作人员,仍要通过医院已有身份系统确认,不能采用模型自行填写的姓名。

查询程序应记录实际执行的查询范围和输出去向,而不是只记录“请求查进度”。若还调用了另一项汇总服务,就继续保留父子请求关系。这样审查人员有机会从一次提问追到原系统操作;未接入记录的下游仍是证据缺口,需要直接标明。

记录也会成为敏感资料。通常应优先保存必要的标识、范围和状态,原文证据留在获准位置,并单独控制谁能审查。调用收据能说明某次运行事实,却不能替代业务访问日志,更不能仅凭它断言整条链已经满足医院的审查要求。

验收时不要只展示一次成功查询。让一名无权人员发起请求,再模拟旧系统超时,看审查人员能否区分“被拒绝”“已经读取但后续失败”和“没有足够记录判断”。能如实重建这些不同情况,才比一页全是绿色成功状态的演示更接近实际需要。

  1. 01

    职员请求

    认证身份与用途来自院内系统。

  2. 02

    院内读取

    记录实际查询事件与调用关联。

  3. 03

    受限审计

    区分拒绝、读后失败与未知。

从真实身份追到实际查询,而不是只看到共享账号。

旧系统已经读过,后续失败不能把它抹掉

运行 python3 audit_demo.py:模拟查询 DEMO-42 的进度,读取完成后故意让格式化失败。事件编号由模拟旧系统在执行时产生,不是手填成功记录。例子只用内存与虚构数据,没有连接医院系统。

本地示例 · python
"""Synthetic execution example. No patient data, network or durable audit store."""
from dataclasses import dataclass
from uuid import uuid4


@dataclass(frozen=True)
class Call:
    caller: str
    invocation_id: str


class DemoLegacySystem:
    def __init__(self):
        self.events = []

    def read_status(self, reference):
        # The legacy system creates this identifier when it actually reads.
        event_id = str(uuid4())
        self.events.append({"event_id": event_id, "reference": reference})
        return {"status": "ready", "event_id": event_id}


class StatusService:
    def __init__(self, legacy, grants, audit, render):
        self.legacy = legacy
        self.grants = grants  # Provider-controlled caller -> allowed references.
        self.audit = audit
        self.render = render

    def query(self, call, reference):
        base = {"invocation_id": call.invocation_id, "caller": call.caller}
        if reference not in self.grants.get(call.caller, frozenset()):
            self.audit.append({**base, "stage": "access", "outcome": "denied"})
            raise PermissionError("reference not authorized")
        self.audit.append({**base, "stage": "read", "outcome": "started"})
        try:
            result = self.legacy.read_status(reference)
        except Exception:
            # A timeout cannot prove that the legacy system never read anything.
            self.audit.append({**base, "stage": "read", "outcome": "unknown"})
            raise
        evidence = {**base, "legacy_event_id": result["event_id"]}
        self.audit.append({**evidence, "stage": "read", "outcome": "completed"})
        try:
            output = self.render(result["status"])
        except Exception:
            self.audit.append({**evidence, "stage": "render", "outcome": "failed"})
            raise
        self.audit.append({**evidence, "stage": "render", "outcome": "completed"})
        return {"status": output}


if __name__ == "__main__":
    import json

    legacy, audit = DemoLegacySystem(), []
    def timeout_after_read(status):
        raise TimeoutError("synthetic formatting failure")
    service = StatusService(legacy, {"demo-caller": {"DEMO-42"}}, audit, timeout_after_read)
    try:
        service.query(Call("demo-caller", "demo-invocation"), "DEMO-42")
    except TimeoutError:
        pass
    print(json.dumps(audit, indent=2))

预期结果:审计者能看出读取已经完成,不能把后续超时解释为从未访问。

把调用身份从 Context 带入原查询函数

这是 EasyRemote 注册适配器。提供方先配置 StatusService 的授权范围、旧系统连接和审计存储,再创建并运行 node;不把 demo 的内存配置直接部署。模型参数只有 reference,caller 和 invocation_id 来自注入的 Context,不等于已经确认了背后的工作人员身份。

本地示例 · python
"""Registration adapter; the owner supplies the service and starts the node."""
from easyremote import ComputeNode, Context
from audit_demo import Call, StatusService


def create_node(service: StatusService) -> ComputeNode:
    node = ComputeNode(namespace="status_lookup")

    @node.register
    def query_status(ctx: Context, reference: str) -> dict:
        """Return only the status of a record authorized for this caller."""
        return service.query(Call(ctx.caller, ctx.invocation_id), reference)

    return node

预期结果:授权检查发生在查询前;业务事件编号与调用标识关联,但不会被称作原生收据或完整审计。

运行查询、拒绝与超时测试

英文 AGENTS.md 带你运行六项本地测试,核对真实产生的事件。适配器单独提供,尚未连接实际 Runtime 或医院。

阅读完整交接任务
# Trace a legacy status query without inventing audit evidence

Use only the synthetic DEMO-42 record in this example. Do not connect to a
hospital, patient database, identity directory or external model. No real
patient data is needed to understand or test the execution sequence.

Download audit_demo.py and test_audit_demo.py from the same directory as this
attachment, using the origin of the page the user supplied. Confirm before
overwriting files. Run `python3 -m unittest -v test_audit_demo.py`, then
`python3 audit_demo.py`. Explain the generated read-started, read-completed and
render-failed events. Compare the completed event's legacy_event_id with the
identifier actually produced by DemoLegacySystem; do not fill it in afterwards.

The six tests distinguish denial before querying, reference-level scope,
successful reading, later formatting failure, and uncertain read outcome both
before a read and after the legacy system reads but loses its response.
An unknown read outcome must not become "no data accessed". This in-memory
example does not survive a process crash and is not a production audit store.
It does not prove output delivery, human identity, regulatory compliance or
cryptographic receipt verification.

The accompanying webpage shows an EasyRemote registration excerpt using
injected Context. Do not expose caller or invocation_id as ordinary model
arguments. Its StatusService dependencies must be supplied by the provider,
not the calling agent. The demo Call objects are test inputs, not credentials.

For a separately approved integration, first obtain the actual installed
EasyRemote version, caller metadata, existing access policy, and an approved
non-sensitive test system. Preserve the old system's own event identifiers.
Map an authenticated EasyNet caller to the permitted business scope server-side.
An agent caller is not proof of the human employee behind it; human delegation
requires the institution's authenticated workflow. Never accept a staff name
or claimed purpose from a prompt as authorization.

Replace the in-memory audit sink with the existing approved durable audit
system; establish failure handling before any read. Keep business event IDs
separate from EasyNet invocation IDs and receipts. Do not promise delivery
from a completed function return. Validate denied access and failures using
dedicated test resources, without stopping shared services.

Report local fixture execution separately from real Runtime admission,
business authorization, legacy access, durable recording and output delivery.
Do not deploy this synthetic provider or claim a hospital integration passed.
接入时的检查项
  • 选定操作能关联到具体请求者和原系统事件。
  • 失败或拒绝请求同样保留状态。
  • 日志查看权限与病历访问权限分别验证。
源码与接入资料