Files
deer-flow/backend/docs/AUTO_TITLE_GENERATION.md
DanielWalnut ab427731dc feat: add AIO sandbox provider and auto title generation (#1)
- Add AioSandboxProvider for Docker-based sandbox execution with
  configurable container lifecycle, volume mounts, and port management
- Add TitleMiddleware to auto-generate thread titles after first
  user-assistant exchange using LLM
- Add Claude Code documentation (CLAUDE.md, AGENTS.md)
- Extend SandboxConfig with Docker-specific options (image, port, mounts)
- Fix hardcoded mount path to use expanduser
- Add agent-sandbox and dotenv dependencies

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 23:29:18 +08:00

257 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自动 Thread Title 生成功能
## 功能说明
自动为对话线程生成标题,在用户首次提问并收到回复后自动触发。
## 实现方式
使用 `TitleMiddleware``after_agent` 钩子中:
1. 检测是否是首次对话1个用户消息 + 1个助手回复
2. 检查 state 是否已有 title
3. 调用 LLM 生成简洁的标题默认最多6个词
4. 将 title 存储到 `ThreadState` 中(会被 checkpointer 持久化)
## ⚠️ 重要:存储机制
### Title 存储位置
Title 存储在 **`ThreadState.title`** 中,而非 thread metadata
```python
class ThreadState(AgentState):
sandbox: SandboxState | None = None
title: str | None = None # ✅ Title stored here
```
### 持久化说明
| 部署方式 | 持久化 | 说明 |
|---------|--------|------|
| **LangGraph Studio (本地)** | ❌ 否 | 仅内存存储,重启后丢失 |
| **LangGraph Platform** | ✅ 是 | 自动持久化到数据库 |
| **自定义 + Checkpointer** | ✅ 是 | 需配置 PostgreSQL/SQLite checkpointer |
### 如何启用持久化
如果需要在本地开发时也持久化 title需要配置 checkpointer
```python
# 在 langgraph.json 同级目录创建 checkpointer.py
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver.from_conn_string(
"postgresql://user:pass@localhost/dbname"
)
```
然后在 `langgraph.json` 中引用:
```json
{
"graphs": {
"lead_agent": "src.agents:lead_agent"
},
"checkpointer": "checkpointer:checkpointer"
}
```
## 配置
`config.yaml` 中添加(可选):
```yaml
title:
enabled: true
max_words: 6
max_chars: 60
model_name: null # 使用默认模型
```
或在代码中配置:
```python
from src.config.title_config import TitleConfig, set_title_config
set_title_config(TitleConfig(
enabled=True,
max_words=8,
max_chars=80,
))
```
## 客户端使用
### 获取 Thread Title
```typescript
// 方式1: 从 thread state 获取
const state = await client.threads.getState(threadId);
const title = state.values.title || "New Conversation";
// 方式2: 监听 stream 事件
for await (const chunk of client.runs.stream(threadId, assistantId, {
input: { messages: [{ role: "user", content: "Hello" }] }
})) {
if (chunk.event === "values" && chunk.data.title) {
console.log("Title:", chunk.data.title);
}
}
```
### 显示 Title
```typescript
// 在对话列表中显示
function ConversationList() {
const [threads, setThreads] = useState([]);
useEffect(() => {
async function loadThreads() {
const allThreads = await client.threads.list();
// 获取每个 thread 的 state 来读取 title
const threadsWithTitles = await Promise.all(
allThreads.map(async (t) => {
const state = await client.threads.getState(t.thread_id);
return {
id: t.thread_id,
title: state.values.title || "New Conversation",
updatedAt: t.updated_at,
};
})
);
setThreads(threadsWithTitles);
}
loadThreads();
}, []);
return (
<ul>
{threads.map(thread => (
<li key={thread.id}>
<a href={`/chat/${thread.id}`}>{thread.title}</a>
</li>
))}
</ul>
);
}
```
## 工作流程
```mermaid
sequenceDiagram
participant User
participant Client
participant LangGraph
participant TitleMiddleware
participant LLM
participant Checkpointer
User->>Client: 发送首条消息
Client->>LangGraph: POST /threads/{id}/runs
LangGraph->>Agent: 处理消息
Agent-->>LangGraph: 返回回复
LangGraph->>TitleMiddleware: after_agent()
TitleMiddleware->>TitleMiddleware: 检查是否需要生成 title
TitleMiddleware->>LLM: 生成 title
LLM-->>TitleMiddleware: 返回 title
TitleMiddleware->>LangGraph: return {"title": "..."}
LangGraph->>Checkpointer: 保存 state (含 title)
LangGraph-->>Client: 返回响应
Client->>Client: 从 state.values.title 读取
```
## 优势
**可靠持久化** - 使用 LangGraph 的 state 机制,自动持久化
**完全后端处理** - 客户端无需额外逻辑
**自动触发** - 首次对话后自动生成
**可配置** - 支持自定义长度、模型等
**容错性强** - 失败时使用 fallback 策略
**架构一致** - 与现有 SandboxMiddleware 保持一致
## 注意事项
1. **读取方式不同**Title 在 `state.values.title` 而非 `thread.metadata.title`
2. **性能考虑**title 生成会增加约 0.5-1 秒延迟,可通过使用更快的模型优化
3. **并发安全**middleware 在 agent 执行后运行,不会阻塞主流程
4. **Fallback 策略**:如果 LLM 调用失败,会使用用户消息的前几个词作为 title
## 测试
```python
# 测试 title 生成
import pytest
from src.agents.title_middleware import TitleMiddleware
def test_title_generation():
# TODO: 添加单元测试
pass
```
## 故障排查
### Title 没有生成
1. 检查配置是否启用:`get_title_config().enabled == True`
2. 检查日志:查找 "Generated thread title" 或错误信息
3. 确认是首次对话:只有 1 个用户消息和 1 个助手回复时才会触发
### Title 生成但客户端看不到
1. 确认读取位置:应该从 `state.values.title` 读取,而非 `thread.metadata.title`
2. 检查 API 响应:确认 state 中包含 title 字段
3. 尝试重新获取 state`client.threads.getState(threadId)`
### Title 重启后丢失
1. 检查是否配置了 checkpointer本地开发需要
2. 确认部署方式LangGraph Platform 会自动持久化
3. 查看数据库:确认 checkpointer 正常工作
## 架构设计
### 为什么使用 State 而非 Metadata
| 特性 | State | Metadata |
|------|-------|----------|
| **持久化** | ✅ 自动(通过 checkpointer | ⚠️ 取决于实现 |
| **版本控制** | ✅ 支持时间旅行 | ❌ 不支持 |
| **类型安全** | ✅ TypedDict 定义 | ❌ 任意字典 |
| **可追溯** | ✅ 每次更新都记录 | ⚠️ 只有最新值 |
| **标准化** | ✅ LangGraph 核心机制 | ⚠️ 扩展功能 |
### 实现细节
```python
# TitleMiddleware 核心逻辑
@override
def after_agent(self, state: TitleMiddlewareState, runtime: Runtime) -> dict | None:
"""Generate and set thread title after the first agent response."""
if self._should_generate_title(state, runtime):
title = self._generate_title(runtime)
print(f"Generated thread title: {title}")
# ✅ 返回 state 更新,会被 checkpointer 自动持久化
return {"title": title}
return None
```
## 相关文件
- [`src/agents/thread_state.py`](../src/agents/thread_state.py) - ThreadState 定义
- [`src/agents/title_middleware.py`](../src/agents/title_middleware.py) - TitleMiddleware 实现
- [`src/config/title_config.py`](../src/config/title_config.py) - 配置管理
- [`config.yaml`](../config.yaml) - 配置文件
- [`src/agents/lead_agent/agent.py`](../src/agents/lead_agent/agent.py) - Middleware 注册
## 参考资料
- [LangGraph Checkpointer 文档](https://langchain-ai.github.io/langgraph/concepts/persistence/)
- [LangGraph State 管理](https://langchain-ai.github.io/langgraph/concepts/low_level/#state)
- [LangGraph Middleware](https://langchain-ai.github.io/langgraph/concepts/middleware/)