Skip to content

feat(doubao): capture conversation_url and classify captcha blocks - #63

Merged
2233admin merged 1 commit into
2233admin:mainfrom
1012839419a-alt:feat/doubao-channel-conversation-url
Aug 7, 2026
Merged

feat(doubao): capture conversation_url and classify captcha blocks#63
2233admin merged 1 commit into
2233admin:mainfrom
1012839419a-alt:feat/doubao-channel-conversation-url

Conversation

@1012839419a-alt

Copy link
Copy Markdown
Contributor

关联

Fixes / 部分实现 #62(doubao_research 批量采集实测反馈)

改动

backend/channels/doubao_research_channel.py

  1. 捕获会话 URL(P0):ask 成功后,best-effort 执行一次 opencli doubao status -f json,提取 https://www.doubao.com/chat/<id> 写入输出 conversation_url 字段。

    • status 失败/无 id 不 fail collect(回答已在手),输出空字符串
    • 新增配置项 capture_conversation_url(默认 true)可关闭
    • 保持默认 site_session=ephemeral(opencli 1.8.6 的 ephemeral = 每次全新浏览器会话,天然每词新对话,无需显式 new)
  2. 验证码错误分类(P1):ask 非零退出且 stderr 含验证码特征(verification challenge/captcha/blocked the request/中文标记)时,error_type="captcha_challenge"——runner 可据此做冷却重试或人工介入策略,而不是当作永久失败。普通错误仍返回 error_type=None

为什么

实测 240 词批量采集(见 #62 评论)确认:

  • 渠道输出缺会话 URL,批量场景需要回链到每词对话(当前需额外 status 调用)
  • 验证码风控每 ~50-60 词触发一次,当前直接 fail 导致整个 task 失败,无重试分类

测试

tests/unit/channels/test_doubao_research_channel.py 新增 8 个用例(原有 4 个全保留):

  • _conversation_url:提取 chat id / 忽略根 /chat / 容错垃圾输入
  • collect 捕获 conversation_url(ask + status 各一次)
  • status 失败时 collect 仍成功(URL 为空)
  • captcha 错误分类为 captcha_challenge;普通错误 error_type=None
pytest tests/unit/channels/test_doubao_research_channel.py
→ 12 passed
ruff check backend/channels/doubao_research_channel.py tests/unit/channels/test_doubao_research_channel.py
→ All checks passed!

- after a successful ask, best-effort `doubao status` to grab the
  active conversation URL (https://www.doubao.com/chat/<id>); status
  failure never fails the collect
- classify captcha/verification-wall errors as error_type
  "captcha_challenge" so runners can apply cooldown/retry policy
- keep default site_session=ephemeral (fresh conversation per ask);
  add capture_conversation_url config flag (default true)
- unit tests: conversation_url parsing (id / root / garbage),
  status captured + tolerated, captcha classified vs generic
@repowise-bot

repowise-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

⏳ Repowise has not indexed this repository yet

No analysis on this PR because there is no index to compare against. Indexing usually runs automatically after install; if this persists, start it from the dashboard.

Index 2233admin/opencli-Razormind · Docs

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Captures and includes the active conversation URL with collected Doubao research results when available.
    • Detects CAPTCHA challenges and reports them with a specific error classification.
  • Bug Fixes

    • Handles status-check failures gracefully without interrupting result collection.
    • Preserves generic command errors when they are not CAPTCHA-related.

Walkthrough

Doubao research collection now detects CAPTCHA challenges, retrieves conversation URLs from status output, and stores them in successful results. Status failures remain non-fatal. Tests cover URL parsing, error classification, collection behavior, and source configuration.

Changes

Doubao collection updates

Layer / File(s) Summary
Conversation URL and CAPTCHA helpers
backend/channels/doubao_research_channel.py, tests/unit/channels/test_doubao_research_channel.py
The channel parses conversation-specific URLs, rejects the root /chat URL, detects CAPTCHA markers, and handles malformed status data.
CAPTCHA error classification
backend/channels/doubao_research_channel.py, tests/unit/channels/test_doubao_research_channel.py
Nonzero doubao ask commands return captcha_challenge when output contains a configured marker. Other errors keep the generic behavior.
Status retrieval and result enrichment
backend/channels/doubao_research_channel.py, tests/unit/channels/test_doubao_research_channel.py
Successful collection performs best-effort status retrieval and stores conversation_url. Status failures do not invalidate the result. Source-schema coverage remains present.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Collector
  participant DoubaoAsk
  participant DoubaoStatus
  Collector->>DoubaoAsk: submit research question
  DoubaoAsk-->>Collector: answer or command error
  alt successful answer
    Collector->>DoubaoStatus: request status as JSON
    DoubaoStatus-->>Collector: conversation URL or status failure
    Collector-->>Collector: store conversation_url
  else CAPTCHA marker detected
    Collector-->>Collector: classify as captcha_challenge
  end
Loading

Suggested reviewers: 2233admin

Poem

A rabbit checks the Doubao trail,
Finds CAPTCHA signs where commands fail.
A chat URL hops into the store,
While status troubles block no more.
“Collect,” says Bun, “and keep it bright!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes both main changes: conversation URL capture and CAPTCHA error classification.
Description check ✅ Passed The description directly explains the implemented changes, rationale, configuration, error behavior, and test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from 2233admin August 6, 2026 23:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/channels/doubao_research_channel.py`:
- Line 166: Update the tuple unpacking in the command execution flow around
_run_doubao_command to discard the unused status-error value instead of binding
it to se, resolving Ruff RUF059 while preserving rc and so handling.
- Around line 57-59: Update the URL extraction logic around the visible Url/url
lookup so it only returns a valid HTTPS Doubao conversation URL matching
www.doubao.com/chat/<id>. Reject HTTP URLs, non-Doubao hosts, and paths with no
non-empty conversation ID before assigning the result to conversation_url.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 144ddca8-ab5a-4e4c-8fb7-8dd65c8c8ea2

📥 Commits

Reviewing files that changed from the base of the PR and between 7838811 and be05d87.

📒 Files selected for processing (2)
  • backend/channels/doubao_research_channel.py
  • tests/unit/channels/test_doubao_research_channel.py

Comment on lines +57 to +59
url = str(row.get("Url", row.get("url", "")) or "").strip()
if "/chat/" in url:
return url

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate the extracted conversation URL.

The predicate accepts http URLs, non-Doubao hosts, and /chat/ with no conversation ID. The method then stores these values in conversation_url, although its contract requires an HTTPS www.doubao.com/chat/<id> URL.

Proposed fix
+from urllib.parse import urlsplit
+
-        if "/chat/" in url:
+        parsed = urlsplit(url)
+        chat_id = parsed.path.removeprefix("/chat/").strip("/")
+        if (
+            parsed.scheme == "https"
+            and parsed.hostname == "www.doubao.com"
+            and chat_id
+            and "/" not in chat_id
+        ):
             return url
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
url = str(row.get("Url", row.get("url", "")) or "").strip()
if "/chat/" in url:
return url
from urllib.parse import urlsplit
url = str(row.get("Url", row.get("url", "")) or "").strip()
parsed = urlsplit(url)
chat_id = parsed.path.removeprefix("/chat/").strip("/")
if (
parsed.scheme == "https"
and parsed.hostname == "www.doubao.com"
and chat_id
and "/" not in chat_id
):
return url
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/channels/doubao_research_channel.py` around lines 57 - 59, Update the
URL extraction logic around the visible Url/url lookup so it only returns a
valid HTTPS Doubao conversation URL matching www.doubao.com/chat/<id>. Reject
HTTP URLs, non-Doubao hosts, and paths with no non-empty conversation ID before
assigning the result to conversation_url.

str(config.get("site_session", "ephemeral")),
]
try:
rc, so, se = await _run_doubao_command(status_command)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the unused status-error binding.

Line 166 binds se but does not use it. Ruff reports RUF059.

Proposed fix
-                rc, so, se = await _run_doubao_command(status_command)
+                rc, so, _ = await _run_doubao_command(status_command)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rc, so, se = await _run_doubao_command(status_command)
rc, so, _ = await _run_doubao_command(status_command)
🧰 Tools
🪛 Ruff (0.16.1)

[warning] 166-166: Unpacked variable se is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/channels/doubao_research_channel.py` at line 166, Update the tuple
unpacking in the command execution flow around _run_doubao_command to discard
the unused status-error value instead of binding it to se, resolving Ruff RUF059
while preserving rc and so handling.

Source: Linters/SAST tools

@2233admin
2233admin merged commit 94ab53d into 2233admin:main Aug 7, 2026
2 checks passed

@2233admin 2233admin left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants