GLM-4-9B-Chat-1M中文长文本专项:200万字小说情节追踪与角色关系图谱生成

你有没有想过,让AI一口气读完一整部《三体》或者《红楼梦》,然后和你聊聊里面的情节发展、人物关系和隐藏的伏笔?这听起来像是科幻小说里的场景,但现在,借助GLM-4-9B-Chat-1M这个支持百万字上下文的大模型,这个想法已经变成了现实。

今天,我们就来一起探索如何用这个“超级大脑”,来处理和分析超长文本,特别是用它来追踪一部200万字小说的复杂情节,并自动生成清晰的角色关系图谱。无论你是文学研究者、内容创作者,还是单纯对AI能力感到好奇的技术爱好者,这篇文章都将为你展示一个全新的文本分析视角。

1. 为什么需要处理百万字长文本?

在开始动手之前,我们先聊聊为什么这个能力如此重要。

想象一下,你是一位网文编辑,每天要审阅几十万字的连载小说;或者你是一位学术研究者,需要分析整部古籍的脉络。传统的方法要么是把文本切成小块分别处理,结果丢失了整体的连贯性;要么是人工阅读,效率低下且容易遗漏细节。

GLM-4-9B-Chat-1M的1M上下文长度,大约能容纳200万个中文字符。这意味着:

  • 完整理解:可以把一整部超长篇小说一次性喂给模型,让它建立完整的“世界观记忆”
  • 关联分析:模型能记住几百章之前埋下的伏笔,并在后续分析中关联起来
  • 效率革命:几分钟内完成人类需要数天甚至数周才能完成的文本梳理工作

接下来,我将带你从环境部署开始,一步步实现这个有趣的应用。

2. 快速部署与验证:让模型跑起来

2.1 环境准备与一键部署

首先,我们需要在CSDN星图镜像广场找到GLM-4-9B-Chat-1M的镜像。这个镜像已经预配置好了vLLM推理框架和Chainlit前端界面,大大简化了部署流程。

部署成功后,我们需要确认服务是否正常运行。打开WebShell,执行以下命令查看日志:

cat /root/workspace/llm.log

如果看到类似下面的输出,说明模型已经成功加载并准备好接收请求了:

INFO:__main__:Loading model from /root/autodl-tmp/glm-4-9b-chat-1m
INFO:__main__:Model loaded successfully
INFO:__main__:Starting vLLM engine...
INFO:__main__:vLLM engine started on port 8000

2.2 测试基础对话能力

模型部署好后,我们先做个简单的测试,确保一切正常。打开Chainlit前端界面,这是一个为对话式AI设计的Web界面,用起来很像ChatGPT。

尝试问一个简单的问题:“你好,请介绍一下你自己。”

如果模型正确响应,说明基础对话功能正常。接下来,我们就可以挑战更复杂的任务了。

3. 实战:200万字小说情节分析

现在进入正题。我们假设手头有一部200万字的小说文本(可以是TXT格式),想要让AI帮我们分析其中的情节发展和人物关系。

3.1 准备超长文本数据

首先,我们需要把小说文本准备好。这里有个小技巧:虽然模型支持1M上下文,但实际处理时,我们可以根据需求灵活控制输入长度。

# 读取小说文本文件
def load_novel_text(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()
    return text

# 如果文本过长,可以按章节分割
def split_by_chapters(text, max_chars_per_chunk=500000):
    """
    将文本按大致均匀的大小分割,尽量保持章节完整性
    """
    chunks = []
    current_chunk = ""
    
    # 这里假设章节以“第X章”开头
    lines = text.split('\n')
    
    for line in lines:
        if len(current_chunk) + len(line) < max_chars_per_chunk:
            current_chunk += line + '\n'
        else:
            chunks.append(current_chunk)
            current_chunk = line + '\n'
    
    if current_chunk:
        chunks.append(current_chunk)
    
    return chunks

# 使用示例
novel_text = load_novel_text("novel.txt")
chunks = split_by_chapters(novel_text)
print(f"文本被分割为 {len(chunks)} 个块")

3.2 情节脉络提取

现在,我们让AI从这200万字中提取主要的情节线。这里的关键是设计合适的提示词(prompt),引导模型关注我们关心的内容。

import requests
import json

def analyze_plot_structure(text_chunk, api_url="http://localhost:8000/v1/chat/completions"):
    """
    分析文本块的情节结构
    """
    prompt = f"""你是一位专业的文学分析师。请分析以下小说文本,提取主要的情节发展脉络。

要求:
1. 找出3-5条主要情节线
2. 每条情节线用一句话概括核心冲突
3. 标注每条线涉及的主要角色
4. 分析情节之间的关联性

小说文本:
{text_chunk[:100000]}  # 控制输入长度,避免过长

请以JSON格式返回分析结果,包含以下字段:
- main_plots: 列表,每个元素包含title(情节标题)、summary(情节概要)、characters(涉及角色)、connections(与其他情节的关联)
"""

    payload = {
        "model": "glm-4-9b-chat-1m",
        "messages": [
            {"role": "user", "content": prompt}
        ],
        "max_tokens": 2000,
        "temperature": 0.3  # 较低的温度值让输出更稳定
    }
    
    response = requests.post(api_url, json=payload)
    result = response.json()
    
    return result["choices"][0]["message"]["content"]

# 对每个文本块进行分析
plot_analyses = []
for i, chunk in enumerate(chunks[:3]):  # 先分析前3个块作为示例
    print(f"正在分析第{i+1}个文本块...")
    analysis = analyze_plot_structure(chunk)
    plot_analyses.append(analysis)
    print(f"第{i+1}块分析完成")

3.3 角色关系图谱生成

这是最有趣的部分——让AI从文本中自动识别角色关系,并生成结构化的关系数据。

def extract_character_relationships(text_chunk):
    """
    从文本中提取角色关系
    """
    prompt = f"""请从以下小说文本中提取所有重要角色及其相互关系。

要求:
1. 识别所有出现的重要角色(主角、重要配角)
2. 分析角色之间的关系类型:亲情、友情、爱情、敌对、师徒、同事等
3. 评估关系强度:强、中、弱
4. 提取关系发展的关键事件

请特别注意:
- 同一个人物的不同称呼要统一(如“张三”、“张先生”、“三爷”都指向同一人物)
- 关系可能随时间变化,请标注关系的变化点

小说文本:
{text_chunk[:80000]}

请以JSON格式返回,包含以下结构:
{{
  "characters": [
    {{
      "name": "角色名",
      "aliases": ["别名1", "别名2"],
      "role": "角色定位(主角/配角/反派等)"
    }}
  ],
  "relationships": [
    {{
      "from": "角色A",
      "to": "角色B", 
      "type": "关系类型",
      "strength": "关系强度",
      "evidence": "文本证据",
      "chapter": "出现章节"
    }}
  ]
}}
"""

    payload = {
        "model": "glm-4-9b-chat-1m",
        "messages": [
            {"role": "user", "content": prompt}
        ],
        "max_tokens": 2500,
        "temperature": 0.2  # 更低的温度确保关系提取的准确性
    }
    
    response = requests.post("http://localhost:8000/v1/chat/completions", json=payload)
    result = response.json()
    
    return json.loads(result["choices"][0]["message"]["content"])

# 提取角色关系
character_data = []
for i, chunk in enumerate(chunks[:2]):
    print(f"正在从第{i+1}个文本块提取角色关系...")
    relationships = extract_character_relationships(chunk)
    character_data.append(relationships)
    print(f"找到{len(relationships['characters'])}个角色,{len(relationships['relationships'])}条关系")

4. 可视化展示:让分析结果一目了然

分析得到的数据是结构化的,但还不够直观。我们可以用一些简单的可视化方法,让结果更加生动。

4.1 生成角色关系图

我们可以用NetworkX和Matplotlib来绘制角色关系网络图:

import networkx as nx
import matplotlib.pyplot as plt
from matplotlib import font_manager

def create_relationship_graph(character_data):
    """
    创建角色关系网络图
    """
    G = nx.Graph()
    
    # 添加节点(角色)
    for character in character_data['characters']:
        G.add_node(character['name'], 
                  role=character['role'],
                  aliases=character.get('aliases', []))
    
    # 添加边(关系)
    for rel in character_data['relationships']:
        # 根据关系类型设置边的属性
        edge_attrs = {
            'type': rel['type'],
            'strength': rel['strength'],
            'label': f"{rel['type']}({rel['strength']})"
        }
        G.add_edge(rel['from'], rel['to'], **edge_attrs)
    
    return G

def draw_relationship_graph(G, output_path="character_relationships.png"):
    """
    绘制关系图
    """
    plt.figure(figsize=(20, 16))
    
    # 根据角色类型设置节点颜色
    node_colors = []
    for node in G.nodes():
        role = G.nodes[node].get('role', 'unknown')
        if role == '主角':
            node_colors.append('red')
        elif role == '重要配角':
            node_colors.append('orange')
        elif role == '反派':
            node_colors.append('purple')
        else:
            node_colors.append('skyblue')
    
    # 根据关系强度设置边的粗细
    edge_weights = []
    for u, v, data in G.edges(data=True):
        strength = data.get('strength', '中')
        if strength == '强':
            edge_weights.append(3)
        elif strength == '中':
            edge_weights.append(2)
        else:
            edge_weights.append(1)
    
    # 绘制网络图
    pos = nx.spring_layout(G, k=2, iterations=50)
    
    # 绘制节点
    nx.draw_networkx_nodes(G, pos, 
                          node_size=800,
                          node_color=node_colors,
                          alpha=0.9)
    
    # 绘制边
    nx.draw_networkx_edges(G, pos,
                          width=edge_weights,
                          alpha=0.5,
                          edge_color='gray')
    
    # 绘制标签
    nx.draw_networkx_labels(G, pos,
                           font_size=10,
                           font_family='SimHei')
    
    # 添加图例
    plt.title("小说角色关系图谱", fontsize=16, fontweight='bold')
    
    # 保存图片
    plt.tight_layout()
    plt.savefig(output_path, dpi=300, bbox_inches='tight')
    plt.show()
    
    print(f"关系图已保存到: {output_path}")

# 使用示例
if character_data:
    # 合并多个文本块的角色数据
    merged_characters = []
    merged_relationships = []
    
    for data in character_data:
        merged_characters.extend(data['characters'])
        merged_relationships.extend(data['relationships'])
    
    combined_data = {
        'characters': merged_characters,
        'relationships': merged_relationships
    }
    
    G = create_relationship_graph(combined_data)
    draw_relationship_graph(G)

4.2 情节时间线可视化

除了角色关系,情节发展的时间线也很重要:

import plotly.graph_objects as go
from datetime import datetime, timedelta

def create_plot_timeline(plot_analyses):
    """
    创建情节发展时间线
    """
    fig = go.Figure()
    
    # 假设每个文本块代表一段时间
    time_points = []
    plot_lines = {}
    
    for i, analysis_str in enumerate(plot_analyses):
        try:
            analysis = json.loads(analysis_str)
            time_point = f"第{i+1}部分"
            time_points.append(time_point)
            
            for plot in analysis.get('main_plots', []):
                plot_title = plot['title']
                if plot_title not in plot_lines:
                    plot_lines[plot_title] = []
                
                # 用情节的复杂程度或重要性作为Y轴值
                importance = len(plot.get('characters', [])) + len(plot.get('connections', []))
                plot_lines[plot_title].append(importance)
        except:
            continue
    
    # 为每条情节线添加轨迹
    colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
    
    for idx, (plot_title, values) in enumerate(plot_lines.items()):
        if len(values) == len(time_points):
            fig.add_trace(go.Scatter(
                x=time_points,
                y=values,
                mode='lines+markers',
                name=plot_title,
                line=dict(color=colors[idx % len(colors)], width=3),
                marker=dict(size=8)
            ))
    
    fig.update_layout(
        title="小说情节发展脉络",
        xaxis_title="故事进展",
        yaxis_title="情节复杂度/重要性",
        hovermode='x unified',
        template='plotly_white',
        height=500
    )
    
    # 保存为HTML文件,方便交互查看
    fig.write_html("plot_timeline.html")
    print("情节时间线已保存为 plot_timeline.html")
    
    return fig

# 生成时间线图
if plot_analyses:
    timeline_fig = create_plot_timeline(plot_analyses)

5. 进阶技巧与优化建议

在实际使用中,你可能会遇到一些挑战。这里分享几个我总结的实用技巧:

5.1 处理超长文本的策略

虽然模型支持1M上下文,但实际使用时还是要讲究策略:

  1. 分层处理:先让模型阅读全文,提取大纲和主要角色;再针对特定章节进行深入分析
  2. 摘要链:对超长文本先进行分段摘要,然后用摘要作为上下文进行详细分析
  3. 增量分析:像人类读书一样,让模型“读完一章,分析一章”,然后累积知识
def incremental_analysis(chunks):
    """
    增量式分析:模拟人类逐章阅读的过程
    """
    accumulated_knowledge = ""
    all_analyses = []
    
    for i, chunk in enumerate(chunks):
        print(f"正在阅读和分析第{i+1}章...")
        
        prompt = f"""基于之前的知识和新的章节内容,请更新对小说的理解。

之前掌握的信息:
{accumulated_knowledge[:5000]}  # 控制知识库大小

新的章节内容:
{chunk[:50000]}

请更新:
1. 新增的角色及其关系
2. 情节的新发展
3. 之前伏笔的揭示情况
4. 整体故事走向的调整
"""
        
        # 调用模型进行分析
        analysis = analyze_with_model(prompt)
        all_analyses.append(analysis)
        
        # 更新知识库(保留最重要的信息)
        accumulated_knowledge = summarize_knowledge(accumulated_knowledge, analysis)
    
    return all_analyses

5.2 提升分析准确性的方法

  1. 多次提问,交叉验证:对同一个问题用不同角度提问,比较答案的一致性
  2. 提供示例:在prompt中给出你期望的输出格式示例
  3. 分步骤引导:不要一次性要求太多分析,而是分步骤进行
  4. 设置检查点:让模型在分析过程中自我检查逻辑一致性

5.3 性能优化建议

  • 批量处理:如果有多个分析任务,尽量批量发送请求
  • 缓存结果:对相同的文本分析进行缓存,避免重复计算
  • 异步处理:对于耗时较长的分析,使用异步请求避免阻塞

6. 实际应用场景拓展

这个技术不仅仅能用于小说分析,在很多实际场景中都有应用价值:

6.1 学术研究助手

  • 分析整部古籍的脉络和思想体系
  • 对比不同版本文献的差异
  • 提取学术专著的核心观点和论证结构

6.2 企业知识管理

  • 分析公司历年文档,构建知识图谱
  • 从会议纪要中提取决策脉络和行动项
  • 整理产品需求文档,追踪功能演进

6.3 内容创作支持

  • 为编剧分析剧本结构和人物弧光
  • 帮网文作者检查情节漏洞和角色一致性
  • 辅助非虚构写作的资料整理和脉络梳理

6.4 教育应用

  • 自动生成经典文学作品的导读和分析
  • 为学生提供个性化的阅读辅助
  • 帮助教师准备教学材料和讨论问题

7. 总结

通过今天的探索,我们看到了GLM-4-9B-Chat-1M在处理超长文本方面的强大能力。从部署模型到实际应用,从基础对话到复杂的文学分析,这个支持百万字上下文的模型为我们打开了一扇新的大门。

关键收获

  1. 技术可行性:1M的上下文长度确实能处理整部小说的分析任务,保持信息的连贯性和完整性
  2. 实用价值:自动化的情节追踪和角色关系提取,能大幅提升文本分析的效率
  3. 易用性:配合Chainlit前端,即使不是专业开发者也能轻松使用
  4. 扩展性:这套方法可以轻松适配到其他长文本分析场景

给初学者的建议

  • 先从短文本开始练习,熟悉prompt设计技巧
  • 多尝试不同的分析角度,找到最适合你需求的方法
  • 不要追求一次完美,迭代优化往往效果更好
  • 结合实际需求,让技术真正为你创造价值

长文本分析的世界才刚刚开启,随着模型能力的不断提升,我们能做的事情会越来越多。无论是文学研究、内容创作还是知识管理,这种“让AI读完整本书”的能力,都将成为我们理解和处理复杂信息的强大工具。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐