findings.md 13 KB

Findings

  • The reference exp_add_feed project uses PyODPS and contains legacy hardcoded credentials; no credential literal may enter tracked files.
  • The portable mini_new/data_query_skill repository already contains four self-contained ODPS query Skills and a Feishu spreadsheet publisher Skill.
  • Codex Python SDK 0.144.4 supports AsyncCodex, thread_start, thread_resume, per-turn JSON Schema output, read-only sandboxing, and explicit approval modes.
  • OpenRouter can be configured as a Codex custom provider using the Responses wire API and OPENROUTER_API_KEY.
  • The existing Feishu implementation uses lark-oapi WebSocket events and the Drive import APIs. The requested permission is tenant-readable, not the reference publisher's public-editable policy.
  • External content and reference repositories are treated as untrusted inputs; only verified interfaces and business definitions are reused.
  • The Agent reference's strongest reusable pattern is a lightweight WebSocket callback plus per-conversation serialization and exact bot-open-id mention checks.
  • lark-oapi INFO logging includes ephemeral WebSocket URL credentials; production uses ERROR for the SDK logger.
  • This host uses a SOCKS proxy, so the isolated environment must include both HTTPX and Requests SOCKS extras.
  • The migrated Feishu and ODPS credentials pass read-only identity/project checks.
  • The project-local OpenRouter key is valid. Initial 401 responses came from a different stale OPENROUTER_API_KEY inherited from the interactive shell because dotenv used override=False; project configuration now explicitly prefers its mode-600 .env.
  • The reference Google route proved the OpenRouter key and dotenv fix. The requested production model is openai/gpt-5.6-terra over OpenRouter Responses.
  • Codex scans all six repository Skills and injects their metadata, but the current worker disables shell/unified tools and constrains output to one SQL, so script-based Skills cannot load or execute their complete workflow.
  • The desired routing policy is: prefer validated SQL/scripts packaged by a matching Skill; use model-generated SQL for generic exploration or uncovered parameters; keep ODPS and Feishu credentials solely in the Python host.
  • query-user-behavior-path is the only fully deterministic end-to-end query Skill currently packaged: user_timeline.py accepts user_id, yyyyMMdd, optional apptype, builds its validated multi-source SQL, executes ODPS, and writes an XLSX.
  • Product-efficiency and growth-fission Skills package request normalization and deterministic report formatting, but their raw-facts SQL is intentionally generated from the Skill contracts; their host execution path must therefore remain Codex generates one validated SQL -> host executes -> Skill formatter.
  • Advertising-risk packages reusable SQL templates plus a standard SQL runner; the Codex planner should select and adapt the matching template, then the existing host SQL guard/ODPS path can execute it.
  • Generic ODPS exploration has no script and should continue through generated, guarded single-SQL execution.
  • Strict structured output requires additionalProperties: false at the root and nested parameter object; Pydantic ConfigDict(extra="forbid") produces the accepted schema for the OpenRouter/OpenAI Responses route.
  • Real SDK routing now selects query-odps-data for an ambiguous DAU request and asks for the missing DAU/table definition rather than fabricating SQL.
  • A fully specified timeline request now returns selected_skill=query-user-behavior-path, execution_mode=skill_script, normalized date/apptype parameters, and sql=null.
  • The resulting Codex session contains user_timeline.py and logs-and-definitions.md, proving the complete Skill workflow and required reference were loaded rather than only its metadata description.
  • Real routing also selects odps-ad-risk-analysis and generates a single SQL from its event-pair template, while an under-specified product-efficiency request selects its dedicated Skill and asks for the required experiment parameters in one message.
  • The Skill-reader permission profile successfully preserves full Skill activation while denying .env, runtime, logs, Git, non-workspace filesystem reads, and tool network access.
  • Behavior-path apptype was previously an explicit default of 0 in the Skill and all three scripts; this caused every source query to filter product 0 even when the user omitted the product.
  • The revised behavior uses a shared conditional SQL fragment: omitted apptype renders no product predicate, while an explicit value is applied identically to all five sources.
  • A real Codex routing check for a timeline question without a product now returns apptype=null, confirming it no longer invents product 0.
  • Product-efficiency run 20260812_145417_f87d8204 repaired two SQL failures and ultimately completed ODPS instance 20260812065533286girvdjg2vxg with the required 16 raw bucket rows; the user-visible failure occurred afterward in deterministic formatting.
  • The raw result has all 26 required columns and no CSV nulls, but dau=0 for all 16 buckets while video/share/return facts are nonzero.
  • add_rates() converts zero DAU denominators to pd.NA; the resulting per-DAU Series has object dtype, and Series.round(4) at formatter line 126 calls Python round(pd.NA), causing the exact NAType TypeError.
  • The final repaired SQL incorrectly buckets offline useractive_log from its physical rootsessionid column. The established offline product-efficiency SQL uses GET_JSON_OBJECT(extparams, '$.rootSessionId'); the Skill reference documented the realtime mapping explicitly but left the offline mapping ambiguous.
  • A one-row read-only diagnostic for 20260808, apptype=4, third-from-end bucket found total DAU 1,610,304; physical-column bucketed DAU 0; extparams bucketed DAU 1,610,300. This proves the all-zero DAU is a SQL field-selection defect, not absent activity.
  • The formatter has a second independent robustness defect: even a legitimately zero DAU baseline should render per-DAU rates as blank/unavailable and continue, per the Skill contract, rather than crash on nullable values.
  • The established offline and realtime product-efficiency SQLs use GET_JSON_OBJECT(extparams, '$.rootSessionId') for both useractive and video-action logs, while source-share logs use their physical rootsessionid column. The Skill must state this exact per-table mapping instead of allowing the planner to choose either source.
  • Existing query Skills reference 14 distinct loghubods source tables across active, video-action, playback, sharing, simple-event, advertising, and operation logs. The behavior-path workflow also joins videoods.dim_video, so the generic catalog needs 15 fully qualified confirmed tables.
  • The catalog must distinguish confirmed business meanings from schema-only fields. Unqualified logical/target names such as ad_own_open_conv are not safe to publish as confirmed source tables without a project-qualified definition.
  • Read-only ODPS metadata confirms all 15 fully qualified tables exist. Most fields are STRING; videoods.dim_video.videoid is BIGINT, and many event-table comments are sparse or null, so business meanings must come from the existing Skill contracts rather than metadata alone.
  • Experiment fields such as rootSessionId, rootSourceId, userShareDepth, and isSpecialLayer may be JSON members of extparams on active/video sources, while share sources expose physical lowercase columns. The generic catalog must preserve these per-table distinctions, especially the prohibition on physical offline useractive_log.rootsessionid for experiment DAU.
  • A live “重新查询” reused an older Codex thread and regenerated the pre-fix physical useractive_log.rootsessionid expression despite the revised Skill on disk. Prompt/Skill constraints alone are insufficient across persisted threads, so the host must validate this critical product-efficiency field contract before ODPS submission and route violations through SQL repair.
  • The 15:39 product-efficiency update changes video-source handling, not report metrics: offline video_action_log_applet and realtime video_action_log_flow must never filter physical business; event selection uses businesstype IN ('videoView','videoPlay','videoShareFriend') with the normal date, app, version, and channel conditions.
  • The same update adjusts host preflight to ignore business when checking video_action_log_applet partition coverage and adds contract checks/tests rejecting business references in both offline and realtime product-efficiency SQL. Removing v.business IS NOT NULL from the latest successful SQL still passes metadata/partition preflight.
  • Product-efficiency run 20260812_154740_92c9afcc produced zero returns because its source-share CTE used user_share_log.type='share'. The established return contract uses source topic='share', click topic='click', same-day/app shareid join, source rootsessionid bucket, and distinct click-side machinecode UV.
  • A bounded diagnostic for 20260811/app 4 found 1,265,139 topic='share' rows versus zero type='share' rows; the correct topic-based join matched 311,039 share IDs and 605,411 globally distinct return users. The zero report is therefore a generated-SQL field error, not absent returns or formatter loss.
  • After the contract update, a fresh Codex thread generated product-efficiency SQL with video businesstype='videoShareFriend', source topic='share', click topic='click', source rootsessionid bucketing, and no type='share' predicate.
  • Product-efficiency run 20260812_152831_290915c8 has a separate all-zero-video defect. Its three rootSessionId mappings are correct: offline active/video use extparams.$.rootSessionId, while share uses physical rootsessionid. The zero facts come from video_action_log_applet.business='applet' added during repair after the partition guard reported missing (dt,business) predicates.
  • Domain correction: the validated product-efficiency query must not filter video_action_log_applet.business, including neither business='applet' nor a three-event business IN (...). It uses exact dt, apptype, and businesstype IN ('videoView','videoPlay','videoShareFriend'). The metadata-driven guard's requirement that every reported partition column appear in predicates is incompatible with this verified table contract and caused the repair failure.
  • The same no-business rule applies to realtime video_action_log_flow: use year/month/dt, apptype, and businesstype, with rootSessionId from extparams. The repository-local Skill and host contract now enforce this symmetrically for offline and realtime product-efficiency queries.
  • The follow-up “分析下 有什么结论吗” was correctly answered by Codex from the current thread, but the structured decision only allowed sql or skill_script; the host discarded the answer and raised “Agent 返回 ready 但没有 SQL”. A first-class direct_reply execution mode fixes the orchestration mismatch without changing SQL generation or validation.
  • A live post-change routing check confirms the boundary: an ROV definition returned a complete direct answer with no SQL, while a fully specified fresh product-efficiency request still selected odps-product-efficiency-report, produced SQL, extracted all parameters, and passed the existing host contract guard.
  • Realtime run 20260812_163712_f891b997 never reached ODPS: all three source CTEs had partition predicates, but the host accepted unqualified partition columns only when the entire SQL contained one partitioned table, so it falsely rejected unambiguous dt/year/month/hh conditions inside single-source CTEs.
  • The same saved SQL also used dt='20260812' on useractive_log_per5min and user_share_log_per5min; the verified realtime contract requires dt LIKE '20260812%' for current-day cumulative data. Total-only product efficiency also needs a direct aggregate workbook path because the deterministic formatter requires experiment buckets and 16 raw bucket rows.
  • Realtime total-only run 20260812_165339_e924353a returned DAU 2,288,634 and return UV 921,517 but zero video facts because it queried video_action_log_flow with dt='20260812'; the actual latest Flow partition is year='2026',month='08',dt='12',hh='16'.
  • The correct Flow hour contains substantial product-0 activity (videoView 2,876,646 PV, videoPlay 2,524,269 PV, videoShareFriend 255,065 PV), proving zero video facts were a partition-value error rather than missing events or the previous offline business predicate defect.
  • video_action_log_per5min uses a single dt='yyyyMMddHHmmss' partition and was current through 20260812165000; product 0 had 25,879,031 videoView, 22,710,351 videoPlay, and 2,378,315 videoShareFriend rows for the day. It contains mid, businesstype, pagesource, extparams, apptype, and physical rootsessionid, but no versioncode; all-version realtime queries should prefer it, while version-filtered queries require Flow.
  • Realtime video partition contracts are materially different and cannot share a generic dt rule: video_action_log_per5min.dt is yyyyMMddHHmmss and daily cumulative reads use LIKE 'yyyyMMdd%'; video_action_log_flow uses year='yyyy'/month='MM'/dt='DD'/hh='HH', where dt is only the day of month.
  • Product-efficiency realtime all-version video now selects video_action_log_per5min; specified-version video may use Flow because per5min has no versioncode.