loop
feishu.agent.loop
¶
StreamResult
dataclass
¶
一轮流式响应归并后的完整结果。
由 feishu.agent.loop.accumulate_stream 将逐个 feishu.agent.llm.StreamChunk 归并而成:
text 为拼接后的全部文本,tool_calls 为重组完成的工具调用列表,stop_reason 为归一化的停止原因,
usage 为可选的用量统计,reasoning 为归并后的(可选)推理/思考文本。
示例:
| Python Console Session | |
|---|---|
源代码位于: feishu/agent/loop.py
AgentEngine
¶
智能体底层主循环:驱动大模型与工具协作,自动回复飞书消息。
每收到一条消息,便载入会话历史、调用 feishu.agent.llm.LlmBackend 流式生成响应,并由
feishu.agent.loop.accumulate_stream 归并结果。若模型请求调用工具,则经
feishu.agent.tools.ToolRegistry 分发执行,并将结果回传后继续下一轮,直至产出最终文本或触及
max_iterations 上限。需要审批的工具会先发送审批卡片并挂起本轮,待用户在卡片上批准或拒绝后由
feishu.agent.loop.AgentEngine.handle_card_action 恢复。
经 feishu.agent.registration.register_agent 注册到事件分发器后,即可自动处理消息与卡片回调事件。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
LlmBackend
|
大模型后端,须实现 feishu.agent.llm.LlmBackend。 |
必需 |
|
ToolRegistry
|
必需 | |
|
SessionStore | None
|
会话历史存储。默认使用 feishu.agent.session.InMemorySessionStore。 |
None
|
|
FeishuClient | None
|
飞书客户端,用于回复消息与发送卡片;为 |
None
|
|
PendingApprovalStore | None
|
挂起审批存储。默认使用 feishu.agent.session.InMemoryPendingApprovalStore。 |
None
|
|
PendingAuthorizationStore | None
|
None
|
|
|
ApprovalEngine | None
|
人在环审批引擎。默认使用 feishu.agent.approval.DefaultApprovalEngine(基于
|
None
|
|
Callable[[PendingApproval], dict[str, Any]] | None
|
审批卡片构造器,签名为 |
None
|
|
Callable[[PendingApproval, str, ApprovalOutcome], dict[str, Any]] | None
|
决策结果卡片构造器,签名为 |
None
|
|
Callable[[str], dict[str, Any]] | None
|
授权卡片构造器,签名为 |
None
|
|
Callable[[list[str], bool, str], dict[str, Any]] | None
|
进度卡片构造器,签名为 |
None
|
|
Callable[[ProgressSnapshot], Any] | None
|
可选的进度文案生成器,签名为 |
None
|
|
Any
|
用户态 token 提供方(feishu.auth),供工具以用户身份执行;为 |
None
|
|
Any
|
产品注入的授权 URL 构造器,签名
|
None
|
|
Any
|
用户分享文件的解析器 feishu.agent.shared_files.SharedFileResolver,是 |
None
|
|
Any
|
入站文件句柄存储 feishu.agent.shared_files.SharedFileStore,用于捕获用户分享的
文件(仅元数据,不落字节);为 |
None
|
|
int
|
文件句柄的存活时长(秒)。默认为 7 天。 |
7 * 24 * 3600
|
|
bool
|
是否仅在单聊(p2p)中捕获分享文件。默认为 |
True
|
|
Any
|
收款账户解析器 feishu.agent.payment_accounts.PaymentAccountResolver,把账户句柄
还原为可提交的账户值(严格按请求用户隔离);为 |
None
|
|
Callable[[str], bool] | None
|
判定文本是否为「清空会话」命令的谓词 |
None
|
|
str
|
清空会话后的回执文案。 |
'会话历史已清空。'
|
|
Callable[[str], bool] | None
|
判定文本是否为「立即压缩上下文」命令的谓词 |
None
|
|
Callable[[int, int], str] | None
|
压缩回执构造器,签名 |
None
|
|
Callable[[list[Message]], Any] | None
|
自定义摘要器 |
None
|
|
int
|
历史超过该估算 token 数时自动压缩(摘要旧轮次、保留最近若干轮),以维持可被
前缀缓存命中的稳定 prefix。为 |
0
|
|
int
|
自动 / 手动压缩时原样保留的最近消息条数。默认为 |
12
|
|
str
|
默认摘要器使用的指令文案。 |
'Summarize the earlier conversation below so it can stand in for the full history: preserve facts, decisions, open tasks, identifiers, and user preferences; drop pleasantries. Be concise.'
|
|
str
|
摘要消息的前缀标记。 |
'[Summary of earlier conversation]'
|
|
int
|
单轮对话中模型与工具往返的最大次数。默认为 |
8
|
|
str | Callable[..., Any] | None
|
系统提示词;也可以传入 callable,每轮调用前动态生成。callable 可接收 |
None
|
|
str | Callable[..., Any] | None
|
每轮附加给模型的动态上下文;也可以传入 callable,签名同 |
None
|
|
float
|
会话空闲超过该秒数后自动清空普通历史; |
0.0
|
|
str | Callable[..., Any] | None
|
本轮默认时区;也可以传入 callable,每轮按事件动态解析。卡片回调中若包含用户时区,会优先使用。 |
None
|
|
str
|
同一会话的新消息打断旧轮次时,旧进度卡收尾展示的文案。 |
'已被更新的消息打断。'
|
|
bool
|
未使用进度卡片时是否以流式卡片回复。为 |
False
|
|
Any
|
透传给 feishu.agent.llm.LlmBackend.stream 的额外参数。 |
{}
|
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
|
示例:
源代码位于: feishu/agent/loop.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 483 484 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 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | |
run
async
¶
处理一条飞书消息事件:载入历史、追加用户消息并驱动主循环。
通常无需直接调用,而是经 feishu.agent.registration.register_agent 注册为消息事件的处理函数。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
Event
|
飞书消息事件,须具备 |
必需 |
飞书文档
示例:
| Python Console Session | |
|---|---|
源代码位于: feishu/agent/loop.py
handle_card_action
async
¶
resume_authorization
async
¶
在 OAuth 回调成功保存用户 token 后,恢复一次挂起授权对应的原工具调用。
accumulate_stream
async
¶
accumulate_stream(chunks: AsyncIterator[StreamChunk], *, on_reasoning: Callable[[str, str], Awaitable[Any] | Any] | None = None) -> StreamResult
将一轮流式响应的增量片段归并为一个 feishu.agent.loop.StreamResult。
文本片段按序拼接;工具调用片段按 index 归并,逐段累积出完整的参数 JSON 字符串,并产出有序的
feishu.agent.llm.ToolCall 列表;停止原因与用量统计取自 feishu.agent.llm.MessageStop;推理片段
(feishu.agent.llm.ReasoningDelta)按序拼接为 reasoning。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
AsyncIterator[StreamChunk]
|
逐个产出 feishu.agent.llm.StreamChunk 的异步迭代器,通常来自 feishu.agent.llm.LlmBackend.stream。 |
必需 |
返回:
| 类型 | 描述 |
|---|---|
StreamResult
|
示例:
源代码位于: feishu/agent/loop.py
session_id_for
¶
从消息事件推导会话标识,用于隔离不同会话的对话历史。
优先使用 chat_id;当消息属于话题(thread)回复时,附加 root_id 以将同一话题归为独立会话;
若事件中没有 chat_id,则回退为 message_id。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
Event
|
飞书消息事件,须具备 |
必需 |
返回:
| 类型 | 描述 |
|---|---|
str
|
会话标识字符串。 |
飞书文档
示例:
源代码位于: feishu/agent/loop.py
user_message_from_event
¶
将飞书消息事件转换为一条用户角色的 feishu.agent.llm.Message。
文本提取委托给 feishu.im.inbound.message_text,因此除纯文本外还支持富文本(post)消息,
并会依据消息的 mentions 数组将 @_user_N 提及占位符解析为 @<姓名>;未被解析的开头占位符
(例如事件未携带 mentions 时)会被去除。当无法解析出任何文本时:纯文本消息退回使用原始 content,
其他类型(如图片、文件)则返回形如 [<message_type> message] 的中性占位文本。
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
|
Event
|
飞书消息事件,须具备 |
必需 |
返回:
| 类型 | 描述 |
|---|---|
Message
|
角色为 |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
事件体中不存在 |
飞书文档
示例:
| Python Console Session | |
|---|---|