一、前言平时我们想用AI写点小故事、随笔、文案要么需要联网、要么要充会员、要么担心内容上传泄露隐私。很多小伙伴电脑配置一般跑不动大型AI项目想找一个简单、干净、低配也能跑、断网也能用的AI文本生成工具真的很难。今天给大家分享我写的这款 Python Streamlit AI叙事生成工具主打一个免费、本地、干净、无脑运行。一共三种使用模式1.完全离线没网也能跑不用任何AI模型2. Ollama 本地大模型纯本地推理不上传数据3. 云端 API 接口追求高质量生成不管你是学生练手、做课程设计、平时写文案、写小故事这个项目都够用界面好看、功能齐全、代码无报错。二、项目能干啥真实实用功能不搞虚的直接说能用的功能四种写作风格都市日常、古风、悬疑、治愈温情日常写文案完全够用一键随机生成三套素材人物、场景、剧情冲突自动搭配不用自己苦想自定义角色和世界观想写校园、科幻、末世都可以自己改可调剧情长短、起伏程度想写短文、长文、平淡日常、跌宕剧情随便拖滑块支持续写上次写一半这次可以接着往下写历史记录保存复用喜欢的写作风格可以一键重来一键导出TXT/MD写完直接保存方便复制到公众号、文档、作业双主题界面深色炫酷、浅色干净看你喜好切换三、环境搭建超简单安装依赖新建 requirements.txt写入下面两行streamlit1.35.0requests终端输入安装命令pip install -r requirements.txt运行项目把代码保存为 main.py直接运行streamlit run main.py自动弹出网页界面不用前端、不用配置开箱即用。四、完整可运行源码已修复全部BUG全网最稳版本修复了 Ollama 接口报错、URL 解析异常等常见问题直接复制即可运行。源码-- coding: utf-8 --接地气 AI 故事生成工具离线 本地大模型 云端三模式低配电脑可跑、断网可用适合练手/课程设计/文案创作import streamlit as stimport randomimport timeimport requestsimport datetime页面全局配置st.set_page_config(page_title“AI故事生成工具”,page_icon“”,layout“wide”,initial_sidebar_state“expanded”)#初始化缓存保存历史、自定义角色if “feature_cache” not in st.session_state:st.session_state.feature_cache {}if “history_list” not in st.session_state:st.session_state.history_list []if “custom_char_lib” not in st.session_state:st.session_state.custom_char_lib []if “last_content” not in st.session_state:st.session_state.last_content “”界面主题切换def set_theme(mode):if mode “dark”:css “”“”st.markdown(css, unsafe_allow_htmlTrue)侧边栏主题theme_opt st.sidebar.radio(界面主题题, [“深色科技模式”, 浅色简约模式式])set_theme(“dark” if theme_opt “深色科技模式” else “light”)st.sidebar.divider()三种推理模式切换run_mode st.sidebar.radio(“运行模式”, [“纯离线使用无需网络”,“Ollama本地大模型不上网”,“云端API生成高质量”])api_key “”api_url “”ollama_host “http://127.0.0.1:11434/api/generate”ollama_model “qwen:7b”if run_mode “云端API生成高质量”:api_key st.sidebar.text_input(“API Key”, type“password”)api_url st.sidebar.text_input(“接口地址”)elif run_mode “Ollama本地大模型不上网”:ollama_host st.sidebar.text_input(“Ollama地址”, valueollama_host)ollama_model st.sidebar.text_input(“模型名称”, valueollama_model)历史记录复用st.sidebar.divider()st.sidebar.markdown(### 历史记录记录if st.session_state.history_list:t for idx, item in enumerate(st.session_state.history_list[-5:]) if st.sidebar.button(f复用记录 {idx1}“, keyfhis_{idx}”) st.session_state.feature_cache item[“feat”]]else:st.sidebar.caption(“暂无记录”)四大写作素材库style_material {“都市日常”: {“char”: [“内敛沉稳的老手艺人”,“不甘平庸的打工人”,“外冷内热的小店店主”,“不善言辞的程序员”],“scene”: [“深夜便利店”,“高层阳台”,“老街书店”,“夜市小摊”],“conflict”: [“老店即将拆迁”,“捡到匿名旧信”,“反复偶遇陌生人”,“旧信物勾起往事”]},“古风叙事”: {“char”: [“隐世郎中”,“落魄书生”,“镖局走卒”,“茶馆掌柜”],“scene”: [“烟雨古巷”,“山间茶寮”,“渡口木船”,“破败书院”],“conflict”: [“遗失祖传玉佩”,“故人十年之约”,“江湖旧仇浮现”,“一纸离别书信”]},“悬疑故事”: {“char”: [“档案管理员”,“私家寻访人”,“旧物收藏家”,“夜班守馆人”],“scene”: [“废弃码头”,“照相馆暗房”,“尘封储藏室”,“雾中小镇”],“conflict”: [“长辈隐藏秘密”,“无主遗物线索”,“诡异重复梦境”,“一模一样的信物”]},“治愈温情”: {“char”: [“旅行摄影师”,“图书馆管理员”,“返乡青年”,“糕点师傅”],“scene”: [“山间村落”,“绿皮火车”,“雪天酒馆”,“午后花店”],“conflict”: [“迟来多年留言”,“一场大雨邂逅”,“丢失的童年物件”,“萍水相逢的善意”]}emotion_list [“温暖治愈、结局美好”,“留白开放式、引人遐想”,“现实写实、略带遗憾”,“平淡日常、温柔治愈”]]离线文字微调微def weight_adjust(text):modifiers [“细微的”, “悄然的”, “静默的”, “温柔的”, “深沉的”, “淡淡的”]]return random.choice(modifiers) textxt离线生成核心不用任何模型、断网也能用def offline_generate(char, scene, conflict, emotion, world, length, drama, gen_type):scene weight_fluctuate(scene)conflict weight_fluctuate(conflict)len_tip “长篇详细描写” if length 0.7 else “简短精炼内容”drama_tip “剧情起伏大、冲突明显” if drama 0.6 else “日常平缓叙事”base_head f【离线生成结果】世界观{world}人物{char}场景{scene}核心剧情{conflict}整体风格{emotion}篇幅{len_tip}剧情强度{drama_tip}“”if gen_type “短篇文艺随笔”:body f““在{scene}平平淡淡的日子里{char}早已习惯了一成不变的生原本安稳平静的日常被{conflict}悄然打破。破。没有轰轰烈烈的剧情只有普通人藏在心底的细碎情绪整体风格{emotion}。””elif gen_type “完整故事大纲”:body f““1.铺垫描绘{scene}的日常氛围交代主角的生活状态2.转折{conflict}突然出现打破平静生活3.发展主角顺着线索慢慢探索发现背后的故事4.结尾以{emotion}的风格收束全文故事完整闭环。””else:body f暮色慢慢铺满{scene}{char}本以为又是普通平淡的一天直到{conflict}悄然出现。过往的回忆悄然翻涌平凡的日常多了一丝波澜整篇故事质感{emotion}。}。“”full_text base_head body if length 0.6: full_text \n\n生活大多是平淡的那些细碎的相遇、遗憾与温柔拼凑成了普通人最真实的人生。 return full_textOllama本地大模型调用def ollama_infer(char, scene, conflict, emotion, world, gen_type, model, host):prompt f写一段{gen_type}世界观{world}主角{char}场景{scene}剧情冲突{conflict}整体风格{emotion文笔自然流畅直接输出正文。文。payload {“model”: model, “prompt”: prompt, “stream”: False}try:res requests.post(host, jsonpayload, timeout30)return res.json()[“response”] if res.status_code 200 else “Ollama请求出错”except Exception as e:return f本地模型连接失败请确认Ollama已启动{str(e)}云端API调用def cloud_api_infer(char, scene, conflict, emotion, world, gen_type, key, url):prompt f生成一段{gen_type}世界观{world}人物{char}场景{scene}剧情冲突{conflict}风格{emotion输出高质量正文。文。headers {“Authorization”: fBearer {key}“, “Content-Type”: “application/json”}data {“model”: “general”,“messages”: [{“role”:“user”,“content”: prompt}],“temperature”:0.7}try:resp requests.post(url, headersheaders, jsondata, timeout25)return resp.json().get(“output”,{}).get(“text”,“接口返回数据异常”)except Exception as e:return 接口请求失败败{str(e)}”页面主体st.title(“ 免费AI故事生成器离线可用本地大模型”)mode_tip {“纯离线使用无需网络”: 零网络、零模型低配电脑随便跑随便跑,“Ollama本地大模型不上网”: “✅ 本地AI推理数据绝不外泄”,“云端API生成高质量”: “✅ 全网高质量文案生成”}st.subheader(mode_tip[run_mode])st.divider()参数设置区域col_a, col_b st.columns([1,1])with col_a:st.markdown(“### 创作参数设置”)style_sel st.selectbox(“写作风格”, list(style_material.keys()))custom_char st.text_inp自定义主角留空则自动随机动随机“)custom_world st.text_inp自定义世界观留空则默认为日常认日常”)gen_type st.selectbox(“生成类型”, [“短篇文艺随笔”, “完整故事大纲”, “小说开篇正文”])text_len st.slid文章篇幅比例文章篇幅, 0.1, 0.9, 0.4, 0.1)drama_level st.slid剧情起伏程度比例起伏程度, 0.1, 0.9, 0.3, 0.1)with col_b:st.markdown(“### 一键生成剧情素材”)refresh_3 st.button(“ 随机生成3套剧情方案”, use_container_widthTrue)add_custom_char st.text_input(“添加自定义人物素材”)if add_custom_char and st.button(“保存人物”):st.session_state.custom_char_lib.append(add_custom_char)st.suc保存成功下次可直接使用可以直接用)st.divider()mat_pool style_material[style_sel] if refresh_3 or not st.session_state.feature_cache: group_list [] for _ in range(3): ch random.choice(mat_pool[char] st.session_state.custom_char_lib) if st.session_state.custom_char_lib else random.choice(mat_pool[char]) group_list.append({ char: ch, scene: random.choice(mat_pool[scene]), conflict: random.choice(mat_pool[conflict]), emotion: random.choice(emotion_list) }) st.session_state.feature_cache group_list for idx, g in enumerate(st.session_state.feature_cache): st.markdown(f**方案{idx1}**) st.write(f人物{g[char]}场景{g[scene]}) st.write(f剧情{g[conflict]}风格{g[emotion]}) if st.button(f选用方案{idx1}, keyfuse_{idx}): st.session_state.active_feat gst.divider()continue_write st.checkbox(“开启上下文续写接着上次内容写”)run_btn st.button(“ 一键生成AI文案”, type“primary”, use_container_widthTrue)output_text “”if run_btn:if “active_feat” not in st.session_state:st.warning(“请先点击选用一套剧情方案”)else:feat st.session_state.active_featbar st.progress(0)for pct in range(0, 101, 10):bar.progress(pct)time.sleep(0.12)bar.empty()final_char custom_char.strip() if custom_char.strip() else feat[char] final_world custom_world.strip() if custom_world.strip() else 普通现实生活世界观 prefix f接续上文创作{st.session_state.last_content}\n if (continue_write and st.session_state.last_content) else if run_mode 纯离线使用无需网络: res_raw offline_generate(final_char, feat[scene], feat[conflict], feat[emotion], final_world, text_len, drama_level, gen_type) elif run_mode Ollama本地大模型不上网: res_raw ollama_infer(final_char, feat[scene], feat[conflict], feat[emotion], final_world, gen_type, ollama_model, ollama_host) else: res_raw cloud_api_infer(final_char, feat[scene], feat[conflict], feat[emotion], final_world, gen_type, api_key, api_url) output_text prefix res_raw st.session_state.last_content output_text st.session_state.history_list.append({feat: feat, content: output_text})结果展示与导出st.divider()st.markdown(“### 生成结果”)st.text_area(“文案内容”, output_text, height420)st.caption(f当前总字数{len(output_text)})if output_text:ts datetime.datetime.now().strftime(“%Y%m%d_%H%M%S”)c1, c2 st.columns(2)with c1:st.download_button(“ 导出 TXT 文件”, output_text, fAI文案_{ts}.txt)with c2:st.download_button(“ 导出 MD 文件”, f# AI故事生成结果\n{output_text}“, fAI文案_{ts}.md”)st.divider()st.caption(纯学习开源## 五、项目总结人话总结项这个项目最大的优点就是不折腾、不氪金、不联网也能用。氪不用部署复杂大模型、不用调参、不用花钱开会员低配电脑、学生机都能流畅运行。、平时写随笔、写小故事、凑文案、做课程设计、练习 Python Web 开发都完全够用。 代码干净无冗余、已知 BUG 已修复直接复制运行即可非常适合新手入门练手。常适合新手入门练手。