Metadata-Version: 2.4
Name: sagax-amadeus
Version: 3.0.0
Summary: Sagax Amadeus —— 投研审计智能体的公网 API 客户端（薄 HTTP SDK）
License-Expression: Apache-2.0
Keywords: audit,investment-research,llm,agent,compliance,financial-data,mcp,api-client,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Natural Language :: Chinese (Simplified)
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Sagax Amadeus

投研审计智能体 **Sagax Audit** 的 Python 客户端。Apache-2.0 开源。

```bash
pip install sagax-amadeus
```

```python
from sagax_amadeus import SagaxAuditClient

client = SagaxAuditClient(
    base_url="https://audit.example.com",
    api_key="sagax_sk_...",
)

result = client.audit(
    task="核对 2025 年报的营业收入、归母净利润、ROE 与 PE",
    candidate_output=my_output,
    evidence=my_evidence,
    required_fields=["revenue", "net_profit", "roe", "pe"],
)

print(result.status)                       # PASS / RETRY / BLOCK / NEED_HUMAN
for f in result.findings():
    print(f.severity, f.field, f.reason)
client.download_report(result.audit_id, "audit_report.md")
```

零运行时依赖，只用标准库。Python 3.10+。

---

## 这个包做什么、不做什么

**做**：拼请求、发 HTTPS、读响应、下载产物。

**不做**：审计。没有校验器、没有修复循环、不生成报告、不生成 Audit Bundle、
不在本地存私有知识库。全部审计逻辑在 Sagax Audit Cloud 上执行。

远程失败时**不会**回落到本地跑一遍 —— 没有本地引擎可回落，这是有意的：
两套引擎会给出两套结论，而审计结论的价值全部来自「只有一套」。

## 为什么客户端开源、引擎不开源

产品是两层：

| | 授权 | 在哪跑 |
|---|---|---|
| **客户端**（本包，`sagax_amadeus`） | Apache-2.0，开源 | 你的进程里 |
| **审计引擎**（`sagax_audit_cloud`） | 订阅服务 / 私有化授权 | 云端或你的机房 |

客户端开源不是姿态，是必要条件：这个包会拿到你的 API Key、读到你的原始
Evidence、决定什么东西被发到网上。**这种位置上的代码，你有权逐行读它。**
包里没有混淆、没有二进制、没有遥测，`grep` 一遍就能确认它只往你配置的那个
地址发请求。

引擎那一半是产品本身（确定性校验器、修复循环、公共规则内容），按订阅授权。

## 想确认它到底往哪发数据

不用信这段话，自己验：

```bash
python3 -c "import sagax_amadeus, os; print(os.path.dirname(sagax_amadeus.__file__))"
grep -rn "urlopen\|Request(" $(python3 -c "import sagax_amadeus,os;print(os.path.dirname(sagax_amadeus.__file__))")
```

全部出站请求都经 `transport.py` 一个地方，目标只有你设的 `base_url`。

## 数据去向

用得着说清楚，所以说清楚：

- 你上传的**原始 Evidence** 会经 HTTPS 传到 Sagax Audit Cloud；
- 你的**私有 Memory / Skill / LLM Wiki / 私有规则**也存在云端；
- 它们绑定你的租户，其他租户检索不到、读不到、改不到、删不到；
- 默认**不用于训练模型**；
- 你的私有资源**不会自动**变成公共资源 —— 贡献到公共库要显式确认，且仍需人工审核；
- 非本机地址一律要求 HTTPS（明文 HTTP 会被客户端直接拒绝）。

需要数据完全不出机房的部署形态，联系我们谈私有化 —— 那是另一套交付，
不是这个包的默认行为。

## 常用调用

```python
# 上传证据（结构化，最常用）
ev = client.upload_evidence_items([
    EvidenceItem(field="revenue", value=85.6, unit="亿元", period="2025A",
                 source="2025年年度报告", source_ref="ann:2025A#p12"),
])

# 上传附件（年报 PDF、导出的 CSV…）
att = client.upload_evidence(path="fy2025.pdf")

# 异步：创建 → 轮询 → 取结果
job = client.create_audit(task="…", evidence_ids=[ev.evidence_id],
                          required_fields=["revenue"])
result = client.wait_for_audit(job.audit_id, timeout=300)

# 只审不修：拿裁决 + RepairPlan，自己去改
result = client.check(task="…", candidate_output=my_output)
plan = result.repair_plan()
print(plan.locked_fields, plan.fields_to_regenerate)

# 产物
client.download_report(job.audit_id, "report.md")
client.download_bundle(job.audit_id, "bundle.tar.gz")
events = client.get_trace(job.audit_id)

# 私有知识（全部远程）
client.create_memory(title="PE 口径事故", body="…", kind="error_case")
client.create_skill(name="估值口径检查", body="…")
client.create_wiki_document(slug="pe-caliber", title="内部 PE 口径", body="…")
client.create_boundary({"rule_id": "priv.no_forecast_pe", "title": "…",
                        "tier": "P1", "validator": "forbid_forecast_basis",
                        "applies_to": ["pe"]})

# 用量
usage = client.usage()
print(usage.audits_used, usage.remaining)
```

异步版本同名：

```python
from sagax_amadeus import AsyncSagaxAuditClient

async with AsyncSagaxAuditClient() as client:
    job = await client.create_audit(task="…")
    result = await client.wait_for_audit(job.audit_id)
```

## 错误处理

状态码到异常的映射是稳定契约，按类型分支，不要按数字：

| 异常 | 状态 | 含义 |
|---|---|---|
| `AuthenticationError` | 401 | Key 缺失 / 无效 / 已吊销 |
| `QuotaExceededError` | 402 | 配额用尽 |
| `PermissionDeniedError` | 403 | 订阅停用；请求里的 tenant 与 Key 不符 |
| `NotFoundError` | 404 | 资源不存在，**或不属于你的租户** |
| `ConflictError` | 409 | 状态冲突（如审计没跑完就取结果） |
| `PayloadTooLargeError` | 413 | 上传超限 |
| `ValidationError` | 422 | 请求体不合法 |
| `RateLimitError` | 429 | 限流 |
| `ServerError` | 5xx | 服务端错误 |
| `APIConnectionError` / `APITimeoutError` | — | 网络层 |
| `AuditFailedError` / `AuditTimeoutError` | — | 任务失败 / 等待超时 |

404 同时表示「不属于你」是有意的：返回 403 等于确认「这个 id 存在」，
那本身就是一次跨租户信息泄露。

**API Key 不会出现在异常、日志、`repr()` 或 URL 里**，只在请求头里。

重试：幂等方法（GET/HEAD/PUT/DELETE）在 429/5xx/连接错误时自动重试并退避；
`POST /v1/audits` **不重试** —— 重发一次就是多跑一次审计、多扣一次配额。

## 环境变量

| 变量 | 用途 |
|---|---|
| `SAGAX_AUDIT_API_BASE_URL` | 云端地址（旧名 `SAGAX_CLOUD_URL` 仍然可用） |
| `SAGAX_AUDIT_API_KEY` | 订阅 Key（旧名 `SAGAX_API_KEY` 仍然可用） |
| `SAGAX_AUDIT_PROJECT_ID` | 项目 id（租户内的二次隔离，默认 `default`） |
| `SAGAX_AUDIT_CA_BUNDLE` | 服务端用私有 CA 时，额外信任的证书（PEM） |
| `SAGAX_AUDIT_ALLOW_INSECURE_HTTP` | 服务端还没上 TLS 时显式放行明文（默认拒绝） |

### 服务端是自签证书时

```bash
curl -k -o sagax-ca.crt https://<你的端点>/ca.crt
openssl x509 -in sagax-ca.crt -noout -fingerprint -sha256   # 带外核对
export SAGAX_AUDIT_CA_BUNDLE=$PWD/sagax-ca.crt
```

这是**追加**信任一张 CA，不会动你系统里原有的信任链。不要改用 `SSL_CERT_FILE`
——那个变量会替换整个进程的信任库，你访问其他 HTTPS 站点会一起挂。

SDK **没有**关闭证书校验的开关：关掉之后中间人可以完整读写你的 API Key 与
Evidence，而这种临时开关几乎不会有人再改回来。

## 命令行

```bash
sagax-amadeus status                      # 连接、租户与配额
sagax-amadeus version
sagax-amadeus audit --input run.json --report out.md
sagax-amadeus audits list|get|status|result|report|bundle|trace|cancel
sagax-amadeus evidence upload|list|get|download|delete
sagax-amadeus memory  add|search|list|get|update|delete
sagax-amadeus skill   add|search|list|get|update|delete|official
sagax-amadeus wiki    write|search|list|get|delete|public
sagax-amadeus boundary add-rule|list|public|disable
sagax-amadeus candidate list|approve|contribute --confirm
sagax-amadeus mcp                         # MCP Server (stdio)
```

未装包时用 `python3 -m sagax_amadeus <同样的子命令>`。

## MCP

```json
{"mcpServers": {"sagax-audit": {
  "command": "python3", "args": ["-m", "sagax_amadeus.mcp_server"],
  "env": {"SAGAX_AUDIT_API_BASE_URL": "https://audit.example.com",
          "SAGAX_AUDIT_API_KEY": "sagax_sk_..."}}}}
```

共 16 个工具，前缀 `sagax.`（`sagax.audit_check`、`sagax.audit_run`、
`sagax.evidence_upload`、`sagax.memory_search` …）。每个都是一次云端 API 调用的
薄封装 —— MCP 这一层**不重复实现审计逻辑**，否则就会出现两套结论。

服务名 `sagax-audit` 与环境变量前缀 `SAGAX_AUDIT_` 沿用产品原名，和包名
`sagax-amadeus` 不一致是**有意的**：它们是线上契约，客户端配置和服务端认证里
写死的就是它们，不随发行包改名而变动。

## 版本说明

3.0.0 是第一个公开发布的版本。此前的 1.x（本地完整运行时）与 2.x（改造成薄
客户端）只在内部存在，从未发布到 PyPI —— 所以 3.0.0 没有需要迁移的旧用户，
也不带任何兼容层：只有 `sagax_amadeus` 一个导入名、`sagax-amadeus` 一个命令。

`AuditClient` 是 `SagaxAuditClient` 的别名（同一个类对象），两个名字都可以用。

## 许可

Apache License 2.0，见 [`LICENSE`](LICENSE)。

审计引擎（`sagax_audit_cloud`）**不在本许可范围内**，按订阅或私有化协议单独授权。
