ENNEW Script Observability SDK
面向 Codex Python skill 的结构化观测 SDK。业务代码用接近普通日志的方式写入, SDK 自动完成 Trace/Session 关联、Langfuse 初始化、脱敏、异常栈与进程退出 flush。
最简用法
import logging
from ennew_script_observability import log, skill_entry
from ennew_script_observability import observe
logger = logging.getLogger(__name__)
@observe("provider.unpaywall")
def fetch_paper(doi: str):
logger.info("开始获取论文", extra={"provider": "unpaywall"})
return fetch_from_unpaywall(doi)
@skill_entry
def main() -> None:
log.input("脚本输入", doi="10.1000/x", provider="unpaywall")
result = fetch_paper("10.1000/x")
log.output("脚本结果", success=True)
推荐优先使用三个入口:
@skill_entry:装饰唯一入口,自动创建和结束根 Trace。@observe("稳定名称"):自动记录函数参数、返回值、耗时和异常。- Python 原生
logging:照常使用logger.info/warning/error/exception,SDK 在@skill_entry运行期间自动桥接到当前 Langfuse 节点,并保留控制台日志。
复杂流程再使用 log.input()、log.output() 和 log.step() 手动控制节点。
log.input() 和 log.output() 会自动判断当前上下文:在 log.step() 外调用时
写入根 script.run,在步骤内调用时写入当前步骤。每次写入还会生成一个轻量的
script.input/output 或 step.input/output 事件。事件在 Log View 中直接显示为
step.input · 请求参数、script.log · 开始获取论文 这类日志行;稳定事件键仍保存
在 metadata 的 event.key 字段中,便于过滤和查询。
结构化字段、纯文本和任意 JSON 值都支持:
log.input("查询参数", query="机理算法", top_k=3)
log.input("一段纯文本")
log.output("文档列表", [{"id": 1}, {"id": 2}])
log.output("模型回答", "共找到 2 篇文档")
错误也保持日志式写法:
# 业务错误:自动关联当前 step,并更新根 Run 状态
log.error("资源不存在", code="PAPER-404", recoverable=False, doi=doi)
# 已捕获代码异常:在 except 块中调用,SDK 自动读取当前异常和 stacktrace
try:
fetch()
except TimeoutError:
log.exception("首次请求超时,准备重试", provider="arxiv", attempt=1)
固定 API
| API | 语义 | Langfuse 映射 |
|---|---|---|
@skill_entry |
脚本生命周期 | 根 tool observation / 独立 Trace |
@skill_entry(capture_args=True, capture_result=True) |
自动记录入口参数和返回值 | root input/output |
@observe(name, kind=..., capture_args=..., capture_result=...) |
自动观测同步或异步函数 | child observation |
logger.info/warning/error/exception(...) |
Python 原生日志 | 当前节点 event;消息直接写入 event input |
log.input(message?, value?, **fields) |
当前上下文输入,每个节点最多一次 | root/child input + 轻量 event |
log.output(message?, value?, **fields) |
当前上下文输出,每个节点最多一次 | root/child output + 轻量 event |
log.step(name, kind="tool", **fields) |
有耗时的业务步骤 | child observation |
log.result(**fields) |
兼容旧 API,始终写根结果 | root output |
step.output(**fields) |
兼容旧 API,写当前步骤结果 | child output |
log.debug/info/warning(message, event=..., **fields) |
结构化业务日志 | event |
log.error(message, code=..., recoverable=..., **fields) |
业务失败 | WARNING/ERROR event + 状态 |
log.exception(message, **fields) |
except 中的代码异常 |
ERROR event + stacktrace |
event 可省略,SDK 会按日志级别使用稳定默认名。只有需要按业务事件单独筛选时才传。
kind 只在步骤确实属于 retriever 或 generation 时设置,默认 tool。
value 与 **fields 二选一,不能在同一次调用中混用。
原生日志的消息和 extra 字段会合并写入 Event Input:
logger.info("检索完成", extra={"count": 3, "provider": "crossref"})
try:
fetch()
except TimeoutError:
logger.exception("请求超时")
在 Langfuse 中分别显示为 INFO · 检索完成 和 ERROR · 请求超时。第一条日志的
Input 是 {"message":"检索完成","count":3,"provider":"crossref"};Metadata
只保留 event.key、logger 名称和级别。异常类型、异常消息与 stacktrace 也保存在
Metadata。
Langfuse 自动初始化
SDK 与 Codex Langfuse 插件读取同一配置,优先级由低到高:
- SDK 默认值;
~/.codex/langfuse.json;<cwd>/.codex/langfuse.json;- 环境变量。
示例配置:
{
"enabled": true,
"public_key": "pk-lf-...",
"secret_key": "sk-lf-...",
"base_url": "https://langfuse.example.com",
"environment": "cloud-codex",
"user_id": "codex-cloud",
"tags": ["codex", "skill"],
"metadata": {"deployment": "cloud"},
"debug": false,
"fail_on_error": false
}
环境变量同时支持插件专用名和 Langfuse 标准名;前者优先:
| 插件专用名 | 标准名 |
|---|---|
LANGFUSE_CODEX_PUBLIC_KEY |
LANGFUSE_PUBLIC_KEY |
LANGFUSE_CODEX_SECRET_KEY |
LANGFUSE_SECRET_KEY |
LANGFUSE_CODEX_BASE_URL |
LANGFUSE_BASE_URL |
LANGFUSE_CODEX_ENVIRONMENT |
LANGFUSE_TRACING_ENVIRONMENT |
LANGFUSE_CODEX_TAGS |
LANGFUSE_TAGS |
LANGFUSE_CODEX_METADATA |
LANGFUSE_METADATA |
业务脚本不需要读取配置文件、传凭据或创建 Langfuse 客户端。
执行上下文
执行层只需给子进程注入:
CODEX_THREAD_ID:Langfusesession_id,云端使用时必需;CODEX_TURN_ID/CODEX_CALL_ID:推荐,写入 metadata;ENNEW_SCRIPT_TRIGGER_SOURCE:skill/agents_md/manual;SKILL_NAME/SKILL_VERSION:skill 标识。
ENNEW_SCRIPT_OBSERVABILITY_STRICT=true 或配置中的 fail_on_error=true 会在缺少
上下文、凭据或初始化失败时直接报错;默认模式则 fail-open,不影响业务脚本。
开发与验证
python3.12 -m venv .venv
.venv/bin/pip install -e . pytest
.venv/bin/python -m pytest tests/unit
.venv/bin/ennew-script-observability lint ../local-e2e/skills/ennew-observability-mock/scripts
本地构建和发布到 PyPI 的完整流程见 PUBLISHING.md。
Metadata
Release files for ennew-script-observability 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ennew_script_observability-0.3.0.tar.gz | 30.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ennew_script_observability-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 67.5 kB
Release files / ennew_script_observability-0.3.0.tar.gz
| Download URL | ennew_script_observability-0.3.0.tar.gz |
|---|---|
| Size | 30.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
de11c1e815796b89e2b6a928d8c3279b2ed2d2f5aeb4a5b42dcd864431908ad8
|
|
BLAKE2b-256 checksum How to use checksums |
6cfacb8a283acec7c69d9bc4507c870d88dde49e61500ba126797f40172fec84
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / ennew_script_observability-0.3.0-py3-none-any.whl
| Download URL | ennew_script_observability-0.3.0-py3-none-any.whl |
|---|---|
| Size | 37.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
95d61ce6ad36c170af0d8090fc9fff0bf34c58dffd68e778a837c2b942fac959
|
|
BLAKE2b-256 checksum How to use checksums |
8d6b2c9dbc684580ed7bd80e503e9471ee912996e126575c90937620e68eec25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|