Skip to content

Day 6:Streamlit 部署 + 项目二总结

今天做什么

前五天你的文档分析能力已经很完整了——解析、提取、并发、缓存、MCP 服务化。但还差最后一步:让非技术人员也能用。 天做什么前五天你的文档分析能力已经很完整了——解析、提取、并发、缓存、MCP 服务化。但还差最后一步:让非技术人员也能用。

不是所有人都能打开终端敲 python doc_analyzer_server.py。产品经理、法务、HR——他们需要的是一个"上传文件,点按钮,看结果"的界面。

今天用 Streamlit 做这件事。Streamlit 是 Python 生态里最快的 Web UI 框架——你不需要学 HTML/CSS/JavaScript,全部用 Python 写。

你需要安装

bash
pip install streamlit

📁 代码目录

doc_analyzer/
├── sample_contract.txt
├── day1.py
├── day2.py
├── day3.py
├── day4.py
├── doc_analyzer_server.py    ← Day5 的 MCP Server
├── app.py                    ← 新增!Streamlit Web界面
└── test_mcp.py               ← Day5 的测试脚本

代码

python
# app.py — Streamlit 文档分析前端

import streamlit as st

# ===== 页面设置 =====
st.set_page_config(
    page_title="文档分析助手",
    page_icon="📄",
    layout="wide"
)

st.title("📄 文档分析助手")
st.caption("上传文档,自动提取关键信息、生成摘要、分类归档")

# ===== 侧边栏:分析选项 =====
with st.sidebar:
    st.header("⚙️ 分析选项")
    enable_summary = st.checkbox("生成摘要", value=True)
    enable_classify = st.checkbox("自动分类", value=True)
    enable_contract = st.checkbox("合同分析(提取甲乙方等)", value=True)
    
    st.divider()
    st.caption("💡 支持格式:TXT / PDF / DOCX(当前演示版支持 TXT)")

# ===== 主区域:文件上传 =====
uploaded_file = st.file_uploader(
    "拖拽文件到这里,或点击上传",
    type=["txt", "pdf", "docx"],
    help="支持 TXT、PDF、DOCX 格式"
)

if uploaded_file is not None:
    # --- 读取文件内容 ---
    try:
        text_content = uploaded_file.read().decode("utf-8", errors="ignore")
    except Exception as e:
        st.error(f"无法读取文件:{e}")
        st.stop()
    
    # --- 显示文档原文(可折叠)---
    with st.expander("📝 查看原文", expanded=False):
        st.text_area("文档内容", text_content, height=250, disabled=True)
    
    # --- 文档基础统计 ---
    col1, col2, col3 = st.columns(3)
    col1.metric("📏 文档字数", len(text_content))
    col2.metric("📄 文件大小", f"{len(text_content.encode('utf-8')) / 1024:.1f} KB")
    col3.metric("📂 文件名", uploaded_file.name)
    
    st.divider()
    
    # --- 开始分析按钮 ---
    if st.button("🔍 开始分析", type="primary", use_container_width=True):
        
        results_container = st.container()
        
        with results_container:
            # 默认分类(即使不勾选"自动分类",后续也可能用到)
            category = "其他"
            
            # 1. 分类
            if enable_classify:
                with st.spinner("📂 正在分类..."):
                    # 基于关键词的快速分类
                    scores = {"合同": 0, "简历": 0, "报告": 0}
                    if any(w in text_content for w in ["合同", "协议", "甲方", "乙方"]):
                        scores["合同"] += 1
                    if any(w in text_content for w in ["简历", "工作经历", "教育背景"]):
                        scores["简历"] += 1
                    if any(w in text_content for w in ["报告", "分析", "调研", "数据"]):
                        scores["报告"] += 1
                    top = max(scores, key=scores.get)
                    category = top if scores[top] > 0 else "其他"
                    
                    st.success(f"📂 文档分类:**{category}**")
                    with st.expander("查看分类得分"):
                        for k, v in scores.items():
                            st.write(f"- {k}: {v} 分")
            
            # 2. 摘要
            if enable_summary:
                with st.spinner("📄 正在生成摘要..."):
                    if len(text_content) <= 150:
                        summary = text_content
                    else:
                        summary = text_content[:150].replace("\n", " ") + " ……"
                    st.info(f"📄 文档摘要:\n{summary}")
            
            # 3. 合同分析
            if enable_contract and category == "合同":
                with st.spinner("📋 正在分析合同..."):
                    # 提取甲乙方等关键信息
                    contract_info = []
                    for line in text_content.split("\n"):
                        line = line.strip()
                        if line.startswith("甲方"):
                            contract_info.append(f"- 🔵 {line}")
                        elif line.startswith("乙方"):
                            contract_info.append(f"- 🟠 {line}")
                        elif "期限" in line or "有效期" in line:
                            contract_info.append(f"- 📅 {line}")
                        elif "知识产权" in line:
                            contract_info.append(f"- ⚖️ {line}")
                    
                    if contract_info:
                        st.success("📋 合同关键信息:")
                        for info in contract_info:
                            st.markdown(info)
                    else:
                        st.warning("未检测到明显的合同格式信息")
            
            st.divider()
            st.success("✅ 分析完成!")
            st.caption("提示:以上是基于规则的演示分析。完整版本调用后端 MCP Server 获取更精准的结果。")

# ===== 空状态 =====
else:
    st.info("👆 请上传一份文档开始分析")
    
    # 示例按钮
    st.divider()
    st.caption("没有文档?试试这个示例:")
    if st.button("📋 加载示例合同"):
        sample = """合作协议

甲方:北京科技有限公司
乙方:上海创新技术有限公司

第一条 合作内容
甲乙双方就人工智能技术研发达成合作,共同开发智能客服系统。

第二条 合作期限
本协议有效期自2025年1月1日起至2027年12月31日止。

第三条 知识产权
合作期间产生的知识产权归双方共同所有。

第四条 保密条款
双方应对合作中获知的对方商业机密予以保密。

签署日期:2025年1月1日"""
        st.session_state["sample_text"] = sample
        st.text_area("示例合同内容", sample, height=250)

运行

bash
streamlit run app.py

浏览器会自动打开 http://localhost:8501

你应该看到:

  • 一个干净的 Web 界面,中间是上传区域
  • 上传文件后,左边显示统计(字数、文件大小),右边显示分析结果
  • 尝试上传 sample_contract.txt——分类、摘要、合同分析三个模块依次呈现

项目二总结

从"读一个 TXT 文件"到"一个能处理上百份文档的 Web 服务"——这就是这六天的旅程。

核心你掌握了
Day 1文档加载 PipelineTextLoader,文档预览,字数统计——让 Agent 能"读"文档
Day 2LCEL 结构化提取prompt | llm | parser 管道,Pydantic 定义输出格式
Day 3asyncio 并发处理asyncio.gather() 批量并发,50 份文档同时分析,提速近 10 倍
Day 4分析缓存SHA256 哈希判重,缓存命中跳过分析——已跑过的不重跑
Day 5MCP 标准化把分析能力包装成 MCP Server,任何客户端都能调用
Day 6Streamlit 部署Web 界面,非技术人员也能用。上传文档,点按钮,看结果

跟项目一的对照

现在你手上有两个完整的 Agent 项目了。看一眼它们的对比:

维度项目一:智能客服项目二:文档分析
核心任务回答用户问题分析文档内容
Agent 输入一句话(用户消息)一个文件(文档)
Agent 输出一句话(回复)结构化数据(JSON/分类/摘要)
并发场景多用户同时对话批量文档同时分析
状态管理LangGraph + MemorySaver缓存 + Store
关键技术StateGraph / Checkpoint / MCPLCEL / asyncio / MCP / Streamlit

两个项目用到的核心技术栈高度重合——这就是反复练习的价值。你现在看 LangChain 的文档、看 MCP 的协议规范、看 asyncio 的用法,已经不会"看不懂"了——因为你亲手用过了。

目二的天花板

今天你做的分析,主要靠关键词匹配。但真实的文档分析系统可以做得更多:

  • 语义理解:用向量搜索找到"跟这条相似的其他条款"(项目一 Days 3-4 的 RAG)
  • 多格式支持:PDF 里的表格、Word 里的批注、扫描件的 OCR 识别
  • 版本对比:同一份合同 v1 和 v2 改了哪里,自动高亮
  • 风险识别:自动标记合同中的"无限期责任""不设上限的违约金"等高危条款

这些虽然今天没做,但你已经有了地基。加一个功能 = 加一个工具 = 在 MCP Server 里多注册一个端——你会的套路是一样的。


🎉 项目二完成。Agent 能读文档、分析内容、并发处理上百份、缓存去重、MCP 服务化、Web 部署。从 TXT 到 Web——你走完了从"脚本"到"产品"的完整路径。