agent_context/lib.rs
1//! # agent-context
2//!
3//! 通用、后端无关的 LLM 对话上下文管理器,基于 [kameo](https://crates.io/crates/kameo) Actor 模型构建。
4//!
5//! ## 核心概念
6//!
7//! ### 三区 + Scratch 消息模型
8//!
9//! 上下文消息按可变性分为三个存储区域,加上一个不存储的 Scratch 层,按序拼接为完整对话:
10//!
11//! | 区域 | 容器 | 可变性 | 用途 |
12//! |------|------|--------|------|
13//! | **immutable** | [`ReadOnly`] | 不可变 | 系统提示词、预设上下文等构造后不变的消息 |
14//! | **compressed** | `Vec` | 仅压缩时写入 | 历史对话压缩后的摘要 |
15//! | **incremental** | `Vec` | 完全可变 | 当前活跃对话,支持增删改查 |
16//! | **scratch** | — | 不存储 | 每轮刷新的临时元数据(时间、工作目录等),通过 [`CommonOpts`] 配置 |
17//!
18//! 拼接顺序:immutable → compressed → incremental → scratch(如有)。
19//! Scratch 由调用方在每次 [`RequestSend`]/[`RequestSendStream`] 时通过 [`CommonOpts`] 传入,
20//! 不存入任何区域,不触发变更事件,不参与序列化。
21//!
22//! ### 后端 trait
23//!
24//! [`ContextBackend`] 是核心抽象,封装 LLM 后端的:
25//!
26//! - **消息工厂**:[`user_message`](ContextBackend::user_message)、[`system_message`](ContextBackend::system_message)、[`tool_message`](ContextBackend::tool_message)
27//! - **格式转换**:[`to_system_message`](ContextBackend::to_system_message)、[`to_request_messages`](ContextBackend::to_request_messages)
28//! - **响应解析**:[`extract_messages`](ContextBackend::extract_messages)
29//! - **模型对话**:[`send`](ContextBackend::send)、[`send_stream`](ContextBackend::send_stream)、[`estimate_tokens`](ContextBackend::estimate_tokens)
30//!
31//! 实现 `ContextBackend` 即可让 AC 支持任意 LLM 后端(DeepSeek、智谱、OpenAI 等)。
32//!
33//! ### 变更通知
34//!
35//! 通过 [`RequestSubscribeChange`] 动态注册订阅者 Actor,接收 [`NotifyChange`] 实时追踪消息变化。
36//!
37//! ### 上下文压缩
38//!
39//! [`RequestSend`]/[`RequestSendStream`] 发送前自动检测上下文是否已满。
40//! 当 [`CommonOpts::auto_compress`] 为 `true` 时自动压缩;为 `false` 时返回错误。
41//! 也可通过 [`RequestCompress`] 手动触发压缩,支持 [`CompressStrategy::Summarize`] 策略。
42//! 通过 [`RequestSubscribeCompressed`] 注册压缩结果订阅者,可在压缩后修改摘要与保留消息。
43//!
44//! ## 快速开始
45//!
46//! ```ignore
47//! use agent_context::{
48//! AgentContext, RequestAppend, RequestSend, RequestSendStream, RequestMessages,
49//! RequestSubscribeChange, ContextBackend, CommonOpts, NotifyChange,
50//! };
51//! use futures::StreamExt;
52//!
53//! // 1. 实现 ContextBackend(以你的 LLM SDK 类型)
54//! // 2. 创建 Actor 并订阅变更通知
55//! let ctx = AgentContext::new(my_backend, vec![backend.system_message("You are a helpful assistant.")]);
56//! let actor = AgentContext::spawn(ctx);
57//! actor.ask(RequestSubscribeChange { recipient: app_ref.recipient() }).await?;
58//!
59//! // 3. 非流式对话
60//! actor.ask(RequestAppend { message: user_msg }).await?;
61//! actor.ask(RequestSend { opts: my_opts }).await?;
62//!
63//! // 4. 流式对话
64//! let mut stream = actor.ask(RequestSendStream { opts: my_opts }).await??;
65//! while let Some(chunk) = stream.next().await { /* 逐块处理 */ }
66//! let chunks = stream.take_chunks();
67//! let msg = backend.merge_chunks(&chunks).expect("chunk merge failed");
68//! let msgs = backend.to_request_messages(vec![msg])?;
69//! for m in msgs {
70//! actor.ask(RequestAppend { message: m }).await?;
71//! }
72//!
73//! // 5. 读取全部消息
74//! let all: Vec<_> = actor.ask(RequestMessages).await?;
75//! ```
76
77#![forbid(
78 unused_must_use,
79 clippy::panic,
80 clippy::let_underscore_must_use,
81 clippy::disallowed_types,
82 clippy::disallowed_methods,
83 clippy::allow_attributes,
84 unfulfilled_lint_expectations
85)]
86#![deny(clippy::unwrap_used, clippy::expect_used)]
87
88mod context;
89mod error;
90mod message;
91mod readonly;
92mod role;
93
94pub use context::{
95 AgentContext, AgentSendStream, CompressStrategy, CommonOpts, ContextBackend,
96 ContextBackendResponse, NotifyChange, NotifyCompressedForReply, RequestAppend, RequestClear,
97 RequestCompress, RequestCompressed, RequestEstimateTokens, RequestExtend, RequestFindByRole,
98 RequestFromJsonl, RequestGet, RequestImmutable, RequestIncremental, RequestInsert,
99 RequestIsEmpty, RequestLen, RequestMessages, RequestPop, RequestRemove, RequestRetain,
100 RequestSend, RequestSendStream, RequestSubscribeChange, RequestSubscribeCompressed,
101 RequestToJsonl, RequestUnsubscribeChange, RequestUnsubscribeCompressed, RequestUpdate,
102 ResponseType, StreamEvent, ToolCallInfo,
103};
104pub use error::AgentError;
105pub use message::ContextMessage;
106pub use readonly::ReadOnly;
107pub use role::Role;