前言

        在信息爆炸的时代,如何高效管理知识成为每个学习者和创作者的必修课。你是否也曾梦想拥有一个属于自己的知识库网站,将碎片化的笔记、灵感、学习资料整合成一个随时可访问的智慧宝库?现在,借助AI的力量,这个梦想可以更轻松地实现。

        今天,我将带你走进一个独特的实践之旅——在VS Code中,通过AI辅助编写Python代码,从零开始搭建一个个人知识库网站。

        我们不仅会用代码赋予网站生命,更会探索如何让AI成为我们的‘编程助理’,加速开发流程,让技术真正服务于知识管理。无论你是编程新手还是有一定经验的开发者,这段旅程都将为你揭示:当人类智慧与AI协作时,创造力的边界究竟在哪里。

本篇文章需要先安装部署好AI大模型,主要教你如何使用AI开发,

本文主要用的是TRAE AI: Coding Assistant 

没部署的可以看看:进阶使用VS Code:解锁AI编程助手的引擎模式-CSDN博客

找到合适自己的AI大模型

📝 核心思路:用 AI 生成「带 YAML frontmatter 的 Markdown 模板」

你的知识库需要的是有规范结构的 Markdown 文件,而不仅仅是空白文件。关键在于:

  1. YAML frontmatter(文件头部的元数据区块):存储标题、标签、日期等信息
  2. 内容模板:提示你后续要填写什么内容
  3. 命名规则:用日期+标题命名,方便系统自动索引

备齐“知识库原材料”

没有初始的 Markdown 文件,整个知识库系统就无法启动。别担心,我来教你如何让 AI(如 VS Code 的 Copilot 或通义灵码)帮你自动生成结构化、带元数据的 Markdown 模板文件

第一步:让 AI 生成「单个 Markdown 模板文件」

你是一个知识管理专家,请帮我生成一个符合知识库标准的 Markdown 文件模板,要求:
1. 文件头部必须包含 YAML frontmatter,字段包括:
   - title: 自动生成一个简洁的标题(例如"Python 装饰器原理笔记")
   - date: 使用当前日期(格式:2026-05-20)
   - tags: 包含3个相关标签(如["python", "编程技巧", "学习笔记"])
   - category: 默认为"技术笔记"
2. 正文部分要有结构化提示:
   - 用"## 核心概念"开头,提示用户填写关键知识点
   - 用"## 代码示例"提示用户插入代码块
   - 用"## 个人思考"引导反思
3. 整个文件用 UTF-8 编码,注释用中文

第二步:个人思考

(记录你对装饰器的理解、使用场景或注意事项)


---

#### 第二步:让 AI 批量生成「多主题模板文件」
**给 VS Code AI 的指令:**
```text
我需要为知识库创建5个初始 Markdown 文件,主题覆盖:Python、AI 工具、时间管理、读书笔记、项目复盘。
请按以下规则生成:
1. 每个文件用"日期_主题关键词.md"命名(例如"20260520_python_decorator.md")
2. 每个文件必须包含第一步要求的 YAML frontmatter 和结构化正文
3. 不同主题的标签要差异化:
   - Python 主题:标签含["python", "代码实践"]
   - AI 工具:标签含["ai", "效率工具"]
   - 时间管理:标签含[" productivity", "方法论"]
   - 读书笔记:标签含["reading", "summary"]
   - 项目复盘:标签含["retrospective", "lessons"]
4. 用代码块形式输出所有文件内容,文件名作为代码块标题

第三步:让 AI 自动创建这些文件到指定文件夹

💡 关键注意事项

  1. 先运行这个脚本:执行后会在项目根目录生成 knowledge_base 文件夹,里面包含5个带结构的笔记
  2. frontmatter 是关键:你的后续程序(如文件扫描器)会依赖这个区块提取元数据
  3. 立即开始填充内容
    • 打开任意 .md 文件
    • 在 ## 核心概念 等提示位置填写你的实际笔记
    • 保存后,你的知识库系统就能自动识别新内容

编写代码来读取、处理并展示笔记

现在文件有了,你需要一个 Python 脚本去把这些文件的内容读出来,变成程序能理解的数据。

第一步:编写“读取器” (数据层)

“我现在有一个文件夹 knowledge_base,里面有很多 .md 文件(就像刚才生成的那样)。
请帮我写一个 Python 类 KnowledgeBase,包含一个方法 load_all_posts()。
功能要求:
遍历 knowledge_base 文件夹。
使用 python-frontmatter 库读取每个文件的 YAML 头部信息(title, date, tags)和正文内容。
把所有文章的信息存储在一个列表中返回,列表里的每一项是一个字典,包含文件的元数据和正文。”

上面那一步会要弄一些环境和依赖,VScode会自动生成安装依赖的命令,直接点运行就好了

第二步:编写“转换器”(逻辑层)

“我已经成功读取了所有文章的数据。现在请帮我写一个函数 convert_markdown_to_html(markdown_text)。
具体要求:
使用 Python 的 markdown 库将 Markdown 文本转换为 HTML 字符串。
必须开启 extensions=['fenced_code', 'codehilite', 'tables'],这样代码块会有语法高亮,表格也能正常显示。
请修改我之前的 load_all_posts 逻辑(或者写一个新的处理函数),在读取文章时,自动把 post['content'] 转换成 HTML 格式,并存入 post['html_content']。
最后写一段测试代码:读取‘AI 工具应用完全指南’这篇文章,打印出转换后的 HTML 代码,让我看看效果。”

为什么要做这一步?

  • Markdown 是给人类看的纯文本(比如用 # 表示标题)。
  • HTML 是给浏览器看的标记语言(比如用 <h1> 表示标题)。
  • 你的博客生成器核心工作就是做这个翻译官

第三步:编写“展示器” (界面层 - 最核心部分)

你需要用 Flask 搭建一个简单的网站来展示你的知识库。

“我要用 Flask 框架做一个简单的博客网站来展示我的知识库。
请帮我写一个 app.py:
首页 (/):显示所有文章的列表(标题、日期、标签),点击标题可以跳转到详情页。
详情页 (/post/<filename>):显示具体文章的内容(要把 Markdown 转成 HTML 显示)。
使用 Jinja2 模板引擎,写两个简单的 HTML 模板(index.html 和 post.html)。
样式稍微美化一下,让阅读体验舒适一点。”

  • Flask 服务器已经成功启动(终端显示 Running on http://127.0.0.1:5000)。
  • 项目结构已经成型:有了 app.py(主程序)、knowledge_base_manager.py(数据逻辑)和 templates 文件夹(网页模板)。
  • 数据加载正常:终端显示“已加载”了所有你的 Markdown 笔记文件。

填充网页模板

目前的 index.html 和 post.html 很可能只是空的骨架或者静态的占位符。我们需要让 AI 帮你把这两个文件写满代码,实现“从 Python 读取数据 -> 渲染到 HTML 页面”的连接。

第一步:完善首页 (index.html)

这个页面需要展示所有文章的列表。

“我正在编写 Flask 项目的 templates/index.html 文件。
请帮我写一个漂亮的首页模板,要求:
继承自基础样式(如果有的话,或者写完整的 HTML 结构)。
使用 Jinja2 语法 {% for post in posts %} 遍历后端传来的 posts 列表。
在页面中显示每篇文章的:
标题(作为链接,指向 /post/<filename> 详情页)
发布日期
标签列表
文章内容的简短摘要(截取前 100 个字符)
样式要求简洁现代,卡片式布局,使用 CSS 让页面居中且美观。”

第二步:完善详情页 (post.html)

这个页面用于展示单篇文章的完整内容(包含转换后的 HTML)。

“接下来请帮我编写 templates/post.html 文件,用于显示单篇文章详情。
要求:
接收后端传来的 post 对象。
页面顶部显示文章标题、日期和标签。
页面主体部分使用 {{ post.html_content | safe }} 来渲染转换后的 HTML 内容(注意:必须加 | safe 过滤器,否则 HTML 标签会直接显示出来而不是渲染)。
页面底部加一个‘返回首页’的按钮。
请为代码块添加 CSS 样式,确保代码高亮显示清晰易读。”

第三步:连接后端逻辑 (app.py)

最后,我们需要确保 app.py 把数据正确传给这两个页面。

“请帮我完善 app.py 的路由逻辑:
导入 KnowledgeBase 类并实例化。
在 @app.route('/') 首页路由中:
调用 kb.load_all_posts() 获取所有文章。
使用 render_template('index.html', posts=posts) 将数据传给首页。
在 @app.route('/post/<filename>') 详情页路由中:
根据 filename 读取单篇文章的详情(包括转换后的 HTML)。
使用 render_template('post.html', post=post) 将数据传给详情页。
请确保代码能处理文件找不到的情况(返回 404)。”

创建好之后怎么开启

好像直接点go live 会出现问题,(这个需要一个拓展,如下图)Live Preview

有这个拓展在右下角,会有一个go live 会直接打开浏览器,跳转到网页页面,这就能在本地打开开发出的网页,像我之前的这篇文章要学使用 CyberChef 吗?这一篇就够了_cyberchef 用法-CSDN博客

如果不是本地打开就会出现问题,功能不全

要用flask环境开启

最后在浏览器打开http://127.0.0.1:5000,就成功了

可能出现的问题:

出现如图情况,是因为你的 Flask 程序目前处于“自动目录浏览”模式,还未设置具体的首页路由。

从截图看,Flask 正在把你当前的项目文件夹(~/)当作一个静态文件服务器,列出了所有文件和文件夹。这说明你的 app.py 中缺少了主页的路由函数,或者该函数没有正确返回渲染后的 HTML 模板。

你需要回到 VS Code,让 AI 帮你补全 app.py 的核心逻辑。

解决方法

请直接在 VS Code 中打开 app.py,然后给 AI 发送以下指令:

“我的 Flask 网站打开后只显示了文件目录列表(如截图所示),而不是漂亮的网页。

请帮我重写 app.py 的核心代码,实现以下功能:

导入依赖:引入 Flask、render_template 以及我们写好的 knowledge_base_manager 模块。
设置首页路由 (/):
定义一个函数 index()。
调用 knowledge_base_manager 里的方法,读取所有文章数据。
使用 render_template('index.html', posts=所有文章数据) 把数据传给网页。
设置详情页路由 (/post/<filename>):
定义一个函数 show_post(filename)。
根据文件名找到对应的文章,转换成 HTML。
使用 render_template('post.html', post=文章数据) 显示详情。
请确保代码完整,可以直接运行。”

为什么会这样?(原理解释)

  • 现状:Flask 找不到 @app.route('/') 对应的函数,或者你的代码里写了 os.listdir() 这种列出文件的逻辑,所以它默认把文件夹里的内容展示给你看了。
  • 目标:我们需要告诉 Flask:“当有人访问首页时,不要去列文件,而是去读 templates/index.html 这个文件,并把读到的笔记数据填进去显示出来。”

最终显示,就是已经成功打开了,之后在浏览器打开:http://127.0.0.1:5000,(最后的5000是AI代码的时候使用的端口,如果你1用的端口不一样的话,改成自己的就行了)

最后继续完善各篇文章内容

因为最开始的命令只是做了个框架,具体内容并没有,那么我想完善一下能怎么操作呢

网页只是忠实地读取并展示了你文件里的内容。要完善文章内容,你需要去 VS Code 里找到对应的 .md 文件,把里面的“提示语”替换成真正的干货。

第一步:在 VS Code 中找到对应的文件

  1. 看你的浏览器地址栏,URL 是:
    .../post/20260520_ai_tools.md
  2. 回到 VS Code 左侧的资源管理器(文件列表)。
  3. 找到 knowledge_base 文件夹。
  4. 在里面找到并点击打开 20260520_ai_tools.md 这个文件。

第二步:利用 AI 填充内容(懒人高效法)

打开文件后,你会看到里面全是“请在此处插入...”的提示语。不要手动打字,直接选中这些文字,然后右键选择“在聊天中询问”(或者用快捷键唤起 AI),输入以下提示词:

“我正在写一篇关于‘AI 工具应用’的笔记。
请根据这个标题和下面的结构提示,帮我生成具体的内容。
要求:

  1. 核心概念:解释什么是 AI 工具,以及它的工作原理。
  2. 代码示例:给出一个 Python 调用 OpenAI API 的简单代码块。
  3. 个人思考:谈谈 AI 对工作效率的提升。
    请直接输出 Markdown 格式的内容,替换掉原来的提示语。”

AI 生成后,你把生成的内容复制粘贴到文件里,替换掉原来的提示语即可。

第三步:保存并刷新

  1. 在 VS Code 中按下 Ctrl + S 保存文件。
  2. 回到浏览器,按下 F5 或 Ctrl + R 刷新网页。

你会发现网页上的内容瞬间变成了充实的文章!(为了展示内容,页面缩小了,后文也一样)

后面的其他文章操作都类似,接下来我粘贴剩余的AI命令,你可以直接发给AI来充实别的内容

操作方法: 在 VS Code 中分别打开这四个 .md 文件,把对应的指令发给 AI(比如通义灵码、Copilot 或 ChatGPT),让它帮你生成内容,然后替换掉原来的占位符即可。

1. 针对《读书笔记精华汇总》

文件对应: 20260520_reading_notes.md

AI 指令:

我正在整理读书笔记,请帮我填充《读书笔记精华汇总》的内容。
请假设我读的是《卡片笔记写作法》这本书,帮我生成一段核心概念介绍。
要求:

  1. 解释什么是“卡片笔记法”(Zettelkasten),以及它如何帮助知识复利。
  2. 语言风格要简洁、有启发性。
  3. 生成一段关于“如何将读书笔记转化为写作素材”的实操建议。
    请直接输出 Markdown 格式。

2. 针对《项目复盘实战经验》

文件对应: 20260520_project_retrospective.md

AI 指令:

请帮我完善《项目复盘实战经验》这篇文章。
主题是关于“软件开发项目的复盘方法论”。
请生成以下内容:

  1. 核心概念:解释什么是项目复盘(Retrospective),以及 KPT 法(Keep, Problem, Try)的具体含义。
  2. 实战案例:虚构一个“项目延期”的案例,演示如何用 KPT 表格进行分析。
  3. 总结:列出复盘的 3 个关键原则(如:对事不对人)。
    请用 Markdown 格式输出,保留二级标题结构。


3. 针对《Python 装饰器深度解析》

文件对应: 20260520_python_decorator.md

AI 指令:

请帮我填充《Python 装饰器深度解析》的技术内容。
目标读者是 Python 初学者。
请生成:

  1. 核心概念:用通俗易懂的比喻(比如“给函数穿衣服”)解释什么是装饰器。
  2. 代码示例:提供一个标准的装饰器代码模板(包含 @wraps),并给出一个计算函数运行时间的具体应用示例。代码中要有详细注释。
  3. 应用场景:列举装饰器在日志记录和权限验证中的用途。
    请直接输出 Markdown 格式。

4. 针对《高效时间管理方法论》

文件对应: 20260520_time_management.md

AI 指令:

请帮我完善《高效时间管理方法论》这篇文章。
请重点介绍“番茄工作法”和“艾森豪威尔矩阵”。
内容要求:

  1. 核心概念:分别简述这两个方法的原理。
  2. 实操步骤:列出执行番茄工作法的 5 个步骤。
  3. 个人思考:写一段关于“如何平衡紧急与重要任务”的心得体会。
    请用 Markdown 格式输出,结构清晰。


最后提醒:
生成内容后,记得在 VS Code 中按 Ctrl + S 保存文件,然后去浏览器刷新页面,你就能看到满满干货的笔记了!

进阶优化

可能有朋友会想:“如果我想继续优化知识库页面呢,这个是不是只能在VScode里添加笔记,不能直接在页面添加,我想在页面上直接搞个前后端交互,生成笔记文件”

那么答案是:

你现在的知识库本质上是一个静态文件浏览器,它只负责读取和展示你已经在 VS Code 里写好的 .md 文件,并不具备“写入”功能。

如果想实现“在网页上直接写笔记,自动保存到电脑里”,这确实需要开发后端写入功能

这个需求完全可以实现,逻辑如下:

  1. 前端:在网页上加一个“新建笔记”按钮和一个输入框。
  2. 后端:在 app.py 里写一个函数,接收网页传来的内容,用 Python 的文件操作(open(..., 'w'))生成新的 .md 文件。

为了让你快速体验这个功能,我写了一个最小可行性版本的代码。你只需要修改 app.py 和 index.html 两个文件。

第一步:修改后端 (app.py)

我们需要增加两个功能:

  1. 显示新建页面(GET 请求)。
  2. 处理保存逻辑(POST 请求):接收数据 -> 生成文件 -> 保存。

要实现“在网页上写笔记并自动保存为文件”,本质上是要给知识库增加“写入”功能(增删改查中的“增”和“改”)。

既然你希望用 AI 在 VS Code 里辅助生成代码,你可以直接把下面这段超级指令发给你的 AI 助手(比如通义灵码、Copilot 或 ChatGPT)。这段指令包含了前后端交互、文件写入和页面跳转的完整逻辑。

下面给你一段代码指令,已完成上述修改过程:

我正在开发一个基于 Flask 的个人知识库网页应用。目前我只能查看 Markdown 文件,我想增加一个“在线写笔记”的功能。

请帮我完成以下代码修改,实现“前后端交互生成笔记文件”的功能:

### 需求描述
1. 在首页 (`index.html`) 增加一个“写新笔记”的按钮。
2. 点击按钮跳转到一个新页面 (`create.html`),包含表单:标题输入框、内容输入框(文本域)、保存按钮。
3. 后端 (`app.py`) 需要接收表单数据,将其保存为 `knowledge_base` 文件夹下的 `.md` 文件。
4. 文件名格式要求:`YYYYMMDD_标题拼音或英文.md`,内容包含 YAML Front Matter(标题、日期、标签)。
5. 保存成功后自动跳转回首页。

### 请提供以下代码
1. `app.py` 的新增路由代码(`/create` 用于显示页面,`/save` 用于处理保存逻辑)。
2. `templates/create.html` 的完整代码(包含简单的 CSS 样式)。
3. `templates/index.html` 中需要添加的按钮代码片段。

### 注意事项
- 请确保文件保存时处理中文编码(utf-8)。
- 请使用 `slugify` 或简单的字符串替换来处理文件名,避免特殊字符报错。

💡 指令解析(让你知道 AI 会帮你做什么)

发送上述指令后,AI 会为你生成一套完整的增删改查(CRUD)逻辑:

后端 (app.py)
  • @app.route('/create'): 这是一个“展示层”,负责把你写的 HTML 表单页面显示在浏览器上。
  • @app.route('/save', methods=['POST']): 这是一个“逻辑层”。
    • 它会使用 request.form 获取你在网页输入框里填的内容。
    • 它会使用 Python 的 open(filename, 'w', encoding='utf-8') 命令,在你的硬盘上真正创建一个新文件。
    • 它会自动拼接 YAML 头部信息(如 title: xxxdate: xxx),保证新文件符合你知识库的格式。

给出指令后,AI会告诉你,直接将app.py的文件代码,修改成如下的代码,添加新路由

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flask 博客网站 - 知识库展示
"""

from flask import Flask, render_template, request, redirect, url_for
from knowledge_base_manager import KnowledgeBase, convert_markdown_to_html
import os
from datetime import datetime
import re

# 创建 Flask 应用
app = Flask(__name__)

# 创建知识库实例
kb = KnowledgeBase()

def slugify(text):
    """将文本转换为适合作为文件名的格式"""
    # 移除特殊字符
    text = re.sub(r'[^\w\s-]', '', text)
    # 替换空格为下划线
    text = re.sub(r'[\s_-]+', '_', text)
    # 转换为小写并去除首尾下划线
    return text.lower().strip('_')

@app.route('/')
def index():
    """首页:显示所有文章列表"""
    posts = kb.load_all_posts()
    return render_template('index.html', posts=posts)

@app.route('/post/<filename>')
def show_post(filename):
    """详情页:显示单篇文章内容"""
    posts = kb.load_all_posts()
    post = None
    for p in posts:
        if p['filename'] == filename:
            post = p
            break
    
    if not post:
        return "文章未找到", 404
    
    if 'html_content' not in post:
        post['html_content'] = convert_markdown_to_html(post['content'])
    
    return render_template('post.html', post=post)

@app.route('/create')
def create():
    """显示写笔记页面"""
    return render_template('create.html')

@app.route('/save', methods=['POST'])
def save():
    """处理保存笔记请求"""
    # 获取表单数据
    title = request.form.get('title', '').strip()
    content = request.form.get('content', '').strip()
    
    # 验证数据
    if not title:
        return "标题不能为空", 400
    
    # 生成文件名
    current_date = datetime.now().strftime('%Y%m%d')
    slug = slugify(title)[:50]  # 限制长度
    filename = f"{current_date}_{slug}.md"
    
    # 生成 YAML Front Matter
    date_str = datetime.now().strftime('%Y-%m-%d')
    tags = ["笔记", "原创"]  # 默认标签
    
    frontmatter = f"""---
title: {title}
date: {date_str}
tags: ["{'", "'.join(tags)}"]
category: 技术笔记
---

"""
    
    # 完整内容
    full_content = frontmatter + content
    
    # 保存文件
    save_path = os.path.join('knowledge_base', filename)
    try:
        with open(save_path, 'w', encoding='utf-8') as f:
            f.write(full_content)
        print(f"文件已保存: {save_path}")
    except Exception as e:
        return f"保存失败: {str(e)}", 500
    
    # 跳转到首页
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)
前端 (create.html)
  • AI 会生成一个包含 <form> 标签的页面。
  • 关键点在于 action="/save" 和 method="POST",这就像给表单装了一个“发射器”,点击保存时,数据就会飞向你在 app.py 里写的 /save 接口。

在templates文件夹里创建create.html文件,然后复制进去以下代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>写新笔记 - 知识库</title>
    <style>
        :root {
            --primary-color: #3b82f6;
            --primary-hover: #2563eb;
            --bg-color: #f8fafc;
            --card-bg: #ffffff;
            --text-primary: #1e293b;
            --text-secondary: #64748b;
            --border-color: #e2e8f0;
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background-color: var(--bg-color);
            min-height: 100vh;
        }
        
        .container {
            max-width: 800px;
            margin: 0 auto;
            padding: 40px 24px;
        }
        
        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 32px;
        }
        
        .back-link {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            color: var(--primary-color);
            text-decoration: none;
            font-weight: 500;
            transition: color 0.2s;
        }
        
        .back-link:hover {
            color: var(--primary-hover);
        }
        
        h1 {
            font-size: 1.8rem;
            color: var(--text-primary);
        }
        
        .form-card {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 32px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
            border: 1px solid var(--border-color);
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 8px;
            font-size: 0.95rem;
        }
        
        input[type="text"] {
            width: 100%;
            padding: 12px 16px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            font-size: 1rem;
            font-family: inherit;
            transition: border-color 0.2s, box-shadow 0.2s;
        }
        
        input[type="text"]:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }
        
        textarea {
            width: 100%;
            padding: 12px 16px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            font-size: 1rem;
            font-family: 'Consolas', 'Monaco', monospace;
            min-height: 300px;
            resize: vertical;
            line-height: 1.6;
            transition: border-color 0.2s, box-shadow 0.2s;
        }
        
        textarea:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }
        
        .form-actions {
            display: flex;
            gap: 12px;
            justify-content: flex-end;
        }
        
        .btn {
            padding: 12px 28px;
            border-radius: 8px;
            font-weight: 500;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.2s;
            border: none;
        }
        
        .btn-primary {
            background-color: var(--primary-color);
            color: white;
            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
        }
        
        .btn-primary:hover {
            background-color: var(--primary-hover);
            transform: translateY(-1px);
            box-shadow: 0 6px 16px rgba(59, 130, 246, 0.35);
        }
        
        .btn-secondary {
            background-color: #f1f5f9;
            color: var(--text-secondary);
        }
        
        .btn-secondary:hover {
            background-color: #e2e8f0;
        }
        
        .hint {
            font-size: 0.85rem;
            color: var(--text-secondary);
            margin-top: 8px;
        }
        
        @media (max-width: 640px) {
            .container {
                padding: 24px 16px;
            }
            
            .form-card {
                padding: 20px;
            }
            
            .form-actions {
                flex-direction: column;
            }
            
            .btn {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <a href="/" class="back-link">
                <span>←</span>
                <span>返回首页</span>
            </a>
            <h1>写新笔记</h1>
        </header>
        
        <form action="/save" method="POST" class="form-card">
            <div class="form-group">
                <label for="title">标题 *</label>
                <input type="text" id="title" name="title" placeholder="输入笔记标题" required>
            </div>
            
            <div class="form-group">
                <label for="content">内容</label>
                <textarea id="content" name="content" placeholder="在这里写笔记内容...

支持 Markdown 格式:
- 使用 ## 表示二级标题
- 使用 **粗体** 和 *斜体*
- 使用 ```python 表示代码块
- 使用 - 或 1. 表示列表"></textarea>
                <p class="hint">💡 支持 Markdown 语法,保存后会自动渲染为 HTML</p>
            </div>
            
            <div class="form-actions">
                <a href="/" class="btn btn-secondary">取消</a>
                <button type="submit" class="btn btn-primary">保存笔记</button>
            </div>
        </form>
    </div>
</body>
</html>
自动化处理
  • 你担心的“文件名乱码”或“格式不对”,AI 会在代码里加入 datetime.now() 获取当前时间,并用简单的替换逻辑把中文标题变成合法的文件名(例如把空格变成下划线)。

更新 templates/index.html,添加"写新笔记"按钮等等,你可以按照你自己的想法给AI指令

可以直接给AI发指令说,给你一个可以直接替换的完整版修改代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>知识库 - 首页</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        :root {
            --primary-color: #3b82f6;
            --primary-hover: #2563eb;
            --secondary-color: #64748b;
            --bg-color: #f8fafc;
            --card-bg: #ffffff;
            --text-primary: #1e293b;
            --text-secondary: #64748b;
            --border-color: #e2e8f0;
            --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
            --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-primary);
            line-height: 1.6;
            min-height: 100vh;
        }
        
        .container {
            max-width: 850px;
            margin: 0 auto;
            padding: 40px 24px;
        }
        
        header {
            text-align: center;
            margin-bottom: 50px;
            position: relative;
        }
        
        .create-btn {
            position: absolute;
            top: 0;
            right: 0;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            background-color: var(--primary-color);
            color: white;
            text-decoration: none;
            padding: 10px 20px;
            border-radius: 8px;
            font-weight: 500;
            font-size: 0.95rem;
            transition: all 0.2s;
            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
        }
        
        .create-btn:hover {
            background-color: var(--primary-hover);
            transform: translateY(-1px);
            box-shadow: 0 6px 16px rgba(59, 130, 246, 0.35);
        }
        
        .logo {
            font-size: 3rem;
            margin-bottom: 12px;
        }
        
        h1 {
            color: var(--text-primary);
            font-size: 2.2rem;
            font-weight: 700;
            margin-bottom: 8px;
            letter-spacing: -0.5px;
        }
        
        .subtitle {
            color: var(--text-secondary);
            font-size: 1.1rem;
        }
        
        .post-count {
            text-align: center;
            color: var(--text-secondary);
            font-size: 0.95rem;
            margin-bottom: 30px;
            padding: 12px 20px;
            background-color: rgba(59, 130, 246, 0.05);
            border-radius: 8px;
            display: inline-block;
        }
        
        .post-grid {
            display: grid;
            gap: 24px;
        }
        
        .post-card {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 28px;
            box-shadow: var(--shadow-sm);
            border: 1px solid var(--border-color);
            transition: all 0.3s ease;
        }
        
        .post-card:hover {
            transform: translateY(-2px);
            box-shadow: var(--shadow-lg);
            border-color: var(--primary-color);
        }
        
        .post-header {
            margin-bottom: 16px;
        }
        
        .post-title {
            font-size: 1.45rem;
            font-weight: 600;
            margin-bottom: 10px;
        }
        
        .post-title a {
            color: var(--text-primary);
            text-decoration: none;
            transition: color 0.2s;
        }
        
        .post-title a:hover {
            color: var(--primary-color);
        }
        
        .post-meta {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 16px;
            color: var(--text-secondary);
            font-size: 0.9rem;
        }
        
        .meta-item {
            display: flex;
            align-items: center;
            gap: 6px;
        }
        
        .meta-icon {
            font-size: 0.85rem;
        }
        
        .post-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 16px;
        }
        
        .tag {
            background-color: rgba(59, 130, 246, 0.1);
            color: var(--primary-color);
            padding: 5px 12px;
            border-radius: 20px;
            font-size: 0.8rem;
            font-weight: 500;
            transition: all 0.2s;
        }
        
        .tag:hover {
            background-color: rgba(59, 130, 246, 0.2);
        }
        
        .post-excerpt {
            color: var(--text-secondary);
            font-size: 0.95rem;
            line-height: 1.7;
            margin-bottom: 20px;
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        
        .read-more {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            color: var(--primary-color);
            text-decoration: none;
            font-size: 0.9rem;
            font-weight: 500;
            transition: all 0.2s;
        }
        
        .read-more:hover {
            color: var(--primary-hover);
            gap: 10px;
        }
        
        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: var(--text-secondary);
        }
        
        .empty-state .icon {
            font-size: 4rem;
            margin-bottom: 20px;
            opacity: 0.5;
        }
        
        footer {
            text-align: center;
            margin-top: 60px;
            padding-top: 30px;
            border-top: 1px solid var(--border-color);
            color: var(--text-secondary);
            font-size: 0.9rem;
        }
        
        @media (max-width: 640px) {
            .container {
                padding: 24px 16px;
            }
            
            h1 {
                font-size: 1.8rem;
            }
            
            .create-btn {
                position: static;
                display: inline-block;
                margin-bottom: 16px;
            }
            
            .post-card {
                padding: 20px;
            }
            
            .post-title {
                font-size: 1.25rem;
            }
            
            .post-meta {
                gap: 12px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <a href="/create" class="create-btn">✏️ 写新笔记</a>
            <div class="logo">📚</div>
            <h1>我的知识库</h1>
            <p class="subtitle">记录学习 · 分享知识 · 持续成长</p>
        </header>
        
        {% if posts %}
            <div class="post-count">📝 共 {{ posts|length }} 篇文章</div>
            
            <div class="post-grid">
                {% for post in posts %}
                <article class="post-card">
                    <div class="post-header">
                        <h2 class="post-title">
                            <a href="/post/{{ post.filename }}">{{ post.title }}</a>
                        </h2>
                        <div class="post-meta">
                            <span class="meta-item">
                                <span class="meta-icon">📅</span>
                                <span>{{ post.date }}</span>
                            </span>
                            {% if post.category %}
                            <span class="meta-item">
                                <span class="meta-icon">📁</span>
                                <span>{{ post.category }}</span>
                            </span>
                            {% endif %}
                        </div>
                    </div>
                    
                    <div class="post-tags">
                        {% for tag in post.tags %}
                        <span class="tag">{{ tag }}</span>
                        {% endfor %}
                    </div>
                    
                    <p class="post-excerpt">
                        {{ post.content[:100] }}{% if post.content|length > 100 %}...{% endif %}
                    </p>
                    
                    <a href="/post/{{ post.filename }}" class="read-more">
                        阅读全文
                        <span>→</span>
                    </a>
                </article>
                {% endfor %}
            </div>
        {% else %}
            <div class="empty-state">
                <div class="icon">📭</div>
                <h2>暂无文章</h2>
                <p>还没有发布任何文章,快来分享你的知识吧!</p>
            </div>
        {% endif %}
        
        <footer>
            <p>© 2026 我的知识库 · 用 Flask 构建</p>
        </footer>
    </div>
</body>
</html>

效果演示

  1. 重启 python app.py
  2. 如果报错,大概率就还是因为python环境多,运行的python命令不是用的有flask的环境,直接截图给AI,他会帮你找到可以用的环境,然后给你生成合适的命令,你再运行就好了这样就是可以了
  3. 打开网页127.0.0.1:5000,就有新建笔记的选项了,点击“新建笔记”。
  4. 输入标题“测试新笔记”,内容“Hello World”。
  5. 点击保存。
  6. 见证奇迹:网页跳回首页,你去 VS Code 的 knowledge_base 文件夹里看一眼,会发现多了一个 .md 文件,内容就是你刚才写的!

进阶建议

如果你想让它更完美,未来可以考虑:

  • Markdown 实时预览:引入 SimpleMDE 或 Editor.md 这样的前端库,让你写的时候右边就能看到渲染效果。
  • 编辑器优化:现在的 textarea 比较简陋,可以换成更高级的富文本编辑器。

然后你想做其他的功能,都和上面步骤差不多,相信你自己可以做到的,我这就不再演示了

注意!

值得注意的是,当前默认情况下,这个知识库只能在你自己的电脑上访问,别人是进不来的。具体原因和解决方案如下:


为什么别人访问不了?

当前 Flask 服务器默认运行在 http://127.0.0.1:5000,这是本地回环地址,意思是只有你自己的电脑能访问这个地址。


如何让别人也能访问?

方案一:局域网内共享(适合宿舍/办公室)

如果你和其他人在同一个 WiFi/局域网内,可以修改 app.py 的运行方式:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

然后告诉其他人你的本机IP地址,格式是:

http://你的IP地址:5000

如何查看本机IP?

Windows 命令行输入:

ipconfig

找到类似 IPv4 地址: 192.168.1.105 这样的地址。


方案二:部署到互联网(适合公开访问)

如果想让外网的人也能访问,需要把项目部署到云服务器,比如:

  • 阿里云、腾讯云、华为云
  • 免费平台如 PythonAnywhere、Heroku

部署步骤

  1. 购买一个云服务器
  2. 安装 Python 和必要依赖
  3. 上传代码到服务器
  4. 配置反向代理(如 Nginx)
  5. 绑定域名(可选)

方案三:生成静态网页(最简单)

如果你只是想分享内容,可以把所有 Markdown 文章转换成静态 HTML 文件,然后上传到任何静态网站托管平台:

  • GitHub Pages(免费)
  • Vercel(免费)
  • Netlify(免费)

总结

方式 适用场景 是否需要服务器
本地运行 自己用 不需要
局域网共享 宿舍/办公室 不需要
云服务器部署 公开访问 需要
静态网页 分享内容 不需要(托管平台免费)

当前最简单的选择:如果只是想和身边的人分享,用方案一修改代码即可;如果想长期公开访问,建议用方案三生成静态网页。


md文件转化成word文件

在之后,如果你要做实验报告或是其他内容文件,你可以将要求给VScode,让他给你生成md文件,如果需要word文件:

🚀 方法一:使用 VS Code 插件(最推荐,无需联网)

这是最无缝的体验,直接在你的编辑器里完成。

  1. 安装插件
    • 在 VS Code 左侧活动栏点击扩展图标(或按 Ctrl+Shift+X)。
    • 搜索 Markdown All in One 并安装(如果你还没有安装的话)。这个插件是 Markdown 写作的神器。
  2. 预览文档
    • 打开你的 .md 实验报告文件。
    • 按 Ctrl+Shift+V 或者右键选择“打开预览”,在右侧会看到渲染后的漂亮效果。
  3. 导出为 Word
    • 在预览窗口的右上角,你会看到几个图标。
    • 点击 “...” (更多操作) 图标。
    • 选择 导出 -> 导出为 Docx
    • 选择保存位置即可。

🌐 方法二:使用 Pandoc 命令行(最专业,格式最准)

Pandoc 是文档转换的“瑞士军刀”,转换质量极高,且能保留复杂的格式(如代码块、数学公式等)。

  1. 安装 Pandoc
  2. 打开终端
    • 在 VS Code 中,按  Ctrl+  打开集成终端。
    • 确保你的终端当前路径是 .md 文件所在的文件夹。(不是的话cd过去就行)
  3. 执行命令
    • 输入以下命令并回车:
      pandoc your_report.md -o 实验报告.docx
    • (把 your_report.md 换成你的文件名)

⚡ 方法三:使用在线转换工具(最快,无需安装)

如果你不想安装任何东西,也不想离开浏览器,这是最快的方法。

  1. 上传文件
    • 打开浏览器,搜索“Markdown 转 Word”或访问专门的转换网站(如 cloudconvert.com 或 ilovepdf.com)。
  2. 转换并下载
    • 点击“选择文件”上传你的 .md 文件。
    • 选择输出格式为 Word (.docx)
    • 点击“转换”按钮,等待几秒钟后下载文件。

💡 小贴士

  • 格式微调:无论用哪种方法,转换后的 Word 文档可能需要稍微调整一下页边距、字体或标题样式,以符合你们学校的实验报告格式要求。
  • 图片路径:如果你的 Markdown 里引用了本地图片(例如 ![](images/fig1.png)),在转换时请确保图片文件夹和 .md 文件在同一个目录下,或者使用在线工具时一并上传,否则 Word 里可能会显示图片丢失。

结语

        从在VS Code中敲击第一行代码,到最终看到一个能动态生成笔记、自动保存文件的知识库网站呈现在眼前,这段用AI辅助开发的旅程,不仅是技术的实践,更是对效率与创造力的一次重新定义。我们见证了AI如何将繁琐的代码编写转化为灵感的加速器,让‘想法→实现’的路径变得更短、更顺畅。

        现在,你的知识库已经初具雏形,但它远未止步——你可以继续探索更多可能性:集成搜索功能、优化Markdown渲染、甚至让AI帮你总结笔记内容……技术的魅力在于迭代,而知识管理的价值在于积累。

        希望这篇文章能为你点燃探索的热情:用代码构建你的知识宇宙,让AI成为你思维的同盟。

        开始行动吧,属于你的知识库网站,正等待着被赋予更多可能。记住,每一次代码的提交,都是向更高效的知识管理迈进一步。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐