共计 2437 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点:Agent 系统的前端挑战
开发 Agent 前端界面时,我们常常遇到几个棘手问题:

- 实时性要求高 :用户输入和 Agent 响应需要近乎即时展示,延迟会明显降低体验
- 状态管理复杂 :对话历史、上下文、加载状态等多维度数据需要同步维护
- 多会话管理 :支持同时进行多个独立对话且互不干扰
- 异常处理频繁 :网络波动、服务超时等情况需要优雅降级
技术选型:为什么选择 React+Hooks
对比主流框架在 Agent 场景的表现:
- Vue:响应式系统优秀,但复杂状态管理需要配合 Vuex/Pinia
- Svelte:编译时优化性能好,但生态和 TypeScript 支持较弱
- React+Hooks:
- 函数组件 +Hook 模式天然适合事件驱动场景
- useReducer 可完美管理对话状态机
- 丰富的生态库(如 react-query 处理异步)
- TS 支持最成熟
核心实现方案
1. 状态管理:自定义 Hook 设计
/**
* 管理 Agent 会话状态的自定义 Hook
* @param initialSession 初始会话数据
*/
function useAgentSession(initialSession: Session) {const [state, dispatch] = useReducer(sessionReducer, {messages: [],
status: 'idle', // idle|loading|error
context: {...initialSession}
});
// 添加新消息
const addMessage = (msg: Message) =>
dispatch({type: 'ADD_MSG', payload: msg});
return {
state,
actions: {addMessage}
};
}
2. 消息队列优化渲染
使用 requestAnimationFrame 避免快速消息导致的频闪:
const messageQueue = useRef<Message[]>([]);
const isRendering = useRef(false);
const flushMessages = () => {if (messageQueue.current.length > 0) {const batch = messageQueue.current.splice(0, 5);
dispatch({type: 'BATCH_ADD', payload: batch});
requestAnimationFrame(flushMessages);
} else {isRendering.current = false;}
};
// 收到新消息时
messageQueue.current.push(newMsg);
if (!isRendering.current) {
isRendering.current = true;
requestAnimationFrame(flushMessages);
}
3. 错误边界处理
class ErrorBoundary extends React.Component {state = { hasError: false};
static getDerivedStateFromError() {return { hasError: true};
}
componentDidCatch(error, info) {logErrorToService(error, info);
}
handleRetry = () => {this.setState({ hasError: false});
};
render() {
return this.state.hasError ? (
<div>
<p> 对话加载失败 </p>
<button onClick={this.handleRetry}> 重试 </button>
</div>
) : this.props.children;
}
}
避坑实践指南
WebSocket 连接稳定方案
- 实现心跳检测机制(30 秒间隔)
- 自动重连策略:指数退避算法
- 离线消息缓存(localStorage 暂存)
大模型响应分块渲染
// 服务端返回分块数据时
const [partialResponse, setPartial] = useState('');
useEffect(() => {const ws = new WebSocket(API_ENDPOINT);
ws.onmessage = (e) => {setPartial(prev => prev + e.data);
// 滚动到最新内容
scrollToBottom();};
}, []);
敏感信息过滤
推荐使用中间件模式:
const sanitizeMessage = (msg: string) => {
return msg.replace(/( 密码 |token)=[^&]+/g,
'$1=***'
);
};
完整示例代码
查看 GitHub 仓库 获取完整 TypeScript 实现,包含:
- 带类型定义的会话状态机
- 响应式布局组件
- 性能监控 Hooks
- 可复用的消息组件
延伸思考
插件机制设计
建议采用事件总线架构:
// 注册插件
agent.use({
name: 'weather',
match: / 天气查询 /,
execute: (query) => fetchWeather(query)
});
// 执行时
const plugins = getMatchedPlugins(input);
if (plugins.length > 0) {await Promise.all(plugins.map(p => p.execute(input)));
}
SSE vs WebSocket
| 特性 | SSE | WebSocket |
|---|---|---|
| 协议 | HTTP | ws:// |
| 双向通信 | 仅服务端推送 | 全双工 |
| 重连机制 | 内置 | 需手动实现 |
| 适用场景 | 低频状态更新 | 实时交互 |
实际建议 :对实时性要求高的核心功能用 WebSocket,辅助状态更新用 SSE。
写在最后
构建 Agent 前端是个持续迭代的过程。建议初期先确保核心链路稳固,再逐步添加高级功能。多使用性能分析工具(React Profiler)定位瓶颈,保持组件粒度适中。遇到复杂状态逻辑时,不妨回归状态机思维,往往能化繁为简。
正文完
