shared_files
feishu.agent.shared_files
¶
用户分享文件的「句柄登记」:把用户发来的文件接入 Agent 的工具世界,且字节绝不进模型上下文。
用户在私聊里发来图片 / 文件后,本模块只登记一个**不透明、按用户隔离、带 TTL 的句柄**(重取配方:
message_id + file_key + 中性元数据),不下载字节。模型只会看到 file_id 及 {name, media_type, size,
kind} 等中性元数据;真正的字节由(后续阶段的)SharedFileResolver 在某个消费工具运行时,经
feishu.im.messages.IMNamespace.get_resource 按需重取——零字节落盘(除审批绑定时的临时 pin 缓存外)。
设计对标 feishu.auth.user_tokens:feishu.agent.shared_files.SharedFileStore 抽象「按用户多别名
(open_id/union_id/user_id)存取句柄」,内置 feishu.agent.shared_files.InMemorySharedFileStore 与
feishu.agent.shared_files.SqliteSharedFileStore(后者经 [feishu._sqlite.connect][] 加固:WAL + 0o700 目录
+ 0o600 文件)。file_id 为随机不可枚举令牌(非内容寻址、非由 file_key 派生),跨用户访问在解析期按请求
用户别名集合拦截。
SharedFile
dataclass
¶
一个用户分享文件的句柄:足以(按请求用户)重取字节并向模型描述,但**不含任何字节**。
resource_type 供 feishu.im.messages.IMNamespace.get_resource 重取(image / file),kind
为面向模型的类别。feishu.agent.shared_files.SharedFile.summary 是唯一应进入模型上下文的视图——
只含中性元数据,绝不含 file_key / message_id / 字节。
示例:
| Python Console Session | |
|---|---|
源代码位于: feishu/agent/shared_files.py
| Python | |
|---|---|
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
is_expired
¶
summary
¶
面向模型的中性元数据视图:**绝不**包含 file_key / message_id / 字节。
源代码位于: feishu/agent/shared_files.py
| Python | |
|---|---|
from_resource
classmethod
¶
from_resource(resource: Mapping[str, Any], *, user_keys: tuple[str, ...], message: Mapping[str, Any], ttl_seconds: int, now: int, file_id: str | None = None) -> SharedFile
由 feishu.im.inbound.message_resource 的资源字典 + 入站消息构造句柄(不取字节)。
源代码位于: feishu/agent/shared_files.py
SharedFileStore
¶
Bases: Protocol
用户分享文件句柄的存储协议:按用户多别名登记 / 读取,并支持审批绑定时的临时字节 pin 缓存。
内置实现为 feishu.agent.shared_files.InMemorySharedFileStore 与
feishu.agent.shared_files.SqliteSharedFileStore。本协议标注了 runtime_checkable。
所有读写均按请求用户的别名集合做隔离——传入他人或臆造的 file_id 一律解析失败。
源代码位于: feishu/agent/shared_files.py
register
async
¶
register(user: Mapping[str, Any], resource: Mapping[str, Any], *, message: Mapping[str, Any], ttl_seconds: int) -> SharedFile
登记一条入站资源句柄(不取字节);同一用户对同一 (message_id, file_key) 幂等,返回既有句柄。
recent
async
¶
pin
async
¶
read_cached
async
¶
InMemorySharedFileStore
¶
基于内存的 feishu.agent.shared_files.SharedFileStore 实现,仅适用于单进程、可接受重启即丢失的场景。
示例:
源代码位于: feishu/agent/shared_files.py
| Python | |
|---|---|
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | |
SqliteSharedFileStore
¶
基于 SQLite 的 feishu.agent.shared_files.SharedFileStore 实现,句柄跨重启存活。
采用两张表:shared_files(每个 file_id 一行,含重取配方 / 可选 pin 缓存字节 / 过期时间)与
shared_file_owners(每个 (别名, file_id) 一行,供 open_id/union_id/user_id 任一命中且不重复缓存字节)。
经 [feishu._sqlite.connect][] 加固(WAL + 0o700 目录 + 0o600 文件),保护可能缓存的文件字节。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
str | Path
|
SQLite 数据库文件路径。 |
必需 |
示例:
| Python Console Session | |
|---|---|
源代码位于: feishu/agent/shared_files.py
| Python | |
|---|---|
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | |
SharedFileResolver
¶
用户分享文件的**唯一取字节收口**:按请求用户解析句柄、经 feishu.im.messages.IMNamespace.get_resource
重取字节(或命中 pin 缓存),施加硬性大小上限,任何失败一律 fail-closed(返回 None)。
字节只会在这里短暂出现,且仅供消费工具在本次调用内使用——绝不进入模型上下文。资源重取使用**租户**
客户端(机器人对收到的消息天然有 im:resource 权限),user 仅用于按请求用户做隔离(绝不可由模型指定)。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
SharedFileStore
|
必需 | |
|
Any
|
租户态飞书客户端,提供 |
必需 |
|
int
|
单次取字节的硬上限;超限按 fail-closed 处理。默认为 20 MiB。 |
20 * 1024 * 1024
|
|
Logger | None
|
日志器。 |
None
|
源代码位于: feishu/agent/shared_files.py
| Python | |
|---|---|
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 | |
read_bytes
async
¶
返回 (bytes, SharedFile);句柄非本人所有 / 已过期 / 源资源失效 / 超限时一律返回 None(fail-closed)。
步骤:按 user 隔离解析句柄 → 命中 pin 缓存直接返回 → 否则以租户客户端 im.get_resource 重取 →
校验大小上限 → 若句柄已 pin 则回填缓存。任何异常都被吞掉并记录,绝不抛给上层(也绝不返回部分/陈旧字节)。
源代码位于: feishu/agent/shared_files.py
recent
async
¶
pin
async
¶
在审批挂起前「钉住」一个文件:立刻取一次字节并缓存,使审批通过后的消费不会因源资源过期而失败。
已有缓存则仅标记 pinned;否则以租户客户端取字节、校验上限后缓存。非本人所有 / 取字节失败 / 超限返回 False。
源代码位于: feishu/agent/shared_files.py
shared_file_keys
¶
用户的稳定多别名键;feishu.auth.user_tokens.user_identity_keys 的别名(全库统一表示)。