mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-22 12:36:26 +00:00
* docs: guardrail runtime attribution spec * docs: guardrail request attribution implementation plan * feat(guardrails): add runtime user context and attribution fields to GuardrailRequest Extend GuardrailRequest with optional runtime attribution fields so that pluggable GuardrailProviders can access authenticated user context and tool-call-level attribution: - Gateway injects user_role, oauth_provider, oauth_id into runtime context alongside the existing user_id (server-authenticated only, client spoofing prevented) - GuardrailRequest gains: user_id, user_role, oauth_provider, oauth_id, run_id, tool_call_id (all optional, backward compatible) - GuardrailMiddleware reads these from ToolCallRequest.runtime.context - thread_id now actually populated from context (was always None before) - Tests: 15 new/expanded tests covering Gateway injection, runtime context reading, partial/missing fields, and client spoofing prevention - Docs: new Runtime Attribution section in GUARDRAILS.md with provider example and YAML policy illustration * fix(guardrails): propagate attribution to subagents * fix(guardrails): complete subagent attribution propagation --------- Co-authored-by: Miracle778 <miracle778@no-reply.com>
135 lines
5.8 KiB
Markdown
135 lines
5.8 KiB
Markdown
# GuardrailRequest 运行时用户上下文与归因字段补充
|
||
|
||
## 概述
|
||
|
||
为 `GuardrailRequest` 补充可选的运行时用户上下文和工具调用归因字段,使可插拔 `GuardrailProvider` 能访问 DeerFlow 已认证用户、外部身份映射、run/thread/tool-call 定位信息。
|
||
|
||
本设计不新增治理系统、不定义统一 policy schema,也不改变默认 allow/deny 行为。它只把 DeerFlow 运行时已经掌握的上下文传给 provider。
|
||
|
||
## 背景
|
||
|
||
DeerFlow 已通过 `GuardrailMiddleware` + 可插拔 `GuardrailProvider` 实现工具调用前授权。当前 `GuardrailDecision` 已能表达 allow/deny、原因、policy_id 和 metadata;缺口在 `GuardrailRequest` 侧:provider 只能看到 `tool_name` 和 `tool_input`,无法可靠知道“谁发起了这次工具调用”以及“这次调用属于哪个 run/tool_call”。
|
||
|
||
源码中 DeerFlow 已有用户身份模型:
|
||
|
||
- `users.id`:DeerFlow 内部稳定用户 ID。
|
||
- `users.system_role`:`admin` / `user`。
|
||
- `users.oauth_provider`、`users.oauth_id`:未来 OAuth/SSO 外部身份映射字段,local user 下可为空。
|
||
|
||
这些字段当前已存在于认证后的 `request.state.user`,但 run-time middleware/tool 阶段只能通过 `runtime.context` 访问上下文。因此应在 Gateway 构建 run config 时注入 server-authenticated user context,再由 GuardrailMiddleware 消费。
|
||
|
||
## 改动范围
|
||
|
||
- `app/gateway/services.py`:`inject_authenticated_user_context()` 注入更多 authenticated user context。
|
||
- `guardrails/provider.py`:`GuardrailRequest` 新增 optional 字段。
|
||
- `guardrails/middleware.py`:从 `ToolCallRequest.runtime.context` 读取字段。
|
||
- `tests/test_setup_agent_e2e_user_isolation.py`:验证 Gateway 注入到 runtime context。
|
||
- `tests/test_guardrail_middleware.py`:验证 runtime context 进入 `GuardrailRequest`。
|
||
- `backend/docs/GUARDRAILS.md`:更新 custom provider 示例。
|
||
|
||
## 设计
|
||
|
||
### GuardrailRequest 字段
|
||
|
||
```python
|
||
@dataclass
|
||
class GuardrailRequest:
|
||
tool_name: str
|
||
tool_input: dict[str, Any]
|
||
agent_id: str | None = None
|
||
thread_id: str | None = None
|
||
is_subagent: bool = False
|
||
timestamp: str = ""
|
||
|
||
user_id: str | None = None
|
||
user_role: str | None = None
|
||
oauth_provider: str | None = None
|
||
oauth_id: str | None = None
|
||
run_id: str | None = None
|
||
tool_call_id: str | None = None
|
||
```
|
||
|
||
所有新增字段均为 optional,缺失时保持 `None`。`GuardrailDecision` 不变。
|
||
|
||
### 字段来源
|
||
|
||
| 字段 | 来源 | 说明 |
|
||
|------|------|------|
|
||
| `user_id` | `request.state.user.id` → `runtime.context["user_id"]` | DeerFlow 内部稳定用户 ID |
|
||
| `user_role` | `request.state.user.system_role` → `runtime.context["user_role"]` | 可用于简单 role-based policy |
|
||
| `oauth_provider` | `request.state.user.oauth_provider` → `runtime.context["oauth_provider"]` | OAuth/SSO 外部 provider,local user 可为空 |
|
||
| `oauth_id` | `request.state.user.oauth_id` → `runtime.context["oauth_id"]` | 外部 provider subject/user id,local user 可为空 |
|
||
| `run_id` | `_build_runtime_context()` 写入 `runtime.context["run_id"]` | run 级审计归因 |
|
||
| `thread_id` | `_build_runtime_context()` 写入 `runtime.context["thread_id"]` | 修正已有字段未填充问题 |
|
||
| `tool_call_id` | `request.tool_call.get("id")` | 单次 tool call 定位 |
|
||
|
||
Gateway 注入只信任服务端认证态 `request.state.user`。客户端 `body.context` 里的 `user_id/user_role/oauth_*` 不应覆盖 authenticated user。
|
||
|
||
## 收益
|
||
|
||
### 稳定审计归因
|
||
|
||
`user_id/run_id/thread_id/tool_call_id` 让 provider 或外部审计系统能回答:
|
||
|
||
- 哪个 DeerFlow 用户触发了 tool call?
|
||
- 哪个 run 里发生了 deny?
|
||
- 同一轮中多次同名工具调用时,具体是哪一次?
|
||
|
||
### 可读的本地策略示例
|
||
|
||
`user_id` 是 UUID,不适合直接写人工 policy。`user_role` 可以支持简单示例:
|
||
|
||
```yaml
|
||
field: role_tool_key
|
||
operator: eq
|
||
value: admin:bash
|
||
```
|
||
|
||
provider 可派生:
|
||
|
||
```python
|
||
role_tool_key = f"{request.user_role or ''}:{request.tool_name}"
|
||
```
|
||
|
||
### 外部身份映射
|
||
|
||
`oauth_provider/oauth_id` 保留了未来接 OAuth/SSO/IAM 时的外部 subject 信息。当前 OAuth 路由仍是 placeholder,local user 下这些字段通常为 `None`,但字段 optional,不影响现有部署。
|
||
|
||
## 兼容性
|
||
|
||
- 所有新增字段 optional。
|
||
- 现有 provider 不读取新字段时行为不变。
|
||
- `GuardrailDecision` 不变。
|
||
- 未认证或无 runtime context 时字段为 `None`。
|
||
- local user 没有 OAuth 信息时 `oauth_provider/oauth_id` 为 `None`。
|
||
- `agent_id` 语义不变,仍保持现有 passport/agent hint 含义。
|
||
|
||
## 测试
|
||
|
||
新增或扩展以下测试:
|
||
|
||
| 测试 | 覆盖 |
|
||
|------|------|
|
||
| `TestConfigAssembly::test_authenticated_user_context_includes_role_and_oauth_identity` | Gateway 将 `user_id/user_role/oauth_provider/oauth_id` 注入 runtime context |
|
||
| `TestConfigAssembly::test_client_supplied_user_id_is_overridden` | 客户端伪造 identity context 不覆盖服务端认证态 |
|
||
| `TestGuardrailRequestAttribution::test_authenticated_user_context_present` | `runtime.context` 中用户上下文进入 `GuardrailRequest` |
|
||
| `TestGuardrailRequestAttribution::test_all_attribution_fields_present` | 用户上下文 + run/tool_call 归因字段同时传递 |
|
||
| 缺失 runtime/context 测试 | 字段保持 `None`,向后兼容 |
|
||
|
||
验证命令:
|
||
|
||
```bash
|
||
cd backend
|
||
PYTHONPATH=. uv run pytest \
|
||
tests/test_guardrail_middleware.py::TestGuardrailRequestAttribution \
|
||
tests/test_setup_agent_e2e_user_isolation.py::TestConfigAssembly -v
|
||
```
|
||
|
||
## 未涉及
|
||
|
||
- 不新增 central governance subsystem。
|
||
- 不新增 DeerFlow 内置 policy schema。
|
||
- 不修改 MCP 配置机制。
|
||
- 不修改 OAuth/SSO 实现状态。
|
||
- 不让 GuardrailMiddleware 直接依赖 FastAPI request、DB 或 auth repository。
|