# draft **Repository Path**: code-me/draft ## Basic Information - **Project Name**: draft - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-18 - **Last Updated**: 2026-05-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # professional-writing-agent 通用专业文章写作 Agent(MVP):感知、规划、多角色执行、记忆与事件总线;支持 OpenAI 兼容 API、阿里云百炼(DashScope)及离线模板;`academic_db` 可调用 Semantic Scholar(失败时降级)。 ## 环境要求 - Python 3.10+ - (可选)网络与 API Key,用于云端大模型或文献检索 ## 快速开始 ### 方式一:脚本(推荐) ```bash ./scripts/setup.sh # 创建 .venv 并安装可编辑包与开发依赖 ./scripts/run-write-demo.sh # 使用离线模板跑一条示例(无需 API Key) ./scripts/run-interactive.sh # 进入终端交互模式(可设 LLM=template) ./scripts/run-tests.sh # 运行 pytest ``` 若脚本无执行权限: ```bash chmod +x scripts/*.sh ``` ### 方式二:手动 ```bash python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -e ".[dev]" ``` ## 命令行入口 安装后使用全局命令 `**writing-agent**`(由 `pip install -e .` 注册)。 | 用法 | 说明 | | ------------------------------------------ | ----------------------------------------------------------------- | | `writing-agent` | **不带子命令**:进入**类 OpenCode 的终端写作助手**(横幅、`›` 提示符、`/` 命令、自然语言一行即跑流水线) | | `writing-agent assistant` / `chat` / `tui` | 与上相同,显式进入助手模式 | | `writing-agent interactive` / `repl` / `i` | **经典 REPL**(`writing-agent>`,需先 `topic` 再 `run`) | | `writing-agent write` / `run` | 单次生成整条流水线(`run` 与 `write` 等价,便于脚本) | | `writing-agent schema` | 打印 AgentMessage 的 JSON Schema | | `writing-agent search …` | 联网搜索(需 `[search]`) | | `writing-agent files …` | 读写文件与目录(见下文) | ### 单次生成(`write` / `run`) ```bash # 默认可读排版;管道/重定向到非 TTY 时自动输出 JSON writing-agent write --topic "文章主题" --audience "读者" --lang zh # 输出格式:auto | json | text | html writing-agent write -t "主题" --format json # 强制 JSON(便于 jq) writing-agent write -t "主题" -F text # 强制可读文本 writing-agent write -t "主题" -F html -O out.html # 自包含 HTML,浏览器打开后用 KaTeX 渲染公式 # 流式:各阶段 LLM token 写入 stderr,最终完整结果仍按 --format 写入 stdout writing-agent write -t "主题" --stream --llm bailian # 自定义章节(逗号分隔,将并行起草;若开启 --stream 则顺序起草以避免输出交错) writing-agent write -t "主题" --sections "引言,分析,结论" # LLM 后端:auto | template | openai | bailian | dashscope writing-agent write -t "主题" --llm template # 离线,无需密钥 writing-agent write -t "主题" --llm bailian # 需 DASHSCOPE_API_KEY writing-agent write -t "主题" --llm openai # 需 OPENAI_API_KEY run 与 write 参数相同,例如: writing-agent run -t "主题" -F json ``` ### 助手模式(默认无子命令) - 提示符为 `**›**` ;以 `**/**` 开头为命令(如 `/help`、`/topic …`、`/run`)。**默认不带 /** 的整行与经典 REPL 相同,按命令词解析**(如 `topic …`、`run`);写作对话请用 `**/chat …`**。 - 环境变量 `**WRITING_ASSISTANT_PLAIN_DIALOG=1`** 或 `**WRITING_ASSISTANT_CHAT=1**`:无 `/` 的整行改为写作对话(不跑流水线)。`**WRITING_ASSISTANT_LINE_RUN=1**`:无 `/` 的整行设 topic 并直接跑全文流水线。 - 帮助:启动后输入 `/help` 查看完整列表;`/help <命令>` 查看单条命令的详细用法。输入错误命令时会自动提示相似命令(如"你是不是想输入: run")。 - **会话管理**:`/session save <路径>` 保存、`/session load <路径>` 恢复、`/session list` 列出、`/session delete <文件>` 删除。退出时自动保存到 `~/.writing_agent/sessions/auto_save.json`,下次启动提示恢复。 - **Tab 补全增强**:除命令名补全外,`lang`、`output`、`llm`、`style`、`session` 等命令的参数值也支持 Tab 补全。 ### 经典交互(`interactive`) ```bash writing-agent interactive --llm template writing-agent repl --llm auto -F text ``` 启动时 `--format`(`-F`)可设为 `auto` / `json` / `text` / `html`,会话内可用 `output` 命令修改。 常用命令:`help [命令]`(可查单条命令详情)、`topic …`、`output …`、`run`(或 `write` / 助手模式 `/w`,行尾可跟主题一次性开跑)、`show`(JSON 会话参数)、`search`、`from-file`、`save`、`stats`(写作统计)、`history`(命令历史)、`version`、`session save|load|list|delete`、`schema`、`quit`。 **反馈与自主学习(上下文学习)**:`feedback <文字>` 记录对上次输出的意见,`rate 1-5` 打分;后续 `run` 会将最近若干条反馈摘要注入各阶段提示(非模型训练)。`learned` 查看将注入的摘要,`learned-clear` 清空本地日志。命令行:`writing-agent feedback …`(可选 `-r` / `-t`)。 **会话文件根**:交互中可使用 `files-root <目录>`(助手模式为 `/files-root`)指定**仅当前会话**的文件相对根目录,优先级高于环境变量 `WRITING_AGENT_FILES_ROOT`;`files-root clear` 取消。`show` 的 JSON 中会显示 `files_root`。 **交互选择**:`pick llm`、`pick output`、`pick lang` 用**编号菜单**选择会话参数;安装可选依赖 `pip install -e ".[tui]"`(`questionary`)且在 TTY 下可用**↑↓ 箭头 + Enter**(`WRITING_AGENT_SIMPLE_MENU=1` 可强制始终用编号)。 **指令 Tab 补全**(需 `pip install -e ".[tui]"` 以安装 `prompt_toolkit`):在 shell 中执行 `export WRITING_AGENT_COMPLETE=1` 后再启动交互,输入命令首词时按 **Tab** 可补全指令名;可选 `WRITING_AGENT_COMPLETE_MATCH=prefix`(仅前缀匹配)、`WRITING_AGENT_COMPLETE_META=1`(可按说明文字子串匹配)。详见下表。 默认交互中 `run` 将各阶段 LLM **流式输出到 stderr**;环境变量 `**WRITING_AGENT_STREAM=0`**(或 `false`/`no`/`off`)可关闭。最终完整结果仍在 stdout。 ## 输出格式说明 | 格式 | 用途 | | -------- | -------------------------------------------------------------------------------------- | | **text** | 终端可读分节排版(默认 TTY 下 `auto` 选用) | | **json** | 完整流水线结果 JSON,便于脚本与 `jq` | | **html** | 单文件 HTML + **KaTeX(CDN)**,在浏览器中渲染 `$…$`、`$$…$$`、`\(…\)`、`\[…\]`;需联网加载 CDN,或配置镜像(见环境变量) | 终端中若正文为结构化 JSON(如出版工具包、小节 `content`+`metadata`),会自动 **美化排版**,避免大块原始 JSON 难以阅读。 ## 文件读取、生成与管理 - **流水线输入/输出** - `writing-agent write -t "主题" --input-file ./参考.md`:将本地 UTF-8 文本作为**参考附件**参与各阶段写作。 - 仅文件、省略主题时,主题默认为**文件名(不含扩展名)**。 - `writing-agent write -t "主题" -O ./out.txt`:将本次**与 --format 一致**的结果写入文件(标准输出仍会打印)。 - **独立文件命令**(可选沙箱根目录 `WRITING_AGENT_FILES_ROOT`):若已设置该变量,**相对路径**一律相对于该根目录解析(而非当前工作目录);未设置时相对路径仍相对于当前工作目录。 - `writing-agent files read <路径>` - `writing-agent files write <路径> -c "内容"` 或 `... --stdin` - `writing-agent files list [目录]` - `writing-agent files mkdir <路径>` - **交互模式**:`from-file /path`、`cat /path`、`save /path`、`ls`、`mkdir /path`。 - **限制**:默认单次读取上限约 5MB(`WRITING_AGENT_MAX_READ_BYTES`);若设置 `WRITING_AGENT_FILES_ROOT`,所有路径须落在该目录下。 ### 文档存储分层与推荐工作区(可选) 更完整的分层说明、与源码模块对应关系及图示见 **[docs/document-storage.md](docs/document-storage.md)**。 为长期写作项目建议区分三类内容(工具不强制目录名,仅供协作约定): 1. **仓库文档**:本 README、代码旁说明——面向安装与开发。 2. **用户写作资产**:参考稿、草稿、导出成品(`-input-file`、`write -O`、`files` 读写的正文与附件);体积可大,宜用普通文件存放。 3. **机读状态**:反馈 episode 等小体积、结构化日志。**勿**把完整长文塞进该 JSON,大段正文请用文件 + `references/` 或 `drafts/`。未设置显式 `WRITING_AGENT_FEEDBACK_PATH` 时:若已设置 `WRITING_AGENT_FILES_ROOT`,默认使用 `/.writing-agent/feedback_episodes.json`;否则为 `~/.writing_agent/feedback_episodes.json`。 4. **运行时记忆**:编排内的 `MemorySystem`(短/长/情节)仅在**当前进程**中有效,**不**写入磁盘;跨会话知识仍依赖文件与反馈 JSON。 未设置 `WRITING_AGENT_FILES_ROOT` 时,`files` 等命令相对于**当前工作目录**解析绝对路径,适合临时使用。长期项目可任选目录作为「工作区根」,并设置沙箱;反馈文件路径按 **显式 `WRITING_AGENT_FEEDBACK_PATH` > 工作区下 `.writing-agent/feedback_episodes.json`(仅当已设置 `WRITING_AGENT_FILES_ROOT`)> 用户目录默认** 解析。例如仅指定工作区根即可让反馈写入该工作区内: ```bash # 示例:工作区在 ~/MyWritingProject(反馈自动为 ~/MyWritingProject/.writing-agent/feedback_episodes.json) export WRITING_AGENT_FILES_ROOT="$HOME/MyWritingProject" # 可选:显式覆盖反馈路径 # export WRITING_AGENT_FEEDBACK_PATH="$HOME/MyWritingProject/.writing-agent/feedback_episodes.json" ``` 在 `WRITING_AGENT_FILES_ROOT` 下可采用如下布局(可自行调整): | 目录 | 用途 | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `references/` | `--input-file`、研究阶段引用的素材 | | `drafts/` | 中间稿、按主题或日期的子目录 | | `exports/` | `write -O` 或 `save` 的最终 HTML / JSON / 文本 | | `.writing-agent/` | 建议:存放 `feedback_episodes.json`(未设置 `WRITING_AGENT_FEEDBACK_PATH` 时会自动使用该路径);亦可显式用 `WRITING_AGENT_FEEDBACK_PATH` 指向任意文件,与全局 `~/.writing_agent` 隔离 | **反馈日志规模化(演进规划)**:当前实现为单文件 JSON,适合典型用量。若 episode 数量极大或需要按主题高效查询,未来可在保持 CLI 与提示注入语义不变的前提下,改为 SQLite 等存储并保留 JSON 导出;在此之前继续使用默认或 `WRITING_AGENT_FEEDBACK_PATH` 即可。 ## 联网搜索(可选) 安装 DuckDuckGo 搜索依赖后,研究阶段 `web_search` 工具会返回真实网页摘要(无需 API Key): ```bash pip install -e ".[search]" # 或单独:pip install ddgs(原 duckduckgo-search 已更名为 ddgs) ``` - **独立命令**:`writing-agent search 关键词`(可加 `--max 10`、`-F json`)。 - **交互模式**:`search 关键词`。 - **环境变量**:`WEB_SEARCH_DISABLE=1` 关闭联网;`WEB_SEARCH_MAX_RESULTS` 默认条数;`WEB_SEARCH_REGION` 区域(可选);`WEB_SEARCH_DDGS_IMPERSONATE` 可指定 primp 指纹(默认库内已避免无效随机指纹告警)。 ## 语音交互(可选) 安装语音相关依赖(麦克风听写 / 本地录音走 Whisper): ```bash pip install -e ".[voice]" ``` 若使用 **麦克风 + SpeechRecognition(Google 网页识别)** 且提示无法打开麦克风,可在**有预编译轮子**的环境尝试 `pip install pyaudio`(Linux 可能需先装 `portaudio` 开发包)。**Python 3.14 + Windows** 上 PyAudio 常无法安装,请改用已配置 `**OPENAI_API_KEY`** 的 **sounddevice 录音 + Whisper** 听写,或使用 `audio` 读 wav 等文件。 - **交互模式**下:`listen` 或 `voice` 使用麦克风听写并写入 `topic`;`voice-run` 听写后直接跑流水线;`audio /path/to.wav` 从文件识别;`speak` 朗读上一次 `run` 的可读输出。**macOS** 使用系统 `say`;**Windows / Linux** 使用 **pyttsx3**(随 `.[voice]` 安装)。Linux 通常还需系统包 **espeak-ng**(或 **espeak**),否则引擎可能无法初始化。 - **朗读参数(pyttsx3)**:可选环境变量 `VOICE_PYTTSX3_VOICE`(引擎语音 id)、`VOICE_PYTTSX3_RATE`(语速,整数);macOS 仍可用 `VOICE_SAY_VOICE`。 - **识别策略**:若已配置 `OPENAI_API_KEY` 且已安装 `sounddevice`+`numpy`,则本地录音后调用 **OpenAI Whisper** 转写;否则使用 **SpeechRecognition**(默认 Google Web Speech,需网络)。 - **Whisper 上传失败**(如 `WinError 10060`、连接超时):表示本机访问 `**OPENAI_BASE_URL`**(默认 `https://api.openai.com/v1`)的 HTTPS 不通或被拦截。请检查网络、VPN、系统代理,或设置 `**HTTPS_PROXY` / `HTTP_PROXY`**;国内访问官方域名常需可连通的 `**OPENAI_BASE_URL**`(自建/兼容网关)。可选 `**VOICE_TRANSCRIBE_TIMEOUT**`(秒,默认 `120`)仅延长等待,不能解决无法建连。 - **语言**:环境变量 `VOICE_LANGUAGE`(默认 `zh-CN`,英文可设 `en-US`)。 - **感知层**:将**音频文件路径**作为输入且扩展名为 wav/mp3/m4a 等时,`PerceptionModule` 会尝试转写为文本(需密钥或 `[voice]`)。 ## 配置文件(可选) 支持 `~/.writing_agent/config.json` 设置默认值,优先级:**命令行参数 > 环境变量 > 配置文件 > 内置默认值**。也可通过 `WRITING_AGENT_CONFIG` 环境变量指定配置文件路径。 ```json { "default_llm": "auto", "default_output": "auto", "default_lang": "zh", "default_style": "academic", "files_root": "/path/to/project" } ``` ## 质量门控与管线鲁棒性 写作管线内置质量门控: - **大纲完整性**:检查大纲是否覆盖用户指定的必需章节 - **综合质量评分**:基于可读性、句子长度分布、引用完整性计算 0-1 评分 - **质量门控**:评分 < 0.5 触发自动重试(使用审核意见修订草稿,最多 1 次);评分 < 0.7 产生警告 - **引用检测**:正文无引用标记时发出 major 警告 - **可读性阈值**:可读性 < 30/100 时建议简化句子 - **异常保护**:各管线阶段独立异常保护,单阶段失败不影响其余阶段;并行草稿节线程隔离 ## 终端颜色与无障碍 - 遵循 **[no-color.org](https://no-color.org/)**:设置 `**NO_COLOR`**(任意非空值)则禁用 ANSI 颜色。 - 额外支持 `**WRITING_AGENT_NO_COLOR=1`**(或 `true`/`yes`/`on`)。 - 非 TTY(管道、重定向)下默认不着色。 ## 环境变量(云端与排版) ### 阿里云百炼(优先于 OpenAI,在 `--llm auto` 时) | 变量 | 说明 | | ----------------------------------------- | ------------------------------------------ | | `DASHSCOPE_API_KEY` | 百炼 API Key | | `DASHSCOPE_MODEL` | 默认 `qwen-plus` | | `DASHSCOPE_BASE_URL` 或 `DASHSCOPE_REGION` | 兼容模式 Base URL 或地域简写(见 `openai_backend.py`) | | `DASHSCOPE_TEMPERATURE` | 默认 `0.7` | ### OpenAI 兼容 | 变量 | 说明 | | -------------------- | ------------------------------ | | `OPENAI_API_KEY` | 密钥 | | `OPENAI_BASE_URL` | 默认 `https://api.openai.com/v1` | | `OPENAI_MODEL` | 默认 `gpt-4o-mini` | | `OPENAI_TEMPERATURE` | 默认 `0.7` | ### HTML / KaTeX(`--format html`) | 变量 | 说明 | | ----------------------------- | ------------------------------------------------------------------------------------------ | | `WRITING_AGENT_KATEX_VERSION` | 未设置 `KATEX_CDN` 时,jsDelivr 上的 KaTeX 版本(默认 `0.16.11`) | | `WRITING_AGENT_KATEX_CDN` | 自定义静态资源根 URL(需包含 `katex.min.css`、`katex.min.js`、`contrib/auto-render.min.js` 的目录结构),用于内网镜像 | ### 文件沙箱与反馈路径 | 变量 | 说明 | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `WRITING_AGENT_FILES_ROOT` | 若设置,`files` 与交互中的文件路径必须落在该目录(经解析)之下;**相对路径**相对于该根目录(非 cwd)。交互中若用 `files-root` 指定了会话根,则以会话根为准,本变量次之;并与下方反馈路径自动规则联动(见 [docs/document-storage.md](docs/document-storage.md)) | | `WRITING_AGENT_MAX_READ_BYTES` | 单次读取文件大小上限(默认约 5MB) | | `WRITING_AGENT_FEEDBACK_PATH` | 若设置(非空),作为反馈 JSON 的**唯一**路径;若未设置,则依次为 `/.writing-agent/feedback_episodes.json`(当已设置 `WRITING_AGENT_FILES_ROOT`)、`~/.writing_agent/feedback_episodes.json` | ### 助手与流式 | 变量 | 说明 | | -------------------------------- | ------------------------------------------------------------------------------- | | `WRITING_ASSISTANT_PLAIN_DIALOG` | `1/true/yes/on`:助手模式下无 `/` 的整行视为写作对话(不跑流水线);默认关闭(整行按命令解析) | | `WRITING_ASSISTANT_CHAT` | 与 `WRITING_ASSISTANT_PLAIN_DIALOG` 相同含义的别名:`1` 开启整行对话;未设置时整行走命令解析 | | `WRITING_ASSISTANT_LINE_RUN` | `1`:无 `/` 的整行设 topic 并直接跑全文流水线(与「整行对话」互斥,优先读此变量) | | `WRITING_AGENT_STREAM` | 默认开启;`0`/`false`/`no`/`off`:交互 `run` 与 `write`/`run` 子命令关闭流式 LLM 输出到 **stderr** | | `WRITING_AGENT_DEBUG_EVENTS` | `1` 等:交互 `run` 时将流水线阶段事件(感知/规划/各 phase/完成)以 dim 文本写入 **stderr**(与流式 token 区分) | | `WRITING_AGENT_LEARN_DISABLE` | `1` 等:不向提示注入历史反馈(仍可用 `learned` 查看记录) | ### REPL 输入增强(需 `prompt_toolkit`,`pip install -e ".[tui]"`) | 变量 | 说明 | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `WRITING_AGENT_COMPLETE` | `1`/`true`/`yes`/`on`:TTY 下启用**指令 Tab 补全**(首词匹配命令名;`lang`/`output`/`llm`/`style` 等参数位置可补全枚举值;下拉含简短说明) | | `WRITING_AGENT_REPL_KEYS` | `1` 等:启用快捷键(空行时 Ctrl+R→`run`、Ctrl+H/F1→`help`、Ctrl+L 清屏等);与 `WRITING_AGENT_COMPLETE` 任一为真即会尝试使用 prompt_toolkit 读入一行 | | `WRITING_AGENT_COMPLETE_MATCH` | `prefix`:指令名仅**前缀**匹配;未设时默认按字符在指令名中的**顺序子序列**匹配 | | `WRITING_AGENT_COMPLETE_META` | `1` 等:过滤词可与补全项的**功能说明**(中文等)做子串匹配 | | `WRITING_AGENT_TOOLBAR` | `0`/`false`/`no`/`off`:关闭输入行**底栏**提示;默认在启用补全或快捷键时显示 | | `WRITING_AGENT_CONFIG` | 指定配置文件路径(默认 `~/.writing_agent/config.json`) | ### Semantic Scholar(研究工具) | 变量 | 说明 | | ---------------------------------- | ------------------------------------------ | | `SEMANTIC_SCHOLAR_API_KEY` | 可选,提高配额、缓解限流 | | `SEMANTIC_SCHOLAR_USER_AGENT` | 建议配置合规 UA | | `SEMANTIC_SCHOLAR_RETRIES` | 失败重试次数(默认 `3`,即最多 4 次请求) | | `SEMANTIC_SCHOLAR_RETRY_BACKOFF` | 指数退避基数(秒,默认 `1.0`) | | `SEMANTIC_SCHOLAR_CONNECT_TIMEOUT` | 连接超时(秒,默认 `20`) | | `SEMANTIC_SCHOLAR_READ_TIMEOUT` | 读超时(秒,默认 `45`) | | `SEMANTIC_SCHOLAR_DISABLE_URLLIB` | 设为 `1` 时关闭「httpx 失败后用标准库 urllib 再试一次」 | | `SEMANTIC_SCHOLAR_DEBUG` | 设为 `1` 时在降级结果里附带 `trace` 堆栈(默认只有简短 `hint`) | 对 `Connection reset by peer`、超时、部分 429/5xx:httpx 会自动重试;httpx 仍失败时会再尝试 **urllib**(连接栈不同,部分网络可通)。仍失败则研究工具降级为占位结果,流水线可继续。 ## 在代码中调用 ```python from writing_agent import ProfessionalWritingAgent from writing_agent.llm_resolve import resolve_llm agent = ProfessionalWritingAgent(resolve_llm("auto")) # 普通运行 out = agent.run( "你的主题或提示", requirements={"audience": "工程师", "language": "zh", "sections": ["引言", "主体", "结论"]}, ) # 流式:将 sink 接到 stderr 或自定义回调 import sys def sink(chunk: str) -> None: sys.stderr.write(chunk) sys.stderr.flush() out = agent.run( "主题", requirements={"audience": "工程师", "language": "zh"}, stream=True, stream_sink=sink, ) ``` ## 项目结构(节选) ``` src/writing_agent/ # 包源码 orchestrator.py # ProfessionalWritingAgent 入口 cli.py # CLI(10+ 子命令) interactive.py # 双模 REPL(经典 + 助手) workflow.py # 五阶段管线(研究→大纲→草稿→审稿→排版)+ 质量门控 agents.py # 多角色 Agent 管理(研究者/写作者/审稿人/排版师) planning.py # 写作任务 DAG 规划 style_guide.py # 风格配置、中英写作规则、可读性评分 env.py # 环境变量统一管理 user_config.py # 配置文件支持(~/.writing_agent/config.json) llm_resolve.py # 模板 / OpenAI / 百炼 解析 openai_backend.py # HTTP Chat Completions(含流式 SSE) output_format.py # text / json / html / rich 输出 studio_app.py # Textual 全屏写作工作室 feedback_learning.py # 反馈学习(上下文注入) tools/ # 工具子包 writing.py # StyleGuide / GrammarChecker / ToneAnalyzer quality.py # FactChecker / PlagiarismChecker / ComplianceValidator formatting.py # CitationManager(APA/MLA/GB/T 7714) research.py # WebSearch / SemanticScholar ... tests/ # pytest(182 个测试) scripts/ # 本地辅助脚本 ``` ## 开发 ```bash source .venv/bin/activate pytest -q ``` ## 许可证 以项目所有者约定为准;未随仓库附带许可证文件时请自行补充。