Files
AgentLab/backend/app/core/events.py
T
ThearchyHelios 7739340607 普通用户建应用集成收尾:
- 问数据和个人应用的图放行绑定只读查询等合规工具的调用工具节点,只读和遮罩在这条路径上生效;被治理成写入的工具在只读运行里直接拒;知识库范围在工具层固定(图级默认值、参数名纠错、Agent 都改不了)
- 个人应用的刷新和个人蓝图的试运行只有拥有者能读:运行、事件、证据、工件、文件、运行与审批列表、对比、可验证报告、质询、假设分析、提取工作流一并收紧;个人蓝图的试运行不论谁发起都按个人应用约束
- 共享蓝图只给拥有者、对应应用的管理者、管理员,看不到的一律 404;审核副本归审核人;管理员看别人的个人应用只有元数据、可以归档
- 记录表跨版本汇总(只读挂载旧版本、按当前列 UNION ALL、各版本各自固定高水位,缺的版本照实写进印章);停用前发起的运行可以继续写旧版本;更正沿用原提交人、更正人记审计
- 约定收口:待办种类 review、事件 file.extracted、表单错误 file、模板定义带示例与预置;前端类型一次补齐、去掉临时的局部类型;审核待办卡不再给打不开的链接;读附件事件有中文说法;检查脚本补断言
2026-10-15 13:55:19 +08:00

129 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import time
import uuid
from enum import StrEnum
from typing import Any
from pydantic import BaseModel, Field
class EventType(StrEnum):
"""前端与后端之间的实时协议。画布高亮、时间线、token 流全部基于这套事件。"""
# run 级
RUN_STARTED = "run.started"
RUN_FINISHED = "run.finished"
RUN_FAILED = "run.failed"
RUN_CANCELLED = "run.cancelled"
RUN_INTERRUPTED = "run.interrupted" # 等待人工介入
RUN_RESUMED = "run.resumed"
# 节点级
NODE_STARTED = "node.started"
NODE_FINISHED = "node.finished"
NODE_FAILED = "node.failed"
NODE_SKIPPED = "node.skipped"
EDGE_TAKEN = "edge.taken" # 条件分支实际走了哪条边
# 模型级
LLM_START = "llm.start"
LLM_TOKEN = "llm.token"
# 思考的流式增量:只推给画布做实时显示,不落库
LLM_THINKING_DELTA = "llm.thinking.delta"
# 一次完整思考:调用结束时落一条,这才是进轨迹的事件
LLM_THINKING = "llm.thinking"
LLM_END = "llm.end"
# 工具 / 沙箱
TOOL_START = "tool.start"
TOOL_END = "tool.end"
TOOL_ERROR = "tool.error"
# 信任档是「始终允许 · 门控把关」的工具,门控模型对这一次调用的判定(tools/trust.py)。
# allow 之后是正常的 tool.start;escalate 之后是人工审批,协作团队里则是不执行
TOOL_GATED = "tool.gated"
# 知识检索。以前只发一条 info 日志,于是"这句结论依据的是哪份文档的哪一段"
# 在轨迹里查不到——而 SQL 取数那条路早就有工件快照可以下钻
RETRIEVE_END = "retrieve.end"
# 记忆读写。以前发的是 info 日志,而解码器丢弃所有 info——于是往长期记忆里
# 写东西这件事在界面上是隐形的。让 Copilot 主动记之后,这条不可接受
MEMORY_END = "memory.end"
# 合并查询做完了:哪几个输入(别名、节点、行数、数据源)、哪条合并 SQL、得到几行、有哪些警告。
# 运行详情据此展示这一步;完整结果在 query_artifact 指向的查询快照里
MERGE_END = "merge.end"
SANDBOX_START = "sandbox.start"
SANDBOX_END = "sandbox.end"
# 多 agent 协作中的单个 agent 步骤
AGENT_STEP_START = "agent.step.start"
AGENT_STEP_END = "agent.step.end"
# 调度者的一次决策。它是一次结构化模型调用,常常占掉团队节点大半的时间;
# 以前前后都没有事件,界面上那几十秒是黑盒
AGENT_ROUTE_START = "agent.route.start"
AGENT_ROUTE_END = "agent.route.end"
# 人工介入
HUMAN_REQUESTED = "human.requested"
HUMAN_RESOLVED = "human.resolved"
# 审批质询:运行停在审批时,审批人就待审的报告问的一个问题和得到的回答(engine/run_append.py 追加,
# node_id 为空)。这次问答的用量写在事件数据的 usage 里,计进运行用量(runner._usage_of_events)。
# 停在审批时追加的随运行结束一起封存;封存之后追加的带 post_seal: true,是批注不是证据
APPROVAL_INQUIRY = "approval.inquiry"
# 出具与口径治理
ISSUANCE = "issuance" # 出具契约的判定结果(档位 / 缺数据 / 未回指数字)
CALIBER_UPGRADE = "caliber.upgrade" # 钉住的方法卡有新版本,按声明的策略处置
# 从发布版本发起的正式运行:发布之后这一版用到的表的数据目录有变化(data/catalog_impact.catalog_drift)。
# 每张表带 impact:direct 写死的 SQL 用到的,possible Agent 可能查询的。
# 只提醒、不拦运行;落在封存范围内,出具物上看得到「当时的目录和发布时不一样」
CATALOG_DRIFT = "catalog.drift"
# 报告撰写节点的自查结果:报告文档工件 id、统计、违规清单。落在封存范围内,
# 证据接口靠它找到文档——不去翻不受封存保护的 artifacts 表
REPORT_CHECKED = "report.checked"
# 探索运行里按需裁判的结论句判定:点开哪句才判哪句,追加在封存之后(post_seal),是批注不是证据。
# 封存核对只覆盖 manifest_seq 之前的事件,追加它不影响 verify;封存的报告文档一个字都不改
EVIDENCE_JUDGED = "evidence.judged"
# 应用平台(docs/design/app-platform-and-look.md §3.11)
# 运行来源:发起时写进 launch_run 的 notes,在封存范围内(engine/origin.py)
RUN_ORIGIN = "run.origin"
# 待办:一次中断面向人的那一面(app/process/tasks.py)
TASK_OPENED = "task.opened"
TASK_RESPONDED = "task.responded" # 谁、怎么决定的、第几人 / 共几人
TASK_REASSIGNED = "task.reassigned"
TASK_REMINDED = "task.reminded"
TASK_ESCALATED = "task.escalated"
TASK_EXPIRED = "task.expired"
TASK_RESOLVED = "task.resolved"
TASK_CANCELLED = "task.cancelled"
# 动作:写外部系统(app/process/actions.py)。prepared 带 payload_hash、环境、遮罩后的预览
ACTION_PREPARED = "action.prepared"
ACTION_SENT = "action.sent"
ACTION_CONFIRMED = "action.confirmed"
ACTION_FAILED = "action.failed"
ACTION_UNKNOWN = "action.unknown"
ACTION_RECONCILED = "action.reconciled"
ACTION_SIMULATED = "action.simulated"
# 普通用户建应用(docs/design/normal-user-apps.md N9、§3.8)
# 读入一个附件的文字(模板函数 file_text,app/files/text.py):哪个文件、读入多少字、全文多少字、是否截断,
# 读入的那一段存成工件(artifact)。抽不出时 ok=false、reason 写原因
FILE_EXTRACTED = "file.extracted"
# 其他
LOG = "log"
USAGE = "usage" # token / 成本累计
class RunEventModel(BaseModel):
id: str = Field(default_factory=lambda: uuid.uuid4().hex)
run_id: str
seq: int = 0
type: EventType
node_id: str | None = None
ts: float = Field(default_factory=time.time)
data: dict[str, Any] = Field(default_factory=dict)
def to_wire(self) -> dict[str, Any]:
return self.model_dump(mode="json")