owlynn-diagnostics
暂无简介
- License 许可
- MIT
- 最近更新
- 2026-09-08
- Stars
- ★ 1
- Forks
- 0
03 SKILL.md 正文
Owlynn Diagnostics
Comprehensive reference for diagnosing and fixing issues in the Owlynn AI coworker agent.
Quick Start
Health Check
# Containers (Qdrant + Redis)
podman ps --format "table {{.Names}}\t{{.Status}}" 2>/dev/null | grep -E "qdrant|redis"
# LM Studio (expect >= 1 model loaded)
curl -s http://127.0.0.1:1234/v1/models | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('data',[])))"
# Backend
curl -s http://127.0.0.1:8000/api/unified-settings | head -c 80
# CI gate
./scripts/ci.sh --quick
"What happened in conversation X?"
# View latest conversation trace
python scripts/trace_view.py --latest
# View specific thread (JSON for programmatic use)
python scripts/trace_view.py <thread_id> --json
# List all traces
python scripts/trace_view.py --list
Start the App
./start.sh # Containers → LM Studio → Backend + Frontend
./start.sh --debug # With DEBUG logging + audit enabled
Architecture
Port Map
| Service | URL | Purpose |
|---|---|---|
| Frontend | http://127.0.0.1:5173 |
React 19 + Vite + Electron |
| Backend | http://127.0.0.1:8000 |
FastAPI (REST + WebSocket) |
| LM Studio | http://127.0.0.1:1234 |
Local LLM inference |
| Qdrant | http://127.0.0.1:6333 |
Vector DB (LTM / Mem0) |
| Redis | redis://localhost:6379 |
LangGraph checkpointing |
| StirlingPDF | http://localhost:8090 |
PDF text extraction + OCR |
Key Modules
| Module | Path |
|---|---|
| Config (single source of truth) | src/config/defaults.yaml |
| Config loader | src/config/config_loader.py |
| Agent graph | src/agent/core/graph.py |
| Router | src/agent/routing/router.py, src/agent/routing/classifier.py, src/agent/routing/budget.py, src/agent/routing/selector.py |
| Simple node | src/agent/core/simple.py |
| Complex node | src/agent/core/complex.py |
| Cloud payload | src/agent/core/complex_utils/cloud_payload.py |
| Cloud invoke | src/agent/core/complex_utils/cloud_invoke.py |
| Vision proxy | src/agent/core/complex_utils/vision_proxy.py |
| Memory nodes | src/agent/nodes/memory.py |
| Memory managers | src/memory/ (STM: memory_manager.py, LTM: long_term.py, personal: personal_assistant.py) |
| Summarizer | src/agent/nodes/summarize.py |
| Security proxy | src/agent/nodes/security_proxy.py |
| Scope clarify | src/agent/nodes/scope_clarify.py |
| Plan review | src/agent/nodes/plan_review.py |
| Coherence check | src/agent/nodes/coherence.py |
| LLM pool | src/agent/llm.py |
| Tool registry | src/agent/tool_sets.py |
| Tool implementations | src/tools/ |
| API server | src/api/server.py |
| WebSocket handler | src/api/ws/handler.py |
| Graph session | src/api/controllers/graph_session.py |
| Audit log | src/config/audit_log.py |
| Log middleware | src/config/log_middleware.py |
| Trace writer | src/config/trace_writer.py |
| AgentState definition | src/agent/core/state.py |
| User profile | src/memory/user_profile.py |
Config Override Priority
defaults.yaml → environment variables (.env, .env.local) → user_profile.json
Validate config at startup:
python3 -c "from src.config.config_loader import validate_config; print(validate_config())"
Agent Graph
Node Flow
START
→ memory_inject_lite (profile, persona, topics — no vector search)
→ router (classify: simple | complex-cloud)
→ memory_retrieve (gated Qdrant/Mem0 + scenario markdown)
→ [simple] OR [scope_clarify → complex_llm]
│
├─ simple → simple_node (Qwen3-VL-4B) → memory_write → END
│
└─ complex → scope_clarify → complex_llm
│
├─ cloud path (complex-cloud, DeepSeek V4)
├─ plan_review / security_proxy (HITL gates)
├─ tool_action → web search, file ops, REPL
├─ complex_llm (cycle until no tools pending)
└─ memory_write → END
Routes
| Route | Model | When |
|---|---|---|
simple |
Qwen3-VL-4B (local) | Simple factual, greetings, short tasks |
complex-cloud |
DeepSeek V4 (cloud) | Tool-augmented, complex reasoning, web search |
Classification Sources
keyword_bypass, deterministic, llm_classifier, hitl
Key AgentState Fields
| Field | Type | Purpose |
|---|---|---|
messages |
Sequence[BaseMessage] |
Conversation history |
route |
str | None |
"simple" or "complex-cloud" |
model_used |
str | None |
"small-local", "large-cloud", or with -fallback suffix |
needs_memory_retrieval |
bool | None |
Router gate for vector retrieval |
selected_toolboxes |
list[str] | None |
e.g. ["web_search", "file_ops"] or ["all"] |
token_budget |
int | None |
Dynamic budget set by router |
pending_tool_calls |
bool | None |
Whether tools are queued |
security_decision |
str | None |
"approved" or "denied" |
active_tokens |
int | None |
Current token count |
context_window |
int | None |
Active model's context window |
api_tokens_used |
dict | None |
{"prompt_tokens": int, "completion_tokens": int} |
router_metadata |
dict | None |
Telemetry from routing decision |
fallback_chain |
list[dict] | None |
Ordered model attempt list |
clarified_scope |
dict | None |
User-approved requirements from scope_clarify |
plan_review_approved |
bool | None |
Plan review HITL result |
response_confidence |
float | None |
Coherence score |
coherence_retry_reason |
str | None |
Last coherence issue |
_coherence_retry_round |
int | None |
Coherence retry counter |
Routing
Current Models
| Slot | Model | Purpose |
|---|---|---|
models.small |
qwen3-vl-4b-instruct-c_abliterated-v2-mlx |
Router, vision, extraction, simple answers |
models.cloud |
deepseek-v4-flash |
Complex reasoning (tool-augmented) |
models.embedding |
text-embedding-nomic-embed-text-v1.5-embedding |
LTM vector embeddings |
Changing models: Edit only defaults.yaml (models.small.model_name, models.cloud.model_name). ConfigLoader centralizes all model name resolution via get_small_model_name() / get_cloud_model_name().
Key Routing Config (defaults.yaml)
routing:
confidence_threshold: 0.6 # HITL triggered below this
skill_clarification_threshold: 0.5
scope_clarification_enabled: true
plan_review_enabled: true
keyword_bypass: true
hitl_enabled: true
max_input_chars: 2000 # Router input truncation
budget_tiers:
- [40, 256]
- [150, 512]
- [400, 1536]
- [800, 3072]
- [1600, 4096]
Environment Variable Overrides
| Env Var | Overrides |
|---|---|
SMALL_LLM_BASE_URL |
models.small.base_url |
SMALL_LLM_MODEL_NAME |
models.small.model_name |
CLOUD_LLM_BASE_URL |
models.cloud.base_url |
CLOUD_LLM_MODEL_NAME |
models.cloud.model_name |
DEEPSEEK_API_KEY |
Cloud API key |
Memory
Three-Tier Architecture
| Tier | Storage | Retrieval |
|---|---|---|
| STM | data/memories.json |
Keyword search (recent 50 entries) |
| LTM | Qdrant via Mem0 | Semantic search (gated by router) |
| Personal | data/topics.json, data/interests.json, data/conversations.json |
Time-decay-weighted relevance |
| Scenarios | scenarios/*/playbook.md, constraints.md |
Router scenario_id + markdown loader |
Injection Flow
memory_inject_lite → router → memory_retrieve → ...
memory_inject_lite: Profile, persona, topics (no vector search). Cached 5 min.- Router sets
needs_memory_retrievaland optionalscenario_id. memory_retrieve: Qdrant/Mem0 only when gated; loads scenario markdown.
Write Flow (after agent responds)
- PII-scrubs content
- Enqueues custom extraction (Redis stream → worker → L1 atoms in Qdrant)
- Extracts topics and updates topic/interests tracking
- Records conversation in
conversations.json - Invalidates memory cache for next turn
Config
memory:
max_facts: 200
search_window: 50
cache:
ttl: 300 # 5 min
decay:
topic_half_life_days: 14
interest_half_life_days: 21
extraction:
idle_cooldown_seconds: 8
defer_while_graph_active: true
Tools
Toolbox Categories
| Toolbox | Tools |
|---|---|
| Always | ask_user |
web_search |
web_search, fetch_webpage, deep_research |
file_ops |
read_workspace_file, write_workspace_file, edit_workspace_file, list_workspace_files, delete_workspace_file, download_to_workspace, upload_from_workspace |
data_viz |
create_docx, create_xlsx, create_pptx, create_pdf, notebook_run, notebook_reset, notebook_vars, read_ipynb, write_ipynb, export_ipynb_html, render_interactive_block |
productivity |
todo_add, todo_list, todo_complete, list_skills, invoke_skill, render_interactive_block |
study |
course_register, course_list, course_get, study_note_save, study_note_search, flashcard_deck_create, flashcard_review, quiz_session_start, quiz_session_answer, mastery_record, export_study_sheet |
memory |
recall_memories, recall_all_memories, forget_memory, search_workspace_docs |
Sensitive Tools (require HITL approval)
write_workspace_file, edit_workspace_file, delete_workspace_file, notebook_run
Adding a New Tool
- Create
@toolfunction insrc/tools/<module>.py - Register in
src/agent/tool_sets.py(ToolboxRegistry) - Add to appropriate toolbox category
- If sensitive, add to
SENSITIVE_TOOLSinsrc/agent/nodes/security_proxy.py
HITL (Human-in-the-Loop)
Four HITL Gates
| Gate | File | Trigger |
|---|---|---|
| Router HITL | src/agent/routing/router.py |
LLM confidence < 60% or ambiguous skill match |
| Scope Clarify | src/agent/nodes/scope_clarify.py |
Underspecified build/create requests |
| Plan Review | src/agent/nodes/plan_review.py |
Sensitive tool plan before execution |
| Security Proxy | src/agent/nodes/security_proxy.py |
Destructive/network tool calls |
API Mode Behavior
- Interrupts disabled when
mode == "api"orexecution_policy == "auto_approve" - Sensitive tools denied by default unless
auto_approve_sensitive: true
Known Issues
- Confidence uncalibrated: Always ~95%, does not reflect actual quality.
- Refactoring false positive: "write an improved version" triggers scope_clarify. Mitigated by
_REFACTOR_SIGNALSinsrc/agent/hitl/scope_heuristics.py.
Logging & Traces
Owlynn has a two-tier logging system.
Tier 1: Audit Log (Metadata)
Structured JSON-lines capturing system-wide metadata — routing, model selection, HITL, memory, tools, timing. No user content.
Output: ~/.owlynn/logs/audit.jsonl (5 × 10 MB rotating) and audit-errors.jsonl (3 × 5 MB rotating).
CLI tool:
python scripts/logcat.py --channel agent.model # Model selection
python scripts/logcat.py --channel agent.hitl --level WARN # HITL decisions
python scripts/logcat.py --channel memory.cache # Cache hits/misses
python scripts/logcat.py --thread-id abc-123 # Filter by thread
python scripts/logcat.py --follow # Live tail
python scripts/logcat.py --channel agent.model --follow # Live model activity
15 channels: agent.lifecycle, agent.model, agent.hitl, agent.tool, agent.token, agent.cloud, agent.coherence, memory.inject, memory.write, memory.cache, memory.ltm, memory.stm, memory.summarize, memory.topics, api.ws, api.file, system
Tier 2: Conversation Trace (Content)
Per-thread JSONL files capturing what happened in a conversation — user messages, router decisions, LLM responses, tool calls with I/O, errors, timing.
Output: ~/.owlynn/traces/{thread_id}.jsonl
CLI tool:
python scripts/trace_view.py --list # List all traces
python scripts/trace_view.py --latest # View latest
python scripts/trace_view.py <thread_id> # View specific thread
python scripts/trace_view.py <thread_id> --json # JSON output
python scripts/trace_view.py <thread_id> --follow # Live tail
python scripts/trace_view.py <thread_id> --type router_decision,llm_response # Filter
Trace Event Types
| Type | When | Key Fields |
|---|---|---|
turn_start |
Graph run begins | ts |
user_message |
User sends message | content (truncated 2000 chars) |
router_decision |
Router completes | route, confidence, source, task_category, toolbox |
llm_response |
LLM node completes | node, model, content_preview, token_usage, fallback_chain |
tool_call |
Tool completes | tool_name, input, output, error, duration_s |
coherence_check |
Coherence check | coherent, confidence, reason, duration_ms |
hitl_interrupt |
HITL fires | interrupt_count |
error |
Graph error | message |
turn_end |
Graph run ends | ts |
IDE Agent Trace Patterns
import json
with open("~/.owlynn/traces/{thread_id}.jsonl") as f:
events = [json.loads(line) for line in f]
# Full timeline
for e in events:
print(f"{e['ts']} [{e['type']}]")
# Routing decisions
[e for e in events if e["type"] == "router_decision"]
# What did the user ask?
[e for e in events if e["type"] == "user_message"]
# What did the LLM respond?
[e for e in events if e["type"] == "llm_response"]
# What tools failed?
[e for e in events if e["type"] == "tool_call" and e.get("error")]
# Errors
[e for e in events if e["type"] == "error"]
Emitting Audit Events in Code
from src.config.audit_log import audit_info, audit_debug, audit_warn
audit_info(
"agent.hitl", "tool_classified", tool="write_workspace_file", decision="sensitive"
)
audit_debug("memory.cache", "cache_hit", age_seconds=12)
audit_warn("agent.model", "swap_load_failed", variant="vision", error=str(e))
Using @log_node Decorator
from src.config.log_middleware import log_node
@log_node("complex_llm")
async def complex_llm_node(state: AgentState) -> AgentState: ...
Debugging Recipes
Wrong route (simple vs complex)
# Check what the router decided
python scripts/trace_view.py <thread_id> --type router_decision
# Check audit log for routing details
python scripts/logcat.py --channel agent.model --thread-id <thread_id>
# Look at classification_source in router_info WS event
# Source types: keyword_bypass, deterministic, llm_classifier, hitl
Fix: Check src/agent/routing/router.py for keyword bypass rules, or defaults.yaml routing.confidence_threshold.
LM Studio not reachable
# Check if LM Studio is running
curl -s http://127.0.0.1:1234/v1/models | head -c 200
# Check model name matches exactly
python scripts/logcat.py --channel agent.model --level WARN
Fix: Open LM Studio, load the model. Model name in defaults.yaml must match what LM Studio reports at /v1/models.
DeepSeek 401/403/429
# Check API key
python scripts/logcat.py --channel agent.model --level WARN
# Check circuit breaker
python scripts/logcat.py --channel agent.cloud
Fix: Set DEEPSEEK_API_KEY in .env.local. Circuit breaker triggers after 3 failures (60s cooldown). Config: cloud.circuit_breaker.
Tool execution fails
# See tool calls and errors
python scripts/trace_view.py <thread_id> --type tool_call
# See tool classification (safe/sensitive)
python scripts/logcat.py --channel agent.hitl
Fix: Check tool implementation in src/tools/. Check _tool_status_from_content in handler.py for false error detection.
HITL approval missing
# Check HITL decisions
python scripts/logcat.py --channel agent.hitl --thread-id <thread_id>
# Check if auto_approve is set
python scripts/logcat.py --channel agent.hitl --level WARN
Fix: If execution_policy: auto_approve in data/user_profile.json, all HITL is skipped. Check src/agent/nodes/security_proxy.py SENSITIVE_TOOLS list.
Context overflow / summarization
# Check summarization triggers
python scripts/logcat.py --channel memory.summarize
# Check token usage in trace
python scripts/trace_view.py <thread_id> --type llm_response --json | \
python -c "import sys,json; [print(e.get('token_usage',{})) for e in [json.loads(l) for l in sys.stdin]]"
Fix: Summarization triggers at 85% of context window (summarization.threshold_ratio). Config in defaults.yaml.
Memory not injected
# Check memory injection
python scripts/logcat.py --channel memory.inject --thread-id <thread_id>
# Check cache
python scripts/logcat.py --channel memory.cache
# Check extraction
python scripts/logcat.py --channel memory.stm
Fix: Memory injection gated by router (needs_memory_retrieval). Cache TTL is 5 min. Check Redis/Qdrant are running.
Slow responses
# Check model fallbacks
python scripts/logcat.py --channel agent.model --level WARN
# Check tool durations
python scripts/trace_view.py <thread_id> --type tool_call --json | \
python -c "import sys,json; [print(f\"{e['tool_name']}: {e.get('duration_s','?')}s\") for e in [json.loads(l) for l in sys.stdin]]"
# Check coherence retries
python scripts/logcat.py --channel agent.coherence
Fix: Cloud fallback adds latency. Coherence retries add extra LLM calls. Check defaults.yaml timeouts.
Infinite agent loop
# Check recursion
python scripts/logcat.py --channel agent.lifecycle --thread-id <thread_id>
Fix: complex.recursion_limit defaults to 100 in defaults.yaml. Tool-calling cycles can hit this. Check edge conditions in src/agent/core/graph.py.
Frontend blank / WS desync
# Check WS connection
python scripts/logcat.py --channel api.ws
# Check for WS errors
python scripts/logcat.py --channel api.ws --level WARN
Fix: Check frontend-v2/src/lib/wsClient.ts for reconnection logic. Check frontend-v2/src/App.tsx for error boundaries.
Cloud fallback triggered unexpectedly
# Check cloud availability
python scripts/logcat.py --channel agent.model | grep -i cloud
# Check circuit breaker
python scripts/logcat.py --channel agent.cloud
Fix: _check_cloud_available() checks: cloud_escalation_enabled in profile, circuit breaker state, API key presence. Check data/user_profile.json cloud_escalation_enabled.
WebSocket crash (transfer_data_task)
Symptom: AttributeError: 'WebSocketProtocol' object has no attribute 'transfer_data_task'
Cause: websockets 15+ / uvicorn race condition. Client disconnects before connection_open() sets transfer_data_task.
Fix: Already fixed in src/api/ws/handler.py — catches AttributeError alongside RuntimeError in WebSocket close handler.
Known Gotchas
-
Model name must match exactly —
defaults.yaml/.envmust be identical to what LM Studio reports at/v1/models. -
Redis unavailable = in-memory fallback — LangGraph falls back to
MemorySaver; session persistence lost. -
Port conflicts — Kill stale:
lsof -ti:8000 | xargs kill -9andlsof -ti:5173 | xargs kill -9. -
PYTHONPATH must be set —
start.shdoes this. Manual:export PYTHONPATH="$(pwd):$PYTHONPATH". -
Confidence uncalibrated — Router confidence always ~95%, does not reflect actual quality.
-
No self-awareness — System never detects wrong answers.
-
Memory context cap — Injected memory text capped at 12,000 chars in
format_memory_context. -
StirlingPDF needs 2GB RAM — 1GB causes Metaspace OOM. Falls back to PyMuPDF automatically.
-
Background extraction defers — LTM atom extraction waits 8s idle cooldown to avoid GPU contention on Apple Silicon.
-
Cloud circuit breaker — 3 failures triggers 60s cooldown.
-
Security proxy danger patterns — Scans for
rm -rf,sudo,curl,ssh; legitimate commands may match. -
API mode HITL skip — When
mode == "api", all interrupts disabled. -
Recursion limit — Default 100. Tool-calling cycles can hit this.
-
Mem0 requires NLP deps —
pip install mem0ai[nlp]for spaCy/fastembed. -
E2B model leak — Backend warmup auto-loads E2B if it's the default in LM Studio. Workaround: unload E2B before starting backend.
Commands Reference
CI & Testing
./scripts/ci.sh --quick # Ruff, mypy, pytest, vitest
./scripts/ci.sh # Above + frontend build
./scripts/ci.sh --network # Live DeepSeek tests
./scripts/ci.sh --benchmarks # Router/complex/memory benchmarks
Key Test Suites
pytest tests/test_router_properties.py tests/test_router_web_intent.py -q
pytest tests/test_toolbox_registry.py -q
pytest tests/test_websocket_event_contract.py -q
pytest tests/test_memory_nodes.py tests/test_crud_operations.py -q
pytest tests/test_llm_pool.py -q
pytest tests/test_graph.py -q
pytest tests/test_audit_log.py -q
Evaluations
python scripts/run_local_frontier_eval.py # ~19-turn mechanical eval
python scripts/run_educator_eval.py # 8-turn educator eval
python scripts/run_browser_eval.py # 12-turn conversation eval
Format & Lint
ruff check . # Python lint
ruff format . # Python format
cd frontend-v2 && npm run lint # TS/JS lint
cd frontend-v2 && npm run format # TS/JS format
Logs & Traces
python scripts/logcat.py --channel agent.model --follow
python scripts/trace_view.py --latest
python scripts/trace_view.py <thread_id> --json
Config Validation
python3 -c "from src.config.config_loader import validate_config; print(validate_config())"
File Map
By Task
| I want to… | File(s) |
|---|---|
| Change routing / model selection | src/config/defaults.yaml (models section), src/agent/routing/router.py, src/agent/routing/classifier.py |
| Add or change a tool | src/tools/, src/agent/tool_sets.py |
| Fix memory / context injection | src/agent/nodes/memory.py, src/memory/ |
| Change HITL / approvals | src/agent/hitl/, src/agent/nodes/security_proxy.py, src/agent/nodes/scope_clarify.py, src/agent/nodes/plan_review.py |
| Debug a symptom | docs/debugging/README.md → symptom table |
| Change cloud / anonymization | src/agent/core/complex.py, src/agent/core/complex_utils/ |
| Run the app | start.sh, setup.sh, .env |
| Run CI / tests / evals | scripts/ci.sh, scripts/run_*_eval.py |
| View logs | scripts/logcat.py, scripts/trace_view.py |
| Change config | src/config/defaults.yaml (single source of truth) |
| Change WebSocket events | src/api/ws/handler.py, src/api/ws/schemas.py, frontend-v2/src/ |
04 同仓库更多 Skills
weather_lookup
Procedural skill synthesized from user workflow
reprompt_on_no_tools
Procedural skill synthesized from user workflow
prioritize_official_gov_sources_over_aggregators
Procedural skill synthesized from user workflow
matplotlib_horizontal_bar
Procedural skill synthesized from user workflow
live-data-lookup
Procedural skill synthesized from user workflow
html_comparison_chart
Offline Chart.js HTML bar/line/pie charts for pre-known comparison data