end
离线原创故事生成 Agent。后端使用 FastAPI + LangGraph 编排并行本地工具,工具从 data/story_index 检索故事结构、风格片段、场景卡、人物卡、冲突卡、剧情节拍、对话模式、感官细节、结尾模式和安全边界,再把结构化结果交给本地 llama-server 首轮生成完整故事。前端使用 React + Vite + TypeScript,保留工具轨迹、节点轨迹、LLM prompt 和吞吐指标面板。
data/story_index
llama-server
以下命令均从 Story-agent 仓库根目录执行。默认 llama.cpp 与本项目位于同一上级目录,模型位于上两级目录下的 models 目录;如果你的目录布局不同,请相应调整相对路径。
Story-agent
llama.cpp
models
终端 1 启动 llama-server,以 qwen2.5-7b 为例:
cd ../llama.cpp ./build-cann/bin/llama-server \ -m ../../models/qwen2.5/7b/qwen2.5-7b-instruct-q4_0-00001-of-00002.gguf \ -ngl 99 \ -np 4 \ --device CANN0,CANN1 \ -sm layer \ -ts 1,1 \ -t 8 \ -c 131072 \ --predict 6144 \ --port 8080 \ --host 0.0.0.0 \ --alias qwen \ --temp 0.55 \ --repeat-penalty 1.12 \ --reasoning-budget 0 \ --cache-ram 0
qwen3-8b 或更大 Qwen 模型按实际模型路径替换 -m,其他参数保持一致。注意:-np 4 会把总上下文分给 4 个 slots;-c 131072 对应每个 slot 约 32768 tokens。若改成 -c 8192 -np 4,每个请求实际只有约 2048 tokens,故事 prompt 会超出上下文。
-m
-np 4
-c 131072
-c 8192 -np 4
终端 2 启动后端:
cd Story-agent PYTHONPATH=backend \ STORY_DATA_DIR=./data/story_index \ STORY_USE_LLM=true \ STORY_MAX_CONCURRENT_LLM=4 \ LLAMA_SERVER_URL=http://127.0.0.1:8080/v1 \ LLAMA_MODEL=qwen \ LLAMA_REPEAT_PENALTY=1.12 \ LLAMA_FIRST_TOKEN_TIMEOUT_SECONDS=0 \ LLAMA_TIMEOUT_SECONDS=3600 \ LLAMA_MAX_TOKENS=6144 \ LLAMA_CONTEXT_TOKENS=32768 \ STORY_PROMPT_MATERIAL_CHARS=1400 \ .venv/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
终端 3 启动前端:
cd Story-agent/frontend npm install npm run build npm run preview -- --host 0.0.0.0 --port 5173
这里使用 preview 而不是 dev,避免 Vite 开发服务器创建大量文件监听器。如果终端三出现 ENOSPC: System limit for number of file watchers reached,通常不是前端代码错误,而是系统 watcher 数量限制触顶。
preview
dev
ENOSPC: System limit for number of file watchers reached
访问:
http://127.0.0.1:5173/
curl http://127.0.0.1:8000/health curl -N -X POST http://127.0.0.1:8000/api/chat/stream \ -H 'Content-Type: application/json' \ -d '{"message":"写一个悬疑现实主义单篇完整长故事。背景是南方雨夜的旧车站,主角是一名刚离职的系统工程师,他在候车室发现一台仍在运行的旧服务器。风格克制、有压迫感,包含人物心理、环境细节、对话和反转结尾。"}'
长 Prompt 压测使用 scripts/benchmark_long_prompt_concurrency.py。脚本会让后端在应用 llama-server 聊天模板后,通过 /tokenize 将最终 Prompt 精确校准到指定 token 数, 并允许独立控制 decode 长度。
scripts/benchmark_long_prompt_concurrency.py
/tokenize
16K Prompt 无法使用普通启动参数中的 -np 4 -c 32768,因为每个 slot 只有约 8192 tokens。压测时把 llama-server 启动命令中的上下文参数改为:
-np 4 -c 32768
-np 4 \ -c 131072 \ --predict 6144
此时每个 slot 约有 32768 tokens。后端启动参数相应改为:
STORY_MAX_CONCURRENT_LLM=4 \ LLAMA_MAX_TOKENS=6144 \ LLAMA_CONTEXT_TOKENS=32768
启动 llama-server 和后端后,运行 20 并发、16K Prompt、3072-token decode:
PYTHONPATH=backend .venv/bin/python scripts/benchmark_long_prompt_concurrency.py \ -c 20 \ --target-llama-prompt-tokens 16384 \ --max-tokens 3072 \ --output logs/prefill-16k-decode-3k-c20.json
参数简要说明:
-c 20
--target-llama-prompt-tokens 16384
--max-tokens
--ignore-eos
--output
例如要求每个请求尽量完成 6144-token decode:
PYTHONPATH=backend .venv/bin/python scripts/benchmark_long_prompt_concurrency.py \ -c 20 \ --target-llama-prompt-tokens 16384 \ --max-tokens 6144 \ --ignore-eos \ --output logs/prefill-16k-decode-6k-c20.json
后端会校验 Prompt tokens + max_tokens <= LLAMA_CONTEXT_TOKENS。当前配置是 20 个客户端并发请求、4 个模型 slot 同时推理,其余请求在后端排队;这与 STORY_MAX_CONCURRENT_LLM=4 和 llama-server 的 -np 4 保持一致。该命令不依赖 前端,运行压测时只需启动 llama-server 和 FastAPI 后端。
Prompt tokens + max_tokens <= LLAMA_CONTEXT_TOKENS
STORY_MAX_CONCURRENT_LLM=4
入口:
data/story_index/manifest.json
主要文件:
story_structures.json
style_fragments.json
setting_cards.json
character_cards.json
conflict_cards.json
plot_beats.json
dialogue_patterns.json
sensory_details.json
ending_patterns.json
safety_rules.json
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802047560号
Story Agent
离线原创故事生成 Agent。后端使用 FastAPI + LangGraph 编排并行本地工具,工具从
data/story_index检索故事结构、风格片段、场景卡、人物卡、冲突卡、剧情节拍、对话模式、感官细节、结尾模式和安全边界,再把结构化结果交给本地llama-server首轮生成完整故事。前端使用 React + Vite + TypeScript,保留工具轨迹、节点轨迹、LLM prompt 和吞吐指标面板。当前能力
启动
以下命令均从
Story-agent仓库根目录执行。默认llama.cpp与本项目位于同一上级目录,模型位于上两级目录下的models目录;如果你的目录布局不同,请相应调整相对路径。终端 1 启动 llama-server,以 qwen2.5-7b 为例:
qwen3-8b 或更大 Qwen 模型按实际模型路径替换
-m,其他参数保持一致。注意:-np 4会把总上下文分给 4 个 slots;-c 131072对应每个 slot 约 32768 tokens。若改成-c 8192 -np 4,每个请求实际只有约 2048 tokens,故事 prompt 会超出上下文。终端 2 启动后端:
终端 3 启动前端:
这里使用
preview而不是dev,避免 Vite 开发服务器创建大量文件监听器。如果终端三出现ENOSPC: System limit for number of file watchers reached,通常不是前端代码错误,而是系统 watcher 数量限制触顶。访问:
验证
20 并发 16K Prefill 压测
长 Prompt 压测使用
scripts/benchmark_long_prompt_concurrency.py。脚本会让后端在应用 llama-server 聊天模板后,通过/tokenize将最终 Prompt 精确校准到指定 token 数, 并允许独立控制 decode 长度。16K Prompt 无法使用普通启动参数中的
-np 4 -c 32768,因为每个 slot 只有约 8192 tokens。压测时把 llama-server 启动命令中的上下文参数改为:此时每个 slot 约有 32768 tokens。后端启动参数相应改为:
启动 llama-server 和后端后,运行 20 并发、16K Prompt、3072-token decode:
参数简要说明:
-c 20:同时发起 20 个 HTTP 请求。--target-llama-prompt-tokens 16384:Agent 传给 llama-server 的最终 Prompt 精确为 16384 tokens。--max-tokens:控制每个请求的最大 decode 长度,范围为 1-6144。--ignore-eos:可选;忽略模型 EOS,使性能测试尽量 decode 到--max-tokens。--output:保存每个请求和汇总后的 TTFT、prefill、decode、吞吐及错误信息。例如要求每个请求尽量完成 6144-token decode:
后端会校验
Prompt tokens + max_tokens <= LLAMA_CONTEXT_TOKENS。当前配置是 20 个客户端并发请求、4 个模型 slot 同时推理,其余请求在后端排队;这与STORY_MAX_CONCURRENT_LLM=4和 llama-server 的-np 4保持一致。该命令不依赖 前端,运行压测时只需启动 llama-server 和 FastAPI 后端。数据库
入口:
主要文件:
story_structures.jsonstyle_fragments.jsonsetting_cards.jsoncharacter_cards.jsonconflict_cards.jsonplot_beats.jsondialogue_patterns.jsonsensory_details.jsonending_patterns.jsonsafety_rules.json