news 2026/9/5 8:20:40

角色互动应用开发实战:用角色卡、对话API与TTS打造AI角色聊天Demo

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
角色互动应用开发实战:用角色卡、对话API与TTS打造AI角色聊天Demo

“Can I get a kiss,sunset?”——这句话如果出现在《小马宝莉》同人社区里,往往不是一声简单的告白,而是一种创作驱动:粉丝想让余晖烁烁(Sunset Shimmer)真的看见自己、回应自己。传统同人创作里,角色是被写出来的,你发一张图、写一段文,小马不会回头看你一眼。但从技术开发的视角看,这句话其实是一个非常好的起点:如果你想让余晖烁烁真的“开口”和你聊天,当前的开发工具链完全支持低成本实现。

这篇文章不是讲同人创作,而是以“余晖烁烁”这个角色为例,讲清楚如何把一类角色向粉丝应用从想法变成可运行的程序:用角色卡(Character Card)定义人格,用对话模型接口实现聊天,用语音合成(TTS)让它说话,再用前端页面把它们串成一个能点开就聊的网页应用。看完这篇文章,你能跑通一个本地可运行的角色互动 demo,也能判断这类应用在真实项目中该怎么设计、该避开哪些坑。

1. 这篇文章真正要解决的问题

先泼一盆冷水:很多第一次做“角色 AI”的开发者,以为难点在模型本身,于是花大量时间折腾微调、RAG、向量数据库。实际做下来你会发现,对单角色互动应用来说,微调和 RAG 都是可选项,真正的难点在另外四件事:

第一,角色的“人格”如何稳定复现。同一个余晖烁烁,在不同轮对话里不能说两句就变成一个完全陌生的角色。这个问题靠提示词工程和角色卡规范就能解决,不需要训练模型。

第二,对话接口怎么接。你需要一个能返回文本的模型服务,它可以是本地部署的开源模型,也可以是云端的合规服务。关键是接口格式要统一,最通用的是 OpenAI 兼容格式。

第三,角色能不能开口说话。文本聊天已经不够沉浸,用户希望角色有声音。这里需要接入 TTS 服务,并把语音文件返回给前端播放。

第四,前端怎么呈现。一个能聊天的网页,或者小程序,需要把用户输入、模型回复、语音播放三件事串起来。这部分如果不提前设计,很容易在联调时手忙脚乱。

文章核心判断:角色互动应用的本质,不是“训练一个角色模型”,而是“用工程手段把模型、语音和人机交互接口组合成一个符合角色设定的体验产品”。

因此这篇文章适合三类读者:

  • 想做粉丝向角色应用、IP 互动应用的独立开发者。
  • 想学习 Agent 对话架构、但对“角色人格”部分比较陌生的后端开发者。
  • 希望把大模型能力落地成可演示产品的产品经理或全栈工程师。

读完你会得到:一套完整的角色应用开发思路、可复制的代码示例、一份常见问题排查清单,以及关于合规和内容安全的工程建议。

2. 核心概念:角色互动的四个技术支柱

角色互动应用听起来很抽象,拆开就四块:角色人格、对话服务、语音输出、页面容器。每块都能用现有工具解决,不需要从零发明。

2.1 角色人格:角色卡(Character Card)

角色卡是给模型看的“剧本”,用结构化文本描述角色是谁、性格如何、说话风格怎样、有哪些不能做的禁忌。它本质上是一段精心设计的 System Prompt,只不过以标准化格式保存,方便复用和分发。

一个角色卡通常包含:

  • 角色名、身份背景。
  • 性格特征与说话风格。
  • 与用户的关系设定。
  • 禁止行为列表。
  • 若干示例对话。

为什么角色卡重要?因为大模型默认是“百科全知助手”风格,你问它太阳系有几颗行星,它列数据;你让它扮演角色,它容易演成“AI 扮演的角色”。要让它稳定,就必须把角色设定写得极其具体,不给模型“滑回助手模式”的空间。

2.2 对话服务:Chat Completion 接口

对话模型的核心接口范式是“多轮消息”。你把用户消息、历史记录、角色卡拼接成消息列表,模型返回一条回复。这个范式如今已经是行业标准,绝大多数模型服务都提供 OpenAI 兼容的 HTTP 接口。

一个最简对话请求的 JSON 结构:

{ "model": "your-chat-model", "messages": [ {"role": "system", "content": "你是余晖烁烁"}, {"role": "user", "content": "你好,今天心情怎么样?"} ] }

模型服务的选型影响成本、私密性和效果。如果是个人玩,可以用本地开源模型,保证数据不出机器;如果是做公开产品,要选有内容审核能力的云服务。文章后面的示例按“兼容 OpenAI 格式”来写,这样不管后端接什么服务,代码都不用改。

2.3 语音合成:TTS 让角色开口

TTS 的角色是“把文本变成声音”。到 2025 年,高质量的 TTS 已经不那么昂贵,甚至有一些免费或低成本的方案能满足 demo 需求。但真实产品落地时,建议选有商业授权、音色稳定、支持多情感控制的正式服务。

从工程角度,你需要的是一个“文本进,音频文件或音频流出”的接口。音频可以保存为 mp3 或 wav,然后交给前端播放。如果你的页面要支持按一下说话、再按一下停止,前端还需要处理音频播放状态。

2.4 页面容器:前端交互层

页面容器的任务是把用户输入发给后端、拿到回复后展示文字并播放语音。这个词听起来大,实际可以是一个单页 HTML 文件。对原型和 demo 来说,后端提供 API,前端负责交互,两者用 HTTP 通信,是最简单可靠的架构。

3. 整体架构与技术选型

基于上面四个支柱,一个适合个人开发和中小型产品的单角色互动应用架构可以这样设计:

  • 前端:一个 HTML 页面,负责聊天展示、输入、语音播放。
  • 后端:一个 Python 服务,负责接收前端请求、调用模型接口、调用 TTS、返回结构化响应。
  • 模型服务:独立部署的本地开源模型,或云端兼容 OpenAI 格式的服务。
  • TTS 服务:可选本地或云端,返回音频 URL;前端拿到后直接播放。

架构关系很简单:

用户浏览器 -> 前端页面(AJAX) -> Python后端 -> 模型对话服务 -> TTS服务 -> 返回音频URL

为什么用 Python 后端而不是 Node?对这个项目,Python 生态对大模型和 TTS 的封装更成熟,写起来最快。FastAPI 自带接口文档,联调时很方便。前端则用原生 HTML + JavaScript,不引入打包器,减少环境依赖,方便读者把代码粘到本地就能跑。

选型原则:

  • 对话模型:优先选支持多轮对话和系统提示词的开源模型,量化后的 7B~14B 模型在个人电脑上也能跑出不错效果。
  • TTS:demo 阶段优先简单方案,正式产品换成商业服务。
  • 前端:原生三件套,不引入复杂框架,避免学习成本。

4. 环境准备与前置条件

开始前需要准备以下环境。版本以当前实际安装为准,本文重点演示通用思路,不写死版本号。

  • 操作系统:Windows / macOS / Linux 均可。
  • Python:3.10 或更高版本。
  • 包管理工具:pip 或 uv。
  • 模型服务:本地部署的开源模型,或任意提供 OpenAI 兼容接口的云端服务。
  • Node.js:不是必需,但如果后面想用 npm 做静态托管,可以先装。

建议创建独立的 Python 虚拟环境,避免依赖冲突:

python3 -m venv venv source venv/bin/activate # Windows 上使用 venv\Scripts\activate

然后安装依赖:

pip install fastapi uvicorn openai pydantic requests edge-tts

如果你选择支持 OpenAI 兼容接口的模型服务,需要在本地启动它。以常见开源模型服务框架为例,先启动模型服务:

python -m vllm.entrypoints.openai.api_server \ --model your-model-path \ --served-model-name sunset-model \ --port 8001

如果本地显存不足,更简单的方式是连接云端服务地址。需要说明的是:这里的代码通过 base_url 指定服务地址,所以无论本地还是云端,后端代码都无需改动。

注意一点:如果你的模型服务启动在 8001 端口,后端 FastAPI 启动在 8000 端口,前端页面的请求必须指向 FastAPI,而不是直接指向模型服务。这样做的原因是:前端只和你的后端通信,后端统一转发到模型服务和 TTS,便于做参数控制、内容审核和日志记录。

5. 后端实现:角色卡与对话接口

后端是整个应用的核心,它承担三件事:加载角色卡、调用模型对话接口、调用 TTS 返回语音。

5.1 设计角色卡 Prompt

角色卡不只是一个名字,而是一段完整指令。以余晖烁烁为例,我设计了一个最小角色卡,重点在于“说话风格”和“禁止项”,这是让角色不跑偏的关键。

你是余晖烁烁(Sunset Shimmer),来自《小马宝莉》系列的独特角色, 你坚强、聪明、有点小骄傲,但内心温暖,重视朋友。 现在你正以人类学校的身份和一位新朋友聊天。 对话风格要求: 1. 语气自然,像朋友聊天,不要念百科。 2. 可以吐槽,但不要真正攻击对方。 3. 句子不要过长,控制在两三句话以内。 4. 不要提醒对方“我是 AI”。 5. 如果用户提到危险、违规、欺骗内容,明确拒绝并转移话题。 这是角色背景,但不是你需要背下来的知识,你要做的是带着这种性格 去聊天,而不是回答关于自己有多厉害的问题。

角色卡不要写成“角色小传”,而要写成“行为准则”。模型不需要背故事,它需要知道怎么说话。

5.2 FastAPI 对话接口代码

新建项目目录,结构如下:

sunset-chat/ ├── app.py ├── role_card.txt ├── static/ │ └── index.html └── requirements.txt

先写后端app.py

# app.py from fastapi import FastAPI from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from openai import OpenAI app = FastAPI() # 模型服务的地址,按实际配置修改 MODEL_BASE_URL = "http://127.0.0.1:8001/v1" MODEL_NAME = "sunset-model" client = OpenAI(base_url=MODEL_BASE_URL, api_key="EMPTY") # 读取角色卡 with open("role_card.txt", "r", encoding="utf-8") as f: ROLE_CARD = f.read().strip() class ChatMessage(BaseModel): content: str class ChatRequest(BaseModel): message: str class ChatResponse(BaseModel): reply: str audio_url: str = "" @app.post("/api/chat") async def chat(req: ChatRequest): messages = [ {"role": "system", "content": ROLE_CARD}, {"role": "user", "content": req.message}, ] try: resp = client.chat.completions.create( model=MODEL_NAME, messages=messages, temperature=0.8, max_tokens=300, ) reply = resp.choices[0].message.content.strip() except Exception as e: return JSONResponse(status_code=502, content={"error": str(e)}) return ChatResponse(reply=reply) @app.get("/") async def index(): return FileResponse("static/index.html") app.mount("/static", StaticFiles(directory="static"), name="static")

这段代码的关键点:

  • MODEL_BASE_URL指向兼容 OpenAI 接口的模型服务地址。
  • 角色卡放在系统消息里,确保每次对话都带着角色人格。
  • temperature设成 0.8,让回复自然但不至于太散。
  • 异常统一捕获,方便前端拿到明确错误信息。

不要在这一步把 TTS 接进去,先把纯文本流程跑通,再逐步加语音。工程上这叫“先打通主干,再加枝叶”。

6. 语音合成:让角色开口说话

对话接口返回文本后,下一步就是让角色把话说出来。这一步在工程上叫 TTS 结果与对话接口的异步集成。

6.1 本地 TTS 方案:edge-tts 演示

edge-tts是一个 Python 库,可以直接把文本合成为 mp3 文件。它调用的是微软边缘服务接口,对个人开发和 demo 来说非常方便。如果是正式商用,请换成有明确授权的 TTS 服务。

写一个独立的 TTS 服务脚本,用 FastAPI 暴露接口:

# tts_service.py import asyncio import uuid import edge_tts from fastapi import FastAPI from fastapi.responses import FileResponse from pydantic import BaseModel app = FastAPI() OUTPUT_DIR = "static/audio" VOICE = "zh-CN-XiaoxiaoNeural" # 可换音色 class TTSRequest(BaseModel): text: str @app.post("/api/tts") async def tts(req: TTSRequest): file_id = str(uuid.uuid4()) output_path = f"{OUTPUT_DIR}/{file_id}.mp3" communicate = edge_tts.Communicate(req.text, VOICE) await communicate.save(output_path) return {"audio_url": f"/static/audio/{file_id}.mp3"}

然后在app.py中把 TTS 调用接进/api/chat

import requests # 需要在开头导入 # 修改后的 chat 接口段落 @app.post("/api/chat") async def chat(req: ChatRequest): messages = [ {"role": "system", "content": ROLE_CARD}, {"role": "user", "content": req.message}, ] try: resp = client.chat.completions.create( model=MODEL_NAME, messages=messages, temperature=0.8, max_tokens=300, ) reply = resp.choices[0].message.content.strip() except Exception as e: return JSONResponse(status_code=502, content={"error": str(e)}) # 调用 TTS try: tts_resp = requests.post( "http://127.0.0.1:8002/api/tts", json={"text": reply}, timeout=30, ) audio_url = tts_resp.json().get("audio_url", "") except Exception: audio_url = "" return ChatResponse(reply=reply, audio_url=audio_url)

这里把 TTS 服务独立部署在 8002 端口,对话服务通过 HTTP 调用它。好处是解耦:以后换 TTS 供应商,只需要改tts_service.py,对话逻辑不受影响。

6.2 TTS 的工程坑

TTS 最容易踩的坑有两个:

一是文本长度。角色回复可能是一整段,直接交给 TTS 会合成出“念课文”的平淡效果。更好的做法是对话模型生成时控制输出长度,或者在后端做简单分段,遇到句号、问号就拆开再合成。

二是音频路径和跨域问题。前端通过 URL 访问音频文件时,如果前端页面和后端不在同一个端口,会触发跨域。最简单的方法是前后端同源:统一让 FastAPI 托管前端页面和音频文件,前端只请求同一个域名下的接口。

7. 前端聊天页实现

前端页面需要完成三件事:渲染聊天记录、发送用户消息、播放返回的语音。我用原生 HTML + CSS + JavaScript 实现,不引入构建工具。

创建static/index.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sunset Chat</title> <style> * { box-sizing: border-box; } body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f5f3fa; margin: 0; padding: 20px; } .chat-container { max-width: 640px; margin: 0 auto; background: white; border-radius: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); overflow: hidden; } .chat-header { background: #7c6bb5; color: white; padding: 16px 20px; font-size: 18px; font-weight: 600; } .chat-messages { height: 480px; overflow-y: auto; padding: 20px; background: #faf8ff; } .msg { margin-bottom: 16px; max-width: 80%; } .msg.user { margin-left: auto; text-align: right; } .msg.bot { margin-right: auto; } .bubble { display: inline-block; padding: 10px 14px; border-radius: 16px; background: white; box-shadow: 0 1px 4px rgba(0,0,0,0.06); } .msg.user .bubble { background: #7c6bb5; color: white; } .msg .meta { font-size: 12px; color: #999; margin-bottom: 4px; } .audio-btn { margin-top: 6px; font-size: 13px; color: #7c6bb5; cursor: pointer; background: none; border: none; text-decoration: underline; } .chat-input { display: flex; gap: 10px; padding: 14px; border-top: 1px solid #eee; } .chat-input input { flex: 1; border: 1px solid #ddd; border-radius: 24px; padding: 10px 16px; outline: none; font-size: 14px; } .chat-input button { border: none; background: #7c6bb5; color: white; padding: 10px 20px; border-radius: 24px; font-size: 14px; cursor: pointer; } </style> </head> <body> <div class="chat-container"> <div class="chat-header">余晖烁烁 · 在线</div> <div class="chat-messages" id="messages"></div> <div class="chat-input"> <input type="text" id="input" placeholder="想对余晖烁烁说点什么?"> <button id="sendBtn">发送</button> </div> </div> <script> const messagesBox = document.getElementById('messages'); const inputBox = document.getElementById('input'); function addMessage(role, text) { const div = document.createElement('div'); div.className = 'msg ' + role; div.innerHTML = '<div class="bubble">' + text + '</div>'; messagesBox.appendChild(div); messagesBox.scrollTop = messagesBox.scrollHeight; } function addAudioButton(url) { if (!url) return; const div = document.createElement('div'); div.className = 'msg bot'; div.innerHTML = '<button class="audio-btn" onclick="playAudio(\'' + url + '\')">播放语音</button>'; messagesBox.appendChild(div); } function playAudio(url) { const audio = new Audio(url); audio.play(); } async function send() { const text = inputBox.value.trim(); if (!text) return; inputBox.value = ''; addMessage('user', text); const response = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({message: text}) }); if (!response.ok) { addMessage('bot', '请求失败,请检查后端服务状态。'); return; } const data = await response.json(); addMessage('bot', data.reply); addAudioButton(data.audio_url); } document.getElementById('sendBtn').addEventListener('click', send); inputBox.addEventListener('keydown', function(e) { if (e.key === 'Enter') send(); }); </script> </body> </html>

这个前端页面的思路是:用户输入后点击发送,通过 fetch 请求/api/chat,拿到replyaudio_url后,把文字渲染到聊天框,如果有音频地址,就加一个“播放语音”按钮。

这里对 XSS 做一点提示:上面示例用innerHTML直接渲染文本,对 demo 来说能跑,但如果做公开产品,必须改成用textContent渲染,防止用户输入注入恶意脚本。这个点我在后面最佳实践部分还会强调。

8. 本地联调与部署上线

代码写完后,按照下面顺序启动服务,就能看到完整效果。

8.1 启动顺序

第一步:启动模型服务。这步需要在你的模型服务目录里执行,端口保持 8001。

第二步:启动 TTS 服务:

uvicorn tts_service:app --host 0.0.0.0 --port 8002

第三步:启动后端主服务:

uvicorn app:app --host 0.0.0.0 --port 8000

打开浏览器访问http://127.0.0.1:8000/,应该能看到聊天页面。

8.2 验证接口

后端启动后,建议先用 curl 验证接口,排除前端问题:

curl -X POST http://127.0.0.1:8000/api/chat \ -H "Content-Type: application/json" \ -d '{"message":"你好,还记得你的朋友吗?"}'

预期返回 JSON:

{ "reply": "当然记得,朋友对我来说比什么荣誉都重要。", "audio_url": "/static/audio/xxxx.mp3" }

如果返回502,说明模型服务地址或模型名称配置不对,查看命令终端输出。如果audio_url为空,说明 TTS 服务没启动,或者请求超时。

8.3 用 Docker Compose 一键部署(可选)

如果你想在服务器上部署,可以用 Docker Compose 把三个服务编排起来。先写三个服务的 Dockerfile,然后用下面这个 compose 文件:

# docker-compose.yml version: "3.9" services: model-api: image: your-model-server-image ports: - "8001:8001" tts: build: . command: uvicorn tts_service:app --host 0.0.0.0 --port 8002 volumes: - ./static:/app/static ports: - "8002:8002" app: build: . command: uvicorn app:app --host 0.0.0.0 --port 8000 depends_on: - model-api - tts environment: - MODEL_BASE_URL=http://model-api:8001/v1 ports: - "8000:8000"

注意:app.py里读取模型服务地址的代码需要改成从环境变量读取:

import os MODEL_BASE_URL = os.getenv("MODEL_BASE_URL", "http://127.0.0.1:8001/v1")

这样在 Docker 环境里,服务名model-api会自动解析到模型服务容器。

部署上线时,建议前置一层 Nginx,把 80 端口转发到 8000 端口,这样访客不需要带端口号访问。同时 Nginx 还可以帮你做静态文件缓存和 HTTPS 终止。

9. 常见问题与排查思路

做这套东西的时候,有几个问题出现频率非常高,整理成表格方便快速定位:

问题现象可能原因排查方式解决方案
访问页面 404FastAPI 没找到static/index.html查看项目目录结构;确认工作目录正确uvicorn app:app在项目根目录启动
聊天接口返回 502模型服务地址错误或服务未启动curl 模型服务的/v1/models接口确认模型服务端口和MODEL_BASE_URL
回复内容完全不像角色角色卡 Prompt 写得太短检查 system prompt 是否被加载补充说话风格和禁止项,参考第 5.1 节
回复到一半中断max_tokens太小查看返回的finish_reason增大max_tokens
返回没有audio_urlTTS 服务没启动或请求超时单独 curl TTS 接口启动 TTS 服务,确认端口
前端页面播放语音没声音跨域问题或路径错误打开浏览器开发者工具,查看网络面板确认音频 URL 可直接访问;前后端保持同源
接口响应很慢模型服务推理慢或 TTS 串行等待记录每步耗时;先关掉 TTS 测试换成更快的模型或并发调用 TTS
角色多轮后会“失忆”没有传历史消息列表看后端 messages 是否只传了一轮将多轮消息保存并拼接到 messages 中

这里特别说明历史消息问题。示例代码为了保持简短,只传了当前用户消息和系统角色卡。但真实场景里,用户上一句说“我喜欢音乐”,下一句说“那你推荐一首”,模型如果没记住上文,会答非所问。解决办法是前端或后端维护消息历史数组,每次请求把最近 N 条消息都传给模型。

维护历史消息时,要设置上限。比如只保留最近 20 条,超出就丢弃最早的。否则请求体越来越大,既增加模型服务压力,也拉长响应时间。

10. 最佳实践与工程建议

到这里,一个可运行的角色互动应用已经能跑通了。但如果要做成真实产品,以下几点值得认真对待。

10.1 角色卡要持续迭代

第一次写的角色卡效果通常不稳定。最好的做法是准备 20 到 30 条测试用例,覆盖普通聊天、角色相关提问、敏感话题、连续多轮对话,每次改完 Prompt 后跑一遍用例。角色卡迭代应该像代码迭代一样有版本记录,不要改完就忘。

10.2 强调内容安全与合规边界

角色互动应用面向用户时,内容安全是第一优先级。必须做三件事:

第一,输入和输出都做敏感内容过滤。用户可能输入引导性内容,模型也可能产生意外回复。在对接模型服务前和后端返回前端前,各加一道过滤或要求模型服务端启用内容审核。

第二,涉及角色 IP 的公开产品要获得授权。以“余晖烁烁”为例,角色形象和名称属于版权方,个人学习没问题,公开商业化必须确认授权边界。这里不是法律建议,但技术产品上线前应该主动排查 IP 风险。

第三,公开部署必须做用户访问控制、使用协议、举报反馈入口。如果提供语音合成能力,还要防止用户把语音用于诈骗、伪造身份等非法用途。

10.3 控制成本和延迟

模型服务和 TTS 都会产生成本和延迟。建议:

  • 对话模型使用流式输出,用户看到第一个字的时间会快很多。
  • TTS 请求只在用户点击播放时才触发,或者在前端缓存已经生成过的音频。
  • 高频问题可以做简单缓存,命中缓存直接返回,减少模型调用。

10.4 前端渲染使用 textContent

示例代码用innerHTML是为了简洁,但存在 XSS 风险。正式项目中,用户输入的内容绝不能直接插入 HTML,要使用textContent或成熟的模板引擎,并对模型输出做同样的处理。

11. 总结与下一步

回到开头那句“Can I get a kiss,sunset?”。这句话的价值不在于表面的表达,而在于它代表了一种需求:用户希望角色有回应。用本文的方法,你可以用角色卡、对话接口、TTS 和前端页面,把这种“回应”做成一个可运行的产品原型。技术栈不复杂,核心在于角色人格的稳定设计和工程链路的串联。

跑通文本聊天后,下一步值得尝试的方向是:为角色加入多轮记忆,让它能够记住用户偏好;接入实时语音识别,让用户可以直接说话而不是打字;把单角色升级成多角色群聊,让不同角色共享上下文。每往前走一步,产品的沉浸感受都会明显提升。

如果你准备用这套思路做自己的项目,建议先保存这个 demo,然后从一个你最熟悉的角色开始改 Prompt,设计一套你自己的测试用例。把第一版跑稳定,再谈扩展。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 3:45:59

大模型输出转Word格式全攻略:从Markdown到docx的自动化方案

我相信你一定遇到过这个场景&#xff1a;让大模型写一份周报、技术方案或者需求文档&#xff0c;生成内容在网页端看整整齐齐&#xff0c;标题是标题&#xff0c;代码块是代码块&#xff0c;表格也有模有样。结果复制到 Word 里&#xff0c;标题层级全没了&#xff0c;列表缩进…

作者头像 李华
网站建设 2026/9/6 3:45:00

基于TensorFlow和CNN的花朵识别系统:从训练到部署全流程实战

简介&#xff1a;花朵识别系统是一套基于Python与TensorFlow实现的深度学习图像分类项目&#xff0c;面向希望入门卷积神经网络&#xff08;CNN&#xff09;的开发者与机器学习爱好者。项目通过TensorFlow搭建CNN模型&#xff0c;可对多种花朵图片进行自动化识别与分类&#xf…

作者头像 李华
网站建设 2026/9/4 8:41:35

Unity古风场景搭建:Asian Dynasty Environment资源包导入与性能优化实战

最近在整理 Unity 场景资源时发现了这个挺适合中国风项目起步的素材包&#xff1a;Asian Dynasty Environment。它瞄准的是东方古风、王朝都城、江湖客栈这类场景需求&#xff0c;适合做独立游戏场景原型、数字孪生沙盘、古风动画短片甚至虚拟展厅。这次我们直接聊它的核心能力…

作者头像 李华
网站建设 2026/9/4 8:24:06

基于.NET的智能试卷生成系统:组卷算法与Word导出实践

简介&#xff1a;这是一份基于.NET平台、使用C#语言开发的试卷生成系统源码&#xff0c;适用于需要快速搭建在线题库与自动组卷功能的教育类项目或课程设计场景。系统支持人工选题与随机选题两种组卷模式&#xff1a;人工模式下可按题型勾选已有试题并设定分值&#xff0c;随机…

作者头像 李华
网站建设 2026/9/4 15:41:54

LaTeX技术写作指南:从入门到精通,高效排版数学公式与算法文档

这类标题看起来像社区里某个具体用户或内容的描述&#xff0c;而不是一个技术项目或工具。它更像是一个带有网络流行语标签的、指向某个特定创作者或现象的称呼。 对于这类非标准技术主题&#xff0c;直接写成技术博文会很奇怪。更合理的做法是把它当作一个引子&#xff0c;去…

作者头像 李华
网站建设 2026/9/4 9:17:20

AI编程插件实战:提示词设计让代码一次跑通

近期不少开发者在聊 AI 编程插件时&#xff0c;常会陷入一种“抽卡心态”&#xff1a;让 AI 生成一段代码&#xff0c;能不能跑&#xff0c;全看运气。有人一句话让 AI 写了 2048 小游戏&#xff0c;也有人让 AI 写个“Hello World”都能跑出五个报错。差距真的在模型智商吗&am…

作者头像 李华