Skip to content

Day 6:Streamlit 审查面板 + 项目三总结

今天做什么

前五天你做了三个完整的项目。

项目三的最后一天,惯例——用 Streamlit 做一个 Web 界面,让非技术人员也能用你的代码审查能力。 天做什么前五天你做了三个完整的项目。项目三的最后一天,惯例——用 Streamlit 做一个 Web 界面,让非技术人员也能用你的代码审查能力。

你需要安装

bash
pip install streamlit

📁 代码目录

code_assistant/
├── sample_code.py
├── day1.py
├── day2.py
├── day3.py
├── day4.py
├── code_reviewer_server.py    ← Day5 MCP Server
├── app.py                     ← 新增!Streamlit审查面板
└── test_module/
    ├── calc.py
    ├── utils.py
    └── helpers.py

代码

python
# app.py — Streamlit 代码审查面板

import streamlit as st
import os
from pathlib import Path

# ===== 审查逻辑(同 Day5)=====
def review_code(code: str) -> list:
    """返回 (issues, score)"""
    issues = []
    
    for line in code.split("\n"):
        line_stripped = line.strip()
        if line_stripped.startswith("def "):
            func_name = line_stripped.split("(")[0].replace("def ", "").strip()
            issues.append({
                "severity": "medium",
                "function": func_name,
                "category": "文档",
                "message": f"函数 `{func_name}` 缺少文档字符串",
                "suggestion": "在函数定义下一行添加三重引号注释"
            })
    
    if any(w in code for w in ["/ 0", "/0"]):
        issues.append({
            "severity": "high",
            "function": "除法相关",
            "category": "安全",
            "message": "存在除零风险",
            "suggestion": "在除法前添加 `if b == 0:` 检查"
        })
    
    if "open(" in code and "with " not in code:
        issues.append({
            "severity": "high",
            "function": "文件操作",
            "category": "安全",
            "message": "`open()` 未使用 `with` 语句",
            "suggestion": "改用 `with open(path) as f:` 确保文件自动关闭"
        })
    
    if "except:" in code:
        issues.append({
            "severity": "medium",
            "function": "异常处理",
            "category": "规范",
            "message": "使用了裸 `except:`",
            "suggestion": "指定具体异常类型,如 `except ValueError:`"
        })
    
    if "def " in code and ("=[]" in code or "={}" in code):
        issues.append({
            "severity": "high",
            "function": "函数定义",
            "category": "陷阱",
            "message": "使用可变对象作为默认参数",
            "suggestion": "将默认值改为 `None`,在函数体内初始化"
        })
    
    score = max(30, 100 - len(issues) * 10)
    return issues, score

# ===== 页面设置 =====
st.set_page_config(
    page_title="AI 代码审查助手",
    page_icon="🔍",
    layout="wide"
)

st.title("🔍 AI 代码审查助手")
st.caption("上传 Python 代码,自动检测代码质量问题")

# ===== 侧边栏 =====
with st.sidebar:
    st.header("⚙️ 审查设置")
    
    rules = {
        "docstring": st.checkbox("文档字符串检查", value=True),
        "zero_division": st.checkbox("除零风险检查", value=True),
        "file_close": st.checkbox("文件未关闭检查", value=True),
        "bare_except": st.checkbox("裸 except 检查", value=True),
        "mutable_default": st.checkbox("可变默认参数检查", value=True),
    }
    
    st.divider()
    
    input_method = st.radio(
        "输入方式",
        ["粘贴代码", "上传文件"],
        horizontal=True
    )
    
    st.divider()
    st.caption("💡 审查规则可定制——在侧边栏勾选/取消")

# ===== 主区域 =====
if input_method == "粘贴代码":
    code_input = st.text_area(
        "Python 代码",
        height=300,
        placeholder="粘贴你的 Python 代码到这里...",
        key="code_input"
    )
else:
    uploaded = st.file_uploader("上传 Python 文件", type=["py"])
    if uploaded:
        code_input = uploaded.read().decode("utf-8", errors="ignore")
        st.text_area("文件内容", code_input, height=300, disabled=True)
    else:
        code_input = ""

# 加载示例按钮
col1, col2 = st.columns([1, 5])
with col1:
    if st.button("📋 加载示例"):
        code_input = '''class Calculator:
    """简单计算器"""
    
    def add(self, a, b):
        return a + b
    
    def divide(self, a, b):
        return a / b

def process_items(items=[]):
    """处理项目列表"""
    result = []
    for item in items:
        result.append(item * 2)
    return result

def read_config(path):
    f = open(path, "r")
    try:
        return f.read()
    except:
        return None
    finally:
        f.close()
'''
        st.session_state["code_input"] = code_input
        st.rerun()

# ===== 审查按钮 =====
if code_input and st.button("🔍 开始审查", type="primary", use_container_width=True):
    issues, score = review_code(code_input)
    
    # 评分仪表盘
    st.divider()
    st.subheader("📊 审查结果")
    
    col1, col2, col3 = st.columns(3)
    
    # 总体评分
    score_color = "green" if score >= 80 else ("orange" if score >= 60 else "red")
    col1.metric("总分", f"{score}/100")
    
    # 问题统计
    high_count = sum(1 for i in issues if i["severity"] == "high")
    med_count = sum(1 for i in issues if i["severity"] == "medium")
    low_count = sum(1 for i in issues if i["severity"] == "low")
    
    col2.metric("🔴 严重", high_count)
    col3.metric("🟡 建议", med_count + low_count)
    
    # 评分条
    st.progress(score / 100)
    
    if score >= 80:
        st.success("✅ 代码质量良好!")
    elif score >= 60:
        st.warning("⚠️ 有一些问题需要关注")
    else:
        st.error("🔴 存在较多问题,建议优先修复")
    
    # 问题列表
    if issues:
        st.divider()
        st.subheader(f"🔍 发现 {len(issues)} 个问题")
        
        for i, issue in enumerate(issues):
            severity_icon = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(issue["severity"], "⚪")
            
            with st.container():
                cols = st.columns([0.5, 8, 1.5])
                cols[0].markdown(f"### {

severity_icon}")
                cols[1].markdown(f"**{issue['message']}**  \n*建议:{issue['suggestion']}*")
                cols[2].caption(f"`{issue['category']}`")
                
                if i < len(issues) - 1:
                    st.divider()
    else:
        st.success("🎉 未发现明显问题!")

# ===== 空状态 =====
if not code_input:
    st.info("👆 粘贴代码或上传文件,然后点击「开始审查」")

运行

bash
streamlit run app.py

浏览器打开 http://localhost:8501。你会看到一个清爽的审查面板——粘贴代码,点按钮,秒出结果。每个问题有 emoji 严重程度标记、具体描述和修改建议。

项目三总结

从"读一个 py 文件"到"Web 端的代码审查面板"——六个步骤,跟项目一和项目二一样扎实。

核心你掌握了
Day 1代码解析骨架ast 模块解析 Python AST,提取类/函数/注释统计
Day 2LCEL 审查链四维度评分(命名/职责/文档/安全),MockLLM 模拟审查
Day 3并发审查asyncio.gather() 同时审查多文件,整个项目一起审
Day 4审查缓存SHA256 文件哈希判重,只审"脏文件"
Day 5MCP 服务化三个工具(片段/文件/项目审查),标准协议调用
Day 6Streamlit 面板Web 界面,粘贴代码即审查,可视化评分和问题列表

三个项目,一套招式

回头看这 18 天(6+6+6),你三次重复了同一个核心模式:

能力项目一:客服项目二:文档项目三:代码
搭骨架回声筒函数TextLoaderAST 解析
结构化输出客服回复ContractInfoCodeReview
并发处理多用户对话多文档分析多文件审查
记忆/缓存LangGraph MemorySaver文档内容哈希缓存文件内容哈希缓存
MCP 服务化客服工具 MCP文档分析 MCP代码审查 MCP
Web 部署FastAPI 部署Streamlit 界面Streamlit 面板

不是三个不同的项目学了三套不同的东西——而是一套东西通过三个不同的场景反复强化。你现在面对一个新的 AI 需求,脑子里会自动浮现这张表:输入是什么、输出结构长什么样、要不要并发、要不要缓存、怎么暴露出 MCP 工具。

这就是这本书第 16-18 章的价值——不是你学会了三个项目,而是你形成了一套 Agent 开发的思维模型。

下一步可以去哪里

三个项目给了你扎实的 AI Agent 开发基础。接下来你可以:

  • 换领域尝试:不用客服、文档、代码。试试做营销文案生成、数据分析日报、法律合同比对——底层全是一样的套路
  • 接入真实 LLM:把三个项目里的 MockLLM 替换为 OpenAI/Claude/Gemini 的 API,代码不需要重构——MockLLM 挡住的只是 LLM 调用那一步
  • 进阶阅读:LangGraph 的更多模式(Human-in-the-loop、Multi-agent)、LangChain 的更多组件(Agents、Toolkits、Callbacks)

🎉 项目三完成,实战篇全三章完成。Agent 能读代码、审查质量、并发审多文件、缓存优化、MCP 服务化、Web 面板部署。跟项目一和项目二一起——你已经有能力从零开发一个完整的 AI Agent 应用了。