# fox-deepseek-chat **Repository Path**: fierce_wolf/fox-deepseek-chat ## Basic Information - **Project Name**: fox-deepseek-chat - **Description**: fox-deepseek-chat,私人代码 - **Primary Language**: Go - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-23 - **Last Updated**: 2026-10-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DeepSeek 网页版登录与问答工具(Go) 一个纯 Go(无第三方依赖)编写的命令行工具,用于: 1. 登录 DeepSeek **网页版**(`chat.deepseek.com`)并获取登录凭证; 2. 使用凭证开启一个新的会话; 3. 在该会话上流式进行 AI 问答(支持多轮上下文与深度思考模式)。 ## 项目结构 分三个包(目录),职责清晰: - **`main.go`(main 包)**:入口与主流程,仅编排「获取凭证 → 准备会话 → 问答」三步 - **`ask/`(命令行交互层)**:参数解析、Markdown 清洗、流式渲染、问答执行 (`flags.go` / `markdown.go` / `render.go` / `ask.go`,说明文档 `markdown.md`) - **`deepseek/`(可复用的 Go 库包)**:登录、会话、PoW、SSE、流式问答等业务逻辑 (其他 Go 工程可直接 `import "deepseekchat/deepseek"` 复用,见下文「作为 Go 库复用」) - 底层接口:登录、会话管理、SSE 流式解析(`client.go` / `sse.go`) - PoW 挑战:挑战申请、求解与请求头生成(`pow.go`,含 `DeepSeekHashV1` 哈希实现) - 高层接口:流式问答 `Completion` 与 prompt 构建(`completion.go` / `prompt.go`) - 业务流程:凭证获取/刷新、标题规则、会话准备、多轮对话(`session.go`) > 说明:本工具调用的是 DeepSeek 网页版内部接口(与浏览器行为一致), > 不是官方 API(官方 API 需要单独申请 API Key)。 > 网页版接口可能随官方调整而变化,请遵守 DeepSeek 服务条款,仅用于个人学习用途。 ## 功能 - 邮箱 + 密码登录,自动获取并保存登录凭证(`token.json`) - 支持直接传入浏览器 `localStorage` 中的 `user_token`,跳过登录 - 自动完成 Web 端 PoW(Proof of Work)挑战(纯 Go 实现 `DeepSeekHashV1`,无需 WASM 运行时) - 创建新会话,流式输出回答(思考过程输出到 stderr,回答输出到 stdout) - 交互式多轮问答与单次提问两种模式 - 创建会话后设置标题(单次提问默认以当天日期 `年-月-日` 命名,也可用 `-title` 自定义) ## 构建 需要 Go 1.22+。 ```bash cd "D:\我的项目\实验项目\deepseek\登录(网页版)" go build -o deepseek-chat.exe . ``` ## 作为 Go 库复用 `deepseek` 包封装了从登录到流式问答的完整能力,其他 Go 工程可通过 `go.mod` 引入本模块(本地开发可用 `replace` 指向本目录): ```go // go.mod require deepseekchat v0.0.0 replace deepseekchat => ../deepseek/登录(网页版) ``` 最小示例(登录 → 新建会话 → 流式问答): ```go package main import ( "context" "fmt" "os" "time" "deepseekchat/deepseek" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) defer cancel() // 方式一:直接用浏览器 localStorage 中的 user_token client := deepseek.NewDeepSeekClient("粘贴的 user_token") defer client.Close() // 释放连接池与内存中的凭证(幂等,可重复调用) // 方式二:邮箱 + 密码登录(返回 token 后同样 NewDeepSeekClient) // tok, err := deepseek.NewDeepSeekClient("").Login(ctx, email, password) // 新建会话(也可传已有 chat_session_id 直接复用) chatID, err := client.CreateSession(ctx) if err != nil { panic(err) } // 流式问答:自动完成 PoW 挑战 + SSE 解析; // onText 回调在收到文本片段时实时触发(可渲染到任意 UI),传 nil 表示只收集不输出。 answer, think, err := client.Completion(ctx, chatID, "你好", false, func(kind deepseek.TextKind, text string) { if kind == deepseek.TextThinking { fmt.Fprint(os.Stderr, text) // 思考过程 } else { fmt.Print(text) // 正式回答 } }) if err != nil { panic(err) } fmt.Printf("\n完整回答: %s\n思考过程: %s\n", answer, think) } ``` 包内主要 API: | 函数 / 方法 | 说明 | | --- | --- | | `NewDeepSeekClient(token)` | 创建客户端(token 为空时可用于登录接口) | | `(*DeepSeekClient) Login(ctx, email, password)` | 邮箱密码登录,返回 token | | `(*DeepSeekClient) RefreshToken(ctx)` | 用旧 token 换取最新 access_token | | `(*DeepSeekClient) CreateSession(ctx)` | 新建会话,返回 `chat_session_id` | | `(*DeepSeekClient) FindRecentSessionByTitle(ctx, title, since)` | 按标题查找最近活跃的会话(用于复用) | | `(*DeepSeekClient) UpdateSessionTitle(ctx, chatID, title)` | 设置会话标题 | | `(*DeepSeekClient) Completion(ctx, chatID, prompt, thinking, search, onText)` | **高层问答接口**:自动 PoW + 流式解析,返回完整回答与思考过程(`search` 开启联网搜索) | | `BuildPrompt(msgs)` / `Message` | 构建多轮对话 prompt | | `(*DeepSeekClient) OpenCompletion(ctx, chatID, prompt, powHeader, thinking, search)` / `ResolvePow(ctx)` | 底层接口(自定义流式处理时使用) | | `SessionOptions` | 聚合一次问答运行的全部参数(凭证/标题/超时/联网搜索/通知回调) | | `PrepareClient(opts)` | 获取并刷新登录凭证,返回客户端与 token;凭证取调用方传入的 `Token`,**默认不读写任何本地文件** | | `ResolveSessionTitle(title, question)` | 确定会话标题(显式标题 > 单次提问当天日期 > 空) | | `PrepareSession(client, opts)` | 准备目标会话:复用指定 ID / 复用同标题会话 / 新建并设标题 | | `SetSessionTitle(client, chatID, title, verbose, opts)` | 设置会话标题(失败不影响问答,返回错误供调用方决定) | | `NewConversation(client, chatID, thinking, search, timeout)` | 创建多轮对话对象(自动维护消息历史;`search` 开启联网搜索) | | `(*Conversation) Ask(ctx, question, onText)` | 携带完整历史发起流式问答,返回 `ChatResult{Answer, Thinking, Markdown, References}` | | `(*Conversation) Reset()` / `(*Conversation) Messages()` | 清空对话历史 / 获取历史副本 | | `(*DeepSeekClient) Close()` / `(*Conversation) Close()` | **释放资源**:关闭连接池并清除凭证 / 清空历史并解除客户端引用(均幂等) | | `CloseClient(clients...)` | 批量释放客户端的便捷函数,支持 nil,适合 `defer deepseek.CloseClient(client)` | | `(*DeepSeekClient) IsClosed()` / `Token()` / `SetToken(tok)` | 是否已关闭 / 读取凭证 / 更换凭证 | | `ErrClientClosed` / `ErrConversationClosed` | 关闭后继续使用的错误,可用 `errors.Is` 判断 | | `SaveTokenTo(file, tok)` / `SaveToken(tok)` | 由调用方**显式**持久化凭证(组件自身不会自动写文件) | | `LoadTokenFrom(file)` / `LoadToken()` | 从指定文件 / `token.json` 读取已保存凭证 | | `SessionOptions.TokenFile` / `.PersistToken` | 凭证文件路径(**默认空 = 不碰文件**) / 是否写回文件(默认 `false`) | | `DefaultTokenFile` / `DefaultSessionTTL` 等 | 常用默认参数常量(凭证文件、复用窗口 48h、超时等) | ## 使用 ### 方式一:邮箱 + 密码登录 ```bash deepseek-chat.exe -email your@email.com -password yourpassword ``` 密码不建议直接写在命令行(进程列表中可见),推荐用环境变量: ```bash set DEEPSEEK_EMAIL=your@email.com set DEEPSEEK_PASSWORD=yourpassword deepseek-chat.exe ``` 登录成功后凭证会保存到当前目录 `token.json`,下次运行自动复用。 ### 方式二:直接提供浏览器里的凭证(推荐,可绕过登录验证码) 1. 浏览器登录 https://chat.deepseek.com 2. F12 打开开发者工具 → Application / 应用 → Local Storage → `https://chat.deepseek.com` 3. 复制 `user_token` 的值 4. 运行: ```bash deepseek-chat.exe -token <粘贴的 user_token> ``` ### 单次提问 ```bash deepseek-chat.exe -token -q "用三句话介绍 Go 语言" ``` ### 自定义会话标题 程序通过 `chat_session/update_title` 接口设置会话标题:创建会话后立即设置一次 (即时生效),并在问答完成后再次确认设置(防止 DeepSeek 收到第一条消息后自动 生成的标题覆盖用户设定的标题)。 **会话复用规则**:新建会话前,程序会查询服务器上 48 小时内(按最近活跃时间) 是否存在标题相同的会话。若存在则直接复用该会话继续问答,不再新建;若不存在 才新建会话并设置标题。这样同一天内多次单次提问会共用同一个标题为日期的会话, 方便在网页端按日期归拢每日问答。 标题按以下规则确定: 1. 显式指定 `-title` → 使用该标题(单次提问与交互模式均适用); 2. 未指定 `-title` 且为单次提问模式(`-q`)→ 默认使用当天日期 `年-月-日`(例如 `2026-08-23`), 方便在网页端按日期定位历史会话; 3. 未指定 `-title` 且为交互模式 → 不修改标题,保持服务端默认。 ```bash # 单次提问:标题自动为当天日期 deepseek-chat.exe -token -q "用三句话介绍 Go 语言" # 自定义标题 deepseek-chat.exe -token -title "我的测试会话" -q "你好" # 自定义标题,例如"2026-08-23|新闻" deepseek-chat.exe -token -title "2026-08-23|新闻" -thinking -q "请详细推导 1+1=2 的过程" # 交互模式自定义标题 deepseek-chat.exe -token -title "学习 Go" ``` > 注意:`-title` 只在**新建**会话时生效;使用 `-session` 复用已有会话时不会改动原标题。 > 标题中若包含 `|` 等特殊字符,请在命令行中用引号包裹(如上例)。 ### 开启深度思考(Reasoner) ```bash deepseek-chat.exe -token -thinking -q "请详细推导 1+1=2 的过程" ``` ### 交互式问答 不带 `-q` 即进入交互模式: ```bash deepseek-chat.exe -token 输入问题开始问答,输入 exit / quit / 退出 结束。 你 > 你好,介绍一下你自己 你 > 继续 ``` ## 完整参数 | 参数 | 说明 | | --- | --- | | `-email` | 登录邮箱(或环境变量 `DEEPSEEK_EMAIL`) | | `-password` | 登录密码(或环境变量 `DEEPSEEK_PASSWORD`) | | `-token` | 已有登录凭证(或环境变量 `DEEPSEEK_TOKEN`),可跳过登录 | | `-q "问题"` | 单次提问模式 | | `-thinking` | 开启深度思考模式 | | `-search` | 开启联网搜索(回答会附带 `[N]` 来源引用与文末参考资料链接) | | `-session ` | 复用已有会话 ID(默认自动新建) | | `-title <文本>` | 新会话标题(默认单次提问模式使用当天日期 `年-月-日`) | | `-md` | 以 Markdown 文档格式输出回答(保留加粗/列表/链接,附参考资料列表) | | `-out <文件>` | 将 Markdown 文档写入指定文件(需配合 `-md`,空则打印到终端) | | `-token-file <文件>` | 凭证文件路径(默认 `token.json`),命令行模式下登录后/刷新后会写回该文件 | ### 开启联网搜索(信息来源链接) 网页版提问时勾选的「联网搜索」在本程序对应 `-search` 参数。**只有开启联网搜索, 服务端才会联网检索,回答才会携带来源引用**——正文中出现 `[N]` 引用标记, 文末整理「参考资料」列表(标题+链接,与正文序号一一对应);不开启时回答完全由 模型内部知识生成,没有任何引用来源(与网页版不勾选联网搜索的行为一致)。 ```bash deepseek-chat.exe -token -search -q "介绍一下2026年8月的科技新闻" ``` ### 以 Markdown 文档格式输出 回答内容本身携带服务端下发的 Markdown 语法(`**加粗**`、列表、代码块、链接等)。 默认模式把它作为纯文本展示;使用 `-md` 参数后,会在问答结束后输出渲染完整的 Markdown 文档,正文中的 `[citation:N]` 引用标记统一替换为 `[N]`,文末附 「参考资料」列表: ```bash # 打印到终端 deepseek-chat.exe -token -search -md -q "介绍一下2026年8月的科技新闻" # 直接保存为 .md 文件(推荐,不依赖 shell 重定向) deepseek-chat.exe -token -search -md -out 回答.md -q "介绍一下2026年8月的科技新闻" # 也可用 shell 重定向保存 deepseek-chat.exe -token -search -md -q "..." > 回答.md ``` > 注意:`-md` 是布尔参数,`-md 回答.md` 中的 `回答.md` 会被当作无效的位置参数 > 忽略,不会生成文件;请使用 `-out 回答.md` 或 shell 重定向 `> 回答.md`。 ### 凭证:由调用方传入,不碰本地文件(作为库复用时) `token` 是**输入参数**:由调用方从自己的配置(ini / 环境变量 / 数据库 / 浏览器 `user_token`)取得后填入 `SessionOptions.Token`,本组件默认**既不写也不读**任何本地文件。 | 行为 | 默认 | 开启方式 | | --- | --- | --- | | 读凭证文件 | 关闭 | 显式设置 `SessionOptions.TokenFile`(如 `"token.json"`) | | 写凭证文件 | 关闭 | 同时设置 `TokenFile` 且 `PersistToken: true` | | 邮箱密码登录 | 关闭 | 同时设置 `Email` + `Password` | 登录成功或刷新出新凭证后,新 token 只作为 `PrepareClient` 的返回值交回调用方, 不落到进程工作目录;需要持久化时二选一: ```go // 方式一:让组件写(显式开启) opts := deepseek.SessionOptions{Token: token, PersistToken: true} // 方式二:自己存到自己的配置里(推荐) client, tok, err := deepseek.PrepareClient(opts) if err != nil { return err } defer deepseek.CloseClient(client) saveToMyConfig(tok) // 例如写回 ini / 数据库,而不是工作目录的 token.json ``` 命令行工具保留原有体验:`ParseFlags` 里显式设置了 `TokenFile`(`token.json`,可用 `-token-file` 改路径)并开启 `PersistToken`,所以 `-email/-password` 登录后凭证仍会 写回该文件,下次运行自动复用。 > 服务端场景(如调度任务批量提问)请保持二者为零值:既不会在工作目录落 `token.json`, > 也不会因每个组合各刷新一次新凭证而互相覆盖。 ### 资源释放(客户端 / 对话) 客户端持有 HTTP 连接池与登录凭证,用完后必须显式释放,否则空闲连接会一直占着: ```go client, _, err := deepseek.PrepareClient(opts) if err != nil { return err } defer deepseek.CloseClient(client) // 或 defer client.Close() cv := deepseek.NewConversation(client, chatID, true, false, 0) defer cv.Close() // 释放对话历史(不关闭共享的 client) ``` - `client.Close()`:关闭本客户端连接池中的空闲连接,并清除内存中的 token; 幂等,可重复调用,也支持 `defer`;关闭后任何接口调用返回 `ErrClientClosed`。 - `NewDeepSeekClient` 自带独立连接池(`DefaultTransport` 的副本),因此 `Close` 不会影响全局默认连接池;若用 `NewDeepSeekClientWithHTTPClient` 传入外部 `http.Client`,则连接池仍归调用方所有,`Close` 只清理凭证与状态。 - `cv.Close()`:清空消息历史并解除对客户端引用;不会关闭底层客户端 (可能被多个对话共享);关闭后 `Ask` 返回 `ErrConversationClosed`。 - 只需换个话题继续聊,可调用 `cv.Reset()` 清空历史,对话仍可用。 ## 实现说明 | 步骤 | 接口 | | --- | --- | | 登录 | `POST /api/v0/users/login`(`email/mobile/password/area_code/device_id/os`) | | 刷新凭证 | `GET /api/v0/users/current`(换取最新 access_token) | | 新建会话 | `POST /api/v0/chat_session/create` | | 设置标题 | `POST /api/v0/chat_session/update_title` | | PoW 挑战 | `POST /api/v0/chat/create_pow_challenge` → 纯 Go 求解 → 请求头 `X-Ds-Pow-Response` | | 问答 | `POST /api/v0/chat/completion`(SSE 流式) | PoW 算法 `DeepSeekHashV1` 为跳过第 0 轮的 SHA3-256(Keccak-f 执行 23 轮,rate=136), 求解过程在 `deepseek/pow.go` 中纯 Go 实现。 ## 常见问题 - **登录失败 `PASSWORD_OR_USER_NAME_IS_WRONG`**:账号或密码错误。 - **登录提示需要验证码 / 设备验证**:网页版在异常登录时会要求人机验证,此时请改用方式二 (从浏览器复制 `user_token`)。 - **提示 "获取 PoW 挑战失败"**:程序会自动降级为不带 PoW 头的请求,多数情况下仍可正常使用。 - **程序运行过程中提示 PoW 未解出**:偶发情况,重试一次即可。 ## 免责声明 本项目仅用于技术学习与研究,不保证长期可用。请遵守 DeepSeek 的服务条款,不要用于批量抓取或滥用。